Избегание ИППП в Rails - PullRequest
       6

Избегание ИППП в Rails

1 голос
/ 18 февраля 2010
class User < ActiveRecord::Base
  has_one :location, :dependent => :destroy, :as => :locatable
  has_one :ideal_location, :dependent => :destroy, :as => :locatable
  has_one :birthplace, :dependent => :destroy, :as => :locatable
end

class Location < ActiveRecord::Base
  belongs_to :locatable, :polymorphic => true
end

class IdealLocation < ActiveRecord::Base
end

class Birthplace < ActiveRecord::Base
end

Я действительно не вижу никакой причины иметь подклассы в этой ситуации. Поведение объектов местоположения идентично, единственная цель - облегчить ассоциации. Я также предпочел бы хранить данные как int, а не как строку, так как это позволит индексам базы данных быть меньше.

Я представляю что-то вроде следующего, но не могу закончить мысль:

class User < ActiveRecord::Base
  LOCATION_TYPES = { :location => 1, :ideal_location => 2, :birthplace => 3 }

  has_one :location, :conditions => ["type = ?", LOCATION_TYPES[:location]], :dependent => :destroy, :as => :locatable
  has_one :ideal_location, :conditions => ["type = ?", LOCATION_TYPES[:ideal_location]], :dependent => :destroy, :as => :locatable
  has_one :birthplace, :conditions => ["type = ?", LOCATION_TYPES[:birthplace]], :dependent => :destroy, :as => :locatable
end

class Location < ActiveRecord::Base
  belongs_to :locatable, :polymorphic => true
end

С этим кодом происходит следующее, что делает его бесполезным:

user = User.first
location = user.build_location
location.city = "Cincinnati"
location.state = "Ohio"
location.save!

location.type # => nil

Это очевидно, потому что нет способа перевести опции: condition в объявлении has_one в тип, равный 1.

Я мог бы встроить идентификатор в представление везде, где появляются эти поля, но это тоже кажется неправильным:

<%= f.hidden_field :type, LOCATION_TYPES[:location] %>

Есть ли способ избежать лишних подклассов или заставить работать подход LOCATION_TYPES?

В нашем конкретном случае приложение хорошо осведомлено о местоположении, и объекты могут иметь много разных типов местоположений. Я просто странный, не хочу всех этих подклассов?

Любые ваши предложения приветствуются, скажите мне, что я сумасшедший, если хотите, но хотите ли вы увидеть более 10 различных моделей местоположения, плавающих вокруг приложения / моделей?

Ответы [ 5 ]

2 голосов
/ 18 февраля 2010

Почему бы не использовать named_scopes?

Что-то вроде:

class User
  has_many :locations
end

class Location
  named_scope :ideal, :conditions => "type = 'ideal'"
  named_scope :birthplace, :conditions => "type = 'birthplace" # or whatever
end

Тогда в вашем коде:

user.locations.ideal => # list of ideal locations
user.locations.birthplace => # list of birthplace locations

Думаю, вам все равно придется настраивать тип при создании.

1 голос
/ 18 февраля 2010

Насколько я вижу, местоположение - это местоположение. Различные «подклассы», на которые вы ссылаетесь (IdealLocation, Birthplace), по-видимому, просто описывают отношение местоположения к определенному пользователю. Остановите меня, если я неправильно понял эту часть.

Зная это, я вижу два решения этого вопроса.

Первый - рассматривать местоположения как объекты-ценности, а не как сущности. (Подробнее об условиях: Значение по сравнению с объектами Entity (Domain Driven Design) ). В приведенном выше примере вы, похоже, устанавливаете местоположение в "Цинциннати, Огайо", а не находите объект "Цинциннати, Огайо" из базы данных. В этом случае, если бы в Цинциннати было много разных пользователей, в вашей базе данных было бы столько же идентичных местоположений "Цинциннати, Огайо", хотя есть только один Цинциннати, Огайо. Для меня это явный признак того, что вы работаете с объектом значения, а не с сущностью.

Как бы выглядело это решение? Вероятно, с помощью простого объекта Location, такого как:

class Location
  attr_accessor :city, :state

  def initialize(options={})
    @city = options[:city]
    @state = options[:state]
  end
end

class User < ActiveRecord::Base
  serialize :location
  serialize :ideal_location
  serialize :birthplace
end

@user.ideal_location = Location.new(:city => "Cincinnati", :state => "OH")
@user.birthplace = Location.new(:city => "Detroit", :state => "MI")
@user.save!

@user.ideal_location.state # => "OH"

Другое решение, которое я вижу, состоит в том, чтобы использовать вашу существующую модель Location ActiveRecord, но просто используйте отношение с пользователем, чтобы определить «тип» отношения, например:

class User < ActiveRecord::Base
  belongs_to :location, :dependent => :destroy
  belongs_to :ideal_location, :class_name => "Location", :dependent => :destroy
  belongs_to :birthplace, :class_name => "Location", :dependent => :destroy
end

class Location < ActiveRecord::Base
end

Все, что вам нужно сделать, чтобы выполнить эту работу, это включить атрибуты location_id, ideal_location_id и birthplace_id в вашу модель пользователя.

0 голосов
/ 18 февраля 2010

Может быть, я упускаю что-то важное здесь, но я думал, что вы могли бы назвать ваши отношения так:

class User < ActiveRecord::Base

  has_one :location, :dependent => :destroy
  #ideal_location_id
  has_one :ideal_location, :class_name => "Location", :dependent => :destroy
  #birthplace_id
  has_one :birthplace, :class_name => "Location", :dependent => :destroy

end

class Location < ActiveRecord::Base
  belongs_to :user # user_id
end
0 голосов
/ 18 февраля 2010

Вы можете инкапсулировать поведение объектов Location с помощью модулей и использовать некоторый макрос для создания отношения:

has_one <location_class>,: conditions => [ "type =?" LOCATION_TYPES [: location]],: dependent =>: destroy,: as =>: locatable

Вы можете использовать что-то вроде этого в вашем модуле:

module Orders
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def some_class_method(param)
    end

    def some_other_class_method(param)
    end

    module InstanceMethods
      def some_instance_method
      end
    end
  end
end

Направляющие Rails: добавить метод действий в качестве активной записи

0 голосов
/ 18 февраля 2010

Попробуйте добавить крючки before_save

class Location
  def before_save
    self.type = 1
  end
end

и аналогично для других типов местоположения

...