структурирование сложных процедур ruby ​​helper - PullRequest
0 голосов
/ 03 октября 2010

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

Проще говоря, приложение берет данные значения ключа и переводит их в формат html или, возможно, в другие форматы. Я знаю, что данные значения ключа отстой, но для рассматриваемого приложения это требуется. Это будет приложение rails, и по сути я имею дело со структурой модели, которая выглядит примерно так:

У Фу много баров Бар имеет много полей Поле состоит из имени, значения, типа

В моем контроллере я хотел бы получить Foo и передать его в представление. Затем представление вызовет некоторый класс построителя с объектом Foo в качестве аргумента вместе с форматом рендеринга (например, html, pdf, xml).

С этого момента о том, как его структурировать, я немного растерялся, и я надеюсь, что кто-то может дать некоторые указатели в правильном направлении, поскольку я довольно плохо знаком с ruby ​​и выясняю, как подходят классы, модули и миксины. в картину меня смущает.

На высоком уровне вот что я думаю ... Класс строителя сказал бы: ах, мы хотим вывод html, поэтому передайте это строителю html. Конструктор html сказал бы, хорошо, у нас есть Foo, и нам нужно отобразить этот вывод. Это логика перебирает поля и пытается выяснить, как визуализировать каждое из них, например:

1) Есть ли у нас что-то, что конкретно знает, как визуализировать поле 4 в Bar.id = 8 в Foo.id = 3?

2) Есть ли у нас что-то, что знает, как отобразить field.type = 'phone' для Bar.id = 8 на Foo.id = 3?

3) Есть ли у нас что-то, что знает, как отображать field.type = 'phone' для Foo.id = 3?

4) Есть ли у нас что-то, что умеет отображать field.type = 'phone'?

5) Вызовите рендерер по умолчанию

В конечном итоге моя цель - написать как можно меньше кода. Например, для более чем половины полей рендеринг для html будет не более чем простым текстом. Тем не менее, характер приложения требует, чтобы мы были в состоянии удовлетворить любой крайний случай, необходимый. Например, вполне возможно, что в некоторых случаях нам нужно отобразить номер телефона как xxx-xxx-xxxx, тогда как в других случаях его нужно будет отображать как (xxx) xxx-xxxx. Моя общая идея состоит в том, чтобы создать что-то, что могло бы принять модель в качестве аргумента и перейти от специфических к универсальным средствам визуализации, чтобы в любом особом случае я мог специально сказать для поля 'phone' в барах 3 и Foo 7, что оно должно выводить особым образом , Но, очевидно, я не хочу создавать иерархию классов для каждого поля всегда Bar of Foo, скорее я хотел бы метод, где я могу реализовать некоторый код, если требуется особый случай, и чтобы подпрограмма Builder находила это.

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

Ответы [ 2 ]

0 голосов
/ 05 октября 2010

Таким образом, ваша цель состоит в том, чтобы использовать какой-то один объект-конструктор или коллекцию объектов-сборщиков, которые могут генерировать аналогично структурированные данные в разных форматах, так что вам не нужно поддерживать слишком много отдельных путей кода.

Если я не очень неверно истолковываю вашу ситуацию здесь, я бы предложил придерживаться ванильных соглашений 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 и т. Д.

Веселись!

0 голосов
/ 04 октября 2010

вы можете установить переменную потока, чтобы знать, какой тип данных вы отображаете:

Thread.current [: render_type] = 'abc'

тогда вам не придетсяпропустите стиль рендеринга до конца через каждую частичку или что-то еще.

Вы можете просто использовать обычную систему рендеринга .rhtml рельсов и использовать различные партиалы, где это уместно, или нет.И, возможно, сделайте имена методов конкретными, чтобы вы знали, что происходит:)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...