lhs 3.4.2 → 4.0.0

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
  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