jsonapi-object-mapper 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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