arstotzka 1.2.0 → 1.2.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/README.md +12 -11
- data/lib/arstotzka.rb +2 -0
- data/lib/arstotzka/fetcher.rb +10 -13
- data/lib/arstotzka/fetcher_builder.rb +1 -1
- data/lib/arstotzka/options.rb +273 -26
- data/lib/arstotzka/version.rb +1 -1
- data/lib/arstotzka/wrapper.rb +28 -2
- data/spec/integration/yard/arstotzka/fetcher_spec.rb +5 -4
- data/spec/integration/yard/arstotzka/options_spec.rb +175 -0
- data/spec/lib/arstotzka/fetcher_spec.rb +53 -8
- data/spec/support/models/application.rb +24 -0
- data/spec/support/models/bar.rb +21 -0
- data/spec/support/models/customer.rb +20 -0
- data/spec/support/models/drink.rb +20 -0
- data/spec/support/models/group.rb +13 -0
- data/spec/support/models/job_seeker.rb +13 -0
- data/spec/support/models/shopping_mall.rb +16 -0
- data/spec/support/models/store.rb +19 -0
- metadata +20 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fcdfefb20e55716afccbdb1db14cb24c1b23f5c3609e0cdaccfb897e6f949c32
|
4
|
+
data.tar.gz: f374f9f717e7f3b867d40270bc531eb3c22e54b9060f232e5984157c4039e612
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c2b0cce20c80bdfdc92bfd3dbb0eeff17585e7c2373daa5cfb960bf9356b54feb5d5c9ea66353a52f9b6400720e3fdcc2d27529f5456b9b2c6f0f50a066adc7
|
7
|
+
data.tar.gz: 2996ef066a6d98673f10961f82962e9d570fc23a34882d3a5d280efdb93677425eac4a52b0786551c324d04b296333b0c551603a5fd8923b99e61f81229ccf8e
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -38,7 +38,7 @@ gem 'arstotzka'
|
|
38
38
|
|
39
39
|
Yard Documentation
|
40
40
|
-------------------
|
41
|
-
https://www.rubydoc.info/gems/arstotzka/1.2.
|
41
|
+
https://www.rubydoc.info/gems/arstotzka/1.2.1
|
42
42
|
|
43
43
|
Getting Started
|
44
44
|
---------------
|
@@ -94,17 +94,18 @@ MyParser.new.name # returns nil
|
|
94
94
|
|
95
95
|
Options
|
96
96
|
-------
|
97
|
-
-
|
98
|
-
-
|
99
|
-
-
|
100
|
-
-
|
101
|
-
-
|
102
|
-
- compact: indicator telling to ignore nil values inside array (false by default)
|
103
|
-
- flatten: indicator telling that to flattern the resulting array (false by default)
|
104
|
-
- after: name of a method to be called after with the resulting value
|
105
|
-
- case: case of the keys from the json (camel by default)
|
106
|
-
- type: Type that the value must be cast into ([TypeCast](#typecast))
|
97
|
+
- after: Name of a method to be called after on the values returned
|
98
|
+
- after_each: Name of a method to be called after each result
|
99
|
+
- cached: Indicator that, once the value has been fetched, it should be cached (false by default)
|
100
|
+
- case: Case of the keys from the json (lower_camel by default)
|
101
|
+
- compact: Indicator telling to ignore nil values inside array (false by default)
|
107
102
|
- default: Default value (prior to casting and wrapping, see [Default](#default))
|
103
|
+
- flatten: Indicator telling that to flattern the resulting array (false by default)
|
104
|
+
- full_path: Full path to fetch the value (empty by default)
|
105
|
+
- klass: Class to be used when wrapping the final value
|
106
|
+
- json: Method that contains the hash to be parsed (json by default)
|
107
|
+
- path: Path where to find the sub hash that contains the key (empty by default)
|
108
|
+
- type: Type that the value must be cast into ([TypeCast](#typecast))
|
108
109
|
|
109
110
|
## TypeCast
|
110
111
|
The type casting, when the option `type` is passed, is done through the `Arstotzka::TypeCast` which can
|
data/lib/arstotzka.rb
CHANGED
data/lib/arstotzka/fetcher.rb
CHANGED
@@ -10,17 +10,13 @@ module Arstotzka
|
|
10
10
|
|
11
11
|
# Creates an instance of Artotzka::Fetcher
|
12
12
|
#
|
13
|
-
# @
|
14
|
-
#
|
15
|
-
# @overload iniitalize(instance, options_hash = {})
|
13
|
+
# @overload iniitalize(options_hash = {})
|
16
14
|
# @param options_hash [Hash] options for {Crawler}, {Wrapper} and {Reader}
|
17
15
|
#
|
18
|
-
# @overload iniitalize(
|
16
|
+
# @overload iniitalize(options)
|
19
17
|
# @param options [Arstotzka::Options] options for {Crawler}, {Wrapper} and {Reader}
|
20
|
-
def initialize(
|
18
|
+
def initialize(options_hash = {})
|
21
19
|
self.options = options_hash
|
22
|
-
|
23
|
-
@instance = instance
|
24
20
|
end
|
25
21
|
|
26
22
|
# Crawls the hash for the value
|
@@ -72,10 +68,11 @@ module Arstotzka
|
|
72
68
|
#
|
73
69
|
# instance = Account.new
|
74
70
|
#
|
75
|
-
# fetcher = Arstotzka::Fetcher.new(
|
76
|
-
#
|
77
|
-
#
|
78
|
-
#
|
71
|
+
# fetcher = Arstotzka::Fetcher.new(
|
72
|
+
# instance: instance,
|
73
|
+
# path: 'transactions',
|
74
|
+
# klass: Transaction,
|
75
|
+
# after: :filter_income
|
79
76
|
# )
|
80
77
|
#
|
81
78
|
# fetcher.fetch # retruns [
|
@@ -93,7 +90,7 @@ module Arstotzka
|
|
93
90
|
|
94
91
|
# @private
|
95
92
|
attr_reader :instance, :options
|
96
|
-
delegate :after, :flatten, to: :options
|
93
|
+
delegate :instance, :after, :flatten, to: :options
|
97
94
|
delegate :wrap, to: :wrapper
|
98
95
|
|
99
96
|
def hash
|
@@ -121,7 +118,7 @@ module Arstotzka
|
|
121
118
|
#
|
122
119
|
# @return [Arstotzka::Wrapper] the wrapper
|
123
120
|
def wrapper
|
124
|
-
@wrapper ||= Wrapper.new(options)
|
121
|
+
@wrapper ||= Wrapper.new(options.merge(instance: instance))
|
125
122
|
end
|
126
123
|
end
|
127
124
|
end
|
data/lib/arstotzka/options.rb
CHANGED
@@ -4,48 +4,295 @@ module Arstotzka
|
|
4
4
|
# @api private
|
5
5
|
#
|
6
6
|
# Class responsible to hold the options
|
7
|
+
#
|
8
|
+
# Options is initialized and merged with {DEFAULT_OPTIONS}
|
9
|
+
# when using {ClassMethods#expose}
|
10
|
+
#
|
11
|
+
# @example Using options klass and after
|
12
|
+
# class Customer
|
13
|
+
# attr_reader :name, :age
|
14
|
+
#
|
15
|
+
# def initialize(name:, age:)
|
16
|
+
# @name = name
|
17
|
+
# @age = age
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# def adult?
|
21
|
+
# age >= 18
|
22
|
+
# end
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# class Store
|
26
|
+
# include Arstotzka
|
27
|
+
#
|
28
|
+
# expose :customers, klass: Customer, after: :filter_adults
|
29
|
+
#
|
30
|
+
# def initialize(json)
|
31
|
+
# @json = json
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# private
|
35
|
+
#
|
36
|
+
# attr_reader :json
|
37
|
+
#
|
38
|
+
# def filter_adults(values)
|
39
|
+
# values.select(&:adult?)
|
40
|
+
# end
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# hash = {
|
44
|
+
# customers: [{
|
45
|
+
# name: 'John', age: 21
|
46
|
+
# }, {
|
47
|
+
# name: 'Julia', age: 15
|
48
|
+
# }, {
|
49
|
+
# name: 'Carol', age: 22
|
50
|
+
# }, {
|
51
|
+
# name: 'Bobby', age: 12
|
52
|
+
# }]
|
53
|
+
# }
|
54
|
+
#
|
55
|
+
# instance = Store.new(hash)
|
56
|
+
#
|
57
|
+
# instance.customers # returns [
|
58
|
+
# # Customer.new(name: 'John', age: 21),
|
59
|
+
# # Customer.new(name: 'Carol', age: 22)
|
60
|
+
# # ]
|
61
|
+
#
|
62
|
+
# @example Using type with klass and after_each
|
63
|
+
# module Arstotzka::TypeCast
|
64
|
+
# def to_symbolized_hash(value)
|
65
|
+
# value.symbolize_keys
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# class Drink
|
70
|
+
# attr_reader :name, :price
|
71
|
+
#
|
72
|
+
# def initialize(name:, price:)
|
73
|
+
# @name = name
|
74
|
+
# @price = price
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# def inflate(inflation)
|
78
|
+
# @price = (price * (1 + inflation)).round(2)
|
79
|
+
# end
|
80
|
+
# end
|
81
|
+
#
|
82
|
+
# class Bar
|
83
|
+
# include Arstotzka
|
84
|
+
#
|
85
|
+
# expose :drinks, type: :symbolized_hash,
|
86
|
+
# klass: Drink, after_each: :add_inflation
|
87
|
+
#
|
88
|
+
# def initialize(json)
|
89
|
+
# @json = json
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# private
|
93
|
+
#
|
94
|
+
# attr_reader :json
|
95
|
+
#
|
96
|
+
# def add_inflation(drink)
|
97
|
+
# drink.inflate(0.1)
|
98
|
+
# drink
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# json = '{"drinks":[{"name":"tequila","price":7.50},{ "name":"vodka","price":5.50}]}'
|
103
|
+
#
|
104
|
+
# hash = JSON.parse(hash)
|
105
|
+
#
|
106
|
+
# instance = Bar.new(hash)
|
107
|
+
#
|
108
|
+
# instance.drinks # returns [
|
109
|
+
# # Drink.new(name: 'tequila', price: 8.25),
|
110
|
+
# # Drink.new(name: 'vodka', price: 6.05)
|
111
|
+
# # ]
|
112
|
+
#
|
113
|
+
# @example Using cached, compact, after and full_path
|
114
|
+
# class Person
|
115
|
+
# attr_reader :name
|
116
|
+
#
|
117
|
+
# def initialize(name)
|
118
|
+
# @name = name
|
119
|
+
# end
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# class Application
|
123
|
+
# include Arstotzka
|
124
|
+
#
|
125
|
+
# expose :users, full_path: 'users.first_name',
|
126
|
+
# compact: true, cached: true,
|
127
|
+
# after: :create_person
|
128
|
+
#
|
129
|
+
# def initialize(json)
|
130
|
+
# @json = json
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# private
|
134
|
+
#
|
135
|
+
# attr_reader :json
|
136
|
+
#
|
137
|
+
# def create_person(names)
|
138
|
+
# names.map do |name|
|
139
|
+
# warn "Creating person #{name}"
|
140
|
+
# Person.new(name)
|
141
|
+
# end
|
142
|
+
# end
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# # Keys are on camel case (lower camel case)
|
146
|
+
# hash = {
|
147
|
+
# users: [
|
148
|
+
# { firstName: 'Lucy', email: 'lucy@gmail.com' },
|
149
|
+
# { firstName: 'Bobby', email: 'bobby@hotmail.com' },
|
150
|
+
# { email: 'richard@tracy.com' },
|
151
|
+
# { firstName: 'Arthur', email: 'arthur@kamelot.uk' }
|
152
|
+
# ]
|
153
|
+
# }
|
154
|
+
#
|
155
|
+
# instance = Application.new(hash)
|
156
|
+
#
|
157
|
+
# instance.users # trigers the warn "Creating person <name>" 3 times
|
158
|
+
# # returns [
|
159
|
+
# # Person.new('Lucy'),
|
160
|
+
# # Person.new('Bobby'),
|
161
|
+
# # Person.new('Arthur')
|
162
|
+
# # ]
|
163
|
+
# instance.users # returns the same value, without triggering warn
|
164
|
+
#
|
165
|
+
# @example Working with snake case hash
|
166
|
+
# class JobSeeker
|
167
|
+
# include Arstotzka
|
168
|
+
#
|
169
|
+
# expose :applicants, case: :snake, default: 'John Doe',
|
170
|
+
# full_path: 'applicants.full_name',
|
171
|
+
# compact: true, json: :@hash
|
172
|
+
#
|
173
|
+
# def initialize(hash)
|
174
|
+
# @hash = hash
|
175
|
+
# end
|
176
|
+
# end
|
177
|
+
#
|
178
|
+
# hash = {
|
179
|
+
# 'applicants' => [
|
180
|
+
# {
|
181
|
+
# 'full_name' => 'Robert Hatz',
|
182
|
+
# 'email' => 'robert.hatz@gmail.com'
|
183
|
+
# }, {
|
184
|
+
# 'full_name' => 'Marina Wantz',
|
185
|
+
# 'email' => 'marina.wantz@gmail.com'
|
186
|
+
# }, {
|
187
|
+
# 'email' => 'albert.witz@gmail.com'
|
188
|
+
# }
|
189
|
+
# ]
|
190
|
+
# }
|
191
|
+
#
|
192
|
+
# instance = JobSeeker.new(hash)
|
193
|
+
#
|
194
|
+
# instance.applicants # returns [
|
195
|
+
# # 'Robert Hatz',
|
196
|
+
# # 'Marina Wantz',
|
197
|
+
# # 'John Doe'
|
198
|
+
# # ]
|
199
|
+
#
|
200
|
+
# @example Deep path with flatten option
|
201
|
+
# class ShoppingMall
|
202
|
+
# include Arstotzka
|
203
|
+
#
|
204
|
+
# expose :customers, path: 'floors.stores',
|
205
|
+
# flatten: true, json: :hash
|
206
|
+
#
|
207
|
+
# def initialize(hash)
|
208
|
+
# @hash = hash
|
209
|
+
# end
|
210
|
+
#
|
211
|
+
# private
|
212
|
+
#
|
213
|
+
# attr_reader :hash
|
214
|
+
# end
|
215
|
+
#
|
216
|
+
# hash = {
|
217
|
+
# floors: [{
|
218
|
+
# name: 'ground', stores: [{
|
219
|
+
# name: 'Starbucks', customers: %w[
|
220
|
+
# John Bobby Maria
|
221
|
+
# ]
|
222
|
+
# }, {
|
223
|
+
# name: 'Pizza Hut', customers: %w[
|
224
|
+
# Danny LJ
|
225
|
+
# ]
|
226
|
+
# }]
|
227
|
+
# }, {
|
228
|
+
# name: 'first', stores: [{
|
229
|
+
# name: 'Disney', customers: %w[
|
230
|
+
# Robert Richard
|
231
|
+
# ]
|
232
|
+
# }, {
|
233
|
+
# name: 'Comix', customers: %w[
|
234
|
+
# Linda Ariel
|
235
|
+
# ]
|
236
|
+
# }]
|
237
|
+
# }]
|
238
|
+
# }
|
239
|
+
#
|
240
|
+
# instance = ShoppingMall.new(hash)
|
241
|
+
#
|
242
|
+
# instance.customers # returns %w[
|
243
|
+
# # John Bobby Maria
|
244
|
+
# # Danny LJ Robert Richard
|
245
|
+
# # Linda Ariel
|
246
|
+
# # ]
|
7
247
|
class Options < ::OpenStruct
|
8
248
|
DEFAULT_OPTIONS = {
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
249
|
+
after: false,
|
250
|
+
after_each: nil,
|
251
|
+
cached: false,
|
252
|
+
case: :lower_camel,
|
253
|
+
compact: false,
|
254
|
+
default: nil,
|
255
|
+
flatten: false,
|
256
|
+
full_path: nil,
|
257
|
+
json: :json,
|
258
|
+
klass: nil,
|
259
|
+
path: nil,
|
260
|
+
type: :none
|
20
261
|
}.freeze
|
21
262
|
|
22
263
|
# Creates a new instance of Options
|
23
264
|
#
|
24
265
|
# @param options [Hash] options hash
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
# @option options [String,Symbol]
|
30
|
-
# value
|
31
|
-
#
|
266
|
+
#
|
267
|
+
# Options hash are initialized and merged with {DEFAULT_OPTIONS}
|
268
|
+
# when using {ClassMethods#expose}
|
269
|
+
#
|
270
|
+
# @option options [String,Symbol] after: {Fetcher} option with the name of the method to be
|
271
|
+
# called once the value is fetched for mapping the value
|
272
|
+
#
|
273
|
+
# @option options [String,Symbol] after_each: {Wrapper} option with method that will be called
|
274
|
+
# on each individual result (while after is called on the whole collection)
|
32
275
|
# @option options [Boolean] cached: flag if the result should be memorized instead of repeating
|
33
276
|
# the crawling
|
34
|
-
# @option options [String,Symbol] case:
|
277
|
+
# @option options [String,Symbol] case: {Reader} flag definining on which case will
|
35
278
|
# the keys be defined
|
36
279
|
# - lower_camel: keys in the hash are lowerCamelCase
|
37
280
|
# - upper_camel: keys in the hash are UpperCamelCase
|
38
281
|
# - snake: keys in the hash are snake_case
|
39
|
-
# @option options [Boolean] compact:
|
282
|
+
# @option options [Boolean] compact: {Crawler} flag to apply Array#compact thus
|
40
283
|
# removing nil results
|
41
|
-
# @option options [
|
42
|
-
#
|
43
|
-
# @option options [String,Symbol] after: {Fetcher} option with the name of the method to be
|
44
|
-
# called once the value is fetched for mapping the value
|
45
|
-
# @option options [Boolean] flatten: {Fetcher} flag to aplly Array#flatten thus
|
284
|
+
# @option options [Boolean] default: {Crawler} option to return default value instead of nil
|
285
|
+
# @option options [Boolean] flatten: {Fetcher} flag to aplly Array#flatten thus
|
46
286
|
# avoing nested arrays
|
287
|
+
# @option options [String,Symbol] full_path: path of hash attributes to find exacttly where the
|
288
|
+
# value live (ignoring the attribute name)
|
289
|
+
# @option options [Class] klass: {Fetcher} option that, when passed, wraps the individual
|
290
|
+
# results in an instance of the given class
|
291
|
+
# @option options [String,Symbol] json: name of the method containing the hash to be crawled
|
292
|
+
# @option options [String,Symbol] path path of hash attributes to find the root
|
293
|
+
# where the attribute live (then fetching it using the attribute name)
|
47
294
|
# @option options [String,Symbol] type: {Fetcher} option declaring the type of the returned
|
48
|
-
# value (to use casting)
|
295
|
+
# value (to use casting) (see {TypeCast})
|
49
296
|
# - integer
|
50
297
|
# - string
|
51
298
|
# - float
|
data/lib/arstotzka/version.rb
CHANGED
data/lib/arstotzka/wrapper.rb
CHANGED
@@ -74,10 +74,12 @@ module Arstotzka
|
|
74
74
|
#
|
75
75
|
# @return [Object]
|
76
76
|
def wrap_element(value)
|
77
|
-
value = cast(value)
|
77
|
+
value = cast(value)
|
78
78
|
return if value.nil?
|
79
79
|
|
80
|
-
|
80
|
+
value = wrap_in_class(value)
|
81
|
+
|
82
|
+
after(value)
|
81
83
|
end
|
82
84
|
|
83
85
|
# @private
|
@@ -108,7 +110,31 @@ module Arstotzka
|
|
108
110
|
#
|
109
111
|
# @return [Object]
|
110
112
|
def cast(value)
|
113
|
+
return if value.nil?
|
114
|
+
return value unless type?
|
115
|
+
|
111
116
|
public_send("to_#{type}", value)
|
112
117
|
end
|
118
|
+
|
119
|
+
# @private
|
120
|
+
#
|
121
|
+
# Wrap resulting value in class
|
122
|
+
#
|
123
|
+
# @return [Object] instance of +options.klass+
|
124
|
+
def wrap_in_class(value)
|
125
|
+
return value unless klass
|
126
|
+
klass.new(value)
|
127
|
+
end
|
128
|
+
|
129
|
+
# @private
|
130
|
+
#
|
131
|
+
# Process and wrap value trhough a method call
|
132
|
+
#
|
133
|
+
# @return [Object] result of method call
|
134
|
+
def after(value)
|
135
|
+
return value unless options.after_each
|
136
|
+
|
137
|
+
options.instance.send(options.after_each, value)
|
138
|
+
end
|
113
139
|
end
|
114
140
|
end
|
@@ -5,14 +5,15 @@ require 'spec_helper'
|
|
5
5
|
describe Arstotzka::Fetcher do
|
6
6
|
describe 'yard' do
|
7
7
|
describe '#fetch' do
|
8
|
-
subject(:fetcher) { described_class.new(
|
8
|
+
subject(:fetcher) { described_class.new(**options) }
|
9
9
|
|
10
10
|
let(:instance) { Account.new(hash) }
|
11
11
|
let(:options) do
|
12
12
|
{
|
13
|
-
path:
|
14
|
-
klass:
|
15
|
-
after:
|
13
|
+
path: 'transactions',
|
14
|
+
klass: Transaction,
|
15
|
+
after: :filter_income,
|
16
|
+
instance: instance
|
16
17
|
}
|
17
18
|
end
|
18
19
|
let(:hash) do
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Arstotzka::Options do
|
6
|
+
subject(:instance) { clazz.new(hash) }
|
7
|
+
|
8
|
+
describe 'yard' do
|
9
|
+
describe 'Using options klass and after' do
|
10
|
+
let(:hash) do
|
11
|
+
{
|
12
|
+
customers: [{
|
13
|
+
name: 'John', age: 21
|
14
|
+
}, {
|
15
|
+
name: 'Julia', age: 15
|
16
|
+
}, {
|
17
|
+
name: 'Carol', age: 22
|
18
|
+
}, {
|
19
|
+
name: 'Bobby', age: 12
|
20
|
+
}]
|
21
|
+
}
|
22
|
+
end
|
23
|
+
|
24
|
+
let(:clazz) { Store }
|
25
|
+
let(:expected) do
|
26
|
+
[
|
27
|
+
Customer.new(name: 'John', age: 21),
|
28
|
+
Customer.new(name: 'Carol', age: 22)
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'returns only the adults' do
|
33
|
+
expect(instance.customers).to eq(expected)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe 'type with klass and after_each' do
|
38
|
+
let(:clazz) { Bar }
|
39
|
+
let(:hash) { JSON.parse(json) }
|
40
|
+
|
41
|
+
let(:json) do
|
42
|
+
'{"drinks":[{"name":"tequila","price":7.50},{ "name":"vodka","price":5.50}]}'
|
43
|
+
end
|
44
|
+
|
45
|
+
let(:expected) do
|
46
|
+
[
|
47
|
+
Drink.new(name: 'tequila', price: 8.25),
|
48
|
+
Drink.new(name: 'vodka', price: 6.05)
|
49
|
+
]
|
50
|
+
end
|
51
|
+
|
52
|
+
before do
|
53
|
+
module Arstotzka::TypeCast
|
54
|
+
def to_symbolized_hash(value)
|
55
|
+
value.symbolize_keys
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns inflated drinks' do
|
61
|
+
expect(instance.drinks).to eq(expected)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe 'Using cached, compact, after and full_path' do
|
66
|
+
let(:clazz) { Application }
|
67
|
+
|
68
|
+
let(:hash) do
|
69
|
+
{
|
70
|
+
users: [
|
71
|
+
{ firstName: 'Lucy', email: 'lucy@gmail.com' },
|
72
|
+
{ firstName: 'Bobby', email: 'bobby@hotmail.com' },
|
73
|
+
{ email: 'richard@tracy.com' },
|
74
|
+
{ firstName: 'Arthur', email: 'arthur@kamelot.uk' }
|
75
|
+
]
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
let(:expected) do
|
80
|
+
[
|
81
|
+
Person.new('Lucy'),
|
82
|
+
Person.new('Bobby'),
|
83
|
+
Person.new('Arthur')
|
84
|
+
]
|
85
|
+
end
|
86
|
+
|
87
|
+
before do
|
88
|
+
# rubocop:disable RSpec/SubjectStub
|
89
|
+
allow(instance).to receive(:warn)
|
90
|
+
# rubocop:enable RSpec/SubjectStub
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'Returns created users' do
|
94
|
+
expect(instance.users).to eq(expected)
|
95
|
+
end
|
96
|
+
|
97
|
+
it 'Triggers warn 3 times' do
|
98
|
+
instance.users
|
99
|
+
instance.users
|
100
|
+
expect(instance).to have_received(:warn).exactly(3).times
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
describe 'working with snake case hash' do
|
105
|
+
let(:clazz) { JobSeeker }
|
106
|
+
|
107
|
+
let(:hash) do
|
108
|
+
{
|
109
|
+
'applicants' => [
|
110
|
+
{
|
111
|
+
'full_name' => 'Robert Hatz',
|
112
|
+
'email' => 'robert.hatz@gmail.com'
|
113
|
+
}, {
|
114
|
+
'full_name' => 'Marina Wantz',
|
115
|
+
'email' => 'marina.wantz@gmail.com'
|
116
|
+
}, {
|
117
|
+
'email' => 'albert.witz@gmail.com'
|
118
|
+
}
|
119
|
+
]
|
120
|
+
}
|
121
|
+
end
|
122
|
+
|
123
|
+
let(:expected) do
|
124
|
+
['Robert Hatz', 'Marina Wantz', 'John Doe']
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'treats keys as snake case keys' do
|
128
|
+
expect(instance.applicants).to eq(expected)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
describe 'Deep path with flatten' do
|
133
|
+
let(:clazz) { ShoppingMall }
|
134
|
+
|
135
|
+
let(:hash) do
|
136
|
+
{
|
137
|
+
floors: [{
|
138
|
+
name: 'ground', stores: [{
|
139
|
+
name: 'Starbucks', customers: %w[
|
140
|
+
John Bobby Maria
|
141
|
+
]
|
142
|
+
}, {
|
143
|
+
name: 'Pizza Hut', customers: %w[
|
144
|
+
Danny LJ
|
145
|
+
]
|
146
|
+
}]
|
147
|
+
}, {
|
148
|
+
name: 'first', stores: [{
|
149
|
+
name: 'Disney', customers: %w[
|
150
|
+
Robert Richard
|
151
|
+
]
|
152
|
+
}, {
|
153
|
+
name: 'Comix', customers: %w[
|
154
|
+
Linda Ariel
|
155
|
+
]
|
156
|
+
}]
|
157
|
+
}]
|
158
|
+
}
|
159
|
+
end
|
160
|
+
|
161
|
+
let(:expected) do
|
162
|
+
%w[
|
163
|
+
John Bobby Maria
|
164
|
+
Danny LJ
|
165
|
+
Robert Richard
|
166
|
+
Linda Ariel
|
167
|
+
]
|
168
|
+
end
|
169
|
+
|
170
|
+
it 'returns all the customers in one array' do
|
171
|
+
expect(instance.customers).to eq(expected)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -4,16 +4,17 @@ require 'spec_helper'
|
|
4
4
|
|
5
5
|
describe Arstotzka::Fetcher do
|
6
6
|
subject(:fetcher) do
|
7
|
-
described_class.new
|
7
|
+
described_class.new options
|
8
8
|
end
|
9
9
|
|
10
|
+
let(:options) { Arstotzka::Options.new(options_hash) }
|
10
11
|
let(:instance) { Arstotzka::Fetcher::Dummy.new(json) }
|
11
12
|
let(:json) { load_json_fixture_file('arstotzka.json') }
|
12
13
|
let(:value) { fetcher.fetch }
|
13
14
|
|
14
15
|
context 'when fetching with no options' do
|
15
|
-
let(:
|
16
|
-
let(:key)
|
16
|
+
let(:options_hash) { { instance: instance, key: key } }
|
17
|
+
let(:key) { 'id' }
|
17
18
|
|
18
19
|
it 'retrieves attribute from base json' do
|
19
20
|
expect(value).to eq(json['id'])
|
@@ -48,7 +49,9 @@ describe Arstotzka::Fetcher do
|
|
48
49
|
let(:value) { [[[1, 2], [3, 4]], [[5, 6], [7, 8]]] }
|
49
50
|
|
50
51
|
context 'when flatten option is true' do
|
51
|
-
let(:
|
52
|
+
let(:options_hash) do
|
53
|
+
{ instance: instance, flatten: true, key: :value }
|
54
|
+
end
|
52
55
|
|
53
56
|
it 'returns the fetched value flattened' do
|
54
57
|
expect(fetcher.fetch).to eq((1..8).to_a)
|
@@ -64,7 +67,9 @@ describe Arstotzka::Fetcher do
|
|
64
67
|
end
|
65
68
|
|
66
69
|
context 'when flatten option is false' do
|
67
|
-
let(:
|
70
|
+
let(:options_hash) do
|
71
|
+
{ instance: instance, flatten: false, key: :value }
|
72
|
+
end
|
68
73
|
|
69
74
|
it 'returns the fetched value non flattened' do
|
70
75
|
expect(fetcher.fetch).to eq(value)
|
@@ -75,7 +80,10 @@ describe Arstotzka::Fetcher do
|
|
75
80
|
describe 'after option' do
|
76
81
|
let(:instance) { MyParser.new(json) }
|
77
82
|
let(:json) { { value: [100, 250, -25] } }
|
78
|
-
|
83
|
+
|
84
|
+
let(:options_hash) do
|
85
|
+
{ instance: instance, after: :sum, key: :value }
|
86
|
+
end
|
79
87
|
|
80
88
|
it 'applies after call ' do
|
81
89
|
expect(fetcher.fetch).to eq(325)
|
@@ -83,12 +91,15 @@ describe Arstotzka::Fetcher do
|
|
83
91
|
end
|
84
92
|
|
85
93
|
describe 'klass options' do
|
86
|
-
let(:path)
|
94
|
+
let(:path) { 'name' }
|
87
95
|
let(:name) { 'Robert' }
|
88
96
|
let(:json) { { name: name } }
|
89
|
-
let(:options) { { klass: wrapper, path: path } }
|
90
97
|
let(:wrapper) { Person }
|
91
98
|
|
99
|
+
let(:options_hash) do
|
100
|
+
{ instance: instance, klass: wrapper, path: path }
|
101
|
+
end
|
102
|
+
|
92
103
|
it 'wraps the result in an object' do
|
93
104
|
expect(fetcher.fetch).to be_a(wrapper)
|
94
105
|
end
|
@@ -97,4 +108,38 @@ describe Arstotzka::Fetcher do
|
|
97
108
|
expect(fetcher.fetch.name).to eq(name)
|
98
109
|
end
|
99
110
|
end
|
111
|
+
|
112
|
+
describe 'after_each options' do
|
113
|
+
let(:full_path) { 'people.name' }
|
114
|
+
let(:instance) { Group.new(json) }
|
115
|
+
|
116
|
+
let(:options_hash) do
|
117
|
+
{
|
118
|
+
instance: instance,
|
119
|
+
full_path: full_path,
|
120
|
+
after_each: :create_person,
|
121
|
+
json: :@hash,
|
122
|
+
compact: true
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
let(:json) do
|
127
|
+
{
|
128
|
+
people: [
|
129
|
+
{ name: 'Robert', age: 20 },
|
130
|
+
{ name: 'John', age: 25 },
|
131
|
+
{ name: 'Leeloo', age: 3570 },
|
132
|
+
{ age: 10 }
|
133
|
+
]
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
it do
|
138
|
+
expect(fetcher.fetch).to be_a(Array)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'calls the given method on each value' do
|
142
|
+
expect(fetcher.fetch).to all(be_a(Person))
|
143
|
+
end
|
144
|
+
end
|
100
145
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Application
|
4
|
+
include Arstotzka
|
5
|
+
|
6
|
+
expose :users, full_path: 'users.first_name',
|
7
|
+
compact: true, cached: true,
|
8
|
+
after: :create_person
|
9
|
+
|
10
|
+
def initialize(json)
|
11
|
+
@json = json
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :json
|
17
|
+
|
18
|
+
def create_person(names)
|
19
|
+
names.map do |name|
|
20
|
+
warn "Creating person #{name}"
|
21
|
+
Person.new(name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Bar
|
4
|
+
include Arstotzka
|
5
|
+
|
6
|
+
expose :drinks, type: :symbolized_hash,
|
7
|
+
klass: Drink, after_each: :add_inflation
|
8
|
+
|
9
|
+
def initialize(json)
|
10
|
+
@json = json
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
attr_reader :json
|
16
|
+
|
17
|
+
def add_inflation(drink)
|
18
|
+
drink.inflate(0.1)
|
19
|
+
drink
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Customer
|
4
|
+
attr_reader :name, :age
|
5
|
+
|
6
|
+
def initialize(name:, age:)
|
7
|
+
@name = name
|
8
|
+
@age = age
|
9
|
+
end
|
10
|
+
|
11
|
+
def adult?
|
12
|
+
age >= 18
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
return false unless other.class == self.class
|
17
|
+
other.name == name &&
|
18
|
+
other.age == age
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Drink
|
4
|
+
attr_reader :name, :price
|
5
|
+
|
6
|
+
def initialize(name:, price:)
|
7
|
+
@name = name
|
8
|
+
@price = price
|
9
|
+
end
|
10
|
+
|
11
|
+
def inflate(inflation)
|
12
|
+
@price = (price * (1 + inflation)).round(2)
|
13
|
+
end
|
14
|
+
|
15
|
+
def ==(other)
|
16
|
+
return false unless other.class == self.class
|
17
|
+
other.name == name &&
|
18
|
+
other.price == price
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Store
|
4
|
+
include Arstotzka
|
5
|
+
|
6
|
+
expose :customers, klass: Customer, after: :filter_adults
|
7
|
+
|
8
|
+
def initialize(json)
|
9
|
+
@json = json
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
attr_reader :json
|
15
|
+
|
16
|
+
def filter_adults(values)
|
17
|
+
values.select(&:adult?)
|
18
|
+
end
|
19
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: arstotzka
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.2.
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Darthjee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-03-
|
11
|
+
date: 2019-03-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -228,6 +228,7 @@ files:
|
|
228
228
|
- spec/integration/yard/arstotzka/fetcher_builder_spec.rb
|
229
229
|
- spec/integration/yard/arstotzka/fetcher_spec.rb
|
230
230
|
- spec/integration/yard/arstotzka/method_builder_spec.rb
|
231
|
+
- spec/integration/yard/arstotzka/options_spec.rb
|
231
232
|
- spec/integration/yard/arstotzka/reader_spec.rb
|
232
233
|
- spec/integration/yard/arstotzka/type_cast_spec.rb
|
233
234
|
- spec/integration/yard/arstotzka/wrapper_spec.rb
|
@@ -244,22 +245,30 @@ files:
|
|
244
245
|
- spec/support/fixture_helpers.rb
|
245
246
|
- spec/support/models.rb
|
246
247
|
- spec/support/models/account.rb
|
248
|
+
- spec/support/models/application.rb
|
247
249
|
- spec/support/models/arstotzka/dummy.rb
|
248
250
|
- spec/support/models/arstotzka/fetcher/dummy.rb
|
249
251
|
- spec/support/models/arstotzka/type_cast.rb
|
250
252
|
- spec/support/models/arstotzka/wrapper/dummy.rb
|
253
|
+
- spec/support/models/bar.rb
|
251
254
|
- spec/support/models/car.rb
|
252
255
|
- spec/support/models/car_collector.rb
|
253
256
|
- spec/support/models/collector.rb
|
254
257
|
- spec/support/models/collector/game.rb
|
258
|
+
- spec/support/models/customer.rb
|
259
|
+
- spec/support/models/drink.rb
|
255
260
|
- spec/support/models/game.rb
|
261
|
+
- spec/support/models/group.rb
|
256
262
|
- spec/support/models/house.rb
|
263
|
+
- spec/support/models/job_seeker.rb
|
257
264
|
- spec/support/models/my_model.rb
|
258
265
|
- spec/support/models/my_parser.rb
|
259
266
|
- spec/support/models/person.rb
|
260
267
|
- spec/support/models/request.rb
|
268
|
+
- spec/support/models/shopping_mall.rb
|
261
269
|
- spec/support/models/star.rb
|
262
270
|
- spec/support/models/star_gazer.rb
|
271
|
+
- spec/support/models/store.rb
|
263
272
|
- spec/support/models/transaction.rb
|
264
273
|
- spec/support/models/type_caster.rb
|
265
274
|
- spec/support/shared_examples/wrapper.rb
|
@@ -299,6 +308,7 @@ test_files:
|
|
299
308
|
- spec/integration/yard/arstotzka/fetcher_builder_spec.rb
|
300
309
|
- spec/integration/yard/arstotzka/fetcher_spec.rb
|
301
310
|
- spec/integration/yard/arstotzka/method_builder_spec.rb
|
311
|
+
- spec/integration/yard/arstotzka/options_spec.rb
|
302
312
|
- spec/integration/yard/arstotzka/reader_spec.rb
|
303
313
|
- spec/integration/yard/arstotzka/type_cast_spec.rb
|
304
314
|
- spec/integration/yard/arstotzka/wrapper_spec.rb
|
@@ -315,22 +325,30 @@ test_files:
|
|
315
325
|
- spec/support/fixture_helpers.rb
|
316
326
|
- spec/support/models.rb
|
317
327
|
- spec/support/models/account.rb
|
328
|
+
- spec/support/models/application.rb
|
318
329
|
- spec/support/models/arstotzka/dummy.rb
|
319
330
|
- spec/support/models/arstotzka/fetcher/dummy.rb
|
320
331
|
- spec/support/models/arstotzka/type_cast.rb
|
321
332
|
- spec/support/models/arstotzka/wrapper/dummy.rb
|
333
|
+
- spec/support/models/bar.rb
|
322
334
|
- spec/support/models/car.rb
|
323
335
|
- spec/support/models/car_collector.rb
|
324
336
|
- spec/support/models/collector.rb
|
325
337
|
- spec/support/models/collector/game.rb
|
338
|
+
- spec/support/models/customer.rb
|
339
|
+
- spec/support/models/drink.rb
|
326
340
|
- spec/support/models/game.rb
|
341
|
+
- spec/support/models/group.rb
|
327
342
|
- spec/support/models/house.rb
|
343
|
+
- spec/support/models/job_seeker.rb
|
328
344
|
- spec/support/models/my_model.rb
|
329
345
|
- spec/support/models/my_parser.rb
|
330
346
|
- spec/support/models/person.rb
|
331
347
|
- spec/support/models/request.rb
|
348
|
+
- spec/support/models/shopping_mall.rb
|
332
349
|
- spec/support/models/star.rb
|
333
350
|
- spec/support/models/star_gazer.rb
|
351
|
+
- spec/support/models/store.rb
|
334
352
|
- spec/support/models/transaction.rb
|
335
353
|
- spec/support/models/type_caster.rb
|
336
354
|
- spec/support/shared_examples/wrapper.rb
|