Decorator Pattern in Middleman

ByJordan Andree

In large projects, it can be a constant struggle to organize objects with clear responsibility and maintain reusability. I am currently working on a complex Rails application with a shared view logic with varying data contexts. My code organization problem is a perfect fit for utilizing the Decorator (or Presenter) Pattern. The pattern principally adds behavior to and can extend methods on objects through other objects.

Rails has a pretty simple way of executing this idea since most views get some sort of model with attributes that gets rendered out to a view. At work, I go back forth between Rails and Middleman projects everyday, so here is a rough version of how Decorators can be written for Middleman.

For example, here is how a normal view would be carried out, using a simple if/else statement:

# source/index.html.haml
- data.articles.each do |article|
  %article
    - if article.url
      %a{ href: article.url, title: article.title}
        = article.title
    - else
      = article.title

This code is okay and it works just fine. It gets messy if something is added down the road. I really don’t want the view handling all the logic though since I really just want it to output formatted data. It makes sense to extract the logic to its own Decorator object.

For this to work with Middleman, we need to first get a helpers added to the global helper scope:

# config.rb
require_relative 'lib/decorators/article_decorator'

# Initialize a decorated object
# example:
#   decorate(my_data, ArticleDecorator)
def decorate(object, decorator_class)
  decorator_class.send(:new, object, self)
end

Here is a simple Decorator example with some simple initialization to create methods out of our data object:

# lib/decorators/article_decorator.rb
class ArticleDecorator
  # view context, for access to other app helpers
  attr_reader :context
  alias_method :h, :context

  # define each key in our :attributes as method
  def initialize(attributes, context)
    @attributes = attributes
    @context = context

    @attributes.each do |key, val|
      define_singleton_method(key) { val }
    end
  end
end

Next, I can extract out the view logic from the template to the Decorator. In this example, I am rendering out using Haml helper methods. It’s not the most elegant solution, but could easily be substituted with a partial fragment render.

# lib/decorators/article_decorator.rb
class ArticleDecorator
  [..]

  def title_with_link
    return name unless respond_to?(:url)

    h.capture_haml do
      h.haml_tag :a, title, href: url
    end
  end
end

Finally, the view gets changed with the decorated objects and each article rendering the method for a title with link:

# source/index.html.haml

- data.article.map { |a| decorate(a, ArticleDecorator) }.each do |article|
  %article
    = article.title_with_link

For an example on how I’m using Decorators, check out the source for my personal website. Hopefully the benefits of Decorators are clear given that there is some extra work to do for them to be used. Feel free to send me tweet if you have any ideas or suggestions.