grape-entity
grape-entity copied to clipboard
Grape::Entity#as_json does not return a recursive set of hashes.
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! ❤️