O Helper no Rails fica localizado em app/helpers
e é nada mais que um módulo auto-injetado no contexto da view, ou seja, os métodos implementados neste módulo Helper estarão disponíveis no contexto da view. Até a versão 3 do Rails, os Helpers eram aplicados considerando o controller que está em uso. O que isso significa, o helper app/helpers/carts_helper.rb
seria aplicado apenas nas views referentes ao controller app/controllers/carts_controller.rb
. Para criar um Helper que fosse comum entre controllers, o mesmo era criado como ApplicationHelper (app/helpers/application_helper.rb)
. A partir do Rails 4, todos os Helpers são aplicados para todos os controllers.
Exemplo:
#!Ruby
# app/helpers/resource_helper.rb
module ApplicationHelper
def say_hello(user)
name = content_tag :span, user&.name || "Guest"
"Hello #{name}"
end
end
# app/views/layouts/application.html.erb
...
<% @current_user = OpenStruct.new(name: "Tino") %>
<%= say_hello @current_user %>
Results:
Hello <span>Tino</span>
<%= say_hello nil %>
Results:
Hello <span>Guest</span>
Por conta que o Helper é injetado no contexto da view, seus métodos podem acessar as variáveis/métodos que também estão disponíveis na view, como no exemplo acima, que está acessando o helper content_tag
. Porém, esse tipo de acesso deve ser evitado ao máximo, por conta de tornar o método dependente do contexto externo. Uma boa prática é, sempre que possível, fazer a passagem dos parametros necessário para execução do método. No exemplo acima, @current_user
poderia ser acessado diretamente no método say_hello
.
Decorator é um pattern que aumenta um objeto com novas propriedades, que nem sempre fazem sentido existir diretamente na classe desse objeto.
Exemplo:
#!Ruby
require 'active_model'
class Product
include ActiveModel::Model
attr_accessor :name, :price
def free_shipping?
price.to_f > 1000.0
end
end
class Tax < SimpleDelegator
def initialize(product)
@product = product
__setobj__ product
end
def price
@product.price * (1 + rate)
end
def rate
raise "should be implemented"
end
end
class IPI < Tax
def rate
0.05
end
end
class ICMS < Tax
def rate
0.12
end
end
@product = Product.new name: "Carrinho de mão", price: 2100.40
puts @product.name # => "Carrinho de mão"
puts @product.price # => 2100.40
@product = ICMS.new(IPI.new(@product))
puts @product.name # => "Carrinho de mão"
puts @product.price # => 2423.02
Um presenter é essencialmente um Decorator. O conceito foi introduzido pelo Jay Fields e é mais voltado à exibição das informações (embora, no exemplo original ele usa como um modo de simplificar os objetos de um formulário).
A idéia principal do Presenter é aplicar lógicas e formatações, facilitando o uso da visualização do objeto "decorado" na camada de apresentação (view)
Exemplo (considerando já a existência da classe Product
acima)
#!Ruby
class ProductPresenter < SimpleDelegator
def initialize(product)
@product = product
__setobj__ product
end
def price
helpers.number_to_currency(product.price)
end
def shipping_method_partial
product.free_shipping? ? "free_shipping" : "shipping_method_selector"
end
private
attr_reader :product
def helpers
ApplicationController.helpers
end
end
@product = ProductPresenter.new(product)
puts @product.name # => "Carrinho de mão"
puts @product.price # => "$2,423.02"
#!ERB
<%# Então na view, podemos usar apenas: %>
<%= @product.price %>
<%# Em vez de poluir a view com algo como: %>
<%= number_to_currency @product.price %>
<%= render partial: @product.shipping_method_partial, product: @product %>
A primeira coisa que você precisa detectar é que tipo de lógica é possível extrair de sua view. Algumas coisas mais genéricas fazem mais sentido serem extraídas como helpers (como exemplo os FormHelpers do Rails). Outras, como ifs
para determinar qual partial deve ser renderizada provavelmente devem ser movidas para seu presenter.
Fontes: