ActiveModel::Serializers

Problem

#as_json in the Model

class SeScorecardCategory < ActiveRecord::Base
  def as_json(options={})
    attributes.merge({
                         questions: se_scorecard_questions_asc.as_json(options),
                         subcategories: subcategories.newest_on_bottom_order.as_json(options)
                     })
  end
end

We can do better!

#as_json in a Presenter

class RequirementsTemplateCategoryPresenter < BasePresenter

  attr_accessor :template_category

  def initialize(template_category)
    @template_category = template_category
  end

  def as_json(opts={})
    template_category.as_json.merge({
                                        template_groups: RequirementsTemplateGroupPresenter.as_collection(template_category.template_groups_root, current_user: opts[:current_user]),
                                        creator_name: template_category.creator.to_s,
                                        created_at_date: template_category.created_at_date,
                                        created_at_time: template_category.created_at_time,
                                        requirement_template_id: template_category.requirement_template.id,
                                        product_category_name: template_category.get_parent_category_name
                                    })

  end
end

We can do better!

class BasePresenter

  include Rails.application.routes.url_helpers

  def self.as_collection(collection, opts={})
    collection.map { |object| self.new(object).as_json(opts) }
  end

  def self.as_presenters_collection(collection)
    collection.map { |object| self.new(object) }
  end

  def link_to(*args, &block)
    ApplicationController.helpers.link_to(*args, &block)
  end

end

rabl

object @project_group => nil
attributes :id, :name, :project_category_id

node :project_id do |project_group|
  project_group.project_category.project.to_param
end

node :created_at_date do |project_group|
  project_group.created_at.to_s
end

node :created_at_time do |project_group|
  project_group.created_at.to_formatted_s(:time)
end

node :can_edit_project do |project_group|
  can?(:edit, @project)
end

child :project_requirements do
  extends 'project_requirements/project_requirement'
end

jbuilder

json.content format_content(@message.content)
json.(@message, :created_at, :updated_at)

json.author do
  json.name @message.creator.name.familiar
  json.email_address @message.creator.email_address_with_name
  json.url url_for(@message.creator, format: :json)
end

if current_user.admin?
  json.visitors calculate_visitors(@message)
end

json.comments @message.comments, :content, :created_at

json.attachments @message.attachments do |attachment|
  json.filename attachment.filename
  json.url url_for(attachment)
end

ActiveModel::Serializers

 

Basic example

class Post < ActiveRecord::Base
end

class PostsController < ApplicationController
  def show
    render json: @post
  end
end

class PostSerializer
  attributes :id, :title, :body, :comments_count
  has_many :comments
end

class CommentSerializer
end

Custom attributes

class RfxSelectedSolutionSerializer < ActiveModel::Serializer
  attributes :id, :final_score, :name

  def final_score
    object.score.final
  end
end

easy delegation for readablitity

class RfxSelectedSolutionSerializer < ActiveModel::Serializer
  attributes :id, :total_questions_count

  def total_questions_count
    rfi_questions.size
  end

  delegate :rfi, to: :object
  delegate :rfi_question, to: :rfi
end

Passing options

class Beta::RfiSolutionsController < ApplicationController
  def show
    render json: @rfi_solution, opportunity_rfi: @rfi_solution.opportunity_rfi
  end
end

Serializing collection

class Beta::RfiSolutionsController < ApplicationController

  def index
    @presenter = RfiSolutionPresenter.new(@rfi, params)
    render json: @presenter.rfi_solutions, each_serializer: RfxSolutionSerializer
  end
end

has_many

class RfxSelectedSolutionSerializer < ActiveModel::Serializer
  attributes :id

  has_many :stakeholders # will use StakeholderSerializer and pass options
  has_many :rfi_categories, serializer: RfiEvaluateRfiCategorySerializer
end

Custom collections

class RfxSelectedSolutionSerializer < ActiveModel::Serializer
  attributes :id

  has_many :stakeholders # will use StakeholderSerializer and pass options

  def stakeholders
    project.view_project_users.limit(6)
  end
end

Caching

class RfxSelectedSolutionSerializer < ActiveModel::Serializer
  attributes :id
  
  delegate :cache_key, to: :object
end

Easy custom keys

class RfxSelectedSolutionSerializer < ActiveModel::Serializer
  attributes :id
  
  def cache_key
    "#{user_cache_key}/#{rfi_cache_key}"
  end
  delegate :cache_key, to: :user, prefix: :true
  delegate :cache_key, to: :rfi, prefix: true
end

Custom scope

class ApplicationController < ActionController::Base
  serialization_scope :current_admin
end

A different user perhaps?

class CitiesController < ApplicationController
  def show
    @city = City.find(params[:id])

    render json: @city, scope: User.most_important
  end
end

And much much more!

Any questions?

ActiveModel::Serializers

By Bartek Kruszczyński

ActiveModel::Serializers

  • 985