为什么迁移需要表块参数?

发布于 2024-08-07 06:21:41 字数 334 浏览 5 评论 0原文

为什么 ruby​​ on Rails 迁移语法看起来像这样:

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

我个人而言,我发现第二个片段更具可读性,该实现使用第一个片段是否有任何原因?

Why does the ruby on rails migration syntax look like this:

create_table :my_table do |t|
     t.integer :col
     t.integer :col2
     t.integer :col3
end

And not:

create_table :my_table do
     integer :col
     integer :col2
     integer :col3
end

Personally I find the second snippet much more readable, are there any reasons why the implementation uses the first?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

装迷糊 2024-08-14 06:21:41

两种方法的根本实现是不同的。
在第一种(也是实际的)情况下,create_table 使用 TableDefinition 对象调用 yield。因此,示例块中的 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 方法可以更清楚地显示您正在使用的对象。

The fundamental implementation of the two approaches is different.
In the first (and actual) case, create_table calls yield with a TableDefinition object. So t in your example block points to that TableDefinition. The alternative method is to use instance_eval. This would look something like:

def create_table(name, &block)
  table_definition = TableDefinition.new
  # Other setup
  table_definition.instance_eval(&block)
  # More work
end

Which way you do it is partially a matter of preference. However, some people are not fans of eval so they like to avoid this. Also, using the yield method makes it clearer what object you're working with.

[旋木] 2024-08-14 06:21:41

我的理解是 ruby​​ 是词法范围的,这意味着“整数”必须引用它在代码中出现时定义的东西。您需要动态范围来完成您所要求的。

也许我错了,过程、块和 lambda 中至少有一个是动态作用域的,但你仍然有答案——期望程序员知道作用域行为方式的模糊细节并不是一件好事。

My understanding is that ruby is lexically scoped, meaning that "integer" has to refer to something defined at the point it occurs in the code. You'd need dynamic scoping to accomplish what you're asking for.

It may be that I'm wrong and at least one of procs, blocks and lambdas is dynamically scoped, but then you still have your answer -- obscure details of how scope behaves is not a good thing to expect a programmer to know.

日裸衫吸 2024-08-14 06:21:41

基本上,界面设计者应该选择这样做,因为这个关于作用域的小技巧以及evalinstance_eval的工作方式,请检查这个示例:

有2个类Foo< /code> 和 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)

我们无法访问此处Foo 实例中 Boo 的实例方法,因为我们使用的是 instance_eval,所以方法 nameBoo 实例定义的内容不在 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

这是一个灵活的接口,您可以根据传递的块参数来评估代码块。现在,当我们需要调用当前的 foo 对象的实例方法时,我们必须将其作为参数传递,让我们重新定义 Boo,检查 expresstalk

class Boo
  attr_reader :name
  def initialize(name) ; @name = name ; end
  def express
    Foo.generate { |f| f.speak name}
  end
  def talk(anything)
    Foo.generate { speak anything}
  end
end

Boo.new("someone").express #=> someone
Boo.new("someone").talk("whatever") #=> whatever

Basically the interface designer should have chosen to do so due to this little trick regarding scopes and how eval and instance_eval work, check this example:

Having 2 classes Foo and Boo with the following definitions:

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

Generally this should work fine for most cases, but some situations like the following statement will issue an error:

Boo.new("someone").express #`express': undefined local variable or method `name' for #<Foo:0xb7f582fc> (NameError)

We don't have access here to the instance methods of Boo inside Foo instances that's because we are using instance_eval, so the method name which is defined for Boo instances is not in the scope for Foo instances.

To overcome such problems it’s better to redefine generate as follows:

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

This is a flexible interface where you evaluate the code block depending on the passed block params. Now we have to pass the current foo object as a param when we need to call instance methods on it, let's redefine Boo, check express and talk:

class Boo
  attr_reader :name
  def initialize(name) ; @name = name ; end
  def express
    Foo.generate { |f| f.speak name}
  end
  def talk(anything)
    Foo.generate { speak anything}
  end
end

Boo.new("someone").express #=> someone
Boo.new("someone").talk("whatever") #=> whatever
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文