Meaning of Syntax for Ruby Module - ruby

I've been going through some tutorials to find this information, but haven't seen anything that directly addresses it.
I've seen several times on modules the following syntax:
module MyModule
def run()
puts "running"
end
end
I've also seen syntax that looks like this:
module MyModule
def MyModule.run()
puts "running"
end
end
What's the advantage to including the module name before the method and vice versa?

module MyModule
def MyModule.run()
puts "running"
end
end
is exactly the same as:
module MyModule
def self.run()
puts "running"
end
end
Usually def self.run is used, because it's better when you have to change the module name and it's more idiomatic. I don't see any advantages in writing def MyModule.run.

This has nothing to do with modules. This is just normal method definition syntax.
The syntax for a method definition in Ruby is
def <target>.<selector>(<parameters>)
# …
end
For example:
def foo.bar(baz)
end
This will define a method named bar on the object referenced by foo (more precisely, in the singleton class of the object referenced by foo), with a single mandatory positional parameter whose binding is named baz.
Like with message sends, you can leave out the target, and Ruby will use an implicit default. In a message send, the implicit default is self, with a method definition, the default is the so-called default definee, which is usually the closest lexically enclosing module definition body.
So,
def MyModule.run
means "define a method named run on the object MyModule (or more precisely in the singleton class of the object MyModule)", whereas
def run
means "define a method named run in the default definee", i.e. the closest lexically enclosing module definition body, which in this case is MyModule.
The second version defines run as an instance method of MyModule, the first version defines run as an instance method of the singleton class of MyModule, which we sometimes call a "module method" or "module function".
Note that the first version is usually more idiomatically written as
def self.run

This is about using a module as a static namespace vs using a module as a mixin. Have a look at the following code (with output in comments):
module MyModule
def MyModule.run()
puts "#{self}: running"
end
def run()
puts "#{self}: running"
end
end
class Foo
include MyModule
end
MyModule.run #MyModule: running
foo = Foo.new
foo.run #<Foo:0x007f9b269bf028>: running
In the first usage, the module is basically just acting as a namespace to which you attach a method.
In the second, the module is mixed in to the class Foo. This means that it acts as if the run method was defined within the foo class in the first place; you could, if you wished, refer to instance variables #bar which aren't defined in the module, but only in the Foo class.

I guess both are same but in case when we call module method inside class then we have to give
ModuleName.method_name()
So that it can understand that this method is required for included or that particular module

Related

Why can't Ruby find a method declared right above?

I have a simple file called helper.rb that looks like this:
module MyHelper
def initialize_helper
puts "Initialized"
end
initialize_helper()
end
And another simple file like this:
require_relative 'helper.rb'
include MyHelper
puts "Done"
But when I run this second file, it results in this error:
helper.rb:6:in `<module:MyHelper>': undefined method `initialize_helper' for MyHelper:Module (NoMethodError)
Why can't Ruby find this initializeHelper method defined directly above where I'm calling it???
Try
def self.initialize_helper
puts "Initialized"
end
Without the self., you're declaring an instance method intended to be called on objects, not the module itself. So, for instance, your original code is intended to be used like
module MyHelper
def initialize_helper
puts "Initialized"
end
end
class Foo
include MyHelper
end
Foo.new.initialize_helper
But if you want to call it on the module, you need to have self. in front of it to make it a method on the module itself.

Ruby metaprogramming: define_method block not maintaining scope

I'm working on dynamically patching a bunch of classes and methods(most of the time these methods are not simple "puts" like a lot of examples I've been able to find on the internet)
Say for instance I have the following code:
foo.rb
module Base
class Foo
def info
puts 'Foo#info called'
end
end
end
& I also have the following class:
test.rb
module Base
class Test
def print
puts "Test#print called"
Foo.new.info
end
end
end
Then in main.rb I have the following where I want to add a method that uses a class within the same module(Foo in this case)
require_relative './foo'
require_relative './test'
new_method_content = "puts 'hi'
Foo.new.info"
Base::Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
Which, when executed will net the following:
Uncaught exception: uninitialized constant Foo
Which sort of makes sense to me because the main.rb file doesn't know that I want Base::Foo, however, I need a way to maintain lookup scope because Base::Test should be able to find the class Foo that I want.
Base::Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
I've done a fair bit of googling and SO'ing but haven't found anything about how to maintain constant lookup scope while class_eval/instance_eval/module_eval/define_method(I've tried a lot of Ruby's dark magic methods all of which have ended in varying degrees of failure lol)
https://cirw.in/blog/constant-lookup
Confusingly however, if you pass a String to these methods, then the String is evaluated with Module.nesting containing just the class itself (for class_eval) or just the singleton class of the object (for instance_eval).
& also this:
https://bugs.ruby-lang.org/issues/6838
Evaluates the string or block in the context of mod, except that when
a block is given, constant/class variable lookup is not affected.
So my question is:
How can I redefine a method BUT maintain constant/class scope?
I've been trying a bunch of other things(in the context of main.rb):
Base::Test.class_eval('def asdf; puts "Test#asdf called"; Foo.new.info; end')
Base::Test.new.asdf
=>
Test#asdf called
Uncaught exception: uninitialized constant Base::Test::Foo
Did you mean? Base::Foo
(which is a diff problem in that it's trying to look it up from the evaluated module nesting? I'm not sure why it doesn't try all module paths available from Base::Test though, I would think it would try Base::Test::Foo which doesn't exist, so then it would go up the module tree looking for the class(Base::Foo) which would exist)
When you reference the class Base::Test like this, ruby does not take the Base:: as the module context to look up constants. That is the normal behaviour and would also not work, if you would define the moethod directly.
But you could do it in this way:
module Base
Test.instance_eval do
def asdf
puts "Test#asdf called"
Foo.new.info
end
end
end

How to pick the method to call when there is mixin method name conflict?

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

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.

ruby modules: using data from a class definition in a module

i've got a module that wants to use data provided by the class that included it - but at the class level, not the instance level.
the goal is to have class 'metadata' provided to a module that the class includes, so that the module can use the metadata during the included call.
this works:
module Bar
def value
#value
end
def baz
puts "the value is: #{value}"
end
end
module Foo
def self.included(mod)
mod.extend(Bar)
mod.baz
end
end
class MyClass
#value = "my class defined this"
include Foo
end
the output of this code is
the value is: my class defined this
i'm not sure if the use of #value is good or not... it seems odd to me that i require this to be set before the include Foo happens, not from a technical perspective (i know why it's required to be done in this order) but from an idiomatic or usability perspective.
... is there a better way / more idiomatic way of accomplishing this?
If you really want to use the class metadata in the moment you're including a module, given the 'included' method runs on its own scope, it's best to have a class method providing the metadata to it.
Also, if the metadata is not going to be manipulated, its better to declare it as a constant.
module Bar
def self.included(base)
puts "the value is: #{base.metadata}"
end
end
class MyClass
VALUE = "MyClass metadata"
def self.metadata
VALUE
end
include Bar
end
class OtherClass
VALUE = "OtherClass metadata"
def self.metadata
VALUE
end
include Bar
end
Of course you can declare the metadata anyway you want, as long as its accessible by a class method to your Module.
Also, its not common to do these kind of metadata manipulation in the module's 'included' method and the necessity of ordering your statements on the class level is a bit brittle, so you might want to try to find a different solution to your original problem instead.
If you want to the class to pass an argument to the mixin, then why not use one of the Ruby constructs that actually does allow passing an argument?
class Object
private
def Bar(metadata)
Module.new do
include Bar
define_singleton_method(:included) do |base|
puts "the value is: #{metadata}"
end
end
end
end
module Bar
# put common behavior here
end
class MyClass
include Bar 'MyClass metadata'
end
class OtherClass
include Bar 'OtherClass metadata'
end
This is a pretty common idiom that is for example used by the delegate library in the stdlib.

Resources