Ruby Mixins: Extend and Include

Ruby mixins: extend and include

The difference is that include will add the included class to the ancestors of the including class, whereas extend will add the extended class to the ancestors of the extending classes' singleton class. Phew. Let's first observe what happens:

Bacon.ancestors
#=> [Bacon, Object, Kernel, BasicObject]

Bacon.singleton_class.ancestors
#=> [Bar, Baz, Class, Module, Object, Kernel, BasicObject]

Bacon.new.singleton_class.ancestors
#=> [Bacon, Object, Kernel, BasicObject]

Bacon.is_a? Bar
#=> true

Bacon.new.is_a? Bar
#=> false

And for the Egg class

Egg.ancestors
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject]

Egg.singleton_class.ancestors
#=> [Class, Module, Object, Kernel, BasicObject]

Egg.new.singleton_class.ancestors
#=> [Egg, Bar, Baz, Object, Kernel, BasicObject]

Egg.is_a? Bar
#=> false

Egg.new.is_a? Bar
#=> true

So what foo.is_a? Klass actually does is to check whether foo.singleton_class.ancestors contains Klass. The other thing happening is that all the ancestors of a class become ancestors of an instances' singleton class when the instance is created. So this will evaluate to true for all newly created instances of any class:

Egg.ancestors == Egg.new.singleton_class.ancestors

So what does all this mean? extend and include do the same thing on different levels, i hope the following example makes this clear as both ways to extend a class are essentially equivalent:

module A
def foobar
puts 'foobar'
end
end

class B
extend A
end

class C
class << self
include A
end
end

B.singleton_class.ancestors == C.singleton_class.ancestors
#=> true

where class << self is just the odd syntax to get to the singleton class. So extend really just is a shorthand for include in the singleton class.

What is the difference between include and extend in Ruby?

What you have said is correct. However, there is more to it than that.

If you have a class Klazz and module Mod, including Mod in Klazz gives instances of Klazz access to Mod's methods. Or you can extend Klazz with Mod giving the class Klazz access to Mod's methods. But you can also extend an arbitrary object with o.extend Mod. In this case the individual object gets Mod's methods even though all other objects with the same class as o do not.

Extend and Include

module Foo
def self.included(base)
base.extend Foo::ClassMethods
end

module ClassMethods
def guitar
"gently weeps"
end
end
end

class Bar
include Foo
end

puts Bar.guitar

ruby inheritance vs mixins

I just read about this topic in The Well-Grounded Rubyist (great book, by the way). The author does a better job of explaining than I would so I'll quote him:


No single rule or formula always results in the right design. But it’s useful to keep a
couple of considerations in mind when you’re making class-versus-module decisions:

  • Modules don’t have instances. It follows that entities or things are generally best
    modeled in classes, and characteristics or properties of entities or things are
    best encapsulated in modules. Correspondingly, as noted in section 4.1.1, class
    names tend to be nouns, whereas module names are often adjectives (Stack
    versus Stacklike).

  • A class can have only one superclass, but it can mix in as many modules as it wants. If
    you’re using inheritance, give priority to creating a sensible superclass/subclass
    relationship. Don’t use up a class’s one and only superclass relationship to
    endow the class with what might turn out to be just one of several sets of characteristics.

Summing up these rules in one example, here is what you should not do:

module Vehicle 
...
class SelfPropelling
...
class Truck < SelfPropelling
include Vehicle
...

Rather, you should do this:

module SelfPropelling 
...
class Vehicle
include SelfPropelling
...
class Truck < Vehicle
...

The second version models the entities and properties much more neatly. Truck
descends from Vehicle (which makes sense), whereas SelfPropelling is a characteristic of vehicles (at least, all those we care about in this model of the world)—a characteristic that is passed on to trucks by virtue of Truck being a descendant, or specialized
form, of Vehicle.

Is it possible to include module per object in ruby?

Yes, you can:

obj = Object.new
obj.extend MyModule

Using extend self in module

First of all, regarding the actual question: No :).

Class (or any other object) cares how methods are defined in a module you're including. Basically, method's in a module you've described are defined as mixin methods. extend self doesn't redefine methods to be a module methods, but, basically, duplicates them to both contexts.

It's pretty much a question about how does extend work, it's just a tricky case.

First of all, think of extend as an include in object's singleton class context. Those two definitions are equal:

module SomeModule
def hi
'hi'
end
end

class SomeClass
extend SomeModule
end

class SomeClass
class << self
include SomeModule
end
end

Given that, by using extend self in a module you're saying: Take all of the mixin methods I've defined and extend module's singleton class with them. This magic is a result of ruby's nature: an ability to re-open any definition. Here's how a verbose version of extend self would look like:

   module Module1
def hi
'hi'
end
end

module Module1
extend Module1 # which is self

#### now "hi" is both here:
# def hi; end
#### and here:
# class << self; def hi; end
end

Module1.hi # => 'hi'
class SomeClass; include Module1; end;
SomeClass.new.hi # => 'hi'

__ EDIT __

Just a quick proof that object cares about how methods in a module are defined:

module SomeModule
def self.hi
'hi'
end
end

object = 'some string'
class << object
include SomeModule
end
object.hi # => NoMethodError: undefined method

Inheriting class methods from modules / mixins in Ruby

A common idiom is to use included hook and inject class methods from there.

module Foo
def self.included base
base.send :include, InstanceMethods
base.extend ClassMethods
end

module InstanceMethods
def bar1
'bar1'
end
end

module ClassMethods
def bar2
'bar2'
end
end
end

class Test
include Foo
end

Test.new.bar1 # => "bar1"
Test.bar2 # => "bar2"


Related Topics



Leave a reply



Submit