Expose collection of items of different types without keys
Hi.
I've recently tried to expose collection of objects of different types, but it seems that it's not possible with a current version.
Here's the result I'm trying to achieve:
{
included: [
{
id: 1,
type: 'posts',
attributes: {
title: 'Title 1',
date: '2015-05-01'
}
},
{
id: 2,
type: 'posts',
attributes: {
title: 'Title 2',
date: '2015-05-02'
}
},
{
id: 1,
type: 'users',
attributes: {
first_name: 'John',
last_name: 'Doe',
email: '[email protected]'
}
}
]
}
And classes:
class Result < Grape::Entity
expose :included, documentation: { is_array: true } do
expose :posts, using: Post
expose :user, using: User
end
end
class Post < Grape::Entity
expose :id
expose :type
expose :attributes do
expose :title
expose :date
end
end
class User < Grape::Entity
expose :id
expose :type
expose :attributes do
expose :first_name
expose :last_name
expose :email
end
end
Is there something I am missing?
Oh yeah, and the response I'm getting is:
{
included: [
posts: [
{
id: 1,
type: 'posts',
attributes: {
title: 'Title 1',
date: '2015-05-01'
}
},
{
id: 2,
type: 'posts',
attributes: {
title: 'Title 2',
date: '2015-05-02'
}
}
],
user: {
id: 1,
type: 'users',
attributes: {
first_name: 'John',
last_name: 'Doe',
email: '[email protected]'
}
}
]
}
It will be possible to achieve this when #56 will be fixed. I made an attempt here: #151.
When #56 will be fixed, try this example (you can already try this on my exposures branch)
require 'grape_entity'
require 'date'
require 'json'
Post = Struct.new(:id, :title, :date) do
def type
'posts'
end
end
User = Struct.new(:id, :first_name, :last_name) do
def type
'users'
end
end
class PostAttributes < Grape::Entity
expose :title
expose :date
end
class UserAttributes < Grape::Entity
expose :first_name
expose :last_name
end
class ResultEntity < Grape::Entity
class Included < Grape::Entity
expose :id
expose :type
expose :attributes,
using: PostAttributes,
if: ->(obj, opts) { obj.is_a? Post },
proc: ->(obj, opts) { obj }
expose :attributes,
using: UserAttributes,
if: ->(obj, opts) { obj.is_a? User },
proc: ->(obj, opts) { obj }
end
def included
object[:posts] + [object[:user]]
end
expose :included, using: Included
end
model = {
posts: [
Post.new(11, 'post #1', Date.parse('2015-01-01')),
Post.new(12, 'post #2', Date.parse('2015-02-01'))
],
user: User.new(1, 'Vladimir', 'Kochnev')
}
puts JSON.pretty_generate ResultEntity.represent(model, serializable: true)
Output:
{
"included": [
{
"id": 11,
"type": "posts",
"attributes": {
"title": "post #1",
"date": "2015-01-01"
}
},
{
"id": 12,
"type": "posts",
"attributes": {
"title": "post #2",
"date": "2015-02-01"
}
},
{
"id": 1,
"type": "users",
"attributes": {
"first_name": "Vladimir",
"last_name": "Kochnev"
}
}
]
}
Main caveat of this approach: as more included entities will be added, more :if conditions you would need. It may be even good in some cases but to fully bypass I'd like to implement lambda passing to using option.
Also it looks like you're trying to implement JSON API compliant entities.
I think it deserves its own implementation. Something like Grape::Entity::JsonApi.
Thanks for the heads up. And yes, I was trying to implement JSON API v1.0, though unsuccessfully with Grape::Entity, so I switched over to Roar. Another problem is that Grape Swagger does not really support deeply nested models/parameters, but that's another story.
I have been using Roar in production as well, and cannot encourage everyone to stick to a Hypermedia standard enough. So, I would welcome Grape::Entity support for HAL and JSON API as well.
Hey guys, I was also trying to implement a JSON API using grape / grape-entity / grape-sagger. So far, I spent half a day trying to figure out which solution might fit all my needs...