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