You are here: Browse Railsplugins 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).
Installationscript/plugin install http://richcollins.net/svn/simply_presentable
Running Testsrake test:plugins
Code CoverageRCOV script/plugin install http://svn.codahale.com/rails_rcov rake test:plugins:rcov
Heckle sudo gem install heckle --include-dependencies
Documentationhttp://simply_presentable.richcollins.net/
Core ClassesThe 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::ActiveRecordUrlPresenterSimplyPresentable::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::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::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::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