job-iteration 1.17.0 → 1.18.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: 657313f73892dc576ba595499f00d97a211936bdbec570db64e72c7431aa0a11
4
- data.tar.gz: f12f068e6a83efc6fd702e4c5068c8b428587ec2b94eb9087c00e7141f689dee
3
+ metadata.gz: 4eeecd22600aa17ab6d0cee8d7ce0b5df6cd7bc984facb2a2b16025d7432881f
4
+ data.tar.gz: b08981a5c35feba0d9077dffd45147ea16838c87c2fc2c3de2c4798b90c3ce26
5
5
  SHA512:
6
- metadata.gz: dd60d3982a00af5e2193739c0e9e62adee8e08414628fa19a9f1d6d85f4ad1c5d9cbbd3b5a931b695fccdd50d60a5592a420a04c6eaa446f3b5cbf2e53beadb9
7
- data.tar.gz: 0e4c271a6a72a11bafcb5bb3fd362f88c2a90ee8b1ddb622c7053c0b0a8eed6b78b2420db0a28d448d7ce12026ad6313d409441567b30acc8e46a46a3243cfdc
6
+ metadata.gz: 57e13085c92355e548c35a96aa8506ab084444dbc8c83857dc1b7e234dc91783cb517613be43d90a9a03ee93f668cecc890a6bb4cfac1dcc835431004f565062
7
+ data.tar.gz: 338fef9982f317132a3f46681c015d9790d98a688e773ad34519a1821acf35e44ea6c4fda2f8b2b7b0e863a533d97aecde2426f478d8329a1129a59e2ad9b8eb
data/CHANGELOG.md CHANGED
@@ -16,6 +16,12 @@ nil
16
16
 
17
17
  nil
18
18
 
19
+ ## v1.18.0 (Nov 6, 2026)
20
+
21
+ ### Features
22
+
23
+ - [760](https://github.com/Shopify/job-iteration/pull/760) - Add the `serialize_cursor` and `deserialize_cursor` hooks, which a job overrides to decide how its cursor is stored in the job payload and rebuilt when it resumes; for example through `ActiveJob::Arguments`, so a `Time` or a custom id class survives the trip through JSON. The defaults keep the cursor as it is.
24
+
19
25
  ## v1.17.0 (Oct 27, 2026)
20
26
 
21
27
  ### Features
@@ -94,7 +94,7 @@ module JobIteration
94
94
 
95
95
  def serialize # @private
96
96
  super.merge(
97
- "cursor_position" => cursor_position,
97
+ "cursor_position" => serialize_cursor(cursor_position),
98
98
  "times_interrupted" => times_interrupted,
99
99
  "total_time" => total_time,
100
100
  )
@@ -102,11 +102,42 @@ module JobIteration
102
102
 
103
103
  def deserialize(job_data) # @private
104
104
  super
105
- self.cursor_position = job_data["cursor_position"]
105
+ self.cursor_position = deserialize_cursor(job_data["cursor_position"])
106
106
  self.times_interrupted = Integer(job_data["times_interrupted"] || 0)
107
107
  self.total_time = Float(job_data["total_time"] || 0.0)
108
108
  end
109
109
 
110
+ # Converts the cursor into the form stored in the job payload when the job is interrupted. The queue
111
+ # backend serializes that payload, typically as JSON, so by default a cursor has to be made of values
112
+ # JSON round-trips as the same objects: Strings, Integers, Floats, Arrays, Hashes, true, false, or nil.
113
+ #
114
+ # Override this method together with #deserialize_cursor to persist cursors of other types. Active Job's
115
+ # argument serialization is a natural choice: it already handles Time, Date, BigDecimal, Symbol and any
116
+ # class with an ActiveJob::Serializers::ObjectSerializer, and leaves JSON-native values untouched.
117
+ #
118
+ # def serialize_cursor(cursor)
119
+ # ActiveJob::Arguments.serialize([cursor]).first
120
+ # end
121
+ #
122
+ # def deserialize_cursor(cursor)
123
+ # ActiveJob::Arguments.deserialize([cursor]).first
124
+ # end
125
+ #
126
+ # @param cursor [Object] the cursor the enumerator yielded last
127
+ # @return [Object] what to store in the job payload
128
+ def serialize_cursor(cursor)
129
+ cursor
130
+ end
131
+
132
+ # Rebuilds the cursor from the form #serialize_cursor stored in the job payload, before it is passed to
133
+ # #build_enumerator as +cursor:+. The default returns it unchanged.
134
+ #
135
+ # @param cursor [Object] the stored cursor, as the queue backend deserialized it
136
+ # @return [Object] the cursor to resume from
137
+ def deserialize_cursor(cursor)
138
+ cursor
139
+ end
140
+
110
141
  def perform(...) # @private
111
142
  interruptible_perform(...)
112
143
 
@@ -184,7 +215,7 @@ module JobIteration
184
215
 
185
216
  enumerator.each do |object_from_enumerator, cursor_from_enumerator|
186
217
  # Deferred until 2.0.0
187
- # assert_valid_cursor!(cursor_from_enumerator)
218
+ # assert_valid_cursor!(serialize_cursor(cursor_from_enumerator))
188
219
 
189
220
  tags = instrumentation_tags.merge(cursor_position: cursor_from_enumerator)
190
221
  ActiveSupport::Notifications.instrument("each_iteration.iteration", tags) do
@@ -312,12 +343,14 @@ module JobIteration
312
343
 
313
344
  # The adapter must be able to serialize and deserialize the cursor back into an equivalent object.
314
345
  # https://github.com/mperham/sidekiq/wiki/Best-Practices#1-make-your-job-parameters-small-and-simple
346
+ # Validated on the result of #serialize_cursor, which is what reaches the adapter.
315
347
  def assert_valid_cursor!(cursor)
316
348
  return if serializable?(cursor)
317
349
 
318
350
  raise CursorError.new(
319
351
  "Cursor must be composed of objects capable of built-in (de)serialization: " \
320
- "Strings, Integers, Floats, Arrays, Hashes, true, false, or nil.",
352
+ "Strings, Integers, Floats, Arrays, Hashes, true, false, or nil. " \
353
+ "Override #serialize_cursor and #deserialize_cursor to persist other types.",
321
354
  cursor: cursor,
322
355
  )
323
356
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module JobIteration
4
- VERSION = "1.17.0"
4
+ VERSION = "1.18.0"
5
5
  end
@@ -23,7 +23,7 @@ module Tapioca
23
23
 
24
24
  sig { override.void }
25
25
  def decorate
26
- return unless constant.instance_methods(false).include?(:build_enumerator)
26
+ return unless constant.method_defined?(:build_enumerator, false)
27
27
 
28
28
  root.create_path(constant) do |job|
29
29
  method = constant.instance_method(:build_enumerator)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: job-iteration
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.17.0
4
+ version: 1.18.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify