lhs 5.3.0 → 5.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +36 -3
- data/lib/lhs/concerns/record/all.rb +1 -3
- data/lib/lhs/concerns/record/batch.rb +1 -1
- data/lib/lhs/concerns/record/chainable.rb +61 -1
- data/lib/lhs/concerns/record/pagination.rb +9 -5
- data/lib/lhs/pagination.rb +17 -6
- data/lib/lhs/version.rb +1 -1
- data/spec/pagination/pages_left_spec.rb +1 -1
- data/spec/record/pagination_chain_spec.rb +92 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 80529532ef8aaafcbe7822abac42c0082f2b2b32
|
4
|
+
data.tar.gz: f3b872fa678981d24ccfdcc24af642a397cffac4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
##
|
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
|
-
|
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] ||
|
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(
|
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
|
16
|
-
case
|
15
|
+
def pagination_class
|
16
|
+
case pagination_strategy.to_sym
|
17
17
|
when :page
|
18
|
-
PagePagination
|
18
|
+
LHS::PagePagination
|
19
19
|
when :start
|
20
|
-
StartPagination
|
20
|
+
LHS::StartPagination
|
21
21
|
else
|
22
|
-
OffsetPagination
|
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
|
data/lib/lhs/pagination.rb
CHANGED
@@ -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::
|
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
|
data/lib/lhs/version.rb
CHANGED
@@ -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.
|
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.
|
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
|