Have you had a problem that it's impossible to understand what methods/attributes are called in your Views? With ActiveModelPresenter you will have a full control of data that is exposed to views. Presenters (Model-View-Presenter, Clean Architecture) is one of the most useful patterns to improve supportability of your rails app.
- List of exposed fields is explicitly defined
- Presenters (serializers) can be an additional isolation layer for a view/frontend/api-related logic
- Makes you 1 step closer to exposing a JSON API or allows you to DRY your code if you already do.
- Easier testing - it's possible to unit-test a Presenter and check all attributes instead of greping an html.
- DRY - Same as Service Objects they allow you to keep domain-related logic in 1 place and have an easy access to it
- All queries and calculations happen in a presenter so no more crazy rendering time.
- Everything is calculated only once. So you don't need to write memoizations like
@foo ||= 2**100
. It means caching a view will not make much sense
- Presenter acts like a plain static ruby object but can be converted to a hash or a json at any moment.
- Makes it easy to pass options to a serializer
- Based on time-tested active_model_serializers gem
- Collections are compatible with paginators like
will_paginate
andkaminari
gems.
- fields filtering - serialize/present only a subset of attributes that are listed in a serializer
- ability to override/extend methods from a model
- Caching - just add
cache: true
in your serializer. It checkes anupdated_at
attribute to refresh
-
Add the gem name to your Gemfile
gem 'active_model_presenter'
-
Define the
-
Call presenter in your action
def show user = User.find(params[:id]) @user = UserPresenter.show(user) end
Collections are handled in a same way.
@users = UserPresenter.list(users)
Here presenter returns a
ActiveModelPresenter::Collection
object, which is compatible with paginators.
See more information about serializers here about serializers syntax.
Presenter:
# /app/presenters/post_presenter.rb
class PostPresenter < ActiveModelPresenter::Base
def show(post)
present(post, [:id, :title, :content, :comments_count]) do |result, model|
result.comments = present(model.comments, [:message, :author_name])
end
end
def list(posts)
present(posts, [:id, :title, :content, :comments_count])
end
end
present
supports a block where you can make extend your presented object.
Presenters are using regular ActiveModelSerializer classes, like these:
# /app/serializers/post_serializer.rb
class PostSerializer < ActiveModel::Serializer
attributes :id, :title, :content, :comments_count
has_many :comments
def comments_count
comments.count
end
end
# /app/serializers/comment_serializer.rb
class CommentSerializer < ActiveModel::Serializer
attributes :id, :message, :author_name
def author_name
object.user.name
end
end
Controller:
# /app/controllers/posts_controller.rb
class PostsController < ApplicationController
def show
post = Post.find(params[:id])
@post = PostPresenter.show(post)
end
end
` Views for show/index are same as default ones, but they can access only defined attributes.
# /app/views/posts/show.html.erb
<h1><%= @post.title %></h1>
<div><%= @post.content %></div>
<h2>Comments (<%= @post.comments_count %>): </h2>
<div>
<% @post.comments.each do |comment| %>
<div>
<div><%= comment.author_name %></div>
<div><%= comment.message %></div>
</div>
<% end %>
</div>
Method present
accepts 3 attributes
1. Model or an object that inherits ActiveModelSerializers::Model
2. Array of fields to present. You can set it to nil
to present all fields but it's not recommended.
3. Hash of params for serialization and for serializer instance. Available:
1.1 instance_options: {foo: :bar}
. It will be available in the serializer methods as instance_options
We encourage you to create issues and to contribute to ActiveModelPresenter! Please discuss your ideas with the authors first.
ActiveModelPresenter is released under the MIT License.