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
Related
Main Question: In the Ruby API docs for the module_function method, I see the following code example:
module Mod
def one
"This is one"
end
module_function :one
end
class Cls
include Mod
def call_one
one
end
end
Mod.one #=> "This is one"
c = Cls.new
c.call_one #=> "This is one"
module Mod
def one
"This is the new one"
end
end
Mod.one #=> "This is one"
c.call_one #=> "This is the new one"
If I'm reading this code right, I see the following:
A module is defined named Mod, along with a method named one. The module_function method is then called, passing the name of this method as the param.
A class is defined named Cls, and the Mod module is included in this class, giving the class access to the method defined inside Mod. Then a wrapper method is created, which just delegates its call to the module method.
The module method is called directly, and the wrapper method is called on a new instance of the class. Both calls return the same thing.
After that point is where I get confused. The module is then re-opened and a new implementation of the one method is created. Then the two original method calls (the module call and the class instance call) are performed again, this time with different outputs.
If two different outputs are being displayed from the respective method calls, this implies that there must now be two different methods with the same name in the module. Am I correct about that?
Related Question: out of curiosity, I tried the following code, but it didn't produce the same results:
module Sprocket
def create
"Sprocket"
end
module_function
def create
"Sprocket 2.0"
end
end
class SprocketFactory
include Sprocket
def make
create
end
end
p Sprocket.create #=> "Sprocket 2.0"
p SprocketFactory.new.make #=> "Sprocket 2.0"
Therefore, what would the API example code look like if the two one methods had been defined at the same time (instead of re-opening the module, as was done in the docs)? Or is such a thing not possible?
what would the API example code look like if the two one methods had been defined at the same time
module Sprocket
def create
"Sprocket"
end
module_function :create
def create
"Sprocket 2.0"
end
end
You should imagine a timeline that is not revertable.
The first example (with ones): introduces an instance method one, declares it to be a module method, introduces new instance method one. The module method remains unchanged.
Your code with create: introduces an instance method create, introduces new “dual” scope for everything declared below with module_function, both introduces new module method and redeclares the instance method.
The code above: introduces an instance method, declares it to be a module method, introduces new instance method.
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
When you include module in class with method name conflict, it would use method defined by the class. Is there a way to choose which one I want to run?
module B
def self.hello
"hello B"
end
end
class A
include B
def self.hello
"hello A"
end
end
A.hello #=> this prints "hello A", what if I want "hello B"?
Ben, when you call a method (say it's hello) in Ruby, this is what happens:
If the receiver's eigenclass has a method called hello, it will be called. If not:
If the receiver's class has an instance method called hello, it will be called. If not:
If any module included by the receiver's class has an instance method called hello, it will be called. If there are more than one, the most recently included module will "win". Otherwise:
If the superclass of the receiver's class has an instance method called hello, it will be called. If not:
If any module included by the superclass of the receiver's class...
(And so on for the superclass of the superclass, all the way to BasicObject...)
If no method called hello is found, the same process is repeated with method_missing, starting from the eigenclass, then the class, then included modules, then the superclass, then modules included by the superclass... this search always succeeds, because Kernel defines a default implementation of method_missing.
So to answer your question, if there is more than one method called hello, you can't choose which one will be invoked. The method lookup rules described above decide which one will be invoked.
HOWEVER: Ruby is a very flexible language, and if you post more details about what you want to do and why, I can probably help you think of a way to simulate the desired effect.
Another point: if you want to add class methods from a module to a class, you can do this:
module B; def hello; "hello B"; end end
A.extend(B)
A.hello
=> "hello B"
Do you see? When a class includes a module, the module's instance methods become instance methods on the class. When a class extends a module, the module's instance methods become class methods on the class.
The way it's set up it'd never call the included module's method anyway. Try leaving out the hello method in A and see what happens when you call A.hello.
This will include the method from B. There's probably a more succinct way to do this. I just cribbed it from my codebase:
module B
def self.included(base)
base.extend Greetings
end
module Greetings
def hello
"hello B"
end
end
end
The module's method will always override the included module's method. That's the way it's supposed to work. However, you could conditionally return A's hello value like this:
module A
include B
def self.hello
if show_hello_a?
"hello A"
else
super
end
end
def self.show_hello_a?
false # insert an actual condition statement here
end
end
When you call A.Hello all that is happening is the message "hello" is being passed to the A object. The A object then must determine how to handle that message. It will first look at it's own methods to determine if it has one called "hello" before looking to it's parents and included modules for a "hello" method.
While you could technically use A.ancestors to see that B is an ancestor of A, and call it's hello method, this would be violating the abstraction of A as an object.
The correct way to allow both methods to be called would be to create another method in A that calls B.hello, or to name A.hello something else so that it wouldn't override the functionality of B.hello.
Edit: Because you have included B in A, creating a method that calls B's hello within A is as simple as adding a method that calls B.hello
def self.hello2
B.hello
end
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.
In what sort of situation is the code:
module M
extend self
def greet
puts "hello"
end
end
more beneficial to use over say something like:
module M
def self.greet
puts "hello"
end
end
In the top, one is an instance method being extended, and the latter is just a class method, but when calling either method, you'd have to M.greet , right? I was just curious if anyone could shed some light on when to use one code over the other. Thanks!
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.
It would be possible to do this with your first example, but not your second:
include M
greet
A module can be used as a namespace by writing module methods, and a module's instance methods can be mixed into another object.
The self-extending module concept allows a module to be used in both ways; either as a stand-alone namespace or as a mixin. Consider this module:
module M
def bar
puts "bar"
end
end
class C
include M
end
It has an instance method and can be mixed in to another object. It does not have a module method and cannot, therefore be used as a namespace:
puts M::bar # => undefined method `bar' for M:Module
puts C.bar # => this is bar
But, a module is an just an object of class Module, as we can demonstrate
puts M.class # => Module
This means that we can do something crazy. We can mix a module into itself so that its methods become both instance and module methods.
module M
extend self
def bar
puts "bar"
end
end
puts M::bar # => this is bar
puts C.bar # => this is bar