We usually hand-roll our own decorators here at Hashrocket. After doing this a few times I've found a pattern that I really like, and now I've turned it into a gem called LittleDecorator. This gem delivers on its name: there are only 42 lines of code in lib
. This blog post explains how to use it and how it works.
Create a Decorator
First create a decorator that subclasses LittleDecorator:
# app/decorators/post_decorator.rb
class PostDecorator < LittleDecorator
end
You can call model methods directly:
def full_title
"#{title}: #{subtitle}"
end
You can override model methods, but make sure to prefix calls to the original model with record
(or its alias model
) to avoid an infinite loop:
def updated_at
record.updated_at.strftime("%A, %B %e, %Y")
end
Helper methods are available to you:
def price
number_to_currency price
end
As well as route helpers:
def post_link
link_to title, record
end
Decorate Models
LittleDecorator gives you a decorate
helper method that's available in both controllers and views. Just pass it a model to get the decorated version. Calling decorate
on a collection will return an array of decorated objects:
decorated_post = decorate(post)
decorated_posts = decorate(posts)
How It Works
LittleDecorator uses method_missing
to send any unimplemented methods to the model. If the method is not found in the model, then it checks to see if the view context of the object responds to the method -- that's how you get free access to view helpers and route helpers. If the method is still not found then you'll get a NoMethodError
on the decorator.
The decorate
helper method works by looking for a decorator with the same class name as the model but with a suffix of "Decorator":
decorator_class = "#{model.class.name}Decorator".constantize
Extras
Decorated records also behave well when used in a link_to
:
<%= link_to decorated_post.title, decorated_post %>
That covers most everything you might want to know about this gem. Maybe it can save you some hassle the next time you need a decorator.
Check it out on GitHub.