Serialize only specific attributes with ActiveModel and Grape API depending on action

1.2k Views Asked by At

In my Grape API the same model can be accessed by different controllers and endpoints. I need to serialize the same model for each of them, but not every attribute applies to all endpoints. I know there is the "filter" method, but this removes an attribute. I would like to list the valid attributes instead. This seems safer. I figured out the following approach. However, is there a built-in way I'm missing?

Using the code below, I would like to return "id, comments, status, user_id" if called by the "/event_signups" endpoint. I would like to return "id, comments, event_id" if called by the "/user_signups" endpoint.

Serializer

module Mobile
  module V4

    class SignupSerializer < ActiveModel::Serializer

      attributes :id,
                 :comments,
                 :status,
                 :event_id,
                 :user_id

      attribute :id,       if: :id?
      attribute :comments, if: :comments?
      attribute :status,   if: :status?
      attribute :event_id, if: :event_id?
      attribute :user_id,  if: :user_id?

      def id?
        !instance_options[:allowed_attributes].index(:id).nil?
      end

      def comments?
        !instance_options[:allowed_attributes].index(:comments).nil?
      end

      def status?
        !instance_options[:allowed_attributes].index(:status).nil?
      end

      def event_id?
        !instance_options[:allowed_attributes].index(:event_id).nil?
      end

      def user_id?
        !instance_options[:allowed_attributes].index(:user_id).nil?
      end

    end

  end
end

Grape API Endpoints

module Mobile
  module V4
    class Signups < Mobile::V4::Root
      include Mobile::V4::Defaults

      resource :signups, desc: 'Operations about the signups' do

        desc 'Returns list of user's signups'
        oauth2 # This endpoint requires authentication

        get '/user_signups', allowed_attributes: [:id, :comments, :event_id] do
          Signup.where(user_id: current_user.id)
        end

        desc 'Returns list of event's signups'
        params do
          requires :event_id, type: Integer, desc: 'Event ID'
        end
        oauth2 # This endpoint requires authentication

        get '/event_signups', allowed_attributes: [:id, :comments, :status, :user_id] do
          Signup.where(event_id: params[:event_id])
        end

      end

    end

  end
end
2

There are 2 best solutions below

2
Alex On

Can you try selecting only the fields you need? Eg

Signup.where(user_id: current_user.id).select(:id, :comments, :status, :user_id)
0
Dat Le Tien On

I assume that your context does not allow you to use the render function directly. You are facing a problem such as excessively fetching extra fields but you want to fetch some fields defined in fields_array

ActiveModelSerializers::SerializableResource.new(
        resource,
        serializer: SerializerClass,
        fields: fields_array
      ).serializable_hash

Or, if you have a collection of resources, use each_serializer instead of serializer

ActiveModelSerializers::SerializableResource.new(
        collection,
        each_serializer: SerializerClass,
        fields: fields_array
      ).serializable_hash