lhs 15.5.1 → 15.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/lhs.gemspec CHANGED
@@ -8,9 +8,9 @@ Gem::Specification.new do |s|
8
8
  s.name = "lhs"
9
9
  s.version = LHS::VERSION
10
10
  s.authors = ['https://github.com/local-ch/lhs/graphs/contributors']
11
- s.email = ['ws-operations@local.ch']
11
+ s.email = ['web@localsearch.ch']
12
12
  s.homepage = 'https://github.com/local-ch/lhs'
13
- s.summary = 'Rails gem providing an easy, active-record-like interface for http json services'
13
+ s.summary = 'REST services accelerator: Rails gem providing an easy, active-record-like interface for http (hypermedia) json services'
14
14
  s.description = s.summary
15
15
 
16
16
  s.files = `git ls-files`.split("\n")
@@ -16,6 +16,10 @@ class LHS::Record
16
16
  Chain.new(self, Parameter.new(hash))
17
17
  end
18
18
 
19
+ def fetch
20
+ Chain.new(self, nil).fetch
21
+ end
22
+
19
23
  def all(hash = nil)
20
24
  chain = Chain.new(self, Parameter.new(hash))
21
25
  chain._links.push(Option.new(all: true))
@@ -60,6 +60,13 @@ class LHS::Record
60
60
  )
61
61
  end
62
62
 
63
+ # Allows record to be configured as not paginated,
64
+ # as by default it's considered paginated
65
+ def paginated
66
+ return true if @configuration.blank?
67
+ @configuration.fetch(:paginated, true)
68
+ end
69
+
63
70
  private
64
71
 
65
72
  def symbolize_unless_complex(value)
@@ -22,7 +22,7 @@ class LHS::Record
22
22
  private
23
23
 
24
24
  def single_request_load_and_merge_remaining_objects!(data, options, endpoint)
25
- return unless options[:all]
25
+ return if options[:all].blank? || !paginated
26
26
  load_and_merge_remaining_objects!(
27
27
  data: data,
28
28
  options: process_options(options, endpoint),
@@ -40,6 +40,7 @@ class LHS::Record
40
40
  # Tries to apply an high value for limit and reacts on the limit
41
41
  # returned by the endpoint to make further requests
42
42
  def apply_limit!(options)
43
+ return if !paginated || options[:all].blank?
43
44
  options[:params] ||= {}
44
45
  options[:params] = options[:params].merge(limit_key(:parameter) => options[:params][limit_key(:parameter)] || LHS::Pagination::Base::DEFAULT_LIMIT)
45
46
  end
@@ -239,6 +240,7 @@ class LHS::Record
239
240
  elsif data.collection? && paginated?(data.first.try(:_raw))
240
241
  load_and_merge_set_of_paginated_collections!(data, options)
241
242
  elsif load_not_paginated_collection
243
+ warn('[Warning] "all" has been requested, but endpoint does not provide pagination meta data. If you just want to fetch the first response, use "where" or "fetch".')
242
244
  load_and_merge_not_paginated_collection!(data, options)
243
245
  end
244
246
  end
@@ -522,7 +524,7 @@ class LHS::Record
522
524
  including = options.delete(:including)
523
525
  referencing = options.delete(:referencing)
524
526
  endpoint = find_endpoint(options[:params], options.fetch(:url, nil))
525
- apply_limit!(options) if options[:all]
527
+ apply_limit!(options)
526
528
  response = LHC.request(process_options(options, endpoint))
527
529
  return nil if !response.success? && response.error_ignored?
528
530
  data = LHS::Data.new(response.body, nil, self, response.request, endpoint)
data/lib/lhs/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module LHS
2
- VERSION = '15.5.1'
2
+ VERSION = '15.6.0'
3
3
  end
@@ -4,7 +4,7 @@ describe LHS, type: :request do
4
4
  context 'autoloading' do
5
5
  it "pre/re-loads all LHS classes initialy,|
6
6
  because it's necessary for endpoint-to-record-class-discovery",
7
- cleanup_before: false do
7
+ reset_before: false do
8
8
  all_endpoints = LHS::Record::Endpoints.all
9
9
  expect(all_endpoints['http://datastore/v2/users']).to be_present
10
10
  expect(all_endpoints['http://datastore/v2/users/{id}']).to be_present
@@ -0,0 +1,34 @@
1
+ class ErrorHandlingWithChainsController < ApplicationController
2
+
3
+ # Example where the query chain is resolved
4
+ # in the view (during render 'show')
5
+ def fetch_in_view
6
+ @records = Record
7
+ .handle(LHC::Error, ->(error) { handle_error(error) })
8
+ .where(color: 'blue')
9
+ render 'show'
10
+ render_error if @error
11
+ end
12
+
13
+ # Example where the query chain is resolved
14
+ # before the view is rendered
15
+ def fetch_in_controller
16
+ @records = Record
17
+ .handle(LHC::Error, ->(error) { handle_error(error) })
18
+ .where(color: 'blue').fetch
19
+ render 'show'
20
+ render_error if @error
21
+ end
22
+
23
+ private
24
+
25
+ def handle_error(error)
26
+ @error = error
27
+ nil
28
+ end
29
+
30
+ def render_error
31
+ self.response_body = nil
32
+ render 'error'
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ class Record < LHS::Record
2
+ endpoint 'http://datastore/v2/records'
3
+ endpoint 'http://datastore/v2/records/{id}'
4
+ end
@@ -0,0 +1 @@
1
+ Sorry there was an error.
@@ -0,0 +1,3 @@
1
+ <% @records.each do |record| %>
2
+ Name: <%= record.name %>
3
+ <% end %>
@@ -1,7 +1,13 @@
1
1
  Rails.application.routes.draw do
2
2
  root 'application#root'
3
+
4
+ # Request Cycle Cache
3
5
  get 'request_cycle_cache/simple' => 'request_cycle_cache#simple'
4
6
  get 'request_cycle_cache/no_caching_interceptor' => 'request_cycle_cache#no_caching_interceptor'
5
7
  get 'request_cycle_cache/parallel' => 'request_cycle_cache#parallel'
6
8
  get 'request_cycle_cache/headers' => 'request_cycle_cache#headers'
9
+
10
+ # Error handling with chains
11
+ get 'error_handling_with_chains/fetch_in_controller' => 'error_handling_with_chains#fetch_in_controller'
12
+ get 'error_handling_with_chains/fetch_in_view' => 'error_handling_with_chains#fetch_in_view'
7
13
  end
@@ -16,9 +16,9 @@ describe LHS::Record do
16
16
  end
17
17
  end
18
18
 
19
- context 'explicit pagination paramters for retreiving pages' do
19
+ context 'explicit pagination parameters for retrieving pages' do
20
20
 
21
- it 'uses explicit parameters when retreiving pages' do
21
+ it 'uses explicit parameters when retrieving pages' do
22
22
  stub_request(:get, "http://uberall/locations?max=100")
23
23
  .to_return(body: {
24
24
  response: {
@@ -101,5 +101,31 @@ describe LHS::Record do
101
101
  records = Category.all(language: 'en').fetch
102
102
  expect(records.length).to eq 300
103
103
  end
104
+
105
+ it 'outputs a warning, if all is requested, but endpoint does not implement pagination meta data' do
106
+ stub_batch('http://store/categories?language=en&max=100', 100)
107
+ stub_batch('http://store/categories?language=en&max=100&offset=100', 100)
108
+ stub_batch('http://store/categories?language=en&max=100&offset=200', 100)
109
+ stub_batch('http://store/categories?language=en&max=100&offset=300', 0)
110
+ expect(-> { Category.all(language: 'en').fetch }).to output(
111
+ %r{\[Warning\] "all" has been requested, but endpoint does not provide pagination meta data. If you just want to fetch the first response, use "where" or "fetch".}
112
+ ).to_stderr
113
+ end
114
+
115
+ context 'with record set to not paginated' do
116
+ before do
117
+ class Record < LHS::Record
118
+ configuration paginated: false
119
+ endpoint 'http://datastore/records'
120
+ end
121
+ end
122
+
123
+ it 'just fetches the first response' do
124
+ stub_request(:get, "http://datastore/records")
125
+ .to_return(body: [{ name: 'Steve' }].to_json)
126
+ records = Record.all
127
+ expect(records.first.name).to eq 'Steve'
128
+ end
129
+ end
104
130
  end
105
131
  end
@@ -0,0 +1,23 @@
1
+ require 'rails_helper'
2
+ require 'lhc/test/cache_helper.rb'
3
+
4
+ describe 'Error handling with chains', type: :request do
5
+ let!(:request) do
6
+ stub_request(:get, "http://datastore/v2/records?color=blue")
7
+ .to_return(status: 404)
8
+ end
9
+
10
+ it 'handles errors in rails controllers when query resolved in controller',
11
+ dummy_models: true do
12
+ get '/error_handling_with_chains/fetch_in_controller'
13
+ expect(request).to have_been_made.once
14
+ expect(response.body).to include('Sorry there was an error.')
15
+ end
16
+
17
+ it 'handles errors in rails controllers when query resolved in view',
18
+ dummy_models: true do
19
+ get '/error_handling_with_chains/fetch_in_view'
20
+ expect(request).to have_been_made.once
21
+ expect(response.body).to include('Sorry there was an error.')
22
+ end
23
+ end
@@ -8,17 +8,32 @@ describe LHS::Record do
8
8
  end
9
9
 
10
10
  context 'fetch' do
11
- let!(:request_stub) do
12
- stub_request(:get, "http://datastore/records/?available=true&color=blue&range=%3E26")
13
- .to_return(body: [{
14
- name: 'Steve'
15
- }].to_json)
11
+
12
+ context 'to resolve the chain' do
13
+ let!(:request_stub) do
14
+ stub_request(:get, "http://datastore/records/?available=true&color=blue&range=%3E26")
15
+ .to_return(body: [{
16
+ name: 'Steve'
17
+ }].to_json)
18
+ end
19
+
20
+ it 'resolves chains' do
21
+ records = Record.where(color: 'blue').where(range: '>26', available: true).fetch
22
+ expect(request_stub).to have_been_requested
23
+ expect(records.first.name).to eq 'Steve'
24
+ end
16
25
  end
17
26
 
18
- it 'resolves chains' do
19
- records = Record.where(color: 'blue').where(range: '>26', available: true).fetch
20
- expect(request_stub).to have_been_requested
21
- expect(records.first.name).to eq 'Steve'
27
+ context 'to fetch the first response' do
28
+ let!(:request_stub) do
29
+ stub_request(:get, "http://datastore/records/")
30
+ .to_return(body: [{ name: 'Steve' }])
31
+ end
32
+
33
+ it 'does fetch the first response if requested directly on the record' do
34
+ records = Record.fetch
35
+ expect(records.first.name).to eq 'Steve'
36
+ end
22
37
  end
23
38
  end
24
39
  end
@@ -21,7 +21,7 @@ describe 'Request Cycle Cache', type: :request do
21
21
  end
22
22
 
23
23
  it 'serves requests that are exactly the same during one request cycle from the cache',
24
- cleanup_before: false, request_cycle_cache: true do
24
+ dummy_models: true, request_cycle_cache: true do
25
25
  get '/request_cycle_cache/simple'
26
26
  expect(request).to have_been_made.once
27
27
 
@@ -31,7 +31,7 @@ describe 'Request Cycle Cache', type: :request do
31
31
  end
32
32
 
33
33
  it 'does not serve from request cycle cache when cache interceptor is not hooked in, but logs a warning',
34
- cleanup_before: false, request_cycle_cache: true do
34
+ dummy_models: true, request_cycle_cache: true do
35
35
  expect(lambda do
36
36
  get '/request_cycle_cache/no_caching_interceptor'
37
37
  end).to output(
@@ -41,14 +41,14 @@ describe 'Request Cycle Cache', type: :request do
41
41
  end
42
42
 
43
43
  it 'serves requests also from cache when LHS/LHC makes requests in parallel',
44
- cleanup_before: false, request_cycle_cache: true do
44
+ dummy_models: true, request_cycle_cache: true do
45
45
  get '/request_cycle_cache/parallel'
46
46
  expect(request).to have_been_made.once
47
47
  expect(second_request).to have_been_made.once
48
48
  end
49
49
 
50
50
  it 'sets different uniq request ids as base for request cycle caching for different requests',
51
- cleanup_before: false, request_cycle_cache: true do
51
+ dummy_models: true, request_cycle_cache: true do
52
52
  get '/request_cycle_cache/simple'
53
53
  first_request_id = LHS::Record::RequestCycleCache::RequestCycleThreadRegistry.request_id
54
54
  second_request_id = nil
@@ -64,7 +64,7 @@ describe 'Request Cycle Cache', type: :request do
64
64
 
65
65
  context 'disabled request cycle cache' do
66
66
  it 'does not serve from request cycle cache when cache interceptor is not hooked in, and does not warn if request cycle cache is explicitly disabled',
67
- cleanup_before: false do
67
+ dummy_models: true do
68
68
  expect(lambda do
69
69
  get '/request_cycle_cache/no_caching_interceptor'
70
70
  end).not_to output(
@@ -74,7 +74,7 @@ describe 'Request Cycle Cache', type: :request do
74
74
  end
75
75
 
76
76
  it 'DOES NOT serve requests that are exactly the same during one request cycle from the cache, when request cycle cache is disabled',
77
- cleanup_before: false do
77
+ dummy_models: true do
78
78
  get '/request_cycle_cache/simple'
79
79
  expect(request).to have_been_made.times(2)
80
80
  end
@@ -82,7 +82,7 @@ describe 'Request Cycle Cache', type: :request do
82
82
 
83
83
  context 'headers' do
84
84
  it 'considers the request headers when setting the cache key',
85
- cleanup_before: false, request_cycle_cache: true do
85
+ dummy_models: true, request_cycle_cache: true do
86
86
  get '/request_cycle_cache/headers'
87
87
  expect(request).to have_been_made.times(2)
88
88
  end
@@ -95,7 +95,7 @@ describe 'Request Cycle Cache', type: :request do
95
95
  after { LHS.config.request_cycle_cache = old_cache }
96
96
 
97
97
  it 'uses the cache passed in',
98
- cleanup_before: true, request_cycle_cache: true do
98
+ dummy_models: true, request_cycle_cache: true do
99
99
  expect(LHS.config.request_cycle_cache).to receive(:fetch).at_least(:once)
100
100
  expect(LHS.config.request_cycle_cache).to receive(:write).at_least(:once)
101
101
  get '/request_cycle_cache/simple'
@@ -0,0 +1,43 @@
1
+ require 'lhc'
2
+ class LHC::Config
3
+
4
+ def _cleanup
5
+ @endpoints = {}
6
+ @placeholders = {}
7
+ @interceptors = nil
8
+ end
9
+ end
10
+
11
+ class LHS::Record
12
+
13
+ CHILDREN = []
14
+
15
+ def self.inherited(child)
16
+ CHILDREN.push(child)
17
+ super
18
+ end
19
+
20
+ end
21
+
22
+ def reset_lhc
23
+ LHC::Config.instance._cleanup
24
+ end
25
+
26
+ def reset_lhs
27
+ LHS::Record::Endpoints.all = {}
28
+ LHS::Record::CHILDREN.each do |child|
29
+ child.endpoints = [] if !child.name['LHS'] && defined?(child.endpoints)
30
+ child.configuration({}) if !child.name['LHS']
31
+ end
32
+ end
33
+
34
+ RSpec.configure do |config|
35
+ config.before do |spec|
36
+ reset_lhc unless spec.metadata.key?(:reset_before) && spec.metadata[:reset_before] == false
37
+ reset_lhs unless spec.metadata.key?(:reset_before) && spec.metadata[:reset_before] == false
38
+ next unless spec.metadata.key?(:dummy_models) && spec.metadata[:dummy_models] == true
39
+ Dir.glob(Rails.root.join('app', 'models', '**', '*.rb')).each do |file|
40
+ load file if File.read(file).match('LHS::Record')
41
+ end
42
+ end
43
+ 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: 15.5.1
4
+ version: 15.6.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: 2018-08-06 00:00:00.000000000 Z
11
+ date: 2018-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activemodel
@@ -178,10 +178,10 @@ dependencies:
178
178
  - - ">="
179
179
  - !ruby/object:Gem::Version
180
180
  version: '0'
181
- description: Rails gem providing an easy, active-record-like interface for http json
182
- services
181
+ description: 'REST services accelerator: Rails gem providing an easy, active-record-like
182
+ interface for http (hypermedia) json services'
183
183
  email:
184
- - ws-operations@local.ch
184
+ - web@localsearch.ch
185
185
  executables: []
186
186
  extensions: []
187
187
  extra_rdoc_files: []
@@ -302,12 +302,16 @@ files:
302
302
  - spec/dummy/app/assets/stylesheets/application.css
303
303
  - spec/dummy/app/controllers/application_controller.rb
304
304
  - spec/dummy/app/controllers/concerns/.keep
305
+ - spec/dummy/app/controllers/error_handling_with_chains_controller.rb
305
306
  - spec/dummy/app/controllers/request_cycle_cache_controller.rb
306
307
  - spec/dummy/app/helpers/application_helper.rb
307
308
  - spec/dummy/app/mailers/.keep
308
309
  - spec/dummy/app/models/.keep
309
310
  - spec/dummy/app/models/concerns/.keep
311
+ - spec/dummy/app/models/record.rb
310
312
  - spec/dummy/app/models/user.rb
313
+ - spec/dummy/app/views/error_handling_with_chains/error.html.erb
314
+ - spec/dummy/app/views/error_handling_with_chains/show.html.erb
311
315
  - spec/dummy/app/views/form_for.html.erb
312
316
  - spec/dummy/app/views/layouts/application.html.erb
313
317
  - spec/dummy/bin/bundle
@@ -371,7 +375,6 @@ files:
371
375
  - spec/record/all_spec.rb
372
376
  - spec/record/build_spec.rb
373
377
  - spec/record/cast_nested_data_spec.rb
374
- - spec/record/chain_error_handling_spec.rb
375
378
  - spec/record/create_spec.rb
376
379
  - spec/record/creation_failed_spec.rb
377
380
  - spec/record/definitions_spec.rb
@@ -382,6 +385,8 @@ files:
382
385
  - spec/record/endpoint_options_spec.rb
383
386
  - spec/record/endpoints_spec.rb
384
387
  - spec/record/equality_spec.rb
388
+ - spec/record/error_handling_integration_spec.rb
389
+ - spec/record/error_handling_spec.rb
385
390
  - spec/record/expanded_spec.rb
386
391
  - spec/record/fetch_spec.rb
387
392
  - spec/record/find_by_chains_spec.rb
@@ -426,15 +431,15 @@ files:
426
431
  - spec/record/where_chains_spec.rb
427
432
  - spec/record/where_spec.rb
428
433
  - spec/record/where_values_hash_spec.rb
429
- - spec/request_cycle_cache/main_spec.rb
434
+ - spec/request_cycle_cache_spec.rb
430
435
  - spec/require_lhs_spec.rb
431
436
  - spec/spec_helper.rb
432
- - spec/support/cleanup.rb
433
437
  - spec/support/fixtures/json/feedback.json
434
438
  - spec/support/fixtures/json/feedbacks.json
435
439
  - spec/support/fixtures/json/localina_content_ad.json
436
440
  - spec/support/load_json.rb
437
441
  - spec/support/request_cycle_cache.rb
442
+ - spec/support/reset.rb
438
443
  - spec/views/form_for_spec.rb
439
444
  homepage: https://github.com/local-ch/lhs
440
445
  licenses:
@@ -457,10 +462,11 @@ required_rubygems_version: !ruby/object:Gem::Requirement
457
462
  requirements:
458
463
  - Ruby >= 2.3.0
459
464
  rubyforge_project:
460
- rubygems_version: 2.7.7
465
+ rubygems_version: 2.7.6
461
466
  signing_key:
462
467
  specification_version: 4
463
- summary: Rails gem providing an easy, active-record-like interface for http json services
468
+ summary: 'REST services accelerator: Rails gem providing an easy, active-record-like
469
+ interface for http (hypermedia) json services'
464
470
  test_files:
465
471
  - spec/autoloading_spec.rb
466
472
  - spec/collection/accessors_spec.rb
@@ -495,12 +501,16 @@ test_files:
495
501
  - spec/dummy/app/assets/stylesheets/application.css
496
502
  - spec/dummy/app/controllers/application_controller.rb
497
503
  - spec/dummy/app/controllers/concerns/.keep
504
+ - spec/dummy/app/controllers/error_handling_with_chains_controller.rb
498
505
  - spec/dummy/app/controllers/request_cycle_cache_controller.rb
499
506
  - spec/dummy/app/helpers/application_helper.rb
500
507
  - spec/dummy/app/mailers/.keep
501
508
  - spec/dummy/app/models/.keep
502
509
  - spec/dummy/app/models/concerns/.keep
510
+ - spec/dummy/app/models/record.rb
503
511
  - spec/dummy/app/models/user.rb
512
+ - spec/dummy/app/views/error_handling_with_chains/error.html.erb
513
+ - spec/dummy/app/views/error_handling_with_chains/show.html.erb
504
514
  - spec/dummy/app/views/form_for.html.erb
505
515
  - spec/dummy/app/views/layouts/application.html.erb
506
516
  - spec/dummy/bin/bundle
@@ -564,7 +574,6 @@ test_files:
564
574
  - spec/record/all_spec.rb
565
575
  - spec/record/build_spec.rb
566
576
  - spec/record/cast_nested_data_spec.rb
567
- - spec/record/chain_error_handling_spec.rb
568
577
  - spec/record/create_spec.rb
569
578
  - spec/record/creation_failed_spec.rb
570
579
  - spec/record/definitions_spec.rb
@@ -575,6 +584,8 @@ test_files:
575
584
  - spec/record/endpoint_options_spec.rb
576
585
  - spec/record/endpoints_spec.rb
577
586
  - spec/record/equality_spec.rb
587
+ - spec/record/error_handling_integration_spec.rb
588
+ - spec/record/error_handling_spec.rb
578
589
  - spec/record/expanded_spec.rb
579
590
  - spec/record/fetch_spec.rb
580
591
  - spec/record/find_by_chains_spec.rb
@@ -619,13 +630,13 @@ test_files:
619
630
  - spec/record/where_chains_spec.rb
620
631
  - spec/record/where_spec.rb
621
632
  - spec/record/where_values_hash_spec.rb
622
- - spec/request_cycle_cache/main_spec.rb
633
+ - spec/request_cycle_cache_spec.rb
623
634
  - spec/require_lhs_spec.rb
624
635
  - spec/spec_helper.rb
625
- - spec/support/cleanup.rb
626
636
  - spec/support/fixtures/json/feedback.json
627
637
  - spec/support/fixtures/json/feedbacks.json
628
638
  - spec/support/fixtures/json/localina_content_ad.json
629
639
  - spec/support/load_json.rb
630
640
  - spec/support/request_cycle_cache.rb
641
+ - spec/support/reset.rb
631
642
  - spec/views/form_for_spec.rb