I am trying some ruby metaprogramming and got some confusion with instance_eval().
see below examples
#instance_var = 'instance_var'
local_var = 'local_var'
obj = Object.new
obj.instance_eval { p #instance_var; p local_var }
obj.instance_eval { #instance_var = 'instance_var_in_obj'; local_var = 'local_var_in_obj' }
p #instance_var; p local_var
I expect both of #instance_var and local_var can be pass/modify in block but i got
nil
"local_var"
"instance_var"
"local_var_in_obj"
as result we can share(pass/modify) local vars in instance_val but instance vars are belong to self CAN NOT share.
and about instance_exec:
obj.instance_exec(#instance_var) {|instance_var| p instance_var; instance_var = #instance_var }
=> "instance_var"
#instance_var
=> "instance_var"
now i can pass my outer instance var and still CAN NOT modify it.
#instance_arr = []
obj.instance_exec(#instance_arr) {|instance_arr| instance_arr << 'in_block' }
#instance_arr
=> ["in_block"]
obj.instance_exec(#instance_arr) {|instance_arr| instance_arr = [] }
#instance_arr
=> ["in_block"]
with a instance var of array i can modify my instance var but ONLY within current array object
in summary play instance_eval or instance_exec with local vars not instance vars?
is there some concepts i missed?
After some search and advices from my friend i think i figured out the problem.
In ruby there is two Context when your code running self and binding, when you work with local vars or method without set self.xxx first thing will be checking is it in your binding object as a local var if not Ruby will think it's a method then search on your self object to find its definition and invoke it.
Think this:
class A
def test
4
end
def use_variable
test = 5
test
end
def use_method
test = 5
self.test
end
end
a = A.new
a.use_variable # returns 5
a.use_method # returns 4
That's explained WHY of instance_eval as its document said instance_eval just changed self in the given block and NOT touch binding so methods will be search on new self, local vals still in same binding object.
About instance_exec i'm not very sure about this but seems like instance vars(with at prefix vars) it will be search on self directly skip on binding, so out of instance_exec your #instance_arr belongs to old self and in instance_exec block you got it as a new local var in the new binding of block(block has own scope) but the value of it actually is the reference of #instance_arr so invoke method on the new local var such like push it will change both of them because they share same Array instance, but when you assign a new Array instance to the new local var they are no longer refer same Array instance that's the second WHY.
In order to evaluate the local variable, you would need to pass in the string `"local_var" and it will return the value of the local variable. If you pass in a block, then an argument can not be passed in, according to my interpretation of the documentation.
The behavior of instance eval in the block form is to access as a closure the instance variables and private methods of the object where that call is.
The behavior of instance eval with an argument allows you to evaluate a string in the scope of that call.
Related
I'm trying to get a better understanding of the instance variable usage outside of standard class declarations. This may be an odd example, but it's because the # symbol is used outside of a class declaration and I have yet to see that.
require "mysql"
#db_host = "localhost"
#db_user = "root"
#db_pass = "root"
#db_name = "your_db_name"
client = Mysql::Client.new(:host => #db_host, :username => #db_user,
:password => #db_pass, :database => #db_name)
#cdr_result = client.query("SELECT * from your_db_table_name')
In ruby, everything is a object. For instance, in your code you are actually in the main object.
In your file, if you do puts self.class, you will see you are under main object, and the class being Object.
In pratical terms, using # or not, will take no difference. If you declare a class #myvar you will be within in the entire main object as a instance variable. Your variable will be visible in any method of that file.
def set_country
#myvar = 'Brazil'
end
def get_country
puts #myvar
# #myvar is visible here
end
------------------
def set_country
myvar = 'Brazil'
end
def get_country
puts myvar
# cannot access 'Brazil' value
end
The initial execution context of a Ruby program is an object, that is an instance of the Object class. If you attempt to query the object with inspect or p, it will simply return "main" as is mentioned here
Setting values such as #blah will create instance variables on this top level object.
The following short snippet should demonstrate this.
Inspecting the context simply returns main, it's an Object, more specifically an instance of theObject class.
The initial list of instance variables is an empty array, but after you set the some using #blah, you can see they have been added to the list of instance variables.
In the example you posted, there is actually no need to use instance variables, local variables would have been fine.
p self
puts self.class
puts instance_of? Object
puts "instance variables = #{self.instance_variables}"
#db_host = "localhost"
#db_user = "root"
#db_pass = "root"
#db_name = "your_db_name"
puts "instance variables = #{self.instance_variables}"
main
Object
true
instance variables = []
instance variables = [:#db_host, :#db_user, :#db_pass, :#db_name]
def gen_times(factor) do
return Proc.new {|n| n*factor}
end
gen_times.class # ArgumentError 0 for 1
gen_times(3).class # Proc
gen_times = 2
gen_times.class # Fixnum
times3 = gen_times(3) # A normal, working Proc
The first gen_times.class gives an ArgumentError, so I assume it returns the class name of gen_times's return value, which is confirmed in the next line.
But then, I assign gen_times, and it becomes a Fixnum. However, I can still use gen_times to return Procs.
I recall that Fixnum objects have immediate values, and that the object itself is used in assignment, rather than a reference to it.
So, is it right to say that gen_times is a Fixnum object that refers to a method?
In ruby you can have local variables and methods with the same name. This has some complications for example with setter methods in classes:
class Test
def active
#active
end
def active=(value)
#active = value
end
def make_active
active = true
end
end
t1 = Test.new
t1.active = true
t1.active #=> true
t2 = Test.new
t2.make_active
t2.active #=> nil
Code for t1 object will return expected result, but code for t2 returns nil, because make_active method is actually creating local variable and not calling active= method. You need to write self.active = true to make this work.
When you write gen_class, ruby tries to access local variable, if it is not defined ruby tries to call method. You can call your method explicit by writing gen_class().
Looking at this instance_eval example:
class KlassWithSecret
def initialize
#secret = 99
end
def get
#secret
end
end
k = KlassWithSecret.new
k.instance_eval { #secret }
print k.get
I added a get method to KlassWithSecret.
Here's the results of running the program:
>ruby InstanceEvalTest.rb
99
So, does instance_eval here somehow call the initialize method?
I think that I understand this method a bit from reading this helpful post. But I'm still in the dark.
The initialize method is automatically called by Ruby after the new method is called. instance_eval runs the block you supply in the context of the object. This means it has access to anything a normal line of code in the KlassWithSecret class would have.
#secret is an instance variable, meaning that it belongs to an instance of KlassWithSecret. Because we're evaluating { #secret } in the context of a KlassWithSecret instance, we can access #secret.
k.instance_eval gives you access to all of the instance variables (here just #secret) and all private methods (if there were any). It executes the code in the block, which in this case returns 99, the value of #secret. Then print k.get prints that value and returns nil.
If the block had been { #secret = 'cat' }, k.instance_val would have changed the value of #secret (and returned the new value).
When using instance_eval, class_eval, class < self and other metaprogramming constructs, you mind find it helpful to track the value of self using puts statements. For example:
k = KlassWithSecret.new #=> #<KlassWithSecret:0x00000101897810 #secret=99>
self #=> main
k.instance_eval { puts "self=#{self}"; #secret }
"self=#<KlassWithSecret:0x00000101897810>"
#=> 99
In my code when trying to print p #stack outside the class,and it shows nil despite putting attr_accessor :stacks, :each_stack_size, :stack_number. As usual it works fine and shows data when using inside the class. What am i missing?
class SetOfStacks
attr_accessor :stacks, :each_stack_size, :stack_number
# Initializing Hash to hold all the stacks and initial stack is created.
def initialize
#stacks = Hash.new # declaring hash to hold all stacks
#each_stack_size = 3 # defining each stack size
#stack_number = 1 # Current stack number
#stacks[#stack_number] = Array.new
end
...
...
end
#obj = SetOfStacks.new
#obj.push(4)
#obj.push(5)
#obj.push(6)
#obj.push(7)
#obj.pop
p #stacks
In order to print an instance variable, you must access it with the context of the instance.
p #obj.stacks
Getter and Setter Methods
You're misunderstanding what Module#attr_accessor does. Essentially, it creates getter and setter methods for your instance variables. In your case, attr_accessor :stacks creates the SetOfStacks#stacks and SetofStacks#= methods. Note that these are still instance methods of your object, not global variables, so you still have to invoke them properly. For example:
#obj = SetOfStacks.new
#obj.stacks # invoke your getter
#obj.stacks = { foo: nil } # invoke your setter
Forgive me, guys. I am at best a novice when it comes to Ruby. I'm just curious to know the explanation for what seems like pretty odd behavior to me.
I'm using the Savon library to interact with a SOAP service in my Ruby app. What I noticed is that the following code (in a class I've written to handle this interaction) seems to pass empty values where I expect the values of member fields to go:
create_session_response = client.request "createSession" do
soap.body = {
:user => #user, # This ends up being empty in the SOAP request,
:pass => #pass # as does this.
}
end
This is despite the fact that both #user and #pass have been initialized as non-empty strings.
When I change the code to use locals instead, it works the way I expect:
user = #user
pass = #pass
create_session_response = client.request "createSession" do
soap.body = {
:user => user, # Now this has the value I expect in the SOAP request,
:pass => pass # and this does too.
}
end
I'm guessing this strange (to me) behavior must have something to do with the fact that I'm inside a block; but really, I have no clue. Could someone enlighten me on this one?
First off, #user is not a "private variable" in Ruby; it is an instance variable. Instance variables are available within the the scope of the current object (what self refers to). I have edited the title of your question to more accurately reflect your question.
A block is like a function, a set of code to be executed at a later date. Often that block will be executed in the scope where the block was defined, but it is also possible to evaluate the block in another context:
class Foo
def initialize( bar )
# Save the value as an instance variable
#bar = bar
end
def unchanged1
yield if block_given? # call the block with its original scope
end
def unchanged2( &block )
block.call # another way to do it
end
def changeself( &block )
# run the block in the scope of self
self.instance_eval &block
end
end
#bar = 17
f = Foo.new( 42 )
f.unchanged1{ p #bar } #=> 17
f.unchanged2{ p #bar } #=> 17
f.changeself{ p #bar } #=> 42
So either you are defining the block outside the scope where #user is set, or else the implementation of client.request causes the block to be evaluated in another scope later on. You could find out by writing:
client.request("createSession"){ p [self.class,self] }
to gain some insight into what sort of object is the current self in your block.
The reason they "disappear" in your case—instead of throwing an error—is that Ruby permissively allows you to ask for the value of any instance variable, even if the value has never been set for the current object. If the variable has never been set, you'll just get back nil (and a warning, if you have them enabled):
$ ruby -e "p #foo"
nil
$ ruby -we "p #foo"
-e:1: warning: instance variable #foo not initialized
nil
As you found, blocks are also closures. This means that when they run they have access to local variables defined in the same scope as the block is defined. This is why your second set of code worked as desired. Closures are one excellent way to latch onto a value for use later on, for example in a callback.
Continuing the code example above, you can see that the local variable is available regardless of the scope in which the block is evaluated, and takes precedence over same-named methods in that scope (unless you provide an explicit receiver):
class Foo
def x
123
end
end
x = 99
f.changeself{ p x } #=> 99
f.unchanged1{ p x } #=> 99
f.changeself{ p self.x } #=> 123
f.unchanged1{ p self.x } #=> Error: undefined method `x' for main:Object
From the documentation:
Savon::Client.new accepts a block inside which you can access local variables and even public methods from your own class, but instance variables won’t work. If you want to know why that is, I’d recommend reading about instance_eval with delegation.
Possibly not as well documented when this question was asked.
In the first case, self evaluates to client.request('createSession'), which doesn't have these instance variables.
In the second, the variables are brought into the block as part of the closure.
Another way to fix the issue would be to carry a reference to your object into the block rather than enumerating each needed attribute more than once:
o = self
create_session_response = client.request "createSession" do
soap.body = {
:user => o.user,
:pass => o.pass
}
end
But now you need attribute accessors.