Ruby / Rails:创建一个对其子实例进行操作的类方法?

时间:2011-08-21 16:04:35

标签: ruby-on-rails ruby ruby-on-rails-3 model law-of-demeter

在我的应用中,Photo has_and_belong_to_many :land_uses

我在Photo模型中有这个辅助方法:

def land_use_list
  land_uses.map(&:name).join(', ')
end

这让我感觉像代码气味(demeter),但我无法弄清楚如何将它移动到LandUse模型。我想做的是:

class LandUse < ActiveRecord::Base
  ...
  def self.list
    self.map(&:name).join(', ')
  end
  ...
end

因此,我可以致电photo.land_use_list而不是致电photo.land_uses.list

但这不起作用,因为它是针对类调用的,而不是针对属于特定照片的作用域实例进行调用。

有没有办法做我正在考虑的事情?而且,更一般地说,您如何在应用中处理此类问题?将列表代码移动到LandUse模型是正确的方法,还是会推荐不同的东西?

3 个答案:

答案 0 :(得分:1)

首先,我不认为这违反了得墨忒耳本身的规律。你有一个对象的方法,它在一个属性上调用一个方法来创建一个临时变量,然后对临时变量进行操作。

如果你是完全从另一个班级做这件事,那将违反得墨忒耳法。例如

class User
  def names_of_lands_ive_known
    photos.map(:land_uses).map(:name).join ', '
  end
end

实际上,它只是隐藏着良好的信息。但是,如果您希望能够编写photo.land_uses.names,则可以为关联添加扩展以执行您想要的操作。

class Photo
  has_and_belong_to_many :land_uses do
    def names_as_list_string
      all.map(:name).join ', '
    end
  end
end

有关关联关联扩展的详细信息,请查看docs

符合demeter法则的最佳方法是尽可能地做你正在做的事情,因为通过在Photo上添加你的方法,这意味着与Photo交互的方法,不需要知道LandUse类,只是该照片有一个返回土地使用名称字符串的方法。

答案 1 :(得分:0)

您可以使用:

class LandUse
  def self.list_for_photo(id)
    LandUse.find_by_photo_id(id).join(', ')
  end

  def to_s
    self.name
  end
end

希望它有所帮助!

答案 2 :(得分:0)

我不在铁轨应用程序前面,但我相信

photo.land_uses

返回一个LandUse个对象

的数组

所以你只需将地图向下移动到那个数组:

photo.land_uses.map(&:name).join(', ')

这是你原来的 - 只是在你的其他模型中。我认为你可能是对的,这意味着PhotoLandUse了解得太多,因此我会把它移出来。