json_parser 1.2.0 → 1.3.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 +4 -4
- data/README.md +140 -14
- data/lib/json_parser/builder.rb +11 -10
- data/lib/json_parser/crawler.rb +7 -8
- data/lib/json_parser/fetcher.rb +1 -1
- data/lib/json_parser/version.rb +1 -1
- data/lib/json_parser/wrapper.rb +15 -9
- data/spec/fixtures/accounts.json +27 -0
- data/spec/fixtures/accounts_missing.json +20 -0
- data/spec/fixtures/json_parser.json +0 -1
- data/spec/fixtures/person.json +5 -0
- data/spec/integration/readme/default_spec.rb +37 -0
- data/spec/integration/readme/my_parser_spec.rb +86 -0
- data/spec/lib/json_parser/builder_spec.rb +3 -3
- data/spec/lib/json_parser/crawler_spec.rb +95 -1
- data/spec/lib/json_parser/fetcher_spec.rb +27 -5
- data/spec/lib/json_parser/wrapper_spec.rb +5 -13
- data/spec/lib/json_parser_spec.rb +4 -4
- data/spec/spec_helper.rb +3 -7
- data/spec/support/models.rb +5 -0
- data/spec/support/models/game.rb +1 -1
- data/spec/support/models/house.rb +1 -1
- data/spec/support/models/{dummy.rb → json_parser/dummy.rb} +4 -4
- data/spec/support/models/json_parser/fetcher/dummy.rb +3 -0
- data/spec/support/models/json_parser/type_cast.rb +6 -0
- data/spec/support/models/json_parser/wrapper/dummy.rb +7 -0
- data/spec/support/models/my_parser.rb +28 -0
- data/spec/support/models/person.rb +1 -1
- data/spec/support/models/star.rb +7 -0
- data/spec/support/models/star_gazer.rb +13 -0
- metadata +28 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23023390388f2c93f237ca15e4ac5305ff793322
|
4
|
+
data.tar.gz: '099a4157fc3fd9841d747de641bd3f4a5534f1e6'
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd160fd952cd8670ed5bbc50f59e6dadd80e8da0acafc0c18c1ef84ffd1bea3c067329712ad273752928d0985fb4779a7c2256794b6cabc6ea903a138e62ae0a
|
7
|
+
data.tar.gz: b95c9fbe45504f95d2e5e9c8298b105ee2cc572da965b2c1f9c8ef93852e65da2ba49c7ef9c6bf31ec1f7e81df8ab0c18d162cc1ccafcef7ecae57041b413368
|
data/README.md
CHANGED
@@ -16,31 +16,61 @@ Getting started
|
|
16
16
|
---------------
|
17
17
|
1. Add JsonParser to your `Gemfile` and `bundle install`:
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
|
19
|
+
```ruby
|
20
|
+
gem 'json_parser'
|
21
|
+
```
|
22
22
|
|
23
23
|
2. Include in a class that you want to wrap a json/hash
|
24
24
|
```ruby
|
25
|
-
class
|
25
|
+
class MyParser
|
26
26
|
include JsonParser
|
27
|
+
```
|
28
|
+
|
29
|
+
3. Declare the keys you want to crawl
|
30
|
+
```ruby
|
31
|
+
class MyParser
|
32
|
+
include JsonParser
|
27
33
|
|
28
|
-
|
34
|
+
json_parse :id
|
35
|
+
json_parse :name, :age, path: :person
|
29
36
|
|
30
|
-
|
31
|
-
|
32
|
-
|
37
|
+
attr_reader :json
|
38
|
+
|
39
|
+
def initialize(json = {})
|
40
|
+
@json = json
|
33
41
|
end
|
42
|
+
end
|
43
|
+
|
34
44
|
```
|
35
45
|
|
36
|
-
|
46
|
+
and let it fetch values from your hash
|
47
|
+
|
48
|
+
|
37
49
|
```ruby
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
50
|
+
object = MyParser.new(
|
51
|
+
id: 10,
|
52
|
+
age: 22
|
53
|
+
person: {
|
54
|
+
name: 'Robert',
|
55
|
+
age: 22
|
56
|
+
}
|
57
|
+
)
|
58
|
+
|
59
|
+
object.name
|
60
|
+
#returns 'Robert'
|
61
|
+
```
|
62
|
+
|
63
|
+
this is usefull when trying to fetch data from hashes missing nodes
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
MyParser.new.name
|
67
|
+
#returns nil
|
42
68
|
```
|
43
69
|
|
70
|
+
4. fully customise the way you crawl / fetch the information with [Options](#options)
|
71
|
+
|
72
|
+
5. Create custom [typecast](#TypeCast)
|
73
|
+
|
44
74
|
Options
|
45
75
|
-------
|
46
76
|
- path: path where to find the sub hash that contains the key (empty by default)
|
@@ -52,4 +82,100 @@ Options
|
|
52
82
|
- flatten: indicator telling that to flattern the resulting array (false by default)
|
53
83
|
- after: name of a method to be called after with the resulting value
|
54
84
|
- case: case of the keys from the json (camel by default)
|
55
|
-
- type: Type that the value must be cast into
|
85
|
+
- type: Type that the value must be cast into ([TypeCast](#typecast))
|
86
|
+
- default: Default value (prior to casting and wrapping, see [Default](#default))
|
87
|
+
|
88
|
+
TypeCast
|
89
|
+
--------
|
90
|
+
The type casting, when the option `type` is passed, is done through the `JsonParser::TypeCast` which can
|
91
|
+
be extended
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
module JsonParser::TypeCast
|
95
|
+
def to_money_float(value)
|
96
|
+
value.gsub(/\$ */, '').to_f
|
97
|
+
end
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
class MyParser
|
103
|
+
include JsonParser
|
104
|
+
|
105
|
+
json_parse :total_money, full_path: 'accounts.balance', after: :sum,
|
106
|
+
cached: true, type: :money_float
|
107
|
+
json_parse :total_owed, full_path: 'loans.value', after: :sum,
|
108
|
+
cached: true, type: :money_float
|
109
|
+
|
110
|
+
attr_reader :json
|
111
|
+
|
112
|
+
def initialize(json = {})
|
113
|
+
@json = json
|
114
|
+
end
|
115
|
+
|
116
|
+
private
|
117
|
+
|
118
|
+
#this method will receive the array of values resulting from the initial mapping
|
119
|
+
def sum(balances)
|
120
|
+
balances.sum if balances
|
121
|
+
end
|
122
|
+
end
|
123
|
+
```
|
124
|
+
|
125
|
+
```ruby
|
126
|
+
object = MyParser.new(
|
127
|
+
accounts: [
|
128
|
+
{ balance: '$ 1000.50', type: 'checking' },
|
129
|
+
{ balance: '$ 150.10', type: 'savings' },
|
130
|
+
{ balance: '$ -100.24', type: 'checking' }
|
131
|
+
],
|
132
|
+
loans: [
|
133
|
+
{ value: '$ 300.50', bank: 'the_bank' },
|
134
|
+
{ value: '$ 150.10', type: 'the_other_bank' },
|
135
|
+
{ value: '$ 100.24', type: 'the_same_bank' }
|
136
|
+
]
|
137
|
+
)
|
138
|
+
|
139
|
+
object.balance
|
140
|
+
#returns 1050.36
|
141
|
+
```
|
142
|
+
|
143
|
+
Default
|
144
|
+
-------
|
145
|
+
Default value returned before typecasting or class wrapping
|
146
|
+
|
147
|
+
```ruby
|
148
|
+
class Star
|
149
|
+
attr_reader :name
|
150
|
+
|
151
|
+
def initialize(name:)
|
152
|
+
@name = name
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class StarGazer
|
157
|
+
include JsonParser
|
158
|
+
|
159
|
+
json_parse :favorite_star, full_path: 'universe.star',
|
160
|
+
default: { name: 'Sun' }, class: ::Star
|
161
|
+
|
162
|
+
attr_reader :json
|
163
|
+
|
164
|
+
def initialize(json = {})
|
165
|
+
@json = json
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
```
|
170
|
+
|
171
|
+
|
172
|
+
```ruby
|
173
|
+
star_gazer = StarGazer.new
|
174
|
+
|
175
|
+
star_gazer.favorite_star.name
|
176
|
+
#returns "Sun"
|
177
|
+
|
178
|
+
star_gazer.favorite_star.class
|
179
|
+
#returns Star
|
180
|
+
```
|
181
|
+
|
data/lib/json_parser/builder.rb
CHANGED
@@ -4,16 +4,17 @@ class JsonParser::Builder < Sinclair
|
|
4
4
|
|
5
5
|
def initialize(attr_names, clazz, options)
|
6
6
|
super(clazz, {
|
7
|
-
|
8
|
-
|
7
|
+
after: false,
|
8
|
+
cached: false,
|
9
|
+
case: :lower_camel,
|
10
|
+
class: nil,
|
11
|
+
compact: false,
|
12
|
+
default: nil,
|
13
|
+
flatten: false,
|
9
14
|
full_path: nil,
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
flatten: false,
|
14
|
-
after: false,
|
15
|
-
case: :lower_camel,
|
16
|
-
type: :none
|
15
|
+
json: :json,
|
16
|
+
path: nil,
|
17
|
+
type: :none
|
17
18
|
}.merge(options.symbolize_keys))
|
18
19
|
|
19
20
|
@attr_names = attr_names
|
@@ -48,7 +49,7 @@ class JsonParser::Builder < Sinclair
|
|
48
49
|
end
|
49
50
|
|
50
51
|
def fetcher_options
|
51
|
-
options.slice(:compact, :after, :type, :flatten).merge({
|
52
|
+
options.slice(:compact, :after, :type, :flatten, :default).merge({
|
52
53
|
clazz: wrapper_clazz,
|
53
54
|
case_type: case_type
|
54
55
|
})
|
data/lib/json_parser/crawler.rb
CHANGED
@@ -1,22 +1,21 @@
|
|
1
1
|
class JsonParser::Crawler
|
2
|
-
|
2
|
+
attr_reader :post_process, :path, :case_type, :compact, :default
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(path, options = {}, &block)
|
9
|
-
@options = options
|
4
|
+
def initialize(path, case_type: :lower_camel, compact: false, default: nil, &block)
|
5
|
+
@case_type = case_type
|
6
|
+
@compact = compact
|
7
|
+
@default = default
|
10
8
|
@path = path.map { |p| change_case(p) }
|
11
9
|
@post_process = block
|
12
10
|
end
|
13
11
|
|
14
12
|
def crawl(json, index = 0)
|
15
|
-
return nil if json.nil?
|
16
13
|
return wrap(json) if is_ended?(index)
|
17
14
|
return crawl_array(json, index) if json.is_a? Array
|
18
15
|
|
19
16
|
crawl(fetch(json, index), index + 1)
|
17
|
+
rescue NoMethodError
|
18
|
+
wrap(default)
|
20
19
|
end
|
21
20
|
|
22
21
|
private
|
data/lib/json_parser/fetcher.rb
CHANGED
data/lib/json_parser/version.rb
CHANGED
data/lib/json_parser/wrapper.rb
CHANGED
@@ -1,30 +1,36 @@
|
|
1
1
|
class JsonParser::Wrapper
|
2
|
-
include Sinclair::OptionsParser
|
3
2
|
include JsonParser::TypeCast
|
4
3
|
|
5
|
-
|
4
|
+
attr_reader :clazz, :type
|
6
5
|
|
7
|
-
def initialize(
|
8
|
-
@
|
6
|
+
def initialize(clazz: nil, type: nil)
|
7
|
+
@clazz = clazz
|
8
|
+
@type = type
|
9
9
|
end
|
10
10
|
|
11
11
|
def wrap(value)
|
12
|
-
return value
|
12
|
+
return wrap_array(value) if value.is_a?(Array)
|
13
|
+
wrap_element(value)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
13
17
|
|
18
|
+
def wrap_element(value)
|
14
19
|
value = cast(value) if has_type? && !value.nil?
|
15
20
|
return if value.nil?
|
16
21
|
|
17
|
-
|
18
|
-
value
|
22
|
+
clazz ? clazz.new(value) : value
|
19
23
|
end
|
20
24
|
|
21
|
-
|
25
|
+
def wrap_array(array)
|
26
|
+
array.map { |v| wrap v }
|
27
|
+
end
|
22
28
|
|
23
29
|
def has_type?
|
24
30
|
type.present? && type != :none
|
25
31
|
end
|
26
32
|
|
27
33
|
def cast(value)
|
28
|
-
|
34
|
+
public_send("to_#{type}", value)
|
29
35
|
end
|
30
36
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
{
|
2
|
+
"banks": [
|
3
|
+
{
|
4
|
+
"name": "bank_1",
|
5
|
+
"accounts": [
|
6
|
+
{
|
7
|
+
"type": "savings",
|
8
|
+
"balance": 1000.00
|
9
|
+
}, {
|
10
|
+
"type": "checking",
|
11
|
+
"balance": 1500.00
|
12
|
+
}
|
13
|
+
]
|
14
|
+
}, {
|
15
|
+
"name": "bank_2",
|
16
|
+
"accounts": [
|
17
|
+
{
|
18
|
+
"type": "savings",
|
19
|
+
"balance": 50.00
|
20
|
+
}, {
|
21
|
+
"type": "checking",
|
22
|
+
"balance": -500.00
|
23
|
+
}
|
24
|
+
]
|
25
|
+
}
|
26
|
+
]
|
27
|
+
}
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'default option' do
|
4
|
+
subject do
|
5
|
+
StarGazer.new(hash).favorite_star
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:hash) { {} }
|
9
|
+
|
10
|
+
context 'when node is not found' do
|
11
|
+
it 'returns the default before wrapping' do
|
12
|
+
expect(subject.name).to eq('Sun')
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'wraps the returned value in a class' do
|
16
|
+
expect(subject).to be_a(Star)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context 'when node is not missing' do
|
21
|
+
let(:hash) do
|
22
|
+
{
|
23
|
+
universe: {
|
24
|
+
star: { name: 'Antares' }
|
25
|
+
}
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'returns the value before wrapping' do
|
30
|
+
expect(subject.name).to eq('Antares')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'wraps the returned value in a class' do
|
34
|
+
expect(subject).to be_a(Star)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MyParser do
|
4
|
+
subject { described_class.new(hash) }
|
5
|
+
|
6
|
+
let(:hash) do
|
7
|
+
{
|
8
|
+
id: 10,
|
9
|
+
person: {
|
10
|
+
name: 'Robert',
|
11
|
+
age: 22
|
12
|
+
},
|
13
|
+
accounts: [
|
14
|
+
{ balance: '$ 1000.50', type: 'checking' },
|
15
|
+
{ balance: '$ 150.10', type: 'savings' },
|
16
|
+
{ balance: '$ -100.24', type: 'checking' }
|
17
|
+
],
|
18
|
+
loans: [
|
19
|
+
{ value: '$ 300.50', bank: 'the_bank' },
|
20
|
+
{ value: '$ 150.10', type: 'the_other_bank' },
|
21
|
+
{ value: '$ 100.24', type: 'the_same_bank' }
|
22
|
+
]
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
describe 'id' do
|
27
|
+
it 'returns the parsed id' do
|
28
|
+
expect(subject.id).to eq(10)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe 'name' do
|
33
|
+
it 'returns the parsed name' do
|
34
|
+
expect(subject.name).to eq('Robert')
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'when person is missing' do
|
38
|
+
subject { described_class.new }
|
39
|
+
|
40
|
+
it do
|
41
|
+
expect { subject.name }.not_to raise_error
|
42
|
+
end
|
43
|
+
|
44
|
+
it do
|
45
|
+
expect(subject.name).to be_nil
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe 'age' do
|
51
|
+
it do
|
52
|
+
expect(subject.age).to be_a(Integer)
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'returns the parsed age' do
|
56
|
+
expect(subject.age).to eq(22)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe '#total_money' do
|
61
|
+
it do
|
62
|
+
expect(subject.total_money).to be_a(Float)
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'summs all the balance in the accounts' do
|
66
|
+
expect(subject.total_money).to eq(1050.36)
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when there is a node missing' do
|
70
|
+
let(:hash) { {} }
|
71
|
+
it 'returns nil' do
|
72
|
+
expect(subject.total_money).to be_nil
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#total_owed' do
|
78
|
+
it do
|
79
|
+
expect(subject.total_owed).to be_a(Float)
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'summs all the balance in the accounts' do
|
83
|
+
expect(subject.total_owed).to eq(550.84)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -92,11 +92,11 @@ describe JsonParser::Builder do
|
|
92
92
|
|
93
93
|
context 'when wrapping with a class' do
|
94
94
|
let(:json) { { person: name } }
|
95
|
-
let(:options) { { class:
|
95
|
+
let(:options) { { class: Person } }
|
96
96
|
let(:attr_name) { :person }
|
97
97
|
|
98
98
|
it do
|
99
|
-
expect(instance.person).to be_a(
|
99
|
+
expect(instance.person).to be_a(Person)
|
100
100
|
end
|
101
101
|
|
102
102
|
it 'fills the new instance with the information fetched' do
|
@@ -104,7 +104,7 @@ describe JsonParser::Builder do
|
|
104
104
|
end
|
105
105
|
|
106
106
|
context 'when option key is a string' do
|
107
|
-
let(:options) { { 'class' =>
|
107
|
+
let(:options) { { 'class' => Person } }
|
108
108
|
|
109
109
|
it 'fills the new instance with the information fetched' do
|
110
110
|
expect(instance.person.name).to eq(name)
|
@@ -8,7 +8,8 @@ describe JsonParser::Crawler do
|
|
8
8
|
let(:path) { '' }
|
9
9
|
let(:default_options) { { case_type: :lower_camel} }
|
10
10
|
let(:options) { {} }
|
11
|
-
let(:
|
11
|
+
let(:json_file) { 'json_parser.json' }
|
12
|
+
let(:json) { load_json_fixture_file(json_file) }
|
12
13
|
let(:value) { subject.crawl(json) }
|
13
14
|
|
14
15
|
context 'when parsing with a path' do
|
@@ -17,6 +18,14 @@ describe JsonParser::Crawler do
|
|
17
18
|
it 'retrieves attribute from base json' do
|
18
19
|
expect(value).to eq(json['user']['name'])
|
19
20
|
end
|
21
|
+
|
22
|
+
context 'when calling twice' do
|
23
|
+
before { subject.crawl(json) }
|
24
|
+
|
25
|
+
it 'can still crawl' do
|
26
|
+
expect(value).to eq(json['user']['name'])
|
27
|
+
end
|
28
|
+
end
|
20
29
|
end
|
21
30
|
|
22
31
|
context 'crawler finds a nil attribute' do
|
@@ -31,6 +40,38 @@ describe JsonParser::Crawler do
|
|
31
40
|
end
|
32
41
|
end
|
33
42
|
|
43
|
+
context 'when there is an array of arrays' do
|
44
|
+
let(:json_file) { 'accounts.json' }
|
45
|
+
let(:path) { %w(banks accounts balance) }
|
46
|
+
|
47
|
+
it 'returns the values as array of arrays' do
|
48
|
+
expect(value).to eq([[1000.0, 1500.0], [50.0, -500.0]])
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when there is a missing node' do
|
52
|
+
let(:json_file) { 'accounts_missing.json' }
|
53
|
+
|
54
|
+
it 'returns the missing values as nil' do
|
55
|
+
expect(value).to eq([[1000.0, nil], nil, nil])
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when setting a default' do
|
59
|
+
let(:options) { { default: 10 } }
|
60
|
+
|
61
|
+
it 'returns the missing values as default' do
|
62
|
+
expect(value).to eq([[1000.0, nil], 10, 10])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context 'when setting compact' do
|
67
|
+
let(:options) { { compact: true } }
|
68
|
+
it 'returns the missing values as nil' do
|
69
|
+
expect(value).to eq([[1000.0]])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
34
75
|
context 'when json is empty' do
|
35
76
|
let(:json) { nil }
|
36
77
|
let(:path) { %w(car model) }
|
@@ -97,6 +138,59 @@ describe JsonParser::Crawler do
|
|
97
138
|
end
|
98
139
|
end
|
99
140
|
|
141
|
+
context 'with default option' do
|
142
|
+
let(:default_value) { 'NotFound' }
|
143
|
+
let(:options) { { default: default_value } }
|
144
|
+
let(:path) { %w(projects name) }
|
145
|
+
|
146
|
+
context 'when there is a key missing' do
|
147
|
+
it 'returns the default value' do
|
148
|
+
expect(value).to eq(default_value)
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'when wrapping it with a class' do
|
152
|
+
let(:block) { proc { |v| Person.new(v) } }
|
153
|
+
|
154
|
+
it 'wrap it with the class' do
|
155
|
+
expect(value).to be_a(Person)
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'wraps the default value' do
|
159
|
+
expect(value.name).to eq(default_value)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
context 'when the key is not missing but the value is nil' do
|
165
|
+
let(:json_file) { 'person.json' }
|
166
|
+
let(:path) { %w(user name) }
|
167
|
+
|
168
|
+
it { expect(value).to be_nil }
|
169
|
+
|
170
|
+
context 'when wrapping it with a class' do
|
171
|
+
let(:block) { proc { |v| Person.new(v) } }
|
172
|
+
|
173
|
+
it 'wrap it with the class' do
|
174
|
+
expect(value).to be_a(Person)
|
175
|
+
end
|
176
|
+
|
177
|
+
it 'wraps the default value' do
|
178
|
+
expect(value.name).to be_nil
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
context 'when the node is missing but default has the same node' do
|
184
|
+
let(:default_value) { { node: { value: 1 } } }
|
185
|
+
let(:path) { %w(node node node) }
|
186
|
+
let(:json) { {} }
|
187
|
+
|
188
|
+
it 'does not crawl through default value' do
|
189
|
+
expect(value).to eq(default_value)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
100
194
|
context 'when using a snake case' do
|
101
195
|
let(:json) { { snake_cased: 'snake', snakeCased: 'Camel' }.stringify_keys }
|
102
196
|
let(:path) { [ 'snake_cased' ] }
|
@@ -1,14 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe JsonParser::Fetcher do
|
4
|
-
class JsonParser::Fetcher::Dummy
|
5
|
-
end
|
6
|
-
|
7
4
|
let(:subject) do
|
8
|
-
described_class.new json, path, instance,
|
5
|
+
described_class.new json, path, instance, options
|
9
6
|
end
|
10
7
|
let(:path) { '' }
|
11
|
-
let(:default_options) { { case_type: :snake} }
|
12
8
|
let(:instance) { JsonParser::Fetcher::Dummy.new }
|
13
9
|
let(:json) { load_json_fixture_file('json_parser.json') }
|
14
10
|
let(:value) { subject.fetch }
|
@@ -71,4 +67,30 @@ describe JsonParser::Fetcher do
|
|
71
67
|
end
|
72
68
|
end
|
73
69
|
end
|
70
|
+
|
71
|
+
describe 'after option' do
|
72
|
+
let(:instance) { MyParser.new(json) }
|
73
|
+
let(:json) { [ 100, 250, -25] }
|
74
|
+
let(:options) { { after: :sum } }
|
75
|
+
|
76
|
+
it 'applies after call ' do
|
77
|
+
expect(subject.fetch).to eq(325)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
describe 'clazz options' do
|
82
|
+
let(:path) { 'name' }
|
83
|
+
let(:name) { 'Robert' }
|
84
|
+
let(:json) { { name: name } }
|
85
|
+
let(:options) { { clazz: wrapper } }
|
86
|
+
let(:wrapper) { Person }
|
87
|
+
|
88
|
+
it 'wraps the result in an object' do
|
89
|
+
expect(subject.fetch).to be_a(wrapper)
|
90
|
+
end
|
91
|
+
|
92
|
+
it 'sets the wrapper with the fetched value' do
|
93
|
+
expect(subject.fetch.name).to eq(name)
|
94
|
+
end
|
95
|
+
end
|
74
96
|
end
|
@@ -1,14 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe JsonParser::Wrapper do
|
4
|
-
|
5
|
-
class JsonParser::Wrapper::DummyWrapper
|
6
|
-
attr_reader :value
|
7
|
-
def initialize(value)
|
8
|
-
@value = value
|
9
|
-
end
|
10
|
-
end
|
11
|
-
|
12
4
|
let(:options) { {} }
|
13
5
|
let(:subject) { described_class.new options }
|
14
6
|
let(:hash) { { a: 1 } }
|
@@ -76,7 +68,7 @@ describe JsonParser::Wrapper do
|
|
76
68
|
end
|
77
69
|
|
78
70
|
context 'when passing clazz parameter' do
|
79
|
-
let(:options) { { type: type, clazz: JsonParser::Wrapper::
|
71
|
+
let(:options) { { type: type, clazz: JsonParser::Wrapper::Dummy } }
|
80
72
|
|
81
73
|
it do
|
82
74
|
expect(result).to be_nil
|
@@ -94,25 +86,25 @@ describe JsonParser::Wrapper do
|
|
94
86
|
}
|
95
87
|
|
96
88
|
context 'when passing clazz parameter' do
|
97
|
-
let(:options) { { type: type, clazz: JsonParser::Wrapper::
|
89
|
+
let(:options) { { type: type, clazz: JsonParser::Wrapper::Dummy } }
|
98
90
|
|
99
91
|
it_behaves_like 'a result that is type cast', {
|
100
92
|
integer: NilClass,
|
101
93
|
float: NilClass,
|
102
|
-
string: JsonParser::Wrapper::
|
94
|
+
string: JsonParser::Wrapper::Dummy
|
103
95
|
}
|
104
96
|
end
|
105
97
|
end
|
106
98
|
|
107
99
|
context 'when passing clazz parameter' do
|
108
100
|
let(:value) { 1 }
|
109
|
-
let(:options) { { type: type, clazz: JsonParser::Wrapper::
|
101
|
+
let(:options) { { type: type, clazz: JsonParser::Wrapper::Dummy } }
|
110
102
|
let(:cast) { result.value }
|
111
103
|
|
112
104
|
it_behaves_like 'casts basic types'
|
113
105
|
|
114
106
|
it 'wraps the result inside the given class' do
|
115
|
-
expect(result).to be_a(JsonParser::Wrapper::
|
107
|
+
expect(result).to be_a(JsonParser::Wrapper::Dummy)
|
116
108
|
end
|
117
109
|
end
|
118
110
|
|
@@ -46,7 +46,7 @@ describe JsonParser do
|
|
46
46
|
let(:attribute) { :house }
|
47
47
|
|
48
48
|
it 'returns an onject wrap' do
|
49
|
-
expect(value).to be_a(
|
49
|
+
expect(value).to be_a(House)
|
50
50
|
end
|
51
51
|
|
52
52
|
it 'creates the object with the given json' do
|
@@ -61,7 +61,7 @@ describe JsonParser do
|
|
61
61
|
it 'returns an array of json wrapped' do
|
62
62
|
expect(value).to be_a(Array)
|
63
63
|
value.each do |object|
|
64
|
-
expect(object).to be_a(
|
64
|
+
expect(object).to be_a(Game)
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -76,7 +76,7 @@ describe JsonParser do
|
|
76
76
|
value.each do |object|
|
77
77
|
expect(object).to be_a(Array)
|
78
78
|
object.each do |game|
|
79
|
-
expect(game).to be_a(
|
79
|
+
expect(game).to be_a(Game)
|
80
80
|
end
|
81
81
|
end
|
82
82
|
end
|
@@ -93,7 +93,7 @@ describe JsonParser do
|
|
93
93
|
end
|
94
94
|
|
95
95
|
it 'returns an onject wrap' do
|
96
|
-
expect(value).to be_a(
|
96
|
+
expect(value).to be_a(House)
|
97
97
|
end
|
98
98
|
|
99
99
|
it 'creates the object with the given json' do
|
data/spec/spec_helper.rb
CHANGED
@@ -1,16 +1,12 @@
|
|
1
1
|
require 'simplecov'
|
2
|
-
SimpleCov.start
|
2
|
+
SimpleCov.start do
|
3
|
+
add_filter 'spec/support/models/'
|
4
|
+
end
|
3
5
|
|
4
6
|
require 'pry-nav'
|
5
7
|
require 'json_parser'
|
6
8
|
require 'safe_attribute_assignment'
|
7
9
|
|
8
|
-
module JsonParser
|
9
|
-
models = File.expand_path("spec/support/models/*.rb")
|
10
|
-
Dir[models].each do |file|
|
11
|
-
autoload file.gsub(/.*\/(.*)\..*/, '\1').camelize.to_sym, file
|
12
|
-
end
|
13
|
-
end
|
14
10
|
support_files = File.expand_path("spec/support/**/*.rb")
|
15
11
|
Dir[support_files].each { |file| require file }
|
16
12
|
|
data/spec/support/models/game.rb
CHANGED
@@ -6,10 +6,10 @@ class JsonParser::Dummy
|
|
6
6
|
json_parse :name, path: 'user'
|
7
7
|
json_parse :father_name, full_path: 'father.name'
|
8
8
|
json_parse :age, cached: true
|
9
|
-
json_parse :house, class:
|
10
|
-
json_parse :old_house, class:
|
11
|
-
json_parse :games, class:
|
12
|
-
json_parse :games_filtered, class:
|
9
|
+
json_parse :house, class: ::House
|
10
|
+
json_parse :old_house, class: ::House, cached: true
|
11
|
+
json_parse :games, class: ::Game
|
12
|
+
json_parse :games_filtered, class: ::Game, after: :filter_games, full_path: 'games'
|
13
13
|
|
14
14
|
def initialize(json)
|
15
15
|
@json = json
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class MyParser
|
2
|
+
include JsonParser
|
3
|
+
|
4
|
+
json_parse :id
|
5
|
+
json_parse :name, :age, path: :person
|
6
|
+
json_parse :total_money, full_path: 'accounts.balance', after: :sum,
|
7
|
+
cached: true, type: :money_float
|
8
|
+
json_parse :total_owed, full_path: 'loans.value', after: :sum,
|
9
|
+
cached: true, type: :money_float
|
10
|
+
|
11
|
+
attr_reader :json
|
12
|
+
|
13
|
+
def initialize(json = {})
|
14
|
+
@json = json
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def sum(balances)
|
20
|
+
balances.sum if balances
|
21
|
+
end
|
22
|
+
|
23
|
+
models = File.expand_path("spec/support/models/my_parser/*.rb")
|
24
|
+
Dir[models].each do |file|
|
25
|
+
autoload file.gsub(/.*\/(.*)\..*/, '\1').camelize.to_sym, file
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
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.
|
4
|
+
version: 1.3.0
|
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-
|
11
|
+
date: 2018-05-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -147,7 +147,12 @@ files:
|
|
147
147
|
- lib/json_parser/type_cast.rb
|
148
148
|
- lib/json_parser/version.rb
|
149
149
|
- lib/json_parser/wrapper.rb
|
150
|
+
- spec/fixtures/accounts.json
|
151
|
+
- spec/fixtures/accounts_missing.json
|
150
152
|
- spec/fixtures/json_parser.json
|
153
|
+
- spec/fixtures/person.json
|
154
|
+
- spec/integration/readme/default_spec.rb
|
155
|
+
- spec/integration/readme/my_parser_spec.rb
|
151
156
|
- spec/lib/json_parser/builder_spec.rb
|
152
157
|
- spec/lib/json_parser/crawler_spec.rb
|
153
158
|
- spec/lib/json_parser/fetcher_spec.rb
|
@@ -155,10 +160,17 @@ files:
|
|
155
160
|
- spec/lib/json_parser_spec.rb
|
156
161
|
- spec/spec_helper.rb
|
157
162
|
- spec/support/fixture_helpers.rb
|
158
|
-
- spec/support/models
|
163
|
+
- spec/support/models.rb
|
159
164
|
- spec/support/models/game.rb
|
160
165
|
- spec/support/models/house.rb
|
166
|
+
- spec/support/models/json_parser/dummy.rb
|
167
|
+
- spec/support/models/json_parser/fetcher/dummy.rb
|
168
|
+
- spec/support/models/json_parser/type_cast.rb
|
169
|
+
- spec/support/models/json_parser/wrapper/dummy.rb
|
170
|
+
- spec/support/models/my_parser.rb
|
161
171
|
- spec/support/models/person.rb
|
172
|
+
- spec/support/models/star.rb
|
173
|
+
- spec/support/models/star_gazer.rb
|
162
174
|
- spec/support/shared_examples/wrapper.rb
|
163
175
|
homepage: https://github.com/Bidu/json_parser
|
164
176
|
licenses: []
|
@@ -184,7 +196,12 @@ signing_key:
|
|
184
196
|
specification_version: 4
|
185
197
|
summary: Json Parser
|
186
198
|
test_files:
|
199
|
+
- spec/fixtures/accounts.json
|
200
|
+
- spec/fixtures/accounts_missing.json
|
187
201
|
- spec/fixtures/json_parser.json
|
202
|
+
- spec/fixtures/person.json
|
203
|
+
- spec/integration/readme/default_spec.rb
|
204
|
+
- spec/integration/readme/my_parser_spec.rb
|
188
205
|
- spec/lib/json_parser/builder_spec.rb
|
189
206
|
- spec/lib/json_parser/crawler_spec.rb
|
190
207
|
- spec/lib/json_parser/fetcher_spec.rb
|
@@ -192,8 +209,15 @@ test_files:
|
|
192
209
|
- spec/lib/json_parser_spec.rb
|
193
210
|
- spec/spec_helper.rb
|
194
211
|
- spec/support/fixture_helpers.rb
|
195
|
-
- spec/support/models
|
212
|
+
- spec/support/models.rb
|
196
213
|
- spec/support/models/game.rb
|
197
214
|
- spec/support/models/house.rb
|
215
|
+
- spec/support/models/json_parser/dummy.rb
|
216
|
+
- spec/support/models/json_parser/fetcher/dummy.rb
|
217
|
+
- spec/support/models/json_parser/type_cast.rb
|
218
|
+
- spec/support/models/json_parser/wrapper/dummy.rb
|
219
|
+
- spec/support/models/my_parser.rb
|
198
220
|
- spec/support/models/person.rb
|
221
|
+
- spec/support/models/star.rb
|
222
|
+
- spec/support/models/star_gazer.rb
|
199
223
|
- spec/support/shared_examples/wrapper.rb
|