Rails 3:如何编写 DRYer 范围

发布于 2024-11-06 15:36:50 字数 808 浏览 0 评论 0原文

我发现自己在两个地方编写非常相似的代码,一次是在模型上定义(虚拟)布尔属性,一次是定义范围以查找与该条件匹配的记录。本质上,

scope :something, where(some_complex_conditions)

def something?
  some_complex_conditions
end

一个简单的例子:我正在为俱乐部会员资格建模; 会员支付费用,该费用仅在特定年份有效。

class Member < ActiveRecord::Base
  has_many :payments
  has_many :fees, :through => :payments

  scope :current, joins(:fees).merge(Fee.current)

  def current?
    fees.current.exists?
  end
end

class Fee < ActiveRecord::Base
  has_many :payments
  has_many :members, :through => :payments

  scope :current, where(:year => Time.now.year)

  def current?
    year == Time.now.year
  end
end

是否有一种更干燥的方法来编写使用虚拟属性的范围(或者,确定模型是否与范围的条件匹配)?

我对 Rails 还很陌生,所以请指出我是否做了一些愚蠢的事情!

I'm finding myself writing very similar code in two places, once to define a (virtual) boolean attribute on a model, and once to define a scope to find records that match that condition. In essence,

scope :something, where(some_complex_conditions)

def something?
  some_complex_conditions
end

A simple example: I'm modelling a club membership; a Member pays a Fee, which is valid only in a certain year.

class Member < ActiveRecord::Base
  has_many :payments
  has_many :fees, :through => :payments

  scope :current, joins(:fees).merge(Fee.current)

  def current?
    fees.current.exists?
  end
end

class Fee < ActiveRecord::Base
  has_many :payments
  has_many :members, :through => :payments

  scope :current, where(:year => Time.now.year)

  def current?
    year == Time.now.year
  end
end

Is there a DRYer way to write a scopes that make use of virtual attributes (or, alternatively, to determine whether a model is matched by the conditions of a scope)?

I'm pretty new to Rails so please do point out if I'm doing something stupid!

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

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

发布评论

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

评论(3

凡间太子 2024-11-13 15:36:50

这不是问题的答案,但您的代码有一个错误(如果您在生产中使用类似的东西):Time.now.year 将返回服务器启动的年份。您希望在 lambda 中运行此范围以使其按预期运行。

scope :current, lambda { where(:year => Time.now.year) }

This in not an answer to the question, but your code has a bug (in case you use something similar in production): Time.now.year will return the year the server was started. You want to run this scope in a lambda to have it behave as expected.

scope :current, lambda { where(:year => Time.now.year) }
过度放纵 2024-11-13 15:36:50

不,没有更好的方法来完成您想要做的事情(除了注意 Geraud 的评论)。在您的范围内,您定义一个类级过滤器,它将生成用于限制查找器返回的结果的 SQL,在您定义要在此类的特定实例上运行的实例级测试的属性中。

是的,代码是相似的,但它在不同的上下文中执行不同的功能。

No, there's no better way to do what you're trying to do (other than to take note of Geraud's comment). In your scope you're defining a class-level filter which will generate SQL to be used in restricting the results your finders return, in the attribute you're defining an instance-level test to be run on a specific instance of this class.

Yes, the code is similar, but it's performing different functions in different contexts.

内心旳酸楚 2024-11-13 15:36:50

是的,您可以在作用域中使用一个或多个带有 lambda 的参数。假设您有一组物品,并且您想要取回“Boot”或“Helmet”:

  scope :item_type, lambda { |item_type|
    where("game_items.item_type = ?", item_type )
  } 

您现在可以执行 game_item.item_type('Boot') 来仅获取靴子或 game_item.item_type('头盔')只获得头盔。这同样适用于你的情况。您可以在作用域中只包含一个参数,以便以一种更干燥的方式检查同一作用域上的一个或多个条件。

Yes, you can use one or more parameters with a lambda in your scopes. Suppose that you have a set of items, and you want to get back those that are either 'Boot' or 'Helmet' :

  scope :item_type, lambda { |item_type|
    where("game_items.item_type = ?", item_type )
  } 

You can now do game_item.item_type('Boot') to get only the boots or game_item.item_type('Helmet') to get only the helmets. The same applies in your case. You can just have a parameter in your scope, in order to check one or more conditions on the same scope, in a DRYer way.

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