3

I am currently developing a small Rails 5 application where I need to pass on an ActiveRecord object to an external service based on certain events. In my model I have defined the following:

# /models/user.rb
after_create :notify_external_service_of_user_creation

def notify_external_service_of_user_creation
  EventHandler.new(
    event_kind: :create_user,
    content: self
  )
end

The EventHandler is then converting this object to JSON and is sending it through an HTTP request to the external service. By calling .to_json on the object this renders a JSON output which would look something like this:

{
  "id":1234,
  "email":"test@testemail.dk",
  "first_name":"Thomas",
  "last_name":"Anderson",
  "association_id":12,
  "another_association_id":356
}

Now, I need a way to include all first level associations directly into this, instead of just showing the foreign_key. So the construct I am looking for would be something like this:

{
  "id":1234,
  "email":"test@testemail.dk",
  "first_name":"Thomas",
  "last_name":"Anderson",
  "association_id":{
    "attr1":"some_data",
    "attr2":"another_value"
  },
  "another_association_id":{
    "attr1":"some_data",
    "attr2":"another_value"
  },
}

My first idea was to reflect upon the Model like so: object.class.name.constantize.reflect_on_all_associations.map(&:name), where object is an instance of a user in this case, and use this list to loop over the associations and include them in the output. This seems rather tedious though, so I was wondering if there would be a better way of achieving this using Ruby 2.4 and Rails 5.

Severin
  • 8,508
  • 14
  • 68
  • 117

1 Answers1

1

If you don't want to use an external serializer, you can override as_json for each model. as_json gets called by to_json.

module JsonWithAssociations
  def as_json
    json_hash = super

    self.class.reflect_on_all_associations.map(&:name).each do |assoc_name|
      assoc_hash = if send(assoc_name).respond_to?(:first)
                     send(assoc_name).try(:map, &:as_json) || [] 
                   else
                     send(assoc_name).as_json 
                   end

      json_hash.merge!(assoc_name.to_s => assoc_hash)
    end 

    json_hash
  end
end

You'll need to prepend this particular module so that it overrides the default as_json method.

User.prepend(JsonWithAssociations)

or

class User
  prepend JsonWithAssociations
end
fylooi
  • 3,840
  • 14
  • 24