json_parser 1.3.0 → 1.3.1

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
  SHA1:
3
- metadata.gz: 23023390388f2c93f237ca15e4ac5305ff793322
4
- data.tar.gz: '099a4157fc3fd9841d747de641bd3f4a5534f1e6'
3
+ metadata.gz: 54a56224cff688292d5d7fcedba88d3056155295
4
+ data.tar.gz: 8ce6c70f4cf77f9c4b9f051a126589958aee78c1
5
5
  SHA512:
6
- metadata.gz: dd160fd952cd8670ed5bbc50f59e6dadd80e8da0acafc0c18c1ef84ffd1bea3c067329712ad273752928d0985fb4779a7c2256794b6cabc6ea903a138e62ae0a
7
- data.tar.gz: b95c9fbe45504f95d2e5e9c8298b105ee2cc572da965b2c1f9c8ef93852e65da2ba49c7ef9c6bf31ec1f7e81df8ab0c18d162cc1ccafcef7ecae57041b413368
6
+ metadata.gz: afe03643bf563a8c063d59b8f0c87233918a1811feb58eee1549b5006c2ec910c8170761463a6c0756638a2a983d4041ed9968c1ab3af48c49274c6f68b8c099
7
+ data.tar.gz: 47563cda0362ea10fdf25b3ecf26ef1c370f0cd5ad8161713b7662b3ceede19fa869d0f6face2596bb31efd5490b8e84a1ac11a94e129e81bb501fbf411543dd
data/README.md CHANGED
@@ -85,8 +85,7 @@ Options
85
85
  - type: Type that the value must be cast into ([TypeCast](#typecast))
86
86
  - default: Default value (prior to casting and wrapping, see [Default](#default))
87
87
 
88
- TypeCast
89
- --------
88
+ ## TypeCast
90
89
  The type casting, when the option `type` is passed, is done through the `JsonParser::TypeCast` which can
91
90
  be extended
92
91
 
@@ -140,8 +139,7 @@ end
140
139
  #returns 1050.36
141
140
  ```
142
141
 
143
- Default
144
- -------
142
+ ## Default
145
143
  Default value returned before typecasting or class wrapping
146
144
 
147
145
  ```ruby
@@ -5,10 +5,12 @@ require 'sinclair'
5
5
  module JsonParser
6
6
  extend ActiveSupport::Concern
7
7
 
8
- autoload :TypeCast, 'json_parser/type_cast'
8
+ autoload :Builder, 'json_parser/builder'
9
+ autoload :ClassMethods, 'json_parser/class_methods'
9
10
  autoload :Crawler, 'json_parser/crawler'
10
- autoload :Wrapper, 'json_parser/wrapper'
11
+ autoload :Exception, 'json_parser/exception'
11
12
  autoload :Fetcher, 'json_parser/fetcher'
12
- autoload :ClassMethods, 'json_parser/class_methods'
13
- autoload :Builder, 'json_parser/builder'
13
+ autoload :Reader, 'json_parser/reader'
14
+ autoload :Wrapper, 'json_parser/wrapper'
15
+ autoload :TypeCast, 'json_parser/type_cast'
14
16
  end
@@ -1,45 +1,42 @@
1
1
  class JsonParser::Builder < Sinclair
2
2
 
3
- attr_reader :attr_names
3
+ attr_reader :attr_names, :path, :full_path, :cached
4
4
 
5
- def initialize(attr_names, clazz, options)
5
+ def initialize(attr_names, clazz, path: nil, full_path: nil, cached: false, **options)
6
6
  super(clazz, {
7
7
  after: false,
8
- cached: false,
9
8
  case: :lower_camel,
10
9
  class: nil,
11
10
  compact: false,
12
11
  default: nil,
13
12
  flatten: false,
14
- full_path: nil,
15
13
  json: :json,
16
- path: nil,
17
14
  type: :none
18
15
  }.merge(options.symbolize_keys))
19
16
 
20
17
  @attr_names = attr_names
18
+ @path = path
19
+ @full_path = full_path
20
+ @cached = cached
21
21
  init
22
22
  end
23
23
 
24
24
  private
25
25
 
26
- delegate :path, :full_path, :cached, :compact,
27
- :type, :after, to: :options_object
28
-
29
26
  def init
30
27
  attr_names.each do |attr|
31
28
  add_attr(attr)
32
29
  end
33
30
  end
34
31
 
35
- def json_name
36
- options[:json]
37
- end
38
-
39
32
  def real_path(attribute)
40
33
  full_path || [path, attribute].compact.join('.')
41
34
  end
42
35
 
36
+ def json_name
37
+ options[:json]
38
+ end
39
+
43
40
  def wrapper_clazz
44
41
  options[:class]
45
42
  end
@@ -48,10 +45,11 @@ class JsonParser::Builder < Sinclair
48
45
  options[:case]
49
46
  end
50
47
 
51
- def fetcher_options
48
+ def fetcher_options(attribute)
52
49
  options.slice(:compact, :after, :type, :flatten, :default).merge({
53
50
  clazz: wrapper_clazz,
54
- case_type: case_type
51
+ case_type: case_type,
52
+ path: real_path(attribute)
55
53
  })
56
54
  end
57
55
 
@@ -62,7 +60,7 @@ class JsonParser::Builder < Sinclair
62
60
  def attr_fetcher(attribute)
63
61
  <<-CODE
64
62
  ::JsonParser::Fetcher.new(
65
- #{json_name}, '#{real_path(attribute)}', self, #{fetcher_options}
63
+ #{json_name}, self, #{fetcher_options(attribute)}
66
64
  ).fetch
67
65
  CODE
68
66
  end
@@ -1,10 +1,7 @@
1
1
  module JsonParser
2
2
  module ClassMethods
3
- def json_parse(*attr_names)
4
- options = attr_names.extract_options!
5
-
6
- builder = Builder.new(attr_names, self, options)
7
- builder.build
3
+ def json_parse(*attr_names, **options)
4
+ Builder.new(attr_names, self, options).build
8
5
  end
9
6
  end
10
7
  end
@@ -1,52 +1,45 @@
1
- class JsonParser::Crawler
2
- attr_reader :post_process, :path, :case_type, :compact, :default
3
-
4
- def initialize(path, case_type: :lower_camel, compact: false, default: nil, &block)
5
- @case_type = case_type
6
- @compact = compact
7
- @default = default
8
- @path = path.map { |p| change_case(p) }
9
- @post_process = block
10
- end
11
-
12
- def crawl(json, index = 0)
13
- return wrap(json) if is_ended?(index)
14
- return crawl_array(json, index) if json.is_a? Array
1
+ module JsonParser
2
+ class Crawler
3
+ attr_reader :post_process, :path, :case_type, :compact, :default
4
+
5
+ def initialize(path:, case_type: :lower_camel, compact: false, default: nil, &block)
6
+ @case_type = case_type
7
+ @compact = compact
8
+ @default = default
9
+ @path = path
10
+ @post_process = block
11
+ end
15
12
 
16
- crawl(fetch(json, index), index + 1)
17
- rescue NoMethodError
18
- wrap(default)
19
- end
13
+ def value(json, index = 0)
14
+ crawl(json, index)
15
+ rescue Exception::KeyNotFound
16
+ wrap(default)
17
+ end
20
18
 
21
- private
19
+ private
22
20
 
23
- def fetch(json, index)
24
- key = path[index]
25
- json.key?(key) ? json[key] : json[key.to_sym]
26
- end
21
+ def crawl(json, index = 0)
22
+ return wrap(json) if reader.is_ended?(index)
23
+ return crawl_array(json, index) if json.is_a?(Array)
27
24
 
28
- def is_ended?(index)
29
- index >= path.size
30
- end
25
+ crawl(reader.read(json, index), index + 1)
26
+ end
31
27
 
32
- def wrap(json)
33
- post_process.call(json)
34
- end
28
+ def reader
29
+ @reader ||= JsonParser::Reader.new(
30
+ path: path,
31
+ case_type: case_type
32
+ )
33
+ end
35
34
 
36
- def change_case(key)
37
- case case_type
38
- when :lower_camel
39
- key.camelize(:lower)
40
- when :upper_camel
41
- key.camelize(:upper)
42
- when :snake
43
- key.underscore
35
+ def wrap(json)
36
+ post_process.call(json)
44
37
  end
45
- end
46
38
 
47
- def crawl_array(array, index)
48
- array.map { |j| crawl(j, index) }.tap do |a|
49
- a.compact! if compact
39
+ def crawl_array(array, index)
40
+ array.map { |j| value(j, index) }.tap do |a|
41
+ a.compact! if compact
42
+ end
50
43
  end
51
44
  end
52
45
  end
@@ -0,0 +1,3 @@
1
+ module JsonParser::Exception
2
+ class KeyNotFound < StandardError; end
3
+ end
@@ -5,9 +5,8 @@ class JsonParser::Fetcher
5
5
 
6
6
  delegate :after, :flatten, to: :options_object
7
7
  delegate :wrap, to: :wrapper
8
- delegate :crawl, to: :crawler
9
8
 
10
- def initialize(json, path, instance, options = {})
9
+ def initialize(json, instance, path:, **options)
11
10
  @path = path.to_s.split('.')
12
11
  @json = json
13
12
  @instance = instance
@@ -15,7 +14,7 @@ class JsonParser::Fetcher
15
14
  end
16
15
 
17
16
  def fetch
18
- value = crawl(json)
17
+ value = crawler.value(json)
19
18
  value.flatten! if flatten && value.respond_to?(:flatten!)
20
19
  value = instance.send(after, value) if after
21
20
  value
@@ -28,13 +27,13 @@ class JsonParser::Fetcher
28
27
  end
29
28
 
30
29
  def buidl_crawler
31
- JsonParser::Crawler.new(path, crawler_options) do |value|
30
+ JsonParser::Crawler.new(crawler_options) do |value|
32
31
  wrap(value)
33
32
  end
34
33
  end
35
34
 
36
35
  def crawler_options
37
- options.slice(:case_type, :compact, :default)
36
+ options.slice(:case_type, :compact, :default).merge(path: path)
38
37
  end
39
38
 
40
39
  def wrapper
@@ -0,0 +1,44 @@
1
+ module JsonParser
2
+ class Reader
3
+ attr_reader :path, :case_type
4
+
5
+ def initialize(path:, case_type:)
6
+ @case_type = case_type
7
+ @path = path.map(&self.method(:change_case))
8
+ end
9
+
10
+ def read(json, index)
11
+ key = path[index]
12
+
13
+ check_key!(json, key)
14
+
15
+ json.key?(key) ? json[key] : json[key.to_sym]
16
+ end
17
+
18
+ def is_ended?(index)
19
+ index >= path.size
20
+ end
21
+
22
+ private
23
+
24
+ def check_key!(json, key)
25
+ return if has_key?(json, key)
26
+ raise Exception::KeyNotFound
27
+ end
28
+
29
+ def has_key?(json, key)
30
+ json&.key?(key) || json&.key?(key.to_sym)
31
+ end
32
+
33
+ def change_case(key)
34
+ case case_type
35
+ when :lower_camel
36
+ key.camelize(:lower)
37
+ when :upper_camel
38
+ key.camelize(:upper)
39
+ when :snake
40
+ key.underscore
41
+ end
42
+ end
43
+ end
44
+ end
@@ -1,3 +1,3 @@
1
1
  module JsonParser
2
- VERSION = '1.3.0'
2
+ VERSION = '1.3.1'
3
3
  end
@@ -8,6 +8,9 @@
8
8
  "balance": 1000.00
9
9
  }, {
10
10
  "type": "checking"
11
+ }, {
12
+ "type": "investiment",
13
+ "balance": null
11
14
  }
12
15
  ]
13
16
  }, {
@@ -0,0 +1,9 @@
1
+ {
2
+ "user": {
3
+ "name": "Robert",
4
+ "full_name": "Robert Smith",
5
+ "LoginName": "robsmith",
6
+ "birthDate": "03/07/1980",
7
+ "password_reminder": null
8
+ }
9
+ }
@@ -18,7 +18,7 @@ describe JsonParser::Builder do
18
18
  let(:instance) { clazz.new(json) }
19
19
 
20
20
  subject do
21
- described_class.new(attr_names, clazz, options)
21
+ described_class.new(attr_names, clazz, **options)
22
22
  end
23
23
 
24
24
  describe '#build' do
@@ -79,14 +79,6 @@ describe JsonParser::Builder do
79
79
  it 'fetches the value within the json' do
80
80
  expect(instance.the_name).to eq(name)
81
81
  end
82
-
83
- context 'when option key is a string' do
84
- let(:options) { { 'full_path' => 'user.name' } }
85
-
86
- it 'fetches the value within the json' do
87
- expect(instance.the_name).to eq(name)
88
- end
89
- end
90
82
  end
91
83
  end
92
84
 
@@ -102,14 +94,6 @@ describe JsonParser::Builder do
102
94
  it 'fills the new instance with the information fetched' do
103
95
  expect(instance.person.name).to eq(name)
104
96
  end
105
-
106
- context 'when option key is a string' do
107
- let(:options) { { 'class' => Person } }
108
-
109
- it 'fills the new instance with the information fetched' do
110
- expect(instance.person.name).to eq(name)
111
- end
112
- end
113
97
  end
114
98
  end
115
99
  end
@@ -2,15 +2,15 @@ require 'spec_helper'
2
2
 
3
3
  describe JsonParser::Crawler do
4
4
  let(:subject) do
5
- described_class.new path, default_options.merge(options), &block
5
+ described_class.new default_options.merge(options), &block
6
6
  end
7
7
  let(:block) { proc { |v| v } }
8
8
  let(:path) { '' }
9
- let(:default_options) { { case_type: :lower_camel} }
9
+ let(:default_options) { { path: path, case_type: :lower_camel} }
10
10
  let(:options) { {} }
11
11
  let(:json_file) { 'json_parser.json' }
12
12
  let(:json) { load_json_fixture_file(json_file) }
13
- let(:value) { subject.crawl(json) }
13
+ let(:value) { subject.value(json) }
14
14
 
15
15
  context 'when parsing with a path' do
16
16
  let(:path) { %w(user name) }
@@ -20,7 +20,7 @@ describe JsonParser::Crawler do
20
20
  end
21
21
 
22
22
  context 'when calling twice' do
23
- before { subject.crawl(json) }
23
+ before { subject.value(json) }
24
24
 
25
25
  it 'can still crawl' do
26
26
  expect(value).to eq(json['user']['name'])
@@ -52,14 +52,14 @@ describe JsonParser::Crawler do
52
52
  let(:json_file) { 'accounts_missing.json' }
53
53
 
54
54
  it 'returns the missing values as nil' do
55
- expect(value).to eq([[1000.0, nil], nil, nil])
55
+ expect(value).to eq([[1000.0, nil, nil], nil, nil])
56
56
  end
57
57
 
58
58
  context 'when setting a default' do
59
59
  let(:options) { { default: 10 } }
60
60
 
61
61
  it 'returns the missing values as default' do
62
- expect(value).to eq([[1000.0, nil], 10, 10])
62
+ expect(value).to eq([[1000.0, 10, nil], 10, 10])
63
63
  end
64
64
  end
65
65
 
@@ -180,6 +180,27 @@ describe JsonParser::Crawler do
180
180
  end
181
181
  end
182
182
 
183
+ context 'when the key last key is missing but the value is nil' do
184
+ let(:json_file) { 'person.json' }
185
+ let(:path) { %w(user nick_name) }
186
+
187
+ it 'returns the default value' do
188
+ expect(value).to eq(default_value)
189
+ end
190
+
191
+ context 'when wrapping it with a class' do
192
+ let(:block) { proc { |v| Person.new(v) } }
193
+
194
+ it 'wrap it with the class' do
195
+ expect(value).to be_a(Person)
196
+ end
197
+
198
+ it 'wraps the default value' do
199
+ expect(value.name).to eq(default_value)
200
+ end
201
+ end
202
+ end
203
+
183
204
  context 'when the node is missing but default has the same node' do
184
205
  let(:default_value) { { node: { value: 1 } } }
185
206
  let(:path) { %w(node node node) }
@@ -2,7 +2,7 @@ require 'spec_helper'
2
2
 
3
3
  describe JsonParser::Fetcher do
4
4
  let(:subject) do
5
- described_class.new json, path, instance, options
5
+ described_class.new json, instance, options.merge(path: path)
6
6
  end
7
7
  let(:path) { '' }
8
8
  let(:instance) { JsonParser::Fetcher::Dummy.new }
@@ -0,0 +1,120 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples 'reader fetchin value' do
4
+ it do
5
+ expect { subject.read(json, index) }.not_to raise_error
6
+ end
7
+
8
+ it do
9
+ expect(subject.read(json, index)).not_to be_nil
10
+ end
11
+
12
+ it 'returns the evaluated value' do
13
+ expect(subject.read(json, index)).to eq(expected)
14
+ end
15
+
16
+ context 'and the json has symbolized_keys' do
17
+ it 'returns the evaluated value' do
18
+ expect(subject.read(sym_json, index)).to eq(expected)
19
+ end
20
+ end
21
+ end
22
+
23
+ describe JsonParser::Reader do
24
+ subject do
25
+ described_class.new(path: path, case_type: case_type)
26
+ end
27
+
28
+ let(:path) { %w(user full_name) }
29
+ let(:json_file) { 'complete_person.json' }
30
+ let(:full_json) { load_json_fixture_file(json_file) }
31
+ let(:json) { full_json }
32
+ let(:sym_json) { json.symbolize_keys }
33
+ let(:case_type) { :snake }
34
+ let(:index) { 0 }
35
+
36
+ describe '#read' do
37
+ context 'when the key is found' do
38
+ let(:expected) { json['user'] }
39
+
40
+ it_behaves_like 'reader fetchin value'
41
+
42
+ context 'when the path case is changed' do
43
+ let(:json) { full_json['user'] }
44
+ let(:index) { 1 }
45
+
46
+ context 'to snake_case' do
47
+ let(:path) { %w(user FullName) }
48
+ let(:expected) { json['full_name'] }
49
+
50
+ it_behaves_like 'reader fetchin value'
51
+ end
52
+
53
+ context 'to upper_camel' do
54
+ let(:case_type) { :upper_camel }
55
+ let(:path) { %w(user login_name) }
56
+ let(:expected) { json['LoginName'] }
57
+
58
+ it_behaves_like 'reader fetchin value'
59
+ end
60
+
61
+ context 'to lower_camel' do
62
+ let(:case_type) { :lower_camel }
63
+ let(:path) { %w(user birth_date) }
64
+ let(:expected) { json['birthDate'] }
65
+
66
+ it_behaves_like 'reader fetchin value'
67
+ end
68
+ end
69
+
70
+ context 'when key is found but value is null' do
71
+ let(:json) { full_json['user'] }
72
+ let(:index) { 1 }
73
+ let(:path) { %w(user password_reminder) }
74
+
75
+ it do
76
+ expect(subject.read(json, index)).to be_nil
77
+ end
78
+
79
+ context 'but keys are symbol' do
80
+ it do
81
+ expect(subject.read(sym_json, index)).to be_nil
82
+ end
83
+ end
84
+ end
85
+
86
+ context 'when json has both string and symble' do
87
+ let(:path) { %w(key) }
88
+ let(:json) { { key: 'symbol', 'key' => 'string' } }
89
+
90
+ it 'fetches the string key first' do
91
+ expect(subject.read(json, index)).to eq('string')
92
+ end
93
+ end
94
+ end
95
+
96
+ context 'when the key is missing' do
97
+ let(:path) { %w(age) }
98
+
99
+ it do
100
+ expect do
101
+ subject.read(json, index)
102
+ end.to raise_error(JsonParser::Exception::KeyNotFound)
103
+ end
104
+ end
105
+ end
106
+
107
+ describe 'is_ended?' do
108
+ context 'when index is within path' do
109
+ let(:index) { 1 }
110
+
111
+ it { expect(subject.is_ended?(index)).to be_falsey }
112
+ end
113
+
114
+ context 'when index is outside path' do
115
+ let(:index) { 2 }
116
+
117
+ it { expect(subject.is_ended?(index)).to be_truthy }
118
+ end
119
+ end
120
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.0
4
+ version: 1.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bidu Dev's Team
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-05-31 00:00:00.000000000 Z
11
+ date: 2018-06-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -143,12 +143,15 @@ files:
143
143
  - lib/json_parser/builder.rb
144
144
  - lib/json_parser/class_methods.rb
145
145
  - lib/json_parser/crawler.rb
146
+ - lib/json_parser/exception.rb
146
147
  - lib/json_parser/fetcher.rb
148
+ - lib/json_parser/reader.rb
147
149
  - lib/json_parser/type_cast.rb
148
150
  - lib/json_parser/version.rb
149
151
  - lib/json_parser/wrapper.rb
150
152
  - spec/fixtures/accounts.json
151
153
  - spec/fixtures/accounts_missing.json
154
+ - spec/fixtures/complete_person.json
152
155
  - spec/fixtures/json_parser.json
153
156
  - spec/fixtures/person.json
154
157
  - spec/integration/readme/default_spec.rb
@@ -156,6 +159,7 @@ files:
156
159
  - spec/lib/json_parser/builder_spec.rb
157
160
  - spec/lib/json_parser/crawler_spec.rb
158
161
  - spec/lib/json_parser/fetcher_spec.rb
162
+ - spec/lib/json_parser/reader_spec.rb
159
163
  - spec/lib/json_parser/wrapper_spec.rb
160
164
  - spec/lib/json_parser_spec.rb
161
165
  - spec/spec_helper.rb
@@ -198,6 +202,7 @@ summary: Json Parser
198
202
  test_files:
199
203
  - spec/fixtures/accounts.json
200
204
  - spec/fixtures/accounts_missing.json
205
+ - spec/fixtures/complete_person.json
201
206
  - spec/fixtures/json_parser.json
202
207
  - spec/fixtures/person.json
203
208
  - spec/integration/readme/default_spec.rb
@@ -205,6 +210,7 @@ test_files:
205
210
  - spec/lib/json_parser/builder_spec.rb
206
211
  - spec/lib/json_parser/crawler_spec.rb
207
212
  - spec/lib/json_parser/fetcher_spec.rb
213
+ - spec/lib/json_parser/reader_spec.rb
208
214
  - spec/lib/json_parser/wrapper_spec.rb
209
215
  - spec/lib/json_parser_spec.rb
210
216
  - spec/spec_helper.rb