Make resource deserialization local. (#17)

This commit is contained in:
Lucas Hosseini 2017-07-25 07:56:40 +02:00 committed by GitHub
parent 046c1de821
commit 1707ec44f5
9 changed files with 109 additions and 157 deletions

View File

@ -35,9 +35,9 @@ module JSONAPI
new(payload).to_h new(payload).to_h
end end
def initialize(payload) def initialize(payload, root: '/data')
@document = payload @data = payload || {}
@data = @document['data'] || {} @root = root
@type = @data['type'] @type = @data['type']
@id = @data['id'] @id = @data['id']
@attributes = @data['attributes'] || {} @attributes = @data['attributes'] || {}
@ -58,7 +58,7 @@ module JSONAPI
def register_mappings(keys, path) def register_mappings(keys, path)
keys.each do |k| keys.each do |k|
@reverse_mapping[k] = path @reverse_mapping[k] = @root + path
end end
end end
@ -74,7 +74,7 @@ module JSONAPI
return {} unless block return {} unless block
hash = block.call(@type) hash = block.call(@type)
register_mappings(hash.keys, '/data/type') register_mappings(hash.keys, '/type')
hash hash
end end
@ -83,7 +83,7 @@ module JSONAPI
return {} unless @id && block return {} unless @id && block
hash = block.call(@id) hash = block.call(@id)
register_mappings(hash.keys, '/data/id') register_mappings(hash.keys, '/id')
hash hash
end end
@ -98,7 +98,7 @@ module JSONAPI
return {} unless block return {} unless block
hash = block.call(val, self.class.key_formatter.call(key)) hash = block.call(val, self.class.key_formatter.call(key))
register_mappings(hash.keys, "/data/attributes/#{key}") register_mappings(hash.keys, "/attributes/#{key}")
hash hash
end end
@ -125,7 +125,7 @@ module JSONAPI
id = val['data'] && val['data']['id'] id = val['data'] && val['data']['id']
type = val['data'] && val['data']['type'] type = val['data'] && val['data']['type']
hash = block.call(val, id, type, self.class.key_formatter.call(key)) hash = block.call(val, id, type, self.class.key_formatter.call(key))
register_mappings(hash.keys, "/data/relationships/#{key}") register_mappings(hash.keys, "/relationships/#{key}")
hash hash
end end
# rubocop: enable Metrics/AbcSize # rubocop: enable Metrics/AbcSize
@ -139,7 +139,7 @@ module JSONAPI
ids = val['data'].map { |ri| ri['id'] } ids = val['data'].map { |ri| ri['id'] }
types = val['data'].map { |ri| ri['type'] } types = val['data'].map { |ri| ri['type'] }
hash = block.call(val, ids, types, self.class.key_formatter.call(key)) hash = block.call(val, ids, types, self.class.key_formatter.call(key))
register_mappings(hash.keys, "/data/relationships/#{key}") register_mappings(hash.keys, "/relationships/#{key}")
hash hash
end end
# rubocop: enable Metrics/AbcSize # rubocop: enable Metrics/AbcSize

View File

@ -5,10 +5,8 @@ describe JSONAPI::Deserializable::Resource, '.attribute' do
context 'when a block is specified' do context 'when a block is specified' do
it 'creates corresponding field' do it 'creates corresponding field' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => { 'foo' => 'bar' }
'attributes' => { 'foo' => 'bar' }
}
} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
attribute(:foo) { |foo| Hash[foo: foo] } attribute(:foo) { |foo| Hash[foo: foo] }
@ -23,10 +21,8 @@ describe JSONAPI::Deserializable::Resource, '.attribute' do
context 'when no block is specified' do context 'when no block is specified' do
it 'defaults to creating a field with same name' do it 'defaults to creating a field with same name' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => { 'foo' => 'bar' }
'attributes' => { 'foo' => 'bar' }
}
} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
attribute(:foo) attribute(:foo)
@ -41,7 +37,7 @@ describe JSONAPI::Deserializable::Resource, '.attribute' do
context 'when attribute is absent' do context 'when attribute is absent' do
it 'does not create corresponding field if attribute is absent' do it 'does not create corresponding field if attribute is absent' do
payload = { 'data' => { 'type' => 'foo', 'attributes' => {} } } payload = { 'type' => 'foo', 'attributes' => {} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
attribute(:foo) { |foo| Hash[foo: foo] } attribute(:foo) { |foo| Hash[foo: foo] }
end end
@ -54,7 +50,7 @@ describe JSONAPI::Deserializable::Resource, '.attribute' do
context 'when attributes member is absent' do context 'when attributes member is absent' do
it 'does not create corresponding field if no attribute specified' do it 'does not create corresponding field if no attribute specified' do
payload = { 'data' => { 'type' => 'foo' } } payload = { 'type' => 'foo' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
attribute(:foo) { |foo| Hash[foo: foo] } attribute(:foo) { |foo| Hash[foo: foo] }
end end

View File

@ -5,10 +5,8 @@ describe JSONAPI::Deserializable::Resource, '.attributes' do
context 'when no keys are specified' do context 'when no keys are specified' do
it 'defaults to creating fields with same name' do it 'defaults to creating fields with same name' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => { 'foo' => 'bar', 'baz' => 'foo' }
'attributes' => { 'foo' => 'bar', 'baz' => 'foo' }
}
} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
attributes attributes
@ -23,10 +21,8 @@ describe JSONAPI::Deserializable::Resource, '.attributes' do
context 'when keys are specified' do context 'when keys are specified' do
it 'creates fields with same name for whitelisted attributes' do it 'creates fields with same name for whitelisted attributes' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => { 'foo' => 'bar', 'baz' => 'foo', 'bar' => 'foo' }
'attributes' => { 'foo' => 'bar', 'baz' => 'foo', 'bar' => 'foo' }
}
} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
attributes :foo, :baz attributes :foo, :baz
@ -43,10 +39,8 @@ describe JSONAPI::Deserializable::Resource, '.attributes' do
context 'when no keys are specified' do context 'when no keys are specified' do
it 'defaults to creating fields with same name' do it 'defaults to creating fields with same name' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => { 'foo' => 'bar', 'baz' => 'foo' }
'attributes' => { 'foo' => 'bar', 'baz' => 'foo' }
}
} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
attributes do |val, key| attributes do |val, key|
@ -63,10 +57,8 @@ describe JSONAPI::Deserializable::Resource, '.attributes' do
context 'when keys are specified' do context 'when keys are specified' do
it 'creates customized fields for whitelisted attributes' do it 'creates customized fields for whitelisted attributes' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => { 'foo' => 'bar', 'baz' => 'foo', 'bar' => 'foo' }
'attributes' => { 'foo' => 'bar', 'baz' => 'foo', 'bar' => 'foo' }
}
} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
attributes(:foo, :baz) do |val, key| attributes(:foo, :baz) do |val, key|

View File

@ -12,15 +12,13 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do
context 'relationship is not empty' do context 'relationship is not empty' do
let(:payload) do let(:payload) do
{ {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {
'relationships' => { 'foo' => {
'foo' => { 'data' => [
'data' => [ { 'type' => 'foo', 'id' => 'bar' },
{ 'type' => 'foo', 'id' => 'bar' }, { 'type' => 'foo', 'id' => 'baz' }
{ 'type' => 'foo', 'id' => 'baz' } ]
]
}
} }
} }
} }
@ -29,7 +27,7 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do
it 'creates corresponding fields' do it 'creates corresponding fields' do
actual = deserializable_foo.call(payload) actual = deserializable_foo.call(payload)
expected = { foo_ids: %w(bar baz), foo_types: %w(foo foo), expected = { foo_ids: %w(bar baz), foo_types: %w(foo foo),
foo_rel: payload['data']['relationships']['foo'] } foo_rel: payload['relationships']['foo'] }
expect(actual).to eq(expected) expect(actual).to eq(expected)
end end
@ -48,18 +46,16 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do
context 'relationship is empty' do context 'relationship is empty' do
it 'creates corresponding fields' do it 'creates corresponding fields' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {
'relationships' => { 'foo' => {
'foo' => { 'data' => []
'data' => []
}
} }
} }
} }
actual = deserializable_foo.call(payload) actual = deserializable_foo.call(payload)
expected = { foo_ids: [], foo_types: [], expected = { foo_ids: [], foo_types: [],
foo_rel: payload['data']['relationships']['foo'] } foo_rel: payload['relationships']['foo'] }
expect(actual).to eq(expected) expect(actual).to eq(expected)
end end
@ -68,10 +64,8 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do
context 'relationship is absent' do context 'relationship is absent' do
it 'does not create corresponding fields' do it 'does not create corresponding fields' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {}
'relationships' => {}
}
} }
actual = deserializable_foo.call(payload) actual = deserializable_foo.call(payload)
expected = {} expected = {}
@ -82,11 +76,7 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do
context 'there is no relationships member' do context 'there is no relationships member' do
it 'does not create corresponding fields' do it 'does not create corresponding fields' do
payload = { payload = { 'type' => 'foo' }
'data' => {
'type' => 'foo'
}
}
actual = deserializable_foo.call(payload) actual = deserializable_foo.call(payload)
expected = {} expected = {}

View File

@ -12,12 +12,10 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do
context 'relationship is not nil' do context 'relationship is not nil' do
let(:payload) do let(:payload) do
{ {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {
'relationships' => { 'foo' => {
'foo' => { 'data' => { 'type' => 'foo', 'id' => 'bar' }
'data' => { 'type' => 'foo', 'id' => 'bar' }
}
} }
} }
} }
@ -26,7 +24,7 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do
it 'creates corresponding fields' do it 'creates corresponding fields' do
actual = deserializable_foo.call(payload) actual = deserializable_foo.call(payload)
expected = { foo_id: 'bar', foo_type: 'foo', expected = { foo_id: 'bar', foo_type: 'foo',
foo_rel: payload['data']['relationships']['foo'] } foo_rel: payload['relationships']['foo'] }
expect(actual).to eq(expected) expect(actual).to eq(expected)
end end
@ -45,19 +43,17 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do
context 'relationship value is nil' do context 'relationship value is nil' do
it 'creates corresponding fields' do it 'creates corresponding fields' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {
'relationships' => { 'foo' => {
'foo' => { 'data' => nil
'data' => nil
}
} }
} }
} }
actual = deserializable_foo.call(payload) actual = deserializable_foo.call(payload)
expected = { foo_id: nil, foo_type: nil, expected = { foo_id: nil, foo_type: nil,
foo_rel: payload['data']['relationships']['foo'] } foo_rel: payload['relationships']['foo'] }
expect(actual).to eq(expected) expect(actual).to eq(expected)
end end
@ -66,10 +62,8 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do
context 'relationship is absent' do context 'relationship is absent' do
it 'does not create corresponding fields' do it 'does not create corresponding fields' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {}
'relationships' => {}
}
} }
actual = deserializable_foo.call(payload) actual = deserializable_foo.call(payload)
expected = {} expected = {}
@ -80,11 +74,7 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do
context 'there is no relationships member' do context 'there is no relationships member' do
it 'does not create corresponding fields' do it 'does not create corresponding fields' do
payload = { payload = { 'type' => 'foo' }
'data' => {
'type' => 'foo'
}
}
actual = deserializable_foo.call(payload) actual = deserializable_foo.call(payload)
expected = {} expected = {}

View File

@ -2,7 +2,7 @@ require 'spec_helper'
describe JSONAPI::Deserializable::Resource, '.id' do describe JSONAPI::Deserializable::Resource, '.id' do
it 'creates corresponding field if id is present' do it 'creates corresponding field if id is present' do
payload = { 'data' => { 'type' => 'foo', 'id' => 'bar' } } payload = { 'type' => 'foo', 'id' => 'bar' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
id { |i| Hash[id: i] } id { |i| Hash[id: i] }
end end
@ -13,7 +13,7 @@ describe JSONAPI::Deserializable::Resource, '.id' do
end end
it 'does not create corresponding field if id is absent' do it 'does not create corresponding field if id is absent' do
payload = { 'data' => { 'type' => 'foo' } } payload = { 'type' => 'foo' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
id { |i| Hash[id: i] } id { |i| Hash[id: i] }
end end
@ -24,7 +24,7 @@ describe JSONAPI::Deserializable::Resource, '.id' do
end end
it 'defaults to creating an id field' do it 'defaults to creating an id field' do
payload = { 'data' => { 'type' => 'foo', 'id' => 'bar' } } payload = { 'type' => 'foo', 'id' => 'bar' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
id id
end end

View File

@ -5,16 +5,14 @@ describe JSONAPI::Deserializable::Resource, '.key_format' do
let(:payload) do let(:payload) do
{ {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => { 'foo' => 'bar', 'foo-bar' => 'baz' },
'attributes' => { 'foo' => 'bar', 'foo-bar' => 'baz' }, 'relationships' => {
'relationships' => { 'baz' => {
'baz' => { 'data' => nil
'data' => nil },
}, 'bar-baz' => {
'bar-baz' => { 'data' => []
'data' => []
}
} }
} }
} }

View File

@ -2,7 +2,7 @@ require 'spec_helper'
describe JSONAPI::Deserializable::Resource, '.type' do describe JSONAPI::Deserializable::Resource, '.type' do
it 'creates corresponding field' do it 'creates corresponding field' do
payload = { 'data' => { 'type' => 'foo' } } payload = { 'type' => 'foo' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
type { |t| Hash[type: t] } type { |t| Hash[type: t] }
end end
@ -13,7 +13,7 @@ describe JSONAPI::Deserializable::Resource, '.type' do
end end
it 'defaults to creating a type field' do it 'defaults to creating a type field' do
payload = { 'data' => { 'type' => 'foo' } } payload = { 'type' => 'foo' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
type type
end end

View File

@ -2,7 +2,7 @@ require 'spec_helper'
describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
it 'generates reverse mapping for default type' do it 'generates reverse mapping for default type' do
payload = { 'data' => { 'type' => 'foo' } } payload = { 'type' => 'foo' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
type type
end end
@ -13,7 +13,7 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
end end
it 'generates reverse mapping for overriden type' do it 'generates reverse mapping for overriden type' do
payload = { 'data' => { 'type' => 'foo' } } payload = { 'type' => 'foo' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
type { |t| { custom_type: t } } type { |t| { custom_type: t } }
end end
@ -24,7 +24,7 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
end end
it 'generates reverse mapping for default id' do it 'generates reverse mapping for default id' do
payload = { 'data' => { 'type' => 'foo', 'id' => 'bar' } } payload = { 'type' => 'foo', 'id' => 'bar' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
id id
end end
@ -35,7 +35,7 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
end end
it 'generates reverse mapping for overriden id' do it 'generates reverse mapping for overriden id' do
payload = { 'data' => { 'type' => 'foo', 'id' => 'bar' } } payload = { 'type' => 'foo', 'id' => 'bar' }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
id { |i| { custom_id: i } } id { |i| { custom_id: i } }
end end
@ -47,12 +47,10 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
it 'generates reverse mapping for default attributes' do it 'generates reverse mapping for default attributes' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => {
'attributes' => { 'foo' => 'bar',
'foo' => 'bar', 'baz' => 'fiz'
'baz' => 'fiz'
}
} }
} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
@ -67,12 +65,10 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
it 'generates reverse mapping for locally overriden attributes' do it 'generates reverse mapping for locally overriden attributes' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => {
'attributes' => { 'foo' => 'bar',
'foo' => 'bar', 'baz' => 'fiz'
'baz' => 'fiz'
}
} }
} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
@ -86,12 +82,10 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
it 'generates reverse mapping for globally overriden attributes' do it 'generates reverse mapping for globally overriden attributes' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'attributes' => {
'attributes' => { 'foo' => 'bar',
'foo' => 'bar', 'baz' => 'fiz'
'baz' => 'fiz'
}
} }
} }
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
@ -109,15 +103,13 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
it 'generates reverse mapping for default has_one' do it 'generates reverse mapping for default has_one' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {
'relationships' => { 'foo' => {
'foo' => { 'data' => nil
'data' => nil },
}, 'baz' => {
'baz' => { 'data' => nil
'data' => nil
}
} }
} }
} }
@ -135,15 +127,13 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
it 'generates reverse mapping for overriden has_one' do it 'generates reverse mapping for overriden has_one' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {
'relationships' => { 'foo' => {
'foo' => { 'data' => nil
'data' => nil },
}, 'baz' => {
'baz' => { 'data' => nil
'data' => nil
}
} }
} }
} }
@ -169,15 +159,13 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
it 'generates reverse mapping for default has_many' do it 'generates reverse mapping for default has_many' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {
'relationships' => { 'foo' => {
'foo' => { 'data' => []
'data' => [] },
}, 'baz' => {
'baz' => { 'data' => []
'data' => []
}
} }
} }
} }
@ -195,15 +183,13 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
it 'generates reverse mapping for overriden has_many' do it 'generates reverse mapping for overriden has_many' do
payload = { payload = {
'data' => { 'type' => 'foo',
'type' => 'foo', 'relationships' => {
'relationships' => { 'foo' => {
'foo' => { 'data' => []
'data' => [] },
}, 'baz' => {
'baz' => { 'data' => []
'data' => []
}
} }
} }
} }