Browse the Ruby on Rails Community.

You are here: Browse Railsplugins Simply Presentable

Simply Presentable

= SimplyPresentable

Save keystrokes and stay DRY with SimplyPresentable:

<% present!(foo).form do |f| %>
  <%= f.text_field :bar %>
<% end %>

Instead of

<% form_for(
     foo.new_record? ? {
       :url => 'http://example.com/foos',
       :html => { :method => 'post', :id => 'new_foo', :class => 'new_foo' }
     } : 
     {
        :url => "http://example.com/foos/#{foo.id}",
        :html => { :method => 'put', :id => 'foo_1', :class => 'foo' }
      } :
    ) do |f| %>
  <%= f.text_field :bar %>
<% end %>

SimplyPresentable is a plugin for that introduces the idea of presenters to rails:

http://martinfowler.com/eaaDev/PresentationModel.html

Rails helpers are functional, and organized around controllers, not domain objects. Helper functions that are intended to operate on data types that span controllers end up being in the ApplicationHelper, or packaged into modules and included in other helpers.

SimplyPresentable attempts to fill the OO void left by a functional helper only approach to presentation:

<%= link_to 'Show Foo', present!(@foo).url %>

In this example, the present! method (added to ActionController::Base and ActionView::Base) is called on @foo, and instance of a Foo object. When present is called:

1. The appropriate presenter(s) is located for foo based on its class 2. The public methods of the presenter are delegated from the foo instance to the presenter 3. the foo object is returned

Some might consider this a violation of MVC, since the interface for the presenter is exposed by the domain object. However MVC is meant to separate IMPLEMENTATION, not interface. You would never want the implementation of a domain model class to depend on the way in which it will be displayed. SimplyPresentable avoids this, by defining all presentation logic in a separate class. The interface for the presenter class is exposed through the domain object as a matter of convenience. The present! method could have been implemented such that it returned the presenter object. I avoided this implementation as it would have lead to more local variables (a presenter and a model) and leaves the user with more to think about (it is also less DRY).

Installation

script/plugin install http://richcollins.net/svn/simply_presentable

Running Tests

rake test:plugins

Code Coverage

RCOV script/plugin install http://svn.codahale.com/rails_rcov rake test:plugins:rcov

Heckle sudo gem install heckle --include-dependencies

Documentation

http://simply_presentable.richcollins.net/

Core Classes

The presenters included in the plugin are mainly oriented around the ActiveRecord::Base class. There are many instances where information about domain objects is used to control a RESTful rails application. SimplyPresentable is strongly influenced by the idea of convention over configuration. This philosophical choice is apparent in the three core presenters.

= SimplyPresentable::ActiveRecordUrlPresenter

SimplyPresentable::ActiveRecordUrlPresenter makes it easy to generate urls based on ActiveRecord::Base instances and named routes. For instance:

routes.rb:

map.resources :foos do |foo|
  map.resources :bars
end

in the template / controller:

present!(bar).url => http://yourdomain.com/foos/1/bars/2

Compare to named url functions:

bar_url(:id => bar, :bar_id => bar.foo)

This assumes a few things:

1. bar has a method named foo that will return the foo associated with bar (belongs_to association most likely) 2. bar has an id of 2 3. foo as an id of 1

The url helper also includes the methods new_url, edit_url and collection_url.

There are additional options available for name_prefixes, actions and other url customization features. See the documentation for SimplyPresentable::ActiveRecordUrlPresenter.

The SimplyPresentable::ActiveRecordUrlPresenter interface is used by:

  • SimplyPresentable::ActiveRecordFormPresenter

=== SimplyPresentable::ActiveRecordPartialPresenter

SimplyPresentable::ActiveRecordPartialPresenter makes it easier to render partials based on an ActiveRecord::Base instance:

present!(Foo.new).render

This will render foos/_new.rhtml

present!(Foo.find(:first)).render

This will render foos/_show.rhtml

Compare to render method:

<%= render(:partial => 'foos/' + foo.new_record? ? 'new' : 'show', :locals => { :foo => foo }) %>

You can also change the partial:

present!(foo).render(:existing => 'edit')

OR

present!(foo).render(:partial => 'edit')

This will render foos/_edit.rhtml

You can also get partial options to be used elsewhere: present!(Foo.find(:first)).partial_options => { :partial => ‘foos/show’, :foo => the_foo_instance }

The SimplyPresentable::ActiveRecordPartialPresenter interface is used by:

  • SimplyPresentable::ActiveRecordPrototypePresenter

=== SimplyPresentable::ActiveRecordDomPresenter

SimplyPresentable::ActiveRecordDomPresenter makes it easy to name and control dom and css elements based on an ActiveRecord::Base instance:

present!(Foo.new).dom_id
=> new_foo
present!(Foo.find(:first)).dom_id
=> foo_1
present!(Foo.find(:first)).dom_class
=> foo

The SimplyPresentable::ActiveRecordPartialPresenter interface is used by:

  • SimplyPresentable::ActiveRecordFormPresenter
  • SimplyPresentable::ActiveRecordPrototypePresenter

=== SimplyPresentable::ArrayPartialPresenter

SimplyPresentable::ArrayPartialPresenter makes it easier to render partials based on an Array instance:

present!(Foo.new, Foo.find(:first)).render_each(:spacer_template => 'foos/between')

This will call render on each Foo instance. The spacer template will be rendered and the results obtained by rendering the Foos will be joined with the spacer template

present!(Foo.new, Foo.find(:first)).render_as('foos')

This will render the template foos/index with the array bound to the variable foos.

== Extensibility

The plugin expects a directory, RAILS_ROOT/app/presenters that will contain application specific presenters. These presenters will be loaded on each request during development mode. All presenters must inherit from SimplyPresentable::Presenter

You can declare which classes the presenter should be used with:

used_for :foo

This will expose the public methods of the presenter through any Foo object (or any of its subclasses) that is included as an argument to the present! method.

You can also tell the presenter to skip a class:

not_used_for :bar

This is useful when you have a subclass that should use a different (or no) presenter:

class Foo
end
class Bar < Foo
end
class FooPresenter
  used_for :foo
  not_used_for :bar
end

If there is a “presenter collision”, the subclass always wins:

class FooPresenter
  used_for :foo
end
class AnotherFooPresenter < FooPresenter
  used_for :foo
end

In this situation, AnotherFooPresenter will be exposed through the foo object, but FooPresenter will not. If presenters do not share the same inheritance lineage, they will both be exposed.

= Credits

Adam Thorsen helped me extensively with testing and other feedback.

This plugin was inspired by its functional counterpart, simply_helpful:

http://dev.rubyonrails.org/browser/plugins/simply_helpful

= Contact

richcollins@gmail.com http://richcollins.net/

NOTE: This description has been extracted from the Plugin README and so the formatting may need updating to make browser friendly