class X
end
class A
def get_it
puts #the_variable
end
end
class B
def init_it
#the_variable = X.new
a = A.new
end
end
In the above code I want methods of Class A to access the instance of X instantiated in B
Try using Object#instance_variable_set:
class B
def init_it
#the_variable = X.new
a = A.new
a.instance_variable_set(:#the_variable, #the_variable)
end
end
Related
I want to execute a method of instance ofA on instance of B, where A and B are not related, independent classes.
a = A.new
b = B.new
b.<method_of_a>
Insane, senseless way:
class A
def a_method
'I am instance of A'
end
end
class B
def method_missing(method_name)
if method_name.to_s =~ /a_method/
A.instance_method(method_name).bind(self).call
else
super
end
end
end
B.new.a_method
#=> "I am instance of A"
Sane, idiomatic way:
module CommonMethods
def common_method
'I am available for all includers'
end
end
class A
include CommonMethods
end
class B
include CommonMethods
end
a = A.new
b = B.new
a.common_method
#=> "I am available for all includers"
b.common_method
#=> "I am available for all includers"
class A
def yay!
puts "¡YAY!"
end
end
b = A.new
A.instance_method(:yay!).bind(b).()
#⇒ "¡YAY!"
For example, how could I implement the following:
class A
def initialize
b = B.new
end
end
class B
def initialize
puts #how can I find out who instantiated me
end
end
class A
def initialize
B.new(self)
end
end
class B
def initialize initializer
puts initializer
end
end
A.new # => #<A:0x007fee78368dc8>
I want to call instance_eval on this class:
class A
attr_reader :att
end
passing this method b:
class B
def b(*args)
att
end
end
but this is happening:
a = A.new
bb = B.new
a.instance_eval(&bb.method(:b)) # NameError: undefined local variable or method `att' for #<B:0x007fb39ad0d568>
When b is a block it works, but b as a method isn't working. How can I make it work?
It's not clear exactly what you goal is. You can easily share methods between classes by defining them in a module and including the module in each class
module ABCommon
def a
'a'
end
end
class A
include ABCommon
end
Anything = Hash
class B < Anything
include ABCommon
def b(*args)
a
end
def run
puts b
end
end
This answer does not use a real method as asked, but I didn't need to return a Proc or change A. This is a DSL, def_b should have a meaningful name to the domain, like configure, and it is more likely to be defined in a module or base class.
class B
class << self
def def_b(&block)
(#b_blocks ||= []) << block
end
def run
return if #b_blocks.nil?
a = A.new
#b_blocks.each { |block| a.instance_eval(&block) }
end
end
def_b do
a
end
end
And it accepts multiple definitions. It could be made accept only a single definition like this:
class B
class << self
def def_b(&block)
raise "b defined twice!" unless #b_block.nil?
#b_block = block
end
def run
A.new.instance_eval(&#b_block) unless #b_block.nil?
end
end
def_b do
a
end
end
I update this question to better reflect what I have problems to grasp. The example below kind of work but how can I access the Sub class then I have defined it inside the Base class? Should it not be better to do the call outside the class? If so how do I do that? The second question I have in this example is how to grab values so I can use them in another class. Here I store the values in an array that I later need to unpack in another class. Should I not be able to use a proc for this?
Basically what I want to do is to sort the methods into two different classes depending on if they are nested or not.
class Sub
def initialize(base_class_method)
#base_class_method = base_class_method
#sub_methods = []
end
# omitted code here
def base_class_method
#base_class_method
end
def sub_actions(method)
#sub_methods << method
end
def return_sub_methods
#sub_methods
end
def method_missing(sub_method, &block)
if sub_method
sub_method
else
super
end
end
end
class Base
def initialize
#base_methods = []
end
# omitted code here
def base_actions(method)
#base_methods << method
end
def return_base_methods
#base_methods
end
def method_missing(method, &block)
if block_given?
Sub.new(method).instance_eval(&block)
elsif method
base_actions(method)
else
super
end
end
end
base = Base.new
base.instance_eval do
something1
something_with_a_block do
something_inside_block1_1
something_inside_block1_2
end
something2
something_with_a_block2_2 do
something_inside_block2_1
end
end
p base.return_base_methods #=> [:something1, :something2] works!
You can do something like this.
class Test
# reserved method to instantiate object
def initialize(a,b,c)
#a = a
#b = b
#c = c
end
# getters
def a
#a
end
def b
#b
end
def c
#c
end
def abc
[#a, #b, #c] # returns an array
end
# setters
def a=(var)
#a = var
end
def b=(var)
#b = var
end
def c=(var)
#c = var
end
# set values all at once
def update(a, b, c)
#a = a
#b = b
#c = c
end
end
z = Test.new('something','something','something')
z.update('something!','nothing!',"a thing!")
z.a
z.b
z.c
z.a = 'wow, new value!'
In Ruby, when defining the contents of a class with class_exec, I am getting unexpected results. When I define a class variable in the block sent to class_exec, the class variable is being defined on Object instead of the class on which class_exec is being called:
class X; end
X.class_exec do
##inner_value = "123"
def inner_value
##inner_value
end
def inner_value=(arg)
##inner_value = arg
end
end
obj1 = X.new
puts obj1.inner_value
puts ##inner_value
puts Object.class_variables
Produces:
123
123
##inner_value
This does not happen when using class_eval:
X.class_eval(<<-RUBY)
##inner_value = "123"
def inner_value
##inner_value
end
def inner_value=(arg)
##inner_value = arg
end
RUBY
obj1 = X.new
puts obj1.inner_value
puts ##inner_value
puts Object.class_variables
Produces:
123
and an error:
uninitialized class variable ##inner_value in Object (NameError)
The results with class_eval are what I would expect to happen in both cases. I have tried this with both MRI 1.8.7 and MRI 1.9.3 and got the same results running on Windows XP.
Is this expected behavior? If so, why? If not, bug?
class variables are bound to the class in which they are declared at compile time. The block passed to class_exec is compiled before it is passed to class_exec, so the class variables are bound to Object.
I guess your class_exec is at the top level, which is in Object, so that's where they go. To demonstrate:
public
class Object
##x = "ribbit"
end
def foo
puts "test: #{##x}"
end
x = Object.new
x.foo
This is why when you use class vars in a module, all classes that include that module (through the included methods) will see the same class variables. The class variables are bound to the module. If you run this:
class WithClassVars
def self.classvars
#classvars ||= {}
end
def classvars
self.class.classvars
end
end
class A < WithClassVars;end
class B < WithClassVars;end
a = A.new
b = B.new
a.classvars[:a] = 1
b.classvars[:a] = 2
puts a.classvars
puts b.classvars
a and b will end up with the same data.
If you pass your code as a string to class_eval, the string is compiled in class_eval, so you can make sure they are in the right class then.
So if you want to store per-class data, you have to either go with class_eval, or use some mechanism to use a class's instance variables. Say:
class WithClassVars
def self.classvars
#classvars ||= {}
end
def classvars
self.class.classvars
end
end
class A < WithClassVars;end
class B < WithClassVars;end
a = A.new
b = B.new
a.classvars[:a] = 1
b.classvars[:a] = 2
puts a.classvars
puts b.classvars