Understanding instance exec in ruby
- 在procs中有上下文
square = lambda { x * x }
x = 20
puts square.call()
# => undefined local variable or method `x' for main:Object (NameError)
- 定義proc時(shí),需要保證里面的變量已經(jīng)定義
x = 2
square = lambda { x * x }
x = 20
puts square.call()
# => 400
上面得出結(jié)果是400垃环,而不是4酪我,是因?yàn)閜roc定義時(shí)只是binding了上下文的變量x夫植,并不是x的值。
- 可以是方法
square = lambda { x * x }
def x
20
end
puts square.call()
# => 400
binding is smart enough to figure out that since no x variable is present颅眶,這里方法可以后定義蜈出。
- lexical binding in procs
def square(p)
x = 2
puts p.call
end
x = 20
square(lambda { x * x })
#=> 400
In the above case the value of x is set as 20 at the code compile time. Don’t get fooled by x being 2 inside the method call. Inside the method call a new scope starts and the x inside the method is not the same x as outside .
proc只跟當(dāng)前上下文的變量綁定
- Issues because of lexical scoping
class Person
code = proc { puts self }
define_method :name do
code.call()
end
end
class Developer < Person
end
Person.new.name # => Person
Developer.new.name # => Person
In the above case when Developer.new.name is executed then output is Person. And that can cause problem. For example in Ruby on Rails at a number of places self is used to determine if the model that is being acted upon is STI or not. If the model is STI then for Developer the query will have an extra where clause like AND "people"."type" IN ('Developer') . So we need to find a solution so that self reports correctly for both Person and ‘Developer` .
- instance_eval can change self
class Person
code = proc { puts self }
define_method :name do
self.class.instance_eval &code
end
end
class Developer < Person
end
Person.new.name #=> Person
Developer.new.name #=> Developer
但是instance_eval不能接受參數(shù)
class Person
code = proc { |greetings| puts greetings; puts self }
define_method :name do
self.class.instance_eval 'Good morning', &code
end
end
class Developer < Person
end
Person.new.name
Developer.new.name
#=> wrong number of arguments (1 for 0) (ArgumentError)
- instance_exec to rescue
class Person
code = proc { |greetings| puts greetings; puts self }
define_method :name do
self.class.instance_exec 'Good morning', &code
end
end
class Developer < Person
end
Person.new.name #=> Good morning Person
Developer.new.name #=> Good morning Developer