Rails Single Table Inheritance - Каков наилучший способ явно установить тип?

Я использую однонамерное наследование в моем приложении rails и хочу явно указать тип экземпляра.

У меня есть следующее:

class Event < ActiveRecord::Base
class SpecialEvent < Event

который реализуется посредством наследования одной таблицы.

SpecialEvent.new работает так, как ожидалось, но я хочу иметь возможность делать такие вещи, как

Event.new(:type => 'SpecialEvent')

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

Однако это не работает и, кажется, устанавливает :type в nil, а не значение, которое я ему задал; Я подозреваю, что это вызвано тем, что при вызове Event.new он перезаписывает аргумент :type.

Кто-нибудь получил хороший способ сделать это?

Ответ 1

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

'SpecialEvent'.constantize.new()

Ответ 2

от "Pragmatic - Agile Web Development с рельсами 3-го издания", стр. 380

Theres также менее очевидное ограничение (с STI). Тип атрибута также является именем встроенного Ruby-метода, поэтому доступ к нему напрямую для установки или изменения типа строки может привести к появлению странного Ruby Сообщения. Вместо этого обращайтесь к нему неявно, создав объекты соответствующий класс или доступ к нему через индексирование объектов модели интерфейс, используя что-то вроде этого:

person [: type] = 'Manager'

человек, эта книга действительно качает

Ответ 3

Нет, я хочу создать экземпляры подтипы, где я хочу программно определить, какие sub_type они - HermanD

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

class EventFactory
  def EventFactory.create_event(event_type)
    event_type.constantize.new()
  end
end

Ответ 4

Мне кажется, что вам понадобится несколько mojo в действии event#create:

type = params[:event].delete(:type)

# check that it is an expected value!!!
die unless ['Event', 'SpecialEvent'].include(type)

type.constantize.new(params[:event])

Ответ 5

По-видимому, Rails не позволяет вам устанавливать Type напрямую. Вот что я делаю...

klass_name = 'Foo'
...
klass = Class.const_get(klass_name)
klass.new # Foo.new

Я считаю, что .constantize - это ярлык фальцевого Rails. const_get - это метод Ruby для класса и модуля.

Ответ 6

Наверху Я соглашусь, что STI часто не лучший способ справиться с вещами. Полиморфизм, да, но часто лучше использовать полиморфную ассоциацию, чем STI.

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

https://github.com/arvanasse/sti_factory

Короче говоря, он использует метод factory для использования общего подхода, описанного выше. В результате контроллер может оставаться нейтральным/не осведомленным о конкретном типе класса STI, который вы создаете.

Ответ 7

Вы можете использовать метод Rails safe_constantize, который гарантирует, что объект/класс действительно существует.

Например:

def typeify(string)
  string.classify.safe_constantize
end

new_special_event = typeify('special_event').new