grape-entity
grape-entity copied to clipboard
Ability to "flatten" nested entities into parent (e.g. for CSV)
This feature allows you to specify another entity to copy exposures from. It's useful when flattening a hierarchy for CSV. It also adds the :object
option to exposures that can define an alternate object to get values from. (Specifying an alternate object is necessary when copying exposures because the exposures don't refer to the original entity/model anymore.)
This would need tests, README and CHANGELOG entries. And of course a passing build. Thanks for contributing!
@dblock updated with docs and tests.
@dblock updated the example in README
I want to play devil's advocate for this feature for a second. Can't this be accomplished outside of presenters? I mean flattening a JSON generically may be easier (like this or this).
@dblock Interestingly enough, we took that approach initially, and then wanted to try and utilize the Entity
paradigm to help document the schema automatically.
Initially, we added an Entity#to_csv
method that took the JSON representation and flattened it (similarly to the links you posted). However, we are using grape-swagger to generate schema documentation for the Swagger UI for testing the API. Flattening the data resulted in the documentation not being accurate -- additional properties magically appearing when using CSV.
The solution was to flatten the entities by copying the exposures so that the schema would describe all attributes, regardless of format. However, the only thing that I wasn't able to implement cleanly in this feature (that we had in our initial hacked-together version of this), was appending " (CSV only)" to the end of each description -- which would indicate why some attributes don't always appear.
You're right, we could simply flatten the data, but that didn't seem to be self-documenting enough and defeated the purpose of using Grape Entity for us :-/
I'd love to hear what others think of this - will leave it open for a bit. In the meantime you might want to squash the commits/rebase this PR.
I stumbled upon this PR looking for that exact functionality. From my point of view it's a perfectly legitimate feature. Consider for instance this case where the internal model doesn't match the representation in the API:
class ItemRevision
(...) # versioned attributes a, b, c
end
class Item
(...) # unversioned attributes x, y, z
has_many ItemRevisions
end
class ItemRevisionEntity < Grape::Entity
expose :a, :b, :c
end
class ItemEntity < Grape::Entity
expose :x, :y, :z
merge_with ItemRevisionEntity { object.current_revision }
expose :revisions, using: ItemRevisionEntity, if: {history: true}
end
I.e., the versioning might be irrelevant for some public API and is therefore hidden by only including the current revision, while the revision entity might be useful on its own in other contexts.
Moreover, I agree with Joel that grape-entity derives most of its usefulness from authoritatively defining the (e.g. ReST) resource. Consequently I think it should reflect the exact format that calls to the API will give you.
If I can help please let me know, I'll be using this one way or the other.
I'll leave this open for a bit. FWIW, this morning my inclination is to merge this.
PR allows make entity partials in grape-entity. :+1: Example:
class DuckOptionsEntity < Grape::Entity
expose :duck_specific_attribute
end
class DogOptionsEntity < Grape::Entity
expose :dog_specific_attribute
end
class AnimalEntity < Grape::Entity
expose :nickname, :type
merge_with DogOptionsEntity, if: lambda { |object,options| object.dog? }
merge_with DuckOptionsEntity, if: lambda { |object,options| object.duck? }
end
Usage:
present Animal.all, with: AnimalEntity
Result
[
{
"nickname": "puppy",
"type": "dog",
"dog_specific_attribute": "value"
},
{
"nickname": "scrooge",
"type": "duck",
"duck_specific_attribute": "value"
}
]
Is there any way to make it with standard grape-enitity?
I am still sitting on this, I'll take a close look. Would appreciate other people's opinion. Generally, I think I want to weed the "flatten" part out of this, the merge_with
part is great.
:+1: on getting the merge_with
bits pulled in
@brianphillips Want to try and extract a merge_with
PR out of this ?