In the discussion of the difference between include
and extend
in Ruby, there is a misconception that extend
would add methods to the singleton class of a ruby object as stated in many posts on this topic. But in fact, it is added to the ancestors chain of the singleton class! Even though it is technically not the same, practically this can be considered the same in most use cases.
Example
This means, that we are able to overwrite these methods or call the parent version with super
depending in which order and in which way they were added to singleton class.
Consider the following code:
module A
def foo
puts "Foo!!"
end
end
module B
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def foo
puts "Foo!"
super
end
end
end
class User
singleton_class.include(A)
include B
end
User.instance_eval do
def foo
puts 'Foo'
super
end
end
User.foo # => "Foo Foo! Foo!!"
User.singleton_class.ancestors # => [#<Class:User>, B::ClassMethods, A, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject]
User.singleton_class.class # => Class
User.singleton_class.ancestors[1].class # => Module
Note that #<Class:User>
is the singleton class and as we have seen the parent versions of foo
have only been added to the ancestor chain of the singleton class and not to the singleton class itself.
Why does
extend
include class methods in this example?The reason why the singleton class defines it's methods as class methods (and not as instance methods) is, because now it is a Module of the singleton class of the actual
User
class object and not on an instance ofUser
. You can also use this to extend the singleton class of an user to add instance methods.
singleton_methods(all)
This is also the reason why
#singleton_methods
Show archive.org snapshot
has a boolean all
parameter to decide whether methods on the singleton inheritance chain should be included.
For example, if module A
defined method #bar
and Module B
defines method #baz
:
User.singleton_methods # => [:foo, :bar, :baz]
User.singleton_methods(false) # => [:foo]
For the technical details have a look at this great card on the Ruby object model.