rails_cursor_pagination 0.3.0 → 0.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
  SHA256:
3
- metadata.gz: 43aad1bc560c58779869951edd254017bd3d15c0909022df0aeecbdc5c48bcef
4
- data.tar.gz: 3ea26d6133869375d61644eebda2878dd6ccc65b906cbdc5bfa0294bfecd7a0c
3
+ metadata.gz: 2410928079b14757601fdbb84b8c516a99070f5eee181ab464beeeeb00f302b6
4
+ data.tar.gz: 7f7e5a25d3977ad18964d35d0e2715978c57ccfdfe8624d229bd275deeb1b542
5
5
  SHA512:
6
- metadata.gz: dadfee2340eafb20eb0cd5d2e93f76544386554f7e0bf09935915816af72185a2eec6930270883fdd9870b90270a32677e364811b3914c809ffd38a4a839a296
7
- data.tar.gz: ba8d85b9cbc57f546bf234f4d4653df7cfe1181cf78f05581c2f0fc4d6ed2a000d8adf39960346fec2b1074d66ab268b45c733d5c0b7e63d3830a54f12352a54
6
+ metadata.gz: 349ba3f27691109cc3e374dbc0c8340163b8af0b452f158e806f859b7b88e4f6ba7b88bb9a5b9a3c9080ccf53d00c100ba7e166f4b0aec2e2fa0c300591b7ff5
7
+ data.tar.gz: 802a9c37b80c59dc52c747d6727afa48650572448294114a1f5b70c6092a72d272beb51eeda50b13d2b009598396688eff4969e1d44f13bfa09f7a2f7097c490
data/CHANGELOG.md CHANGED
@@ -14,6 +14,18 @@ These are the latest changes on the project's `master` branch that have not yet
14
14
  Follow the same format as previous releases by categorizing your feature into "Added", "Changed", "Deprecated", "Removed", "Fixed", or "Security".
15
15
  --->
16
16
 
17
+ ## [0.4.0] - 2023-10-06
18
+
19
+ ### Changed
20
+ - **Breaking change:** Raised minimum required Ruby version to 2.7
21
+ - **Breaking change:** Raised minimum required `activerecord` version to 6.0
22
+
23
+ ### Added
24
+ - Test against Ruby version 3.2
25
+
26
+ ### Fixed
27
+ - **Breaking change:** Ensure timestamp `order_by` fields (like `created_at`) will paginate results by honoring timestamp order down to microsecond resolution on comparison. This was done by changing the cursor logic for timestamp fields, which means that the cursors strings change from version 0.3.0 to 0.4.0 and old cursors cannot be decoded by the new gem version anymore.
28
+
17
29
  ## [0.3.0] - 2022-07-08
18
30
 
19
31
  ### Added
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'base64'
4
+
3
5
  module RailsCursorPagination
4
6
  # Cursor class that's used to uniquely identify a record and serialize and
5
7
  # deserialize this cursor so that it can be used for pagination.
@@ -365,7 +365,7 @@ module RailsCursorPagination
365
365
  # @param record [ActiveRecord] Model instance for which we want the cursor
366
366
  # @return [String]
367
367
  def cursor_for_record(record)
368
- Cursor.from_record(record: record, order_field: @order_field).encode
368
+ cursor_class.from_record(record: record, order_field: @order_field).encode
369
369
  end
370
370
 
371
371
  # Decode the provided cursor. Either just returns the cursor's ID or in case
@@ -375,7 +375,25 @@ module RailsCursorPagination
375
375
  # @return [Integer, Array]
376
376
  def decoded_cursor
377
377
  memoize(:decoded_cursor) do
378
- Cursor.decode(encoded_string: @cursor, order_field: @order_field)
378
+ cursor_class.decode(encoded_string: @cursor, order_field: @order_field)
379
+ end
380
+ end
381
+
382
+ # Returns the appropriate class for the cursor based on the SQL type of the
383
+ # column used for ordering the relation.
384
+ #
385
+ # @return [Class<RailsCursorPagination::Cursor>]
386
+ def cursor_class
387
+ order_field_type = @relation
388
+ .column_for_attribute(@order_field)
389
+ .sql_type_metadata
390
+ .type
391
+
392
+ case order_field_type
393
+ when :datetime
394
+ TimestampCursor
395
+ else
396
+ Cursor
379
397
  end
380
398
  end
381
399
 
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RailsCursorPagination
4
+ # Cursor class that's used to uniquely identify a record and serialize and
5
+ # deserialize this cursor so that it can be used for pagination.
6
+ # This class expects the `order_field` of the record to be a timestamp and is
7
+ # to be used only when sorting a
8
+ class TimestampCursor < Cursor
9
+ class << self
10
+ # Decode the provided encoded cursor. Returns an instance of this
11
+ # `RailsCursorPagination::Cursor` class containing both the ID and the
12
+ # ordering field value. The ordering field is expected to be a timestamp
13
+ # and is always decoded in the UTC timezone.
14
+ #
15
+ # @param encoded_string [String]
16
+ # The encoded cursor
17
+ # @param order_field [Symbol]
18
+ # The column that is being ordered on. It needs to be a timestamp of a
19
+ # class that responds to `#strftime`.
20
+ # @raise [RailsCursorPagination::InvalidCursorError]
21
+ # In case the given `encoded_string` cannot be decoded properly
22
+ # @return [RailsCursorPagination::TimestampCursor]
23
+ # Instance of this class with a properly decoded timestamp cursor
24
+ def decode(encoded_string:, order_field:)
25
+ decoded = JSON.parse(Base64.strict_decode64(encoded_string))
26
+
27
+ new(
28
+ id: decoded[1],
29
+ order_field: order_field,
30
+ # Turn the order field value into a `Time` instance in UTC. A Rational
31
+ # number allows us to represent fractions of seconds, including the
32
+ # microseconds. In this way we can preserve the order of items with a
33
+ # microsecond precision.
34
+ # This also allows us to keep the size of the cursor small by using
35
+ # just a number instead of having to pass seconds and the fraction of
36
+ # seconds separately.
37
+ order_field_value: Time.at(decoded[0].to_r / (10**6)).utc
38
+ )
39
+ rescue ArgumentError, JSON::ParserError
40
+ raise InvalidCursorError,
41
+ "The given cursor `#{encoded_string}` " \
42
+ 'could not be decoded to a timestamp'
43
+ end
44
+ end
45
+
46
+ # Initializes the record. Overrides `Cursor`'s initializer making all params
47
+ # mandatory.
48
+ #
49
+ # @param id [Integer]
50
+ # The ID of the cursor record
51
+ # @param order_field [Symbol]
52
+ # The column or virtual column for ordering
53
+ # @param order_field_value [Object]
54
+ # The value that the +order_field+ of the record contains
55
+ def initialize(id:, order_field:, order_field_value:)
56
+ super id: id,
57
+ order_field: order_field,
58
+ order_field_value: order_field_value
59
+ end
60
+
61
+ # Encodes the cursor as an array containing the timestamp as microseconds
62
+ # from UNIX epoch and the id of the object
63
+ #
64
+ # @raise [RailsCursorPagination::ParameterError]
65
+ # The order field value needs to respond to `#strftime` to use the
66
+ # `TimestampCursor` class. Otherwise, a `ParameterError` is raised.
67
+ # @return [String]
68
+ def encode
69
+ unless @order_field_value.respond_to?(:strftime)
70
+ raise ParameterError,
71
+ "Could not encode #{@order_field} " \
72
+ "with value #{@order_field_value}." \
73
+ 'It does not respond to #strftime. Is it a timestamp?'
74
+ end
75
+
76
+ Base64.strict_encode64(
77
+ [
78
+ @order_field_value.strftime('%s%6N').to_i,
79
+ @id
80
+ ].to_json
81
+ )
82
+ end
83
+ end
84
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsCursorPagination
4
- VERSION = '0.3.0'
4
+ VERSION = '0.4.0'
5
5
  end
@@ -164,6 +164,8 @@ module RailsCursorPagination
164
164
 
165
165
  require_relative 'rails_cursor_pagination/cursor'
166
166
 
167
+ require_relative 'rails_cursor_pagination/timestamp_cursor'
168
+
167
169
  class << self
168
170
  # Allows to configure this gem. Currently supported configuration values
169
171
  # are:
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_cursor_pagination
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nicolas Fricke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-07-08 00:00:00.000000000 Z
11
+ date: 2023-10-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: '6.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5.0'
26
+ version: '6.0'
27
27
  description: This library is an implementation of cursor pagination for ActiveRecord
28
28
  relations. Where a regular limit & offset pagination has issues with items that
29
29
  are being deleted from or added to the collection on previous pages, cursor pagination
@@ -42,6 +42,7 @@ files:
42
42
  - lib/rails_cursor_pagination/configuration.rb
43
43
  - lib/rails_cursor_pagination/cursor.rb
44
44
  - lib/rails_cursor_pagination/paginator.rb
45
+ - lib/rails_cursor_pagination/timestamp_cursor.rb
45
46
  - lib/rails_cursor_pagination/version.rb
46
47
  homepage: https://github.com/xing/rails_cursor_pagination
47
48
  licenses:
@@ -59,14 +60,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
59
60
  requirements:
60
61
  - - ">="
61
62
  - !ruby/object:Gem::Version
62
- version: 2.6.0
63
+ version: 2.7.0
63
64
  required_rubygems_version: !ruby/object:Gem::Requirement
64
65
  requirements:
65
66
  - - ">="
66
67
  - !ruby/object:Gem::Version
67
68
  version: '0'
68
69
  requirements: []
69
- rubygems_version: 3.1.6
70
+ rubygems_version: 3.2.33
70
71
  signing_key:
71
72
  specification_version: 4
72
73
  summary: Add cursor pagination to your ActiveRecord backed application.