codereading / cuba

Rum based microframework for web development.
http://cuba.is
MIT License
6 stars 2 forks source link

Top-level Module vs Top-level Class #2

Closed agis closed 8 years ago

agis commented 12 years ago

This is propably a silly question but..

I was wondering what are the differences between wrapping the library in a top-level module, like Sinatra does: https://github.com/codereading/sinatra/blob/ver1.3.2/lib/sinatra/base.rb#L15 vs a top-level class, like Cuba does: https://github.com/codereading/cuba/blob/codereading/lib/cuba.rb#L4 and how could one choose between these two techniques.

adamakhtar commented 12 years ago

Hey @agis- nice question. I thought i knew the answer well but when i came to writing the answer it was hard to forumulate the words. hope this helps though.

Ok modules have 3 main uses

1) Protecting the global namespace So many gems and im sure every gem has classes called Account, User, Print, Render etc. What if you used two gems with a Print class. If they werent namespaced and both visible, how would ruby know which one to use when it comes across

Print.new

Placing a project within a module prevents any name collisions.

i.e. AdamsProject::Print.new is different from Agis::Print.new

2) Acting as folders and packaging away related classes and modules together. Well this is related to 1 but within one project you may have 30 modules / classes but maybe they could be split into 5 groups based on similarity. Using modules helps to pack things away nicely and its also great for codreaders because we can quickly get an idea of whats happening under the hood without going into the nitty gritty.

module pdf_renderer_gem
   module dimensions
       def cm_to_inches
       end
       ....
   end

   module renderer
      def to_pdf
      end
      ....
   end
end

you could use a class too ie.

class Pdf_renderer module Dimentions def print_yo; puts "yo"; end end

... end

include Pdf_renderer::Dimensions print_yo

"yo"

but Classes suggest they will be instantiated and hold state. If all the class is doing is acting like a folder then it mores expressive to use a module instead. So what about Cuba's case, why is it's class acting like a folder? Well it is but it does more than that, a lot more => it get instantiated to act as a Rack app.

3) Housing a bunch of methods which can be included into other classes via include MyModule. Without modules the only way for a number of classes to share common methods would be by inheritance. But in many languages you can only inherit from one class. Once youve inherited from a parent, thats it, no more. A bit of a valuable resource. Multiple inheritance - ive heard its a pain so dont go there but not possible in ruby anyway.

If you look at sinatra, the outer module is just acting as a folder. No where do you see nclude Sinatra(i think?!) but you do see include Sinatra::Delegator in main.rb.

Cuba on the other hand is so small. Its main class's instance is the rack app. It needs to use a class called Response but thats only internally. Well why not just define it within the class.

Classes that are intended to be used within a parent class are often defined internally. One example would be when you define your own exception errors

ie..

class User class HasNoNameException < ExceptionError; end

... end

Hope that helps. Perhaps someone else can pitch in / correct any mistakes ive made.

anbotero commented 12 years ago

This explanation blew me away! I can’t wait to give and receive knowledge myself!

agis commented 12 years ago

Indeed, great explanation! I also found this RubyRogues episode to be useful regarding Modules: http://rubyrogues.com/022-rr-when-to-use-modules/

adamakhtar commented 12 years ago

Theres an excellent post here that i think does a much better job than my orginal reply of explaining how to choose between a module and a class when writing your own code.

http://matt.aimonetti.net/posts/2012/07/30/ruby-class-module-mixins/