-
Notifications
You must be signed in to change notification settings - Fork 152
Description
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:
grape-entity/lib/grape_entity/entity.rb
Lines 560 to 563 in 675d3c0
def to_json(options = {}) | |
options = options.to_h if options&.respond_to?(:to_h) | |
MultiJson.dump(serializable_hash(options)) | |
end |
Indeed, .as_json
is just an alias for serializable_hash
which is what isn't properly recursive.
grape-entity/lib/grape_entity/entity.rb
Line 558 in 675d3c0
alias as_json serializable_hash |
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! ❤️