lhs 6.3.1 → 6.4.0

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
  SHA1:
3
- metadata.gz: e9f38d7e7d79b9099b282d29cdd8c126d9ec2a97
4
- data.tar.gz: b01e749df675ef68f1a13923a7d73b2317a58614
3
+ metadata.gz: 8657e6a2962b433061940b5fbed11d69538233b0
4
+ data.tar.gz: 588206ef328828b3ffa4ad5a33019f1c8e11f3fa
5
5
  SHA512:
6
- metadata.gz: ac30a7cd582675b6ba8d7b02748563b64ee6c572858102dcccd9be8db7e70213575dc5a35ded169449d738c78d6a79636df1eb35b0ffe48f068814038c59be93
7
- data.tar.gz: 8eb224deb70fa275b5ea13722854ee1d4a971f9ffa509c468aafd74dbf7684edc2a0d37551ff876733d87137b82386cd50c41b18cb9797f07217495b43ca5b68
6
+ metadata.gz: f18c4f116090fbf214ca85552455b79e282f47f72c60f9484f35b927616c036a8ae732850e98cd7484db0d889444082afa94b69d653dd4e0a1b23b747d5012ee
7
+ data.tar.gz: f241d8737f772951a5549ad847ff2905e190071254c8af57022c4e5d05d0375254d46b27496d4f1964c47e0cb4a0948557861b440120a71b1efeb86e7decfbe2
data/.rubocop.localch.yml CHANGED
@@ -19,10 +19,7 @@ AllCops:
19
19
 
20
20
  Rails:
21
21
  Enabled: true
22
-
23
- require:
24
- - rubocop-rspec
25
-
22
+
26
23
  Lint/HandleExceptions:
27
24
  Exclude:
28
25
  - spec/**/*
data/Gemfile CHANGED
@@ -8,4 +8,4 @@ gemspec
8
8
  # Declare any dependencies that are still in development here instead of in
9
9
  # your gemspec. These might include edge Rails or gems from your path or
10
10
  # Git. Remember to move these dependencies to your gemspec before releasing
11
- # your gem to rubygems.org.
11
+ # your gem to rubygems.org.
data/README.md CHANGED
@@ -8,15 +8,15 @@ LHS uses [LHC](//github.com/local-ch/LHC) for http requests.
8
8
  Access data that is provided by an http json service with ease using a LHS::Record.
9
9
 
10
10
  ```ruby
11
- class Feedback < LHS::Record
11
+ class Record < LHS::Record
12
12
 
13
- endpoint ':datastore/v2/content-ads/:campaign_id/feedbacks'
14
- endpoint ':datastore/v2/feedbacks'
13
+ endpoint ':service/v2/records'
14
+ endpoint ':service/v2/association/:association_id/records'
15
15
 
16
16
  end
17
17
 
18
- feedback = Feedback.find_by(email: 'somebody@mail.com') #<Feedback>
19
- feedback.review # "Lunch was great"
18
+ record = Record.find_by(email: 'somebody@mail.com') #<Record>
19
+ record.review # "Lunch was great"
20
20
  ```
21
21
 
22
22
  ## Where to store LHS::Records
@@ -28,12 +28,12 @@ Please store all defined LHS::Records in `app/models` as they are not autoloaded
28
28
  You setup a LHS::Record by configuring one or multiple endpoints. You can also add request options for an endpoint (see following example).
29
29
 
30
30
  ```ruby
31
- class Feedback < LHS::Record
31
+ class Record < LHS::Record
32
32
 
33
- endpoint ':datastore/v2/content-ads/:campaign_id/feedbacks'
34
- endpoint ':datastore/v2/content-ads/:campaign_id/feedbacks/:id'
35
- endpoint ':datastore/v2/feedbacks', cache: true, cache_expires_in: 1.day
36
- endpoint ':datastore/v2/feedbacks/:id', cache: true, cache_expires_in: 1.day
33
+ endpoint ':service/v2/association/:association_id/records'
34
+ endpoint ':service/v2/association/:association_id/records/:id'
35
+ endpoint ':service/v2/records', cache: true, cache_expires_in: 1.day
36
+ endpoint ':service/v2/records/:id', cache: true, cache_expires_in: 1.day
37
37
 
38
38
  end
39
39
  ```
@@ -44,10 +44,10 @@ Please use placeholders when configuring endpoints also for hosts. Otherwise LHS
44
44
  If you try to setup a LHS::Record with clashing endpoints it will immediately raise an exception.
45
45
 
46
46
  ```ruby
47
- class Feedback < LHS::Record
47
+ class Record < LHS::Record
48
48
 
49
- endpoint ':datastore/v2/reviews'
50
- endpoint ':datastore/v2/feedbacks'
49
+ endpoint ':service/v2/records'
50
+ endpoint ':service/v2/something_else'
51
51
 
52
52
  end
53
53
  # raises: Clashing endpoints.
@@ -58,16 +58,16 @@ end
58
58
  You can query a service for records by using `where`.
59
59
 
60
60
  ```ruby
61
- Feedback.where(has_reviews: true)
61
+ Record.where(color: 'blue')
62
62
  ```
63
63
 
64
- This uses the `:datastore/v2/feedbacks` endpoint, cause `:campaign_id` was not provided. In addition it would add `?has_reviews=true` to the get parameters.
64
+ This uses the `:service/v2/records` endpoint, cause `:association_id` was not provided. In addition it would add `?color=blue` to the get parameters.
65
65
 
66
66
  ```ruby
67
- Feedback.where(campaign_id: 'fq-a81ngsl1d')
67
+ Record.where(association_id: 'fq-a81ngsl1d')
68
68
  ```
69
69
 
70
- Uses the `:datastore/v2/content-ads/:campaign_id/feedbacks` endpoint.
70
+ Uses the `:service/v2/association/:association_id/records` endpoint.
71
71
 
72
72
  ## Chaining where statements
73
73
 
@@ -149,7 +149,7 @@ If no record is found an error is raised.
149
149
  `find` can also be used to find a single uniqe record with parameters:
150
150
 
151
151
  ```ruby
152
- Feedback.find(campaign_id: 123, id: 456)
152
+ Record.find(association_id: 123, id: 456)
153
153
  ```
154
154
 
155
155
  `find_by` finds the first record matching the specified conditions.
@@ -159,20 +159,37 @@ If no record is found, `nil` is returned.
159
159
  `find_by!` raises LHC::NotFound if nothing was found.
160
160
 
161
161
  ```ruby
162
- Feedback.find_by(id: 'z12f-3asm3ngals')
163
- Feedback.find_by(id: 'doesntexist') # nil
162
+ Record.find_by(id: 'z12f-3asm3ngals')
163
+ Record.find_by(id: 'doesntexist') # nil
164
164
  ```
165
165
 
166
166
  `first` is an alias for finding the first record without parameters.
167
167
 
168
168
  ```ruby
169
- Feedback.first
169
+ Record.first
170
170
  ```
171
171
 
172
172
  If no record is found, `nil` is returned.
173
173
 
174
174
  `first!` raises LHC::NotFound if nothing was found.
175
175
 
176
+ # Find multiple single records in parallel
177
+
178
+ In case you want to fetch multiple records by id in parallel, you can also do this with `find`:
179
+
180
+ ```ruby
181
+ Record.find(1, 2, 3)
182
+ ```
183
+
184
+ If you want to inject values for the failing records, that might not have been found, you can inject values for them with error handlers:
185
+
186
+ ```ruby
187
+ data = Record
188
+ .handle(LHC::Unauthorized, ->(response) { Record.new(name: 'unknown') })
189
+ .find(1, 2, 3)
190
+ data[1].name # 'unknown'
191
+ ```
192
+
176
193
  ## Navigate data
177
194
 
178
195
  After fetching [single](#find-single-records) or [multiple](#find-multiple-records) records you can navigate the received data with ease.
@@ -219,7 +236,7 @@ You can apply options to the request chain. Those options will be forwarded to t
219
236
  `all` fetches all records from the service by doing multiple requests if necessary.
220
237
 
221
238
  ```ruby
222
- data = Feedback.all
239
+ data = Record.all
223
240
  data.count # 998
224
241
  data.length # 998
225
242
  ```
@@ -229,26 +246,26 @@ data.length # 998
229
246
  `find_each` is a more fine grained way to process single records that are fetched in batches.
230
247
 
231
248
  ```ruby
232
- Feedback.find_each(start: 50, batch_size: 20, params: { has_reviews: true }) do |feedback|
249
+ Record.find_each(start: 50, batch_size: 20, params: { has_reviews: true }) do |record|
233
250
  # Iterates over each record. Starts with record nr. 50 and fetches 20 records each batch.
234
- feedback
235
- break if feedback.some_attribute == some_value
251
+ record
252
+ break if record.some_attribute == some_value
236
253
  end
237
254
  ```
238
255
 
239
256
  `find_in_batches` is used by `find_each` and processes batches.
240
257
  ```ruby
241
- Feedback.find_in_batches(start: 50, batch_size: 20, params: { has_reviews: true }) do |feedbacks|
258
+ Record.find_in_batches(start: 50, batch_size: 20, params: { has_reviews: true }) do |records|
242
259
  # Iterates over multiple records (batch size is 20). Starts with record nr. 50 and fetches 20 records each batch.
243
- feedbacks
244
- break if feedback.some_attribute == some_value
260
+ records
261
+ break if records.first.name == some_value
245
262
  end
246
263
  ```
247
264
 
248
265
  ## Create records
249
266
 
250
267
  ```ruby
251
- feedback = Feedback.create(
268
+ record = Record.create(
252
269
  recommended: true,
253
270
  source_id: 'aaa',
254
271
  content_ad_id: '1z-5r1fkaj'
@@ -258,9 +275,9 @@ end
258
275
  When creation fails, the object contains errors. It provides them through the `errors` attribute:
259
276
 
260
277
  ```ruby
261
- feedback.errors #<LHS::Errors>
262
- feedback.errors.include?(:ratings) # true
263
- feedback.errors[:ratings] # ['REQUIRED_PROPERTY_VALUE']
278
+ record.errors #<LHS::Errors>
279
+ record.errors.include?(:ratings) # true
280
+ record.errors[:ratings] # ['REQUIRED_PROPERTY_VALUE']
264
281
  record.errors.messages # {:ratings=>["REQUIRED_PROPERTY_VALUE"], :recommended=>["REQUIRED_PROPERTY_VALUE"]}
265
282
  record.errors.message # ratings must be set when review or name or review_title is set | The property value is required; it cannot be null, empty, or blank."
266
283
  ```
@@ -270,8 +287,8 @@ When creation fails, the object contains errors. It provides them through the `e
270
287
  Build and persist new items from scratch are done either with `new` or it's alias `build`.
271
288
 
272
289
  ```ruby
273
- feedback = Feedback.new(recommended: true)
274
- feedback.save
290
+ record = Record.new(recommended: true)
291
+ record.save
275
292
  ```
276
293
 
277
294
  ## Custom setters and getters
@@ -280,21 +297,21 @@ Sometimes it is the case that you want to have your custom getters and setters a
280
297
  The initializer will now use custom setter if one is defined:
281
298
 
282
299
  ```ruby
283
- class Feedback < LHS::Record
300
+ class Record < LHS::Record
284
301
  def ratings=(ratings)
285
302
  _raw[:ratings] = ratings.map { |k, v| { name: k, value: v } }
286
303
  end
287
304
  end
288
305
 
289
- feedback = Feedback.new(ratings: { quality: 3 }) # <Feedback{:ratings=>[{:name=>:quality, :value=>3}]}>
290
- feedback.ratings # #<LHS::Data:0x007fc8fa6d4050 ... @_raw=[{:name=>:quality, :value=>3}]>
306
+ record = Record.new(ratings: { quality: 3 }) # <Record{:ratings=>[{:name=>:quality, :value=>3}]}>
307
+ record.ratings # #<LHS::Data:0x007fc8fa6d4050 ... @_raw=[{:name=>:quality, :value=>3}]>
291
308
 
292
309
  ```
293
310
 
294
311
  If you have an accompanying getter the whole data manipulation would be internal only.
295
312
 
296
313
  ```ruby
297
- class Feedback < LHS::Record
314
+ class Record < LHS::Record
298
315
  def ratings=(ratings)
299
316
  _raw[:ratings] = ratings.map { |k, v| { name: k, value: v } }
300
317
  end
@@ -304,8 +321,8 @@ class Feedback < LHS::Record
304
321
  end
305
322
  end
306
323
 
307
- feedback = Feedback.new(ratings: { quality: 3 }) # <Feedback{:ratings=>[{:name=>:quality, :value=>3}]}>
308
- feedback.ratings # {:quality=>3}
324
+ record = Record.new(ratings: { quality: 3 }) # <Record{:ratings=>[{:name=>:quality, :value=>3}]}>
325
+ record.ratings # {:quality=>3}
309
326
 
310
327
  ```
311
328
 
@@ -360,9 +377,9 @@ After include:
360
377
  ### Two-Level `includes`
361
378
 
362
379
  ```ruby
363
- # a feedback has a campaign, which has an entry
364
- feedbacks = Feedback.includes(campaign: :entry).where(has_reviews: true)
365
- feedbacks.first.campaign.entry.name # 'Casa Ferlin'
380
+ # a record has a association, which has an entry
381
+ records = Record.includes(association: :entry).where(has_reviews: true)
382
+ records.first.association.entry.name # 'Casa Ferlin'
366
383
  ```
367
384
 
368
385
  ### Multiple `includes`
@@ -375,7 +392,7 @@ After include:
375
392
  claims = Claims.includes([:localch_account, :entry]).where(place_id: 'huU90mB_6vAfUdVz_uDoyA')
376
393
 
377
394
  # Two-level with array of includes
378
- feedbacks = Feedback.includes(campaign: [:entry, :user]).where(has_reviews: true)
395
+ records = Record.includes(campaign: [:entry, :user]).where(has_reviews: true)
379
396
  ```
380
397
 
381
398
  ### Known LHS::Records are used to request linked resources
@@ -389,15 +406,15 @@ The [Auth Inteceptor](https://github.com/local-ch/lhc-core-interceptors#auth-int
389
406
  ```ruby
390
407
  class Favorite < LHS::Record
391
408
 
392
- endpoint ':datastore/:user_id/favorites', auth: { basic: { username: 'steve', password: 'can' } }
393
- endpoint ':datastore/:user_id/favorites/:id', auth: { basic: { username: 'steve', password: 'can' } }
409
+ endpoint ':service/:user_id/favorites', auth: { basic: { username: 'steve', password: 'can' } }
410
+ endpoint ':service/:user_id/favorites/:id', auth: { basic: { username: 'steve', password: 'can' } }
394
411
 
395
412
  end
396
413
 
397
414
  class Place < LHS::Record
398
415
 
399
- endpoint ':datastore/v2/places', auth: { basic: { username: 'steve', password: 'can' } }
400
- endpoint ':datastore/v2/places/:id', auth: { basic: { username: 'steve', password: 'can' } }
416
+ endpoint ':service/v2/places', auth: { basic: { username: 'steve', password: 'can' } }
417
+ endpoint ':service/v2/places/:id', auth: { basic: { username: 'steve', password: 'can' } }
401
418
 
402
419
  end
403
420
 
@@ -421,7 +438,7 @@ To influence how data is accessed/provied, you can use mappings to either map de
421
438
 
422
439
  ```ruby
423
440
  class LocalEntry < LHS::Record
424
- endpoint ':datastore/v2/local-entries'
441
+ endpoint ':service/v2/local-entries'
425
442
 
426
443
  def name
427
444
  addresses.first.business.identities.first.name
@@ -436,7 +453,7 @@ Nested records (in nested data) are automaticaly casted when the href matches an
436
453
 
437
454
  ```ruby
438
455
  class Place < LHS::Record
439
- endpoint ':datastore/v2/places'
456
+ endpoint ':service/v2/places'
440
457
 
441
458
  def name
442
459
  addresses.first.business.identities.first.name
@@ -444,7 +461,7 @@ class Place < LHS::Record
444
461
  end
445
462
 
446
463
  class Favorite < LHS::Record
447
- endpoint ':datastore/v2/favorites'
464
+ endpoint ':service/v2/favorites'
448
465
  end
449
466
 
450
467
  favorite = Favorite.includes(:place).find(1)
@@ -458,7 +475,7 @@ If automatic-detection of nested records does not work, make sure your LHS::Reco
458
475
  You can change attributes of LHS::Records:
459
476
 
460
477
  ```ruby
461
- record = Feedback.find(id: 'z12f-3asm3ngals')
478
+ record = Record.find(id: 'z12f-3asm3ngals')
462
479
  rcord.recommended = false
463
480
  ```
464
481
 
@@ -467,9 +484,9 @@ You can change attributes of LHS::Records:
467
484
  You can persist changes with `save`. `save` will return `false` if persisting fails. `save!` instead will raise an exception.
468
485
 
469
486
  ```ruby
470
- feedback = Feedback.find('1z-5r1fkaj')
471
- feedback.recommended = false
472
- feedback.save
487
+ record = Record.find('1z-5r1fkaj')
488
+ record.recommended = false
489
+ record.save
473
490
  ```
474
491
 
475
492
  ## Update
@@ -479,8 +496,8 @@ You can persist changes with `save`. `save` will return `false` if persisting fa
479
496
  `update` always updates the data of the local object first, before it tries to sync with an endpoint. So even if persisting fails, the local object is updated.
480
497
 
481
498
  ```ruby
482
- feedback = Feedback.find('1z-5r1fkaj')
483
- feedback.update(recommended: false)
499
+ record = Record.find('1z-5r1fkaj')
500
+ record.update(recommended: false)
484
501
  ```
485
502
 
486
503
  ## Destroy
@@ -488,8 +505,8 @@ feedback.update(recommended: false)
488
505
  You can delete records remotely by calling `destroy` on an LHS::Record.
489
506
 
490
507
  ```ruby
491
- feedback = Feedback.find('1z-5r1fkaj')
492
- feedback.destroy
508
+ record = Record.find('1z-5r1fkaj')
509
+ record.destroy
493
510
  ```
494
511
 
495
512
  ## Validation
@@ -500,7 +517,7 @@ The specific endpoint has to support validations with the `persist=false` parame
500
517
 
501
518
  ```ruby
502
519
  class User < LHS::Record
503
- endpoint ':datastore/v2/users', validates: true
520
+ endpoint ':service/v2/users', validates: true
504
521
  end
505
522
 
506
523
  user = User.build(email: 'im not an email address')
@@ -513,7 +530,7 @@ In case endpoints define other parameter names for validation like `publish` you
513
530
 
514
531
  ```ruby
515
532
  class User < LHS::Record
516
- endpoint ':datastore/v2/users', validates: 'publish'
533
+ endpoint ':service/v2/users', validates: 'publish'
517
534
  end
518
535
  ```
519
536
 
@@ -595,7 +612,7 @@ You can use chainable pagination in combination with query chains:
595
612
 
596
613
  ```ruby
597
614
  class Record < LHS::Record
598
- endpoint ':datastore/records'
615
+ endpoint ':service/records'
599
616
  end
600
617
  Record.page(3).per(20).where(color: 'blue')
601
618
  # /records?offset=40&limit=20&color=blue
@@ -605,7 +622,7 @@ The applied pagination strategy depends on the actual configured pagination, so
605
622
 
606
623
  ```ruby
607
624
  class Record < LHS::Record
608
- endpoint ':datastore/records'
625
+ endpoint ':service/records'
609
626
  configuration pagination_strategy: 'page'
610
627
  end
611
628
  Record.page(3).per(20).where(color: 'blue')
@@ -614,7 +631,7 @@ The applied pagination strategy depends on the actual configured pagination, so
614
631
 
615
632
  ```ruby
616
633
  class Record < LHS::Record
617
- endpoint ':datastore/records'
634
+ endpoint ':service/records'
618
635
  configuration pagination_strategy: 'start'
619
636
  end
620
637
  Record.page(3).per(20).where(color: 'blue')
data/lhs.gemspec CHANGED
@@ -20,7 +20,7 @@ Gem::Specification.new do |s|
20
20
  s.requirements << 'Ruby >= 2.0.0'
21
21
  s.required_ruby_version = '>= 2.0.0'
22
22
 
23
- s.add_dependency 'lhc', '>= 3.5.2'
23
+ s.add_dependency 'lhc', '>= 3.6.0'
24
24
  s.add_dependency 'lhc-core-interceptors', '>= 2.0.1'
25
25
 
26
26
  s.add_development_dependency 'rspec-rails', '>= 3.0.0'
@@ -161,8 +161,10 @@ class LHS::Record
161
161
  push ErrorHandling.new(error_class => handler)
162
162
  end
163
163
 
164
- def find(args)
165
- @record_class.find(args, chain_options)
164
+ def find(*args)
165
+ options = chain_options
166
+ options = options.merge(error_handler: chain_error_handler) if chain_error_handler.any?
167
+ @record_class.find(*args.push(options))
166
168
  end
167
169
 
168
170
  def find_by(params = {})
@@ -198,12 +200,11 @@ class LHS::Record
198
200
  end
199
201
 
200
202
  def resolve
203
+ options = chain_options
204
+ options = options.merge(params: chain_parameters.merge(chain_pagination))
205
+ options = options.merge(error_handler: chain_error_handler) if chain_error_handler.any?
201
206
  @resolved ||= @record_class.new(
202
- @record_class.request(
203
- chain_options
204
- .merge(params: chain_parameters.merge(chain_pagination))
205
- .merge(error_handling: chain_error_handling)
206
- )
207
+ @record_class.request(options)
207
208
  )
208
209
  end
209
210
 
@@ -223,7 +224,7 @@ class LHS::Record
223
224
  merge_links _links.select { |link| link.is_a? Option }
224
225
  end
225
226
 
226
- def chain_error_handling
227
+ def chain_error_handler
227
228
  _links.select { |link| link.is_a? ErrorHandling }
228
229
  end
229
230
 
@@ -7,22 +7,36 @@ class LHS::Record
7
7
 
8
8
  module ClassMethods
9
9
  # Find a single uniqe record
10
- def find(args, options = nil)
10
+ def find(*args)
11
+ args, options = process_args(args)
11
12
  data =
12
- if args.is_a? Hash
13
+ if args.is_a? Array
14
+ find_in_parallel(args, options)
15
+ elsif args.is_a? Hash
13
16
  find_with_parameters(args, options)
14
17
  else
15
18
  find_by_id(args, options)
16
19
  end
17
- return data unless data._record
20
+ return data if data.is_a?(Array) || !data._record
18
21
  data._record.new(data)
19
22
  end
20
23
 
21
24
  private
22
25
 
23
- def find_with_parameters(params, options = {})
24
- options ||= {}
25
- data = request(options.merge(params: params))
26
+ def process_args(args)
27
+ if args.length == 1
28
+ args = args.first
29
+ elsif args.length == 2 && args.last.is_a?(Hash)
30
+ options = args.pop if args.last.is_a?(Hash)
31
+ args = args.first
32
+ elsif args.last.is_a?(Hash)
33
+ options = args.pop
34
+ end
35
+ options ||= nil
36
+ [args, options]
37
+ end
38
+
39
+ def get_unique_item!(data)
26
40
  if data._proxy.is_a?(LHS::Collection)
27
41
  fail LHC::NotFound.new('Requested unique item. Multiple were found.', data._request.response) if data.length > 1
28
42
  data.first || fail(LHC::NotFound.new('No item was found.', data._request.response))
@@ -31,9 +45,26 @@ class LHS::Record
31
45
  end
32
46
  end
33
47
 
34
- def find_by_id(id, options = {})
35
- options ||= {}
36
- request(options.merge(params: { id: id }))
48
+ def find_with_parameters(args, options = {})
49
+ data = request(request_options(args, options))
50
+ get_unique_item!(data)
51
+ end
52
+
53
+ def find_by_id(args, options = {})
54
+ request(request_options(args, options))
55
+ end
56
+
57
+ def find_in_parallel(args, options)
58
+ options = args.map { |argument| request_options(argument, options) }
59
+ request(options)
60
+ end
61
+
62
+ def request_options(args, options)
63
+ if args.is_a? Hash
64
+ (options || {}).merge(params: args)
65
+ else
66
+ (options || {}).merge(params: { id: args })
67
+ end
37
68
  end
38
69
  end
39
70
  end
@@ -7,6 +7,7 @@ class LHS::Record
7
7
 
8
8
  module ClassMethods
9
9
  def request(options)
10
+ options ||= {}
10
11
  if options.is_a? Array
11
12
  multiple_requests(options)
12
13
  else
@@ -181,12 +182,12 @@ class LHS::Record
181
182
  next unless option.present?
182
183
  process_options(option, find_endpoint(option[:params]))
183
184
  end
184
- data = LHC.request(options.compact).map { |response| LHS::Data.new(response.body, nil, self, response.request) }
185
- data = restore_with_nils(data, locate_nils(options)) # nil objects in data provide location information for mapping
186
- unless data.empty?
187
- data = LHS::Data.new(data, nil, self)
188
- handle_includes(including, data, referencing) if including
185
+ data = LHC.request(options.compact).map do |response|
186
+ LHS::Data.new(response.body, nil, self, response.request)
189
187
  end
188
+ data = restore_with_nils(data, locate_nils(options)) # nil objects in data provide location information for mapping
189
+ data = LHS::Data.new(data, nil, self)
190
+ handle_includes(including, data, referencing) if including && !data.empty?
190
191
  data
191
192
  end
192
193
 
@@ -217,6 +218,7 @@ class LHS::Record
217
218
  # Merge explicit params and take configured endpoints options as base
218
219
  def process_options(options, endpoint)
219
220
  options[:params].deep_symbolize_keys! if options[:params]
221
+ options[:error_handler] = merge_error_handlers(options[:error_handler]) if options[:error_handler]
220
222
  options = (endpoint.options || {}).merge(options)
221
223
  options[:url] = compute_url!(options[:params]) unless options.key?(:url)
222
224
  merge_explicit_params!(options[:params])
@@ -224,6 +226,23 @@ class LHS::Record
224
226
  options
225
227
  end
226
228
 
229
+ # LHC supports only one error handler, merge all error handlers to one
230
+ # and reraise
231
+ def merge_error_handlers(handlers)
232
+ lambda do |response|
233
+ return_data = nil
234
+ error_class = LHC::Error.find(response)
235
+ error = error_class.new(error_class, response)
236
+ handlers = handlers.to_a.select { |error_handler| error.is_a? error_handler.class }
237
+ fail(error) unless handlers.any?
238
+ handlers.each do |handler|
239
+ handlers_return = handler.call(response)
240
+ return_data = handlers_return if handlers_return.present?
241
+ end
242
+ return return_data
243
+ end
244
+ end
245
+
227
246
  def record_for_options(options)
228
247
  records = []
229
248
  if options.is_a?(Array)
@@ -243,25 +262,10 @@ class LHS::Record
243
262
  options ||= {}
244
263
  options = options.dup
245
264
  endpoint = find_endpoint(options[:params])
246
- error_handling = options.delete(:error_handling)
247
- begin
248
- response = LHC.request(process_options(options, endpoint))
249
- data = LHS::Data.new(response.body, nil, self, response.request, endpoint)
250
- handle_includes(including, data, referencing) if including
251
- data
252
- rescue => error
253
- handle_error(error, error_handling)
254
- nil
255
- end
256
- end
257
-
258
- def handle_error(error, error_handling)
259
- error_handlers = (error_handling || []).select { |error_handler| error.is_a? error_handler.class }
260
- if error_handlers.any?
261
- error_handlers.each { |handler| handler.call(error) }
262
- else
263
- fail error
264
- end
265
+ response = LHC.request(process_options(options, endpoint))
266
+ data = LHS::Data.new(response.body, nil, self, response.request, endpoint)
267
+ handle_includes(including, data, referencing) if including
268
+ data
265
269
  end
266
270
 
267
271
  def url_option_for(item, key = nil)
data/lib/lhs/item.rb CHANGED
@@ -26,8 +26,10 @@ class LHS::Item < LHS::Proxy
26
26
  return set(name, args.try(&:first)) if name.to_s[/=$/]
27
27
  name = args.first if name == :[]
28
28
  value = _data._raw[name.to_s]
29
- value = _data._raw[name.to_sym] if value.nil?
30
- value = _data._raw[name.to_s.classify.to_sym] if value.nil?
29
+ if value.nil? && _data._raw.present?
30
+ value = _data._raw[name.to_sym]
31
+ value ||= _data._raw[name.to_s.classify.to_sym]
32
+ end
31
33
  if value.is_a?(Hash)
32
34
  handle_hash(value)
33
35
  elsif value.is_a?(Array)
data/lib/lhs/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LHS
2
- VERSION = "6.3.1"
2
+ VERSION = "6.4.0"
3
3
  end
@@ -0,0 +1,39 @@
1
+ require 'rails_helper'
2
+
3
+ describe LHS::Record do
4
+ before(:each) do
5
+ class Record < LHS::Record
6
+ endpoint 'http://datastore/records/:id'
7
+ end
8
+ end
9
+
10
+ context 'find in parallel' do
11
+ before(:each) do
12
+ stub_request(:get, "http://datastore/records/1").to_return(status: 200, body: { id: 1 }.to_json)
13
+ stub_request(:get, "http://datastore/records/3").to_return(status: 200, body: { id: 3 }.to_json)
14
+ end
15
+
16
+ it 'finds records in parallel' do
17
+ stub_request(:get, "http://datastore/records/2").to_return(status: 200, body: { id: 2 }.to_json)
18
+ allow(Record).to receive(:request).and_call_original
19
+ data = Record.find([1, 2, 3])
20
+ expect(Record).to have_received(:request).once
21
+ expect(data[0].id).to eq 1
22
+ expect(data[1].id).to eq 2
23
+ expect(data[2].id).to eq 3
24
+ end
25
+
26
+ it 'raises an exeption if one of the parallel request fails' do
27
+ stub_request(:get, "http://datastore/records/2").to_return(status: 401)
28
+ expect(-> { Record.find([1, 2, 3]) }).to raise_error(LHC::Unauthorized)
29
+ end
30
+
31
+ it 'applies error handlers from the chain and returns whatever the error handler returns' do
32
+ stub_request(:get, "http://datastore/records/2").to_return(status: 401)
33
+ data = Record
34
+ .handle(LHC::Unauthorized, ->(_response) { Record.new(name: 'unknown') })
35
+ .find(1, 2, 3)
36
+ expect(data[1].name).to eq 'unknown'
37
+ end
38
+ end
39
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhs
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.3.1
4
+ version: 6.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/local-ch/lhs/graphs/contributors
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-11 00:00:00.000000000 Z
11
+ date: 2016-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lhc
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 3.5.2
19
+ version: 3.6.0
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 3.5.2
26
+ version: 3.6.0
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: lhc-core-interceptors
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -298,6 +298,7 @@ files:
298
298
  - spec/record/find_by_spec.rb
299
299
  - spec/record/find_each_spec.rb
300
300
  - spec/record/find_in_batches_spec.rb
301
+ - spec/record/find_in_parallel_spec.rb
301
302
  - spec/record/find_spec.rb
302
303
  - spec/record/first_spec.rb
303
304
  - spec/record/immutable_chains_spec.rb
@@ -351,7 +352,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
351
352
  requirements:
352
353
  - Ruby >= 2.0.0
353
354
  rubyforge_project:
354
- rubygems_version: 2.6.5
355
+ rubygems_version: 2.4.6
355
356
  signing_key:
356
357
  specification_version: 4
357
358
  summary: Rails gem providing an easy, active-record-like interface for http json services
@@ -443,6 +444,7 @@ test_files:
443
444
  - spec/record/find_by_spec.rb
444
445
  - spec/record/find_each_spec.rb
445
446
  - spec/record/find_in_batches_spec.rb
447
+ - spec/record/find_in_parallel_spec.rb
446
448
  - spec/record/find_spec.rb
447
449
  - spec/record/first_spec.rb
448
450
  - spec/record/immutable_chains_spec.rb