From 4d1ed42095878bc3b50f04d5dc95d9f0063082db Mon Sep 17 00:00:00 2001 From: Lucas Hosseini Date: Fri, 14 Jul 2017 15:07:02 +0200 Subject: [PATCH] New DSL. (#13) --- lib/jsonapi/deserializable/relationship.rb | 17 ++- .../deserializable/relationship/dsl.rb | 11 +- lib/jsonapi/deserializable/resource.rb | 79 ++++++------- .../deserializable/resource/configuration.rb | 28 ----- lib/jsonapi/deserializable/resource/dsl.rb | 40 +++++-- spec/relationship/has_many_spec.rb | 8 +- spec/relationship/has_one_spec.rb | 8 +- spec/resource/DSL/attribute_spec.rb | 98 +++++++++------- spec/resource/DSL/attributes_spec.rb | 83 ++++++++++++++ spec/resource/DSL/has_many_spec.rb | 16 +-- spec/resource/DSL/has_one_spec.rb | 16 +-- spec/resource/DSL/id_spec.rb | 10 +- spec/resource/DSL/type_spec.rb | 4 +- spec/resource/configuration_spec.rb | 108 ------------------ spec/resource/reverse_mapping_spec.rb | 75 ++++++------ 15 files changed, 293 insertions(+), 308 deletions(-) delete mode 100644 lib/jsonapi/deserializable/resource/configuration.rb create mode 100644 spec/resource/DSL/attributes_spec.rb delete mode 100644 spec/resource/configuration_spec.rb diff --git a/lib/jsonapi/deserializable/relationship.rb b/lib/jsonapi/deserializable/relationship.rb index 5e2b321..c7821f3 100644 --- a/lib/jsonapi/deserializable/relationship.rb +++ b/lib/jsonapi/deserializable/relationship.rb @@ -25,6 +25,7 @@ module JSONAPI @document = payload @data = payload['data'] deserialize! + freeze end @@ -45,23 +46,19 @@ module JSONAPI end def deserialize_has_one + block = self.class.has_one_block + return {} unless block id = @data && @data['id'] type = @data && @data['type'] - if self.class.has_one_block - self.class.has_one_block.call(@document, id, type) - else - { id: id, type: type } - end + block.call(@document, id, type) end def deserialize_has_many + block = self.class.has_many_block + return {} unless block ids = @data.map { |ri| ri['id'] } types = @data.map { |ri| ri['type'] } - if self.class.has_many_block - self.class.has_many_block.call(@document, ids, types) - else - { ids: ids, types: types } - end + block.call(@document, ids, types) end end end diff --git a/lib/jsonapi/deserializable/relationship/dsl.rb b/lib/jsonapi/deserializable/relationship/dsl.rb index f7592f6..c87f35f 100644 --- a/lib/jsonapi/deserializable/relationship/dsl.rb +++ b/lib/jsonapi/deserializable/relationship/dsl.rb @@ -2,12 +2,19 @@ module JSONAPI module Deserializable class Relationship module DSL + DEFAULT_HAS_ONE_REL_BLOCK = proc do |_val, id, type| + { type: type, id: id } + end + DEFAULT_HAS_MANY_REL_BLOCK = proc do |_val, ids, types| + { types: types, ids: ids } + end + def has_one(&block) - self.has_one_block = block + self.has_one_block = block || DEFAULT_HAS_ONE_REL_BLOCK end def has_many(&block) - self.has_many_block = block + self.has_many_block = block || DEFAULT_HAS_MANY_REL_BLOCK end end end diff --git a/lib/jsonapi/deserializable/resource.rb b/lib/jsonapi/deserializable/resource.rb index 7a348f4..c607576 100644 --- a/lib/jsonapi/deserializable/resource.rb +++ b/lib/jsonapi/deserializable/resource.rb @@ -1,4 +1,3 @@ -require 'jsonapi/deserializable/resource/configuration' require 'jsonapi/deserializable/resource/dsl' require 'jsonapi/parser/resource' @@ -10,32 +9,24 @@ module JSONAPI class << self attr_accessor :type_block, :id_block, :attr_blocks, :has_one_rel_blocks, :has_many_rel_blocks, - :configuration + :default_attr_block, :default_has_one_rel_block, + :default_has_many_rel_block end - @class_cache = {} - - self.configuration = Configuration.new self.attr_blocks = {} self.has_one_rel_blocks = {} self.has_many_rel_blocks = {} def self.inherited(klass) super - klass.configuration = configuration.dup klass.type_block = type_block klass.id_block = id_block klass.attr_blocks = attr_blocks.dup klass.has_one_rel_blocks = has_one_rel_blocks.dup klass.has_many_rel_blocks = has_many_rel_blocks.dup - end - - def self.configure - yield(configuration) - end - - def self.[](name) - @class_cache[name] ||= Class.new(self) + klass.default_attr_block = default_attr_block + klass.default_has_one_rel_block = default_has_one_rel_block + klass.default_has_many_rel_block = default_has_many_rel_block end def self.call(payload) @@ -63,10 +54,6 @@ module JSONAPI private - def configuration - self.class.configuration - end - def register_mappings(keys, path) keys.each do |k| @reverse_mapping[k] = path @@ -81,16 +68,19 @@ module JSONAPI end def deserialize_type - block = self.class.type_block || configuration.default_type + block = self.class.type_block + return {} unless block + hash = block.call(@type) register_mappings(hash.keys, '/data/type') hash end def deserialize_id - return {} unless @id - block = self.class.id_block || configuration.default_id - hash = block.call(@id) + block = self.class.id_block + return {} unless @id && block + + hash = block.call(@id) register_mappings(hash.keys, '/data/id') hash end @@ -102,11 +92,10 @@ module JSONAPI end def deserialize_attr(key, val) - hash = if self.class.attr_blocks.key?(key) - self.class.attr_blocks[key].call(val) - else - configuration.default_attribute.call(key, val) - end + block = self.class.attr_blocks[key] || self.class.default_attr_block + return {} unless block + + hash = block.call(val, key) register_mappings(hash.keys, "/data/attributes/#{key}") hash end @@ -118,36 +107,38 @@ module JSONAPI end def deserialize_rel(key, val) - hash = if val['data'].is_a?(Array) - deserialize_has_many_rel(key, val) - else - deserialize_has_one_rel(key, val) - end - register_mappings(hash.keys, "/data/relationships/#{key}") - hash + if val['data'].is_a?(Array) + deserialize_has_many_rel(key, val) + else + deserialize_has_one_rel(key, val) + end end # rubocop: disable Metrics/AbcSize def deserialize_has_one_rel(key, val) + block = self.class.has_one_rel_blocks[key] || + self.class.default_has_one_rel_block + return {} unless block + id = val['data'] && val['data']['id'] type = val['data'] && val['data']['type'] - if self.class.has_one_rel_blocks.key?(key) - self.class.has_one_rel_blocks[key].call(val, id, type) - else - configuration.default_has_one.call(key, val, id, type) - end + hash = block.call(val, id, type, key) + register_mappings(hash.keys, "/data/relationships/#{key}") + hash end # rubocop: enable Metrics/AbcSize # rubocop: disable Metrics/AbcSize def deserialize_has_many_rel(key, val) + block = self.class.has_many_rel_blocks[key] || + self.class.default_has_many_rel_block + return {} unless block + ids = val['data'].map { |ri| ri['id'] } types = val['data'].map { |ri| ri['type'] } - if self.class.has_many_rel_blocks.key?(key) - self.class.has_many_rel_blocks[key].call(val, ids, types) - else - configuration.default_has_many.call(key, val, ids, types) - end + hash = block.call(val, ids, types, key) + register_mappings(hash.keys, "/data/relationships/#{key}") + hash end # rubocop: enable Metrics/AbcSize end diff --git a/lib/jsonapi/deserializable/resource/configuration.rb b/lib/jsonapi/deserializable/resource/configuration.rb deleted file mode 100644 index bfd2a78..0000000 --- a/lib/jsonapi/deserializable/resource/configuration.rb +++ /dev/null @@ -1,28 +0,0 @@ -module JSONAPI - module Deserializable - class Resource - class Configuration - DEFAULT_TYPE_BLOCK = proc { |t| { type: t } } - DEFAULT_ID_BLOCK = proc { |i| { id: i } } - DEFAULT_ATTR_BLOCK = proc { |k, v| { k.to_sym => v } } - DEFAULT_HAS_ONE_BLOCK = proc do |k, _, i, t| - { "#{k}_id".to_sym => i, "#{k}_type".to_sym => t } - end - DEFAULT_HAS_MANY_BLOCK = proc do |k, _, i, t| - { "#{k}_ids".to_sym => i, "#{k}_types".to_sym => t } - end - - attr_accessor :default_type, :default_id, :default_attribute, - :default_has_one, :default_has_many - - def initialize - self.default_type = DEFAULT_TYPE_BLOCK - self.default_id = DEFAULT_ID_BLOCK - self.default_attribute = DEFAULT_ATTR_BLOCK - self.default_has_one = DEFAULT_HAS_ONE_BLOCK - self.default_has_many = DEFAULT_HAS_MANY_BLOCK - end - end - end - end -end diff --git a/lib/jsonapi/deserializable/resource/dsl.rb b/lib/jsonapi/deserializable/resource/dsl.rb index 88cf357..16d4571 100644 --- a/lib/jsonapi/deserializable/resource/dsl.rb +++ b/lib/jsonapi/deserializable/resource/dsl.rb @@ -2,24 +2,50 @@ module JSONAPI module Deserializable class Resource module DSL + DEFAULT_TYPE_BLOCK = proc { |t| { type: t } } + DEFAULT_ID_BLOCK = proc { |i| { id: i } } + DEFAULT_ATTR_BLOCK = proc { |v, k| { k.to_sym => v } } + DEFAULT_HAS_ONE_BLOCK = proc do |_, i, t, k| + { "#{k}_id".to_sym => i, "#{k}_type".to_sym => t } + end + DEFAULT_HAS_MANY_BLOCK = proc do |_, i, t, k| + { "#{k}_ids".to_sym => i, "#{k}_types".to_sym => t } + end + def type(&block) - self.type_block = block + self.type_block = block || DEFAULT_TYPE_BLOCK end def id(&block) - self.id_block = block + self.id_block = block || DEFAULT_ID_BLOCK end def attribute(key, &block) - attr_blocks[key.to_s] = block + attr_blocks[key.to_s] = block || DEFAULT_ATTR_BLOCK end - def has_one(key, &block) - has_one_rel_blocks[key.to_s] = block + def attributes(*keys, &block) + if keys.empty? + self.default_attr_block = block || DEFAULT_ATTR_BLOCK + else + keys.each { |k| attribute(k, &block) } + end end - def has_many(key, &block) - has_many_rel_blocks[key.to_s] = block + def has_one(key = nil, &block) + if key + has_one_rel_blocks[key.to_s] = block || DEFAULT_HAS_ONE_BLOCK + else + self.default_has_one_rel_block = block || DEFAULT_HAS_ONE_BLOCK + end + end + + def has_many(key = nil, &block) + if key + has_many_rel_blocks[key.to_s] = block || DEFAULT_HAS_MANY_BLOCK + else + self.default_has_many_rel_block = block || DEFAULT_HAS_MANY_BLOCK + end end end end diff --git a/spec/relationship/has_many_spec.rb b/spec/relationship/has_many_spec.rb index 87c25ad..e79efb3 100644 --- a/spec/relationship/has_many_spec.rb +++ b/spec/relationship/has_many_spec.rb @@ -28,7 +28,9 @@ describe JSONAPI::Deserializable::Relationship, '.has_many' do end it 'defaults to creating ids and types fields' do - klass = Class.new(JSONAPI::Deserializable::Relationship) + klass = Class.new(JSONAPI::Deserializable::Relationship) do + has_many + end actual = klass.call(payload) expected = { ids: %w(bar baz), types: %w(foo foo) } @@ -56,10 +58,10 @@ describe JSONAPI::Deserializable::Relationship, '.has_many' do end context 'relationship is not to-many' do - it 'falls back to default to-one deserialization scheme' do + it 'does not deserialize relationship' do payload = { 'data' => nil } actual = deserializable_foo.call(payload) - expected = { id: nil, type: nil } + expected = {} expect(actual).to eq(expected) end diff --git a/spec/relationship/has_one_spec.rb b/spec/relationship/has_one_spec.rb index d000ac3..c6a77f5 100644 --- a/spec/relationship/has_one_spec.rb +++ b/spec/relationship/has_one_spec.rb @@ -22,7 +22,9 @@ describe JSONAPI::Deserializable::Relationship, '.has_one' do end it 'defaults to creating id and type fields' do - klass = Class.new(JSONAPI::Deserializable::Relationship) + klass = Class.new(JSONAPI::Deserializable::Relationship) do + has_one + end actual = klass.call(payload) expected = { id: 'bar', type: 'foo' } @@ -50,10 +52,10 @@ describe JSONAPI::Deserializable::Relationship, '.has_one' do end context 'relationship is not to-one' do - it 'falls back to default has_many deserialization scheme ' do + it 'does not deserialize relationship' do payload = { 'data' => [] } actual = deserializable_foo.call(payload) - expected = { ids: [], types: [] } + expected = {} expect(actual).to eq(expected) end diff --git a/spec/resource/DSL/attribute_spec.rb b/spec/resource/DSL/attribute_spec.rb index 1e4d48e..002bf51 100644 --- a/spec/resource/DSL/attribute_spec.rb +++ b/spec/resource/DSL/attribute_spec.rb @@ -1,55 +1,67 @@ require 'spec_helper' describe JSONAPI::Deserializable::Resource, '.attribute' do - it 'creates corresponding field if attribute is present' do - payload = { - 'data' => { - 'type' => 'foo', - 'attributes' => { 'foo' => 'bar' } - } - } - klass = Class.new(JSONAPI::Deserializable::Resource) do - attribute(:foo) { |foo| Hash[foo: foo] } - end - actual = klass.call(payload) - expected = { foo: 'bar', type: 'foo' } + context 'when attribute is present' do + context 'when a block is specified' do + it 'creates corresponding field' do + payload = { + 'data' => { + 'type' => 'foo', + 'attributes' => { 'foo' => 'bar' } + } + } + klass = Class.new(JSONAPI::Deserializable::Resource) do + attribute(:foo) { |foo| Hash[foo: foo] } + end + actual = klass.call(payload) + expected = { foo: 'bar' } - expect(actual).to eq(expected) + expect(actual).to eq(expected) + end + end + + context 'when no block is specified' do + it 'defaults to creating a field with same name' do + payload = { + 'data' => { + 'type' => 'foo', + 'attributes' => { 'foo' => 'bar' } + } + } + klass = Class.new(JSONAPI::Deserializable::Resource) do + attribute(:foo) + end + actual = klass.call(payload) + expected = { foo: 'bar' } + + expect(actual).to eq(expected) + end + end end - it 'does not create corresponding field if attribute is absent' do - payload = { 'data' => { 'type' => 'foo', 'attributes' => {} } } - klass = Class.new(JSONAPI::Deserializable::Resource) do - attribute(:foo) { |foo| Hash[foo: foo] } - end - actual = klass.call(payload) - expected = { type: 'foo' } + context 'when attribute is absent' do + it 'does not create corresponding field if attribute is absent' do + payload = { 'data' => { 'type' => 'foo', 'attributes' => {} } } + klass = Class.new(JSONAPI::Deserializable::Resource) do + attribute(:foo) { |foo| Hash[foo: foo] } + end + actual = klass.call(payload) + expected = {} - expect(actual).to eq(expected) + expect(actual).to eq(expected) + end end - it 'does not create corresponding field if no attribute specified' do - payload = { 'data' => { 'type' => 'foo' } } - klass = Class.new(JSONAPI::Deserializable::Resource) do - attribute(:foo) { |foo| Hash[foo: foo] } + context 'when attributes member is absent' do + it 'does not create corresponding field if no attribute specified' do + payload = { 'data' => { 'type' => 'foo' } } + klass = Class.new(JSONAPI::Deserializable::Resource) do + attribute(:foo) { |foo| Hash[foo: foo] } + end + actual = klass.call(payload) + expected = {} + + expect(actual).to eq(expected) end - actual = klass.call(payload) - expected = { type: 'foo' } - - expect(actual).to eq(expected) - end - - it 'defaults to creating a field with same name' do - payload = { - 'data' => { - 'type' => 'foo', - 'attributes' => { 'foo' => 'bar' } - } - } - klass = JSONAPI::Deserializable::Resource - actual = klass.call(payload) - expected = { foo: 'bar', type: 'foo' } - - expect(actual).to eq(expected) end end diff --git a/spec/resource/DSL/attributes_spec.rb b/spec/resource/DSL/attributes_spec.rb new file mode 100644 index 0000000..d7ab563 --- /dev/null +++ b/spec/resource/DSL/attributes_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +describe JSONAPI::Deserializable::Resource, '.attributes' do + context 'when no block is specified' do + context 'when no keys are specified' do + it 'defaults to creating fields with same name' do + payload = { + 'data' => { + 'type' => 'foo', + 'attributes' => { 'foo' => 'bar', 'baz' => 'foo' } + } + } + klass = Class.new(JSONAPI::Deserializable::Resource) do + attributes + end + actual = klass.call(payload) + expected = { foo: 'bar', baz: 'foo' } + + expect(actual).to eq(expected) + end + end + + context 'when keys are specified' do + it 'creates fields with same name for whitelisted attributes' do + payload = { + 'data' => { + 'type' => 'foo', + 'attributes' => { 'foo' => 'bar', 'baz' => 'foo', 'bar' => 'foo' } + } + } + klass = Class.new(JSONAPI::Deserializable::Resource) do + attributes :foo, :baz + end + actual = klass.call(payload) + expected = { foo: 'bar', baz: 'foo' } + + expect(actual).to eq(expected) + end + end + end + + context 'when a block is specified' do + context 'when no keys are specified' do + it 'defaults to creating fields with same name' do + payload = { + 'data' => { + 'type' => 'foo', + 'attributes' => { 'foo' => 'bar', 'baz' => 'foo' } + } + } + klass = Class.new(JSONAPI::Deserializable::Resource) do + attributes do |val, key| + Hash["#{key}_attr".to_sym => val] + end + end + actual = klass.call(payload) + expected = { foo_attr: 'bar', baz_attr: 'foo' } + + expect(actual).to eq(expected) + end + end + + context 'when keys are specified' do + it 'creates customized fields for whitelisted attributes' do + payload = { + 'data' => { + 'type' => 'foo', + 'attributes' => { 'foo' => 'bar', 'baz' => 'foo', 'bar' => 'foo' } + } + } + klass = Class.new(JSONAPI::Deserializable::Resource) do + attributes(:foo, :baz) do |val, key| + Hash["#{key}_attr".to_sym => val] + end + end + actual = klass.call(payload) + expected = { foo_attr: 'bar', baz_attr: 'foo' } + + expect(actual).to eq(expected) + end + end + end +end diff --git a/spec/resource/DSL/has_many_spec.rb b/spec/resource/DSL/has_many_spec.rb index e6ab544..9b581ae 100644 --- a/spec/resource/DSL/has_many_spec.rb +++ b/spec/resource/DSL/has_many_spec.rb @@ -29,16 +29,17 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do it 'creates corresponding fields' do actual = deserializable_foo.call(payload) expected = { foo_ids: %w(bar baz), foo_types: %w(foo foo), - foo_rel: payload['data']['relationships']['foo'], - type: 'foo' } + foo_rel: payload['data']['relationships']['foo'] } expect(actual).to eq(expected) end it 'defaults to creating a #{name}_ids and #{name}_types fields' do - klass = JSONAPI::Deserializable::Resource + klass = Class.new(JSONAPI::Deserializable::Resource) do + has_many + end actual = klass.call(payload) - expected = { foo_ids: %w(bar baz), foo_types: %w(foo foo), type: 'foo' } + expected = { foo_ids: %w(bar baz), foo_types: %w(foo foo) } expect(actual).to eq(expected) end @@ -58,8 +59,7 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do } actual = deserializable_foo.call(payload) expected = { foo_ids: [], foo_types: [], - foo_rel: payload['data']['relationships']['foo'], - type: 'foo' } + foo_rel: payload['data']['relationships']['foo'] } expect(actual).to eq(expected) end @@ -74,7 +74,7 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do } } actual = deserializable_foo.call(payload) - expected = { type: 'foo' } + expected = {} expect(actual).to eq(expected) end @@ -88,7 +88,7 @@ describe JSONAPI::Deserializable::Resource, '.has_many' do } } actual = deserializable_foo.call(payload) - expected = { type: 'foo' } + expected = {} expect(actual).to eq(expected) end diff --git a/spec/resource/DSL/has_one_spec.rb b/spec/resource/DSL/has_one_spec.rb index c283fe9..aad4efb 100644 --- a/spec/resource/DSL/has_one_spec.rb +++ b/spec/resource/DSL/has_one_spec.rb @@ -26,16 +26,17 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do it 'creates corresponding fields' do actual = deserializable_foo.call(payload) expected = { foo_id: 'bar', foo_type: 'foo', - foo_rel: payload['data']['relationships']['foo'], - type: 'foo' } + foo_rel: payload['data']['relationships']['foo'] } expect(actual).to eq(expected) end it 'defaults to creating #{name}_id and #{name}_type' do - klass = JSONAPI::Deserializable::Resource + klass = Class.new(JSONAPI::Deserializable::Resource) do + has_one + end actual = klass.call(payload) - expected = { foo_id: 'bar', foo_type: 'foo', type: 'foo' } + expected = { foo_id: 'bar', foo_type: 'foo' } expect(actual).to eq(expected) end @@ -56,8 +57,7 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do actual = deserializable_foo.call(payload) expected = { foo_id: nil, foo_type: nil, - foo_rel: payload['data']['relationships']['foo'], - type: 'foo' } + foo_rel: payload['data']['relationships']['foo'] } expect(actual).to eq(expected) end @@ -72,7 +72,7 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do } } actual = deserializable_foo.call(payload) - expected = { type: 'foo' } + expected = {} expect(actual).to eq(expected) end @@ -86,7 +86,7 @@ describe JSONAPI::Deserializable::Resource, '.has_one' do } } actual = deserializable_foo.call(payload) - expected = { type: 'foo' } + expected = {} expect(actual).to eq(expected) end diff --git a/spec/resource/DSL/id_spec.rb b/spec/resource/DSL/id_spec.rb index 31e872d..4025a24 100644 --- a/spec/resource/DSL/id_spec.rb +++ b/spec/resource/DSL/id_spec.rb @@ -7,7 +7,7 @@ describe JSONAPI::Deserializable::Resource, '.id' do id { |i| Hash[id: i] } end actual = klass.call(payload) - expected = { id: 'bar', type: 'foo' } + expected = { id: 'bar' } expect(actual).to eq(expected) end @@ -18,16 +18,18 @@ describe JSONAPI::Deserializable::Resource, '.id' do id { |i| Hash[id: i] } end actual = klass.call(payload) - expected = { type: 'foo' } + expected = {} expect(actual).to eq(expected) end it 'defaults to creating an id field' do payload = { 'data' => { 'type' => 'foo', 'id' => 'bar' } } - klass = JSONAPI::Deserializable::Resource + klass = Class.new(JSONAPI::Deserializable::Resource) do + id + end actual = klass.call(payload) - expected = { id: 'bar', type: 'foo' } + expected = { id: 'bar' } expect(actual).to eq(expected) end diff --git a/spec/resource/DSL/type_spec.rb b/spec/resource/DSL/type_spec.rb index ed6b031..c8a31f5 100644 --- a/spec/resource/DSL/type_spec.rb +++ b/spec/resource/DSL/type_spec.rb @@ -14,7 +14,9 @@ describe JSONAPI::Deserializable::Resource, '.type' do it 'defaults to creating a type field' do payload = { 'data' => { 'type' => 'foo' } } - klass = JSONAPI::Deserializable::Resource + klass = Class.new(JSONAPI::Deserializable::Resource) do + type + end actual = klass.call(payload) expected = { type: 'foo' } diff --git a/spec/resource/configuration_spec.rb b/spec/resource/configuration_spec.rb deleted file mode 100644 index ede5de5..0000000 --- a/spec/resource/configuration_spec.rb +++ /dev/null @@ -1,108 +0,0 @@ -require 'spec_helper' - -describe JSONAPI::Deserializable::Resource, '.configure' do - it 'overrides global default attribute deserialization scheme' do - payload = { - 'data' => { - 'type' => 'foo', - 'attributes' => { - 'foo' => 'bar', - 'baz' => 'foo' - } - } - } - begin - JSONAPI::Deserializable::Resource.configure do |cfg| - cfg.default_attribute = proc do |key, value| - { "custom_#{key}".to_sym => value } - end - end - klass = JSONAPI::Deserializable::Resource - actual = klass.call(payload) - expected = { custom_foo: 'bar', custom_baz: 'foo', type: 'foo' } - - expect(actual).to eq(expected) - ensure - JSONAPI::Deserializable::Resource.configuration = - JSONAPI::Deserializable::Resource::Configuration.new - end - end - - it 'overrides default attribute deserialization scheme' do - payload = { - 'data' => { - 'type' => 'foo', - 'attributes' => { - 'foo' => 'bar', - 'baz' => 'foo' - } - } - } - JSONAPI::Deserializable::Resource[:c1].configure do |cfg| - cfg.default_attribute = proc do |key, value| - { "custom_#{key}".to_sym => value } - end - end - klass = JSONAPI::Deserializable::Resource[:c1] - actual = klass.call(payload) - expected = { custom_foo: 'bar', custom_baz: 'foo', type: 'foo' } - - expect(actual).to eq(expected) - end - - it 'overrides the default has_many relationship deserialization scheme' do - payload = { - 'data' => { - 'type' => 'foo', - 'relationships' => { - 'foo' => { - 'data' => [{ 'type' => 'bar', 'id' => 'baz' }, - { 'type' => 'foo', 'id' => 'bar' }] - }, - 'bar' => { - 'data' => [{ 'type' => 'baz', 'id' => 'foo' }, - { 'type' => 'baz', 'id' => 'buz' }] - } - } - } - } - JSONAPI::Deserializable::Resource[:c1].configure do |cfg| - cfg.default_has_many = proc do |name, _value, ids, types| - { "custom_#{name}_ids".to_sym => ids, - "custom_#{name}_types".to_sym => types } - end - end - klass = JSONAPI::Deserializable::Resource[:c1] - actual = klass.call(payload) - expected = { custom_foo_ids: %w(baz bar), custom_foo_types: %w(bar foo), - custom_bar_ids: %w(foo buz), custom_bar_types: %w(baz baz), - type: 'foo' } - - expect(actual).to eq(expected) - end - - it 'overrides the default has_one relationship deserialization scheme' do - payload = { - 'data' => { - 'type' => 'foo', - 'relationships' => { - 'foo' => { 'data' => { 'type' => 'bar', 'id' => 'baz' } }, - 'bar' => { 'data' => { 'type' => 'foo', 'id' => 'bar' } } - } - } - } - JSONAPI::Deserializable::Resource[:c1].configure do |cfg| - cfg.default_has_one = proc do |name, _value, id, type| - { "custom_#{name}_id".to_sym => id, - "custom_#{name}_type".to_sym => type } - end - end - klass = JSONAPI::Deserializable::Resource[:c1] - actual = klass.call(payload) - expected = { custom_foo_id: 'baz', custom_foo_type: 'bar', - custom_bar_id: 'bar', custom_bar_type: 'foo', - type: 'foo' } - - expect(actual).to eq(expected) - end -end diff --git a/spec/resource/reverse_mapping_spec.rb b/spec/resource/reverse_mapping_spec.rb index dc16011..f106a5a 100644 --- a/spec/resource/reverse_mapping_spec.rb +++ b/spec/resource/reverse_mapping_spec.rb @@ -3,7 +3,9 @@ require 'spec_helper' describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do it 'generates reverse mapping for default type' do payload = { 'data' => { 'type' => 'foo' } } - klass = JSONAPI::Deserializable::Resource + klass = Class.new(JSONAPI::Deserializable::Resource) do + type + end actual = klass.new(payload).reverse_mapping expected = { type: '/data/type' } @@ -23,9 +25,11 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do it 'generates reverse mapping for default id' do payload = { 'data' => { 'type' => 'foo', 'id' => 'bar' } } - klass = JSONAPI::Deserializable::Resource + klass = Class.new(JSONAPI::Deserializable::Resource) do + id + end actual = klass.new(payload).reverse_mapping - expected = { id: '/data/id', type: '/data/type' } + expected = { id: '/data/id' } expect(actual).to eq(expected) end @@ -36,7 +40,7 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do id { |i| { custom_id: i } } end actual = klass.new(payload).reverse_mapping - expected = { custom_id: '/data/id', type: '/data/type' } + expected = { custom_id: '/data/id' } expect(actual).to eq(expected) end @@ -51,10 +55,11 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do } } } - klass = JSONAPI::Deserializable::Resource + klass = Class.new(JSONAPI::Deserializable::Resource) do + attributes + end actual = klass.new(payload).reverse_mapping - expected = { type: '/data/type', - foo: '/data/attributes/foo', + expected = { foo: '/data/attributes/foo', baz: '/data/attributes/baz' } expect(actual).to eq(expected) @@ -74,9 +79,7 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do attribute(:foo) { |foo| { custom_foo: foo } } end actual = klass.new(payload).reverse_mapping - expected = { type: '/data/type', - custom_foo: '/data/attributes/foo', - baz: '/data/attributes/baz' } + expected = { custom_foo: '/data/attributes/foo' } expect(actual).to eq(expected) end @@ -92,22 +95,19 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do } } klass = Class.new(JSONAPI::Deserializable::Resource) do - attribute(:foo) { |foo| { other_foo: foo } } - end - klass.configure do |config| - config.default_attribute = proc do |key, value| + attributes do |value, key| { "custom_#{key}".to_sym => value } end + attribute(:foo) { |foo| { other_foo: foo } } end actual = klass.new(payload).reverse_mapping - expected = { type: '/data/type', - other_foo: '/data/attributes/foo', + expected = { other_foo: '/data/attributes/foo', custom_baz: '/data/attributes/baz' } expect(actual).to eq(expected) end - it 'generates reverse mapping for default has_many' do + it 'generates reverse mapping for default has_one' do payload = { 'data' => { 'type' => 'foo', @@ -121,10 +121,11 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do } } } - klass = JSONAPI::Deserializable::Resource + klass = Class.new(JSONAPI::Deserializable::Resource) do + has_one + end actual = klass.new(payload).reverse_mapping - expected = { type: '/data/type', - foo_id: '/data/relationships/foo', + expected = { foo_id: '/data/relationships/foo', foo_type: '/data/relationships/foo', baz_id: '/data/relationships/baz', baz_type: '/data/relationships/baz' } @@ -147,20 +148,18 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do } } klass = Class.new(JSONAPI::Deserializable::Resource) do + has_one do |_val, id, type, key| + { "custom_#{key}_id".to_sym => id, + "custom_#{key}_type".to_sym => type } + end + has_one(:foo) do |_val, id, type| { other_foo_id: id, other_foo_type: type } end end - klass.configure do |config| - config.default_has_one = proc do |key, _val, id, type| - { "custom_#{key}_id".to_sym => id, - "custom_#{key}_type".to_sym => type } - end - end actual = klass.new(payload).reverse_mapping - expected = { type: '/data/type', - other_foo_id: '/data/relationships/foo', + expected = { other_foo_id: '/data/relationships/foo', other_foo_type: '/data/relationships/foo', custom_baz_id: '/data/relationships/baz', custom_baz_type: '/data/relationships/baz' } @@ -182,10 +181,11 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do } } } - klass = JSONAPI::Deserializable::Resource + klass = Class.new(JSONAPI::Deserializable::Resource) do + has_many + end actual = klass.new(payload).reverse_mapping - expected = { type: '/data/type', - foo_ids: '/data/relationships/foo', + expected = { foo_ids: '/data/relationships/foo', foo_types: '/data/relationships/foo', baz_ids: '/data/relationships/baz', baz_types: '/data/relationships/baz' } @@ -208,20 +208,17 @@ describe JSONAPI::Deserializable::Resource, '#reverse_mapping' do } } klass = Class.new(JSONAPI::Deserializable::Resource) do + has_many do |_val, ids, types, key| + { "custom_#{key}_ids".to_sym => ids, + "custom_#{key}_types".to_sym => types } + end has_many(:foo) do |_val, ids, types| { other_foo_ids: ids, other_foo_types: types } end end - klass.configure do |config| - config.default_has_many = proc do |key, _val, ids, types| - { "custom_#{key}_ids".to_sym => ids, - "custom_#{key}_types".to_sym => types } - end - end actual = klass.new(payload).reverse_mapping - expected = { type: '/data/type', - other_foo_ids: '/data/relationships/foo', + expected = { other_foo_ids: '/data/relationships/foo', other_foo_types: '/data/relationships/foo', custom_baz_ids: '/data/relationships/baz', custom_baz_types: '/data/relationships/baz' }