stepper_motor 0.1.2 → 0.1.3

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: 375de8ed8ab1ea1229ad1a8a99a2b0aeb3a0a0fd9b4397abcc2ba9c73968dac5
4
- data.tar.gz: acae28d6c2f058991f0a1a50b5b22a73fb54954309a74d8f8de73a6ce6da9597
3
+ metadata.gz: 2496495ef6f6748068d2a726432ef1e23c8c17880b464da61530457396342701
4
+ data.tar.gz: 003f7998b41ccf526038de029a0e839ebc9a413ddc7cf1fed47c2f2ec07e96e5
5
5
  SHA512:
6
- metadata.gz: ecb52e4b5b8160aefa6764a7b1f7275410f5337a4d09672c9a1ea9b729a2b0f2fc1f667c3a253187e8ac96b535a00cf3d9b7e83f7fbc4b6220e9ddfee7dbdb1a
7
- data.tar.gz: c6a28cdcd8935c4369c79ca34b0cdcf0b59066a818bd8da7c08e5f5b0435cfd462fb0de27030963abd484db8ef3897bd3cdf078058f6559f24151cba1c04fb99
6
+ metadata.gz: d522012c349187573966dacf65f90ecc476ccc1f34e20d818d7c4619b6804881491b9058361f16cf2eeb020a15b837645c76e2a951f8c5620dd3c9d6ba79e42f
7
+ data.tar.gz: 97a05abbd99ef244d5de94f37f50d10338e4618dd4b103a7efeffa5de4957e3e0dce854ebd8122f51b6e8c15d7a8735c108950df5f24adf6456f6136f767128e
data/README.md CHANGED
@@ -16,7 +16,7 @@ class SignupJourney < StepperMotor::Journey
16
16
  ServiceUpdateMailer.two_days_spent_email(hero).deliver_later
17
17
  end
18
18
 
19
- step :onboarding_complete_, wait: 15.days do
19
+ step :onboarding_complete, wait: 15.days do
20
20
  OnboardingCompleteMailer.onboarding_complete_email(hero).deliver_later
21
21
  end
22
22
  end
@@ -358,16 +358,16 @@ This creates a large number of jobs on your queue, but will be easier to manage.
358
358
  ```ruby
359
359
  StepperMotor.configure do |c|
360
360
  # Use jobs per journey step and enqueue them early
361
- c.schedule_via = :waiting_jobs
361
+ c.scheduler = StepperMotor::ForwardScheduler.new
362
362
  end
363
363
  ```
364
364
 
365
- or, for in-time scheduling
365
+ or, for cyclic scheduling (less jobs on the queue, but you need a decent scheduler for your background jobs to be present:
366
366
 
367
367
  ```ruby
368
368
  StepperMotor.configure do |c|
369
- # Use jobs per journey step and enqueue them early
370
- c.schedule_via = :central_task
369
+ # Check for jobs to be created every 5 minutes
370
+ c.scheduler = StepperMotor::CyclicScheduler.new((cycle_duration: 5.minutes)
371
371
  end
372
372
  ```
373
373
 
@@ -29,7 +29,8 @@ class StepperMotorMigration001 < ActiveRecord::Migration[<%= migration_version %
29
29
  add_index :stepper_motor_journeys, [:type, :hero_type, :hero_id]
30
30
 
31
31
  # A unique index prevents multiple journeys of the same type from being created for a particular hero
32
- add_index :stepper_motor_journeys, [:type, :hero_id, :hero_type], where: "allow_multiple = 'f' AND state IN ('ready', 'performing')", unique: true, name: :one_per_hero_index
32
+ quoted_false = connection.quote(false)
33
+ add_index :stepper_motor_journeys, [:type, :hero_id, :hero_type], where: "allow_multiple = '#{quoted_false}' AND state IN ('ready', 'performing')", unique: true, name: :one_per_hero_index
33
34
 
34
35
  # An index is also needed for cleaning up finished and canceled journeys quickly
35
36
  # for a specific hero of a specific class
@@ -13,8 +13,6 @@
13
13
  # this scheduler is not a good fit for you, and you will need to use the {CyclicScheduler} instead.
14
14
  class StepperMotor::ForwardScheduler
15
15
  def schedule(journey)
16
- wait = journey.next_step_to_be_performed_at - Time.current
17
- wait = 0 if wait.negative?
18
- StepperMotor::PerformStepJob.set(wait: wait).perform_later(journey.to_global_id.to_s)
16
+ StepperMotor::PerformStepJob.set(scheduled_at: journey.next_step_to_be_performed_at).perform_later(journey.to_global_id.to_s)
19
17
  end
20
18
  end
@@ -44,7 +44,7 @@ module StepperMotor
44
44
  belongs_to :hero, polymorphic: true, optional: true
45
45
 
46
46
  STATES = %w[ready performing canceled finished]
47
- enum state: STATES.zip(STATES).to_h, _default: "ready"
47
+ enum :state, STATES.zip(STATES).to_h, default: "ready"
48
48
 
49
49
  # Allows querying for journeys for this specific hero. This uses a scope for convenience as the hero
50
50
  # is referenced using it's global ID (same ID that ActiveJob uses for serialization)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module StepperMotor
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
@@ -24,6 +24,13 @@ module SideEffects
24
24
  end
25
25
 
26
26
  def self.touch!(name)
27
+ if Thread.current[:side_effects].nil?
28
+ raise <<~ERROR
29
+ The current thread locals do not contain :side_effects, which means that your job
30
+ is running on a different thread than the specs. This is probably due to bad configuration
31
+ of the ActiveJob test adapter.
32
+ ERROR
33
+ end
27
34
  Thread.current[:side_effects][name.to_s] = true
28
35
  end
29
36
 
data/spec/spec_helper.rb CHANGED
@@ -6,6 +6,7 @@ require "active_job"
6
6
  require "active_record"
7
7
  require "globalid"
8
8
  require_relative "helpers/side_effects"
9
+ require "fileutils"
9
10
 
10
11
  module StepperMotorRailtieTestHelpers
11
12
  def establish_test_connection
@@ -34,6 +35,33 @@ module StepperMotorRailtieTestHelpers
34
35
  ActiveRecord::Tasks::DatabaseTasks.root = fake_app_root
35
36
  ActiveRecord::Tasks::DatabaseTasks.migrate
36
37
  end
38
+ extend self
39
+ end
40
+
41
+ module ActiveSupportTestCaseMethodsStub
42
+ def self.included(into)
43
+ into.before(:each) { before_setup }
44
+ into.after(:each) { after_teardown }
45
+ end
46
+
47
+ def before_setup
48
+ # Blank implementation as TestCase modules super() into it
49
+ end
50
+
51
+ def after_teardown
52
+ # Blank implementation as TestCase modules super() into it
53
+ end
54
+ end
55
+
56
+ module JourneyDefinitionHelper
57
+ def create_journey_subclass(&blk)
58
+ # https://stackoverflow.com/questions/4113479/dynamic-class-definition-with-a-class-name
59
+ random_component = Random.hex(8)
60
+ random_name = "JourneySubclass#{random_component}"
61
+ klass = Class.new(StepperMotor::Journey, &blk)
62
+ Object.const_set(random_name, klass)
63
+ klass
64
+ end
37
65
  end
38
66
 
39
67
  RSpec.configure do |config|
@@ -50,4 +78,12 @@ RSpec.configure do |config|
50
78
  config.include ActiveSupport::Testing::TimeHelpers
51
79
  config.include StepperMotorRailtieTestHelpers
52
80
  config.include SideEffects::SpecHelper
81
+ config.include ActiveSupportTestCaseMethodsStub
82
+ config.include JourneyDefinitionHelper
83
+
84
+ config.before :suite do
85
+ StepperMotorRailtieTestHelpers.establish_test_connection
86
+ StepperMotorRailtieTestHelpers.run_generator
87
+ StepperMotorRailtieTestHelpers.run_migrations
88
+ end
53
89
  end
@@ -3,17 +3,8 @@ require_relative "../spec_helper"
3
3
  RSpec.describe "StepperMotor::CyclicScheduler" do
4
4
  include ActiveJob::TestHelper
5
5
 
6
- class FarFutureJourney < StepperMotor::Journey
7
- step :do_thing, wait: 40.minutes do
8
- raise "We do not test this so it should never run"
9
- end
10
- end
11
-
12
6
  before do
13
7
  @previous_scheduler = StepperMotor.scheduler
14
- establish_test_connection
15
- run_generator
16
- run_migrations
17
8
  StepperMotor::Journey.delete_all
18
9
  end
19
10
 
@@ -21,12 +12,22 @@ RSpec.describe "StepperMotor::CyclicScheduler" do
21
12
  StepperMotor.scheduler = @previous_scheduler
22
13
  end
23
14
 
15
+ def far_future_journey_class
16
+ @klass ||= begin
17
+ create_journey_subclass do
18
+ step :do_thing, wait: 40.minutes do
19
+ raise "We do not test this so it should never run"
20
+ end
21
+ end
22
+ end
23
+ end
24
+
24
25
  it "does not schedule a journey which is too far in the future" do
25
26
  scheduler = StepperMotor::CyclicScheduler.new(cycle_duration: 30.seconds)
26
27
  StepperMotor.scheduler = scheduler
27
28
 
28
- expect(scheduler).to receive(:schedule).with(instance_of(FarFutureJourney)).once.and_call_original
29
- _journey = FarFutureJourney.create!
29
+ expect(scheduler).to receive(:schedule).with(instance_of(far_future_journey_class)).once.and_call_original
30
+ _journey = far_future_journey_class.create!
30
31
 
31
32
  expect(scheduler).not_to receive(:schedule)
32
33
  scheduler.run_scheduling_cycle
@@ -36,8 +37,8 @@ RSpec.describe "StepperMotor::CyclicScheduler" do
36
37
  scheduler = StepperMotor::CyclicScheduler.new(cycle_duration: 40.minutes)
37
38
  StepperMotor.scheduler = scheduler
38
39
 
39
- expect(scheduler).to receive(:schedule).with(instance_of(FarFutureJourney)).once.and_call_original
40
- journey = FarFutureJourney.create!
40
+ expect(scheduler).to receive(:schedule).with(instance_of(far_future_journey_class)).once.and_call_original
41
+ journey = far_future_journey_class.create!
41
42
 
42
43
  expect(scheduler).to receive(:schedule).with(journey).and_call_original
43
44
  scheduler.run_scheduling_cycle
@@ -47,8 +48,8 @@ RSpec.describe "StepperMotor::CyclicScheduler" do
47
48
  scheduler = StepperMotor::CyclicScheduler.new(cycle_duration: 10.seconds)
48
49
  StepperMotor.scheduler = scheduler
49
50
 
50
- expect(scheduler).to receive(:schedule).with(instance_of(FarFutureJourney)).once.and_call_original
51
- journey = FarFutureJourney.create!
51
+ expect(scheduler).to receive(:schedule).with(instance_of(far_future_journey_class)).once.and_call_original
52
+ journey = far_future_journey_class.create!
52
53
  journey.update!(next_step_to_be_performed_at: 10.minutes.ago)
53
54
 
54
55
  expect(scheduler).to receive(:schedule).with(journey).and_call_original
@@ -4,44 +4,21 @@ require_relative "../spec_helper"
4
4
  RSpec.describe "StepperMotor::Journey" do
5
5
  include ActiveJob::TestHelper
6
6
 
7
- before :all do
8
- establish_test_connection
9
- run_generator
10
- run_migrations
11
- ActiveJob::Base.queue_adapter = :test
12
- ActiveJob::Base.logger = Logger.new(nil)
13
- end
14
-
15
- after :all do
16
- FileUtils.rm_rf(fake_app_root)
17
- end
18
-
19
- before :each do
20
- Thread.current[:stepper_motor_side_effects] = {}
21
- end
22
-
23
- after :each do
24
- # Remove all jobs that remain in the queue
25
- ActiveJob::Base.queue_adapter.enqueued_jobs.clear
26
- end
27
-
28
7
  it "allows an empty journey to be defined and performed to completion" do
29
- class PointlessJourney < StepperMotor::Journey
30
- end
31
-
32
- journey = PointlessJourney.create!
8
+ pointless_class = create_journey_subclass
9
+ journey = pointless_class.create!
33
10
  journey.perform_next_step!
34
11
  expect(journey).to be_finished
35
12
  end
36
13
 
37
14
  it "allows a journey consisting of one step to be defined and performed to completion" do
38
- class SingleStepJourney < StepperMotor::Journey
15
+ single_step_class = create_journey_subclass do
39
16
  step :do_thing do
40
17
  SideEffects.touch!("do_thing")
41
18
  end
42
19
  end
43
20
 
44
- journey = SingleStepJourney.create!
21
+ journey = single_step_class.create!
45
22
  expect(journey.next_step_to_be_performed_at).not_to be_nil
46
23
  journey.perform_next_step!
47
24
  expect(journey).to be_finished
@@ -51,16 +28,15 @@ RSpec.describe "StepperMotor::Journey" do
51
28
  it "allows a journey consisting of multiple named steps to be defined and performed to completion" do
52
29
  step_names = [:step1, :step2, :step3]
53
30
 
54
- # Use Class.new so that step_names can be passed into the block
55
- MultiStepJourney = Class.new(StepperMotor::Journey) do
56
- step_names.each do |step_name|
31
+ multi_step_journey_class = create_journey_subclass do
32
+ [:step1, :step2, :step3].each do |step_name|
57
33
  step step_name do
58
34
  SideEffects.touch!("from_#{step_name}")
59
35
  end
60
36
  end
61
37
  end
62
38
 
63
- journey = MultiStepJourney.create!
39
+ journey = multi_step_journey_class.create!
64
40
  expect(journey.next_step_name).to eq("step1")
65
41
 
66
42
  journey.perform_next_step!
@@ -82,7 +58,7 @@ RSpec.describe "StepperMotor::Journey" do
82
58
  end
83
59
 
84
60
  it "allows a journey consisting of multiple anonymous steps to be defined and performed to completion" do
85
- AnonymousStepsJourney = Class.new(StepperMotor::Journey) do
61
+ anonymous_steps_class = create_journey_subclass do
86
62
  3.times do |n|
87
63
  step do
88
64
  SideEffects.touch!("sidefx_#{n}")
@@ -90,7 +66,7 @@ RSpec.describe "StepperMotor::Journey" do
90
66
  end
91
67
  end
92
68
 
93
- journey = AnonymousStepsJourney.create!
69
+ journey = anonymous_steps_class.create!
94
70
  expect(journey.next_step_name).to eq("step_1")
95
71
 
96
72
  journey.perform_next_step!
@@ -112,27 +88,22 @@ RSpec.describe "StepperMotor::Journey" do
112
88
  end
113
89
 
114
90
  it "allows an arbitrary ActiveRecord to be attached as the hero" do
115
- class SomeOtherJourney < StepperMotor::Journey
116
- step do
117
- # nothing, but we need to have a step so that the journey doesn't get destroyed immediately after creation
118
- end
119
- end
120
-
121
- class CarryingJourney < StepperMotor::Journey
91
+ carried_journey_class = create_journey_subclass
92
+ carrier_journey_class = create_journey_subclass do
122
93
  step :only do
123
- raise "Incorrect" unless hero.instance_of?(SomeOtherJourney)
94
+ raise "Incorrect" unless hero.instance_of?(carried_journey_class)
124
95
  end
125
96
  end
126
97
 
127
- hero = SomeOtherJourney.create!
128
- journey = CarryingJourney.create!(hero: hero)
98
+ hero = carried_journey_class.create!
99
+ journey = carrier_journey_class.create!(hero: hero)
129
100
  expect {
130
101
  journey.perform_next_step!
131
102
  }.not_to raise_error
132
103
  end
133
104
 
134
105
  it "allows a journey where steps are delayed in time using wait:" do
135
- class TimelyJourney < StepperMotor::Journey
106
+ timely_journey_class = carrier_journey_class = create_journey_subclass do
136
107
  step wait: 10.hours do
137
108
  SideEffects.touch! "after_10_hours.txt"
138
109
  end
@@ -147,7 +118,7 @@ RSpec.describe "StepperMotor::Journey" do
147
118
  end
148
119
 
149
120
  freeze_time
150
- TimelyJourney.create!
121
+ timely_journey_class.create!
151
122
 
152
123
  expect {
153
124
  perform_enqueued_jobs
@@ -174,7 +145,7 @@ RSpec.describe "StepperMotor::Journey" do
174
145
  end
175
146
 
176
147
  it "allows a journey where steps are delayed in time using after:" do
177
- class TimelyJourneyUsingAfter < StepperMotor::Journey
148
+ journey_class = create_journey_subclass do
178
149
  step after: 10.hours do
179
150
  SideEffects.touch! "step1"
180
151
  end
@@ -188,36 +159,41 @@ RSpec.describe "StepperMotor::Journey" do
188
159
  end
189
160
  end
190
161
 
191
- TimelyJourneyUsingAfter.create!
162
+ timely_journey = journey_class.create!
192
163
  freeze_time
164
+
165
+ # Note that the "perform_enqueued_jobs" helper method performs the job even if
166
+ # its "scheduled_at" lies in the future. Presumably this is done so that testing is
167
+ # easier to do, but we check the time the journey was set to perform the next step at
168
+ # - and therefore a job which runs too early will produce another job that replaces it.
193
169
  expect { perform_enqueued_jobs }.to not_have_produced_any_side_effects
194
170
 
195
- travel 10.hours
196
- perform_enqueued_jobs
171
+ travel_to(timely_journey.next_step_to_be_performed_at + 1.second)
197
172
  expect { perform_enqueued_jobs }.to have_produced_side_effects_named("step1")
198
173
 
199
- travel 4.minutes
174
+ travel(4.minutes)
200
175
  expect { perform_enqueued_jobs }.to not_have_produced_any_side_effects
201
176
 
202
- travel 1.minutes
177
+ travel(1.minutes + 1.second)
203
178
  expect { perform_enqueued_jobs }.to have_produced_side_effects_named("step2")
204
179
  expect { perform_enqueued_jobs }.to have_produced_side_effects_named("step3")
180
+ expect(enqueued_jobs).to be_empty # Journey ended
205
181
  end
206
182
 
207
183
  it "tracks steps entered and completed using counters" do
208
- class FailingJourney < StepperMotor::Journey
184
+ failing = create_journey_subclass do
209
185
  step do
210
186
  raise "oops"
211
187
  end
212
188
  end
213
189
 
214
- class NotFailingJourney < StepperMotor::Journey
190
+ not_failing = create_journey_subclass do
215
191
  step do
216
192
  true # no-op
217
193
  end
218
194
  end
219
195
 
220
- failing_journey = FailingJourney.create!
196
+ failing_journey = failing.create!
221
197
  expect { failing_journey.perform_next_step! }.to raise_error(/oops/)
222
198
  expect(failing_journey.steps_entered).to eq(1)
223
199
  expect(failing_journey.steps_completed).to eq(0)
@@ -227,7 +203,7 @@ RSpec.describe "StepperMotor::Journey" do
227
203
  expect(failing_journey.steps_entered).to eq(2)
228
204
  expect(failing_journey.steps_completed).to eq(0)
229
205
 
230
- non_failing_journey = NotFailingJourney.create!
206
+ non_failing_journey = not_failing.create!
231
207
  non_failing_journey.perform_next_step!
232
208
  expect(non_failing_journey.steps_entered).to eq(1)
233
209
  expect(non_failing_journey.steps_completed).to eq(1)
@@ -235,7 +211,7 @@ RSpec.describe "StepperMotor::Journey" do
235
211
 
236
212
  it "does not allow invalid values for after: and wait:" do
237
213
  expect {
238
- class MisconfiguredJourney1 < StepperMotor::Journey
214
+ create_journey_subclass do
239
215
  step after: 10.hours do
240
216
  # pass
241
217
  end
@@ -247,7 +223,7 @@ RSpec.describe "StepperMotor::Journey" do
247
223
  }.to raise_error(ArgumentError)
248
224
 
249
225
  expect {
250
- class MisconfiguredJourney2 < StepperMotor::Journey
226
+ create_journey_subclass do
251
227
  step wait: -5.hours do
252
228
  # pass
253
229
  end
@@ -255,7 +231,7 @@ RSpec.describe "StepperMotor::Journey" do
255
231
  }.to raise_error(ArgumentError)
256
232
 
257
233
  expect {
258
- class MisconfiguredJourney3 < StepperMotor::Journey
234
+ create_journey_subclass do
259
235
  step after: 5.hours, wait: 2.seconds do
260
236
  # pass
261
237
  end
@@ -264,14 +240,14 @@ RSpec.describe "StepperMotor::Journey" do
264
240
  end
265
241
 
266
242
  it "allows a step to reattempt itself" do
267
- class DeferringJourney < StepperMotor::Journey
243
+ deferring = create_journey_subclass do
268
244
  step do
269
245
  reattempt! wait: 5.minutes
270
246
  raise "Should never be reached"
271
247
  end
272
248
  end
273
249
 
274
- journey = DeferringJourney.create!
250
+ journey = deferring.create!
275
251
  perform_enqueued_jobs
276
252
 
277
253
  journey.reload
@@ -289,7 +265,7 @@ RSpec.describe "StepperMotor::Journey" do
289
265
  end
290
266
 
291
267
  it "allows a journey consisting of multiple steps where the first step bails out to be defined and performed to the point of cancellation" do
292
- class InterruptedJourney < StepperMotor::Journey
268
+ interrupting = create_journey_subclass do
293
269
  step :step1 do
294
270
  SideEffects.touch!("step1_before_cancel")
295
271
  cancel!
@@ -301,7 +277,7 @@ RSpec.describe "StepperMotor::Journey" do
301
277
  end
302
278
  end
303
279
 
304
- journey = InterruptedJourney.create!
280
+ journey = interrupting.create!
305
281
  expect(journey.next_step_name).to eq("step1")
306
282
 
307
283
  perform_enqueued_jobs
@@ -311,32 +287,31 @@ RSpec.describe "StepperMotor::Journey" do
311
287
  end
312
288
 
313
289
  it "forbids multiple similar journeys for the same hero at the same time unless allow_multiple is set" do
314
- class SomeActor < StepperMotor::Journey
315
- end
316
- hero = SomeActor.create!
290
+ actor_class = create_journey_subclass
291
+ hero = actor_class.create!
317
292
 
318
- class ExclusiveJourney < StepperMotor::Journey
293
+ exclusive_journey_class = create_journey_subclass do
319
294
  step do
320
295
  raise "The step should never be entered as we are not testing the step itself here"
321
296
  end
322
297
  end
323
298
 
324
299
  expect {
325
- 2.times { ExclusiveJourney.create! }
300
+ 2.times { exclusive_journey_class.create! }
326
301
  }.not_to raise_error
327
302
 
328
303
  expect {
329
- 2.times { ExclusiveJourney.create!(hero: hero) }
304
+ 2.times { exclusive_journey_class.create!(hero: hero) }
330
305
  }.to raise_error(ActiveRecord::RecordNotUnique)
331
306
 
332
307
  expect {
333
- 2.times { ExclusiveJourney.create!(hero: hero, allow_multiple: true) }
308
+ 2.times { exclusive_journey_class.create!(hero: hero, allow_multiple: true) }
334
309
  }.not_to raise_error
335
310
  end
336
311
 
337
312
  it "forbids multiple steps with the same name within a journey" do
338
313
  expect {
339
- class RepeatedStepsJourney < StepperMotor::Journey
314
+ create_journey_subclass do
340
315
  step :foo do
341
316
  true
342
317
  end
@@ -349,7 +324,7 @@ RSpec.describe "StepperMotor::Journey" do
349
324
  end
350
325
 
351
326
  it "finishes the journey after perform_next_step" do
352
- class RapidlyFinishingJourney < StepperMotor::Journey
327
+ rapid = create_journey_subclass do
353
328
  step :one do
354
329
  true # no-op
355
330
  end
@@ -358,7 +333,7 @@ RSpec.describe "StepperMotor::Journey" do
358
333
  end
359
334
  end
360
335
 
361
- journey = RapidlyFinishingJourney.create!
336
+ journey = rapid.create!
362
337
  expect(journey).to be_ready
363
338
  journey.perform_next_step!
364
339
  expect(journey).to be_ready
@@ -367,7 +342,7 @@ RSpec.describe "StepperMotor::Journey" do
367
342
  end
368
343
 
369
344
  it "does not enter next step on a finished journey" do
370
- class NearInstantJourney < StepperMotor::Journey
345
+ near_instant = create_journey_subclass do
371
346
  step :one do
372
347
  finished!
373
348
  end
@@ -377,7 +352,7 @@ RSpec.describe "StepperMotor::Journey" do
377
352
  end
378
353
  end
379
354
 
380
- journey = NearInstantJourney.create!
355
+ journey = near_instant.create!
381
356
  expect(journey).to be_ready
382
357
  journey.perform_next_step!
383
358
  expect(journey).to be_finished
@@ -386,20 +361,20 @@ RSpec.describe "StepperMotor::Journey" do
386
361
  end
387
362
 
388
363
  it "raises an exception if a step changes the journey but does not save it" do
389
- class MutatingJourney < StepperMotor::Journey
364
+ mutating = create_journey_subclass do
390
365
  step :one do
391
366
  self.state = "canceled"
392
367
  end
393
368
  end
394
369
 
395
- journey = MutatingJourney.create!
370
+ journey = mutating.create!
396
371
  expect {
397
372
  journey.perform_next_step!
398
373
  }.to raise_error(StepperMotor::JourneyNotPersisted)
399
374
  end
400
375
 
401
376
  it "resets the instance variables after performing a step" do
402
- class SelfResettingJourney < StepperMotor::Journey
377
+ self_resetting = create_journey_subclass do
403
378
  step :one do
404
379
  raise unless @current_step_definition
405
380
  end
@@ -409,7 +384,7 @@ RSpec.describe "StepperMotor::Journey" do
409
384
  end
410
385
  end
411
386
 
412
- journey = SelfResettingJourney.create!
387
+ journey = self_resetting.create!
413
388
  expect { journey.perform_next_step! }.not_to raise_error
414
389
  expect(journey.instance_variable_get(:@current_step_definition)).to be_nil
415
390
 
@@ -10,22 +10,24 @@ RSpec.describe "StepperMotor::TestHelper" do
10
10
  run_migrations
11
11
  end
12
12
 
13
- class SpeedyJourney < StepperMotor::Journey
14
- step :step_1, wait: 40.minutes do
15
- SideEffects.touch!("step_1")
16
- end
17
-
18
- step :step_2, wait: 2.days do
19
- SideEffects.touch!("step_2")
20
- end
21
-
22
- step do
23
- SideEffects.touch!("step_3")
13
+ def speedy_journey_class
14
+ create_journey_subclass do
15
+ step :step_1, wait: 40.minutes do
16
+ SideEffects.touch!("step_1")
17
+ end
18
+
19
+ step :step_2, wait: 2.days do
20
+ SideEffects.touch!("step_2")
21
+ end
22
+
23
+ step do
24
+ SideEffects.touch!("step_3")
25
+ end
24
26
  end
25
27
  end
26
28
 
27
29
  it "speedruns the journey despite waits being configured" do
28
- journey = SpeedyJourney.create!
30
+ journey = speedy_journey_class.create!
29
31
  expect(journey).to be_ready
30
32
 
31
33
  expect {
@@ -34,7 +36,7 @@ RSpec.describe "StepperMotor::TestHelper" do
34
36
  end
35
37
 
36
38
  it "is able to perform a single step forcibly" do
37
- journey = SpeedyJourney.create!
39
+ journey = speedy_journey_class.create!
38
40
  expect(journey).to be_ready
39
41
 
40
42
  expect {
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: stepper_motor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Julik Tarkhanov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-01-01 00:00:00.000000000 Z
11
+ date: 2025-02-28 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: '6'
19
+ version: '7'
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: '6'
26
+ version: '7'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activejob
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -108,6 +108,20 @@ dependencies:
108
108
  - - "~>"
109
109
  - !ruby/object:Gem::Version
110
110
  version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
126
  name: standard
113
127
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +150,20 @@ dependencies:
136
150
  - - ">="
137
151
  - !ruby/object:Gem::Version
138
152
  version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: redcarpet
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
139
167
  description: Step workflows for Rails/ActiveRecord
140
168
  email:
141
169
  - me@julik.nl
@@ -173,14 +201,14 @@ files:
173
201
  - spec/stepper_motor/journey_spec.rb
174
202
  - spec/stepper_motor/test_helper_spec.rb
175
203
  - spec/stepper_motor_spec.rb
176
- homepage: https://github.com/stepper_motor/stepper_motor
204
+ homepage: https://steppermotor.dev
177
205
  licenses:
178
206
  - LGPL
179
207
  metadata:
180
208
  allowed_push_host: https://rubygems.org
181
- homepage_uri: https://github.com/stepper_motor/stepper_motor
182
- source_code_uri: https://github.com/stepper_motor/stepper_motor
183
- changelog_uri: https://github.com/stepper_motor/stepper_motor/CHANGELOG.md
209
+ homepage_uri: https://steppermotor.dev
210
+ source_code_uri: https://github.com/stepper-motor/stepper_motor
211
+ changelog_uri: https://github.com/stepper-motor/stepper_motor/blob/main/CHANGELOG.md
184
212
  post_install_message:
185
213
  rdoc_options: []
186
214
  require_paths: