Introducing ActivePresenter: The presenter library you already know.
Presenters were a hot topic in the rails community last year. A lot of prominent bloggers wrote about using them, and the implementations they had come up with. Oddly, though, when I needed one a couple of weeks ago, I was unable to find a suitable implementation. Lots of articles — no code.
Let's answer the question on everybody's mind before we move on. Feel free to skip ahead if you already know the answer.
WTF is a presenter?!
In its simplest form, a presenter is an object that wraps up several other objects to display, and manipulate them on the front end. For example, if you have a form that needs to manipulate several models, you'd probably want to wrap them in a presenter.
Indeed, attribute_fu solves this problem for some cases. However, when you're dealing with unrelated models, or, really, any situation other than a parent saving its children, you're probably better off using a presenter.
Presenters take the multi model saving code out of your controller, and put it in to a nice object. Because presenter logic is encapsulated, it's reusable, and easy to test.
Want one?
ActivePresenter
Daniel and I wrote most of this on the train ride over to RubyFringe. It is an ultra-simple presenter base class that is designed to wrap ActiveRecord models (and, potentially, others that act like them).
ActivePresenter works just like an ActiveRecord model, except that it works with multiple models at the same time. Let me show you.
Imagine we've got a signup form that needs to create a new User, and a new Account. We'd create a presenter that looks like this.
class SignupPresenter < ActivePresenter::Base
presents :user, :account
end
Then, we'd write a new action like this one:
def new
@signup_presenter = SignupPresenter.new
end
And a form:
<%= error_messages_for :signup_presenter %>
<%- form_for @signup_presenter, :url => signup_url do |f| -%>
<%= f.label :account_subdomain, "Subdomain" %>
<%= f.text_field :account_subdomain %>
<%= f.label :user_login, "Login" %>
<%= f.text_field :user_login %>
<%- end -%>
A create action:
def create
@signup_presenter = SignupPresenter.new(params[:signup_presenter])
if @signup_presenter.save
redirect_to dashboard_url
else
render :action => "new"
end
end
Lastly, an update action:
def update
@signup_presenter = SignupPresenter.new(:user => current_user, :account => current_account)
if @signup_presenter.update_attributes(params[:signup_presenter])
redirect_to dashboard_url
else
render :action => "edit"
end
end
Seem familiar?
If you're using r_c, most of this comes for free with:
class SignupsController < ResourceController::Base
model_name :signup_presenter
end
For more on complex forms in rails, and the presenter pattern, see my upcoming PeepCode screencast!
Organization
I have been sticking my presenters in app/presenters. If you want to do the same, you'll need to add a line like this to your environment.rb:
config.load_paths += %W( #{RAILS_ROOT}/app/presenters )
Get It!
As a gem:
$ sudo gem install active_presenter
As a rails gem dependency:
config.gem 'active_presenter'Or get the source from github:
$ git clone git://github.com/giraffesoft/active_presenter.git
(or fork it at http://github.com/giraffesoft/active_presenter)
Also, check out the RDoc.