module Entities
class StuffEntity < Grape::Entity
root 'stuffs', 'stuff'
...
How can I DRY up my code by reusing this entity while still having the flexibility to rename the root keys ('stuffs' and 'stuff') defined in the entity?
I might need to do this in a scenario where I'm exposing a subset of a collection represented by an existing entity or exposing an associated collection that can be represented by an existing entity.
Let's say I have an object with a name
attribute and some collection of scoped_stuff
that I want to expose as some_stuffs
. I could do that with an entity like this:
module Entities
class CoolStuffEntity < Grape::Entity
root 'cool_stuffs', 'cool_stuff'
expose :some_stuffs, documentation: {
type: Entities::StuffEntity
} do |object, _options|
Entities::StuffEntity.represent(
object.class.scoped_stuff,
root: false
)
end
expose :name, documentation: { type: 'string' }
end
end
Passing root: false
to the represent
method ensures that the nested association is represented without a root key. Here are what the representations look like with and without that argument:
# Without root: false
cool_stuff: {
some_stuffs: {
stuffs: [ /* collection represented by StuffEntity */ ]
},
name: 'Something'
}
# With root: false
cool_stuff: {
some_stuffs: [ /* collection represented by StuffEntity */ ],
name: 'Something'
}
In this instance, passing root: false
ensures that the nested entity's root key isn't included in our representation.
Let's say we have this entity where we did not specify root:
module Entities
class StuffEntity < Grape::Entity
expose :name, documentation: { type: 'string' }
end
end
The serializable hash for an object represented with this entity will look like: { name: 'Object name' }
In our API, we can specify the response key like so:
get do
stuff_object = Stuff.find_by(user_id: current_user)
present stuff_object,
with: Entities::StuffEntity,
root: :stuff
end
So that our response will look like this: { stuff: { name: 'Object name' } }
Note that 'root' accepts string and symbol arguments here.
So what if I have an entity where I specified a root key and I want the key in my response to be different (e.g., exposing a subset of the collection)? Instead of using present
, I can use represent
again. Except this time, instead of disabling the root key by passing 'false', I can give it a key name:
get do
my_stuff = Stuff.find_by(user_id: current_user)
Entities::StuffEntity.represent(
my_stuff,
root: :my_stuff
)
end