Легкая верстка в вынужденных местах: хелперы, декораторах, элеманты форм

в 3:53, , рубрики: ruby, метки:

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

Действительно, при таком подходе view-файлы начинают выглядить лучше. Но кастомные элементы форм и хелперы становятся просто невыносимыми.

Но есть простой и элегантный способ как сделать их чище и проще…

К примеру возьмем кастомный элемент формы PriceRangeInput.

class PriceRangeInput < SimpleForm::Inputs::Base
  def input
    output = template.content_tag(:div, class: 'j-price-slider') do
      div = ''
      div << template.content_tag(:div, class: 'row') do
        row = ""
        row << template.content_tag(:span, class: 'span3') do
          @builder.input(:min_total_price, label: false, input_html: { class: 'input-small j-min-total-price'})
        end
        row << template.content_tag(:span, class: 'span3') do
          @builder.input(:max_total_price, label: false, input_html: { class: 'input-small j-max-total-price'})
        end
        row.html_safe
      end
    
      div << template.content_tag(:div, class: 'row') do
        template.content_tag(:span, class: 'span6') do
          template.content_tag(:div, class: 'j-slider', :data => :slider_data) do
          end
        end
      end
      div.html_safe
    end
    output.html_safe
  end
end

Этот элемент легко вызвать из формы:

= simple_form_for current_search_form, :url => :search, :method => "get" do |f|
    = f.input :price_range, :label => false, :as => :price_range

но упростив саму форму, кастомны элемент стало сложно понимать. В его структуре легко запутаться.

Выход есть

Решение — использовать Arbre — Ruby Object Oriented HTML Views.

Он позволяет легко использовать верстку в коде, а также создавать повторно используемые компоненты. Arbre рожден в проекте acive_admin и является, по сути, его основой.

Ближе к делу

Начнем с добавление помощничка в базовый класс элементов форм. Это один из редких примеров уместного monkey patch.

class SimpleForm::Inputs::Base
private
  def arbre assigns={}, &block
    Arbre::Context.new assigns.reverse_merge(:builder=>@builder), template, &block
  end
end

Теперь можем зарефаторить элемент формы:

 def input
    arbre slider_data: slider_data do
      div class: 'j-price-slider' do
        div class: 'row' do
          span class: 'span3' do
            builder.input :min_total_price, label: false, input_html: { class: 'input-small j-min-total-price'}
          end
          span class: 'span3' do
            builder.input :max_total_price, label: false, input_html: { class: 'input-small j-max-total-price'}
          end
        end

        div class: 'row' do
          span class: 'span6' do
            div class: 'j-slider', data: slider_data
          end
        end
      end
    end
  end

Убрали все лишнее, оставили только то, что действительно нужно. Код выглядит приятнее и понятнее.

Достоинства Arbre

И так подведем краткое резюме.

1. Использование

Arbre

позволяет избавиться от буфера, для хранение генерируемых тегов:

# было

buffer = ''
buffer << template.content_tag(:div, class: 'row') do
  ...
buffer << template.content_tag(:div, class: 'row') do
buffer.html_safe

# стало

div class: 'row' do
...
div class: 'row' do
 ...

2. Избавляет нас от необходимости применять мусорный content_tag в коде и дает возмжоность прямо указывать необходимый тег:

# было
template.content_tag(:div, class: 'row')

 # стало
div class: 'row'

3. И самое интересное — компоненты.

Собственные компоненты

Удивительное еще и то, что Arbre позволяет легко добавлять собственные элементы и использовать их в любом контексте.

class Row < Arbre::Component
  builder_method :row

  def build(title, attributes = {})
    super(attributes.merge class: 'row')
  end
end

Сразу после обьявления компонент готов к использованию в любом месте arbre-контекста. Теперь вместо:

template.content_tag(:div, class: 'row') do
  ...

можно писать

row do
  ...

а на выходе полчим код:

 <div class="row">
   ...

В общем arbre помогает сделать неизбежное — верстку в коде, более приятным. Рекомендую.

Автор: dapi

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js