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 def
s 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
Iterate Over a Deeply Nested Level of Hashes in Ruby
Ruby Hash Default Value Behavior
Strange Inability to Require Config/Boot After Upgrading to Ruby 1.9.2
Flattening Nested Hash to a Single Hash with Ruby/Rails
How to Convert a Ruby Hash So That All of Its Keys Are Symbols
How to Set Default Ruby Version with Rvm
How to Generate a Human Readable Time Range Using Ruby on Rails
Ruby Read CSV File as Utf-8 And/Or Convert Ascii-8Bit Encoding to Utf-8
How to Deal with the Conflict Between Activesupport::JSON and the JSON Gem
Differencebetween Integer and Fixnum
How to Compare Strings Ignoring the Case
"Whenever" Gem Running Cron Jobs on Heroku