The Simplest Rails Decorator Implementation That Just Might Work

Decorators/Presenters are (or were?) kind of a hot topic in Railslandia.  (If you already have no idea what I’m talking about, I do recommend the Giant Robots post linked above as background. ).

I’m going to stick to the word ‘decorator’, because I think the approach described the the old school Decorator (or here) pattern is the most interesting/useful one, especially expressed as: “a class that surrounds a given class, adds new capabilities to it, and passes all the unchanged methods to the underlying class”.

(tldr? If you just want the code without all the background, see: https://gist.github.com/4342817 and https://gist.github.com/4342928)

I think interest in Decorators in Rails is largely due to some architectural problems of Rails helpers. Your helpers can turn into a big pile of non-OO functions, all sharing a namespace and stepping on each other. But much worse is that  there’s no great way to use any kind of polymorphism with Rails helpers — providing different implementations for different contexts or types, over-riding or sub-classing helpers.  

Still, I think your first question should be:  Do I really need Decorators at all?

In general, I’d rather avoid em — I’d rather take the pain of a bit of pile-of-mud chaos in my helpers (and in a typical app, while it offends one aesthetics it’s not really that much pain) — over dealing with Yet Another Architectural Element that I and future readers of my code will have to understand (you don’t get out of having to understand rails helpers, you’ve just got one more), complexity, code interacting with Rails in mysterious ‘magic’ ways that might break in future versions of Rails, etc.

Why I decided I needed Decorators anyway

I’m developing a rails ‘engine’ gem, reusable code that will give you some presentational elements users can use in their rails apps. It’s got a fair amount of presentational logic in it — and while the logic needs to give you a decent prototyping environment out of the box, it also needs to be locally over-rideable and customizable — AND the user needs the ability to change the presentational logic differently in different contexts (trying not to get into details not because it’s secret (it’s open source), but just because it’ll turn into a confusing sidetrack).

I resisted Decorators, but everything else I tried ended up decidedly not simple, as my gem grew.

  • Tried putting it all in Rails helpers — I was polluting the helper namespace of the host app using my gem, and it’s very confusing to over-ride helpers provided by a gem in a local app (let alone try to over-ride such logic differently in different contexts).
  • Tried actually jamming all the logic into the view templates, which made me ashamed from the start, but at least was workable at the start when the logic was trivial, but got untenable as the gem grew non-trivial.
  • Tried putting the presentational logic directly into the models (which are NOT ActiveRecord in this case, but relatively simple plain old ruby objects), and letting local implementers over-ride it with a chain of run-time specified mixins added into objects with “extend”.  It started to get as complicated as that sounds, without actually being as flexible as one would like anyway (and no good way to have presentational logic call Rails helpers — more on that later).

And wound up with a hodge podge mix of all three techniques.

So I bit the bullet and after thinking about it and re-reading various blog posts on Decorators figured, okay, Decorator really is what I want.

But what’s the Simplest Possible Decorator Implementation That Just Might Work?   If I have to give in and use a Decorator, let’s at least keep it as transparent, unmagical, few lines of code, not over-engineered as possible. I suspected I could keep it pretty darn short, and so far it seems to be working.

Start With: plain old ruby, delegating via SimpleDelegator

The straightforward description of a Decorator is relatively easy to write in plain old ruby: “a class that surrounds a given class, adds new capabilities to it, and passes all the unchanged methods to the underlying class”

Various blog posts discuss using plain old ruby objects one way or another implement a Decorator — most of them use method_missing.  The examples may or may not correctly fix respond_to to match, which is important so as to have objects that behave like ruby objects should and don’t create surprises for other code. Oh, and then there’s respond_to_missing to get right too.

Why try to do all this yourself and get it right, when ruby already provides something in the stdlib that can do it all for you just fine?  Maybe because SimpleDelegator’s documentation doesn’t clearly tell you about the simplest way to use it to do this kind of delegation: Just sub-class SimpleDelegator, and call it’s initializer with the base object, that’s it. (This morning I read the first comment you’ll find at the ruby-doc.org link above,  and thought, gee, that’s helpful — before noticing I had authored it myself a year ago, hah.)

Check it out, super simple ruby Decorator/Delegator:


require 'delegate'
class Base
def foo
"foo"
end
def bar
"bar"
end
end
class MyDecorator < SimpleDelegator
def new_method
"new_method"
end
def foo
"Overridden #{super}"
end
end
class DecoratorTest < Test::Unit::TestCase
def setup
@base = Base.new
@decorated = MyDecorator.new(@base)
end
def test_pass_through_methods
assert_equal "bar", @decorated.bar
end
def test_decorator_can_add_method
assert_equal "new_method", @decorated.new_method
end
def test_override_with_super
assert_equal "Overridden foo", @decorated.foo
end
end

view raw

gistfile1.rb

hosted with ❤ by GitHub

Uh oh, but what about Rails helpers

Okay, that’s great, if all you need is a plain old ruby decorator with a pass-through interface, I don’t know why you wouldn’t do that with SimpleDelegator and call it a day.

But, see, remember our motivation here was cleaning up our Rails helpers. And local Rails helpers often need to be defined in terms of other Rails helpers (from the framework or local). Which aren’t available in the decorator.

You can start out trying to manually include Rails helper modules, or local helper modules. include ActionView::Helpers::TagHelper in your decorator, no problem!

But the most useful/interesting helpers end up needing access to the local request context, or they won’t work: url_for, a local current_user.

Draper is a popular rails Decorator gem, whose power is precisely in letting your decorators use Rails helpers from the current context, using the h accessor. h.content_tag, or even h.url_for whatever or h.whatever_path(id).

Draper tries to hide how it provides this ‘h’ object. Which it does by kind of hooking into ActionController to keep  the ‘current controller’ in what’s effectively a global variable.

I’m a bit scared of this kind of magic (might i have been burned in the past by Rails gems doing behind-the-scenes magic with Rails, which stop working, and I’m not capable of debugging my dependencies cause I never understood them? I might have).  And it’s the sort of design that may or may not be thread-safe under various multi-threading scenarios. I’m not saying it’s not — I’m saying it’s just barely complicatd enough that I’d  have to do some serious thinking/analysis to know if it was or not, and I hate thinking.

Draper is possibly quite great, lots of people are using it and happy with it and you should check it out. (I have not used it myself, just looked at the docs and code :) ). But I was curious to see how much trouble Draper’s “magic” really saved me — can we do much of the same thing, with a simpler and more transparent implementation, without it being too ugly?

So Draper’s h, that’s pretty much just what ‘self’ is when you’re in a view render or existing helper, right? (Pretty much, Draper wraps it in a proxy to take care of some edge cases and oddities). What if we just manually pass that to the Decorator? But what if you’re in a controller? Turns out that you can call view_context in a controller too… to give you, oh, maybe it’s an ActionView::Base, or some kind of ActionView::Context, I’m honestly not sure exactly what it is, this aspect of Rails architecture isn’t documented super well and the code is a bit convoluted… but it pretty much just works anyway, or seems to to me.

And this is just barely complicated enough that I feel like encapsulating it in a BaseDecorator class, so I can create many of my own decorators on top. I’m going to use _h with an underscore, instead of Draper’s h, just because it seems safer to me and not that much worse to write.


require 'delegate'
class Base
def foo
"foo"
end
def bar
"bar"
end
end
class BaseDecorator < SimpleDelegator
def initialize(base, view_context)
super(base)
@view_context = view_context
end
def _h
@view_context
end
end
class MyDecorator < BaseDecorator
def new_method
"new_method"
end
def foo
"Overridden #{super}"
end
def a_rails_helper
_h.tag("br")
end
end
class DecoratorTest < ActionView::TestCase
def setup
@base = Base.new
# Hey, ActionView::TestCase gives us a `view` method that
# returns an ActionView::Context, pretty convenient
# for our tests.
@decorated = MyDecorator.new(@base, view)
end
def test_pass_through_methods
assert_equal "bar", @decorated.bar
end
def test_decorator_can_add_method
assert_equal "new_method", @decorated.new_method
end
def test_override_with_super
assert_equal "Overridden foo", @decorated.foo
end
def test_can_access_view_context_method
assert_equal tag("br"), @decorated.a_rails_helper
end
end

view raw

gistfile1.rb

hosted with ❤ by GitHub

(Where to put your decorator classes? Did you know that Rails for a while now has allowed you to create arbitrary directories of whatever name you choose under ./app, and they’ll Just Plain Work, same as app/models, app/controllers? Just create a directory app/decorators if you like, stick things in there, they’ll be on the load path and just work, no extra config or code required to setup.)

How to use it?

We’ve got a model object or a collection of model objects — we don’t even care if they’re ActiveRecord or not — mine actually aren’t, but it doesn’t matter, this method is entirely agnostic about that, which is a nice thing about it. But we want to wrap them our decorators.

From a controller action method:

   @some_obj           = MyDecorator.new(@some_obj, view_context)
   @collection_of_objs = @collection_of_objs.collect do |o|
      MyDecorator.new(@some_obj, o) }
   end

Or from a view template or helper, just use self instead of self.view_context

  @some_obj           = MyDecorator.new(@some_obj, self)
  @collection_of_objs = @collection_of_objs.collect do |o|
    MyDecorator.new(@some_obj, o)
  end

Having to explicitly pass in the view_context or self arg aint’ so bad,  is it? Especially when it means our complete Decorator implementation can still be only a few dozen lines!

More convenient use in Views

Draper’s docs seem to suggest that decorating in the controller is the best way to go.

But for my own uses, it was actually looking more convenient to sometimes be able to decorate at the view layer (template or helper), perhaps deciding what Decorators to use based on helper methods,  or even using different decorators in different partials. At first, not being able to figure out a good way to do this was keeping me from pursuing this whole design.

I didn’t want to end up with ugly view code like this:

## BAD, what I did NOT want my template:
<% decorated = SomeDecorator.new(@model_obj, self) %>
<p>
  <%= decorated.something %>

I mean, really, that’s terrible.

But then while continuing to read up on Decorators, I noticed Ryan Bates “Presenters From Scratch” RailsCast episode, and the freely available sourcecode accompanying the episode had a nice solution.


# some ./app/helpers/some_helper.rb, in my case actually in an engine gem
module SomeGemHelper
def my_gem_decorate(model)
# Here I'm hard-coding to always decorate with MyDecorator,
# but it could also be passed in as a method arg, or guessed
# from the model.class name, or from a differnet model attribute
# like model.presenter_class, or taken from configuration, or
# some combination — whatever meets the needs of your design.
decorated = MyDecorator.new(model, self)
yield if block_given?
decorated
end
end

view raw

gistfile1.rb

hosted with ❤ by GitHub


# Now in any old view template, check this out:
<% my_gem_decorate(@model_obj) do |model_obj| %>
<p><%= model_obj.decorator_method %></p>
<%# Pass it to a partial, the partial gets the decorated
# object, great. %>
<%= render "some_partial", :object => model_obj %>
<% end %>

view raw

gistfile2.rhtml

hosted with ❤ by GitHub

Much better! And only a few more lines of implementation! Oh yeah!

One tweak: html_escape is weird

Oops, as i started to use this, ran into a problem. Inside a decorator, the code ought to be able to call _h.any_helper to call any Rails helper (from the framework, from the local app, from another gem, whatever) in the current context. And it mostly works.

It doesn’t work with the html_escape. Because for some reason html_escape is declared as ruby private method access. I have no idea why ; I don’t even know how, it doesn’t look like it’s being declared private in what looks like it’s source code, but if you try to call _h.html_escape you get a “can’t call private method on another object” exception.

Draper actually makes note of this in a comment too, and makes some architectural designs to try and accomodate that, involving another layer of proxy indirection. Me? Trying to keep it simple, I’d rather treat html_escape just a bit special rather than write a lot of architecture to pretend it ain’t. I just give my BaseDecorator it’s own html_escape, and decorator code calls self.html_escape directly instead of _h.html_escape like the other helpers. Not great, but not worth more than these three lines of extra code.

class BaseDecorator < SimpleDelegator
  # .....

  def html_escape(*args)
    ERB::Util.html_escape(*args)
  end
end

So how’s it working out?

Design-wise, I’m very happy with it. My gem provides it’s own standard decorator for it’s model objects, but provides ways for local host application to specify it’s own decorators (and what contexts they should be used in) — which turns out to be a pretty nice way to allow the local app to over-ride presentational behavior, in nice context-specific polymorphic ways. (The local app will generally subclass the standard decorator and then over-ride some methods, or even add new methods).

And I’m pretty happy with the simplicity of this implementation — I was scared of adding complicated ‘rails magic’, I was scared of ‘too much architecture’, or adding more dependencies to my gem. But I’m pretty happy that I ended up with what seems to be just enough decorator implementation to make my code nice and clean how I wanted, in only a few dozen lines of code!

But is it really enough to work? Am I missing important things to work around weird edge cases that, say, Draper might have, being a much more mature and robust solution? I’m not sure, I very well might be — this code hasn’t even made it into my master branch yet, and the gem it’s going to be a part of isn’t even 1.0 yet, check back in a couple months and see how it went. But I’m feeling optimistic.

I don’t really mind the few compromises made so far, having to explicitly provide the view_context argument, having to treat html_escape differently, having to spend an extra line manually wrapping things in the decorators. I’d almost rather have the transparency, then have a bunch of lines of ‘magic’ code saving me a line or two of fairly readable code here or there.

Does anyone read this far?

I like to think I don’t use too many more words than required,but I tend to write a lot, I like to cover everything.

Is there actually an audience for this kind of long-form writing on Rails tricks, on the web? If you appreciated this article, please let me know in comments.

15 thoughts on “The Simplest Rails Decorator Implementation That Just Might Work

  1. yes, i read all of these. unfortunately this is well over my head, but i am hoping that through osmosis this will all make sense in a year or so ;)

  2. Note that if you use DelegateClass (also provided by the stdlib delegator), you can specify the type of the underlying object. This has some nice benefits if you have expectations of what it will do.

  3. Ah, interesting, thanks Kris. I just tested it, and it doens’t seem that `< DelegateClass(YourClass)` changes #class either. And don't those kinds of rails helpers generally use #model_name, not #class.name? I confess I mostly don't use those aspects of Rails though.

  4. (Those parts of Rails that _do_ depend on #class should probably be considered bad designs and patched, sez me — that’s not duck typing. But yeah, those tend to be the parts I consider ‘too much magic’ getting in the way of me doing what I need in the first place.)

  5. Great post, looking at using this.
    I’m confused by the way you set up your collection of decorated objects. Are you passing the view context in to a new decorator for each object? Will this be expensive if done this way?

  6. michael: Yes, I am passing the view_context to each decorator. The way this kind of decorator works, every individual decorator needs a view_context, yep.

    I can’t think of any reason that ought to be expensive. It’s just passing a reference around, not making a copy of anything. I don’t think it should be expensive to have 10, 20, or even 100 or probably even 1000 references to the view_context. But I have not benchmarked it.

    (Making 1000 decorator objects might be expensive itself if you have a collection that large, whcih would be unusual. But giving each one a reference to the view_context should not, even in the case of a 1000 item collection, be any significant added expense for any reason I can think of.)

Leave a comment