【问题标题】:Where to put method that returns a collection将返回集合的方法放在哪里
【发布时间】:2015-03-02 18:27:01
【问题描述】:

我有一个商店模型。

在我的课堂上,我创建了几种方法,使我的代码更易于阅读。

类似的东西:

# Returns True if the shop has an owner
def is_claimed
  return self.owner_id != nil
end

现在我想创建一个返回 Shops 集合的方法。

类似:

def get_all_open
  return Shop.where("shops.closed != 1")
end

我只是想知道我应该把它放在哪里,所以它可以从我可能使用的任何地方获得

Shop.all

在我的代码中。

  • 如果我将它放在模型中,它将对商店实例可用,这听起来很奇怪。
  • 这个方法也属于Shop,所以我不太喜欢放在ApplicationController中。
  • 我也考虑过将它放在shops_controller 中,但这样其他控制器就无法轻松使用它了。

我已经在互联网上查找过,但到目前为止还没有找到我喜欢的东西。什么会被认为是 rails 的好习惯?

谢谢

【问题讨论】:

  • 或者我实际上是在我的问题中定义辅助方法吗? api.rubyonrails.org/classes/ActionController/Helpers.html 。听起来像。
  • Shop 模型def self.all_shops; self.all;end 内部会不会很好?这里完全有道理。你可以在任何地方调用它,比如Shop.all_shops...
  • 这绝对是 类方法 的理想选择,正如 Arup 所建议的那样:def self.get_all_open ...。通过 Shop.get_all_open 调用。或者,将其设为范围scope :all_open ....

标签: ruby-on-rails ruby


【解决方案1】:

您可以将其保留在 Model 中,但不要将其定义为实例方法,而是将其定义为类方法:

def self.all_open
  where("shops.closed != 1")
end

这样你就可以这样使用它了:

Shop.all_open

甚至嵌套方法,像这样:

Shop.all_open.where(field: "value")

更新:

就像其他人指出的那样,您也可以使用范围(这是首选方式):

class Shop < ActiveRecord::Base
  scope :all_open, -> { where("shops.closed != 1") }
end

用法相同:

Shop.all_open

【讨论】:

  • 好的,谢谢。来自 Java,我在将概念转换为 ruby​​ 表示法时遇到了一些麻烦。 :)
  • scopes 绝对是这里的标准 Rails 约定......我要补充的唯一挑剔是 all_open 可以缩短为 open (除非将其用作动词有混淆)
【解决方案2】:

这就是Scopes 通常的用途。

class Shop < ActiveRecord::Base
  scope :open, ->{ where("shops.closed != 1") }
  #            ->{ where.not(closed: true) } # <- Either will work, but I prefer this

  # ...
end

然后:

Shop.open.all # => (a collection of Shops)

范围的优点是您可以将它们链接起来,因此,例如,如果您的 Shop 模型具有 type 属性,您可以使用某个 type 获取所有打开的 Shops,如下所示:

Shop.open.where(type: "pharmacy").all

【讨论】:

  • 我不知道,我可以在 scope 上调用#all .. :D
  • 实际上,您通常不需要调用all ...许多可枚举类型方法已经在结果范围上定义。 Shop.open.each{|s| puts s}
  • all 在这种情况下实际上是不必要的(在 Rails 4 中)。 Shop.openShop.open.all 将返回完全相同的内容。 all 现在返回一个关系,如果你想要一个数组——比如缓存,你应该使用 to_a
  • 是的,你们都说得对。为了清楚起见,我在代码中包含了all
【解决方案3】:

将其定义为 Shop 中的类方法。

class Shop
  def self.all_open
    where("shops.closed != 1")
  end
end

open_shops = Shop.all_open

注意:

0) def self.all_open 中的 self 使它成为类方法,定义在类 Shop 本身上,并且可以在类本身上调用为 Shop.all_open,而不是需要调用的普通实例方法在一个特定的实例上。

1) 当您调用Shop.all_open 时,调用方法all_open 而不是get_all_open 会导致更自然和惯用的代码。事实上,我可能只是称它为open,导致open_shops = Shop.open

2) 请注意,建议的实现省略了return,只是为了便于阅读。请注意,它还省略了Shop.where,而只省略了where。这仅在您最终创建 Shop 的子类时才重要,您可能不会这样做,但风格很好,代码更短。只是whereself.where 相同,并且由于这是一个类方法,self 是类本身,Shop(除非您有继承此代码的子类,在这种情况下self 可能是一个子类)。

3) 实际上,您可以将其与其他东西链接起来,没问题。看看:Shop.all_open.where(:city =&gt; "Baltimore").order(:updated_at)。非常简洁,它只是工作!

4) 有些人会告诉你为此使用 ActiveRecord 'scopes'。对于这种情况,您实际上并不需要,您可以编写一个普通的旧方法,就像那样。 Rails docs 承认使用范围来处理此类事情:“这与定义类方法完全相同,您使用哪种方法取决于个人喜好。”我只是在这里使用一个普通的旧方法,就像你开始时一样(除了定义为模型上的类方法)——它更简单,它可以满足你的期望,你不需要了解“范围” ',这只是一种方法。使用作用域来代替它是Rails支持使用普通类方法之前的一种遗留物,普通类方法就可以了。

【讨论】:

  • 使用作用域的主要原因是它们的目的是隐含的。如果我正在阅读同事的代码并看到scope,我知道,除非我的同事做了一些奇怪的事情,否则它将返回一个 ActiveRecord::Relation,我可以将它与其他范围和查找器方法链接起来。如果模型有一堆类似作用域的类方法与它的其他类方法混合在一起,这会使它们更难找到。你说得对,scope 只是一个约定,但就像 Rails 的大多数其他约定一样,它是一个非常有用的约定。
  • 我不同意,我更喜欢普通的方法,这样你就不需要弄清楚(或调试)关于范围的任何事情——它只是 ruby​​,当它不需要 Rails 魔法时,谁需要它添加任何东西。如果需要,您可以在评论中记录意图。但是每个人都可以有自己的喜好!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-14
  • 2015-04-18
  • 1970-01-01
  • 1970-01-01
  • 2015-07-13
  • 2023-03-13
  • 2012-07-18
相关资源
最近更新 更多