Ruby: Extend Self

Ruby: extend self

It is a convenient way to make instance methods into class methods. But you can also use it as a more efficient singleton.

extend self in a module

Your first example defines two instance methods and makes them also available as class (or module) methods via extend:

module MyModule 
def first_method; end
def second_method; end
end

MyModule.instance_methods #=> [:second_method, :first_method]
MyModule.methods - Module.methods #=> []

MyModule.extend MyModule

MyModule.instance_methods #=> [:second_method, :first_method]
MyModule.methods - Module.methods #=> [:second_method, :first_method]

Whereas your second example just defines two class (or module) methods and no instance methods:

module MyModule 
def self.first_method; end
def self.second_method; end
end

MyModule.instance_methods #=> []
MyModule.methods - Module.methods #=> [:second_method, :first_method]

The first variant can be useful when you want to provide some utility functions that can be called as:

MyModule.first_method

or be included in other modules / classes:

class Foo
include MyModule

def another_method
first_method # <- no explicit receiver needed
end
end

Ruby also provides the helper method module_function to define methods that way:

module MyModule
def first_method
end
module_function :first_method
end

It adds the method as a class methods and makes the instance method private. It's how the methods in Kernel work.

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

Ruby modules and extend self

The first example is typically a way people achieve the functionality of module_function (when they do not know the existence of this method).

A module_function is both an instance method and a class method. In your second code example the method is just a class method.

Ruby self.extended gets called as instance method

def bar without an explicit definee (i.e. def foo.bar) defines bar in the closest lexically enclosing module. The closest lexically enclosing module for all three of your defs is always Country, so all three methods are defined in the Country module.

If you want to define a singleton method, you could use

<pre class="lang-rb prettyprint-override">module Country
def self.extended(base)
def base.animals
puts "animals"
end
end
end

See Ruby what class gets a method when there is no explicit receiver? for more details.

Is extend self the same as module_function?

module_function makes the given instance methods private, then duplicates and puts them into the module's metaclass as public methods. extend self adds all instance methods to the module's singleton, leaving their visibilities unchanged.

module M
extend self

def a; end

private
def b; end
end

module N
def c; end

private
def d; end

module_function :c, :d
end

class O
include M
include N
end

M.a
M.b # NoMethodError: private method `b' called for M:Module
N.c
N.d
O.new.a
O.new.b # NoMethodError: private method `b' called for O
O.new.c # NoMethodError: private method `c' called for O
O.new.d # NoMethodError: private method `d' called for O


Related Topics



Leave a reply



Submit