When to use self in module's methods - ruby

My module definition looks like this:
module RG::Stats
def self.sum(a, args = {})
a.inject(0){ |accum, i| accum + i }
end
end
To use this method I simply require the file containing this definition so that I can do:
RG::Stats.sum(array)
and also
RG::Stats.method(:sum)
However, if I need to know the list of methods using RG::Stats.instance_methods I get an empty array. This is because I have used self. If I omit self then RG::Stats.instance_methods gives the list of methods, but I cannot access them anymore.
The question is: how to use self in module's method definition?

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.

If you use self, then you can use the module method directly using: RG::Stats.sum(array) as you mentioned above.
If you don't use self to define the method inside your module, then the method will be included as instance_method for a class where you include the module.
So, you can define the sum instance method inside your RG::Stats module:
module RG::Stats
def sum(a, args = {})
a.inject(0){ |accum, i| accum + i }
end
end
Then, you can include RG::Stats module inside a class and then the sum method will be available as a instance_method for objects of the class:
class YourClass
include RG::Stats
. . .
. . .
end
And you can call the sum method on an instance of YourClass:
YourClass.new.sum
To answer your question, you can use self to define your method as a class method inside your module, and without self to define the method as instance method and it depends on your need and usecase that when you want what type of behaviour from your module methods.

It is because def self.sum does not define an instance method; you are using the wrong method to select it. Try this instead:
RG::Stats.methods(false)
# => [:sum]

Class: RuboCop::Cop::Style::ModuleFunction rule recommends to use module_function to make all methods self.
ref: https://www.rubydoc.info/gems/rubocop/RuboCop/Cop/Style/ModuleFunction
# bad
module Test
extend self
# ...
end
# good
module Test
module_function
# ...
end

Related

NoMethodError in ruby module

module Add
def addition
sum=1+2
puts sum
end
a=Add.addition
Can anyone tell me what I'm missing and why I am getting this error->
undefined method `addition' for Add:Module (NoMethodError)
You are confusing class methods and instance methods. Your definition:
module Add
def addition
...
end
end
defines methods on instances of Add whereas you called a method on the module Add. If you want to define a class/module method, you need to define like:
module Add
def self.addition
...
end
end
If you want to be able to call it directly, define it as a directly accessible method:
def self.addition
# ...
end
Or you can always rework this using:
module Add
# ...(methods)...
extend self
end
Where that will automatically promote all mixin-type methods as being directly accessible.
You can also tag them more selectively like this:
module Add
def addition
# ...
end
module_method :addition
end
That method is then available either as Add.addition or if some other module or class calls include Add.

Ruby metaprogramming: cannot send a method to a module

For example I have following custom class and module:
module SimpleModule
def hello_world
puts 'i am a SimpleModule method'
end
def self.class_hello_world
puts 'i am a SimpleModule class method'
end
end
class SimpleClass
def hello_world
puts 'i am SimpleClass method'
end
def self.class_hello_world
puts 'i am a SimpleClass class method'
end
end
I tried to called those methods inside class and module by using method send
SimpleClass.send(class_hello_world) # work
SimpleClass.new.send(hello_world) # work
SimpleModule.send(class_hello_world) # work
SimpleModule.new.send(hello_world) # not work
SimpleModule.send(hello_world) # not work
In other word, I don't know how to invoke hello_world from SimpleModule. It is possible if that method is defined with self before.
I need to do this because I want to implement a "custom-include": that include all methods from module to another class.
Please tell me how to do this.
The five statements
Let's consider those five statements one at a time (but in a different order than as presented). Note that send's argument must be the name of the method expressed as a string or symbol.
SimpleModule.send("class_hello_world")
# i am a SimpleModule class method
This is normal, though such methods are normally called module methods. Some common built-in modules, such as Math, contain module methods only.
SimpleClass.send(:class_hello_world)
# i am a SimpleClass class method
Since a class is a module, the behaviour is the same as above. class_hello_world is usually referred to as a class method.
SimpleClass.new.send(:hello_world)
# i am SimpleClass method
This is the normal invocation of an instance method.
SimpleModule.send("hello_world")
#=> NoMethodError: undefined method `hello_world' for SimpleModule:Module
There is no module method hello_world.
SimpleModule.new.send(hello_world)
#=> NoMethodError: undefined method `new' for SimpleModule:Module
One cannot create an instance of a module.
include vs prepend
Suppose one wrote
SimpleClass.include SimpleModule
#=> SimpleClass
SimpleClass.new.hello_world
# i am SimpleClass method
so SimpleClass' original method hello_world is not overwritten by the module's method by the same name. Consider SimpleClass' ancestors.
SimpleClass.ancestors
#=> [SimpleClass, SimpleModule, Object, Kernel, BasicObject]
Ruby will look for hello_world in SimpleClass--and find it--before considering SimpleModule.
One can, however, use Module#prepend to put SimpleModule#hello_world before SimpleClass#hello_world.
SimpleClass.prepend SimpleModule
#=> SimpleClass
SimpleClass.new.hello_world
# i am a SimpleModule method
SimpleClass.ancestors
#=> [SimpleModule, SimpleClass, Object, Kernel, BasicObject]
Binding unbound methods
There is one other thing you do. SimpleModule's instance methods (here just one) are unbound. You could use UnboundMethod#bind to bind each to an instance of SimpleClass and then execute it using call or send.
sc = SimpleClass.new
#=> #<SimpleClass:0x007fcbc2046010>
um = SimpleModule.instance_method(:hello_world)
#=> #<UnboundMethod: SimpleModule#hello_world>
bm = um.bind(sc)
#=> #<Method: SimpleModule#hello_world>
bm.call
#=> i am a SimpleModule method
sc.send(:hello_world)
#=> i am a SimpleModule method
Module's can't have instance methods because they aren't classes. If you define an instance method in a module it is called a mixin. These "mixins" can be included in another class and are then available for use.
The full explanation is here in the docs
Edit:
For example, you could do something like this:
module SimpleModule
def hello_world
p 'hello world'
end
end
class Example
include SimpleModule
end
Example.new.send(:hello_world)
This is one way to call the mixin of a module.
Since you seem to want to create your own version of include.
Take a look at Module.append_features documentation
When this module is included in another, Ruby calls append_features in
this module, passing it the receiving module in mod. Ruby's default
implementation is to add the constants, methods, and module variables
of this module to mod if this module has not already been added to mod
or one of its ancestors. See also Module#include.
So this is what you have to do if you want to rewrite include yourself.
And since you're adventurous, maybe you will enjoy reading Ruby's source code to see how exactly this is implemented internally. See https://github.com/ruby/ruby...class.c#L853:L934
You also can use constantize:
def call_method(method)
"SimpleModule::#{method}".constantize
end

Inheriting module/class methods

I have this module with a method:
module MySqlConnection
def database_connect(application)
# Code in here
end
end
I have a second module with a class that uses database_connect:
module ResetPopups
class PopupsOff
include MySqlConnection
def self.reset_popup
database_connect("#{APP}")
end
end
end
When I call:
ResetPopups::PopupsOff.reset_popup
I get an undefined method database_connect. Why does this happen?
include adds module methods as instance methods, while extend adds them as singleton methods. Since you want to use this in the context of class (singleton context), you need to use extend:
extend MySqlConnection
module ResetPopups
class PopupsOff
extend MySqlConnection
def self.reset_popup
database_connect("#{APP}")
end
end
end
would work
What is hapening here, is that you include MySqlConnection, which makes method defined in it (database_connect) instance methods. But you use this module in the class scope, calling the database_connect on the class.

Using extend self in module

Before voting for closing due to question duplication I want to say that my question is really simple one (not asked in above mentioned questions).
There are two modules, one defines module method using extend self, another defines mixin method.
module A
extend self
def module_a_meth
"Called module_a_meth"
end
end
module B
def module_b_meth
"Called module_b_meth"
end
end
There is a class, where I both include and extend these modules:
class Test
include A
extend A
include B
extend B
end
When we includeing module, its methods become class' instance methods, when extending - class methods.
Question:
it doesn't matter for class, if methods in module defined as module methods or mixin methods, right? I mean, when included - EVERY method (either module methods or mixin methods) become instance methods, and when extended - either become class methods.
If I'm wrong - where is the difference?
obj = Test.new
puts obj.module_a_meth
puts obj.module_b_meth
puts Test.module_a_meth
puts Test.module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth
#=> Called module_a_meth
#=> Called module_b_meth
EDIT
Please start your answer with Yes or No, since my question implies this type of answer :).
Regardless of whether you are using extend or include you are always copying over instance methods. The difference is where those instance methods live.
When you call Class#include you are "copying" all of the instance methods in the module to be instance methods in the class. It's similar to how inheritance work, and if you call Class#ancestors you'll see the module there.
When you call Object#extend you are copying all of the instance methods of the module to the object's singleton class. This is a class reserved just for this object instance that is created at runtime. This is how you get "class methods" (e.g. MyClass.hello_world); by adding them to the class's singleton. You can also do things like extend a particular object instance (e.g. s = String.new; s.extend(SomeModule); s.hello_world)
There are some other differences too. The context binding is different depending on whether you use extend or include. extend doesn't cause the module to show up in the ancestor chain while include does.
When trying to add both "class" and instance methods, one common pattern you'll see is doing things like this which uses the included callback to extend the base class with a ClassMethods module:
module MyModule
def self.included(base)
base.extend ClassMethods
end
module ClassMethods
def hello_world
end
end
end
ActiveSupport::Concerns also abstracts this pattern allowing you to add both instance and "class" methods in one call.
I personally prefer having modules only work with instance methods and using singleton methods (e.g. def self.my_method) to have scoped methods (sort of like how you would use private methods). This allows consumers to use either extend or include however they want and have it work as expected.
I'm not sure if that answers your question or not, but there's some info for you
Let's look at this in steps.
module A
puts "self = #{self}"
extend self
def module_a_meth
"Called module_a_meth"
end
end
class Test
end
Test.include A
#-> self = Test
Test.instance_methods.include?(:module_a_meth)
#=> true
Test.methods.include?(:module_a_meth)
#=> false - no class method
So include includes :module_a_meth as an instance method. As self is Test, the line:
extend self
is equivalent to:
extend Test
which of course makes no reference to the module. Now we extend and obtain the expected result:
Test.extend A
#=> true
Test.methods.include?(:module_a_meth)
#=> true
including and extending B is normal:
module B
def module_b_meth
"Called module_b_meth"
end
end
Test.include B
Test.instance_methods.include?(:module_b_meth)
#=> true
Test.extend B
Test.methods.include?(:module_b_meth)
#=> true
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

Modules in Ruby

Please explain why self is used in def self.included (klass) below.
module A
def self.included(klass)
puts "A -> #{klass}"
puts A
puts self
end
end
class X
include A
end
By writing def self.included you are defining a method that is part of the singleton class of module A. In general, singleton methods can only be called by doing A.included() but this singleton method has a special name included that causes the Ruby interpreter to call when the module gets included in to a class.
A normal method in a module (defined with def foo) can only be called if the module gets included in to something else.
This is how you declare a module method that can be called directly. Normally methods defined within a module are only usable if another class or module includes them, like class X in this example.
module Example
def self.can_be_called
true
end
def must_be_included
true
end
end
In this case you will see these results:
Example.can_be_called
# => true
Example.must_be_included
# => NoMethodError: undefined method `must_be_included' for Example:Module
The self declared methods are not merged in to the classes or modules that include it, though. They are special-purpose that way.

Resources