Tuesday, 3 February 2009

Modules

A module is a collection of methods and constants, much like a method. However, a module cannot be instantiated. Instead, a module is added to an existing class or object to create a "mixin". Here is an example of a module, with both a method and a constant defined:
module TestModule
TEST_CONST = 1000

def hello
p "Hello"
end
end

The are two ways to add a module to your class, using the keywords include or extend:
class TestClass1
include TestModule
end

class TestClass2
extend TestModule
end

The include statement causes all the methods in the module to be added as instance methods, and also allows the constants to be accessed through the method. In contrast, the extend statement adds all the methods as class methods, and does nothing with the constants. Let us look at the constants first:
p TestModule::TEST_CONST
p TestClass1::TEST_CONST
begin
p TestClass2::TEST_CONST
rescue NameError
p $!
end

The class methods. TestClass2 had the module added using extend, so the method in the module is a class method:
begin
TestClass1.hello
rescue NoMethodError
p $!
end
TestClass2.hello

Finally the instance methods. TestClass1 had the module added using include, so the method in the module is an instance method. Note that you can add a module to an object at run time, but in that case the extend keyword is used:
# Create some instances
tc1 = TestClass1.new
tc2 = TestClass2.new
tc2too = TestClass2.new

# Methods accessible at the instance level
tc1.hello
begin
tc2.hello
rescue NoMethodError
p $!
end

# Module added at runtime
tc2.extend TestModule
tc2.hello
begin
tc2too.hello
rescue NoMethodError
p $!
end

I think the logic here is that extend is used for singletons; a single class or a single instance of the class.

Instantiation
Ruby will invoke the included or extended methods in your module, if they exist, when the module is included or extended respectively. These have to be defined as class methods - even though this is not a class. As far as I know, this is the only time you do tat for a module. Both methods should take a single parameter; the object or class to which the module is being added. An example:

module TestModule
def self.included base
p "I am being included by #{base}"
end

def self.extended base
p "I am being extended by #{base}"
end
end

class TestClass1
include TestModule
end

class TestClass2
extend TestModule
end

p 'Classes now defined'

tc2 = TestClass2.new
tc2.extend TestModule

# Output
#
# => "I am being included by TestClass1"
# => "I am being extended by TestClass2"
# => "Classes now defined"
# => "I am being extended by #<TestClass2:0x987a33>"

See also:
http://www.juixe.com/techknow/index.php/2006/06/15/mixins-in-ruby/

Struggling with Ruby: Contents Page

1 comment:

Unknown said...

Do give a look here:
http://adhirajrankhambe.wordpress.com/2009/01/04/a-word-about-modules-in-ruby/