Rails STI: как изменить сопоставление между именем и значением класса столбца типа

Из-за правил компании я не могу использовать наши имена классов домена; Вместо этого я буду использовать аналогию. У меня есть таблица под названием проекты, в которой есть столбец с названием "тип" с возможными значениями как "закрытый" и "открытый". Записи, имеющие внутренний и наружный, имеют четкое разделение функций и будут довольно аккуратно использоваться в качестве реализации ИППП. К сожалению, я не могу изменить имена типов и не могу добавлять классы в глобальное пространство имен. Есть ли способ указать другое значение для "type"?

Примечание. Я не пытаюсь использовать другое имя столбца вместо "type" для STI. Я ищу, чтобы иметь другое значение для типа, кроме имени моего класса.

Ответ 1

Вы можете попробовать что-то вроде этого:

class Proyect < ActiveRecord::Base
end

Затем класс Indoor, но с другим именем

class Other < Proyect
  class << self
    def find_sti_class(type_name)
      type_name = self.name
      super
    end

    def sti_name
      "Indoor"
    end
  end
end

То же самое относится к классу Outdoor. Вы можете проверить sti_name в http://apidock.com/rails/ActiveRecord/Base/find_sti_class/class

Ответ 2

Это возможно, но немного запутанно. По сути, когда вы сохраняете запись унаследованного класса, метод перемещается к родительскому классу с добавленным значением типа.

Стандартное определение:

class Vehicle < ActiveRecord::Base
  ..
  def type=(sType)
  end
  ..
end

class Truck < Vehicle
  # calling save here will trigger a save on a vehicle object with type=Truck
end

Изменение, на мой взгляд, в лучшем случае нестабильно. Вероятно, вы столкнетесь с другими проблемами.

Недавно я обнаружил AR-метод becomes, который должен позволить вам преобразовывать дочерние объекты в родительские объекты или как документация предлагает родителям детей, вам может быть повезло с этим.

vehicle = Vehicle.new
vehicle = vehicle.becomes(Truck) # convert to instance from Truck to Vehicle
vehicle.type = "Truck"
vehicle.save!

Не использовать это сам, но просто, вы должны иметь возможность изменить значение столбца type перед сохранением довольно легко. Это, вероятно, вызовет несколько проблем с встроенным интерфейсом запросов и ассоциаций Active Record.

Кроме того, вы можете сделать что-то вроде:

class Truck
 ..
 def self.model_name
  "MyNewClassName"
 end
 ..
end

Но при таком подходе будьте осторожны, что остальные рельсы, включая маршруты и контроллеры, будут относиться к модели как "MyNewClassName", а не "Грузовик".

PS: Если вы не можете добавлять классы внутри своего глобального пространства имен, то почему бы не добавить их в другое пространство имен? Родитель и ребенок могут принадлежать к разным пространствам имен или могут принадлежать к уникальному пространству имен (а не глобальному).

Ответ 3

Если требуется только удаление имен модулей из поля type, можно использовать следующий вспомогательный модуль, основанный на ответе VAIRIX:

# lib/util/namespaceless_sti.rb

module NamespacelessSti
  def find_sti_class(type_name)
    type_name = self.name
    super
  end

  def sti_name
    self.name.sub(/^.*:/,"")
  end
end

Этот модуль должен быть расширен в каждый базовый класс STI, например. Project из описания OP (воображаемые модели живут в Acme пространстве имен/модуле)

# app/models/acme/project.rb

require "util/namespaceless_sti"

module Acme
  class Project < ActiveRecord::Base
    extend NamespacelessSti
  end
end

# app/models/acme/indoor.rb

module Acme
  class Indoor < Project
    # nothing special needs to be done here
  end
end

Это гарантирует, что записи Acme::Indoor в таблице projects будут иметь "закрытый" в поле type.

Ответ 4

При взгляде на исходный код, кажется, есть опция store_full_sti_class (я не знаю, когда она была введена):

config.active_record.store_full_sti_class = false

Это избавит меня от модулей namespacing для меня в Rails 4.2.7