So you’ve got a controller method
class DressesController < ApplicationController def show @dress = Dress.find_by(params.permit(:id)) end end
And a view
<div class="<%= dress ? 'dress&.status' : '' %>">Status Indicator</div>
Looks good! Works great! Now your app grows, and you have a lot of other views that use the dress. Long after your view has been forgotten, and there’s no clear owner because everyone owns it, but that’s cool because it’s regarded as stable, someone comes along and builds another view
<h1><%= dress.named? ? dress.name.gsub(' ','_').titleize : 'Unnamed' %></h1>
Ew gross, so much logic in the view? Gross! Let’s put that logic in a decorator!
class DressDecorator < Draper::Decorator delegate_all def title dress.named? ? dress.name.gsub(' ','_').titleize : 'Unnamed' end end
delegate_all will forward all methods it's sent to whatever it's decorating.
class DressesController < ApplicationController def show @dress = DressDecorator.new(Dress.find_by(params.permit(:id))) end end
And let's clean up our view.
<h1><%= dress.title %></h1>
But now @dress will always be there, even if it decorates nil, so our old code is broken, and will return
NoMethodError (undefined method `status' for #<DressDecorator:0x00007fbc2696d568>)
Because of this, it's always important to code your intention, rather than code to save keystrokes. Use
dress.present?, because that's what you're checking, even though it's the same as
dress right now.
<div class="<%= dress.present? ? 'dress&.status' : '' %>">Status Indicator</div>
In other words, watch out for this...
d = Dress.new(foo: 'bar') !d.nil? => true d.present? => true d&.foo => 'bar' dd = DressDecorator.new(d) !dd.nil? => true dd.present? => false dd&.foo Traceback (most recent call last): NameError (undefined local variable or method `foo' for main:Object)