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":"[email protected]",
  "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":"[email protected]",
  "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.

2
  • What do you use for JSON serialization? github.com/rails-api/active_model_serializers supports associations. Commented Jan 21, 2017 at 8:09
  • @MikDiet I am calling .to_json on the obejct, that's all. Commented Jan 21, 2017 at 8:12

1 Answer 1

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
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.