Guirec Corbel
2013-Jul-19 17:27 UTC
Refactor a view by a decorator, a presenter, an helper or something else?
Hi,
In a view, I have this code :
<% MeasureUnitDecorator.all.each do |mu| %>
<p>
<b>Dimensions in <%= mu.title %> :</b> <%=
painting.convert(:width,
mu) %> x <%= painting.convert(:height, mu) %>
<% if painting.depth %>
x <%= painting.convert(:depth, mu) %>
<% end %>
</p>
<% end %>
<% MeasureUnitDecorator.all.each do |mu| %>
<p>
<b>Dimensions with frame in <%= mu.title %> :</b>
<%painting.convert(:width_with_frame, mu) %> x
<%painting.convert(:height_with_frame, mu) %>
<% if painting.depth %>
x <%= painting.convert(:depth_with_frame, mu) %>
<% end %>
</p>
<% end %>
The result is this this :
<p><b>Dimensions in cm :</b> 10 x 14 x 16</p>
<p><b>Dimensions with frame in cm :</b> 12 x 16 x
18</p>
It work but I think there is too many logic in a view and I have a lot of
duplications.
I tried to refactor this code with a decorator like this (in this view : ) :
<% MeasureUnitDecorator.all.each do |mu| %>
<%= painting.dimensions_in(mu) %>
<% end %>
And in a decorator :
#....
def dimensions_in(mu)
properties = [:width, :height, :depth]
dimensions = formated_dimensions(properties)
h.content_tag(:p, "<b>Dimensions in #{mu.title} :</b>
#{dimensions}".html_safe)
end
def dimensions_with_frame_in(mu)
properties = [:width_with_frame, :height_with_frame,
:depth_with_frame]
dimensions = formated_dimensions(properties)
h.content_tag(:p, "<b>Dimensions with frame in #{mu.title}
:</b>
#{dimensions}".html_safe)
end
private
def formated_dimensions(properties)
properties.map { |p| convert(p, mu) }.compact.join(" x ")
end
def convert(property, unit)
source_unit = model.measure_unit.title
target_unit = unit.title.to_sym
depth.send(source_unit).convert(target_unit).to_f.round(2)
end
#....
It''s cleaner but I think the decorator have too many responsabilities
so I
did this (in a presenter file) :
class DimensionPresenter
attr_accessor :painting, :measure_unit, :source_unit, :target_unit
def initialize(painting, measure_unit)
@painting = painting
@measure_unit = measure_unit
end
def format(properties)
properties.map { |p| convert(p, mu) }.compact.join(" x ")
end
def source_unit
@source_unit ||= painting.measure_unit.title
end
def target_unit
@target_unit ||= measure_unit.unit.title.to_sym
end
def convert(property, unit)
depth.send(source_unit).convert(target_unit).to_f.round(2)
end
end
And I use it in my decorator like this :
def dimensions_in(mu)
properties = [:width, :height, :depth]
formated_dimensions = DimensionPresenter.new(painting,
mu).format(properties)
h.content_tag(:p, "<b>Dimensions in #{mu.title} :</b>
#{dimensions}".html_safe)
end
def dimensions_with_frame_in(mu)
properties = [:width_with_frame, :height_with_frame,
:depth_with_frame]
formated_dimensions = DimensionPresenter.new(painting,
mu).format(properties)
h.content_tag(:p, "<b>Dimensions with frame in #{mu.title}
:</b>
#{dimensions}".html_safe)
end
It''s ok but I don''t like to pass a model as argument in a
decorator
function. A decorator function should use only the decorated model.
I can also use a presenter like this :
<% MeasureUnitDecorator.all.each do |mu| %>
<%= DimensionPresenter.new(painting, mu).format %>
<% end %>
<% MeasureUnitDecorator.all.each do |mu| %>
<%= DimensionPresenter.new(painting, mu).format_with_frame %>
<% end %>
But I don''t like to initialize an object in a view. I can also create
an
instance variable in the controller but I don''t want to pass many
instance
variable. It''s not in the Sandy Metz best practices (
http://robots.thoughtbot.com/post/50655960596/sandi-metz-rules-for-developers
).
I also think to an helper by I think an help method but be generic and
should can be used by every models. This is not the case for this because
the is only a painting with this attributes.
So... I don''t know what to do. It''s a very little thing but I
don''t find
the solution.
What do you think? Do you have a better solution?
Thanks!
--
You received this message because you are subscribed to the Google Groups
"Ruby on Rails: Talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to
rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
To post to this group, send email to
rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org
To view this discussion on the web visit
https://groups.google.com/d/msgid/rubyonrails-talk/CABfX5Pa%2Bzz0gmNCfYOofyqqie_H9r8772pmjqacn6bQ7BN-9fA%40mail.gmail.com.
For more options, visit https://groups.google.com/groups/opt_out.