grape-entity icon indicating copy to clipboard operation
grape-entity copied to clipboard

Grape::Entity#as_json does not return a recursive set of hashes.

Open synth opened this issue 4 years ago • 0 comments

Given: A Grape::Entity that exposes another Grape::Entity Expected: When calling .as_json (or serializable_hash) it should recursively convert all objects to hashes Actual: Only the top level is converted, nested entities remain as: Grape::Entity::Exposure::NestingExposure::OutputBuilder

Discussed tangentially in: #39, #299, #313

The reason why perhaps this isn't more of an issue is that .to_json works correctly because that returns output from MultiJson: https://github.com/ruby-grape/grape-entity/blob/675d3c0e20dfc1d6cf6f5ba5b46741bd404c8be7/lib/grape_entity/entity.rb#L560-L563

Indeed, .as_json is just an alias for serializable_hash which is what isn't properly recursive. https://github.com/ruby-grape/grape-entity/blob/675d3c0e20dfc1d6cf6f5ba5b46741bd404c8be7/lib/grape_entity/entity.rb#L558

This is easily reproducible:

class TestCompany
  attr_accessor :domain
end

class TestUser
  attr_accessor :name
  attr_accessor :company
end

class TestCompanyEntity < Grape::Entity
  expose :domain
end

class TestUserEntity < Grape::Entity
  expose :name
  expose :company, using: TestCompanyEntity
end

tc = TestCompany.new.tap{|tc| tc.domain = "https://example.com" }
tu = TestUser.new.tap{|tu| tu.name = "John"}
tu.company = tc

tue = TestUserEntity.new(tu)

[26] pry(main)> tue.serializable_hash
=> {:name=>"John", :company=>{:domain=>"https://example.com"}}
[27] pry(main)> tue.serializable_hash.deep_stringify_keys
=> {"name"=>"John", "company"=>{:domain=>"https://example.com"}} # notice :domain is still a symbol
[28] pry(main)> tue.serializable_hash[:company].class # that's because company isn't a hash despite kinda looking like one
=> Grape::Entity::Exposure::NestingExposure::OutputBuilder

A simple workaround is for developers is to send it through JSON dump/parse

JSON.parse(JSON.dump(tue.serializable_hash))

But this seems unnecessary work and would be nice if this gem implemented recursive output at a higher level than just .to_json which gives us a string not a hash.

PS. Thank you to everyone who has worked on this gem! ❤️

synth avatar Apr 19 '21 05:04 synth