wor-paginate 0.1.7 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.rubocop.yml +1 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +2 -1
- data/README.md +130 -4
- data/lib/generators/templates/wor_paginate.rb +30 -3
- data/lib/wor/paginate.rb +8 -1
- data/lib/wor/paginate/adapters/base.rb +7 -0
- data/lib/wor/paginate/adapters/helpers/total_count.rb +3 -4
- data/lib/wor/paginate/adapters/kaminari.rb +4 -0
- data/lib/wor/paginate/adapters/will_paginate.rb +1 -1
- data/lib/wor/paginate/config.rb +34 -2
- data/lib/wor/paginate/exceptions/dependency_error.rb +11 -0
- data/lib/wor/paginate/formatters/ams_formatter.rb +31 -0
- data/lib/wor/paginate/formatters/base.rb +61 -0
- data/lib/wor/paginate/formatters/panko_formatter.rb +20 -0
- data/lib/wor/paginate/paginate.rb +24 -18
- data/lib/wor/paginate/rspec.rb +11 -5
- data/lib/wor/paginate/utils/preserve_modes.rb +33 -0
- data/lib/wor/paginate/utils/preserve_records_helper.rb +49 -0
- data/lib/wor/paginate/utils/uri_helper.rb +20 -0
- data/lib/wor/paginate/version.rb +1 -1
- data/test.sqlite3 +0 -0
- data/wor-paginate.gemspec +1 -1
- metadata +11 -11
- data/lib/wor/paginate/formatter.rb +0 -50
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 616213ea69272167621606631c90c60e4c14bdb5a2e31f933576ccc5d8dc04e8
|
4
|
+
data.tar.gz: 24486490fa441d027284f1ef4d53ec686d849dc21f0d12bbc81116ed51865c4e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d3eb17ff497fae64cdc0edd117591ac02bc03b74669f82271475202e7d7d0d7c341c85804bbc93f7220751d28c45659265b1c68fd122557d8b558e00d7822d11
|
7
|
+
data.tar.gz: 8a76d0a58b6a8b9fb5af30e450de603a91fa8efd9de0f1e4a881cf813be5d19386b65fd60c7ed4d40d7b63c7a3ea4dd8d570f2a0f5ba08a7ebee3ce166e1fd82
|
data/.rubocop.yml
CHANGED
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,26 @@
|
|
1
1
|
## Change log
|
2
2
|
|
3
|
+
### V0.3.0
|
4
|
+
* [#94](https://github.com/Wolox/wor-paginate/pull/94) Add Panko formatter - [@blacksam07](https://github.com/blacksam07).
|
5
|
+
* [#96](https://github.com/Wolox/wor-paginate/pull/96) Dynamic Adapters - [@juanpablo-rojas](https://github.com/juanpablo-rojas).
|
6
|
+
|
7
|
+
### V0.2.0
|
8
|
+
* [#95](https://github.com/Wolox/wor-paginate/pull/95) Added total_count option to overwrite the count in render_paginated - [@juanpablo-rojas](https://github.com/juanpablo-rojas).
|
9
|
+
* [#93](https://github.com/Wolox/wor-paginate/pull/93) Infinite scroll - [@mnmallea](https://github.com/mnmallea).
|
10
|
+
* [#92](https://github.com/Wolox/wor-paginate/pull/92) Ruby 2.7.0 support - [@mnmallea](https://github.com/mnmallea).
|
11
|
+
* [#91](https://github.com/Wolox/wor-paginate/pull/91) Navigation links - [@mnmallea](https://github.com/mnmallea).
|
12
|
+
|
13
|
+
### V0.1.10
|
14
|
+
* [#90](https://github.com/Wolox/wor-paginate/pull/90) Verify that default adapter adapts - [@mtejedorwolox](https://github.com/mtejedorwolox).
|
15
|
+
|
16
|
+
### V0.1.9
|
17
|
+
* [#88](https://github.com/Wolox/wor-paginate/pull/88) Default adapter - [@mtejedorwolox](https://github.com/mtejedorwolox).
|
18
|
+
* [#87](https://github.com/Wolox/wor-paginate/pull/87) Supporting expect(response).to be_paginated - [@mnmallea](https://github.com/mnmallea).
|
19
|
+
* [#86](https://github.com/Wolox/wor-paginate/pull/86) Added previous_page parameter - [@juanpablo-rojas](https://github.com/juanpablo-rojas).
|
20
|
+
|
21
|
+
### V0.1.8
|
22
|
+
* [#85](https://github.com/Wolox/wor-paginate/pull/85) Delete max Railties dependency in gemspec - [@lucasvoboril](https://github.com/lucasvoboril).
|
23
|
+
|
3
24
|
### V0.1.7
|
4
25
|
* [#61](https://github.com/Wolox/wor-paginate/pull/61) Allows requests on offset pages for enumerables - [@mtejedorwolox](https://github.com/mtejedorwolox).
|
5
26
|
* [#63](https://github.com/Wolox/wor-paginate/pull/63) Add max_limit with optional parameter - [@blacksam07](https://github.com/blacksam07).
|
data/Gemfile
CHANGED
@@ -15,7 +15,7 @@ gemspec
|
|
15
15
|
|
16
16
|
group :development, :test do
|
17
17
|
gem 'active_model_serializers', '~> 0.10.0'
|
18
|
-
gem 'bundler', '
|
18
|
+
gem 'bundler', '>= 2.0.1'
|
19
19
|
gem 'byebug', '~> 9.0'
|
20
20
|
gem 'codeclimate-test-reporter', '~> 1.0.0'
|
21
21
|
gem 'database_cleaner', '~> 1.6.0'
|
@@ -23,6 +23,7 @@ group :development, :test do
|
|
23
23
|
gem 'faker', '~> 1.7.0'
|
24
24
|
gem 'generator_spec', '~> 0.9.0'
|
25
25
|
gem 'kaminari', '~> 0.17.0'
|
26
|
+
gem 'panko_serializer', '~> 0.7.2'
|
26
27
|
gem 'rake', '~> 10.0'
|
27
28
|
gem 'rspec', '~> 3.0'
|
28
29
|
gem 'rspec-rails', '~> 3.6.0'
|
data/README.md
CHANGED
@@ -14,6 +14,7 @@ Wor::Paginate
|
|
14
14
|
- [Custom serializers](#custom-serializers)
|
15
15
|
- [Custom options](#custom-options)
|
16
16
|
- [Custom formatters](#custom-formatters)
|
17
|
+
- [Custom adapters](#custom-adapters)
|
17
18
|
- [Working with Kaminari or will_paginate](#working-with-kaminari-or-will_paginate)
|
18
19
|
- [Test helpers](#test-helpers)
|
19
20
|
- [Contributing](#contributing)
|
@@ -88,7 +89,10 @@ The response to the index will then be:
|
|
88
89
|
"total_pages": 2,
|
89
90
|
"total_count": 28,
|
90
91
|
"current_page": 1,
|
91
|
-
"
|
92
|
+
"previous_page": null,
|
93
|
+
"next_page": 2,
|
94
|
+
"previous_page_url": null,
|
95
|
+
"next_page_url": "http://api.example.com/users?page=2
|
92
96
|
}
|
93
97
|
```
|
94
98
|
|
@@ -130,7 +134,44 @@ class CustomSerializer < ActiveModel::Serializer
|
|
130
134
|
end
|
131
135
|
```
|
132
136
|
|
133
|
-
|
137
|
+
##### total_count
|
138
|
+
You can overwrite the `total_count` pagination param by passing it as a single option to the method. This could be used if the whole collection to be paginated is complex and has the risk to broke when counting all the records.
|
139
|
+
|
140
|
+
```ruby
|
141
|
+
render_paginated DummyModel, total_count: 50
|
142
|
+
```
|
143
|
+
|
144
|
+
##### preserve_records
|
145
|
+
> WARNING: This option only works with an ActiveRecord collection.
|
146
|
+
|
147
|
+
Preserve records option can be added to `render_paginated` to mantain current records. This allow to navigate pages like an infinite scroll without adding new records when switching pages.
|
148
|
+
|
149
|
+
- Timestamp mode (default)
|
150
|
+
```ruby
|
151
|
+
def index
|
152
|
+
render_paginated SomeModel, preserve_records: true
|
153
|
+
end
|
154
|
+
|
155
|
+
# You can customize the field used to preserve this records (default is 'created_at')
|
156
|
+
def index
|
157
|
+
render_paginated SomeModel, preserve_records: { by: :timestamp, field: :custom_time_field }
|
158
|
+
end
|
159
|
+
```
|
160
|
+
|
161
|
+
- PK mode
|
162
|
+
```ruby
|
163
|
+
def index
|
164
|
+
render_paginated SomeModel, preserve_records: { by: :id }
|
165
|
+
end
|
166
|
+
|
167
|
+
# You can customize the field used to preserve this records (default is 'id')
|
168
|
+
def index
|
169
|
+
render_paginated SomeModel, preserve_records: { by: :id, field: :my_custom_id_field }
|
170
|
+
end
|
171
|
+
```
|
172
|
+
|
173
|
+
|
174
|
+
### Custom formatters
|
134
175
|
A formatter is an object that defines the output of the render_paginated method. In case the application needs a different format for a request, it can be passed to the `render_paginated` method using the `formatter` option:
|
135
176
|
```ruby
|
136
177
|
render_paginated DummyModel, formatter: CustomFormatter
|
@@ -140,7 +181,7 @@ or it can also be set as a default in the initializer.
|
|
140
181
|
A new formatter can be created inheriting from the default one. The `format` method should be redefined returning something that can be converted to json.
|
141
182
|
|
142
183
|
```ruby
|
143
|
-
class CustomFormatter < Wor::Paginate::
|
184
|
+
class CustomFormatter < Wor::Paginate::Formatters::Base
|
144
185
|
def format
|
145
186
|
{ page: serialized_content, current: current_page }
|
146
187
|
end
|
@@ -155,6 +196,74 @@ Available helper methods are:
|
|
155
196
|
* `paginated_content`: its class depends on the original content passed to render_paginated, it's the paginated but not yet serialized content.
|
156
197
|
* `serialized_content`: array with all the items after going through the serializer (either the default or a supplied one)
|
157
198
|
|
199
|
+
|
200
|
+
### Custom adapters
|
201
|
+
An adapter is an object that defines how to show the rendered content, and how to calculate several methods of the pagination, such as 'total_count', 'total_pages', 'paginated_content' among others. In case the application needs a different adapter or a custom one, it can be passed to the `render_paginated` method using the `adapter` option:
|
202
|
+
```ruby
|
203
|
+
render_paginated DummyModel, adapter: CustomAdapter
|
204
|
+
```
|
205
|
+
or it can also be set as a default in the initializer.
|
206
|
+
|
207
|
+
A new adapter can be created inheriting from the default Base Adapter. Some methods must be redefined in order to make the adapter "adaptable" to the content that will be rendered.
|
208
|
+
Below is an example of a simple posible CustomAdapter that extends from the base Adapter.
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
class CustomAdapter < Wor::Paginate::Adapters::Base
|
212
|
+
def required_methods
|
213
|
+
%i[count]
|
214
|
+
end
|
215
|
+
|
216
|
+
def paginated_content
|
217
|
+
@paginated_content ||= @content.first(5)
|
218
|
+
end
|
219
|
+
|
220
|
+
def total_pages
|
221
|
+
(total_count / @limit.to_f).ceil
|
222
|
+
end
|
223
|
+
|
224
|
+
def total_count
|
225
|
+
@content.count
|
226
|
+
end
|
227
|
+
|
228
|
+
delegate :count, to: :paginated_content
|
229
|
+
end
|
230
|
+
```
|
231
|
+
Here's a brief explanation on every overwritten method in this CustomAdapter example:
|
232
|
+
|
233
|
+
##### `#required_methods`
|
234
|
+
These will be the methods (as symbols) that the content to be rendered has to support. The next expression will be evaluated for every method added here: `@content.respond_to? method`. All required_methods must answer `true` to the previous expression, in order to make the adapter "adaptable" for the content. For example, if we rendered an ActiveRecord::Relation, this CustomAdapter would be adaptable because an ActiveRecord::Relation responds to the `#count` method. At least one symbol has to be returned in this method, otherwise the adapter won't be able to render content.
|
235
|
+
|
236
|
+
##### `#paginated_content`
|
237
|
+
This is how the content will be shown. As the content comes in the inherited instance variable `@content`, we can transform the content however we want. In the CustomAdapter example, will always be shown the first 5 records.
|
238
|
+
|
239
|
+
##### `#total_pages`
|
240
|
+
This could be defined as the number of pages, given the limit requested. As the other values, this can be a custom number of pages, depending on your needs. For this example, this number is just an integer calculation of the total pages, depending on the limit. Also, like `@content`, we are inheriting the `@limit` variable, which allows us to operate with it however we want.
|
241
|
+
|
242
|
+
##### `#total_count`
|
243
|
+
This will be the number that will tell us 'how many records is returning the request'. Again, we can customize it however we want. For this particular example this will be just the count of `@content`.
|
244
|
+
|
245
|
+
##### `#count` method as delegate
|
246
|
+
In the end of the CustomAdapter we are delegating the `#count` method to the `paginated_content`. This is because the Base Adapter delegates that method to the inherited adapter, so our custom adapter has to know "how to calculate" that method, that's why we are defining a `#count` method in the delegation (It is always mandatory to define the `#count` method in a custom adapter, whether is a method definition or a delegate).
|
247
|
+
|
248
|
+
If the content is an ActiveRecord::Relation, for example, this adapter would work, because `paginated_content` would become an ActiveRecord::Relation, which actually knows "how to calculate" the count method. This works as a delegate, because ActiveRecord::Relation has an internal `#count` definition, but we would have to provide the needed method definition if it is a custom method, or we want a custom behaviour of a known method.
|
249
|
+
|
250
|
+
##### Other available methods to overwrite
|
251
|
+
`Wor::Paginate::Adapters::Base` also has implementations for `#next_page` and `#previous_page` methods (which calculate the number of the next and previous pages, respectively). If you want, you can also overwrite those methods, to calculate custom 'next' and 'previous' page numbers.
|
252
|
+
|
253
|
+
To understand better the implementation of the Base Adapter and how you could overwrite methods in order to make a functional Custom Adapter, take a look at its definition in: [Base adapter Class](https://github.com/Wolox/wor-paginate/blob/master/lib/wor/paginate/adapters/base.rb).
|
254
|
+
Keep in mind that an instance of your Custom Adapter must answer `true` to the `#adapt?` method inherited from the Base Adapter, in order to make it "adaptable" to the content.
|
255
|
+
|
256
|
+
#### Adapters Operations
|
257
|
+
There are also helper methods available to dynamically operate the gem's adapters, so you can configurate them, whether in the initializer or in an internal part of your application. Once you include the gem, you'll be provided with the following methods, inside the Config module:
|
258
|
+
* `Wor::Paginate::Config.add_adapter(adapter)`: Add a specific adapter to the array of the gem's adapters. The 'adapter' variable must be a Class reference to an Adapter Class (that class has to have a similar structure as the CustomAdapter example above).
|
259
|
+
* `Wor::Paginate::Config.remove_adapter(adapter)`: Remove an specific adapter from the array of the gem's adapters.
|
260
|
+
* `Wor::Paginate::Config.clear_adapters`: This method empties the array of the gem's adapters.
|
261
|
+
* `Wor::Paginate::Config.adapters`: Returns all the current internal adapters inside the gem.
|
262
|
+
* `Wor::Paginate::Config.reset_adapters!`: This helper resets the gem's adapters to its default array of adapters. You can see how the array of default adapters looks like at the beggining of the `Wor::Paginate::Config` module: [Config module](https://github.com/Wolox/wor-paginate/blob/master/lib/wor/paginate/config.rb).
|
263
|
+
|
264
|
+
When the gem paginates, it tries to adapt the content to the first adapter that is "adaptable" for the content (unless a custom adapter has been passed to render_paginated or a default_adapter has been defined in the initializer). So beware of which adapters (and in which order) are you leaving in the `Wor::Paginate::Config.adapters` array, because depending on those, the gem will try to adapt the content.
|
265
|
+
|
266
|
+
|
158
267
|
### Working with Kaminari or will_paginate
|
159
268
|
If either Kaminari or will_paginate are required in the project, Wor::Paginate will use them for pagination with no code or configuration change.
|
160
269
|
|
@@ -192,6 +301,24 @@ describe YourController do
|
|
192
301
|
end
|
193
302
|
```
|
194
303
|
|
304
|
+
### Working with panko-serializer
|
305
|
+
|
306
|
+
The default formatter is [Active Model Serializer](https://github.com/rails-api/active_model_serializers).
|
307
|
+
If you want to change it, you should replace the formatter to another one. In this section, we are going to work with `PankoFormatter`
|
308
|
+
|
309
|
+
#### example
|
310
|
+
```ruby
|
311
|
+
Wor::Paginate.configure do |config
|
312
|
+
config.formatter = Wor::Paginate::Formatters::PankoFormatter
|
313
|
+
end
|
314
|
+
```
|
315
|
+
and next pass the specific serializer that you can use in the specific endpoint
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
def index
|
319
|
+
render_paginated DummyModel, each_serializer: DummyModelPankoSerializer
|
320
|
+
end
|
321
|
+
```
|
195
322
|
## Contributing
|
196
323
|
|
197
324
|
1. Fork it
|
@@ -208,7 +335,6 @@ end
|
|
208
335
|
## About ##
|
209
336
|
|
210
337
|
The current maintainers of this gem are :
|
211
|
-
* [Lucas Voboril](https://github.com/lucasVoboril)
|
212
338
|
* [Martín Mallea](https://github.com/mnmallea)
|
213
339
|
|
214
340
|
This project was developed by:
|
@@ -6,8 +6,35 @@ Wor::Paginate.configure do |config|
|
|
6
6
|
config.per_page_param = :limit
|
7
7
|
|
8
8
|
# In case you want to use other format for your response, you can override our formatter here
|
9
|
-
# You can extend from Wor::Paginate::
|
9
|
+
# You can extend from Wor::Paginate::Formatters::Base and override the 'format' method
|
10
10
|
# For more info about available methods for formatters see:
|
11
|
-
# https://github.com/Wolox/wor-paginate/blob/master/lib/wor/paginate/
|
12
|
-
# config.formatter = Wor::Paginate::
|
11
|
+
# https://github.com/Wolox/wor-paginate/blob/master/lib/wor/paginate/formatters/base.rb
|
12
|
+
# config.formatter = Wor::Paginate::Formatters::AmsFormatter
|
13
|
+
|
14
|
+
# Configure a default adapter to use on pagination
|
15
|
+
# config.default_adapter = nil
|
16
|
+
|
17
|
+
# Default: nil
|
18
|
+
# Possible values:
|
19
|
+
# Wor::Paginate::Adapters::KaminariAlreadyPaginated
|
20
|
+
# Wor::Paginate::Adapters::WillPaginateAlreadyPaginated
|
21
|
+
# Wor::Paginate::Adapters::WillPaginate
|
22
|
+
# Wor::Paginate::Adapters::Kaminari
|
23
|
+
# Wor::Paginate::Adapters::ActiveRecord
|
24
|
+
# Wor::Paginate::Adapters::Enumerable
|
25
|
+
|
26
|
+
# Custom adapters
|
27
|
+
|
28
|
+
# config.adapters = [
|
29
|
+
# Adapters::KaminariAlreadyPaginated,
|
30
|
+
# Adapters::WillPaginateAlreadyPaginated,
|
31
|
+
# Adapters::WillPaginate,
|
32
|
+
# Adapters::Kaminari,
|
33
|
+
# Adapters::ActiveRecord,
|
34
|
+
# Adapters::Enumerable
|
35
|
+
# ]
|
36
|
+
# config.add_adapter(adapter)
|
37
|
+
# config.remove_adapter(adapter)
|
38
|
+
# config.clear_adapters
|
39
|
+
# config.reset_adapters!
|
13
40
|
end
|
data/lib/wor/paginate.rb
CHANGED
@@ -5,10 +5,13 @@ require_relative 'paginate/adapters/kaminari'
|
|
5
5
|
require_relative 'paginate/adapters/will_paginate'
|
6
6
|
require_relative 'paginate/adapters/kaminari_already_paginated'
|
7
7
|
require_relative 'paginate/adapters/will_paginate_already_paginated'
|
8
|
+
require_relative 'paginate/exceptions/dependency_error'
|
8
9
|
require_relative 'paginate/exceptions/no_pagination_adapter'
|
9
10
|
require_relative 'paginate/exceptions/invalid_page_number'
|
10
11
|
require_relative 'paginate/exceptions/invalid_limit_number'
|
11
|
-
require_relative 'paginate/
|
12
|
+
require_relative 'paginate/formatters/base'
|
13
|
+
require_relative 'paginate/formatters/panko_formatter'
|
14
|
+
require_relative 'paginate/formatters/ams_formatter'
|
12
15
|
require_relative 'paginate/config'
|
13
16
|
require_relative 'paginate/paginate'
|
14
17
|
|
@@ -16,6 +19,10 @@ module Wor
|
|
16
19
|
module Paginate
|
17
20
|
def self.configure
|
18
21
|
yield Config
|
22
|
+
|
23
|
+
return if Config.adapters.any? || Config.default_adapter.present?
|
24
|
+
|
25
|
+
raise Exceptions::NoPaginationAdapter
|
19
26
|
end
|
20
27
|
end
|
21
28
|
end
|
@@ -4,11 +4,10 @@ module Wor
|
|
4
4
|
module Helpers
|
5
5
|
module TotalCount
|
6
6
|
def total_count
|
7
|
-
|
8
|
-
content_size
|
9
|
-
return content.to_a.size if content_size.is_a? Hash
|
7
|
+
@content_size ||= @content.reorder(nil).size
|
8
|
+
return @content_size.keys.size if @content_size.is_a? Hash
|
10
9
|
|
11
|
-
content_size
|
10
|
+
@content_size
|
12
11
|
end
|
13
12
|
end
|
14
13
|
end
|
data/lib/wor/paginate/config.rb
CHANGED
@@ -6,10 +6,22 @@ module Wor
|
|
6
6
|
default_page: 1,
|
7
7
|
page_param: :page,
|
8
8
|
per_page_param: :limit,
|
9
|
-
formatter: Wor::Paginate::
|
10
|
-
max_limit: 50
|
9
|
+
formatter: Wor::Paginate::Formatters::Base,
|
10
|
+
max_limit: 50,
|
11
|
+
default_adapter: nil
|
11
12
|
}.freeze
|
12
13
|
|
14
|
+
DEFAULT_ADAPTERS = {
|
15
|
+
kaminari_paginated: Adapters::KaminariAlreadyPaginated,
|
16
|
+
will_paginate_paginated: Adapters::WillPaginateAlreadyPaginated,
|
17
|
+
will_paginate: Adapters::WillPaginate,
|
18
|
+
kaminari: Adapters::Kaminari,
|
19
|
+
active_record: Adapters::ActiveRecord,
|
20
|
+
enumerable: Adapters::Enumerable
|
21
|
+
}.freeze
|
22
|
+
|
23
|
+
@adapters = DEFAULT_ADAPTERS.values
|
24
|
+
|
13
25
|
module_function
|
14
26
|
|
15
27
|
DEFAULTS_CONFIGS.each do |key, value|
|
@@ -22,10 +34,30 @@ module Wor
|
|
22
34
|
end
|
23
35
|
end
|
24
36
|
|
37
|
+
def add_adapter(adapter)
|
38
|
+
@adapters << adapter
|
39
|
+
end
|
40
|
+
|
41
|
+
def remove_adapter(adapter)
|
42
|
+
@adapters.delete(adapter)
|
43
|
+
end
|
44
|
+
|
45
|
+
def clear_adapters
|
46
|
+
@adapters.clear
|
47
|
+
end
|
48
|
+
|
49
|
+
def adapters
|
50
|
+
@adapters
|
51
|
+
end
|
52
|
+
|
25
53
|
# This is mostly useful for the tests
|
26
54
|
def reset!
|
27
55
|
DEFAULTS_CONFIGS.each { |k, v| send("#{k}=", v) }
|
28
56
|
end
|
57
|
+
|
58
|
+
def reset_adapters!
|
59
|
+
@adapters = DEFAULT_ADAPTERS.values
|
60
|
+
end
|
29
61
|
end
|
30
62
|
end
|
31
63
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Wor
|
2
|
+
module Paginate
|
3
|
+
module Formatters
|
4
|
+
class AmsFormatter < Base
|
5
|
+
include ActiveSupport::Callbacks
|
6
|
+
define_callbacks :raise_dependency_error
|
7
|
+
set_callback :raise_dependency_error, :before, :serialized_content, unless: :ams_defined?
|
8
|
+
|
9
|
+
def serialized_content
|
10
|
+
return serializable_resource.new(paginated_content).as_json unless serializer.present?
|
11
|
+
raise_dependency_error unless serializer.respond_to?('_attributes_data')
|
12
|
+
paginated_content.map { |item| serializer.new(item, options) }
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def serializable_resource
|
18
|
+
ActiveModelSerializers::SerializableResource
|
19
|
+
end
|
20
|
+
|
21
|
+
def ams_defined?
|
22
|
+
defined? serializable_resource
|
23
|
+
end
|
24
|
+
|
25
|
+
def raise_dependency_error
|
26
|
+
raise Wor::Paginate::Exceptions::DependencyError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'wor/paginate/utils/uri_helper'
|
2
|
+
|
3
|
+
module Wor
|
4
|
+
module Paginate
|
5
|
+
module Formatters
|
6
|
+
class Base
|
7
|
+
include Utils::UriHelper
|
8
|
+
attr_accessor :adapter, :content, :formatter, :options
|
9
|
+
|
10
|
+
def initialize(adapter, options = {})
|
11
|
+
@adapter = adapter
|
12
|
+
@options = options
|
13
|
+
end
|
14
|
+
|
15
|
+
def format # rubocop: disable Metrics/MethodLength
|
16
|
+
{
|
17
|
+
page: serialized_content,
|
18
|
+
count: count,
|
19
|
+
total_pages: total_pages,
|
20
|
+
total_count: options[:total_count] || total_count,
|
21
|
+
current_page: current_page,
|
22
|
+
previous_page: previous_page,
|
23
|
+
next_page: next_page,
|
24
|
+
next_page_url: page_url(next_page),
|
25
|
+
previous_page_url: page_url(previous_page)
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
protected
|
30
|
+
|
31
|
+
delegate :count, :total_count, :total_pages, :previous_page, :next_page, to: :adapter
|
32
|
+
|
33
|
+
def current_page
|
34
|
+
adapter.page.to_i
|
35
|
+
end
|
36
|
+
|
37
|
+
def paginated_content
|
38
|
+
@content ||= adapter.paginated_content
|
39
|
+
end
|
40
|
+
|
41
|
+
def serialized_content
|
42
|
+
paginated_content.as_json
|
43
|
+
end
|
44
|
+
|
45
|
+
def serializer
|
46
|
+
options[:each_serializer]
|
47
|
+
end
|
48
|
+
|
49
|
+
def page_url(page)
|
50
|
+
return nil unless page
|
51
|
+
|
52
|
+
replace_query_params(current_url, page: page)
|
53
|
+
end
|
54
|
+
|
55
|
+
def current_url
|
56
|
+
options[:_current_url]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Wor
|
2
|
+
module Paginate
|
3
|
+
module Formatters
|
4
|
+
class PankoFormatter < Base
|
5
|
+
def serialized_content
|
6
|
+
raise Wor::Paginate::Exceptions::DependencyError unless valid_serializer
|
7
|
+
ActiveRecord::Base.transaction do
|
8
|
+
Panko::ArraySerializer.new(paginated_content, each_serializer: serializer).to_a
|
9
|
+
end
|
10
|
+
rescue ActiveRecord::StatementInvalid
|
11
|
+
retry
|
12
|
+
end
|
13
|
+
|
14
|
+
def valid_serializer
|
15
|
+
serializer.respond_to?('_descriptor') && defined?(Panko::Serializer)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -1,16 +1,7 @@
|
|
1
|
+
require_relative 'utils/preserve_records_helper'
|
2
|
+
|
1
3
|
module Wor
|
2
4
|
module Paginate
|
3
|
-
# The order of this array is important!
|
4
|
-
# In a future release we'll provide an interface to manipulate it
|
5
|
-
ADAPTERS = [
|
6
|
-
Adapters::KaminariAlreadyPaginated,
|
7
|
-
Adapters::WillPaginateAlreadyPaginated,
|
8
|
-
Adapters::WillPaginate,
|
9
|
-
Adapters::Kaminari,
|
10
|
-
Adapters::ActiveRecord,
|
11
|
-
Adapters::Enumerable
|
12
|
-
].freeze
|
13
|
-
|
14
5
|
def render_paginated(content, options = {})
|
15
6
|
return render_paginate_with_include(content, options) if includes?(options)
|
16
7
|
|
@@ -18,9 +9,16 @@ module Wor
|
|
18
9
|
end
|
19
10
|
|
20
11
|
def paginate(content, options = {})
|
12
|
+
current_url = request.original_url
|
13
|
+
if (preserve_records = options[:preserve_records])
|
14
|
+
content, current_url = Wor::Paginate::Utils::PreserveRecordsHelper
|
15
|
+
.new(content, current_url,
|
16
|
+
preserve_records.is_a?(Hash) ? preserve_records : {}).call
|
17
|
+
end
|
21
18
|
adapter = find_adapter_for_content(content, options)
|
22
19
|
raise Exceptions::NoPaginationAdapter if adapter.blank?
|
23
|
-
|
20
|
+
|
21
|
+
formatter_class(options).new(adapter, options.merge(_current_url: current_url)).format
|
24
22
|
end
|
25
23
|
|
26
24
|
def render_paginate_with_include(content, options)
|
@@ -28,12 +26,20 @@ module Wor
|
|
28
26
|
end
|
29
27
|
|
30
28
|
def formatter_class(options)
|
31
|
-
options[:formatter].presence ||
|
29
|
+
options[:formatter].presence || Config.formatter
|
32
30
|
end
|
33
31
|
|
34
32
|
def find_adapter_for_content(content, options)
|
35
|
-
|
36
|
-
|
33
|
+
return instance_adapter(options[:adapter], content, options) unless options[:adapter].nil?
|
34
|
+
|
35
|
+
adapters = []
|
36
|
+
adapters << Config.default_adapter if Config.default_adapter.present?
|
37
|
+
adapters += Config.adapters
|
38
|
+
adapters.map { |adapter| instance_adapter(adapter, content, options) }.find(&:adapt?)
|
39
|
+
end
|
40
|
+
|
41
|
+
def instance_adapter(adapter, content, options)
|
42
|
+
adapter.new(content, page(options), limit(options))
|
37
43
|
end
|
38
44
|
|
39
45
|
def page(options)
|
@@ -41,15 +47,15 @@ module Wor
|
|
41
47
|
end
|
42
48
|
|
43
49
|
def option_limit(options)
|
44
|
-
options[:limit]
|
50
|
+
options[:limit]&.to_i
|
45
51
|
end
|
46
52
|
|
47
53
|
def option_max_limit(options)
|
48
|
-
options[:max_limit]
|
54
|
+
options[:max_limit]&.to_i
|
49
55
|
end
|
50
56
|
|
51
57
|
def param_limit
|
52
|
-
params[Config.per_page_param]
|
58
|
+
params[Config.per_page_param]&.to_i
|
53
59
|
end
|
54
60
|
|
55
61
|
def includes?(options)
|
data/lib/wor/paginate/rspec.rb
CHANGED
@@ -28,9 +28,14 @@ end
|
|
28
28
|
|
29
29
|
RSpec::Matchers.define :be_paginated do
|
30
30
|
match do |actual_response|
|
31
|
-
|
32
|
-
|
33
|
-
|
31
|
+
response = parse_response(actual_response)
|
32
|
+
formatter = @custom_formatter || Wor::Paginate::Formatters::Base
|
33
|
+
@formatted_keys = formatter.new(MockedAdapter.new, _current_url: 'http://exaple.com/').format.as_json.keys
|
34
|
+
response.keys == @formatted_keys
|
35
|
+
end
|
36
|
+
|
37
|
+
def parse_response(response)
|
38
|
+
response.is_a?(Hash) ? response : JSON.parse(response.body)
|
34
39
|
end
|
35
40
|
|
36
41
|
chain :with do |custom_formatter|
|
@@ -38,10 +43,11 @@ RSpec::Matchers.define :be_paginated do
|
|
38
43
|
end
|
39
44
|
|
40
45
|
failure_message do |actual_response|
|
41
|
-
"expected that #{actual_response} to be paginated with keys #{@formatted_keys}"
|
46
|
+
"expected that #{parse_response(actual_response)} to be paginated with keys #{@formatted_keys}"
|
42
47
|
end
|
43
48
|
|
44
49
|
failure_message_when_negated do |actual_response|
|
45
|
-
"expected that #{actual_response} not
|
50
|
+
"expected that #{parse_response(actual_response)} not " \
|
51
|
+
"to be paginated with keys #{@formatted_keys}"
|
46
52
|
end
|
47
53
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Wor
|
2
|
+
module Paginate
|
3
|
+
module Utils
|
4
|
+
module PreserveModes
|
5
|
+
module Timestamp
|
6
|
+
def self.default_field
|
7
|
+
:created_at
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.last_value(query_param_value, _content, _field)
|
11
|
+
query_param_value ? Time.parse(query_param_value) : now_timestamp
|
12
|
+
end
|
13
|
+
|
14
|
+
private_class_method
|
15
|
+
|
16
|
+
def self.now_timestamp
|
17
|
+
Time.zone.now.iso8601(10)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
module Id
|
22
|
+
def self.default_field
|
23
|
+
:id
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.last_value(query_param_value, content, field)
|
27
|
+
query_param_value || content.maximum(field)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'uri_helper'
|
2
|
+
require_relative 'preserve_modes'
|
3
|
+
|
4
|
+
module Wor
|
5
|
+
module Paginate
|
6
|
+
module Utils
|
7
|
+
class PreserveRecordsHelper
|
8
|
+
def initialize(content, url, options)
|
9
|
+
@content = content
|
10
|
+
@url = url
|
11
|
+
@options = options
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
[content.where("#{field} <= :last_value", last_value: last_value),
|
16
|
+
UriHelper.replace_query_params(url, query_param_name => last_value)]
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
attr_reader :content, :url, :options
|
22
|
+
|
23
|
+
def by
|
24
|
+
@by ||= begin
|
25
|
+
by = options[:by]&.to_s || 'timestamp'
|
26
|
+
raise ArgumentError, "'by' option should be 'id' or 'timestamp'" unless
|
27
|
+
%w[timestamp id].include? by
|
28
|
+
"Wor::Paginate::Utils::PreserveModes::#{by.classify}".constantize
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def field
|
33
|
+
@field ||= options[:field] || by.default_field
|
34
|
+
end
|
35
|
+
|
36
|
+
def last_value
|
37
|
+
@last_value ||= begin
|
38
|
+
query_param_value = UriHelper.query_params(url)[query_param_name]
|
39
|
+
by.last_value(query_param_value, content, field)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def query_param_name
|
44
|
+
@query_param_name ||= "#{field}_let"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Wor
|
2
|
+
module Paginate
|
3
|
+
module Utils
|
4
|
+
module UriHelper
|
5
|
+
def replace_query_params(uri_string, new_query)
|
6
|
+
uri = URI.parse(uri_string)
|
7
|
+
query = Rack::Utils.parse_query(uri.query)
|
8
|
+
uri.query = Rack::Utils.build_query(query.with_indifferent_access.merge(new_query))
|
9
|
+
uri.to_s
|
10
|
+
end
|
11
|
+
|
12
|
+
def query_params(uri_string)
|
13
|
+
Rack::Utils.parse_query(URI.parse(uri_string).query).with_indifferent_access
|
14
|
+
end
|
15
|
+
|
16
|
+
module_function :replace_query_params, :query_params
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
data/lib/wor/paginate/version.rb
CHANGED
data/test.sqlite3
ADDED
File without changes
|
data/wor-paginate.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: wor-paginate
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- icoluccio
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2020-09-17 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: railties
|
@@ -20,9 +20,6 @@ dependencies:
|
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
22
|
version: 4.1.0
|
23
|
-
- - "<="
|
24
|
-
- !ruby/object:Gem::Version
|
25
|
-
version: 6.0.0
|
26
23
|
type: :runtime
|
27
24
|
prerelease: false
|
28
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -30,9 +27,6 @@ dependencies:
|
|
30
27
|
- - ">="
|
31
28
|
- !ruby/object:Gem::Version
|
32
29
|
version: 4.1.0
|
33
|
-
- - "<="
|
34
|
-
- !ruby/object:Gem::Version
|
35
|
-
version: 6.0.0
|
36
30
|
- !ruby/object:Gem::Dependency
|
37
31
|
name: rails
|
38
32
|
requirement: !ruby/object:Gem::Requirement
|
@@ -80,14 +74,21 @@ files:
|
|
80
74
|
- lib/wor/paginate/adapters/will_paginate.rb
|
81
75
|
- lib/wor/paginate/adapters/will_paginate_already_paginated.rb
|
82
76
|
- lib/wor/paginate/config.rb
|
77
|
+
- lib/wor/paginate/exceptions/dependency_error.rb
|
83
78
|
- lib/wor/paginate/exceptions/invalid_limit_number.rb
|
84
79
|
- lib/wor/paginate/exceptions/invalid_page_number.rb
|
85
80
|
- lib/wor/paginate/exceptions/no_pagination_adapter.rb
|
86
|
-
- lib/wor/paginate/
|
81
|
+
- lib/wor/paginate/formatters/ams_formatter.rb
|
82
|
+
- lib/wor/paginate/formatters/base.rb
|
83
|
+
- lib/wor/paginate/formatters/panko_formatter.rb
|
87
84
|
- lib/wor/paginate/paginate.rb
|
88
85
|
- lib/wor/paginate/rspec.rb
|
86
|
+
- lib/wor/paginate/utils/preserve_modes.rb
|
87
|
+
- lib/wor/paginate/utils/preserve_records_helper.rb
|
88
|
+
- lib/wor/paginate/utils/uri_helper.rb
|
89
89
|
- lib/wor/paginate/version.rb
|
90
90
|
- pull_request_template.md
|
91
|
+
- test.sqlite3
|
91
92
|
- wor-paginate.gemspec
|
92
93
|
homepage: https://github.com/Wolox/wor-paginate
|
93
94
|
licenses:
|
@@ -108,8 +109,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
108
109
|
- !ruby/object:Gem::Version
|
109
110
|
version: '0'
|
110
111
|
requirements: []
|
111
|
-
|
112
|
-
rubygems_version: 2.5.2.1
|
112
|
+
rubygems_version: 3.0.3
|
113
113
|
signing_key:
|
114
114
|
specification_version: 4
|
115
115
|
summary: Simplified pagination for Rails API controllers
|
@@ -1,50 +0,0 @@
|
|
1
|
-
module Wor
|
2
|
-
module Paginate
|
3
|
-
class Formatter
|
4
|
-
attr_accessor :adapter, :content, :formatter, :options
|
5
|
-
|
6
|
-
def initialize(adapter, options = {})
|
7
|
-
@adapter = adapter
|
8
|
-
@options = options
|
9
|
-
end
|
10
|
-
|
11
|
-
def format
|
12
|
-
{
|
13
|
-
page: serialized_content,
|
14
|
-
count: count,
|
15
|
-
total_pages: total_pages,
|
16
|
-
total_count: total_count,
|
17
|
-
current_page: current_page,
|
18
|
-
next_page: next_page
|
19
|
-
}
|
20
|
-
end
|
21
|
-
|
22
|
-
protected
|
23
|
-
|
24
|
-
delegate :count, :total_count, :total_pages, :next_page, to: :adapter
|
25
|
-
|
26
|
-
def current_page
|
27
|
-
adapter.page.to_i
|
28
|
-
end
|
29
|
-
|
30
|
-
def paginated_content
|
31
|
-
@content ||= adapter.paginated_content
|
32
|
-
end
|
33
|
-
|
34
|
-
def serialized_content
|
35
|
-
if serializer.present?
|
36
|
-
return paginated_content.map { |item| serializer.new(item, options) }
|
37
|
-
end
|
38
|
-
if defined? ActiveModelSerializers::SerializableResource
|
39
|
-
ActiveModelSerializers::SerializableResource.new(paginated_content).as_json
|
40
|
-
else
|
41
|
-
paginated_content.as_json
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def serializer
|
46
|
-
options[:each_serializer]
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|