Rails: рефакторинг, представления, помощники: как все это сочетается? - PullRequest
16 голосов
/ 03 февраля 2010

Предупреждение: Нуб здесь.

Я знаю, что это тривиальный предмет, но мне очень трудно понять, как именно я могу упростить свои взгляды, превращая их части в помощников. Например, я всегда читал, что условные выражения в ваших представлениях являются основными кандидатами для извлечения в помощники, но я не смог найти примеры этого, и мои попытки достичь этого не дали результата.

Например, предположим, что у меня есть:

#index.html.erb

<% for beast in @beasts do -%>
  <% if beast.dead? -%>
    <%= beast.body %>
    <%= link_to "bury", bury_beast_path( :id => beast.id ) %>
  <% else -%>
    <%= beast.body %>
    <%= link_to "kill!", kill_beast_path( :id => beast.id ) %>
  <% end -%>
<% end -%>

Меня немного раздражает, когда я вижу это, но как именно я могу вместо этого передать это помощнику? И еще упростим, если это возможно. (Я где-то читал, что условные обозначения плохие, но я не могу программировать что-либо без них.)

Другой пример: мне нужно id моих body тегов в формате controller_action. Лучшее, что у меня есть, это:

#index.html.erb

<body id="<%= controller_action %>">

... и ...

#application_helper.rb

def controller_action
  @id = @controller.controller_name + "_" + @controller.action_name
end

Я не эксперт, но это все еще безобразно даже для меня.

Чтобы сделать вещи более сложными, Райан Сингер сказал что-то, что мне понравилось : относиться к ERB как к тегу изображения, используя помощников для «раскрытия намерения». Затем на следующем вдохе говорите, что у вас не должно быть HTML в помощниках, потому что это путь в ад. WTF? Как обе вещи совместимы? Если дошло до того, что вы можете просто объявить поведение в представлении, наверняка за кулисами должно быть много HTML-кода? Я не могу понять это.

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

Ответы [ 4 ]

26 голосов
/ 03 февраля 2010

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

Вы можете выбрать частичные и помощники . Там нет каменных правил, предписывающих, где и где их следует использовать. Существует пара рекомендаций, в которых говорится, что помощники не должны содержать HTML.

Обычно партиалы лучше подходят для рефакторинга разделов, которые больше HTML / ERB / ​​HAML, чем ruby. Помощники, с другой стороны, используются для фрагментов кода ruby ​​с минимальным HTML или для генерации простого HTML из параметров.

Однако я не согласен с мнением о том, что помощники вообще не должны содержать HTML. Немного в порядке, просто не переусердствуйте. То, как обрабатываются помощники, затрудняет их использование для создания больших объемов HTML. Вот почему предполагается, что ваши помощники содержат минимальное количество HTML. Если вы посмотрите на источник помощников, которые поставляются с рельсами, вы заметите, что большинство из них генерируют html. Те немногие, которые этого не делают, в основном используются для генерации параметров и оценки общих условий.

Например, любой из помощников формы или вариантов link_to соответствуют первой форме помощников. В то время как такие вещи, как url_for и logged_in? как поставляются различные модели аутентификации второго типа.

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

  1. Повторяющиеся или почти идентичные операторы, создающие один неглубокий HTML-тег? => Помощник.
  2. Общее выражение используется в качестве аргумента для другого помощника? => Помощник.
  3. Длинное выражение (более 4 терминов) используется в качестве аргумента для другого помощника? => Помощник.
  4. 4 или более строк ruby ​​(что не оценивается в HTML)? => Помощник.
  5. Практически все остальное => частичное.

В качестве примера я собираюсь использовать код, который вы ищете для рефакторинга:

Я бы изменил мнение в вопросе следующим образом:

приложение / хелперы / beast_helper.rb:

def beast_action(beast)
  if beast.dead?
    link_to "bury", bury_beast_path(beast)
  else
    link_to "kill!", kill_beast_path(beast)
  end
end

приложение / просмотров / животные / _beast.html.erb:

<%= beast.body %>
<%= beast_action(beast) %>

приложение / просмотров / животные / index.html.erb:

<%= render :partial => "beast", :collection => @beasts %>

Технически это сложнее, потому что это 3 файла и всего 10 строк, а не 1 файл и 10 строк. Представления в настоящее время только 3 строки объединены в 2 файла. Конечный результат - ваш код намного более СУХОЙ. Позволяет вам повторно использовать части или все это в других контроллерах / действиях / представлениях с минимальной дополнительной сложностью.

Что касается идентификатора вашего тега тела. Вы действительно должны использовать content_for / yield. Для такого рода вещей.

приложение / просмотров / макеты / application.html.erb

...
<body id="<%= yield(:body_id) %>">
...

Приложение / просмотров / животные / index.html.erb

<% content_for :body_id, controller_action %>
...

Это позволит вам переопределить идентификатор тела в любом представлении, которое требует этого. Например:

приложение / просмотров / пользователей / preferences.html.erb

<% content_for :body_id, "my_preferences" %>
8 голосов
/ 03 февраля 2010

Первое, что я хотел бы сделать, это:

#index.html.erb
<%= render @beasts %>

#_beast.html.erb
<%= beast.body %>
<%= link_to_next_beast_action(beast) %>    

#beast_helper.rb
def link_to_next_beast_action(beast)
  if beast.dead?
    link_to "bury", bury_beast_path( :id => beast.id ) 
  else
    link_to "kill!", kill_beast_path( :id => beast.id )
  end
end

Что я сделал, так это разделил рендеринг зверя на частичное, которое использует семантику коллекции.

Затем я переместил логику для отображения ссылок на убийство / похоронение в помощника зверя. Таким образом, если вы решите добавить другое действие (например, «вернуть из мертвых»), вам нужно будет только сменить помощника.

Помогает ли это?

1 голос
/ 24 июня 2014

Третий вариант - использовать модель вида из камня Cells . Это очень популярный фреймворк, который переносит объектную ориентацию на слой представления в Rails.

# app/cells/beast/cell.rb

class Beast::Cell < Cell::Concept
  def show
    return dead if model.dead?
    kill
  end

private
  def dead
    link_to "bury", bury_beast_path( :id => model.id ) 
    # you could render a view here, too!
  end

  def kill
    link_to "kill!", kill_beast_path( :id => model.id )
  end
end

Затем вы визуализируете модель представления с помощью помощника (в представлении или контроллере).

# app/views/beasts/index.erb

<%= concept(:beast, @beast).call %>
<%-# this returns the link content %>

Вот и все! Вы можете проверить эту ячейку изолированно в отдельном тесте. Клетки также дают вам представление рендеринга, просмотр наследования и многое другое.

Например, вы можете использовать представление для ссылки kill.

# app/cells/beast/cell.rb

class Beast::Cell < Cell::Concept

  # ..

  def kill
    render :kill
  end
end

Это делает вид убийцы клетки.

# app/cells/beast/views/index.erb

<%= link_to "kill!", kill_beast_path( :id => model.id ) %>

Обратите внимание на расположение представления, оно красиво упаковано в каталог ячеек.

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

0 голосов
/ 10 июня 2013

Другой стратегией было бы вообще не использовать шаблоны и помощников . Для рендеринга вы можете:

  1. рендеринг ваших представлений напрямую с ваших контроллеров с использованием render (: inline =>). Если вы по-прежнему хотите формально разделить представления и контроллеры, вы можете создавать модули / миксины, которые вы включаете в контроллеры.
  2. или создайте свои собственные классы представлений и используйте их для отображения вашего ответа.

Идея заключается в том, что система шаблонов помощников и rails erb не использует преимущества ООП, так что в конце дня вы не можете определить общее поведение, которое вы будете специализировать в соответствии с потребностями каждого контроллера / запроса ; чаще всего получается перезапись очень похожих кусков кода, что не очень приятно с точки зрения обслуживания.

Тогда, если вам все еще нужны некоторые вспомогательные методы (например, form_tag, h, raw, ...), вам нужно всего лишь включить их в ваш класс контроллера / выделенного представления.

См. Это: rails-mispre понятьings-helpers-are-shit для забавной, но полезной статьи.

РЕДАКТИРОВАТЬ: чтобы не звучать как полный душ, я бы сказал, что реализация этого зависит от того, насколько большим должно быть ваше приложение, и как часто вам придется обновлять код. Кроме того, если вы делегируете дизайн непрограммисту, он / она вполне может пройти некоторые курсы по программированию, прежде чем копаться в вашем коде, что, по общему признанию, будет менее понятным, чем с синтаксисом шаблонов.

...