为什么ruby on rails migration语法如下所示:
create_table :my_table do |t|
t.integer :col
t.integer :col2
t.integer :col3
end
而不是:
create_table :my_table do
integer :col
integer :col2
integer :col3
end
就个人而言,我发现第二个片段更具可读性,是否有任何理由为什么实现使用第一个?
答案 0 :(得分:3)
这两种方法的基本实施是不同的。
在第一个(实际)案例中,create_table
使用yield
对象调用TableDefinition
。因此,示例块中的t
指向TableDefinition
。另一种方法是使用instance_eval。这看起来像是:
def create_table(name, &block)
table_definition = TableDefinition.new
# Other setup
table_definition.instance_eval(&block)
# More work
end
你采用哪种方式部分取决于偏好。然而,有些人不是eval的粉丝所以他们喜欢避免这种情况。此外,使用yield
方法可以更清楚地了解您正在使用的对象。
答案 1 :(得分:0)
我的理解是ruby是词法范围的,意味着“整数”必须引用在代码中出现的点定义的东西。你需要动态范围来完成你所要求的。
可能是我错了,并且至少有一个procs,blocks和lambdas是动态范围的,但是你仍然有你的答案 - 范围行为的模糊细节并不是一个好东西,期望程序员知道。
答案 2 :(得分:0)
基本上,界面设计者应该选择这样做,因为这个关于范围的小技巧以及eval
和instance_eval
的工作方式,请查看此示例:
有两个类Foo
和Boo
具有以下定义:
class Foo
def speak(phrase)
puts phrase
end
def self.generate(&block)
f = Foo.new
f.instance_eval(&block)
end
end
class Boo
attr_reader :name
def initialize(name) ; @name = name ; end
def express
Foo.generate { speak name}
end
end
一般情况下,这应该适用于大多数情况,但是某些情况如下面的语句会发出错误:
Boo.new("someone").express #`express': undefined local variable or method `name' for #<Foo:0xb7f582fc> (NameError)
我们无权访问Boo
个Foo
个实例中的实例方法,因为我们使用的是instance_eval
,因此方法{{为name
个实例定义的1}}不在Boo
个实例的范围内。
为了克服这些问题,最好按以下方式重新定义生成:
Foo
这是一个灵活的界面,您可以根据传递的块参数来评估代码块。现在我们必须在当前需要调用实例方法时将当前foo对象作为参数传递,让我们重新定义class Foo
def speak(phrase)
puts phrase
end
def self.generate(&block)
f = Foo.new
block.arity < 1 ? f.instance_eval(&block) : block.call(f)
end
end
,检查Boo
和express
:
talk