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