jsonapi-object-mapper 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35390dfcd2da06f3fadc628d41986b07891efa2590a4271896a9717ed44ed7b6
4
- data.tar.gz: eb48c0ab5e7409c53ba03828704adcef9e27377443498b4f5a97430cde8cd5ad
3
+ metadata.gz: 8f1ffe31b2bc6266364e9ce22de8f3326fe91d7b6209f421a12a70d9cbe68cdc
4
+ data.tar.gz: 8603e6c43ae05a4159811a64fe041e2470cdb011b24223b759d405ddbf2e8e01
5
5
  SHA512:
6
- metadata.gz: 96a5b2682366e3d45ff87b768c1ace3463847721293a0237b7661fe4c0d19b2049ed2f1616b73f9d47eea4a62b69d7fb359e1e86753ff1123388c171a7f62e97
7
- data.tar.gz: 5d75d3a36a2cd47ef118dcd4e51f31129e5f7cd8e8e7b0daaa93077e8d809551261f52a2ef321e4c20a30a8a82d75dad2344056413e95d740234978b45d6fe72
6
+ metadata.gz: 1d6a3abc3c030e77d4ca61c26610c46772e3fa1f98b7add1f486502e42036998d7f76a7c7896375efd76f336558b6e0e6d0c1bec7611a76727b7cb6101194a27
7
+ data.tar.gz: 05226150aac758f77f1d8e0af7b13624b97194e42dd075f4e80b544cb674414563a69ed3fc609c42f44585bd47f44c1c8440320c5f79860f15e716d5eb7f02f1
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- jsonapi-object-mapper (0.4.0)
4
+ jsonapi-object-mapper (0.5.0)
5
5
  oj (~> 3.0)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "lib"))
4
- require "jsonapi_object_mapper/version"
4
+ require "jsonapi-object-mapper/version"
5
5
 
6
6
  Gem::Specification.new do |spec|
7
7
  spec.name = "jsonapi-object-mapper"
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jsonapi-object-mapper/parser/errors"
4
+
5
+ module JsonAPIObjectMapper
6
+ module Deserializer
7
+ class Collection
8
+ include Enumerable
9
+ include JsonAPIObjectMapper::Parser::Errors
10
+
11
+ attr_accessor :collection_data
12
+
13
+ def initialize(parser, klass:)
14
+ raise ArgumentError, "Must provide a valid resource klass" unless klass.is_a?(Class)
15
+ raise ArgumentError, "Must provide a parsed document" unless parser.is_a?(JsonAPIObjectMapper::Parser::Document)
16
+ @errors = parser.errors
17
+ @collection_data =
18
+ if document_invalid?
19
+ []
20
+ else
21
+ Array(parser.document["data"]).map do |doc|
22
+ klass.new(parser, document: doc)
23
+ end
24
+ end.freeze
25
+
26
+ freeze
27
+ end
28
+
29
+ def each
30
+ @collection_data.each do |data|
31
+ yield data
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "jsonapi_object_mapper/deserializer/included_resources"
4
-
5
3
  module JsonAPIObjectMapper
6
4
  module Deserializer
7
5
  module DSL
@@ -43,13 +41,16 @@ module JsonAPIObjectMapper
43
41
 
44
42
  module ClassMethods
45
43
  def initialize(*args)
46
- super
47
44
  @_class_attributes = {}
48
45
  @_class_relationships = {}
46
+ super
49
47
  end
50
48
 
51
49
  def to_hash
52
- [@_class_attributes, @_class_relationships].reduce(:merge)
50
+ hashed_relationships = @_class_relationships.map do |key, value|
51
+ { key => value.respond_to?(:to_hash) ? value.to_hash : value }
52
+ end
53
+ [@_class_attributes, *hashed_relationships].reduce({}, :merge)
53
54
  end
54
55
  alias to_h to_hash
55
56
 
@@ -73,13 +74,15 @@ module JsonAPIObjectMapper
73
74
  end
74
75
 
75
76
  def assign_relationship(key, value)
76
- block = self.class.rel_blocks[key.to_s] || DEFAULT_ATTR_BLOCK
77
- rel_embed_class = self.class.rel_options[key.to_s]
77
+ block = self.class.rel_blocks[key.to_s] || DEFAULT_ATTR_BLOCK
78
+ rel_embed_class = self.class.rel_options[key.to_s]
79
+ rel_value = @includes.fetch(value)
80
+
78
81
  @_class_relationships[key.to_s] =
79
- if rel_embed_class.respond_to?(:embed!)
80
- block.call(rel_embed_class.embed!(value))
82
+ if rel_value != value && rel_embed_class.respond_to?(:embed!)
83
+ block.call(rel_embed_class.embed!(rel_value))
81
84
  else
82
- block.call(value)
85
+ block.call(rel_value)
83
86
  end
84
87
  end
85
88
 
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "jsonapi_object_mapper/deserializer/dsl"
4
- require "jsonapi_object_mapper/deserializer/included_resources"
3
+ require "jsonapi-object-mapper/deserializer/dsl"
4
+ require "jsonapi-object-mapper/deserializer/collection"
5
+ require "jsonapi-object-mapper/parser/errors"
5
6
 
6
7
  module JsonAPIObjectMapper
7
8
  module Deserializer
8
9
  class Resource
10
+ include JsonAPIObjectMapper::Parser::Errors
9
11
  extend DSL
10
12
 
11
13
  class << self
@@ -24,37 +26,48 @@ module JsonAPIObjectMapper
24
26
  klass.instance_variable_set("@type_block", type_block)
25
27
  end
26
28
 
27
- def self.call_collection(document)
28
- parsed_document = document.is_a?(Hash) ? document : ::Oj.load(document)
29
- parsed_includes = IncludedResources.load(parsed_document["included"])
30
- Array(parsed_document["data"]).map { |doc| new(doc, parsed_includes) }
31
- end
32
-
33
- def self.call(document, parsed_includes = nil)
34
- parsed_document = document.is_a?(Hash) ? document : ::Oj.load(document)
35
- parsed_includes ||= IncludedResources.load(parsed_document["included"])
36
- new(parsed_document["data"], parsed_includes)
29
+ def self.call(document)
30
+ parser = JsonAPIObjectMapper::Parser::Document.new(document)
31
+ if parser.document["data"].is_a?(Array) || parser.invalid?
32
+ Collection.new(parser, klass: self)
33
+ else
34
+ new(parser)
35
+ end
37
36
  end
38
37
 
39
38
  def self.embed!(attributes)
40
- new("attributes" => attributes)
39
+ parser = JsonAPIObjectMapper::Parser::Document.new("attributes" => attributes)
40
+ new(parser)
41
41
  end
42
42
 
43
- def initialize(payload = nil, included = nil)
43
+ def initialize(parser, document: nil)
44
44
  super()
45
- data = payload || {}
46
- @id = data["id"]
47
- @type = data["type"]
48
- @attributes = data["attributes"] || {}
49
- @relationships = data["relationships"] || {}
50
- @includes = IncludedResources.load(included)
51
- deserialize!
45
+ raise ArgumentError, "Must provide a parsed document" unless parser.is_a?(JsonAPIObjectMapper::Parser::Document)
46
+ @errors = parser.errors
47
+
48
+ if document_valid?
49
+ @includes = parser.includes
50
+ @data = document_data(parser, document)
51
+ @id = @data["id"]
52
+ @type = @data["type"]
53
+ @attributes = @data.fetch("attributes", {})
54
+ @relationships = @data.fetch("relationships", {})
55
+ deserialize!
56
+ end
52
57
 
53
58
  freeze
54
59
  end
55
60
 
61
+ def each
62
+ yield self
63
+ end
64
+
56
65
  private
57
66
 
67
+ def document_data(parser, document)
68
+ document.nil? ? (parser.document["data"] || parser.document) : (document["data"] || document)
69
+ end
70
+
58
71
  def deserialize!
59
72
  deserialize_id_type!
60
73
  deserialize_attributes!
@@ -62,8 +75,8 @@ module JsonAPIObjectMapper
62
75
  end
63
76
 
64
77
  def deserialize_id_type!
65
- assign_attribute("id", self.class.id_block&.call(@id))
66
- assign_attribute("type", self.class.type_block&.call(@id))
78
+ assign_attribute("id", self.class.id_block.call(@id)) if self.class.id_block
79
+ assign_attribute("type", self.class.type_block.call(@type)) if self.class.type_block
67
80
  end
68
81
 
69
82
  def deserialize_attributes!
@@ -83,7 +96,7 @@ module JsonAPIObjectMapper
83
96
 
84
97
  def initialize_relationship(rel_type, rel_value)
85
98
  return unless include_relationship?(rel_type)
86
- assign_relationship(rel_type, @includes.fetch(rel_value["data"]))
99
+ assign_relationship(rel_type, rel_value["data"])
87
100
  end
88
101
  end
89
102
  end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "jsonapi-object-mapper/parser/included_resources"
4
+ require "jsonapi-object-mapper/parser/errors"
5
+
6
+ module JsonAPIObjectMapper
7
+ module Parser
8
+ class Document
9
+ include Errors
10
+
11
+ attr_accessor :document, :includes
12
+
13
+ def initialize(document)
14
+ @document = document.is_a?(Hash) ? document : ::Oj.load(document)
15
+ @includes = IncludedResources.load(@document["included"])
16
+ @errors = deserialize_errors!.freeze
17
+ freeze
18
+ end
19
+
20
+ def deserialize_errors!
21
+ return [] unless @document.key?("errors")
22
+ Set.new(@document["errors"]) { |error| OpenStruct.new(error) }
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JsonAPIObjectMapper
4
+ module Parser
5
+ module Errors
6
+ attr_accessor :errors
7
+
8
+ def valid?
9
+ @errors.nil? || @errors.empty?
10
+ end
11
+ alias document_valid? valid?
12
+
13
+ def invalid?
14
+ !valid?
15
+ end
16
+ alias errors? invalid?
17
+ alias document_invalid? invalid?
18
+ end
19
+ end
20
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JsonAPIObjectMapper
4
- module Deserializer
4
+ module Parser
5
5
  class IncludedResources
6
6
  extend Forwardable
7
7
 
@@ -15,14 +15,15 @@ module JsonAPIObjectMapper
15
15
 
16
16
  def initialize(included_resources = [])
17
17
  included_resources ||= []
18
-
19
18
  @resource = included_resources.each_with_object({}) do |include, hash|
20
19
  hash[format_key(include)] = include["attributes"]
21
20
  end
21
+
22
+ freeze
22
23
  end
23
24
 
24
- def fetch(relationship)
25
- @resource.fetch(format_key(relationship), relationship)
25
+ def fetch(relationship, fallback = nil)
26
+ @resource.fetch(format_key(relationship), fallback || relationship)
26
27
  end
27
28
 
28
29
  def included?(relationship)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JsonAPIObjectMapper
4
- VERSION = "0.4.0"
4
+ VERSION = "0.5.0"
5
5
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "oj"
4
+ require "jsonapi-object-mapper/parser/document"
5
+ require "jsonapi-object-mapper/deserializer/resource"
6
+ require "jsonapi-object-mapper/version"
7
+
8
+ module JsonAPIObjectMapper
9
+ end
@@ -21,7 +21,7 @@ module JsonAPIObjectMapper
21
21
  attribute :baz
22
22
  end
23
23
 
24
- actual = klass.new(payload)
24
+ actual = klass.call(payload)
25
25
  expect(actual.foo).to eq("bar")
26
26
  expect(actual.baz).to eq("nas")
27
27
  end
@@ -34,7 +34,7 @@ module JsonAPIObjectMapper
34
34
  end
35
35
  end
36
36
 
37
- actual = klass.new(payload)
37
+ actual = klass.call(payload)
38
38
  expect(actual.baz).to eq("THIS IS THE REAL DEAL!")
39
39
  end
40
40
  end
@@ -55,13 +55,15 @@ module JsonAPIObjectMapper
55
55
 
56
56
  context "Has included Resource" do
57
57
  let(:included_payload) do
58
- [
59
- {
60
- "id" => "1",
61
- "type" => "photo",
62
- "attributes" => { "image" => "good_day_sir.jpg" },
63
- },
64
- ]
58
+ payload.merge(
59
+ "included" => [
60
+ {
61
+ "id" => "1",
62
+ "type" => "photo",
63
+ "attributes" => { "image" => "good_day_sir.jpg" },
64
+ },
65
+ ],
66
+ )
65
67
  end
66
68
 
67
69
  it "Should embed the included resource into the relationship" do
@@ -69,7 +71,7 @@ module JsonAPIObjectMapper
69
71
  has_one :photo
70
72
  end
71
73
 
72
- actual = klass.new(payload, included_payload)
74
+ actual = klass.call(included_payload)
73
75
  expect(actual.photo).to eq("image" => "good_day_sir.jpg")
74
76
  end
75
77
 
@@ -82,7 +84,7 @@ module JsonAPIObjectMapper
82
84
  has_one :photo, embed_with: photo_klass
83
85
  end
84
86
 
85
- actual = core_klass.new(payload, included_payload)
87
+ actual = core_klass.call(included_payload)
86
88
  expect(actual.photo).to be_a(photo_klass)
87
89
  expect(actual.photo.image).to eq("good_day_sir.jpg")
88
90
  end
@@ -94,11 +96,11 @@ module JsonAPIObjectMapper
94
96
  has_one :photo
95
97
  end
96
98
 
97
- actual = klass.new(payload)
99
+ actual = klass.call(payload)
98
100
  expect(actual.photo).to eq("id" => "1", "type" => "photo")
99
101
  end
100
102
 
101
- it "Should set the attribute to nil when no included resource is present" do
103
+ it "Should set the default relationship values if no includes can be found" do
102
104
  photo_klass = Class.new(described_class) do
103
105
  attribute :image
104
106
  end
@@ -107,9 +109,9 @@ module JsonAPIObjectMapper
107
109
  has_one :photo, embed_with: photo_klass
108
110
  end
109
111
 
110
- actual = core_klass.new(payload)
111
- expect(actual.photo).to be_a(photo_klass)
112
- expect(actual.photo.image).to be_nil
112
+ actual = core_klass.call(payload)
113
+ expect(actual.photo).to be_a(Hash)
114
+ expect(actual.photo).to eq("id" => "1", "type" => "photo")
113
115
  end
114
116
  end
115
117
  end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/setup"
4
- require "jsonapi_object_mapper"
4
+ require "jsonapi-object-mapper"
5
5
 
6
6
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require File.expand_path(f) }
7
7
  Dir["#{File.dirname(__FILE__)}/**/*examples.rb"].each { |f| require f }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jsonapi-object-mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - George Protacio-Karaszi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-05 00:00:00.000000000 Z
11
+ date: 2018-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: oj
@@ -89,12 +89,15 @@ files:
89
89
  - Rakefile
90
90
  - bin/console
91
91
  - bin/setup
92
- - jsonapi_object_mapper.gemspec
93
- - lib/jsonapi_object_mapper.rb
94
- - lib/jsonapi_object_mapper/deserializer/dsl.rb
95
- - lib/jsonapi_object_mapper/deserializer/included_resources.rb
96
- - lib/jsonapi_object_mapper/deserializer/resource.rb
97
- - lib/jsonapi_object_mapper/version.rb
92
+ - jsonapi-object-mapper.gemspec
93
+ - lib/jsonapi-object-mapper.rb
94
+ - lib/jsonapi-object-mapper/deserializer/collection.rb
95
+ - lib/jsonapi-object-mapper/deserializer/dsl.rb
96
+ - lib/jsonapi-object-mapper/deserializer/resource.rb
97
+ - lib/jsonapi-object-mapper/parser/document.rb
98
+ - lib/jsonapi-object-mapper/parser/errors.rb
99
+ - lib/jsonapi-object-mapper/parser/included_resources.rb
100
+ - lib/jsonapi-object-mapper/version.rb
98
101
  - spec/deserializer/resource_spec.rb
99
102
  - spec/jsonapi_object_mapper_spec.rb
100
103
  - spec/spec_helper.rb
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "oj"
4
- require "jsonapi_object_mapper/deserializer/resource"
5
- require "jsonapi_object_mapper/version"
6
-
7
- module JsonAPIObjectMapper
8
- end