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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3d37d619db8888a43c76400066d7657b53bbf94297e2be0f900fd7f1c391d080
4
- data.tar.gz: 98d542ee52adac431bfe9acfcbc4a1bdd6cfdc002647b594b0f4fedfe371a208
3
+ metadata.gz: fcdfefb20e55716afccbdb1db14cb24c1b23f5c3609e0cdaccfb897e6f949c32
4
+ data.tar.gz: f374f9f717e7f3b867d40270bc531eb3c22e54b9060f232e5984157c4039e612
5
5
  SHA512:
6
- metadata.gz: 94dcbbed1f5665dab68a52f82222016aad12c68aca3f6676bbf95727f2131bd94c48217e584a245342519ac7c3da887852f85ca3300bf9b5c2a12b2e9d6847e1
7
- data.tar.gz: cc368f423651b046af8965f8ca5fc34efbfc8e026c5f894d615621d597360009f713a81554b57e5d8361559f4cf013909be0411ee47bce5a2259f25eef3a8851
6
+ metadata.gz: 9c2b0cce20c80bdfdc92bfd3dbb0eeff17585e7c2373daa5cfb960bf9356b54feb5d5c9ea66353a52f9b6400720e3fdcc2d27529f5456b9b2c6f0f50a066adc7
7
+ data.tar.gz: 2996ef066a6d98673f10961f82962e9d570fc23a34882d3a5d280efdb93677425eac4a52b0786551c324d04b296333b0c551603a5fd8923b99e61f81229ccf8e
@@ -13,3 +13,7 @@ Metrics/LineLength:
13
13
 
14
14
  RSpec/AlignLeftLetBrace:
15
15
  Enabled: true
16
+
17
+ Style/ClassAndModuleChildren:
18
+ Exclude:
19
+ - spec/integration/**/*.rb
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.0
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
- - path: path where to find the sub hash that contains the key (empty by default)
98
- - json: method that contains the hash to be parsed (json by default)
99
- - full_path: full path to fetch the value (empty by default)
100
- - cached: indicator that once the value has been fetched, it should be cached (false by default)
101
- - klass: class to be used when wrapping the final value
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
@@ -159,6 +159,8 @@ require 'sinclair'
159
159
  # # Collector::Game.new(name: "Zelda", played: 90.0)
160
160
  # # ]
161
161
  #
162
+ # @example (see Arstotzka::Options)
163
+ #
162
164
  # @see Arstotzka::MethodBuilder
163
165
  # @see Arstotzka::ClassMethods
164
166
  module Arstotzka
@@ -10,17 +10,13 @@ module Arstotzka
10
10
 
11
11
  # Creates an instance of Artotzka::Fetcher
12
12
  #
13
- # @param instance [Object] object whose methods will be called after for processing
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(instance, options)
16
+ # @overload iniitalize(options)
19
17
  # @param options [Arstotzka::Options] options for {Crawler}, {Wrapper} and {Reader}
20
- def initialize(instance, options_hash = {})
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(instance,
76
- # path: 'transactions',
77
- # klass: Transaction,
78
- # after: :filter_income
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
@@ -156,7 +156,7 @@ module Arstotzka
156
156
  #
157
157
  # @return Arstotzka::Fetcher
158
158
  def build(instance)
159
- Fetcher.new(instance, options)
159
+ Fetcher.new(options.merge(instance: instance))
160
160
  end
161
161
 
162
162
  private
@@ -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
- json: :json,
10
- path: nil,
11
- full_path: nil,
12
- cached: false,
13
- after: false,
14
- case: :lower_camel,
15
- klass: nil,
16
- compact: false,
17
- default: nil,
18
- flatten: false,
19
- type: :none
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
- # Options hash will be merged with {DEFAULT_OPTIONS}
26
- # @option options [Class] klass class to receive the methods
27
- # @option options [String,Symbol] path path of hash attributes to find the root
28
- # where the attribute live (then fetching it using the attribute name)
29
- # @option options [String,Symbol] full_path: path of hash attributes to find exacttly where the
30
- # value live (ignoring the attribute name)
31
- # @option options [String,Symbol] json: name of the method containing the hash to be crawled
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: {Reader} flag definining on which case will
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: {Crawler} flag to apply Array#compact thus
282
+ # @option options [Boolean] compact: {Crawler} flag to apply Array#compact thus
40
283
  # removing nil results
41
- # @option options [Class] klass: {Fetcher} option thatwhen passed, wraps the result in an
42
- # instance of the given class
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Arstotzka
4
- VERSION = '1.2.0'
4
+ VERSION = '1.2.1'
5
5
  end
@@ -74,10 +74,12 @@ module Arstotzka
74
74
  #
75
75
  # @return [Object]
76
76
  def wrap_element(value)
77
- value = cast(value) if type? && !value.nil?
77
+ value = cast(value)
78
78
  return if value.nil?
79
79
 
80
- klass ? klass.new(value) : value
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(instance, **options) }
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: 'transactions',
14
- klass: Transaction,
15
- after: :filter_income
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 instance, options
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(:options) { { key: key } }
16
- let(:key) { 'id' }
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(:options) { { flatten: true, key: :value } }
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(:options) { { flatten: false, key: :value } }
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
- let(:options) { { after: :sum, key: :value } }
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) { 'name' }
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,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Group
4
+ def initialize(hash)
5
+ @hash = hash
6
+ end
7
+
8
+ private
9
+
10
+ def create_person(name)
11
+ Person.new(name)
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class JobSeeker
4
+ include Arstotzka
5
+
6
+ expose :applicants, case: :snake, default: 'John Doe',
7
+ full_path: 'applicants.full_name',
8
+ compact: true, json: :@hash
9
+
10
+ def initialize(hash)
11
+ @hash = hash
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ShoppingMall
4
+ include Arstotzka
5
+
6
+ expose :customers, path: 'floors.stores',
7
+ flatten: true, json: :hash
8
+
9
+ def initialize(hash)
10
+ @hash = hash
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :hash
16
+ 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.0
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-18 00:00:00.000000000 Z
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