lhs 3.4.2 → 4.0.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: 3487e37bda58a0a1b774e11778cb239f16489c95
4
- data.tar.gz: 702c8edef48c3971724878e0eff8416b36dac3db
3
+ metadata.gz: a00b04e289a4748446105fc8701ddabc87f58d77
4
+ data.tar.gz: 44f10bf8b376e4d849506074f39fd4373e7dc13b
5
5
  SHA512:
6
- metadata.gz: 035800d35582128787be14d1dcc58cd43e1fe7fece5165f3ec84c63dc41bc0f295ac5423f282f3f747e98479ef88e7c7e3bf59e09a9e9408f1090078a688e953
7
- data.tar.gz: 815442c0a04adff1b361a9f5015cb02f8d7e749ef879cf4e4245bfd1d4fe479bf6048b59a83122fd93d4faf87799a093d9d18310353a2de5eda161aeedb90ce9
6
+ metadata.gz: 15b4da5598314dc9ad1029e1c2b9562ef0610bb4f2f240e6c1be7d813a49e2e94dbebed77fd4f0a8c699998970069274a4b4a0939bc49d52769b416315259873
7
+ data.tar.gz: 83a9e1c944011beaea79e4a0b3c6cebaef12305234a91ebc0ed94741567a5dd919f9953af7d7d8e485bc944326d914f462ff91fa75507c4873ee42a2b752fa00
data/README.md CHANGED
@@ -111,9 +111,11 @@ If no record is found, `nil` is returned.
111
111
  ```ruby
112
112
  data = Feedback.all
113
113
  data.count # 998
114
- data.total # 998
114
+ data.length # 998
115
115
  ```
116
116
 
117
+ [Count vs. Length](#count-vs-length)
118
+
117
119
  `find_each` is a more fine grained way to process single records that are fetched in batches.
118
120
 
119
121
  ```ruby
@@ -355,46 +357,90 @@ unless user.valid?
355
357
  end
356
358
  ```
357
359
 
358
- ## Collections: Offset / Limit / Pagination
360
+ ## How to work with paginated APIs
361
+
362
+ LHS supports paginated APIs and it also supports various pagination strategies and by providing configuration possibilities.
359
363
 
360
- You can paginate by passing offset, and limit params. They will be forwarded to the service.
364
+ LHS diffentiates between the *pagination strategy* (how items/pages are navigated) itself and *pagination keys* (how stuff is named).
361
365
 
366
+ *Example 1 "offset"-strategy (default configuration)*
362
367
  ```ruby
363
- data = Feedback.where(limit: 50)
364
- data.count // 50
365
- Feedback.where(limit: 50, offset: 51)
368
+ # API response
369
+ {
370
+ items: [{...}, ...]
371
+ total: 300,
372
+ limit: 100,
373
+ offset: 0
374
+ }
375
+ # Next 'pages' are navigated with offset: 100, offset: 200, ...
376
+
377
+ # Nothing has to be configured in LHS because this is default pagination naming and strategy
378
+ class Results < LHS::Record
379
+ endpoint 'results'
380
+ end
366
381
  ```
367
382
 
368
- `total` provides total amount of items (even if paginated).
369
- `limit` provides amount of items per page.
370
- `offset` provides how many items where skipped to start the current page.
371
-
372
- ### Configure the name of the keys for offset, limit, total and name of items
373
-
374
- Endpoints provide different interfaces to deal with paginated resources.
375
- They differ for example for the key that is used for providing the current page items, the total amount of items, the current page size etc.
376
- In order to have `LHS::Record` deal with those different interfaces you can configure it:
383
+ *Example 2 "page"-strategy and some naming configuration*
384
+ ```ruby
385
+ # API response
386
+ {
387
+ docs: [{...}, ...]
388
+ totalPages: 3,
389
+ limit: 100,
390
+ page: 1
391
+ }
392
+ # Next 'pages' are navigated with page: 1, offset: 2, ...
393
+
394
+ # How LHS has to be configured
395
+ class Results < LHS::Record
396
+ configuration items_key: 'docs', total_key: 'totalPages', pagination_key: 'page', pagination_strategy: 'page'
397
+ endpoint 'results'
398
+ end
399
+ ```
377
400
 
401
+ *Example 3 "start"-strategy and naming configuration*
378
402
  ```ruby
379
- class Search < LHS::Record
380
- configuration items: :docs, limit: :size, offset: :start, total: :totalResults
381
- endpoint ':search/:type'
403
+ # API response
404
+ {
405
+ results: [{...}, ...]
406
+ total: 300,
407
+ badgeSize: 100,
408
+ startAt: 1
409
+ }
410
+ # Next 'pages' are navigated with startWith: 101, startWith: 201, ...
411
+
412
+ # How LHS has to be configured
413
+ class Results < LHS::Record
414
+ configuration items_key: 'results', limit_key: 'badgeSize', pagination_key: 'startAt', pagination_strategy: 'start'
415
+ endpoint 'results'
382
416
  end
383
417
  ```
384
- `items` key used to determine items of the current page.
385
- `limit` key used to work with page limits.
386
- `offset` key used to paginate multiple pages.
387
- `total` key used to determine the total amount of items.
418
+
419
+ `items_key` key used to determine items of the current page (e.g. `docs`, `items`, etc.).
420
+
421
+ `limit_key` key used to work with page limits (e.g. `size`, `limit`, etc.)
422
+
423
+ `pagination_key` key used to paginate multiple pages (e.g. `offset`, `page`, `startAt` etc.).
424
+
425
+ `pagination_strategy` used to configure the strategy used for navigating (e.g. `offset`, `page`, `start`, etc.).
426
+
427
+ `total_key` key used to determine the total amount of items (e.g. `total`, `totalResults`, etc.).
428
+
429
+ In case of paginated resources it's important to know the difference between [count vs. length](#count-vs-length)
388
430
 
389
431
  ### Partial Kaminari support
390
432
 
391
433
  LHS implements an interface that makes it partially working with Kaminari.
392
434
 
393
- For example, you can use kaminari to render paginations based on LHS Records:
435
+ The kaminari’s page parameter is in params[:page]. For example, you can use kaminari to render paginations based on LHS Records. Typically, your code will look like this:
394
436
 
395
437
  ```ruby
396
438
  # controller
397
- @items = Record.where(offset: offset, limit: limit)
439
+ params[:page] = 0 if params[:page].nil?
440
+ page = params[:page].to_i
441
+ limit = 100
442
+ offset = (page - 1) * limit
443
+ @items = Record.where({ limit: limit, offset: offset }))
398
444
  ```
399
445
 
400
446
  ```ruby
@@ -402,7 +448,7 @@ For example, you can use kaminari to render paginations based on LHS Records:
402
448
  = paginate @items
403
449
  ```
404
450
 
405
- ### form_for Helper
451
+ ## form_for Helper
406
452
  Rails `form_for` view-helper can be used in combination with instances of LHS::Record to autogenerate forms:
407
453
  ```
408
454
  <%= form_for(@instance, url: '/create') do |f| %>
@@ -411,3 +457,11 @@ Rails `form_for` view-helper can be used in combination with instances of LHS::R
411
457
  <%= f.submit "Create" %>
412
458
  <% end %>
413
459
  ```
460
+
461
+ ## Count vs. Length
462
+
463
+ The behaviour of `count` and `length` is based on ActiveRecord's behaviour.
464
+
465
+ `count` Determine the number of elements by taking the number of total elements that is provided by the endpoint/api.
466
+
467
+ `length` This returns the number of elements loaded from an endpoint/api. In case of paginated resources this can be different to count, as it depends on how many pages have been loaded.
@@ -6,19 +6,12 @@ Dir[File.dirname(__FILE__) + '/concerns/collection/*.rb'].each { |file| require
6
6
  class LHS::Collection < LHS::Proxy
7
7
  include InternalCollection
8
8
 
9
- delegate :select, to: :_collection
10
- delegate :_record, to: :_data
9
+ delegate :select, :length, :size, to: :_collection
10
+ delegate :_record, :_raw, to: :_data
11
+ delegate :limit, :count, :total, :limit, :offset, :current_page, :start, to: :_pagination
11
12
 
12
- def total
13
- _data._raw[_record.total_key]
14
- end
15
-
16
- def limit
17
- _data._raw[_record.limit_key]
18
- end
19
-
20
- def offset
21
- _data._raw[_record.offset_key]
13
+ def _pagination
14
+ _record.pagination(_data)
22
15
  end
23
16
 
24
17
  def href
@@ -31,8 +24,6 @@ class LHS::Collection < LHS::Proxy
31
24
  Collection.new(raw, _data, _record)
32
25
  end
33
26
 
34
- delegate :_raw, to: :_data
35
-
36
27
  protected
37
28
 
38
29
  def method_missing(name, *args, &block)
@@ -12,7 +12,7 @@ class LHS::Collection < LHS::Proxy
12
12
  include Enumerable
13
13
 
14
14
  attr_accessor :raw
15
- delegate :last, :sample, :[], :present?, :blank?, :empty?, to: :raw
15
+ delegate :length, :size, :last, :sample, :[], :present?, :blank?, :empty?, to: :raw
16
16
 
17
17
  def initialize(raw, parent, record)
18
18
  self.raw = raw
@@ -31,18 +31,23 @@ class LHS::Record
31
31
  end
32
32
 
33
33
  def request_all_the_rest(data, params)
34
- total_left = data._raw[total_key] - data.count
35
- limit = data._raw[limit_key] || data.count
36
- if limit > 0
37
- requests = total_left / limit
38
- requests.times do |i|
39
- offset = limit * (i + 1) + 1
40
- data._raw[items_key].concat all_items_from request(
34
+ pagination = data._record.pagination(data)
35
+ if pagination.pages_left
36
+ last_data = data
37
+ pagination.pages_left.times do |_index|
38
+ return data if last_data.length.zero?
39
+ pagination = data._record.pagination(last_data)
40
+ response_data = request(
41
41
  params: params.merge(
42
- data._record.limit_key => limit,
43
- data._record.offset_key => offset
42
+ data._record.limit_key => pagination.limit,
43
+ data._record.pagination_key => pagination.next_offset
44
44
  )
45
45
  )
46
+ data._raw[items_key].concat all_items_from response_data
47
+ data._raw[limit_key] = response_data._raw[limit_key]
48
+ data._raw[total_key] = response_data._raw[total_key]
49
+ data._raw[pagination_key] = response_data._raw[pagination_key]
50
+ last_data = response_data
46
51
  end
47
52
  end
48
53
  end
@@ -11,23 +11,29 @@ class LHS::Record
11
11
 
12
12
  module ClassMethods
13
13
  def configuration(args)
14
- @configuration ||= args.freeze || {}
14
+ @configuration = args.freeze || {}
15
15
  end
16
16
 
17
17
  def items_key
18
- @configuration.try(:[], :items) || :items
18
+ @configuration.try(:[], :items_key) || :items
19
19
  end
20
20
 
21
21
  def limit_key
22
- @configuration.try(:[], :limit) || :limit
22
+ @configuration.try(:[], :limit_key) || :limit
23
23
  end
24
24
 
25
25
  def total_key
26
- @configuration.try(:[], :total) || :total
26
+ @configuration.try(:[], :total_key) || :total
27
27
  end
28
28
 
29
- def offset_key
30
- @configuration.try(:[], :offset) || :offset
29
+ # Key used for determine current page
30
+ def pagination_key
31
+ @configuration.try(:[], :pagination_key) || :offset
32
+ end
33
+
34
+ # Strategy used for calculationg next pages and navigate pages
35
+ def pagination_strategy
36
+ @configuration.try(:[], :pagination_strategy) || :offset
31
37
  end
32
38
  end
33
39
  end
@@ -23,7 +23,7 @@ class LHS::Record
23
23
  def find_with_parameters(params)
24
24
  data = request(params: params)
25
25
  if data._proxy.is_a?(LHS::Collection)
26
- fail LHC::NotFound.new('Requested unique item. Multiple were found.', data._request.response) if data.count > 1
26
+ fail LHC::NotFound.new('Requested unique item. Multiple were found.', data._request.response) if data.length > 1
27
27
  data.first || fail(LHC::NotFound.new('No item was found.', data._request.response))
28
28
  else
29
29
  data
@@ -4,33 +4,24 @@ class LHS::Record
4
4
 
5
5
  module Pagination
6
6
  extend ActiveSupport::Concern
7
+ # Kaminari-Interface
8
+ delegate :current_page, :first_page, :last_page, :prev_page, :next_page, :limit_value, :total_pages, to: :_pagination
7
9
 
8
- def current_page
9
- offset + 1
10
+ def _pagination
11
+ self.class.pagination(_data)
10
12
  end
11
13
 
12
- def first_page
13
- 1
14
- end
15
-
16
- def last_page
17
- total_pages
18
- end
19
-
20
- def prev_page
21
- current_page - 1
22
- end
23
-
24
- def next_page
25
- current_page + 1
26
- end
27
-
28
- def limit_value
29
- limit
30
- end
31
-
32
- def total_pages
33
- total / limit
14
+ module ClassMethods
15
+ def pagination(data)
16
+ case data._record.pagination_strategy.to_sym
17
+ when :page
18
+ PagePagination.new(data)
19
+ when :start
20
+ StartPagination.new(data)
21
+ else
22
+ OffsetPagination.new(data)
23
+ end
24
+ end
34
25
  end
35
26
  end
36
27
  end
@@ -92,7 +92,7 @@ class LHS::Record
92
92
  end
93
93
 
94
94
  def multiple_requests(options)
95
- options = options.map { |option| process_options(option) }
95
+ options = options.map { |option| process_options(option, find_endpoint(option[:params])) }
96
96
  responses = LHC.request(options)
97
97
  data = responses.map { |response| LHS::Data.new(response.body, nil, self, response.request) }
98
98
  data = LHS::Data.new(data, nil, self)
@@ -107,11 +107,8 @@ class LHS::Record
107
107
  end
108
108
 
109
109
  # Merge explicit params and take configured endpoints options as base
110
- def process_options(options)
111
- options ||= {}
112
- options = options.dup
110
+ def process_options(options, endpoint)
113
111
  options[:params].deep_symbolize_keys! if options[:params]
114
- endpoint = find_endpoint(options[:params])
115
112
  options = (endpoint.options || {}).merge(options)
116
113
  options[:url] = compute_url!(options[:params]) unless options.key?(:url)
117
114
  merge_explicit_params!(options[:params])
@@ -135,8 +132,11 @@ class LHS::Record
135
132
  end
136
133
 
137
134
  def single_request(options)
138
- response = LHC.request(process_options(options))
139
- data = LHS::Data.new(response.body, nil, self, response.request)
135
+ options ||= {}
136
+ options = options.dup
137
+ endpoint = find_endpoint(options[:params])
138
+ response = LHC.request(process_options(options, endpoint))
139
+ data = LHS::Data.new(response.body, nil, self, response.request, endpoint)
140
140
  handle_includes(including, data) if including
141
141
  data
142
142
  end
@@ -5,17 +5,18 @@ Dir[File.dirname(__FILE__) + '/concerns/data/*.rb'].each { |file| require file }
5
5
  class LHS::Data
6
6
  include Json
7
7
 
8
- delegate :instance_methods, :items_key, :limit_key, :total_key, :offset_key, to: :class
8
+ delegate :instance_methods, :items_key, :limit_key, :total_key, :pagination_key, to: :class
9
9
 
10
10
  # prevent clashing with attributes of underlying data
11
- attr_accessor :_proxy, :_raw, :_parent, :_record, :_request
11
+ attr_accessor :_proxy, :_raw, :_parent, :_record, :_request, :_endpoint
12
12
 
13
- def initialize(input, parent = nil, record = nil, request = nil)
13
+ def initialize(input, parent = nil, record = nil, request = nil, endpoint = nil)
14
14
  self._raw = raw_from_input(input)
15
15
  self._parent = parent
16
16
  self._record = record
17
17
  self._proxy = proxy_from_input(input)
18
18
  self._request = request
19
+ self._endpoint = endpoint
19
20
  end
20
21
 
21
22
  # merging data
@@ -0,0 +1,101 @@
1
+ # Pagination is used to navigate paginateable collections
2
+ class Pagination
3
+
4
+ delegate :_record, to: :data
5
+ attr_accessor :data
6
+
7
+ def initialize(data)
8
+ self.data = data
9
+ end
10
+
11
+ # as standard in Rails' ActiveRecord count is not summing up, but using the number provided from data source
12
+ def count
13
+ total
14
+ end
15
+
16
+ def total
17
+ data._raw[_record.total_key.to_sym]
18
+ end
19
+
20
+ def limit
21
+ data._raw[_record.limit_key.to_sym] || LHS::Record::DEFAULT_LIMIT
22
+ end
23
+
24
+ def offset
25
+ data._raw[_record.pagination_key.to_sym]
26
+ end
27
+ alias current_page offset
28
+ alias start offset
29
+
30
+ def pages_left
31
+ total_pages - current_page
32
+ end
33
+
34
+ def next_offset
35
+ fail 'to be implemented in subclass'
36
+ end
37
+
38
+ def current_page
39
+ fail 'to be implemented in subclass'
40
+ end
41
+
42
+ def first_page
43
+ 1
44
+ end
45
+
46
+ def last_page
47
+ total_pages
48
+ end
49
+
50
+ def prev_page
51
+ current_page - 1
52
+ end
53
+
54
+ def next_page
55
+ current_page + 1
56
+ end
57
+
58
+ def limit_value
59
+ limit
60
+ end
61
+
62
+ def total_pages
63
+ (total.to_f / limit).ceil
64
+ end
65
+ end
66
+
67
+ class PagePagination < Pagination
68
+
69
+ def current_page
70
+ offset
71
+ end
72
+
73
+ def next_offset
74
+ current_page + 1
75
+ end
76
+
77
+ end
78
+
79
+ class StartPagination < Pagination
80
+
81
+ def current_page
82
+ (offset + limit - 1) / limit
83
+ end
84
+
85
+ def next_offset
86
+ offset + limit
87
+ end
88
+
89
+ end
90
+
91
+ class OffsetPagination < Pagination
92
+
93
+ def current_page
94
+ (offset + limit) / limit
95
+ end
96
+
97
+ def next_offset
98
+ offset + limit
99
+ end
100
+
101
+ end
@@ -17,6 +17,7 @@ class LHS::Record
17
17
  include Pagination
18
18
 
19
19
  delegate :_proxy, to: :_data
20
+ delegate :_endpoint, to: :_data
20
21
 
21
22
  def initialize(data = nil)
22
23
  data = LHS::Data.new({}, nil, self.class) unless data
@@ -1,3 +1,3 @@
1
1
  module LHS
2
- VERSION = "3.4.2"
2
+ VERSION = "4.0.0"
3
3
  end
@@ -4,32 +4,29 @@ describe LHS::Collection do
4
4
  let(:search) { 'http://local.ch/search' }
5
5
  let(:limit) { 10 }
6
6
  let(:total) { 20 }
7
- let(:offset) { 0 }
8
- let(:first_response_data) do
9
- {
10
- docs: (1..10).to_a,
11
- start: offset,
12
- size: limit,
13
- totalResults: total
14
- }
15
- end
16
- let(:second_response_data) do
17
- {
18
- docs: (11..20).to_a,
19
- start: offset,
20
- size: limit,
21
- totalResults: total
22
- }
23
- end
24
7
 
25
8
  before(:each) do
26
9
  LHC.config.placeholder('search', search)
27
10
  class Search < LHS::Record
28
- configuration items: :docs, limit: :size, offset: :start, total: :totalResults
11
+ configuration items_key: :docs, limit_key: :size, pagination_key: :start, pagination_strategy: :start, total_key: :totalResults
29
12
  endpoint ':search/:type'
30
13
  end
31
- stub_request(:get, "http://local.ch/search/phonebook?size=10").to_return(body: first_response_data.to_json)
32
- stub_request(:get, "http://local.ch/search/phonebook?size=10&start=11").to_return(body: second_response_data.to_json)
14
+ stub_request(:get, "http://local.ch/search/phonebook?size=10").to_return(
15
+ body: {
16
+ docs: (1..10).to_a,
17
+ start: 1,
18
+ size: limit,
19
+ totalResults: total
20
+ }.to_json
21
+ )
22
+ stub_request(:get, "http://local.ch/search/phonebook?size=10&start=11").to_return(
23
+ body: {
24
+ docs: (11..20).to_a,
25
+ start: 11,
26
+ size: limit,
27
+ totalResults: total
28
+ }.to_json
29
+ )
33
30
  end
34
31
 
35
32
  context 'lets you configure how to deal with collections' do
@@ -38,7 +35,7 @@ describe LHS::Collection do
38
35
  expect(results.count).to eq total
39
36
  expect(results.total).to eq total
40
37
  expect(results.limit).to eq limit
41
- expect(results.offset).to eq offset
38
+ expect(results.offset).to eq 11
42
39
  end
43
40
  end
44
41
  end
@@ -32,13 +32,13 @@ describe LHS::Collection do
32
32
  stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=201").to_return(status: 200, body: api_response((201..300).to_a, 201))
33
33
  stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=301").to_return(status: 200, body: api_response((301..400).to_a, 301))
34
34
  stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=401").to_return(status: 200, body: api_response((401..total).to_a, 401))
35
- count = 0
35
+ length = 0
36
36
  Record.find_in_batches do |records|
37
- count += records.count
37
+ length += records.length
38
38
  expect(records).to be_kind_of Record
39
39
  expect(records._proxy).to be_kind_of LHS::Collection
40
40
  end
41
- expect(count).to eq total
41
+ expect(length).to eq total
42
42
  end
43
43
 
44
44
  it 'adapts to backend max limit' do
@@ -47,19 +47,19 @@ describe LHS::Collection do
47
47
  stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=201").to_return(status: 200, body: api_response((201..300).to_a, 201))
48
48
  stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=301").to_return(status: 200, body: api_response((301..400).to_a, 301))
49
49
  stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=401").to_return(status: 200, body: api_response((401..total).to_a, 401))
50
- count = 0
50
+ length = 0
51
51
  Record.find_in_batches(batch_size: 230) do |records|
52
- count += records.count
52
+ length += records.length
53
53
  expect(records).to be_kind_of Record
54
54
  expect(records._proxy).to be_kind_of LHS::Collection
55
55
  end
56
- expect(count).to eq total
56
+ expect(length).to eq total
57
57
  end
58
58
 
59
59
  it 'forwards offset' do
60
60
  stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=401").to_return(status: 200, body: api_response((401..total).to_a, 401))
61
61
  Record.find_in_batches(start: 401) do |records|
62
- expect(records.count).to eq(total - 400)
62
+ expect(records.length).to eq(total - 400)
63
63
  end
64
64
  end
65
65
  end
@@ -0,0 +1,278 @@
1
+
2
+ require 'rails_helper'
3
+
4
+ describe LHS::Record do
5
+ before(:each) { LHC.config.placeholder('datastore', datastore) }
6
+ let(:datastore) { 'http://local.ch/v2' }
7
+
8
+ context 'default pagination behaviour' do
9
+ before(:each) do
10
+ class Record < LHS::Record
11
+ endpoint ':datastore/feedbacks'
12
+ end
13
+ end
14
+
15
+ it 'also works when there is no item in the first response' do
16
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
17
+ .to_return(
18
+ status: 200,
19
+ body: { items: [], total: 300, offset: 0 }.to_json
20
+ )
21
+ all = Record.all
22
+ expect(all).to be_kind_of Record
23
+ expect(all._proxy).to be_kind_of LHS::Collection
24
+ expect(all.length).to eq 0
25
+ end
26
+
27
+ it 'also works when there is no total in the stubbing' do
28
+ stub_request(:get, %r{/feedbacks}).to_return(body: { items: (1..100).to_a }.to_json)
29
+ all = Record.all
30
+ expect(all).to be_kind_of Record
31
+ expect(all._proxy).to be_kind_of LHS::Collection
32
+ expect(all.length).to eq 100
33
+ end
34
+
35
+ it 'also works when there is no key "items" in the stubbing' do
36
+ stub_request(:get, %r{/feedbacks}).to_return(body: (1..100).to_a.to_json)
37
+ all = Record.all
38
+ expect(all).to be_kind_of Record
39
+ expect(all._proxy).to be_kind_of LHS::Collection
40
+ expect(all.length).to eq 100
41
+ end
42
+ end
43
+
44
+ context 'pagination using offset(0,100,200,...)' do
45
+ before(:each) do
46
+ class Record < LHS::Record
47
+ configuration pagination_strategy: 'offset', pagination_key: 'offset'
48
+ endpoint ':datastore/feedbacks'
49
+ end
50
+ end
51
+
52
+ it 'fetches all records from the backend' do
53
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
54
+ .to_return(
55
+ status: 200,
56
+ body: { items: (1..100).to_a, limit: 100, total: 300, offset: 0 }.to_json
57
+ )
58
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=100")
59
+ .to_return(
60
+ status: 200,
61
+ body: { items: (101..200).to_a, limit: 100, total: 300, offset: 100 }.to_json
62
+ )
63
+ last_request = stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=200")
64
+ .to_return(
65
+ status: 200,
66
+ body: { items: (201..300).to_a, limit: 100, total: 300, offset: 200 }.to_json
67
+ )
68
+ all = Record.all
69
+ assert_requested last_request
70
+ expect(all).to be_kind_of Record
71
+ expect(all._data._proxy).to be_kind_of LHS::Collection
72
+ expect(all.count).to eq 300
73
+ expect(all.last).to eq 300
74
+ end
75
+
76
+ it 'fetches all, also if there is a rest and the total is not divideable trough the limit' do
77
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
78
+ .to_return(
79
+ status: 200,
80
+ body: { items: (1..100).to_a, limit: 100, total: 223, offset: 0 }.to_json
81
+ )
82
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=100")
83
+ .to_return(
84
+ status: 200,
85
+ body: { items: (101..200).to_a, limit: 100, total: 223, offset: 100 }.to_json
86
+ )
87
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=200")
88
+ .to_return(
89
+ status: 200,
90
+ body: { items: (201..223).to_a, limit: 100, total: 223, offset: 200 }.to_json
91
+ )
92
+ all = Record.all
93
+ expect(all).to be_kind_of Record
94
+ expect(all._data._proxy).to be_kind_of LHS::Collection
95
+ expect(all.count).to eq 223
96
+ expect(all.last).to eq 223
97
+ end
98
+
99
+ it 'also fetches all when there is not meta information for limit' do
100
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
101
+ .to_return(
102
+ status: 200,
103
+ body: { items: (1..100).to_a, total: 300, offset: 0 }.to_json
104
+ )
105
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=100")
106
+ .to_return(
107
+ status: 200,
108
+ body: { items: (101..200).to_a, total: 300, offset: 100 }.to_json
109
+ )
110
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=200")
111
+ .to_return(
112
+ status: 200,
113
+ body: { items: (201..300).to_a, total: 300, offset: 200 }.to_json
114
+ )
115
+ all = Record.all
116
+ expect(all).to be_kind_of Record
117
+ expect(all._proxy).to be_kind_of LHS::Collection
118
+ expect(all.count).to eq 300
119
+ expect(all.last).to eq 300
120
+ end
121
+ end
122
+
123
+ context 'pagination using page(1,2,3,...)' do
124
+ before(:each) do
125
+ class Record < LHS::Record
126
+ configuration pagination_strategy: 'page', pagination_key: 'page'
127
+ endpoint ':datastore/feedbacks'
128
+ end
129
+ end
130
+
131
+ it 'fetches all records from the backend' do
132
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
133
+ .to_return(
134
+ status: 200,
135
+ body: { items: (1..100).to_a, limit: 100, total: 300, page: 1 }.to_json
136
+ )
137
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&page=2")
138
+ .to_return(
139
+ status: 200,
140
+ body: { items: (101..200).to_a, limit: 100, total: 300, page: 2 }.to_json
141
+ )
142
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&page=3")
143
+ .to_return(
144
+ status: 200,
145
+ body: { items: (201..300).to_a, limit: 100, total: 300, page: 3 }.to_json
146
+ )
147
+ all = Record.all
148
+ expect(all).to be_kind_of Record
149
+ expect(all._data._proxy).to be_kind_of LHS::Collection
150
+ expect(all.count).to eq 300
151
+ expect(all.last).to eq 300
152
+ end
153
+
154
+ it 'also fetches all when there is not meta information for limit' do
155
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
156
+ .to_return(
157
+ status: 200,
158
+ body: { items: (1..100).to_a, total: 300, page: 1 }.to_json
159
+ )
160
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&page=2")
161
+ .to_return(
162
+ status: 200,
163
+ body: { items: (101..200).to_a, total: 300, page: 2 }.to_json
164
+ )
165
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&page=3")
166
+ .to_return(
167
+ status: 200,
168
+ body: { items: (201..300).to_a, total: 300, page: 3 }.to_json
169
+ )
170
+ all = Record.all
171
+ expect(all).to be_kind_of Record
172
+ expect(all._proxy).to be_kind_of LHS::Collection
173
+ expect(all.count).to eq 300
174
+ expect(all.last).to eq 300
175
+ end
176
+
177
+ it 'fetches all, also if there is a rest and the total is not divideable trough the limit' do
178
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
179
+ .to_return(
180
+ status: 200,
181
+ body: { items: (1..100).to_a, limit: 100, total: 223, page: 1 }.to_json
182
+ )
183
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&page=2")
184
+ .to_return(
185
+ status: 200,
186
+ body: { items: (101..200).to_a, limit: 100, total: 223, page: 2 }.to_json
187
+ )
188
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&page=3")
189
+ .to_return(
190
+ status: 200,
191
+ body: { items: (201..223).to_a, limit: 100, total: 223, page: 3 }.to_json
192
+ )
193
+ all = Record.all
194
+ expect(all).to be_kind_of Record
195
+ expect(all._data._proxy).to be_kind_of LHS::Collection
196
+ expect(all.count).to eq 223
197
+ expect(all.last).to eq 223
198
+ end
199
+ end
200
+
201
+ context 'pagination using start(1,101,201,...)' do
202
+ before(:each) do
203
+ class Record < LHS::Record
204
+ configuration pagination_strategy: 'start', pagination_key: 'start'
205
+ endpoint ':datastore/feedbacks'
206
+ end
207
+ end
208
+
209
+ it 'fetches all records from the backend' do
210
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
211
+ .to_return(
212
+ status: 200,
213
+ body: { items: (1..100).to_a, limit: 100, total: 300, start: 1 }.to_json
214
+ )
215
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&start=101")
216
+ .to_return(
217
+ status: 200,
218
+ body: { items: (101..200).to_a, limit: 100, total: 300, start: 101 }.to_json
219
+ )
220
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&start=201")
221
+ .to_return(
222
+ status: 200,
223
+ body: { items: (201..300).to_a, limit: 100, total: 300, start: 201 }.to_json
224
+ )
225
+ all = Record.all
226
+ expect(all).to be_kind_of Record
227
+ expect(all._data._proxy).to be_kind_of LHS::Collection
228
+ expect(all.count).to eq 300
229
+ expect(all.last).to eq 300
230
+ end
231
+
232
+ it 'also fetches all when there is not meta information for limit' do
233
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
234
+ .to_return(
235
+ status: 200,
236
+ body: { items: (1..100).to_a, total: 300, start: 1 }.to_json
237
+ )
238
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&start=101")
239
+ .to_return(
240
+ status: 200,
241
+ body: { items: (101..200).to_a, total: 300, start: 101 }.to_json
242
+ )
243
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&start=201")
244
+ .to_return(
245
+ status: 200,
246
+ body: { items: (201..300).to_a, total: 300, start: 201 }.to_json
247
+ )
248
+ all = Record.all
249
+ expect(all).to be_kind_of Record
250
+ expect(all._proxy).to be_kind_of LHS::Collection
251
+ expect(all.count).to eq 300
252
+ expect(all.last).to eq 300
253
+ end
254
+
255
+ it 'fetches all, also if there is a rest and the total is not divideable trough the limit' do
256
+ stub_request(:get, "#{datastore}/feedbacks?limit=100")
257
+ .to_return(
258
+ status: 200,
259
+ body: { items: (1..100).to_a, limit: 100, total: 223, start: 1 }.to_json
260
+ )
261
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&start=101")
262
+ .to_return(
263
+ status: 200,
264
+ body: { items: (101..200).to_a, limit: 100, total: 223, start: 101 }.to_json
265
+ )
266
+ stub_request(:get, "#{datastore}/feedbacks?limit=100&start=201")
267
+ .to_return(
268
+ status: 200,
269
+ body: { items: (201..223).to_a, limit: 100, total: 223, start: 201 }.to_json
270
+ )
271
+ all = Record.all
272
+ expect(all).to be_kind_of Record
273
+ expect(all._data._proxy).to be_kind_of LHS::Collection
274
+ expect(all.count).to eq 223
275
+ expect(all.last).to eq 223
276
+ end
277
+ end
278
+ end
@@ -56,5 +56,14 @@ describe LHS::Record do
56
56
  it 'responds to next_page' do
57
57
  expect(record.next_page).to eq(next_page)
58
58
  end
59
+
60
+ context 'when amount of total pages is not diviable by the limit' do
61
+ let(:total) { 2738 }
62
+ let(:limit) { 100 }
63
+
64
+ it 'rounds up' do
65
+ expect(record.total_pages).to eq(28)
66
+ end
67
+ end
59
68
  end
60
69
  end
@@ -13,6 +13,7 @@ RSpec.configure do |config|
13
13
  config.before(:each) do
14
14
  LHS::Record::CHILDREN.each do |child|
15
15
  child.endpoints = [] if !child.name['LHS']
16
+ child.configuration({}) if !child.name['LHS']
16
17
  end
17
18
  end
18
19
  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: 3.4.2
4
+ version: 4.0.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-04-06 00:00:00.000000000 Z
11
+ date: 2016-04-07 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: lhc
@@ -202,6 +202,7 @@ files:
202
202
  - lib/lhs/endpoint.rb
203
203
  - lib/lhs/errors.rb
204
204
  - lib/lhs/item.rb
205
+ - lib/lhs/pagination.rb
205
206
  - lib/lhs/proxy.rb
206
207
  - lib/lhs/record.rb
207
208
  - lib/lhs/version.rb
@@ -274,7 +275,6 @@ files:
274
275
  - spec/item/validation_spec.rb
275
276
  - spec/proxy/load_spec.rb
276
277
  - spec/rails_helper.rb
277
- - spec/record/all_spec.rb
278
278
  - spec/record/build_spec.rb
279
279
  - spec/record/create_spec.rb
280
280
  - spec/record/creation_failed_spec.rb
@@ -291,6 +291,7 @@ files:
291
291
  - spec/record/mapping_spec.rb
292
292
  - spec/record/model_name_spec.rb
293
293
  - spec/record/new_spec.rb
294
+ - spec/record/paginatable_collection_spec.rb
294
295
  - spec/record/pagination_spec.rb
295
296
  - spec/record/persisted_spec.rb
296
297
  - spec/record/request_spec.rb
@@ -398,7 +399,6 @@ test_files:
398
399
  - spec/item/validation_spec.rb
399
400
  - spec/proxy/load_spec.rb
400
401
  - spec/rails_helper.rb
401
- - spec/record/all_spec.rb
402
402
  - spec/record/build_spec.rb
403
403
  - spec/record/create_spec.rb
404
404
  - spec/record/creation_failed_spec.rb
@@ -415,6 +415,7 @@ test_files:
415
415
  - spec/record/mapping_spec.rb
416
416
  - spec/record/model_name_spec.rb
417
417
  - spec/record/new_spec.rb
418
+ - spec/record/paginatable_collection_spec.rb
418
419
  - spec/record/pagination_spec.rb
419
420
  - spec/record/persisted_spec.rb
420
421
  - spec/record/request_spec.rb
@@ -1,68 +0,0 @@
1
- require 'rails_helper'
2
-
3
- describe LHS::Collection do
4
- let(:datastore) { 'http://local.ch/v2' }
5
-
6
- before(:each) do
7
- LHC.config.placeholder('datastore', datastore)
8
- class Record < LHS::Record
9
- endpoint ':datastore/:campaign_id/feedbacks'
10
- endpoint ':datastore/feedbacks'
11
- end
12
- end
13
-
14
- context 'all' do
15
- it 'fetches all records from the backend' do
16
- stub_request(:get, "#{datastore}/feedbacks?limit=100")
17
- .to_return(status: 200, body: { items: (1..100).to_a, total: 300, limit: 100, offset: 0 }.to_json)
18
- stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=101")
19
- .to_return(status: 200, body: { items: (101..200).to_a, total: 300, limit: 100, offset: 101 }.to_json)
20
- stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=201")
21
- .to_return(status: 200, body: { items: (201..300).to_a, total: 300, limit: 100, offset: 201 }.to_json)
22
- all = Record.all
23
- expect(all).to be_kind_of Record
24
- expect(all._data._proxy).to be_kind_of LHS::Collection
25
- expect(all.count).to eq 300
26
- expect(all.last).to eq 300
27
- end
28
-
29
- it 'also fetches all when there is not meta information for limit' do
30
- stub_request(:get, "#{datastore}/feedbacks?limit=100")
31
- .to_return(status: 200, body: { items: (1..100).to_a, total: 300, offset: 0 }.to_json)
32
- stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=101")
33
- .to_return(status: 200, body: { items: (101..200).to_a, total: 300, offset: 101 }.to_json)
34
- stub_request(:get, "#{datastore}/feedbacks?limit=100&offset=201")
35
- .to_return(status: 200, body: { items: (201..300).to_a, total: 300, offset: 201 }.to_json)
36
- all = Record.all
37
- expect(all).to be_kind_of Record
38
- expect(all._proxy).to be_kind_of LHS::Collection
39
- expect(all.count).to eq 300
40
- expect(all.last).to eq 300
41
- end
42
-
43
- it 'also works when there is no item in the first response' do
44
- stub_request(:get, "#{datastore}/feedbacks?limit=100")
45
- .to_return(status: 200, body: { items: [], total: 300, offset: 0 }.to_json)
46
- all = Record.all
47
- expect(all).to be_kind_of Record
48
- expect(all._proxy).to be_kind_of LHS::Collection
49
- expect(all.count).to eq 0
50
- end
51
-
52
- it 'alsow works when there is no total in the stubbing' do
53
- stub_request(:get, %r{/feedbacks}).to_return(body: { items: (1..100).to_a }.to_json)
54
- all = Record.all
55
- expect(all).to be_kind_of Record
56
- expect(all._proxy).to be_kind_of LHS::Collection
57
- expect(all.count).to eq 100
58
- end
59
-
60
- it 'alsow works when there is no key "items" in the stubbing' do
61
- stub_request(:get, %r{/feedbacks}).to_return(body: (1..100).to_a.to_json)
62
- all = Record.all
63
- expect(all).to be_kind_of Record
64
- expect(all._proxy).to be_kind_of LHS::Collection
65
- expect(all.count).to eq 100
66
- end
67
- end
68
- end