How can I define a method aside a class inside a module? - ruby

How can I define a method aside a class where I use this method inside the same module?
What I want to do:
module X
def bar
puts 'Hello'
end
class Y
def foo
bar
end
end
end
I want to call bar method using Y.new.foo for example. I want to define methods in my module so that all the classes in this module will inherit them.
Why isn't this working? And what is the closest way to do this?

Assuming that you don't want to include the module in the class, you can't reference the method, as there is no instance to call it on. You can instead:
module X
def self.bar
puts 'Hello'
end
class Y
def foo
X.bar
end
end
end

I think it's better to use a separate object to define shared methods rather than polluting the name space object.
module X
module InstanceMethods
def bar
puts 'Hello'
end
end
class Y
include InstanceMethods
def foo
bar
end
end
end

if you want to use many methods, it's better to use include
module X
def bar
puts 'Hello'
end
class Y
include X
def foo
bar
end
end
end

Related

accessing method in module of module ruby

I'm trying to modify existing ruby code, and ruby is not my first languange. Part of the code is like below:
#someFile1.rb
module A
module B
def somefunction()
end
end
end
class X::Y
include A::B
end
#someFile2.rb
module A
module C
def anotherfunction()
#somefunction() <-- error
end
end
end
class X::Y
include A::C
end
Somehow I can't access method somefunction() in anotherfunction.
How to access method defined in module B in method inside module C? Why it's not working?
Instance methods in modules aren't generally accessible until you mix them into a class and create an object of that class.
module A
module B
def some_method
"foo"
end
end
end
module A
module C
def another_method
some_method
end
end
end
class X
include A::B
include A::C
end
X.new.another_method
# => "foo"
But I'd say it isn't very elegant to have a module that depends upon the fact that some other module has also been mixed into the same object
Class methods in modules, on the other hand, are accessible like this:
module A
module B
def self.somefunction
"foo"
end
end
end
module A
module C
def self.another_function
A::B.somefunction
end
end
end
A::C.another_function
# => "foo"
Assuming you want to call the module functions themselves, you would first need to make them module functions (think static in Java or namespace in C++). Then you can use the :: (namespace resolution) operator. See foo and bar.
If you want to import them into classes, just import both, and both will be visible. See baz and qux.
module A
module B
def self.foo
puts "foo"
end
def baz
puts "baz"
end
end
end
module A
module C
def self.bar
puts "bar"
A::B::foo
end
def qux
puts "qux"
baz
end
end
end
class X
include A::B
include A::C
end
A::C::bar
x = X.new
x.qux
Output:
bar
foo
baz
qux

How to mixin some class methods and some instance methods from a module in Ruby?

I have a module and would like to mixin some methods as class methods and some as instance methods.
For example:
module Foo
def self.class_method
end
def instance_method
end
end
class Bar
include Foo
end
Usage:
Bar.class_method
Bar.new.instance_method
Is it possible to do this in Ruby?
If not, is it possible to define which methods are class methods and which are instance methods within the Bar class?
I don't want the same method defined as both a class and instance method.
This pattern is very common in Ruby. So common, in fact, that ActiveSupport::Concern abstracts it a bit.
Your typical implementation looks like this:
module Foo
def self.included(other_mod)
other_mod.extend ClassMethods
end
def instance_method
end
module ClassMethods
def class_method
end
end
end
class Bar
include Foo
end
You can't accomplish this easily as you describe without somehow splitting the included module into multiple pieces, though, unfortunately.
You can, but not quite like that. This is a common pattern for including both instance and class methods in one module.
module Foo
def self.included(base)
base.extend ClassMethods
end
def instance_method
puts 'instance'
end
module ClassMethods
def class_method
puts 'class'
end
end
end
class Bar
include Foo
end
bar = Bar.new
Bar.class_method #=> 'class'
bar.instance_method #=> 'instance'
You are close. You probably noticed that the instance method works fine. The problem with the class method is that self => Foo when it's defined, so it does not respond to Bar. If you add the line puts "I'm a module method" in self.class_method, you will find
Foo.class_method => "I'm a module method"
Here's an easy way to accomplish what you want to do:
module Foo_class
attr_accessor :cat
def class_method
puts "I'm a class method"
end
end
module Foo_instance
def instance_method
puts "I'm an instance method"
end
end
class Bar
extend Foo_class
include Foo_instance
end
Bar.class_method #=> I'm a class method
Bar.cat = "meow"
Bar.cat #=> "meow"
Bar.new.instance_method #=> I'm an instance method
I added a class instance variable, #cat, and an accessor for it, just to show how easy that is to do.
Object#extend is great, because you can just add instance variables and methods to a module, just as you would do with Object#include to mixin instance variables and methods, and extend mixes them in as class instance variables and class methods. You can also do this:
bar = Bar.new
bar.extend Foo_class
to have the instance variables and methods in Foo_class apply to the instance bar.

Execute methods with the same name of multiple modules included

I have two modules with the same method name. When I include both modules in some class, only the method of the last module is executed. I need instead both to be executed when I initialize the class:
class MyClass
include FirstModule
include SecondModule
def initialize
foo # foo is contained in both modules but only the one in SecondModules is executed
end
end
Is it doable?
As Yusuke Endoh might say, everything is doable in Ruby. In this case, you have to forget about convenience of just saying 'foo', and you have to be very explicit about what you actually want to do, like this:
class MyClass
include FirstModule
include SecondModule
def initialize
FirstModule.instance_method( :foo ).bind( self ).call
SecondModule.instance_method( :foo ).bind( self ).call
end
end
The line 'FirstModule.instance_method...' can be replaced by simply saying 'foo', but by being explicit, you ensure that no matter what, you are calling the method from that mixin, from which you think you do.
Can you modify the included modules? Perhaps you just call super in the second module?
module M1
def foo
p :M1
end
end
module M2
def foo
p :M2
defined?(super) && super
end
end
class SC
include M1
include M2
def initialize
foo
end
end
SC.new
Or perhaps you actually want to do this?
module M1
def bar; p :M1 end
end
module M2
include M1
def foo; bar; p :M2 end
end
class SC
include M2
def initialize; foo end
end
See live demo here

Can I "retroactively" add class methods from a module (after it's already been included)?

In Ruby, I can do this:
module Foo
end
class Bar
include Foo
end
module Foo
def do_something_instancey
puts "I'm an instance!"
end
end
Then, if I instantiate a Bar object, I can call do_something_instancey on it:
b = Bar.new
b.do_something_instancey
However, if I do this...
module Foo
def self.included(base)
def base.do_something_classy do
puts "I'm a class!"
end
end
end
My understanding is that because I included Foo in Bar before defining that class method, I cannot call Bar.do_something_classy because it never got "attached" to Bar.
I realize that might be slightly inaccurate/not really the right terminology. Regardless, is there a way, in the above example, to attach a class method to Bar from Foo after the module has already been included?
Here's an example for both, class and instance methods:
module Foo
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
end
end
class Bar
include Foo
end
module Foo
def do_something_instancey
puts "I'm an instance!"
end
module ClassMethods
def do_something_classy
puts "I'm a class!"
end
end
end
b = Bar.new
b.do_something_instancey
# => I'm an instance!
Bar.do_something_classy
# => I'm a class!
To add class methods to each class that has (already) included a specific module, you could traverse Ruby's ObjectSpace:
ObjectSpace.each_object(Class) do |klass|
if klass.include? Foo
klass.define_singleton_method(:do_something_classy) do
puts "I'm a class!"
end
end
end
Description of retroactive_module_inclusion gem:
This gem circumvents the "dynamic module include" (aka "double
inclusion") problem, which is the fact that M.module_eval { include N
} does not make the methods of module N available to modules and
classes which had included module M beforehand, only to the ones that
include it thereafter. This behaviour hurts the least surprise
principle, specially because if K is a class, then K.class_eval {
include M } does make all methods of M available to all classes
which had previously inherited it.

adding class specific functionality in ruby modules

possibly I'm not explaining the concept very well, but I'm looking to add class methods to a series of ruby classes to enable them to hold class specific information which will then be called by individual instance methods of the classes.
I can make it work, but it is a bit ugly. Can anyone as it requires 2 modules, one included and the other extended (see example code below).
Can anyone think of a more elegant way of implementing this functionality ?
Thanks
Steve
This module is extended to give class methods but adding an instance member to each class it is included in
module My1
def my_methods (*sym_array)
#my_methods=sym_array
end
def method_list
#my_methods
end
end
This module is included to give instance methods
module My2
def foo
self.class.method_list.each { |m| self.send m }
end
end
Now use the modules - the ugliness is having to use an include and extend statement to allow me to pass a set of symbols to a class method which will then be implemented in an
instance
class Foo
extend My1
include My2
my_methods :baz
def baz
puts "Baz!"
end
end
class Bar
extend My1
include My2
my_methods :frodo
def frodo
puts "Frodo!"
end
end
class Wibble < Bar
extend My1
include My2
my_methods :wobble
def wobble
puts "Wobble!"
end
end
Here is the required output - note that each class has its own instance #my_methods so the behaviour is different for the derived class Wibble < Bar
f=Foo.new
b=Bar.new
w=Wibble.new
f.foo #=> "Bar!"
b.foo #=> "Frodo!"
w.foo #=> "Wobble!"
When a module is included, a hook is called on it. You can use that to do the extend you want.
module M1
def self.included(base)
base.extend(M2)
end
end
People often call that second module M1::ClassMethods. If you're using rails, ActiveSupport::Concern encapsulates this pattern
I would suggest to use a hook from module instead:
module MyModule
def self.included(klass)
klass.extend ClassMethods
end
def foo
self.class.method_list.each{ |m| self.send m }
end
module ClassMethods
attr_reader :method_list
def my_methods(*sym_array)
#method_list = sym_array
end
end
end
So it simplifies to call include only a module whenever you want the functionality to given classes.
class Foo
include MyModule
my_methods :baz
def baz
puts "Baz!"
end
end

Resources