mixing in modules that depend on each other? - ruby

Module A relies on module B, and class C relies on modules A and B. If I include A and B into C, this works, but I do not like the fact that A has a dependency on another module that must be mixed in along with it for it to work.
What is the "right" way to mix in modules in this situation? Should A mix in B itself? What if C also uses B directly?
module B
def g
12
end
end
module A
def f
2 * g
end
end
class C
include A, B
def h
3 * f
end
end

You can override Module#included
module A
def self.included(base)
base.class_eval do
include B
end
end
end
When this is done, B is included right after A.

Nothing is wrong with writing include redundantly. If A and C both rely on B, then include B in both A and C.
module B
def g; 12 end
def i; 7 end
end
module A
include B
def f; 2 * g end
end
class C
include A, B
def h; 3 * f * i end
end
If C does no rely on B, then include B only in A.
module B
def g; 12 end
end
module A
include B
def f; 2 * g end
end
class C
include A
def h; 3 * f end
end

Since in your case, you have no overriding methods with the same names, whatever way you mix the mixins in, the instance methods will work together as intended. You just need to make sure that they are really mixed in:
[ A, B ].all? { |m| C.kind_of? m } # must be true
So, in your case, it does not matter whether you do this
class C; include A, B; end
or this
class C; include B, A; end
or this
module B; include A end
class C; include B end
or this
module A; include B end
class C; include A end
One caveat to remind you of is, that this will not work:
class C; include A; end # including the module A first
module A; include B end # and trying to remix it after
c = C.new # does not work
c.h # raises NoMethodError
# Modules have to have finished mixing when they are included.
That's all!

Module A has a method that relies on functionality found in module B. [...]. What is the "right" way to mix in modules in this situation? Should A mix in B itself? What if C also uses B directly?
You've got few choice. Each of which should be chosen depending on the relationship between A, B and C.
The first one is to declare a module D that will define the shared functionality between A and B. In this way you can easily mix D with both A and B without worrying too much:
module D; ...; end
module A; include D; ...; end
module B; include D; ...; end
The other one that I can think of is to transform A and B into classes in order to use inheritance. Inheritance is good when having two classes that has a strong dependence relationship. If the functionality used by A is strong enough you should definitely choose this one:
class B; ...; end
class A < B; ...; end
class C < A; ...; end

I dont see anything wrong with just including B inside A if A depends on B and your modules only define instance methods. If you need class methods, you might want to consider http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
module B
def method_b
"B instance method"
end
end
module A
include B
def method_a
"A instance method is using #{method_b}"
end
end
class Klass
include A
end
k = Klass.new
puts k.method_a #=> A instance method is using B instance method
puts k.method_b #=> B instance method
puts k.class.ancestors.inspect #=> [Klass, A, B, Object, Kernel, BasicObject]

Related

Dynamically define a super method for an instance of a class in Ruby

Say we have a class that we cannot change,
class C
def foo
super
puts "Low!"
end
end
We'll need to dynamically define the method foo in something that we'll be able to inject into C's ancestry chain. The behavior of super must be specific to a given object, not class-wide. We'll be able to enclose that logic into an anonymous module (let's name it for now):
module M
def foo
puts "High!"
end
end
Extending an instance of C with the module:
c = C.new
c.extend(M)
c.foo
# High!
will not work since we've put the method from the module before the method we've defined in the class. Looking at our object's ancestors
c.singleton_class.ancestors
# => [#<Class:#<C:0x00005652be630b20>>, M, C, ...]
I came up with an ugly workaround, which is redefining the methods from our class in our singleton_class, i.e.
c.define_singleton_method(:foo, c.class.instance_method(:foo))
c.foo
# High!
# Low!
While this works (does it work? I've tested it for a bit and it seems to, but I'm no longer certain), I wonder whether I'm missing something obvious and there's an easier way to dynamically define a "super" method for an instance of a class.
To be clear, we want to be able to extend another instance of C with another module, i.e.
C.new.extend(Module.new do
def foo
puts "Medium!"
end
end).foo
# Medium!
# Low!
and have its output not tainted by other instances.
Now that I understand you're trying to work around an issue in some third-party code, I can suggest a more reasonable solution. In the code below, I'm thinking that B and C are defined by the gem and you don't want to change their source code, but you want to inject some code into the place where C#foo calls B#foo.
class B
def foo
puts "Highest!"
end
end
class C < B
def foo
super
puts "Low!"
end
end
module CE
def foo
super
foo_injected
end
end
C.include(CE)
module M
def foo_injected
puts "High!"
end
end
c = C.new
c.extend(M)
p c.singleton_class.ancestors
c.foo
The output is:
[#<Class:#<C:0x000055ce443366a8>>, M, C, CE, B, Object, Kernel, BasicObject]
Highest!
High!
Low!

How to access the return of Module.nesting outside of the module definition

When using the Module.nesting method, I can return a list of nested Modules from the point of call. The only examples I saw in documentation and elsewhere show the method call being placed and run from within the nested module definition:
module M1
module M2
Module.nesting #=> [M1::M2, M1]
end
end
In the following example:
module A
module B; end
module C
module D
# I want to access the name-space chain from this point
end
end
end
# But how can I reference it from our here?
# A::C::D .... ?
is there any way to check the name-space chain within the module once it has been defined?
The only examples I can see create a variable within the definition to hold the resulting call to be referenced again outside of the definition:
module A
module B; end
module C
module D
$chain = Module.nesting
end
end
end
p $chain
#=> [A::C::D, A::C, A]
Is there a method to looking this name-spacing chain up, without creating a variable in the definition to be referenced later?
In Ruby, the way to "remember" the value of an expression is to assign that value to a variable.
Also, the value of a module definition is the value of the last expression that was evaluated inside the body.
If you put the two together, you get something like this:
chain = module A
module B; end
module C
module D
Module.nesting
end
end
end
p chain
# [A::C::D, A::C, A]
Consider the following nested modules mentioned in the question.
module A
module B; end
module C
module D
Module.nesting
end
end
end
#=> [A::C::D, A::C, A]
Now try this:
module A::C::D
Module.nesting
end
#=> [A::C::D]
Module::nesting resolves the nesting of modules lexicographically, meaning that modules are ordered by their point of inclusion, beginning at A::C::D and working outward. Module::nesting does not "remember" the nesting from where the module was defined; it merely examines the code locally. This is in contrast to subclassing.
Perhaps you can obtain the information you need by using the method ObjectSpace::each_method.
outer_module = A
ObjectSpace.each_object(Module).select { |m| m.to_s.match?(/\A#{outer_module}::/) }
#=> [A::B, A::C::D, A::C]

Sharing variables between Ruby classes within same module

I have module
# File 1.rb
module Search
class A
attr_accessor :results
def find_results
self.results = [somethings]
end
def do_something_with_results
b = B.new.do_something
b.response #=> something
c = C.new.use_b_response_do_something
return c.did_something
end
end
end
# File 2.rb
module Search
class B
end
class C
end
end
I have module Search with classes A, B, C
Class A does something and brings back data which needs to be shared with Classes B and C to do something with that data (refine it, send it somewhere, return true or false).
How can I share this information between the classes? I have been doing this which I think is wrong
def do_something
b = B.new.doing_something
c = C.new.something_else(b.some_attr)
end
which is not efficient
I don't see any issues in your code. You seem to be doing things as it should be done. Using methods to get and pass data around.
One thing that could make your code better is passing b to c instead.
def do_something
b = B.new.doing_something
c = C.new(b).something_else
end
But it depends on your use case...

How to trace the singleton class and m_tbl in Ruby

I started to touch the meta-programming in Ruby
If I want to trace all the details in meta-programming
Like to lookup m_tbl methods_table in a specific object,
I mean if there is a test method and defined in class B
A < B
B < C
What's the convenient way to know the method is defined in class B
Any good methods or tools to share about discovery the meta-programing in Ruby.
To look up all the relation between object's hierachy relation in quick way.
Thanks !
Use method method:
class C
def c
end
end
class B < C
def b
end
end
class A < B
def a
end
end
a = A.new
a.method(:b).owner
# => B

Ruby Static Inheritance

I am trying to use a class variable in ruby. But class variables change throughout the entire hierarchy, and thus are useless for this goal:
Assume I have 3 classes, each inherited, except the parent.
class A
end
class B < A
end
class C < B
end
How would I modify or create a static variable in the middle class so that class A does not have it but class C does.
B.num = 2
A.num # undefined or nil
C.num # 2
I should also specify that A.num should still be able to be used, without changing B.num or C.num, unless its inherited.
Edited since the OP changed the question
Use a class instance variable for A and B.
class A
singleton_class.class_eval{attr_accessor :num}
end
class B < A
singleton_class.class_eval{attr_accessor :num}
end
class C < B
def self.num; superclass.num end
def self.num= v; superclass.num = v end
end
B.num = 2
A.num # => nil
C.num # => 2

Resources