trailblazer / roar-jsonapi Goto Github PK
View Code? Open in Web Editor NEWJSON API support for Roar.
Home Page: http://trailblazer.to/gems/roar/jsonapi.html
License: MIT License
JSON API support for Roar.
Home Page: http://trailblazer.to/gems/roar/jsonapi.html
License: MIT License
When files containing classes that inherit from Roar::Decorator
and use the include Roar::JSON::JSONAPI.resource
declaration are dynamically reloaded, there is a performance degradation. As the number of reloads goes up, the performance degradation seems to increase as well.
Simulate dynamic reloading by running the Kernel::load
method multiple with the same file. Again, that file should contain a class that inherits from Roar::Decorator
and uses the include Roar::JSON::JSONAPI.resource
declaration, like so:
class DocumentSingleResourceObjectDecorator < Roar::Decorator
include Roar::JSON::JSONAPI.resource :articles
attributes do
property :title
end
end
(In our case, we use trailblazer
and roar-jsonapi
within Rails, and the performance slowdown initially showed up in our development environment. We were able to reproduce the problem without Rails being involved, both in this repo and with a minimal benchmark test case in #29.)
The time to reload the file with the class in it should exhibit constant-time performance characteristics.
Translation: It should just load quickly and move on.
Every time the same file is reloaded, there appears to be a significant performance degradation.
Translation: It slows down significantly while loading, causing further use of the class to be delayed.
Roar version: 1.1.0
Roar-JSONAPI version: 0.0.3
From @matheusca on November 19, 2015 22:15
Hi guys,
I've trying create association links using Roar::JSON::JSONAPI
but I can't reference represented
method inside of the has_one
block.
For instance:
class RequesterRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI
type :requester
has_one :residence_address do
type :address
link :self do
# I need way to get requester id here.
"http://example.com/requester/#{requester.id}/relationships/residence_address"
end
end
property :id
# ... other properties
end
How I described, I would need some way to get requester.id
inside of has_one
. Anyway to do it?
Copied from original issue: trailblazer/roar#168
I have model User which has many Roles and Role has many Permissions
The UserRepresenter
class UserRepresenter < BaseRepresenter
type :users
link :self, toplevel: true do
"/users"
end
attributes do
property :first_name
property :last_name
end
link(:self) { "/users/#{represented.id}" }
link(:all) { "/users" }
has_many :roles, class: Role, decorator: RoleRepresenter do
relationship do
link(:related) {"/users/#{represented.id}/roles"}
end
end
end
The RoleRepresenter
class RoleRepresenter < BaseRepresenter
type :roles
link :self, toplevel: true do
"/roles"
end
attributes do
property :name
end
link :self do
"/roles/#{represented.id}"
end
has_many :permissions, decorator: PermissionRepresenter
end
When I call it, I receive included
for user and included
for roles. I think that in Json API there should be only one included
field
{
"data": {
"relationships": {
"roles": {
"data": [
{
"id": "1",
"type": "roles"
}
],
"links": {
"related": "/users/2/roles"
}
}
},
"id": "2",
"attributes": {
"first-name": "Lady",
"last-name": "Gaga",
},
"type": "users",
"links": {
"self": "/users/2",
"all": "/users"
}
},
"included": [
{
"relationships": {
"permissions": {
"data": [
{
"id": "4",
"type": "permissions"
},
{
"id": "15",
"type": "permissions"
}
]
}
},
"included": [
{
"id": "4",
"attributes": {
"name": "View project",
},
"type": "permissions",
"links": {
"self": "/permissions/4"
}
},
{
"id": "15",
"attributes": {
"name": "View project user",
},
"type": "permissions",
"links": {
"self": "/permissions/15"
}
}
],
"id": "1",
"attributes": {
"name": "Project User",
},
"type": "roles",
"links": {
"self": "/roles/1"
}
}
]
}
From @franzliedke on December 16, 2015 13:44
As can be seen in the first example on jsonapi.org, the "links" defined for a relationship are not to be mistaken with the links for a resource.
Thus, relationships defined like the following (taken from the Roar tests), should only add the links to either the relationship or the included resource itself:
has_one :author, class: Author, populator: ::Representable::FindOrInstantiate do
type :authors
property :id
property :email
link(:self) { "http://authors/#{represented.id}" }
end
Copied from original issue: trailblazer/roar#177
Per the jsonapi spec for links http://jsonapi.org/format/1.1/#fetching
We should be able to provide relationship links without including data about the related records:
{
"links": {
"self": "http://example.com/articles/1"
},
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "JSON API paints my bikeshed!"
},
"relationships": {
"author": {
"links": {
"related": "http://example.com/articles/1/author"
}
}
}
}
}
If we do this (which is pretty ugly for such a common usecase):
class ArticleRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI.resource :articles
has_one :author, class: Person do
relationship do
link(:related) { "/articles/#{represented.id}/relationships/author" }
end
end
end
# output =>
{
"data": {
"relationships": {
"author": {
"data": {
"id": "1",
"type": "author"
},
"links": {
"related": "/articles/#{represented.id}/relationships/author"
}
}
},
"id": "1",,
"type": "articles"
},
"included": [
{
"id": "1",
"type": "author"
}
]
}
The outputted json includes an includes block with the type and id of the related record. This causes issues with some API clients as they assume that the entire record is included and don't bother to fetch it from the related link.
Maybe the answer is to introduce a relationships api?
class ArticleRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI.resource :articles
relationships do
link(:author) { "/articles/#{represented.id}/relationships/author" } # would just return the link
included(:author, class: Person, decorator: Person::Representer) # would include the record data
end
end
The included
method should accept a block and all options current has_one
/ has_many
support. We could add a collection: true
option to keep the API for single and collections in one call.
We might not need the option at all as we could work it out based on whether we're dealing with an array of objects.
Any way to auto-convert properties to camelize(:lower)
like first_name => firstName
?
i tried http://trailblazer.to/gems/representable/3.0/api.html#defaults but seems it doesn't work for roar json api
Cross-referencing trailblazer/trailblazer-operation#17, where I originially submitted this issue
When using trailblazer-operation, roar-jsonapi, and reform in conjunction with an Update call, the id
field of a resource is forced to be editable.
That seems like a bad idea to me. I personally would prefer it to ignore the id part of the JSONAPI request.
Admittedly this is also a bit of an inconsistency with the JSONAPI spec. An update as a PATCH /resources/1
MUST include the id field. An inconsistency between the URL id and the given id in the document is not handled. Maybe there is an edge case where you would want to edit the ID. However whether you want to or not, it has to be provided as a parameter.
class MyResource < Struct.new(:id, :name)
def self.find_by(id:)
DB[id.to_i]
end
def save
DB[id.to_i] = self
end
end
DB = {1 => MyResource.new(1, 'one')} # Simulating database
class MyContract < Reform::Form
property :name
end
class MyRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI.resource :resources
attributes do
property :name
end
end
class MyOperation < Trailblazer::Operation
step Model(MyResource, :find_by)
step Contract::Build(constant: MyContract)
step Contract::Validate(representer: MyRepresenter)
step Contract::Persist()
end
MyOperation.({id: 1}, 'document' => '{"data":{"type":"resources","attributes":{"name":"changed"},"id":"9999"}}')
# => NoMethodError: undefined method `id=' for #<MyContract>
# Adding `property :id` to MyContract fixes the NoMethodError,
# but allows editing the ID field which is usually a security vulnerability:
MyOperation.({id: 1}, 'document' => '{"data":{"type":"resources","attributes":{"name":"changed"},"id":"9999"}}')
# => <Result:true ...>
DB
# => {1 => #<struct name="one">, 9999 => #<struct name="changed">}
As stated, in edge cases this might be the actually desired behavior. In most cases one would not want an ID to be editable. So an opt-in would be preferable to me.
Roar version: roar-edcd8efa0181
Roar JSONAPI version: roar-jsonapi-3ca7fa8e0f5a
From @desheikh on July 21, 2016 19:18
Calling to_hash on a representer with ("meta" => {"foo" => "bar"}} does not render the meta attributes.
It looks like the implementation partially exists but is missing actually making the call and returning the response: https://github.com/apotonick/roar/blob/master/lib/roar/json/json_api.rb#L166
Copied from original issue: trailblazer/roar#196
I have a record with two 'has_one' relationships to the same type of object. I've found I can set the type using the 'type' option. However, the 'type' in the 'included' list is incorrect.
Here is some code that reproduces my problem. I need the 'type' in the 'included' list to be 'item'. Currently it is 'type':'second-item'.
require "roar/json/json_api"
require "ostruct"
class BaseModel < OpenStruct
end
class Item < BaseModel
end
class User < BaseModel
#belongs_to :item
#belongs_to :second_item, class_name: "Item"
end
class ItemRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI.resource :item
end
class UserRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI.resource :user
has_one :item, extend: ItemRepresenter
has_one :second_item, extend: ItemRepresenter, type: :item
end
user = User.new(id: 1)
user.item = Item.new(id: 1)
user.second_item = Item.new(id: 2)
puts UserRepresenter.for_collection.prepare([user]).to_json
{"data":[{"relationships":{"item":{"data":{"id":"1","type":"item"}},"second-item":{"data":{"id":"2","type":"item"}}},"id":"1","type":"user"}],"included":[{"id":"1","type":"item"},{"id":"2","type":"item"}]}
{"data":[{"relationships":{"item":{"data":{"id":"1","type":"item"}},"second-item":{"data":{"id":"2","type":"item"}}},"id":"1","type":"user"}],"included":[{"id":"1","type":"item"},{"id":"2","type":"second-item"}]}
roar version: 1.1.0
roar-jsonapi version: 0.0.3
representable version: 3.0.4
So this seems really basic to me so I'm not sure if I'm doing anything wrong, but it seems to me like compound documents are not serialized per jsonapi standards. I tried to reproduce a simplified version of the sample given at http://jsonapi.org/format/#document-compound-documents without links and reduced number of fields:
class Article < Struct.new(:id, :title, :author, :comments)
end
class Person < Struct.new(:id, :name)
end
class Comment < Struct.new(:id, :body)
end
class PersonRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI.resource :people
attributes do
property :name
end
end
class CommentRepresenter < Roar:: Decorator
include Roar::JSON::JSONAPI.resource :comments
attributes do
property :body
end
end
class ArticleRepresenter < Roar:: Decorator
include Roar::JSON::JSONAPI.resource :articles
attributes do
property :title
end
has_one :author, extend: PersonRepresenter
has_many :comments, extend: CommentRepresenter
end
article = Article.new(
1,
"JSON API paints my bikeshed!",
Person.new(9, "Dan"),
[Comment.new(5, "First!"), Comment.new(12, "I like XML better")]
)
ArticleRepresenter.new(article).to_hash
Nested data attributes
should be detailed in the included
part. relationships
should only contain id
and type
.
{"data"=>
{"relationships"=>
{"author"=>
{"data"=>{"id"=>"9", "type"=>"people"}},
"comments"=>
{"data"=>
[{"id"=>"5", "type"=>"comments"},
{"id"=>"12", "type"=>"comments"}]}},
"id"=>"1",
"attributes"=>{"title"=>"JSON API paints my bikeshed!"},
"type"=>"articles"},
"included"=>
[{"id"=>"9", "type"=>"people", "attributes"=>{"name"=>"Dan"}},
{"id"=>"5", "type"=>"comments", "attributes"=>{"body"=>"First!"}},
{"id"=>"12", "type"=>"comments", "attributes"=>{"body"=>"I like XML better"}}]}
relationships
contain attributes
, included
does not.
{"data"=>
{"relationships"=>
{"author"=>
{"data"=>{"id"=>"9", "attributes"=>{"name"=>"Dan"}, "type"=>"people"}},
"comments"=>
{"data"=>
[{"id"=>"5", "attributes"=>{"body"=>"First!"}, "type"=>"comments"},
{"id"=>"12",
"attributes"=>{"body"=>"I like XML better"},
"type"=>"comments"}]}},
"id"=>"1",
"attributes"=>{"title"=>"JSON API paints my bikeshed!"},
"type"=>"articles"},
"included"=>
[{"id"=>"9", "type"=>"author"},
{"id"=>"5", "type"=>"comments"},
{"id"=>"12", "type"=>"comments"}]}
(I also just noticed a type=>author instead of people there, don't know how that happened but it is the actual console output)
Roar version: roar-edcd8efa0181
Roar JSONAPI version: roar-jsonapi-3ca7fa8e0f5a
From @caseymct on January 22, 2016 2:22
Trying to transition from a standard representer format to json-api. I have a model, Notification, that has a polymorphic target
- it can be one of many different types. How would I write this
class NotificationRepresenter < ApplicationRepresenter
wrap_with :notifications
timestamp :read_at
timestamp :sent_at, writeable: false
property :notification_type, writeable: false
property :message_variables, writeable: false
property :representable_target, as: :target, decorator: proc {
representable_target.representer
}
end
as a JSON-API compliant response? or can I? Somehow I need to set a has_one
with a class that is determined by representable_target
, then have the block return that target's data.
Copied from original issue: trailblazer/roar#183
From @felixbuenemann on February 18, 2016 1:6
It should be possible to express recursive relationships using JSONAPI has_one/has_many.
Without has_one/has_many I could just use a self-referential collection:
class TaxonRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI
LimitDepth = ->(user_options: {}, **) {
user_options[:depth] ? depth < user_options[:depth] : true
}
type :taxons
property :id
property :name
# ...
collection :children, extend: self, class: Taxon, if: LimitDepth
end
# Serialize up to depth 2
TaxonRepresenter.prepare(taxon).to_hash(user_options: { depth: 2 })
However has_one/has_many requires a block, so I can't just specify self
as the decorator.
After looking at the source of Roar::JSON::JSONAPI
I came up with this beautiful solution hack:
class TaxonRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI
LimitDepth = ->(user_options: {}, **) {
user_options[:depth] ? depth < user_options[:depth] : true
}
type :taxons
property :id
class RelationshipRepresenter < self; end
property :name
# ...
nested :included do
collection :children, decorator: TaxonRepresenter, class: Taxon, if: LimitDepth
end
nested :relationships do
collection :children, decorator: RelationshipRepresenter, class: Taxon, if: LimitDepth
end
end
Surely there should be an easier solution, given that trees are not exactly uncommon.
I'm using roar master and representable 3.0.0.
Copied from original issue: trailblazer/roar#185
From @wuarmin on September 20, 2016 17:59
At http://trailblazer.to/gems/representable/3.0/api.html#include-and-exclude there's a documenation about the top-level-options include and exclude. Today I tried to exclude a has_many relationship while using Roar::JSON::JSONAPI, but can't achieve it.
class ArticleDecorator < Roar::Decorator
include Roar::JSON::JSONAPI
type :articles
# a property exclude works
property :id
property :title
# a relationship exclude does not work
has_one :author, class: Author, populator: ::Representable::FindOrInstantiate do
type :authors
property :id
property :email
end
end
#===============================================
ArticleDecorator.new(article).to_json(exclude: [:title]) #works
ArticleDecorator.new(article).to_json(exclude: [:author]) # does not work
Is the feature limited to properties?
Copied from original issue: trailblazer/roar#199
From @caseymct on January 8, 2016 4:16
as per glitter chat:
I have an Event model that has_one
host, a User. the User model has it's own (quite large) decorator. It would be great to have
has_one :host, class: User, decorator: UserDecorator
I'd like to avoid doing
has_one :host, class: User, populator: ::Representable::FindOrInstantiate do
# many properties
end
and duplicating this in every decorator that has a user.
Copied from original issue: trailblazer/roar#182
From @jtzero on July 20, 2016 0:15
has_many :stores do
type :stores
property :id
link :self do
"http://localhost:3000/stores/#{represented.id}"
end
end
produces
"relationships"=>
{"stores"=>
{"data"=>[
{"type"=>"stores", "id"=>"1"},
{"type"=>"stores", "id"=>"2"},
{"type"=>"stores", "id"=>"3"}
],
"links"=>{"self"=>"http://localhost:3000/stores/3"}
}
},
"links"=>{"self"=>"://localhost:3000/retailers/9"}},
"included"=>
[
{"type"=>"stores", "id"=>"1", "links"=>{"self"=>"http://localhost:3000/stores/1"}},
{"type"=>"stores", "id"=>"2", "links"=>{"self"=>"http://localhost:3000/stores/2"}},
{"type"=>"stores", "id"=>"3", "links"=>{"self"=>"http://localhost:3000/stores/3"}}
]
}
the links are correct in the "included" but the one in "stores" is incorrect,and changing the self link in has_many
changes all the ones in the 'included'. How do I change the one in stores?
Copied from original issue: trailblazer/roar#194
Due to legacy design, we want to use the non-strict behavior for as:
for property-names, allowing underscores.
Following the Documentation I implemented a representer with the default set to strict: false
.
require "roar/json"
require "roar/json/json_api"
class RuleRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI.resource :rules
defaults do |name, _|
{ as: JSONAPI::MemberName.call(name, strict: false) }
end
attributes do
property :chain_id
property :name
property :event_type
end
end
However, the output was hyphenated!
[{:id=>"1c9c618a-86ad-437f-b3dd-50dd5a3e290f", :attributes=>{:"chain-id"=>"072bb8ca-a7c7-4408-b5f1-831d7277f676", :"event-type"=>"transactions"}, :type=>"rules"}]
I was able to workaround by changing my Representer. I placed the defaults block inside of the attributes
block.
require "roar/json"
require "roar/json/json_api"
class RuleRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI.resource :rules
attributes do
defaults do |name, _|
{ as: JSONAPI::MemberName.call(name, strict: false) }
end
property :chain_id
property :name
property :event_type
end
end
Create a representer which sets a defaults block like so:
defaults do |name, _|
{ as: JSONAPI::MemberName.call(name, strict: false) }
end
and has attributes
attributes do
property :name
end
The attributes should be allowed to pass the non-strict conversion and remain underscored.
Attributes are hyphenated.
Roar version: 1.1.0
Roar JSONAPI version: 0.0.3
Declarative version: 0.0.9
During research, I found many things I didn't understand. Please excuse my ignorance.
attributes
-> Roar::JSON::JSONAPI::Declarative
, sets the inherit: true
property, presumably so you can have multiple attributes blocks and they merge together.nested
-> When a block is passed, the options[:_base]
is set to Decorator.default_nested_class
, which is just an base Representable::Decorator
. At this point, self
is actually still RuleRepresenter
.Declarative::Definitions::Definition#add
Called with attributes
, options and the block. Here it extracts some properties, calls the _defaults
pipeline, and then calls the nested builder with the _base
, name, and block.Declarative::Schema::DSL::NestedBuilder (proc)
is called, wherein self
is Declarative::Schema::DSL
. No context anymore here, except what was provided in options
. which is provided for that purpose. This proc begins with Class.new(options[:_base])
, which extends the Representable::Decorator
supplied above. Then a block is evaluated which adds the features from the options context, and finally the block is class_eval
d. The evaluation of feature
with the Roar::JSON::JSONAPI::Defaults
feature sets up @options
with a fresh set, and the options[:_defaults]
from the parent is not used here. There's no way to set the options. There's no way to access the @dynamic_options
block of options[:_defaults]
either. I can't determine if there's some means by which to merge
without hacking too much further.Now, if you were to change Roar::JSON::JSONAPI::Declarative
to pass the private option _defaults
, then those configurations would be available inside of Declarative::Definitions::Definition#add
. However, getting it merged inside of Declarative::Schema::DSL::NestedBuilder
is beyond me at this time.
From @ottodog on July 14, 2016 18:56
I'm using the as: option with :id inside a has_many relationship:
class ParentRepresenter < Roar::Decorator
include Roar::JSON::JSONAPI
type :parents
has_many :children do
type :children
property :alias_id, as: :id
link :self do
parent_child_url(represented.parent.id, represented.alias_id)
end
end
{
"data": {
"relationships": {
"documents": {
"data": [
{
"type": "documents",
"id": ""
}
],
"links": {
"self": "http://127.0.0.1:3000/parent/57879b4a3bb752aaa6e24a35/child/0"
}
}
}
},
"included": [
{
"type": "documents",
"id": "0",
"attributes": {},
"links": {
"self": "http://127.0.0.1:3000/parent/57879b4a3bb752aaa6e24a35/child/0"
}
}
]
}
The ID is properly 0 in the included array, but empty in relationships.
Note:
I'm using roar-rails with the master branch of roar.
I'm also doing something a bit weird with the child IDs in my API. For each parent, they count up from 0, and so are not unique amongst all children, but are accessed through both the child and parent ID, ie. parent/:parent_id/children/child_id. That is why the child ID needs to be aliased, as they are stored in the database using unique ids, but only this separate index will be exposed through API.
Copied from original issue: trailblazer/roar#193
From @dwhelan on May 14, 2016 0:30
I am trying to use Roar::JSON::JSONAPI
to accept and return JSONAPI payloads. I think I must be missing something because the json rendered in a get does not seem to conform JSONAPI v1.0.
require_relative 'spec_helper'
class AppleSauce
attr_reader :id, :taste
def initialize
@id = '1'
@taste = 'tart'
end
end
class Decorator < Roar::Decorator
include Roar::JSON::JSONAPI
include Roar::Hypermedia
include Grape::Roar::Representer
type :apple_sauce
property :id
property :taste
end
module Representer
include Roar::JSON::JSONAPI
include Roar::Hypermedia
include Grape::Roar::Representer
type :apple_sauce
property :id
property :taste
end
describe 'roar' do
let(:expected) { { data: { type: 'apple_sauce', id: '1', attributes: { taste: 'tart' } } } }
it { expect(JSON.parse(Decorator.prepare(AppleSauce.new).to_json)).to eq expected }
it { expect(JSON.parse(AppleSauce.new.extend(Representer).to_json)).to eq expected }
end
Failures:
1) roar should eq {:data=>{:type=>"apple_sauce", :id=>"1", :attributes=>{:taste=>"tart"}}}
Failure/Error: it { expect(JSON.parse(Decorator.prepare(AppleSauce.new).to_json)).to eq expected }
expected: {:data=>{:type=>"apple_sauce", :id=>"1", :attributes=>{:taste=>"tart"}}}
got: {"apple_sauce"=>{"id"=>"1", "taste"=>"tart"}}
(compared using ==)
Diff:
@@ -1,2 +1,2 @@
-:data => {:type=>"apple_sauce", :id=>"1", :attributes=>{:taste=>"tart"}},
+"apple_sauce" => {"id"=>"1", "taste"=>"tart"},
# ./spec/test_spec.rb:35:in `block (2 levels) in <top (required)>'
2) roar should eq {:data=>{:type=>"apple_sauce", :id=>"1", :attributes=>{:taste=>"tart"}}}
Failure/Error: it { expect(JSON.parse(AppleSauce.new.extend(Representer).to_json)).to eq expected }
expected: {:data=>{:type=>"apple_sauce", :id=>"1", :attributes=>{:taste=>"tart"}}}
got: {"apple_sauce"=>{"id"=>"1", "taste"=>"tart"}}
(compared using ==)
Diff:
@@ -1,2 +1,2 @@
-:data => {:type=>"apple_sauce", :id=>"1", :attributes=>{:taste=>"tart"}},
+"apple_sauce" => {"id"=>"1", "taste"=>"tart"},
# ./spec/test_spec.rb:36:in `block (2 levels) in <top (required)>'
Finished in 0.01212 seconds (files took 0.47538 seconds to load)
2 examples, 2 failures
Any suggestions would be most appreciated!
Copied from original issue: trailblazer/roar#191
From @lasseebert on April 15, 2016 21:53
I would like to render empty (nil
) relationships with JSONAPI
like so:
{
"relationships": {
"some_relation": { "data": null }
}
}
This is what the jsonapi specification says. See http://jsonapi.org/format/#document-resource-object-linkage
When I use render_nil: true
in a has_one
I get
undefined method `each' for nil:NilClass
It seems like a simple issue to fix inside the JSON::JSONAPI
. Can anyone confirm that it's not just me doing something stupid? If so I'll be happy to fix it in a pull request :)
Copied from original issue: trailblazer/roar#189
I have some issues using the JSONAPI output for collections. I'm generating the collection output with:
orgs = paginate(Libis::Ingester::Organization.all)
Representer::OrganizationRepresenter.for_collection.prepare(orgs).to_hash(pagination_hash(orgs))
The options generated by 'pagination_hash' looks like this:
{
user_options:
{
...,
links:
{
self: "http://localhost:9393/api/organizations?per_page=2&page=1",
first: "http://localhost:9393/api/organizations?per_page=2&page=1",
last: "http://localhost:9393/api/organizations?per_page=2&page=3",
next: "http://localhost:9393/api/organizations?per_page=2&page=2"
}
},
meta: { per_page:2, :total=>5, :page=>1, :max_page=>3, :next_page=>2, :prev_page=>nil}
}
The generated hash looks like this:
{
"data": [
{
"id": "58b7ccd7e852dd0775f51cae",
"attributes": {
"name": "Test Ingest Organization"
},
"type": "organization",
"links": {
"self": "http://localhost:9393/api/organizations/58b7ccd7e852dd0775f51cae",
"all": "http://localhost:9393/api/organizations"
},
"meta": {
"per_page": 2,
"total": 5,
"page": 1,
"max_page": 3,
"next_page": 2,
"prev_page": null
}
},
{
"id": "58b7ccd7e852dd0775f51caf",
"attributes": {
"name": "Dummy test organization"
},
"type": "organization",
"links": {
"self": "http://localhost:9393/api/organizations/58b7ccd7e852dd0775f51caf",
"all": "http://localhost:9393/api/organizations"
},
"meta": {
"per_page": 2,
"total": 5,
"page": 1,
"max_page": 3,
"next_page": 2,
"prev_page": null
}
}
],
"meta": {
"per_page": 2,
"total": 5,
"page": 1,
"max_page": 3,
"next_page": 2,
"prev_page": null
}
}
There are two issues with this:
The output I'm expecting is:
{
"data": [
{
"id": "58b7ccd7e852dd0775f51cae",
"attributes": {
"name": "Test Ingest Organization"
},
"type": "organization",
"links": {
"self": "http://localhost:9393/api/organizations/58b7ccd7e852dd0775f51cae",
"all": "http://localhost:9393/api/organizations"
}
},
{
"id": "58b7ccd7e852dd0775f51caf",
"attributes": {
"name": "Dummy test organization"
},
"type": "organization",
"links": {
"self": "http://localhost:9393/api/organizations/58b7ccd7e852dd0775f51caf",
"all": "http://localhost:9393/api/organizations"
}
}
],
"links": {
"self": "http://localhost:9393/api/organizations?per_page=2&page=1",
"first": "http://localhost:9393/api/organizations?per_page=2&page=1",
"last": "http://localhost:9393/api/organizations?per_page=2&page=3",
"next": "http://localhost:9393/api/organizations?per_page=2&page=2"
},
"meta": {
"per_page": 2,
"total": 5,
"page": 1,
"max_page": 3,
"next_page": 2
}
}
BTW: I'm using latest releases of grape, grape-roar, roar and roar-jsonapi on Ruby 2.3.1.
From @wuarmin on September 27, 2016 17:49
I know JSON API module is not finished yet, but maybe this can be accomplished with less effort. In my opinion the serialized compound doc, generated from following code, should contain the ressource object (type: organisations, id: 8) in the included-payload.
require 'roar/json'
require 'roar/decorator'
require 'roar/json/json_api'
class ArticleDecorator < Roar::Decorator
include Roar::JSON::JSONAPI
type :articles
property :id
property :title
has_one :author, class: Authors do
type :authors
property :id
property :email
has_one :organisation, class: Organisations do
type :organisations
property :id
property :name
end
end
end
organisation = Organisations.new(id: 8, name: 'Articles23')
author = Authors.new(id: 4711, email: '[email protected]', organisation: organisation)
article = Articles.new(id: 1, author: author, title: 'Programming')
puts ArticleDecorator.new(article).to_json
The result json contains the relationship, but the ressource-object is missing:
{
"data": {
"type": "articles",
"id": "1",
"attributes": {
"title": "Programming"
},
"relationships": {
"author": {
"data": {
"type": "authors",
"id": "4711"
}
}
}
},
"included": [
{
"type": "authors",
"id": "4711",
"attributes": {
"email": "[email protected]"
},
"relationships": {
"organisation": {
"data": {
"type": "organisations",
"id": "8"
}
}
}
}
]
}
best regards
Copied from original issue: trailblazer/roar#200
From @thhermansen on November 30, 2015 14:31
Hi,
I wasn't able to figure this one out myself. Given the following:
class User < Roar::Decorator
include Roar::JSON::JSONAPI
property :full_name
end
Is there a way to make the serialised version of a user to have property named full-name
?
I can solve it by using #property
's as
-option, but I'd like to dasherize all property names automatically.
Copied from original issue: trailblazer/roar#173
When I call to_json()
on a representer from collection without specifying included records I get the full compound document complete with the included records.
"{\"data\":[
{\"relationships\":{
\"feature-image\":{\"data\":{\"id\":\"1\",\"type\":\"images\"}},
\"images\":{\"data\":[{\"id\":\"1\",\"type\":\"images\"}]},
\"documents\":{\"data\":[]},
},
\"id\":\"1\",
\"attributes\":{
\"name\":\"Site Name 1\",
\"reference\":\"760\",
\"address\":{
\"address_line_one\":\"Addr 1\",
\"town_city\":\"Town\",
\"county\":\"County\",
\"postcode\":\"Some Postcode\",
\"country\":\"England\"
}
},\"type\":\"sites\"},
{\"relationships\":{
\"feature-image\":{\"data\":{\"id\":\"2\",\"type\":\"images\"}},
\"images\":{\"data\":[{\"id\":\"2\",\"type\":\"images\"}]},
\"documents\":{\"data\":[]},
},
\"id\":\"2\",\"attributes\":{
\"name\":\"Site Name 2\",
\"reference\":\"114\",
\"address\":{
\"address_line_one\":\"Addr 1\",
\"town_city\":\"Town\",
\"county\":\"County\",
\"postcode\":\"Some Postcode\",
\"country\":\"England\"
}
},\"type\":\"sites\"}
],
\"included\":[
{\"id\":\"1\",\"attributes\":{\"caption\":\"Exterior view of building\",\"file-url\":\"some_url\"},\"type\":\"images\",\"links\":{\"self\":\"http://images/1\"}},
{\"id\":\"2\",\"attributes\":{\"caption\":\"Interior view of building\",\"file-url\":\"some_url\"},\"type\":\"images\",\"links\":{\"self\":\"http://images/2\"}}
]}"
When I pass in an included option, all relationship attributes are rendered, but nothing is included at all.
"{\"data\":[
{\"relationships\":{
\"feature-image\":{\"data\":{\"id\":\"1\",\"type\":\"images\"}},
\"images\":{\"data\":[{\"id\":\"1\",\"type\":\"images\"}]},
\"documents\":{\"data\":[]},
},
\"id\":\"1\",
\"attributes\":{
\"name\":\"Site Name 1\",
\"reference\":\"760\",
\"address\":{
\"address_line_one\":\"Addr 1\",
\"town_city\":\"Town\",
\"county\":\"County\",
\"postcode\":\"Some Postcode\",
\"country\":\"England\"
}
},\"type\":\"sites\"},
{\"relationships\":{
\"feature-image\":{\"data\":{\"id\":\"2\",\"type\":\"images\"}},
\"images\":{\"data\":[{\"id\":\"2\",\"type\":\"images\"}]},
\"documents\":{\"data\":[]},
},
\"id\":\"2\",\"attributes\":{
\"name\":\"Site Name 2\",
\"reference\":\"114\",
\"address\":{
\"address_line_one\":\"Addr 1\",
\"town_city\":\"Town\",
\"county\":\"County\",
\"postcode\":\"Some Postcode\",
\"country\":\"England\"
}
},\"type\":\"sites\"}
]}"
When doing the same thing for a single representer I get the expected behaviour so I assume this issue is restricted to collections
From @ekosz on January 6, 2015 15:42
Given the code
module SongsRepresenter
include Roar::JSON::JSONAPI
type :songs
property :id
property :name
end
class Song < OpenStruct
include Roar::JSON::JSONAPI
include SongRepresenter
include Roar::Client
end
When I run
Song.new.get(uri: 'http://example.com/songs/1', as: 'application/json')
I get the error
NoMethodError: undefined method `deserialize' for #<Song>
from /roar-1.0.0/lib/roar/http_verbs.rb:65:in `handle_response'
from /roar-1.0.0/lib/roar/http_verbs.rb:40:in `get'
I then tried replacing Roar::JSON::JSONAPI
with Roar::JSON
in the client (as it does have #deserialize
there) but I then got this error instead:
TypeError: no implicit conversion of String into Integer
from /roar-1.0.0/lib/roar/json/json_api.rb:45:in `[]'
from /roar-1.0.0/lib/roar/json/json_api.rb:45:in `from_hash'
from /roar-1.0.0/lib/roar/json/json_api.rb:126:in `from_hash'
from /representable-2.1.3/lib/representable/json.rb:30:in `from_json'
from /roar-1.0.0/lib/roar/json.rb:21:in `from_json'
from /roar-1.0.0/lib/roar/json.rb:30:in `deserialize'
from /roar-1.0.0/lib/roar/http_verbs.rb:65:in `handle_response'
from /roar-1.0.0/lib/roar/http_verbs.rb:40:in `get'
As an aside, I also noticed the current README is out of date in a few locations regarding JSON-API. For example the README refers to the JSON-API module as Roar::JSON::JsonApi
but in the source its Roar::JSON::JSONAPI
.
Copied from original issue: trailblazer/roar#119
Attributes with null values are excluded from resource document.
Per the JSONAPI spec:
If a request does not include all of the attributes for a resource, the server MUST interpret the missing attributes as if they were included with their current values. The server MUST NOT interpret missing attributes as null values.
By not including attributes with null values, API consumers will treat their values as unchanged, rather than NULL.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.