Alessandro Santamaria
Alessandro Santamaria

Reputation: 967

How to combine two as_json methods to one correctly?

In my Model I have a working as_json method as follows:

  def as_json(options = {})
    super(options.merge(include: [:user, comments: {include: :user}]))
  end

This method is for including users in comments.

Now I need to add almost the same thing in the same model for answers:

  def as_json(options = {})
    super(options.merge(include: [:user, answers: {include: :user}]))
  end

How do I combine these two as_json methods, so that I have one as_json method?

Don't laugh but I am struggling with this for 3 days.

Upvotes: 2

Views: 1543

Answers (3)

Fran Martinez
Fran Martinez

Reputation: 3042

def as_json(other_arg, options = {})
  as_json(options.merge(include: [:user, other_arg: {include: :user}])) 
end

And then you can call:

MyModel.as_json(:comments)
MyModel.as_json(:answers)

Upvotes: 0

Simone Carletti
Simone Carletti

Reputation: 176412

This is one of the reasons why you should not use the built-in to_json to serialize ActiveRecord models.

Instead, you should delegate the task to another object called serializer. Using a serializer allows you to have illimitate representations (serializations) of the same object (useful if the object can have different variants such as with/without comments, etc) and separation of concerns.

Creating your own serializer is stupid simply, as simple as having

class ModelWithCommentsSerializer
  def initialize(object)
    @object = object
  end

  def as_json
    @object.as_json(include: [:user, comments: {include: :user}])) 
  end
end

class ModelWithAnswersSerializer
  def initialize(object)
    @object = object
  end

  def as_json
    @object.as_json(include: [:user, answers: {include: :user}])) 
  end
end

Of course, that's just an example. You can extract the feature to avoid duplications.

There are also gems such as ActiveModelSerializers that provides that feature, however I prefer to avoid them as they tend to provide a lot of more of what most of users really need.

Upvotes: 3

Richard Peck
Richard Peck

Reputation: 76774

Why are you trying to override core Rails functionality - not good practice unless absolutely necessary.

--

This says the following:

To include associations use :include:

user.as_json(include: :posts)
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
#      "created_at" => "2006/08/01", "awesome" => true,
#      "posts" => [ { "id" => 1, "author_id" => 1, "title" => "Welcome to the weblog" },
#                   { "id" => 2, "author_id" => 1, "title" => "So I was thinking" } ] }

You could call:

@answers.as_json(include :users)

--

Ohhhhhhhh:

Second level and higher order associations work as well:

user.as_json(include: { posts: {
                       include: { comments: {
                                      only: :body } },
                       only: :title } })
# => { "id" => 1, "name" => "Konata Izumi", "age" => 16,
#      "created_at" => "2006/08/01", "awesome" => true,
#      "posts" => [ { "comments" => [ { "body" => "1st post!" }, { "body" => "Second!" } ],
#                     "title" => "Welcome to the weblog" },
#                   { "comments" => [ { "body" => "Don't think too hard" } ],
#                     "title" => "So I was thinking" } ] }

So looks like you could call:

@answers.to_json(include: comments: { include: :users })

Upvotes: 0

Related Questions