Included Modules, or “Mixins”

An object can access the instance methods of a module by including that module using the include method. If you were to include MyModule in your program, everything inside that module would suddenly pop into existence within the current scope. So, the greet method of MyModule will now be accessible:

modules2.rb

include MyModule

Note that only instance methods are included. In the previous example, the greet (instance) method has been included, but the MyModule.greet (module) method has not. As it’s included, the greet instance method can be used just as though it were a normal instance method within the current scope, whereas the module method, also named greet, is accessed using dot notation:

puts( greet )            #=> I'm happy. How are you?
puts( MyModule.greet )   #=> I'm grumpy. How are you?

The process of including a module is also called mixing in, which explains why included modules are often called mixins. When you mix modules into a class definition, any objects created from that class will be able to use the instance methods of the mixed-in module just as though they were defined in the class itself. Here the MyClass class mixes in the MyModule module:

modules3.rb

class MyClass
    include MyModule

    def sayHi
        puts( greet )
    end

end

Not only can the methods of this class access the greet method from MyModule, but so too can any objects created from the class:

ob = MyClass.new
ob.sayHi          #=> I'm happy. How are you?
puts(ob.greet)    #=> I'm happy. How are you?

You can think of modules as discrete code units that may simplify the creation of reusable code libraries. On the other hand, you might be more interested in using modules as an alternative to multiple inheritance.

Returning to an example that I mentioned at the start of this chapter, let’s assume you have a Sword class that is not only a weapon but also a treasure. Maybe Sword is a descendant of the Weapon class (so it inherits the Weapon’s deadliness attribute), but it also needs to have the attributes of a Treasure (such as value and owner). Moreover, since this happens to be an Elvish Sword, it also requires the attributes of a MagicThing. If you define these attributes inside Treasure and MagicThing modules rather than Treasure and MagicThing classes, the Sword class would be able to include those modules in order to “mix in” their methods or attributes:

modules4.rb

module MagicThing
    attr_accessor :power
end

module Treasure
    attr_accessor :value
    attr_accessor :owner
end

class Weapon
    attr_accessor :deadliness
end

class Sword < Weapon        # descend from Weapon
    include Treasure        # mix in Treasure
    include MagicThing      # mix in MagicThing
    attr_accessor :name
end

The Sword object now has access to the methods and attributes of the Sword class, of its ancestor class, Weapon, and also of its mixed-in modules, Treasure and MagicThing:

s = Sword.new
s.name = "Excalibur"
s.deadliness = "fatal"
s.value = 1000
s.owner = "Gribbit The Dragon"
s.power = "Glows when Orcs appear"
puts(s.name)            #=> Excalibur
puts(s.deadliness)      #=> fatal
puts(s.value)           #=> 1000
puts(s.owner)           #=> Gribbit The Dragon
puts(s.power)           #=> Glows when Orcs appear

Note, incidentally, that any variables that are local to the module cannot be accessed from outside the module. This is the case even if a method inside the module tries to access a local variable and that method is invoked by code from outside the module, such as when the module is mixed in through inclusion:

mod_vars.rb

x = 1             # local to this program

module Foo
    x = 50        # local to module Foo

                  # this can be mixed in but the variable x won't be visible
    def no_bar
        return x
    end

    def bar
         @x = 1000
         return  @x
    end
    puts( "In Foo: x = #{x}" )   # this can access the module-local x
end

include Foo                      # mix in the Foo module

When you run this program, the puts method executes when the module is initialized, and it displays the value of the module-local variable x:

In Foo: x = 50

If you display the x variable within the main scope of the program, the value of the variable x local to the main scope of the program is used, not the value of the variable x local to the module:

puts(x)           #=> 1

But any attempt to execute the no_bar method will fail:

puts( no_bar )    # Error: undefined local variable or method 'x'

Here the no_bar method is unable to access either of the local variables named x even though x is declared both in the scope of the module (x = 50) and in the current or “main” scope (x = 1). But there is no such problem with instance variables. The bar method is able to return the value of the instance variable @x:

puts(bar)         #=> 1000

A module may have its own instance variables that belong exclusively to the module “object.” These instance variables will be in scope to a module method:

inst_class_vars.rb

module X
    @instvar = "X's @instvar"

    def self.aaa
        puts(@instvar)
    end
end

X.aaa #=> X's @instvar

But instance variables that are referenced in instance objects “belong” to the scope into which that module is included:

module X
    @instvar = "X's @instvar"
    @anotherinstvar = "X's 2nd @instvar"

        def amethod
             @instvar = 10       # creates @instvar in current scope
             puts(@instvar)
        end
end

include X
p( @instvar )                    #=> nil
amethod                          #=> 10
puts( @instvar )                 #=> 10
@instvar = "hello world"
puts( @instvar )                 #=> "hello world"

Class variables are also mixed in, and like instance variables, their values may be reassigned within the current scope:

module X
    @@classvar = "X's @@classvar"
end

include X

puts( @@classvar )         #=> X's @classvar
@@classvar = "bye bye"
puts( @@classvar )         #=> "bye bye"

You may obtain an array of instance variable names using the instance_variables method:

p( X.instance_variables )      #=> [:@instvar, @anotherinstvar]
p( self.instance_variables )   #=> [:@instvar]

Here, X.instance_variables returns a list of the instance variables belonging to the X class, while self.instance_variables returns the instance variables of the current, main, object. The @instvar variable is different in each case.

Note

In Ruby 1.9, the instance_variables method returns an array of symbols. In Ruby 1.8, it returns an array of strings.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.217.17