Таким образом, ваша цель состоит в том, чтобы использовать какой-то один объект-конструктор или коллекцию объектов-сборщиков, которые могут генерировать аналогично структурированные данные в разных форматах, так что вам не нужно поддерживать слишком много отдельных путей кода.
Если я не очень неверно истолковываю вашу ситуацию здесь, я бы предложил придерживаться ванильных соглашений Rails. Пусть частички и помощники будут вашим представителем. Я обрисую подход здесь ...
Маршрутизация и моделирование
конфиг / routes.rb
map.resources :foos
Этот маршрут дает вам возможность различать различные форматы в ваших запросах. Таким образом, метод respond_to
в вашем действии контроллера будет проверять params[:format]
, чтобы определить, какое представление визуализировать. Запрос /foos/1.xml
установит params[:format]
в xml
и т. Д.
app / models / foo.rb, app / models / bar.rb, app / models / field.rb
class Foo < ActiveRecord::Base
has_many :bars
has_many :fields, :through => :bars
end
class Bar < ActiveRecord::Base
belongs_to :foo
has_many :fields
def quux?
self[:quux] || foo.quux? # sample overridable property
end
end
class Field < ActiveRecord::Base
# t.string :name, :value, :type
belongs_to :bar
delegate :foo, :to => :bar
def quux?
self[:quux] || bar.quux? # sample overridable property
end
end
Мое базовое понимание моделирования, которое вы описали. Особенно интересно отметить: если у вас есть объект Field в области видимости, у вас есть доступ ко всем его родителям в иерархии. Вы можете создать методы доступа в модели для любого свойства, которое вы хотите переопределить на более высоком уровне в цепочке. Пример такого свойства приведен выше.
Контроллер
foos_controller.rb
class FoosController < ApplicationController
def show
@foo = Foo.find(params[:id])
respond_to do |format|
format.html
format.xml
format.pdf
end
end
end
Ваш контроллер действительно не должен быть более сложным, чем этот. «Тощие контроллеры, толстые модели» и все такое. Это respond_to
, который делает тяжелую работу по выбору другого представления для запрошенного формата (с которым вы, возможно, уже знакомы).
просмотров:
Здесь все становится интереснее, и пути кода начинают расходиться.
Я собираюсь доказать, что вместо того, чтобы прыгать прямо в какой-то объект-строитель здесь, вы должны придерживаться частичных функций и помощников. Используя условные обозначения (map.resources
и respond_to
), вы выберете шаблоны правильного формата. Так что, да, вы можете получить много маленьких файлов для каждого типа поля и каждого формата. Но оттуда вы можете абстрагировать общую логику в помощников.
Такой подход будет работать особенно хорошо, если вам нужны строители, прежде всего, на уровне поля, где все ваши документы имеют похожую структуру выше этого.
Ниже приведен произвольный пример, просто чтобы подчеркнуть простоту подхода, который я защищаю:
приложение / просмотров / Foos / show.html.erb
<%= render @foo.bars %>
приложение / просмотров / бары / _bar.html.erb
<%= render bar.fields %>
Приложение / просмотров / поля / _field.html.erb
<%= render field.type, :field => field %>
Вот важный прием: используйте значение field.type
, чтобы выбрать соответствующий частичный. Не забудьте также передать объект поля, чтобы он был доступен в области этого частичного.
(Я предполагаю, что Rails правильно построит для вас оставшуюся часть этого частичного пути - заслуживает некоторого эксперимента. Этот метод может оказаться достаточно сложным, чтобы оправдать абстракцию в качестве помощника. render_field(field)
или что-то подобное.)
Приложение / просмотров / поля / _telephone.html.erb
<!--
This partial has access to field, which has access to both foo and bar.
We know the format based on the context of the partial file name.
Need further abstraction? Call a helper.
-->
<some_wrapper_code_for_this_format>
<%= format_telephone_field(field) %>
</some_wrapper_code_for_this_format>
Наконец: помощники
приложение / хелперы / fields_helper.rb
class FieldsHelper
def format_telephone_field(field)
# don't forget, we have access to params here, too
# including params[:format]
if field.quux?
number_to_phone(field.value)
else
number_to_phone(field.value, :area_code => true)
end
end
end
XML, PDF и т. Д.
Поддержка различных форматов будет зависеть от создания новых шаблонов с соответствующими расширениями. В этих шаблонах и частичках вы можете разместить разметку для конкретного формата. Семантика общего документа должна быть как можно больше вставлена в помощников.
В сумме
Не думаю, что ответил на ваш точный вопрос. Или, возможно, я сделал. Суть в том, что вместо того, чтобы начинать с создания чего-то сложного с самого начала, я бы придерживался простых соглашений и расходился только для того, чтобы иметь дело с чрезмерным дублированием, как это происходит. YAGNI, TDD, YMMV и т. Д.
Веселись!