Skip to content


Subversion checkout URL

You can clone with
Download ZIP
decorate your view objects. one frill at a time.
Ruby Other
Tree: ec44ad6328

Fetching latest commit…

Cannot retrieve the latest commit at this time

Failed to load latest commit information.



Build Status Build Dependency Status

Simple decoration of objects for presentation. Out of the box integration with Rails.


Throw this in your Gemfile:

gem "frill"

Generate a frill:

$ rails g frill Timestamp --test-framework=rspec
      create  app/frills/timestamp_frill.rb
      invoke  rspec
      create    spec/frills/timestamp_frill_spec.rb

Refactoring timestamp helpers with decorators

Your product manager writes the following story for you:

Feature: Consistent Timestamp Presentation
  As a user
  I want "created at" timestamps presented in a uniform way on the site
  So that I can easily discover the age of content on the site

  Scenario: Presenting timestamps
    When I navigate to a page that displays a created_at timestamp
    Then I should see that timestamp marked up as bold and formatted as follows: YYYY/MM/DD

You see this and roll your eyes. You're thinking about all of the places that you show created_at timestamps on the site. Regardless you roll up your sleeves and start by writing the following helper and partial:

module ApplicationHelper
  def format_timestamp(t)
    render partial: "shared/timestamp", locals: { time: t.strftime "%Y/%m/%d" }

You then begin the tedious task of tracking down all of the places you render timestamps on the site and wrapping them with format_timestamp helper calls:

Written on <%=format_timestamp @article.created_at %>

You hate this approach.

  1. It's tedious
  2. It's procedural
  3. Developers have to remember to manually wrap timestamps with your format_timestamp helper. Developers suck at remembering things like that. FACEPALM

After you deliver the story, your product owner says "Great! But what about the format of timestamps in the JSON api? Here's another story."

Feature: Consistent Timestamp Presentation in the API
  As an API consumer
  I want "created at" timestamps presented in the API in uniform way
  So that I can easily discover the age of data I consume

  Scenario: Presenting timestamps
    When I retrieve content with a "created_at" timestamp via the JSON API
    Then I should see that timestamp formatted as follows: YYYY/MM/DD

You attempt to salvage the helper, updating it with concerns for the JSON format:

module ApplicationHelper
  def format_timestamp(t)
    time = t.strftime "%Y/%m/%d" 

    if request.format.html?
      render partial: "shared/timestamp", locals: { time: time }
    elsif request.format.json?

And now you begin the tedious track of updating all of the JSON views with the helper:

json.created_at format_timestamp(@article.created_at)

At this point, you're banging your head against a table.

Enter Frill

Let's refactor this using the decorator pattern. First, revert all of your changes. Next, add the frill gem to your Gemfile, run bundle, then generate a frill: rails g frill TimestampFrill:

module TimestampFrill
  include Frill

  def self.frill? object, context

  def created_at
    super.strftime "%Y/%m/%d"

The frill? method tells Frill when to extend an object with this module. Then we redefine the created_at method, calling super and then formatting the date returned with strftime.

Simple enough.

Next, generate another frill for presenting timestamps via HTML (rails g frill HtmlTimestampFrill):

module HtmlTimestampFrill
  include Frill
  after TimestampFrill

  def self.frill? object, context
    object.respond_to?(:created_at) && context.request.format.html?

  def created_at
     h.render partial: "shared/timestamp", locals: { time: super }

There's two important things to note:

  1. This frill comes after TimestampFrill. That tells Frill that it should only attempt to extend an object with this module after attempting to extend it with TimestampFrill.
  2. The frill? method only returns true if it's an HTML request, meaning this frill won't be extended onto objects for your JSON api.

Lastly, opt objects into frilling inside your controllers:

class ArticlesController < ApplicationController
  respond_to :json, :html

  def show
    @article = frill Article.find(params[:id])
    respond_with @article

And that's it. You don't have to update any of your views. Why? When you call the frill method inside your controller and pass it an object (or a collection of objects), frill will attempt to extend the object with any applicable frills (i.e., frills that return true for the frill? method when passed the object and the request context).

That way, you can simple render your created_at attributes without any helpers, and they will automatically present themselves appropriately for their context (e.g., HTML v. JSON requests).

Note that if prefer, you can configure your controllers to automatically frill all objects for presentation by calling the auto_frill method inside your ApplicationController, instead of manually having to opt them it via the frill method:

class ApplicationController < ActionController::Base

Now, you could remove the frill from your ArticlesController:

class ArticlesController < ApplicationController
  respond_to :json, :html

  def show
    @article = Article.find(params[:id])
    respond_with @article

Now, any instance variables you create in your controllers will be automatically frilled before handed off to your views.

'frill' decorates individual objects and collections

As I've hinted at, the frill helper will decorate both single objects and collections of objects. You can use it both within your controller and within your views.

For example, inside a controller:

class PostsController < ApplicationController
  def index
    @posts = frill Post.all

  def show
    @post = frill Post.find(params[:id])

Or, in a view:

<%= render frill(@post.comments) %>

Usage outside Rails

There are really just two integrations in a Rails app: the frill method inside of your controller, plus the ability to call helper methods inside of your module methods.

To kickoff the decoration of an object outside of a Rails application, simply call Frill.decorate:

Frill.decorate my_object, my_context


  • Ben Moss
  • Nicholas Greenfield
Something went wrong with that request. Please try again.