3

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.

2
  • SimpleModule.new.send(hello_world) won't work because you just can't have an instance of a module. Commented Feb 4, 2017 at 5:53
  • @EddeAlmeida thanks. So Ruby implement keyword "include" by its language or I can do a similar things by using metaprogramming ? If metaprogramming possible, should have a way for call method from module. Commented Feb 4, 2017 at 6:00

4 Answers 4

5

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
Sign up to request clarification or add additional context in comments.

5 Comments

thanks. I understand the problem I have met now is because I cannot initialize a module. I tried to proof-of-concept about implement again Ruby keyword include. So if I cannot call method on module like this, can I do by some way ?
thanks for prepend keyword. I don't know this before :D but anyway, they still little like include in term of language-based keyword. ( so I cannot implement this on my own) :D
Didn't know about prepend, I could see that causing a lot of headaches if it was misused :D. Thanks for the writeup!
"I could see how ___ could cause a lot of headaches if it were misused." Ruby--any programming language--provides many, many ways to fill in that blank.
Please refer to this link as how to implement custom include/extend. I have learnt many thing from this post :D
2

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.

2 Comments

thanks. I know "include" keyword. Can I implement "include" on my own ? no real world application, just I want to demonstrate concept but I stuck at call method from module. thanks
I added an example of how you could include the module in a class and then call send on the class to invoke the module method.
2

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

3 Comments

Thanks for linking to source code. it's great. About append, I see I looks like "include", just a little different in behavior compare to include. So I don't think using append is a way for rewrite include :D
about link to source code. How can you find this ? Are you a contributor of Ruby? I am just little curious. So I can know how can I do same thing in the future. thanks
I use pry and pry-docs gem to locate source code, the $ command shows the source code. And then dig further using search at github.com/ruby/ruby
0

You also can use constantize:

def call_method(method)
  "SimpleModule::#{method}".constantize
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.