How It Works

When To Use It

Example

ActionController::Routing::Routes.draw do |map|

map.with_options :controller => 'order' do |route|

route.complete "complete", :action => "complete"

route.thank_you "thank_you", :action => "thank_you"

end

...

end

class CreateModels < ActiveRecord::Migration

def self.up

create_table :user_accounts do |t|

t.column :name, :string

end

create_table :addresses do |t|

t.column :line_1, :string

t.column :line_2, :string

t.column :city, :string

t.column :state, :string

t.column :zip_code, :string

end

create_table :user_credentials do |t|

t.column :username, :string

t.column :password, :string

end

end



def self.down

drop_table :user_accounts

drop_table :addresses

drop_table :user_credentials

end

end

class Address < ActiveRecord::Base

end



class UserAccount < ActiveRecord::Base

end



class UserCredential < ActiveRecord::Base

end

<% form_for :presenter do |form| %>

<table>

<tr><td colspan="2">Billing Information:</td></tr>

<tr>

<td>Name</td>

<td><%= form.text_field :name %></td>

</tr>

<tr>

<td>Address Line 1</td>

<td><%= form.text_field :line_1 %></td>

</tr>

<tr>

<td>Address Line 2</td>

<td><%= form.text_field :line_2 %></td>

</tr>

<tr>

<td>City</td>

<td><%= form.text_field :city %></td>

</tr>

<tr>

<td>State</td>

<td><%= form.text_field :state %></td>

</tr>

<tr>

<td>Zip Code</td>

<td><%= form.text_field :zip_code %></td>

</tr>

<tr><td colspan="2">Account Information:</td></tr>

<tr>

<td>Username</td>

<td><%= form.text_field :username %></td>

</tr>

<tr>

<td>Password</td>

<td><%= form.text_field :password %></td>

</tr>

</table>

<%= submit_tag "Complete Order" %>

<% end %>

class OrderController < ApplicationController

def complete

@presenter = CompletePresenter.new(params[:presenter])

redirect_to thank_you_url if request.post? && @presenter.save

end



def thank_you

end

end

class CompletePresenter < Presenter

def_delegators :user_account, :name, :name=

def_delegators :address, :line_1, :line_2, :city, :state, :zip_code

:line_1=, :line_2=, :city=, :state=, :zip_code=

def_delegators :user_credentials, :username, :password, :username=, :password=



def user_account

@user_account ||= UserAccount.new

end



def address

@address ||= Address.new

end



def user_credentials

@credentials ||= UserCredential.new

end



def save

user_account.save && address.save && user_credentials.save

end

end

class Presenter

extend Forwardable



def initialize(params)

params.each_pair do |attribute, value|

self.send :"#{attribute}=", value

end unless params.nil?

end

end

The default architecture for Ruby on Rails, Model View Controller, can begin to break down as Controllers become bloated and logic begins to creep into view templates. The Presenter pattern addresses this problem by adding another layer of abstraction: a class representation of the state of the view.Presenter was inspired by the various GUI patterns documented by Martin Fowler The Presenter pattern addresses bloated controllers and views containing logic in concert by creating a class representation of the state of the view. An architecture that uses the Presenter pattern provides view specific data as attributes of an instance of the Presenter. The Presenter's state is an aggregation of model and user entered data.As non-trivial applications grow Controllers can approach sizes that compromise maintainability. Complex controller actions may require instantiation of multiple objects and take on the responsibility of aggregating data from various objects. A Presenter can encapsulate this aggregation behavior and leave the controller with more focused responsibilities. This also allows for testing of aggregation or calculation behavior without a dependency on setting up a Controller to a testable state.Since views are templates they can also contain behavior. The most common behavior you encounter in a view is formatting; however, since the view is a template you will occasionally see computations or worse. Because this logic is stored in a template it can be problematic to test. Presenters address this problem by pulling all formatting, computation, and any additional behavior into a class that can be easily tested.Presenters do add an additional layer, and thus more complexity. A presenter is not likely to be a automatic decision or standard. Presenters are generally introduced when actions are required to act upon various models or the data in the database needs to be manipulated in various ways before it is displayed in the view.The example used to demonstrate a usage of the Presenter pattern is a page that allows you to enter your address information, personal information, and user credentials.To get to our example page (and the following page) we'll need to add a few routes to routes.rb.The example relies on three different models: address, user_account, user_credential. The migration for creating these tables is straightforward.The models need not contain any behavior for our example.The template for our example view is also straightforward, and this is one of the large benefits for using a presenter.And, the last example before we dive into the Presenter will be the controller. The controller is also simple, thus maintainable, due to the usage of the Presenter.Finally, the CompletePresenter aggregates all the data for the view.The CompletePresenter does inherit from Presenter, but only to get Forwardable behavior and a constructor that allows you to create an instance with attributes set from a hash.By using the presenter an easily testable layer has been created. This additional layer can coordinate with the models that this view is responsible for. The added layer also allows the models to be tested independent of any controller behavior. The presenter also provides the ability for extension where other solutions prove inadequate. For example, in a real scenario, the models would also likely contain validations. The presenter provides a layer that can validate the various models and merge their errors collections to provide one error collection that the view can work with.Used appropriately, Presenters greatly benefit an application's architecture and maintainability