Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why would we put a module inside a class in Ruby?

In Ruby, I see that it can be useful to put classes inside modules for the sake of namespacing. I also see that it's possible to put modules inside classes. But I don't see why you'd do that.

Modules are generally mixed into classes, right? So, what would be the purpose of defining a module inside a class?

like image 231
Nathan Long Avatar asked Aug 23 '10 00:08

Nathan Long


People also ask

What is the use of module in Ruby?

Modules are a way of grouping together methods, classes, and constants. Modules give you two major benefits. Modules provide a namespace and prevent name clashes.

Can we use module inside class?

No, you can't instantiate a module inside a class. Module are static objects that are instantiated before time 0 during elaboration. Classes are dynamic objects that are constructed at or after time 0 during simulation.

How do I use a class module in Ruby?

A user cannot access instance method directly with the use of the dot operator as he cannot make the instance of the module. To access the instance method defined inside the module, the user has to include the module inside a class and then use the class instance to access that method.

Why we use module in Rails?

Modules provide a structure to collect Ruby classes, methods, and constants into a single, separately named and defined unit. This is useful so you can avoid clashes with existing classes, methods, and constants, and also so that you can add (mix in) the functionality of modules into your classes.


Video Answer


2 Answers

We could use it when writing ape-like code like this:

class DrugDealer   module Drug     def happy?; true; end   end    def approach(victim)     victim.extend Drug   end end  o = Object.new DrugDealer.new.approach(o) o.happy? # => true 

Another example that would be more practical in the real world is to have mixins that are only applied by subclasses.

This is useful when some facets of a thing apply to some subclasses and other facets apply to other subclasses, without there being enough order in the way these aspects apply to make way for a clear class hierarchy (tree). Think multiple inheritance! A simplified example:

class Person   def handshake     :sloppy   end    def mind_contents     :spam   end    module Proper     def handshake       :firm     end   end    module Clever     def mind_contents       :theories     end   end end  class Professor < Person   include Proper   include Clever    # ... end 

And so on. Kind of nice, when used sensibly. Even super calls and constructors (I didn't define any here though) flow through all the mixins and classes the way I want them to.

like image 188
Jostein Avatar answered Sep 24 '22 07:09

Jostein


I've since run into a use case in a large Rails app with complex namespacing. A simplified example:

# app/models/invoice/dependents/item.rb class Invoice   module Dependents     class Item       # Define invoice item     end   end end 

Here Invoice is a class of its own, but is also a good namespace for its dependent items. We can't say module Invoice because that constant is already defined as a class, but we can still use it as a namespace.

Giant Caveat

If you use a class as a namespace, and you're using Rails, ensure you do not accidentally declare that class elsewhere. Autoloading will ruin your day. For instance:

# app/helpers/invoice/dependents/items_helper.rb class Invoice       # This line will cause you grief   module Dependents     module ItemsHelper       # view helper methods     end   end end 

The fact that class Invoice is stated in this file creates a load order dependency; if this file's class Invoice line is executed before your intended class definition, your intended class definition may not work properly. In this example, I can't declare that Invoice sublcasses ActiveRecord::Base if Invoice has already been declared with no parent class.

You could require your "true" class definition file at the top of another file, but at least in a Rails autoloading scenario, you'll have less wrangling to do if you do this instead:

# app/helpers/invoice/dependents/items_helper.rb module Invoice:Dependents::ItemsHelper     # view helper methods end 

With this syntax, Rails will see the Invoice constant and use autoload to look it up, finding it in your model file and defining it the way you intended.

like image 22
Nathan Long Avatar answered Sep 26 '22 07:09

Nathan Long