Рассмотрите абстрактную концепцию, где есть различные виды тегов, скажем Topic
и Location
(среди прочих), которые не связаны друг с другом, кроме тегов. Они имеют одинаковые базовые свойства Tag
, но в остальном они отличаются.Может ли концепция Trailblazer наследовать от другого и иметь возможность расширять ее операции (множественное наследование)?
A Topic
концепция основана на аналогичной концепции Tag
. Операция типа Topic::Update
обычно наследуется от Topic::Create
, но такая операция также должна унаследовать от Tag::Update
. Ruby не поддерживает множественное наследование - может ли Trailblazer поддерживать это?
операция Trailblazer поддерживает наследование через
builds
блока, что позволяет им создать экземпляр подкласса, основываясь на содержимом прилагаемыхparams
хэша. Это работает там, где базовый класс (Tag
) является открытым, а операции вызывается через базовый класс. Однако в этом примере класс, открытый для публики, является подклассомTopic
.Операции должны вызываться через подкласс (
Topic
), но его операции будут основаны от общегоTag
базового класса (обратного строитель?).
Вот один способ, которым это может быть достигнуто с помощью одиночного наследования (но он иллюстрирует недостатки этого подхода) ...
Каждый тип тега хранится в своей таблице базы данных и имеет ActiveRecord классы, как это:
class Tag < ActiveRecord::Base
self.abstract_class = true
end
class Topic < Tag; end
первопроходца концепция будет следовать аналогичной конструкции - это Tag
операция будет обеспечивать базовую функциональность и быть подклассы более конкретной операции (Topic
). Операция Tag
не будет использоваться напрямую - контроллер Topic
, например, будет использовать операцию Topic
.
Операция Topic
наследует от Tag
но должен указать свою собственную Topic
модель, которая представляется возможным только в рамках каждой операции, требующие каждый быть подклассы явно:
class Topic < Tag
class Create < Tag::Create
model Topic
end
class Update < Tag::Update
model Topic
end
class Delete < Tag::Delete
model Topic
end
end
Проблема с этим состоит в том, что контракт, определяется как базовая операция, считается, что это Tag
, а не Topic
, и это приводит к проблемам, когда он используется в качестве модели. Пример, показывающий, где это проблема, имеет вид ячейки: в концепции Topic
есть ячейка, которая представляет представления для управления своими объектами.Это делает форму с помощью simple_form_for
, как это:
simple_form_for operation.contract
Это не работает, как ожидалось, потому что контракт думает, что это Tag
и это ломает форму:
- его параметры передаются в
params[:tag]
вместо отparams[:topic]
- подпись кнопки отправки Создать тег вместо Создать тему.
Ячейка не может использовать operation.model
(что в противном случае работало бы), потому что оно не увидит ошибок формы при рендеринге после того, как поданная операция завершится с ошибкой.
способ решить это будет явно simple_form_for
:
simple_form_for operation.contract, as: :topic, url: topics_path ...
Другая проблема возникает при добавлении свойства Topic
, потому что это требует расширения Tag
контракта. Обычный способ сделать это - добавить блок contract do..end
к операции Topic::Create
. Проблема возникает из-за того, что такой блок не будет виден Topic::Update
и Topic::Delete
, потому что они наследуют от своих коллег Tag
, а не от Topic::Create
.
Альтернативой может быть операция подкласса Topic::Update
для наследования от Topic::Create
. Это устранило бы необходимость указать модель (потому что Topic::Create
это делает), но будет означать, что все добавлено Tag::Update
операции будут потеряны:
class Update < Create
action :update
end
В action
потребности быть respecified, потому что Tag::Update
не передается по наследству, но, потому что Topic::Create
наследуется, свойства добавлены в Topic::Create
доступны в Topic::Update
.
Оба эти стиля работают до тех пор, пока изменения находятся только в одном базовом классе. Это прерывается, так как в Ruby не поддерживается множественное наследование. Рассмотрим Delete
операцию, которая обычно выглядит следующим образом:
class Delete < Create
action :find
def process(params)
# validate params and then delete
end
end
Если это Tag::Delete
то Topic::Delete
может быть либо
class Delete < Tag::Delete
model Topic
end
или
class Delete < Create
action :find
end
В первом случае Topic::Delete
бы не знать свойства добавлены Topic::Create
, и в последнем случае Topic::Delete
не хватит process
метод defined в Tag::Delete
.
Каким образом концепция Trailblazer наследует другую и сможет расширять ее операции?