Search code examples
ruby-on-railsruby

render json returns different response passing literal value vs variable


Using rails 7.1.3.4 and Ruby 3.3.4

I have this code in the controller:

    render json: item.item_tags.map do |item_tag|
      {
        things_count: item_tag.tag.things.count,
        other_count: item_tag.tag.other_association.count,
        tag: {
          id: item_tag.tag.id,
          name: item_tag.tag.name,
          slug: item_tag.tag.slug,
          things: ThingSerializer.new.index(item_tag.tag.things),
        }
      }
    end

ThingSerializer here also has a map which goes through and serializes all the passed things.

It's supposed to return a list based on the map value. However, it returns a list of default item_tags, i.e.:

[{
    "id": 236,
    "status": "draft",
    "position": 0,
    "item_id": 234,
    "tag_id": 123,
    "inserted_at": "2024-03-19T10:23:56.000+03:00",
    "updated_at": "2024-03-19T10:23:56.000+03:00"
}]

But if I do this:

    actual_json = item.item_tags.map do |item_tag|
      {
        things_count: item_tag.tag.things.count,
        other_count: item_tag.tag.other_association.count,
        tag: {
          id: item_tag.tag.id,
          name: item_tag.tag.name,
          slug: item_tag.tag.slug,
          things: ThingSerializer.new.index(item_tag.tag.things),
        }
      }
    end

    render json: actual_json

It works as expected.

Why is it this way?

PS. I do similar things in other controllers and it works as expected. Only here it's not returning the expected value.


Solution

  • Your block is passed to render which doesn't do anything:

    render json: [1,2,3].map do |i|
      {i => i}
    end
    #=> [1,2,3]
    

    You have to either use {} instead of do end or add some parenthesis:

    render json: [1,2,3].map { |i|
      {i => i}
    }
    #=> [{"1":1},{"2":2},{"3":3}]
    
    render(
      json: [1,2,3].map do |i|
        {i => i}
      end
    )
    #=> [{"1":1},{"2":2},{"3":3}]