wor-paginate 0.4.2 → 0.5.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
  SHA256:
3
- metadata.gz: bd8ccaf4d5f8af78619449bf39383c133e41d193e3db20db6bb98f79077b6bdc
4
- data.tar.gz: d42ed079f99f0ed72379507f0f9bfa4225e977074d9eea85dace43ef7120dce0
3
+ metadata.gz: decca93406ee541c53c128fb024c0be0ba58576b47a0c9cde5e834e33365c8e2
4
+ data.tar.gz: baae1708bf393145b589b872e3bf2a4ba44f3215efecc1a3bae34025cac0c9c4
5
5
  SHA512:
6
- metadata.gz: f6bd84a58f0c0879756e2954c0e25ee7e0cdf3206c471b4640304f2cd9853e60029857d21000618a95d6678aa60b2c14a24fac4c6991f9d9df95419c4651a7d7
7
- data.tar.gz: 3066ece4481767bf0adcdbe2c295c453b7ad2c8340d0175a20b27680886c79e01ede4c102b59aed0f806405275509e3108545c79448cd670e4b791af7b0ad8b4
6
+ metadata.gz: 75eb5ddfc44ed747a69fa797125776a4a2cdbe5e40ba9a3e7502a9af1685b844cad52d50c910d3856d591e8a31b7f6f1495a42542eabfb50a057992ba183d717
7
+ data.tar.gz: a140a0337bdb012c9821217f34cd2ea2099c0f8407cab21e8042c80ab5ee2c0ca8a375ad83d8931ed159f855cbc8ffc72458252c2c006694a9fa3e7da7d858a9
data/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  ## Change log
2
2
 
3
+ ### V0.5.0
4
+ * [#6](https://github.com/icoluccio/wor-paginate/pull/6) Add block serialization to `render_paginated` - [@icoluccio](https://github.com/icoluccio).
5
+
3
6
  ### V0.4.2
4
7
  * [#3](https://github.com/icoluccio/wor-paginate/pull/3) Add Pagy adapter - [@icoluccio](https://github.com/icoluccio).
5
8
  * [#4](https://github.com/icoluccio/wor-paginate/pull/4) Remove remaining rubocop:disable/:enable comments - [@icoluccio](https://github.com/icoluccio).
data/README.md CHANGED
@@ -11,12 +11,14 @@ Wor::Paginate
11
11
  - [Basic usage](#basic-usage)
12
12
  - [Customizing output](#customizing-output)
13
13
  - [Custom serializers](#custom-serializers)
14
+ - [Block serialization](#block-serialization)
14
15
  - [Custom options](#custom-options)
15
16
  - [Custom formatters](#custom-formatters)
16
17
  - [Custom adapters](#custom-adapters)
17
18
  - [Working with Kaminari, will_paginate, or Pagy](#working-with-kaminari-will_paginate-or-pagy)
18
19
  - [Kaminari and will_paginate both define `Model.page`](#kaminari-and-will_paginate-both-define-modelpage)
19
20
  - [Test helpers](#test-helpers)
21
+ - [Field value chains](#field-value-chains)
20
22
  - [Contributing](#contributing)
21
23
  - [Releases](#releases)
22
24
  - [About](#about)
@@ -109,6 +111,19 @@ render_paginated DummyModel, each_serializer: CustomDummyModelSerializer
109
111
  ```
110
112
  where the serializer is just an [`ActiveModel::Serializer`](https://github.com/rails-api/active_model_serializers).
111
113
 
114
+ #### Block serialization
115
+ When you need a custom per-record payload that doesn't fit a serializer class, pass a block to `render_paginated`. The block receives each record and its return value is used as the serialized entry:
116
+
117
+ ```ruby
118
+ def index
119
+ render_paginated(DummyModel) { |record| { id: record.id, label: record.name.upcase } }
120
+ end
121
+ ```
122
+
123
+ All pagination metadata (`count`, `total_pages`, `next_page_url`, etc.) is still computed and returned normally. The block only controls the content of the `page` array.
124
+
125
+ The block form works with any adapter and takes precedence over `each_serializer` when both are supplied.
126
+
112
127
  #### Custom options
113
128
  ##### max_limit
114
129
  The max amount of items is passed through the `max_limit` option. You can set the value in the initializer or in the `render_paginated` method (If none is supplied, take the default value configured in the initializer). Default is 50.
@@ -299,7 +314,7 @@ render_paginated SomeModel, adapter: KaminariRenamedAdapter
299
314
  ```
300
315
 
301
316
  ### Test helpers
302
- You can use the `be_paginated` matcher to test your endpoints. It also accepts the `with` chain method to receive a formatter.
317
+ You can use the `be_paginated` matcher to test your endpoints.
303
318
 
304
319
  You only need to add this in your rails_helper.rb
305
320
 
@@ -332,6 +347,22 @@ describe YourController do
332
347
  end
333
348
  ```
334
349
 
350
+ #### Field value chains
351
+
352
+ You can assert specific pagination field values by chaining matchers:
353
+
354
+ ```ruby
355
+ expect(response_body).to be_paginated
356
+ .with_total_count(42)
357
+ .with_current_page(1)
358
+ .with_next_page(2)
359
+ .with_previous_page(nil)
360
+ .with_total_pages(3)
361
+ .with_count(25)
362
+ ```
363
+
364
+ Each chain is optional and independent. When a chain assertion fails, the error message names the specific field and the expected vs actual value.
365
+
335
366
  ### Working with panko-serializer
336
367
 
337
368
  The default formatter is [Active Model Serializer](https://github.com/rails-api/active_model_serializers).
@@ -359,7 +390,7 @@ and next, pass the specific serializer that you can use in the specific endpoint
359
390
  5. Commit your changes (`git commit -am 'Add some feature'`)
360
391
  6. Run RuboCop lint (`bundle exec rubocop lib spec --format simple`)
361
392
  7. Run rspec tests (`BUNDLE_GEMFILE=gemfiles/rails_8.1.gemfile bundle exec rspec`)
362
- 8. Push your branch (`git push origin my-new-feature`) the pre-push hook re-verifies both automatically
393
+ 8. Push your branch (`git push origin my-new-feature`) - the pre-push hook re-verifies both automatically
363
394
  9. Create a new Pull Request to `main` branch
364
395
 
365
396
  ## Releases
@@ -7,14 +7,15 @@ module Wor
7
7
  include Utils::UriHelper
8
8
  attr_accessor :adapter, :formatter, :options
9
9
 
10
- def initialize(adapter, options = {})
10
+ def initialize(adapter, options = {}, &block)
11
11
  @adapter = adapter
12
12
  @options = options
13
+ @serializer_block = block
13
14
  end
14
15
 
15
16
  def format # rubocop: disable Metrics/MethodLength
16
17
  {
17
- page: serialized_content,
18
+ page: serialized_page,
18
19
  count: count,
19
20
  total_pages: total_pages,
20
21
  total_count: options[:total_count] || total_count,
@@ -38,6 +39,12 @@ module Wor
38
39
  @paginated_content ||= adapter.paginated_content
39
40
  end
40
41
 
42
+ def serialized_page
43
+ return paginated_content.map(&@serializer_block) if @serializer_block
44
+
45
+ serialized_content
46
+ end
47
+
41
48
  def serialized_content
42
49
  paginated_content.as_json
43
50
  end
@@ -2,13 +2,13 @@ require_relative 'utils/preserve_records_helper'
2
2
 
3
3
  module Wor
4
4
  module Paginate
5
- def render_paginated(content, options = {})
6
- return render_paginate_with_include(content, options) if includes?(options)
5
+ def render_paginated(content, options = {}, &block)
6
+ return render_paginate_with_include(content, options, &block) if includes?(options)
7
7
 
8
- render json: paginate(content, options)
8
+ render json: paginate(content, options, &block)
9
9
  end
10
10
 
11
- def paginate(content, options = {})
11
+ def paginate(content, options = {}, &block)
12
12
  current_url = request.original_url
13
13
  if (preserve_records = options[:preserve_records])
14
14
  content, current_url = Wor::Paginate::Utils::PreserveRecordsHelper
@@ -18,11 +18,12 @@ module Wor
18
18
  adapter = find_adapter_for_content(content, options)
19
19
  raise Exceptions::NoPaginationAdapter if adapter.blank?
20
20
 
21
- formatter_class(options).new(adapter, options.merge(_current_url: current_url)).format
21
+ merged = options.merge(_current_url: current_url)
22
+ formatter_class(options).new(adapter, merged, &block).format
22
23
  end
23
24
 
24
- def render_paginate_with_include(content, options)
25
- render json: paginate(content, options), include: options[:include]
25
+ def render_paginate_with_include(content, options, &block)
26
+ render json: paginate(content, options, &block), include: options[:include]
26
27
  end
27
28
 
28
29
  def formatter_class(options)
@@ -1,3 +1,5 @@
1
+ CHAINABLE_FIELDS = %w[total_count count total_pages current_page next_page previous_page].freeze
2
+
1
3
  class MockedAdapter < Wor::Paginate::Adapters::Base
2
4
  def initialize
3
5
  super(nil, 1, 1)
@@ -28,28 +30,63 @@ class MockedAdapter < Wor::Paginate::Adapters::Base
28
30
  end
29
31
  end
30
32
 
33
+ class PaginatedAssertion
34
+ attr_reader :failures, :formatted_keys
35
+
36
+ def initialize(response, formatter, expectations)
37
+ @response = response
38
+ formatted = formatter.new(MockedAdapter.new, _current_url: 'http://example.com/')
39
+ @formatted_keys = formatted.format.as_json.keys
40
+ @failures = key_failures + field_failures(expectations)
41
+ end
42
+
43
+ def pass?
44
+ @failures.empty?
45
+ end
46
+
47
+ private
48
+
49
+ def key_failures
50
+ return [] if @response.keys == @formatted_keys
51
+
52
+ ["expected keys #{@formatted_keys} but got #{@response.keys}"]
53
+ end
54
+
55
+ def field_failures(expectations)
56
+ CHAINABLE_FIELDS.filter_map do |field|
57
+ expected = expectations[field]
58
+ next if expected.nil?
59
+
60
+ actual = @response[field]
61
+ next if actual == expected
62
+
63
+ "expected #{field} to be #{expected.inspect} but got #{actual.inspect}"
64
+ end
65
+ end
66
+ end
67
+
31
68
  RSpec::Matchers.define :be_paginated do
32
69
  match do |actual_response|
33
- response = parse_response(actual_response)
34
70
  formatter = @custom_formatter || Wor::Paginate::Formatters::Base
35
- @formatted_keys = formatter.new(MockedAdapter.new, _current_url: 'http://exaple.com/').format.as_json.keys
36
- response.keys == @formatted_keys
71
+ expectations = CHAINABLE_FIELDS.index_with { |f| instance_variable_get(:"@expected_#{f}") }
72
+ @assertion = PaginatedAssertion.new(parse_response(actual_response), formatter, expectations)
73
+ @assertion.pass?
37
74
  end
38
75
 
39
76
  def parse_response(response)
40
77
  response.is_a?(Hash) ? response : JSON.parse(response.body)
41
78
  end
42
79
 
43
- chain :with do |custom_formatter|
44
- @custom_formatter = custom_formatter
45
- end
80
+ chain(:with) { |formatter| @custom_formatter = formatter }
46
81
 
47
- failure_message do |actual_response|
48
- "expected that #{parse_response(actual_response)} to be paginated with keys #{@formatted_keys}"
82
+ CHAINABLE_FIELDS.each do |field|
83
+ chain(:"with_#{field}") { |value| instance_variable_set(:"@expected_#{field}", value) }
49
84
  end
50
85
 
86
+ failure_message { |_| @assertion.failures.join("\n") }
87
+
51
88
  failure_message_when_negated do |actual_response|
52
89
  "expected that #{parse_response(actual_response)} not " \
53
- "to be paginated with keys #{@formatted_keys}"
90
+ "to be paginated with keys #{@assertion.formatted_keys}"
54
91
  end
55
92
  end
@@ -1,5 +1,5 @@
1
1
  module Wor
2
2
  module Paginate
3
- VERSION = '0.4.2'.freeze
3
+ VERSION = '0.5.0'.freeze
4
4
  end
5
5
  end
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.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - icoluccio
@@ -10,7 +10,7 @@ authors:
10
10
  - lucasVoboril
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2026-08-04 00:00:00.000000000 Z
13
+ date: 2026-08-30 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: railties
@@ -121,7 +121,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
121
121
  - !ruby/object:Gem::Version
122
122
  version: '0'
123
123
  requirements: []
124
- rubygems_version: 4.0.6
124
+ rubygems_version: 3.6.9
125
125
  specification_version: 4
126
126
  summary: Simplified pagination for Rails API controllers
127
127
  test_files: []