Sharing variables between Ruby classes within same module - ruby

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...

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 allow only one class to access a method from another class?

I would like to have something like this:
class A
def only_B_can_call_me
'called_by_B'
end
end
class B
def do_stuff(a)
a.only_B_can_call_me
end
end
class C
def do_stuff(a)
a.only_B_can_call_me # how to forbid it?
end
end
B.new.do_stuff(A.new) # => 'called_by_B'
C.new.do_stuff(A.new) # => it should not be allowed!!! but how to do it?
One way to do this is to make only_B_can_call_me a private method and use a.send(:only_B_can_call_me) inside B. OK, it works. But I could be tempted in doing the same thing inside C... so, I think it is not a good approach. Is there any other way to do this? (Allow a method be accessed just by instances of a specific class.)
(I know that ultimately it is always possible to access any method from anywhere using send. But I want to keep myself away from send in this case.)
There are no clear solutions. If B can do it, so can C. Unlike some other languages, ruby doesn't have "internal" or "package" visibility modifier, which could help you if A and B were in the same "package", but C was external. If the method is private, even B has to use send. If it's public, C can just call it. B is not a subclass of A in your example, so protected modifier doesn't apply.
One dirty-dirty way would be to check caller in only_B_can_call_me. It returns the entire callstack. So you can check if it is, indeed, B or reject otherwise. But this is super-fragile and totally not recommended.
This is the best I could do with help of #Sergio Tulentsev:
class A
def only_B_can_call_me(b)
return unless b.class == B # here you are asking
'called_by_B'
end
end
class B
def do_stuff(a)
a.only_B_can_call_me(self) # how to forbid it? ask if self is B
end
end
class C
def do_stuff(a)
a.only_B_can_call_me(self) # how to forbid it? ask if self is B
end
end
Other way is using subclass:
class A
def only_B_can_call_me
'called_by_B'
end
end
class B < A
def do_stuff
self.only_B_can_call_me
end
end
class C
def do_stuff
self.only_B_can_call_me # how to forbid it?, like this C hasn't the method, B does
end
end
puts(B.new.do_stuff) # => 'called_by_B'
puts(C.new.do_stuff) # => it should not be allowed!!! but how to do it?
Sergio's answer is correct, however if you really need a hack take a look at the code below:
class CallerClass
def self.get (c)
line = c.first.scan(/^.*:(.*):.*/).first.first.to_i
file = c.first.scan(/^(.*?):/).first.first
func = c.first.scan(/:in `(.*)?'/).first.first
fc = File.readlines(file)
caller_class = nil
caller_class = '<main>' if func == '<main>'
line.downto(0) do |it|
break if caller_class
caller_class = fc[it].scan(/^\s*class\s+(.*)\s+/).first.first if fc[it] =~ /^\s*class\s+(.*)\s+/
end
caller_class
end
end
class A
def only_B_can_call_me
caller_class = CallerClass.get(caller)
raise "'#{caller_class}' is not an allowed caller" unless caller_class == 'B'
'called_by_B'
end
end
class B
def do_stuff
A.new.only_B_can_call_me
end
end
class C
def do_stuff
A.new.only_B_can_call_me
end
end
B.new.do_stuff #called_by_B
C.new.do_stuff #Raises exception
OBS. This is a fragile parsing of ruby code using regex, It's a HACK you've been warned!!

Get a reference to an object while instantiating it

I'd like to reference an object while instantiating it in order to pass it to another object I'm instantiating. What I mean:
A.new(B.new(self))
In this case, self would refer to the scope in which I'm actually calling A.new. What I want is for self (or whatever other keyword) to refer to the newly instantiated A object, so that B would have a reference to A. Is there a way to do this?
The way you have written it (A.new(B.new(self))) is impossible, due to a circular reference.
In order to create an instance of A, you need an instance of B; in order to create the instance of B, you need the instance of A.
There are a few ways you tweak the implementation to make this possible, but you must first resolve this chicken-and-egg problem between the A and B. For example:
class A
def initialize
#b = yield(self)
end
end
class B
def initialize(a)
#a = a
end
end
A.new { |a| B.new(a) }
Note that in the above code, a is being initialized first. It is only being yielded in the scope after the object has been created.
Or, here's another way:
class A
def initialize
#b = B.new(self)
end
end
class B
def initialize(a)
#a = a
end
end
A.new
Like above, the instance of A is being created first. But this time, I've done all the initialization in one go rather than building it within the new() methed call.
One final example:
class A
attr_writer :b
def initialize
end
end
class B
def initialize(a)
#a = a
end
end
A.new.tap { |a| a.b = B.new(a) }
In this example, I have fully initialized a before defining its attribute of b. This could just as easily have been written in two lines of code, with a regular variable instead of the closure:
a = A.new
a.b = B.new(a)

Making functions available as instance methods - Ruby

module Example
def a (parameter)
#do something
end
def b (parameter_2)
#do something
end
end
class C
include Example
end
This is a sample Ruby code. I wish to know how to make functions a and b available to class C as instance methods. These will be tested automatically by another program.
They already are.
c = C.new
c.methods - 0.methods
# => [:a, :b]
By virtue of including the module, you've extended the functionality of C to include that of the module Example.

mixing in modules that depend on each other?

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]

Resources