MetaPresenter is a Ruby gem for writing highly focused and testable Rails view presenter classes. For each controller/action pair you get a presenter class in app/presenters
that you can use in your views with with presenter.method_name
. This helps you decompose your helper logic into tight, easily testable classes. There's even a DSL for method delegation on objects to reduce boilerplate.
Installation
- Add this line to your application's Gemfile:
gem 'meta_presenter'
- Bundle from the command line:
$ bundle
- Include MetaPresenter::Helpers in your controller or mailer:
class ApplicationController < ActionController::Base
include MetaPresenter::Helpers
end
class ApplicationMailer < ActionMailer::Base
include MetaPresenter::Helpers
end
Example
Say you have a PagesController with #home
and #logs
actions. Underneath app/presenters you can add a presenter class for each action (Pages::HomePresenter and Pages::LogsPresenter). We'll also create an ApplicationPresenter superclass for methods that can be used in any action throughout the app.
app/
controllers/
application_controller.rb
pages_controller.rb
presenters/
application_presenter.rb
pages_presenter.rb
pages/
home_presenter.rb
logs_presenter.rb
views
pages
home.html.erb
logs.html.erb
spec/ (or test/)
presenters/
application_presenter_spec.rb
pages_presenter_spec.rb
pages/
home_presenter_spec.rb
logs_presenter_spec.rb
app/controllers/page_controller.rb
class ApplicationController < ActionController::Base
include MetaPresenter::Helpers
# Controller methods automatically become available in views and other presenters.
# So this gives you presenter.current_user in views, and you can call `current_user`
# within your presenters as well
def current_user
User.first
end
end
app/controllers/dashboard_controller.rb
class ApplicationController < ActionController::Base
def home
end
def logs
end
private
# presenter.logs in views
def logs
Log.all
end
end
app/presenters/application_presenter.rb
class ApplicationPresenter < MetaPresenter::BasePresenter
# Makes presenter.page_title available in all of your app's views
def page_title
"My App"
end
# presenter.last_login_at in views
def last_login_at
# controller methods from within the same scope
# as the presenter are directly available
current_user.last_login_at
end
end
app/presenters/pages_presenter.rb
class PagesPresenter < ApplicationPresenter
# Makes presenter.nav_items available for
# all actions on PagesController
def nav_items
[
{name: "Home", path: home_path},
{name: "Logs", path: logs_path}
]
end
end
app/presenters/pages/home_presenter.rb
class Pages::HomePresenter < PagesPresenter
# presenter.email, presenter.id or any other
# method not already defined will delegate to
# the current_user
delegate_all_to = :current_user
# presenter.greeting in views
def greeting
"Hello, #{current_user.name}"
end
end
app/views/pages/home.html.erb
<h1>Home</h1>
<p><%= presenter.greeting %></p>
<p>Last login <%= distance_of_time_in_words_to_now(presenter.last_login_at) %></p>
app/presenters/pages/logs_presenter.rb
class Pages::LogsPresenter < PagesPresenter
# presenter.size and presenter.last will delegate to
# the controller's private `#logs`
delegate :size, :last, to: :logs
# presenter.log_text(log) in view
def log_text(log)
log.description
end
end
app/views/pages/logs.html.erb
<h1>Logs</h1>
<p>Num logs: #{presenter.size}</p>
<p>Last log: #{presenter.log_text(presenter.last)}</p>
<ul>
<% presenter.logs.each do |log| %>
<li>
<%= presenter.log_text(log) %>
</li>
<% end %>
</ul>
Aliasing the presenter methods
If you want to customize the presenter
method you can specify a shorthand by adding an alias_method to your controller or mailer:
class ApplicationController < ActionController::Base
including MetaPresenter
# So convenient!
alias_method :presenter, :pr
end
Requirements
MetaPresenter supports Ruby >= 2.1 and ActionPack/ActionMailer >= 3.0.12. If you'd like to help adding support for older versions please submit a pull request with passing specs.
Links
Specs
To run the specs for the currently running Ruby version, run bundle install
and then bundle exec rspec
. To run specs for every supported version of ActionPack, run bundle exec appraisal install
and then bundle exec appraisal rspec
.
Gem release
Make sure the specs pass, bump the version number in meta_presenter.gemspec, build the gem with gem build meta_presenter.gemspec
. Commit your changes and push to Github, then tag the commit with the current release number using Github's Releases interface (use the format vx.x.x, where x is the semantic version number). You can pull the latest tags to your local repo with git pull --tags
. Finally, push the gem with gem push meta_presenter-version-number-here.gem
.
TODO
- create an example app and link to the repo for it in this README
- proofread the README instructions to make sure everything is correct
- optional
rake meta_presenter:install
that generates the scaffolding for you. Or, you can manually create the files you want. - add support for layout-level presenters
- add Rails 6 support once it comes out (hopefully just have to add a gemfiles/actionpack6.gemfile and it will run with the Appraisal suite)
Contributing
- Fork it
- Create your feature branch (
git checkout -b feature/my-new-feature
) or bugfix branch (git checkout -b bugfix/my-helpful-bugfix
) - Commit your changes (
git commit -am 'Add some feature'
) - Push to the branch (
git push origin feature/my-new-feature
) - Make sure specs are passing (
bundle exec rspec
) - Create new Pull Request
License
See the LICENSE file.