lhs 5.3.0 → 5.4.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: b5e2d8c23fc37ece5d7b13aebd02f43df60aec96
4
- data.tar.gz: 650560241439a1cb5b97c86894ef4a5e133d5bd4
3
+ metadata.gz: 80529532ef8aaafcbe7822abac42c0082f2b2b32
4
+ data.tar.gz: f3b872fa678981d24ccfdcc24af642a397cffac4
5
5
  SHA512:
6
- metadata.gz: 7d086164639d3fd4b3a40a772c2c98360cf212f6b58e6d55a5debe7317daecb341e42aec62948783e79f3eb18f398a607a9cffc99a7828d73f9fc02c894cc36a
7
- data.tar.gz: 329fbdb7a8edc677a156588061af8e99e9a15db6bec9684f50e057157f6c4489fd1eb71d5c58c8378bb7e194682b2679e77b58c0ada1f3fc2b012bdfc70c2e9d
6
+ metadata.gz: bbfea24429cac66bfc5684d31411ed975456d52be8110df299dcd762b2771205e4827b9fcb183305e9444cd0f3e3165fc8b3cf055c5352504f485fbb45cd890a
7
+ data.tar.gz: 47678b919459a2a896995a01588d1c29876982324872d889dbef21297ff57dc76674561fae8d1f98062ab6dc6e93058c9e9cd608b1cb373c89db4a23b1c3a3c0
data/README.md CHANGED
@@ -449,7 +449,7 @@ unless user.valid?
449
449
  end
450
450
  ```
451
451
 
452
- ## How to work with paginated APIs
452
+ ## Pagination
453
453
 
454
454
  LHS supports paginated APIs and it also supports various pagination strategies and by providing configuration possibilities.
455
455
 
@@ -520,6 +520,40 @@ end
520
520
 
521
521
  In case of paginated resources it's important to know the difference between [count vs. length](#count-vs-length)
522
522
 
523
+ ### Pagination Chains
524
+
525
+ You can use chainable pagination in combination with query chains:
526
+
527
+ ```ruby
528
+ class Record < LHS::Record
529
+ endpoint 'http://local.ch/records'
530
+ end
531
+ Record.page(3).per(20).where(color: 'blue')
532
+ # http://local.ch/records?offset=40&limit=20&color=blue
533
+ ```
534
+
535
+ The applied pagination strategy depends on the actual configured pagination, so the interface is the same for all strategies:
536
+
537
+ ```ruby
538
+ class Record < LHS::Record
539
+ endpoint 'http://local.ch/records'
540
+ configuration pagination_strategy: 'page'
541
+ end
542
+ Record.page(3).per(20).where(color: 'blue')
543
+ # http://local.ch/records?page=3&limit=20&color=blue
544
+ ```
545
+
546
+ ```ruby
547
+ class Record < LHS::Record
548
+ endpoint 'http://local.ch/records'
549
+ configuration pagination_strategy: 'start'
550
+ end
551
+ Record.page(3).per(20).where(color: 'blue')
552
+ # http://local.ch/records?start=41&limit=20&color=blue
553
+ ```
554
+
555
+ `limit(argument)` is an alias for `per(argument)`. Take notice that `limit` without argument instead, makes the query resolve and provides the current limit from the responds.
556
+
523
557
  ### Partial Kaminari support
524
558
 
525
559
  LHS implements an interface that makes it partially working with Kaminari.
@@ -531,8 +565,7 @@ The kaminari’s page parameter is in params[:page]. For example, you can use ka
531
565
  params[:page] = 0 if params[:page].nil?
532
566
  page = params[:page].to_i
533
567
  limit = 100
534
- offset = (page - 1) * limit
535
- @items = Record.where({ limit: limit, offset: offset }))
568
+ @items = Record.page(page).per(limit)
536
569
  ```
537
570
 
538
571
  ```ruby
@@ -5,8 +5,6 @@ class LHS::Record
5
5
  module All
6
6
  extend ActiveSupport::Concern
7
7
 
8
- DEFAULT_LIMIT = 100
9
-
10
8
  module ClassMethods
11
9
  # Should be an edge case but sometimes all objects from a certain resource
12
10
  # are required. In this case we load the first page with the default max limit,
@@ -14,7 +12,7 @@ class LHS::Record
14
12
  # for the following pages and concatenate all the results in order to return
15
13
  # all the objects for a given resource.
16
14
  def all(params = {})
17
- limit = params[limit_key] || DEFAULT_LIMIT
15
+ limit = params[limit_key] || LHS::Pagination::DEFAULT_LIMIT
18
16
  data = request(params: params.merge(limit_key => limit))
19
17
  request_all_the_rest(data, params) if paginated?(data._raw)
20
18
  data._record.new(LHS::Data.new(data, nil, self))
@@ -20,7 +20,7 @@ class LHS::Record
20
20
  def find_in_batches(options = {})
21
21
  fail 'No block given' unless block_given?
22
22
  start = options[:start] || 1
23
- batch_size = options[:batch_size] || 100
23
+ batch_size = options[:batch_size] || LHS::Pagination::DEFAULT_LIMIT
24
24
  params = options[:params] || {}
25
25
  loop do # as suggested by Matz
26
26
  data = request(params: params.merge(limit: batch_size, offset: start))
@@ -18,6 +18,18 @@ class LHS::Record
18
18
  def options(hash = nil)
19
19
  Chain.new(self, Option.new(hash))
20
20
  end
21
+
22
+ def page(page)
23
+ Chain.new(self, Pagination.new(page: page))
24
+ end
25
+
26
+ def per(limit)
27
+ Chain.new(self, Pagination.new(per: limit))
28
+ end
29
+
30
+ def limit(argument = nil)
31
+ Chain.new(self, Pagination.new(per: argument))
32
+ end
21
33
  end
22
34
 
23
35
  # Link: A part of a chain
@@ -26,6 +38,10 @@ class LHS::Record
26
38
  @hash = hash
27
39
  end
28
40
 
41
+ def [](parameter)
42
+ @hash[parameter]
43
+ end
44
+
29
45
  def to_hash
30
46
  @hash
31
47
  end
@@ -39,6 +55,10 @@ class LHS::Record
39
55
  class Option < Link
40
56
  end
41
57
 
58
+ # Pagination: Part of the chain that will be used to controll pagination
59
+ class Pagination < Link
60
+ end
61
+
42
62
  # A sequence of links
43
63
  class Chain
44
64
 
@@ -99,6 +119,19 @@ class LHS::Record
99
119
  push Option.new(hash)
100
120
  end
101
121
 
122
+ def page(page)
123
+ push Pagination.new(page: page)
124
+ end
125
+
126
+ def per(per)
127
+ push Pagination.new(per: per)
128
+ end
129
+
130
+ def limit(argument = nil)
131
+ return resolve.limit if argument.blank?
132
+ push Pagination.new(per: argument)
133
+ end
134
+
102
135
  def find(args)
103
136
  @record_class.find(args, chain_options)
104
137
  end
@@ -117,6 +150,11 @@ class LHS::Record
117
150
  chain_options
118
151
  end
119
152
 
153
+ # Returns a hash of pagination values
154
+ def pagination_values_hash
155
+ chain_pagination
156
+ end
157
+
120
158
  protected
121
159
 
122
160
  def method_missing(name, *args, &block)
@@ -132,7 +170,10 @@ class LHS::Record
132
170
 
133
171
  def resolve
134
172
  @resolved ||= @record_class.new(
135
- @record_class.request(chain_options.merge(params: chain_parameters))
173
+ @record_class.request(
174
+ chain_options
175
+ .merge(params: chain_parameters.merge(chain_pagination))
176
+ )
136
177
  )
137
178
  end
138
179
 
@@ -151,6 +192,25 @@ class LHS::Record
151
192
  merge_links @chain.select { |link| link.is_a? Option }
152
193
  end
153
194
 
195
+ def chain_pagination
196
+ resolve_pagination @chain.select { |link| link.is_a? Pagination }
197
+ end
198
+
199
+ def resolve_pagination(links)
200
+ return {} if links.empty?
201
+ page = 1
202
+ per = LHS::Pagination::DEFAULT_LIMIT
203
+ links.each do |link|
204
+ page = link[:page] if link[:page].present?
205
+ per = link[:per] if link[:per].present?
206
+ end
207
+ pagination = @record_class.pagination_class
208
+ {
209
+ @record_class.pagination_key => pagination.page_to_offset(page, per),
210
+ @record_class.limit_key => per
211
+ }
212
+ end
213
+
154
214
  def merge_links(links)
155
215
  hash = {}
156
216
  links.each do |link|
@@ -12,16 +12,20 @@ class LHS::Record
12
12
  end
13
13
 
14
14
  module ClassMethods
15
- def pagination(data)
16
- case data._record.pagination_strategy.to_sym
15
+ def pagination_class
16
+ case pagination_strategy.to_sym
17
17
  when :page
18
- PagePagination.new(data)
18
+ LHS::PagePagination
19
19
  when :start
20
- StartPagination.new(data)
20
+ LHS::StartPagination
21
21
  else
22
- OffsetPagination.new(data)
22
+ LHS::OffsetPagination
23
23
  end
24
24
  end
25
+
26
+ def pagination(data)
27
+ pagination_class.new(data)
28
+ end
25
29
  end
26
30
  end
27
31
  end
@@ -1,5 +1,7 @@
1
1
  # Pagination is used to navigate paginateable collections
2
- class Pagination
2
+ class LHS::Pagination
3
+
4
+ DEFAULT_LIMIT = 100
3
5
 
4
6
  delegate :_record, to: :data
5
7
  attr_accessor :data
@@ -18,7 +20,7 @@ class Pagination
18
20
  end
19
21
 
20
22
  def limit
21
- data._raw[_record.limit_key.to_sym] || LHS::Record::DEFAULT_LIMIT
23
+ data._raw[_record.limit_key.to_sym] || LHS::Pagination::DEFAULT_LIMIT
22
24
  end
23
25
 
24
26
  def offset
@@ -62,9 +64,13 @@ class Pagination
62
64
  def total_pages
63
65
  (total.to_f / limit).ceil
64
66
  end
67
+
68
+ def self.page_to_offset(page, _limit)
69
+ page
70
+ end
65
71
  end
66
72
 
67
- class PagePagination < Pagination
73
+ class LHS::PagePagination < LHS::Pagination
68
74
 
69
75
  def current_page
70
76
  offset
@@ -73,10 +79,9 @@ class PagePagination < Pagination
73
79
  def next_offset
74
80
  current_page + 1
75
81
  end
76
-
77
82
  end
78
83
 
79
- class StartPagination < Pagination
84
+ class LHS::StartPagination < LHS::Pagination
80
85
 
81
86
  def current_page
82
87
  (offset + limit - 1) / limit
@@ -86,9 +91,12 @@ class StartPagination < Pagination
86
91
  offset + limit
87
92
  end
88
93
 
94
+ def self.page_to_offset(page, limit = LHS::Pagination::DEFAULT_LIMIT)
95
+ (page - 1) * limit + 1
96
+ end
89
97
  end
90
98
 
91
- class OffsetPagination < Pagination
99
+ class LHS::OffsetPagination < LHS::Pagination
92
100
 
93
101
  def current_page
94
102
  (offset + limit) / limit
@@ -98,4 +106,7 @@ class OffsetPagination < Pagination
98
106
  offset + limit
99
107
  end
100
108
 
109
+ def self.page_to_offset(page, limit = LHS::Pagination::DEFAULT_LIMIT)
110
+ (page - 1) * limit
111
+ end
101
112
  end
@@ -1,3 +1,3 @@
1
1
  module LHS
2
- VERSION = "5.3.0"
2
+ VERSION = "5.4.0"
3
3
  end
@@ -8,7 +8,7 @@ describe LHS::Record do
8
8
  LHS::Data.new(data_hash, nil, Record)
9
9
  end
10
10
 
11
- let(:pagination) { OffsetPagination.new(data) }
11
+ let(:pagination) { LHS::OffsetPagination.new(data) }
12
12
 
13
13
  before(:each) do
14
14
  class Record < LHS::Record
@@ -0,0 +1,92 @@
1
+ require 'rails_helper'
2
+
3
+ describe LHS::Record do
4
+ context 'pagination chain' do
5
+ context 'default pagination (offset)' do
6
+ before(:each) do
7
+ class Record < LHS::Record
8
+ endpoint 'http://local.ch/records'
9
+ endpoint 'http://local.ch/records/:id'
10
+ end
11
+ end
12
+
13
+ it 'allows to chain pagination methods' do
14
+ request = stub_request(:get, "http://local.ch/records?color=blue&offset=200&limit=100").to_return(body: [].to_json)
15
+ Record.where(color: 'blue').page(3).first
16
+ expect(request).to have_been_made.times(1)
17
+ request = stub_request(:get, "http://local.ch/records?color=blue&offset=20&limit=10").to_return(body: [].to_json)
18
+ Record.where(color: 'blue').page(3).per(10).first
19
+ Record.where(color: 'blue').per(10).page(3).first
20
+ Record.where(color: 'blue').per(20).page(5).per(10).page(3).first
21
+ expect(request).to have_been_made.times(3)
22
+ end
23
+
24
+ it 'allows to start chains with pagination methods' do
25
+ request = stub_request(:get, "http://local.ch/records?color=blue&offset=200&limit=100").to_return(body: [].to_json)
26
+ Record.page(3).where(color: 'blue').first
27
+ expect(request).to have_been_made.times(1)
28
+ request = stub_request(:get, "http://local.ch/records?color=blue&offset=20&limit=10").to_return(body: [].to_json)
29
+ Record.page(3).per(10).where(color: 'blue').first
30
+ Record.per(10).page(3).where(color: 'blue').first
31
+ Record.per(20).page(5).where(color: 'blue').per(10).page(3).first
32
+ expect(request).to have_been_made.times(3)
33
+ end
34
+
35
+ it 'defaults page to 1' do
36
+ request = stub_request(:get, "http://local.ch/records?limit=10&offset=0").to_return(body: [].to_json)
37
+ Record.per(10).first
38
+ Record.per(10).page("").first
39
+ expect(request).to have_been_made.times(2)
40
+ end
41
+
42
+ it 'provides limit as alias for per' do
43
+ request = stub_request(:get, "http://local.ch/records?limit=10&offset=0").to_return(body: [].to_json)
44
+ Record.limit(10).first
45
+ Record.page("").limit(10).first
46
+ expect(request).to have_been_made.times(2)
47
+ end
48
+ end
49
+
50
+ context 'start pagination' do
51
+ before(:each) do
52
+ class Record < LHS::Record
53
+ configuration pagination_strategy: 'start', pagination_key: 'start'
54
+ endpoint 'http://local.ch/records'
55
+ endpoint 'http://local.ch/records/:id'
56
+ end
57
+ end
58
+
59
+ it 'allows to chain pagination methods' do
60
+ request = stub_request(:get, "http://local.ch/records?color=blue&start=201&limit=100").to_return(body: [].to_json)
61
+ Record.where(color: 'blue').page(3).first
62
+ expect(request).to have_been_made.times(1)
63
+ request = stub_request(:get, "http://local.ch/records?color=blue&start=21&limit=10").to_return(body: [].to_json)
64
+ Record.where(color: 'blue').page(3).per(10).first
65
+ Record.where(color: 'blue').per(10).page(3).first
66
+ Record.where(color: 'blue').per(20).page(5).per(10).page(3).first
67
+ expect(request).to have_been_made.times(3)
68
+ end
69
+ end
70
+
71
+ context 'page pagination' do
72
+ before(:each) do
73
+ class Record < LHS::Record
74
+ configuration pagination_strategy: 'page', pagination_key: 'page'
75
+ endpoint 'http://local.ch/records'
76
+ endpoint 'http://local.ch/records/:id'
77
+ end
78
+ end
79
+
80
+ it 'allows to chain pagination methods' do
81
+ request = stub_request(:get, "http://local.ch/records?color=blue&page=3&limit=100").to_return(body: [].to_json)
82
+ Record.where(color: 'blue').page(3).first
83
+ expect(request).to have_been_made.times(1)
84
+ request = stub_request(:get, "http://local.ch/records?color=blue&page=3&limit=10").to_return(body: [].to_json)
85
+ Record.where(color: 'blue').page(3).per(10).first
86
+ Record.where(color: 'blue').per(10).page(3).first
87
+ Record.where(color: 'blue').per(20).page(5).per(10).page(3).first
88
+ expect(request).to have_been_made.times(3)
89
+ end
90
+ end
91
+ end
92
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lhs
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.3.0
4
+ version: 5.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - https://github.com/local-ch/lhs/graphs/contributors
@@ -304,6 +304,7 @@ files:
304
304
  - spec/record/new_spec.rb
305
305
  - spec/record/options_spec.rb
306
306
  - spec/record/paginatable_collection_spec.rb
307
+ - spec/record/pagination_chain_spec.rb
307
308
  - spec/record/pagination_spec.rb
308
309
  - spec/record/persisted_spec.rb
309
310
  - spec/record/request_spec.rb
@@ -343,7 +344,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
343
344
  requirements:
344
345
  - Ruby >= 1.9.2
345
346
  rubyforge_project:
346
- rubygems_version: 2.5.1
347
+ rubygems_version: 2.2.2
347
348
  signing_key:
348
349
  specification_version: 4
349
350
  summary: Rails gem providing an easy, active-record-like interface for http json services
@@ -441,6 +442,7 @@ test_files:
441
442
  - spec/record/new_spec.rb
442
443
  - spec/record/options_spec.rb
443
444
  - spec/record/paginatable_collection_spec.rb
445
+ - spec/record/pagination_chain_spec.rb
444
446
  - spec/record/pagination_spec.rb
445
447
  - spec/record/persisted_spec.rb
446
448
  - spec/record/request_spec.rb