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
.