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