Association's attributes as model's attributes (no nesting) in active model serializers

125 views Asked by At

I need the attributes of the belongs_to and has_one associations in an active model serializer be prefixed with the association name and serialize it as the model's immediate attributes. I don't want them nested, under the model but have it flat in the same level.

For example,

class StudentSerializer
  attributes :name, :email
  belongs_to :school
end

class SchoolSerializer
  attributes :name, :location
end

For the above serializer, the output will be

{
  id: 1,
  name: 'John',
  email: '[email protected]',
  school: {
   id: 1,
   name: 'ABC School',
   location: 'US'
  }
}

But I need it as,

{
  id: 1,
  name: 'John',
  email: '[email protected]',
  school_id: 1,
  school_name: 'ABC School',
  school_location: 'US'
}

I can do this by adding the following methods to the serializer like

attributes :school_id, :school_name, :school_location  

def school_name
  object.school.name
end

def school_location
  object.school.location
end

But I don't think it's a 'great' solution as I need to define methods for all the attributes in the association. Any idea or workaround (or a direct solution if I'm missing) to achieve this elegantly? TIA.

Update: I've handled this with the following workaround for each association for now temporarily.

attributes :school_name, :school_location

[:name, :location].each do |attr|
  define_method %(school_#{attr}) do
    object.school.send(attr)
  end
end
1

There are 1 answers

1
Mat On

You could manage the virtual attributes as missing methods:

  attributes :school_id, :school_name, :school_location 

  def method_missing( name, *_args, &_block )
    method = name.to_s
    if method.start_with?( 'school_' )
      method.slice! 'school_'
      object.school.send( method )
    end
  end

  def respond_to_missing?( name, include_all = false )
    return true if name.to_s.start_with?( 'school_' )
    super
  end