simple-jsonapi-deserializer 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 980fce8cc9deb0eed44e09bf5a0157695511c4326cd3a46996765fafad7f67e2
4
+ data.tar.gz: b220ec286904182dac33abf44ffab207d1e130cce5a8e82f0a0d9a80be601b48
5
+ SHA512:
6
+ metadata.gz: 91b4be24bff49bc7488bace9a390b34808e9e60d2a5557d8808fce67c743dedf1f04bd57d88df5b95f2ca9a67e4318bb049022dabf513438ced57be66a1f1f0b
7
+ data.tar.gz: 9624fe3086ba808588cee162df42c65a44ad7ae348f400260e24eeecd817b16b42885fab93d42f71746b721e7f1f1711c816c60e8f129d81acb0c08ba4d3340e
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ /.bundle
2
+ /html/
3
+ /pkg/
4
+ /vendor/cache/*.gem
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation
data/.rubocop.yml ADDED
@@ -0,0 +1,14 @@
1
+ Metrics/BlockLength:
2
+ Exclude:
3
+ - 'spec/**/*.rb'
4
+
5
+ Metrics/LineLength:
6
+ Max: 120
7
+
8
+ Style/Documentation:
9
+ Exclude:
10
+ - '**/*.rb'
11
+
12
+ Style/SpecialGlobalVars:
13
+ Exclude:
14
+ - 'simple-jsonapi-deserializer.gemspec'
data/ChangeLog.md ADDED
@@ -0,0 +1,4 @@
1
+ ### 0.1.0 / 2018-09-18
2
+
3
+ * Initial release:
4
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,50 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ simple-jsonapi-deserializer (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ ast (2.4.0)
10
+ diff-lcs (1.3)
11
+ parallel (1.12.1)
12
+ parser (2.4.0.2)
13
+ ast (~> 2.3)
14
+ powerpack (0.1.1)
15
+ rainbow (3.0.0)
16
+ rspec (3.8.0)
17
+ rspec-core (~> 3.8.0)
18
+ rspec-expectations (~> 3.8.0)
19
+ rspec-mocks (~> 3.8.0)
20
+ rspec-core (3.8.0)
21
+ rspec-support (~> 3.8.0)
22
+ rspec-expectations (3.8.1)
23
+ diff-lcs (>= 1.2.0, < 2.0)
24
+ rspec-support (~> 3.8.0)
25
+ rspec-mocks (3.8.0)
26
+ diff-lcs (>= 1.2.0, < 2.0)
27
+ rspec-support (~> 3.8.0)
28
+ rspec-support (3.8.0)
29
+ rspec_snapshot_matcher (0.1.0)
30
+ rubocop (0.52.0)
31
+ parallel (~> 1.10)
32
+ parser (>= 2.4.0.2, < 3.0)
33
+ powerpack (~> 0.1)
34
+ rainbow (>= 2.2.2, < 4.0)
35
+ ruby-progressbar (~> 1.7)
36
+ unicode-display_width (~> 1.0, >= 1.0.1)
37
+ ruby-progressbar (1.9.0)
38
+ unicode-display_width (1.3.0)
39
+
40
+ PLATFORMS
41
+ ruby
42
+
43
+ DEPENDENCIES
44
+ rspec (~> 3.8.0, >= 3.0.0)
45
+ rspec_snapshot_matcher (~> 0.1.0)
46
+ rubocop (~> 0.52.0)
47
+ simple-jsonapi-deserializer!
48
+
49
+ BUNDLED WITH
50
+ 1.16.1
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2018 Gerrit Seger
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # simple-jsonapi-deserializer
2
+
3
+ Painless, zero config JSON API deserialization.
4
+
5
+ ## Installation
6
+
7
+ Add to you application's Gemfile
8
+
9
+ ```ruby
10
+ gem 'simple-jsonapi-deserializer'
11
+ ```
12
+
13
+ and install
14
+
15
+ ```sh
16
+ $ bundle
17
+ ```
18
+
19
+ or install it directly
20
+
21
+ ```shell
22
+ $ gem install 'simple-jsonapi-deserializer'
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```ruby
28
+ SimpleJSONAPIDeserializer.deserialize(json_api_hash)
29
+ ```
30
+
31
+ ## Examples
32
+
33
+ ```ruby
34
+ json_api_hash = {
35
+ 'data' => {
36
+ 'id' => '1234',
37
+ 'type' => 'planets',
38
+ 'attributes' => {
39
+ 'name' => 'Earth'
40
+ },
41
+ 'relationships' => {
42
+ 'satellites' => {
43
+ 'data' => [
44
+ {
45
+ 'id' => '914',
46
+ 'type' => 'satellites'
47
+ }
48
+ ]
49
+ }
50
+ }
51
+ },
52
+ 'include' => [
53
+ {
54
+ 'id' => '914',
55
+ 'type' => 'satellites',
56
+ 'attributes' => {
57
+ 'name' => 'Moon'
58
+ }
59
+ }
60
+ ]
61
+ }
62
+
63
+ SimpleJSONAPIDeserializer.deserialize(json_api_hash)
64
+
65
+ # =>
66
+ # {
67
+ # "id" => "1234",
68
+ # "type" => "planets",
69
+ # "name" => "Earth",
70
+ # "satellites" => [
71
+ # {
72
+ # "id" => "914"
73
+ # "type" => "satellites",
74
+ # "name" => "Moon",
75
+ # }
76
+ # ]
77
+ # }
78
+ ```
79
+
80
+ ## License
81
+
82
+ MIT
@@ -0,0 +1,14 @@
1
+ require 'simple_jsonapi_deserializer/deserializer'
2
+ require 'simple_jsonapi_deserializer/parse_error'
3
+ require 'simple_jsonapi_deserializer/resource'
4
+ require 'simple_jsonapi_deserializer/resource/cache'
5
+ require 'simple_jsonapi_deserializer/resource/includes'
6
+ require 'simple_jsonapi_deserializer/version'
7
+
8
+ module SimpleJSONAPIDeserializer
9
+ class << self
10
+ def deserialize(resource)
11
+ Deserializer.new(resource).deserialize
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,35 @@
1
+ module SimpleJSONAPIDeserializer
2
+ class Deserializer
3
+ def initialize(resource)
4
+ @resource = resource
5
+ end
6
+
7
+ def deserialize
8
+ return Resource.new(data, includes, cache).deserialize unless data.is_a?(Array)
9
+
10
+ data.map do |resource|
11
+ Resource
12
+ .new(resource, includes, cache)
13
+ .deserialize(without_attributes: true)
14
+ end
15
+ rescue TypeError, NoMethodError => e
16
+ raise ParseError, e
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :resource
22
+
23
+ def cache
24
+ Resource::Cache.new
25
+ end
26
+
27
+ def data
28
+ resource['data'] || {}
29
+ end
30
+
31
+ def includes
32
+ Resource::Includes.new(resource['include'] || [])
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ module SimpleJSONAPIDeserializer
2
+ class ParseError < StandardError; end
3
+ end
@@ -0,0 +1,91 @@
1
+ module SimpleJSONAPIDeserializer
2
+ class Resource
3
+ def initialize(resource, includes, cache)
4
+ @resource = resource
5
+ @includes = includes
6
+ @cache = cache
7
+ end
8
+
9
+ def deserialize(without_attributes: false, without_relationships: false)
10
+ {}.tap do |deserialized_resource|
11
+ deserialized_resource.merge!(attributes) unless without_attributes
12
+ deserialized_resource.merge!(deserialized_relationships) unless without_relationships
13
+ deserialized_resource['type'] = type if type
14
+ deserialized_resource['id'] = id if id
15
+ end
16
+ end
17
+
18
+ def id
19
+ resource['id']
20
+ end
21
+
22
+ def type
23
+ resource['type']
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :cache, :includes, :resource
29
+
30
+ def attributes
31
+ resource['attributes'] || {}
32
+ end
33
+
34
+ def relationships
35
+ resource['relationships'] || {}
36
+ end
37
+
38
+ def deserialized_relationships
39
+ {}.tap do |deserialized_relationships|
40
+ relationships.each do |key, value|
41
+ data = value['data']
42
+
43
+ deserialized_relationships[key] = if data.is_a?(Array)
44
+ deserialize_relationships(data)
45
+ else
46
+ deserialize_relationship(data)
47
+ end
48
+ end
49
+ end
50
+ end
51
+
52
+ def deserialize_relationships(relationships)
53
+ relationships.map do |relationship|
54
+ deserialize_relationship(relationship)
55
+ end
56
+ end
57
+
58
+ def deserialize_relationship(relationship)
59
+ id = relationship['id']
60
+ type = relationship['type']
61
+ cached_resource = read_from_cache(id, type)
62
+ included_resource = read_from_includes(id, type)
63
+
64
+ return cached_resource if cached_resource
65
+
66
+ deserialize_and_cache_relationship(included_resource || relationship)
67
+ end
68
+
69
+ def deserialize_and_cache_relationship(relationship)
70
+ resource = Resource.new(relationship, includes, cache)
71
+
72
+ write_to_cache(resource.id, resource.type, resource.deserialize(without_relationships: true))
73
+
74
+ resource.deserialize.tap do |deserialized_resource|
75
+ write_to_cache(resource.id, resource.type, deserialized_resource)
76
+ end
77
+ end
78
+
79
+ def read_from_includes(id, type)
80
+ includes.find(id, type)
81
+ end
82
+
83
+ def write_to_cache(id, type, deserialized_resource)
84
+ cache.cache(id, type, deserialized_resource)
85
+ end
86
+
87
+ def read_from_cache(id, type)
88
+ cache.find(id, type)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,26 @@
1
+ module SimpleJSONAPIDeserializer
2
+ class Resource
3
+ class Cache
4
+ def initialize
5
+ @cached_resources = {}
6
+ end
7
+
8
+ def cache(id, type, deserialized_resource)
9
+ cached_resources[type] ||= {}
10
+ cached_resources[type][id] = deserialized_resource
11
+ end
12
+
13
+ def cached?(id, type)
14
+ cached_resource(id, type)
15
+ end
16
+
17
+ def find(id, type)
18
+ cached_resources.dig(type, id)
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :cached_resources
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,29 @@
1
+ module SimpleJSONAPIDeserializer
2
+ class Resource
3
+ class Includes
4
+ def initialize(includes)
5
+ @includes = includes
6
+ end
7
+
8
+ def find(id, type)
9
+ indexed_includes.dig(type, id)
10
+ end
11
+
12
+ private
13
+
14
+ attr_reader :includes
15
+
16
+ def indexed_includes
17
+ @indexed_includes ||= {}.tap do |indexed_includes|
18
+ includes.each do |included_resource|
19
+ id = included_resource['id']
20
+ type = included_resource['type']
21
+
22
+ indexed_includes[type] = {} unless indexed_includes[type]
23
+ indexed_includes[type][id] = included_resource
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,3 @@
1
+ module SimpleJSONAPIDeserializer
2
+ VERSION = '0.1.0'.freeze
3
+ end
@@ -0,0 +1,33 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'simple_jsonapi_deserializer/version'
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.name = 'simple-jsonapi-deserializer'
7
+ gem.version = SimpleJSONAPIDeserializer::VERSION
8
+ gem.summary = 'JSON API resource deserialization.'
9
+ gem.description = 'Straight forward, painless, zero config deserialization of JSON API resources.'
10
+ gem.license = 'MIT'
11
+ gem.authors = ['Gerrit Seger']
12
+ gem.email = 'gerrit.seger@gmail.com'
13
+ gem.homepage = 'https://rubygems.org/gems/simple-jsonapi-deserializer'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+
17
+ `git submodule --quiet foreach --recursive pwd`.split($/).each do |submodule|
18
+ submodule.sub!("#{Dir.pwd}/", '')
19
+
20
+ Dir.chdir(submodule) do
21
+ `git ls-files`.split($/).map do |subpath|
22
+ gem.files << File.join(submodule, subpath)
23
+ end
24
+ end
25
+ end
26
+ gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
27
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
28
+ gem.require_paths = ['lib']
29
+
30
+ gem.add_development_dependency 'rspec', '~> 3.8.0', '>= 3.0.0'
31
+ gem.add_development_dependency 'rspec_snapshot_matcher', '~> 0.1.0'
32
+ gem.add_development_dependency 'rubocop', '~> 0.52.0'
33
+ end