Search code examples
jsonruby-on-rails-5active-model-serializers

How do I cause ActiveModelSerializers to serialize with :attributes and respect my key_transform?


I have a very simple model that I wish to serialize in a Rails (5) API. I want to produce the resulting JSON keys as CamelCase (because that's what my client expects). Because I expect the model to increase in complexity in future, I figured I should use ActiveModelSerializers. Because the consumer of the API expects a trivial JSON object, I want to use the :attributes adapter. But, I cannot seem to get AMS to respect my setting of :key_transform, regardless of whether I set ActiveModelSerializers.config.key_transform = :camel in my configuration file or create the resource via s = ActiveModelSerializers::SerializableResource.new(t, {key_transform: :camel}) (where t represents the ActiveModel object to be serialized) in the controller. In either case, I call render json: s.as_json.

Is this a configuration problem? Am I incorrectly expecting the default :attributes adapter to respect the setting of :key_transform (this seems unlikely, based on my reading of the code in the class, but I'm often wrong)? Cruft in my code? Something else?

If additional information would be helpful, please ask, and I'll edit my question.

Controller(s):

class ApplicationController < ActionController::API

  before_action :force_json

  private

  def force_json
    request.format = :json
  end
end

require 'active_support'
require 'active_support/core_ext/hash/keys'
class AvailableTrucksController < ApplicationController

  def show
    t = AvailableTruck.find_by(truck_reference_id: params[:id])
    s = ActiveModelSerializers::SerializableResource.new(t, {key_transform: :camel})
    render json: s.as_json
  end
end

config/application.rb

require_relative 'boot'
require 'rails/all'

Bundler.require(*Rails.groups)

module AvailableTrucks
  class Application < Rails::Application
    config.api_only = true
    ActiveModelSerializers.config.key_transform = :camel
    # ActiveModelSerializers.config.adapter = :json_api
    # ActiveModelSerializers.config.jsonapi_include_toplevel_object = false
  end
end


class AvailableTruckSerializer < ActiveModel::Serializer
  attributes :truck_reference_id, :dot_number, :trailer_type, :trailer_length, :destination_states,
             :empty_date_time, :notes, :destination_anywhere, :destination_zones

end

Solution

  • FWIW, I ended up taking an end-around to an answer. From previous attempts to resolve this problem, I knew that I could get the correct answer if I had a single instance of my model to return. What the work with ActiveModel::Serialization was intended to resolve was how to achieve that result with both the #index and #get methods of the controller.

    Since I had this previous result, I instead extended it to solve my problem. Previously, I knew that the correct response would be generated if I did:

      def show
        t = AvailableTruck.find_by(truck_reference_id: params[:id])
        render json: t.as_json.deep_transform_keys(&:camelize) unless t.nil?
      end
    

    What had frustrated me was that the naive extension of that to the array returned by AvailableTruck.all was failing in that the keys were left with snake_case.

    It turned out that the "correct" (if unsatisfying) answer was:

      def index
        trucks = []
        AvailableTruck.all.inject(trucks) do |a,t|
          a << t.as_json.deep_transform_keys(&:camelize)
        end
        render json: trucks
      end