Use case #7: defining the way we would use parts
One of the aspects I expected the most out of Hanami2 was the improvements about the views & templates.
More modular, more Ruby (less ERB), no code duplication: I knew that we would greatly benefit from more decoupling. Once it was out, we had to find our new methodology and to pick our preferred way of working in all the possibilities offered by the view layer. It took me some time, but I think I’ve found a way that suits us, I’ll try to explain it here.
(Disclaimer: it’s just one way to use the framework, optimized for our daily use with productivity and longevity in mind. I’m sure there are many more ways to do. Feel free to share !)
PARTS: here is our rule: if you need to render a chunk of html related to a core object: use a part instead of a raw partial
A part is some ruby code wrapping (aka decorating) another object of yours, allowing you to attach presentation related stuff to it, without the original object being aware of it.
We decided to define 3 kinds of methods allowed on a part:
- render methods, the entry points to render a part, starting with ‘render_’
- presentation methods, called by html partials, starting with ‘_’
- internal methods, called by information methods, regular methods
It would look like this
# auto_register: false
module MySlice
module Views
module Parts
class Product < MySlice::Views::Part
# Rendering functions, should start with render (Deck convention proposal)
def render_product_card(is_small: false, is_addable_to_selection: false)
render("partials/product/product_card",
is_small: is_small,
is_interact_droppable: false,
is_addable_to_selection: is_addable_to_selection,
)
end
def render_interact_droppable_product_card
render("partials/product/product_card",
is_small: true,
is_interact_droppable: true,
is_addable_to_selection: false,
)
end
def render_product_main_picture(is_small: false)
render("partials/product/product_main_picture",
is_small: is_small,
)
end
def render_sample_product_title
helpers.tag.li(displayed_name)
end
# Presentation functions, should start with _ (Deck convention proposal)
def _truncated_displayed_name(limit = 50)
if displayed_name.size > limit
displayed_name(raw_value: true)[0..limit] + "[..]"
else
displayed_name(raw_value: true)
end
end
def _img_manufacturer_logo
helpers.manufacturer_logo(value)
end
def _img_main_picture(size: 300)
helpers.product_image(value, size: size)
end
end
end
end
end
<!-- partials/product/_product_main_picture.html.erb -->
<div class="" style="<%= is_small ? 'width: 80px' : 'width: 300px' %>;margin: 2px;position: relative;">
<%= product._img_main_picture(size: is_small ? 80 : 300) %>
</div>
And would be called like this in any partial, assuming we have declared expose :products, as: :product
in the view
<% products.each do |product| %>
<%= product.render_product_main_picture %>
<% end %>
The idea behind this is to use one of the (potentially many) render methods as soon as we want to display a core object. It’s main responsibility is to target the correct partial and give i the correct locals.
And to put any display logic that the erb partial would need, in the presentation methods. Making them start with “_” has been chosen to avoid the confusion with native methods on the objects, that can be quite complicated on their own.
Next episode, using SCOPES !
Thank you for reading