Search code examples
ruby-on-railsactive-model-serializers

Apply filter to ActiveModel Serializer attributes to change the key


I have a serializer for my rest API. Currently, it looks like:

class TestSerializer < ActiveModel::Serializer
    attributes :id, :name, :field_one__c, :field_two__c
end

I'm wondering if there are any ways to filter all the fields to have the __c removed when serializing, if there is a way to apply logic to ALL fields.

The case is I have a lot of fields with __c on the end, and I'd like to remove them all with a minimal amount of code on the serializer level.


Solution

  • Yes you can customize an attribute name in the serializer using the :key option:

    attribute :field_one__c, key: :field_one
    attribute :field_two__c, key: :field_two
    

    You can also make any attribute conditional using :if or :unless options.

    Doc: https://github.com/rails-api/active_model_serializers/blob/v0.10.6/docs/general/serializers.md#attribute


    Update:

    For your special case, you can hack this by defining the attributes class method before the attributes list:

    class TestSerializer < ActiveModel::Serializer
      class << self
        def attributes(*attrs)
          attrs.each do |attr|
            options = {}
            options[:key] = attr.to_s[0..-4].to_sym if attr.to_s.end_with?('__c')
    
            attribute(attr, options)
          end
        end
      end
    
      attributes :id, :name, :field_one__c, :field_two__c
    end
    

    If you have multiple serializer classes with the same requirement of filtering lots of attributes, you can apply the DRY principle in your solution by creating another serializer class which will inherit from ActiveModel::Serializer. Put the above class method definition inside this new serializer and inherit all the serializers from this new one which have list of attributes with __c.

    class KeyFilterSerializer < ActiveModel::Serializer
      class << self
        def attributes(*attrs)
          attrs.each do |attr|
            options = {}
            options[:key] = attr.to_s[0..-4].to_sym if attr.to_s.end_with?('__c')
    
            attribute(attr, options)
          end
        end
      end
    end
    
    class TestSerializer < KeyFilterSerializer
      attributes :id, :name, :field_one__c, :field_two__c
    end
    
    class AnotherTestSerializer < KeyFilterSerializer
      attributes :id, :name, :field_one__c, :field_two__c
    end