Ruby on Rails Part 3 - Modules and mixins
Table of content
- Modules
- Inside a module method
- require vs include vs extend
- Mixins
- Accessing variable
- Class vs instance methods
Modules
A Module is a collection of methods, constants, and class variables. Modules are defined as a class, but with the module keyword instead of class keyword. The name of a module must start with a capital letter.
You cannot inherit modules using the < i.e. you cannot create a subclass of a module. You cannot create an objects of a module. Modules are used for namespaces and as mixins.
All the classes are modules, but all the modules are not classes. A class can use namespaces, but they cannot use mixins like modules.
The syntax for declaring a module is :
module Module_name
# statements to be executed
end
end
module ModuleName # Create a module
C = 10; # Module constant
# Method name must be preceded by the module name, this is a module method
def ModuleName.methodName
puts "Inside a module method!"
end
end
puts ModuleName::C # This is how to use a module Constant
ModuleName.methodName # calling the methods of the module
Output
$ ruby module_example.rb
10
Inside a module method!
$
Inside a module method
We can define a method with def keyword inside a module i.e. def method_name then it is considered as an instance method. We cannot access instance method directly using MODULE_NAME. use of the dot operator as he cannot make the instance of the module.
To access the instance method defined inside the module, we have to include the module inside a class and then use the class instance to access that method. Below example illustrate this concept.
We include the module in the class. In this case, the module works like a namespace.
module SomeModule
def inst_method
puts "Inside instance method of module"
end
def SomeModule.moduleMethod
puts "Inside class method of module"
end
end
class SomeClass
include SomeModule # include a module in a class
def instSomeClassMethod
puts "Inside class method"
end
end
obj = SomeClass.new
obj.instSomeClassMethod # class instance method can be called using the object
obj.inst_method # module instance method can be called using the ONLY object
SomeModule.moduleMethod # Module method can only be called using module name
Output
$ ruby module_methods.rb
Inside class method
Inside instance method of module
Inside class method of module
$
require vs include vs extend
1. include: is used to include modules in a class at instance level. In short, you need to create an object of the class to access the module’s method. Multiple modules can be included using comma separated statements.
2. extend: is used to include modules in a class at class/static level. In short, you don't need to create an object of the class to access the module’s method. These methods can be
directly accessed using class name.
3. require: is similar to import statement in Java. It’s used to add dependent modules to your code.
Let us assume below two modules in headerfile.rb
# This is a seperate file named headerFile.rb
module ModuleName
I = 10
def ModuleName.methodName
puts "Inside headerFile >> ModuleName.methodName"
end
end
module ModuleName2
I = 10
def ModuleName2.methodName
puts "Inside headerFile >> ModuleName2.methodName"
end
end
The headerFile.rb module is included in mutiple_modules.rb file as below.
# This is the main file multiple_modules.rb
require './headerFile.rb' # added headerFile.rb using require
class ClassName
include ModuleName, ModuleName2 # inclue modules from headerFile.rb to this class
def classMethod
ModuleName2.methodName # calling the module method in the class
end
end
obj = ClassName.new
obj.classMethod
Output
$ ruby multiple_modules.rb
Inside headerFile >> ModuleName2.methodName
$
Mixins
Ruby does not support multiple inheritance directly and instead uses a facility called mixin.
Mixins in Ruby allows modules to access instance methods of another using the include keyword.
In OOP languages, a mixin (or mix-in) is a class that contains methods for use by other classes/modules without the need to inherit from others.
Mixins are an example of has-a relationship and not is-a relationship.
module Base_1
def a1
puts 'This is Base one.'
end
end
module Base_2
def a2
puts 'This is Base two.'
end
end
module Base_3
@val
def a3
puts 'This is Base three.'
end
end
# include all 3 modules in below class
class SubClass
include Base_1
include Base_2
include Base_3
def display
puts "Three modules have been included"
end
end
obj = SubClass.new
obj.display
obj.a1
obj.a2
obj.a3
Output
$ ruby mixin_example.rb
Three modules have been included
This is Base one.
This is Base two.
This is Base three.
$
Above same example can be implemented in a hierarchical mixin.
module Base_1
def a1
puts 'This is Base one.'
end
end
module Base_2
include Base_1 # include Base_1 in Base_2
def a2
puts 'This is Base two.'
end
end
module Base_3
include Base_2 # include Base_3 in Base_3
def a3
puts 'This is Base three.'
end
end
class SubClass
include Base_3 # include Base_3 in SubClass
def display
puts "Three modules have been included"
end
end
obj = SubClass.new
obj.display
obj.a1
obj.a2
obj.a3
Output
$ ruby hierarchy_mixin.rb
Three modules have been included
This is Base one.
This is Base two.
This is Base three.
$
Accessing Variables
In Ruby, object methods are public by default, while data members is private. To access and modify data members we have the attr_reader and attr_writer. These attribute define the getter and setter methods for the data member.
attr_accessor is a shortcut method when you need to do both attr_reader and attr_writer
Below example illustrates the use attr_accessor.
class ClassName
attr_accessor :name # attr_accessor internally creates a getter and setter method
# It maps the variables to class's instance variable using the symbol :name
def initialize(name)
@name = name
end
end
obj = ClassName.new("Hello")
puts obj.name
obj.name = "SWE Crunch Team"
puts obj.name
Output
$ ruby attribute_accessor.rb
Hello
SWE Crunch Team
$
Class vs Instance Methods
A class method is defined using self. It is similar to static method in other languages
. These methods are called using class.
An instance method is defined without using self. These methods can only be called from an object. An instance method cannot be called from a class method and vice-versa
class ClassName
def instanceMethod
puts "Instance method"
end
def self.classMethod
puts "Class method"
end
end
obj = ClassName.new
obj.instanceMethod # instance methods can be called by instances (objects) only
ClassName.classMethod # class methods can only be called by the class only
Output
$ ruby class_inst.rb
Instance method
Class method
$
We cannot call class method from instance method and vice-versa.
class ClassName
def instanceMethod
self.classMethod # This throws an error as you cannot call a class method from an instance method
end
def self.classMethod
instanceMethod # This throws an error as you cannot call an instance method from a class method
end
end
obj = ClassName.new
obj.instanceMethod
# ClassName.classMethod
Output
$ ruby class_m_from_instance.rb
class_m_from_instance.rb:3:in `instanceMethod': undefined method `classMethod' for #<ClassName:0x00007ffa5711c160> (NoMethodError)
from class_m_from_instance.rb:12:in `<main>'
$
Calling instance method in a class method.
class ClassName
def instanceMethod
self.classMethod # This throws an error as you cannot call a class method from an instance method
end
def self.classMethod
instanceMethod # This throws an error as you cannot call an instance method from a class method
end
end
obj = ClassName.new
#obj.instanceMethod
ClassName.classMethod
Output
$ ruby instance_m_from_class.rb
instance_m_from_class.rb:7:in `classMethod': undefined local variable or method `instanceMethod' for ClassName:Class (NameError)
from instance_m_from_class.rb:13:in `<main>'
$