ruby on rails 4 - Does ActiveModel::Serializer require an explicit render call? -


i know when using view templates (html, rabl), don't need explicit render call in controller action because default, rails renders template name corresponding controller action name. concept (not caring rendering in controller code) , therefore wonder whether possible when using activemodel::serializers?

example, code generated controller (rails 4.1.0):

class productscontroller < applicationcontroller   before_action :set_product, only: [:show, :edit, :update, :destroy]    #other actions   # /products/1   # /products/1.json   def show   end end 

and serializer:

class productserializer < activemodel::serializer   attributes :id, :name, :description, :url, :quantity, :price end 

hitting /products/1.json, expect 2 things happen:

  1. fields not listed in serializer ommited,
  2. whole json object incapsulated within 'product' top level field.

however, not happen, whole serializer ignored. if modify show method following:

# /products/1 # /products/1.json def show   @product = product.find(params[:id])   respond_to |format|     format.html     format.json { render json: @product }   end end 

and fine, have lost benefit of before_action filter (and seems me have redundant code).

how should done?

without explicit render or respond_with or respond_to rails matching template. if template not exist rails throws error.

however, can create own resolver bypass this. instance, suppose created app\models\serialize_resolver.rb , put it:

class serializeresolver < actionview::resolver   protected   def find_templates(name, prefix, partial, details)     if details[:formats].to_a.include?(:json) && prefix !~ /layout/       instance = prefix.to_s.singularize       source = "<%= @#{instance}.active_model_serializer.new(@#{instance}).to_json.html_safe %>"       identifier = "serializeresolver - #{prefix} - #{name}"       handler = actionview::template.registered_template_handler(:erb)       details = {         format: mime[:json],         updated_at: date.today,         virtual_path: "/#{normalize_path(name, prefix)}"       }       [actionview::template.new(source, identifier, handler, details)]     else       []     end   end    def normalize_path(name, prefix)     prefix.present? ? "#{prefix}/#{name}" : name   end end 

and then, in either application controller (or in individual controller) place:

  append_view_path ::serializeresolver.new 

with should able want. if json request, create erb template right content , return it.

limitations:

  • this bit clunky because relies on erb, not needed. if have time create simple template handler. can invoke without erb.
  • this wipe out default json response.
  • it relies on controller name find instance variable (/posts converted @post.)
  • i've tested little. logic smarter.

notes:

  • if template present, used first. allows override behavior.
  • you can't create new renderer , register it, because default process doesn't hit it. if template not found, error. if file found, goes straight invoking template handler.

Comments