The html times

Elegantly Powered by Google

Presenters in Ruby on Rails Applications by Joshua Clayton November 26th, 2008 Delicious



Download the demo App



Presenters in Ruby on Rails Applications

MVC (Usually) Works

I am a firm believer that the MVC pattern works very well in terms of web applications. The controller manages routing and all the request types (hopefully RESTfully), the views display the data (whether it be for a standard web browser, an iPhone, or an output to PDF), and the model interacts with the data layer so users can use the application. The Ruby on Rails framework follows the MVC pattern, pretty closely, but there are other inner workings that don’t fall directly into that. Some examples are mailers, observers, and presenters. Instead of muddying up one (or many) areas of the pattern, we as developers pull out this code into separate models to allow for easier testing, readability, and code maintainability.

PORO or Otherwise?

Presenters are, in my terms, a PORO that follows the decorator pattern (adding additional methods when necessary), although programmers I’ve spoken to have different feelings. Zach Dennis, a programmer for Mutually Human Software, has written a very nice caching presenter plugin and believes that a presenter should follow the adapter pattern, which would encourage a programmer not to call methods inherited from ActiveRecord::Base (like save and update_attributes). Either stance you take, the discussion is there and will benefit developers regardless.

What’s the Point?

Presenters are nice if you have any sort of display logic in your code, want to remove logic from the view, or find yourself checking lots of permissions for display purposes in a view. For example, let’s say you’ve got a forum application and have permissions checking within the controller to determine if a user can post to a topic. You’d probably write some type of permissions checking like this:

!@topic.locked && current_user

This would ensure that, if someone tries to post to this page, it ensures that the user posting is logged in (through the current_user call) and that the topic is not locked. Now, what about when you want to conditionally display a text box within the view, performing the same call? This isn’t something you’d want in the controller, since you end up forcing the view to rely on this permission check, and it’s not appropriate in the model because it depends on current_user. This is where a presenter comes in.

The presenter in this case would be a TopicPresenter and would need two bits of information: the topic being viewed and the current user. I prefer to name this presenter instance @topic (instead of @topic_presenter) because the view shouldn’t know that it’s dealing with a presenter. Again, following the decorator pattern, the @topic should behave exactly like the Topic class that inherits from ActiveRecord::Base. Within the TopicPresenter class, we’ll add instance methods that wrap permissions up into descriptive methods that will be available in both the view and controller.

Let’s See It in Action

Here’s my TopicPresenter class in a forum application:
class TopicPresenter
  delegate :id, :class, :errors, :to_param, :new_record?, :respond_to?, :is_a?, :to => :@topic

  def initialize(options = {})
    options.assert_valid_keys(:topic, :current_user)
    @topic = options[:topic]
    @current_user = options[:current_user]
  end

  def can_be_edited_by_current_user?
    @topic.creator == @current_user
  end

  def can_be_locked_by_current_user?
    is_current_user_admin?
  end

  def can_be_stickied_by_current_user?
    is_current_user_admin?
  end

  def can_be_replied_to_by_current_user?
    !@topic.locked? && @current_user
  end

  def method_missing(call, *args)
    @topic.send call, *args
  end

  private

  def is_current_user_admin?
    !!(@current_user && @current_user.has_privilege?(:admin))
  end
end

Starting at the top, we see (as I stated before) that it’s a Plain Old Ruby Object. We go ahead and delegate a handful of methods to the :topic that’s passed into the constructor. The constructor then ensures that only :topic and :current_user are passed in as keys of the hash, and we assign both to instance variables on the presenter. We don’t make these publicly available because it would cause confusion if you call @topic.topic or @topic.current_user within the view. These are already available as @topic and current_user in the view anyway, so this is just one extra precaution. Following the constructor are a handful of instance methods that check different things; if the current user can edit the topic, if the current user can lock or flag the topic as sticky, etc. Finally, we use method_missing to pass anything else to the topic passed into the constructor. This presenter is supposed to be transparent to the developer, so it should walk, talk, and quack like the ActiveRecord Topic only.

This now allows permission checking in the view. For example, I conditionally render a partial that contains my reply form based on permission checking in the presenter:

<%= render :partial => "posts/reply_slider", :locals => {:forum => @forum, :topic => @topic} if @topic.can_be_replied_to_by_current_user? %>

Within my PostsController (which uses the mhs_authentication_system and resource_controller plugins):

restrict_to :only => [:create] do
  parent_object.can_be_replied_to_by_current_user?
end

You can hook these presenters into any views that you’d like; I typically construct them within a before_filter to ensure that I cover all my bases. Aside from permissions checking, they’re a great way to encapsulate data that would go in a drop-down list or conditionally display sections of a page based on the object’s state. This keeps as much logic out of the view and controller, which will in turn make your views a lot easier to update (if permissions change) or add to later (if another developer is brought onto the project).

Pass the Test

If you write tests, you know that logic within the views and controllers is a pain to write tests against; encapsulating this in a presenter is quite ideal. By stubbing out return values on permissions, you can very easily check each case without having to stub the various methods that get called. Presenter tests will soon be the only place you test view/controller logic and will DRY up your tests considerably.

One Bit of Advice

With great power comes great responsibility. If you’re performing any type of manipulation in the presenter, you’re doing something wrong. Let me say this again so you truly understand it: if you are performing any type of data manipulation within the presenter, YOU ARE WRONG. The presenter is only to decorate the model with additional methods to be used within the view and controller and nothing more. Imagine it as a view helper if you need to, but do not manipulate, modify, or change data within the class. I can guarantee that it will defeat the purpose of encapsulating view/controller logic.

Finally…

If you don’t understand how to apply presenters within your application, dig through the code within my demo application. I keep it up to date on github at /fuzzy-monster. Examples always help, and a working application that employs the concepts I’ve covered will go a long way in helping to understand presenters.


Comments

Comments are now closed

Related Articles:

Open Source Debugger

By Rick Waldron

Check out the new and improved A.Unit debugging software.
Read more …


Information Wants To Be Free

Elegantly Powered by Google