A simple and Rubyish view helper for Rails 4, Rails 5, Rails 6, and Rails 7. Keep your helpers and views Object-Oriented!
- automatically mixes decorator module into corresponding model only when:
- passing a model or collection of models or an instance of ActiveRecord::Relation from controllers to views
- rendering partials with models (using
:collection
or:object
or:locals
explicitly or implicitly) - fetching already decorated Active Record model object's association
- the decorator module runs in the model's context. So, you can directly call any attributes or methods in the decorator module
- since decorators are considered as sort of helpers, you can also call any ActionView's helper methods such as
content_tag
orlink_to
-
Ruby 2.1.x, 2.2.x, 2.3.x, 2.4.x, 2.5.x, 2.6.x, 2.7.x, 3.0.x, 3.1.x, 3.2.x, and 3.3 (trunk)
-
Rails 4.2.x, 5.0, 5.1, 5.2, 6.0, 6.1, 7.0, and 7.1 (edge)
ActiveRecord, ActiveResource, and any kind of ORMs that uses Ruby Objects as model objects
- bundle 'active_decorator' gem
- create a decorator module for each AR model. For example, a decorator for a model
User
should be namedUserDecorator
. You can use the generator for doing this (% rails g decorator user
) - Then it's all done. Without altering any single line of the existing code, the decorator modules will be automatically mixed into your models only in the view context.
- Model
class Author < ActiveRecord::Base
# first_name:string last_name:string
end
- Controller
class AuthorsController < ApplicationController
def show(id) # powered by action_args
@author = Author.find id
end
end
- Decorator
module AuthorDecorator
def full_name
"#{first_name} #{last_name}"
end
end
- View
<%# @author here is auto-decorated in between the controller and the view %>
<p><%= @author.full_name %></p>
- Models
class Author < ActiveRecord::Base
# name:string
has_many :books
end
class Book < ActiveRecord::Base
# title:string url:string
belongs_to :author
end
- Controller
class AuthorsController < ApplicationController
def show(id)
@author = Author.find id
end
end
- Decorator
module BookDecorator
def link
link_to title, url
end
end
- View
<p><%= @author.name %></p>
<ul>
<% @author.books.order(:id).each do |book| %>
<%# `book` here is auto-decorated because @author is a decorated instance %>
<li><%= book.link %></li>
<% end %>
</ul>
Sometimes you may want to use decorators outside of Action View, for example,
for background tasks for ActiveJob.
For such use case, ActiveDecorator module provides run_with
method
that takes some kind of Action View and a block.
ActiveDecorator::ViewContext.run_with ApplicationController.new.view_context do
## perform some heavy jobs here
end
You can test a decorator using your favorite test framework by decorating the model instance with
ActiveDecorator::Decorator.instance.decorate(model_instance)
Considering an Organization
model and its simple decorator:
module OrganizationDecorator
def full_name
"#{first_name} #{last_name}"
end
end
An RSpec test would look like:
describe '#full_name' do
it 'returns the full organization name' do
organization = Organization.new(first_name: 'John', last_name: 'Doe')
decorated_organization = ActiveDecorator::Decorator.instance.decorate(organization)
expect(decorated_organization.full_name).to eq('John Doe')
end
end
By default, ActiveDecorator searches a decorator module named target_class.name + "Decorator"
If you would like a different rule, you can configure in your initializer.
ActiveDecorator.configure do |config|
config.decorator_suffix = 'Presenter'
end
- Fork, fix, then send me a pull request.
Copyright (c) 2011 Akira Matsuda. See MIT-LICENSE for further details.