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,11 +5,9 @@ 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] }
end end
@ -23,11 +21,9 @@ 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)
end end
@ -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,11 +5,9 @@ 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
end end
@ -23,11 +21,9 @@ 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
end end
@ -43,11 +39,9 @@ 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|
Hash["#{key}_attr".to_sym => val] Hash["#{key}_attr".to_sym => val]
@ -63,11 +57,9 @@ 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|
Hash["#{key}_attr".to_sym => val] Hash["#{key}_attr".to_sym => val]

View File

@ -12,7 +12,6 @@ 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' => {
@ -23,13 +22,12 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do
} }
} }
} }
}
end end
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,7 +46,6 @@ 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' => {
@ -56,10 +53,9 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do
} }
} }
} }
}
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,11 +64,9 @@ 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,7 +12,6 @@ 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' => {
@ -20,13 +19,12 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do
} }
} }
} }
}
end end
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,7 +43,6 @@ 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' => {
@ -53,11 +50,10 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do
} }
} }
} }
}
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,11 +62,9 @@ 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,7 +5,6 @@ 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' => {
@ -17,7 +16,6 @@ describe JSONAPI::Deserializable::Resource, '.key_format' do
} }
} }
} }
}
end end
context 'when all fields are whitelisted' do context 'when all fields are whitelisted' do

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,14 +47,12 @@ 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
attributes attributes
end end
@ -67,14 +65,12 @@ 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
attribute(:foo) { |foo| { custom_foo: foo } } attribute(:foo) { |foo| { custom_foo: foo } }
end end
@ -86,14 +82,12 @@ 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
attributes do |value, key| attributes do |value, key|
{ "custom_#{key}".to_sym => value } { "custom_#{key}".to_sym => value }
@ -109,7 +103,6 @@ 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' => {
@ -120,7 +113,6 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
} }
} }
} }
}
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
has_one has_one
end end
@ -135,7 +127,6 @@ 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' => {
@ -146,7 +137,6 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
} }
} }
} }
}
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
has_one do |_val, id, type, key| has_one do |_val, id, type, key|
{ "custom_#{key}_id".to_sym => id, { "custom_#{key}_id".to_sym => id,
@ -169,7 +159,6 @@ 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' => {
@ -180,7 +169,6 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
} }
} }
} }
}
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
has_many has_many
end end
@ -195,7 +183,6 @@ 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' => {
@ -206,7 +193,6 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do
} }
} }
} }
}
klass = Class.new(JSONAPI::Deserializable::Resource) do klass = Class.new(JSONAPI::Deserializable::Resource) do
has_many do |_val, ids, types, key| has_many do |_val, ids, types, key|
{ "custom_#{key}_ids".to_sym => ids, { "custom_#{key}_ids".to_sym => ids,