nesquena / rabl

General ruby templating with json, bson, xml, plist and msgpack support
http://blog.codepath.com/2011/06/27/building-a-platform-api-on-rails/
MIT License
3.64k stars 334 forks source link

Child removes node from response when the provided object is an empty array #725

Open Kinaan opened 5 years ago

Kinaan commented 5 years ago

I have 2 different rabl templates and their actual outputs are provided below.

File 1: test.rabl

object false

child @test_objects => :test_objects do
  attribute :object_id
end

node(false) do
  node(:total)   { 10 }
  node(:pages)   { 10 }
  node(:page_no) { 1 }
end

This works as expected

> puts Rabl::Renderer.new('test', nil, format: :json, locals: { test_objects: [OpenStruct.new] }).render
{"test_objects":[{"test_object":{"object_id":70186699097320}}],"total":10,"pages":10,"page_no":1}

## With test_objects as empty array 
> puts Rabl::Renderer.new('test', nil, format: :json, locals: { test_objects: [] }).render
{"test_objects":[],"total":10,"pages":10,"page_no":1}

File 2: test2.rabl

object false

child @test_objects, :root => :test_objects, :object_root => false do
  attribute :object_id
end

node(false) do
  node(:total)   { 10 }
  node(:pages)   { 10 }
  node(:page_no) { 1 }
end

The issue occurs when @test_objects is an empty array (2nd case). The test_objects attribute doesn't make it to the final response.

> puts Rabl::Renderer.new('test2', nil, format: :json, locals: { test_objects: [OpenStruct.new] }).render
{"test_objects":[{"object_id":70186677972660}],"total":10,"pages":10,"page_no":1}

## With test_objects as empty array 
> puts Rabl::Renderer.new('test2', nil, format: :json, locals: { test_objects: [] }).render
{"total":10,"pages":10,"page_no":1}

The only work around / hack that seems to work is if we change the definition of child block to something like this

child({@test_objects => :test_objects}, {:root => :test_objects, :object_root => false}) do

In case this is accepted as a bug, the line that causes this behavior is here https://github.com/nesquena/rabl/blob/master/lib/rabl/builder.rb#L171

When first argument is hash (with empty array as the value), the data.present? won't fail. This behavior is different obviously as it fails when empty array is passed as first argument.

I found that this check was introduced around 8 years back and I am not sure if it should be removed or not, so filing issue to discuss a better way to handle this scenario.

Kinaan commented 5 years ago

Anyone ? 🙂

caifara commented 2 years ago

The same problem occurs when a child is defined, but the given object ends up to be nil: the key will not be present in the response because of the early return mentioned above.

My suggestion would be to omit the key when the condition is not met (second branch of the conditional mentioned above). If the condition is met, an empty array or null value is returned if necessary.

Another - backward compatible solution - would be to allow a default value. (Although we would prefer a change as it makes an api more predictable and adds less burden on the developer who may forget to add a default on every child).

I can submit a PR if you want such a change.