Is "Extend Self" the Same as "Module_Function"

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

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

When to use self in module's methods

Use self in each method definition if you want the methods to be defined only in the singleton class of the module (where the methods defined using self live). Omit self and extend self if you want the methods of the module to be defined as instance methods and singleton methods at the same time.

For instance, you can call the method using RG::Stats.sum(array) and still have it listed by the instance_methods method if you do this:

module RG::Stats
extend self

def sum(a, args = {})
a.inject(0){ |accum, i| accum + i }
end
end

This way, the sum method is defined as an instance method and it is included in the singleton class of the module after using extend self.

You can check the instance methods of RG::Stats module to verify this:

RG::Stats.instance_methods
=> [:sum]

With this technique you don't have to worry about defining the method without the self keyword because modules can't have instances so it cannot be called like an instance method of RG::Stats module. It can only be called as a singleton method RG::Stats.sum(array) thanks to the extend self statement.

When to use a class vs. module extending self in Crystal?

There is not much difference between class and module regarding definition of class methods. They are however fundamentally different in the fact that a class defines a type that can be instantiated (Service.new). Modules can have instance methods as well, but they can't be instantiated directly, only included in a class.

If you only need a namespace for class methods, you should use module. class would work fine for this too, but conveys a different meaning.

Btw: While you can't extend or include a class, in a module you can write def self.get instead of extend.

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

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.



Related Topics



Leave a reply



Submit