Ruby / Rails: создать метод класса, который работает с экземплярами своих потомков? - PullRequest
2 голосов
/ 21 августа 2011

В моем приложении Photo has_and_belong_to_many :land_uses

У меня есть этот вспомогательный метод в модели Photo:

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

Это кажется мне запахом кода (деметра), но я не смог понять, как перенести его в модель LandUse. Я хотел бы сделать что-то вроде:

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

Чтобы вместо звонка photo.land_use_list я мог позвонить photo.land_uses.list.

Но это не работает, потому что он вызывается против класса, а не против экземпляров с областью действия, принадлежащих определенной фотографии.

Есть ли способ сделать то, о чем я думаю? И, в целом, как вы подходите к таким вопросам в своем приложении? Является ли перемещение кода списка в модель LandUse правильным подходом, или вы бы порекомендовали что-то другое?

Ответы [ 3 ]

1 голос
/ 22 августа 2011

Во-первых, я не думаю, что это нарушает закон Деметры как таковой. У вас есть метод для объекта, который вызывает один метод для атрибута для создания временной переменной, а затем воздействует на временную переменную.

Было бы нарушением Закона Деметры, если бы вы делали это из другого класса полностью. например,

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 .

Лучший способ подчиниться закону деметры - это делать более или менее то, что вы делаете, хотя, потому что добавление вашего метода в Photo означает, что методы, взаимодействующие с Photo, не Также необходимо знать о классе LandUse, просто у этой фотографии есть метод, который возвращает строку с именами землепользования.

0 голосов
/ 21 августа 2011

Я не перед приложением рельсов, но я верю

photo.land_uses

с возвратом массива LandUse объектов

Так что вам просто нужно переместить карту вниз в этот массив, например:

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

это то, что вы имели изначально - просто в вашей другой модели. Я думаю, что вы, возможно, правы, и это означает, что Photo слишком много знает о LandUse, поэтому я бы убрал его.

0 голосов
/ 21 августа 2011

Вы можете использовать:

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

  def to_s
    self.name
  end
end

Надеюсь, это поможет!

...