arstotzka 1.2.0 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  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