how to dynamic add attributes on Active Model Serializers

newBike picture newBike · Nov 15, 2014 · Viewed 15k times · Source

I want to decide numbers of attributes to output in my controller.

But I have no idea have to do it?

controller.rb

  respond_to do |format|
    if fields
      # less attributes : only a,c
    elsif xxx
      # default attributes
    else
      # all attributes : a,c,b,d,...
    end
  end

serializer.rb

class WeatherLogSerializer < ActiveModel::Serializer
  attributes :id, :temperature
  def temperature
    "Celsius: #{object.air_temperature.to_f}"
  end
end

Answer

Chris picture Chris · Apr 13, 2015

I would probably do it with a different endpoint for all, but you could also pass in a different url parameter or something of the sort. Typically, I think you would want the default to be more limited.

Here's how I would suggest doing it:

Controller

Different Endpoint for All

render json: objects, each_serializer: WeatherLogAllSerializer

Allow custom fields

fields = params[:fields] # Csv string of specified fields.

# pass the fields into the scope
if fields.present?
  render json: objects, each_serializer: WeatherLogCustomSerializer, scope: fields
else
  render json: objects, each_serializer: WeatherLogSerializer
end

Three different serializers: all, default, custom

All

class WeatherLogAllSerializer < ActiveModel::Serializer
  attributes :id, :temperature, :precipitation, :precipitation
  has_many :measurements

  def temperature
    "Celsius: #{object.air_temperature.to_f}"
  end
end

Default

class WeatherLogSerializer < ActiveModel::Serializer
  attributes :id, :temperature
  def temperature
    "Celsius: #{object.air_temperature.to_f}"
  end
end

Custom

class WeatherLogCustomSerializer < WeatherLogSerializer

  def attributes
    data = super
    if scope
      scope.split(",").each do |field|
        if field == 'precipitation'
          data[:precipitation] = object.precipitation
        elsif field == 'humidity'
          data[:humidity] = object.humidity
        elsif field == 'measurements'
          data[:measurements] = ActiveModel::ArraySerializer.new(object.measurements)
        end
      end
    end
    data
  end
end