statesman 8.0.2 → 8.0.3

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: 1b67b01e36452d54ed3a46731198cf31b62bc4b96c658f1555c1246a859a369a
4
- data.tar.gz: d6bedd395500f7a079c8a7a3dda16c48f78b532dbd37bf9242644d1fb6f5186c
3
+ metadata.gz: 3d099ca7f60641163125a158ca189d41b571a46e4da8d5277a9391ff12df69e4
4
+ data.tar.gz: 5a33e698ded14cbc080f6d3e385d4d00d8191b5d1887e0b4fb062d592a35214f
5
5
  SHA512:
6
- metadata.gz: 573dd79df9f8bbf958cfca8bd2a2477b10904916d43917fb269b41f154bde0f40e8bda641f5984c56772d1aa918bbfd84f8a14be881917370167ee1f3c4d6324
7
- data.tar.gz: 9440bfaa20b003060c9dfa152d93c7142cff6ffa6ffc56ae52e966c1f96fdd95bd47d9e411987802bc986c7b6557223ce8696ffe446468735b360ed80d130ca3
6
+ metadata.gz: 1b7e63cd44278ca20c9aac95b6ff1372b83131d5df988b72d6fabfbbc98057917f33bc6cdbc447c2d1d04f075ac215e58eb5e0beb09d0fd7d2d16e672cebacd2
7
+ data.tar.gz: 63588d5869538bab0f4a7900f2761ed3bcfb124895485d2fc1667ec159763e4fad69327af25d7221f113f6c43396aabb3d8548085d6bb25229487c5d19ba11c0
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## UNRELEASED
2
+
3
+ ### Added
4
+ - Implement `Machine#last_transition_to`, to find the last transition to a given state
5
+ [#xxx](https://github.com/gocardless/statesman/pull/xxx)
6
+
1
7
  ## v8.0.2 30th March 2021
2
8
 
3
9
  ### Changed
data/README.md CHANGED
@@ -30,7 +30,7 @@ protection.
30
30
  To get started, just add Statesman to your `Gemfile`, and then run `bundle`:
31
31
 
32
32
  ```ruby
33
- gem 'statesman', '~> 7.1.0'
33
+ gem 'statesman', '~> 8.0.3'
34
34
  ```
35
35
 
36
36
  ## Usage
@@ -109,6 +109,8 @@ Order.first.state_machine.allowed_transitions # => ["checking_out", "cancelled"]
109
109
  Order.first.state_machine.can_transition_to?(:cancelled) # => true/false
110
110
  Order.first.state_machine.transition_to(:cancelled, optional: :metadata) # => true/false
111
111
  Order.first.state_machine.transition_to!(:cancelled) # => true/exception
112
+ Order.first.state_machine.last_transition # => transition model or nil
113
+ Order.first.state_machine.last_transition_to(:pending) # => transition model or nil
112
114
 
113
115
  Order.in_state(:cancelled) # => [#<Order id: "123">]
114
116
  Order.not_in_state(:checking_out) # => [#<Order id: "123">]
@@ -159,7 +161,8 @@ class Order < ActiveRecord::Base
159
161
 
160
162
  # Optionally delegate some methods
161
163
 
162
- delegate :can_transition_to?, :current_state, :history, :last_transition,
164
+ delegate :can_transition_to?,
165
+ :current_state, :history, :last_transition, :last_transition_to,
163
166
  :transition_to!, :transition_to, :in_state?, to: :state_machine
164
167
  end
165
168
  ```
@@ -335,6 +338,9 @@ Returns a sorted array of all transition objects.
335
338
  #### `Machine#last_transition`
336
339
  Returns the most recent transition object.
337
340
 
341
+ #### `Machine#last_transition_to(:state)`
342
+ Returns the most recent transition object to a given state.
343
+
338
344
  #### `Machine#allowed_transitions`
339
345
  Returns an array of states you can `transition_to` from current state.
340
346
 
@@ -351,6 +357,66 @@ Transition to the passed state, returning `true` on success. Swallows all
351
357
  Statesman exceptions and returns false on failure. (NB. if your guard or
352
358
  callback code throws an exception, it will not be caught.)
353
359
 
360
+
361
+ ## Errors
362
+
363
+ ### Initialization errors
364
+ These errors are raised when the Machine and/or Model is initialized. A simple spec like
365
+ ```ruby
366
+ expect { OrderStateMachine.new(Order.new, transition_class: OrderTransition) }.to_not raise_error
367
+ ```
368
+ will expose these errors as part of your test suite
369
+
370
+ #### InvalidStateError
371
+ Raised if:
372
+ * Attempting to define a transition without a `to` state.
373
+ * Attempting to define a transition with a non-existent state.
374
+ * Attempting to define multiple states as `initial`.
375
+
376
+ #### InvalidTransitionError
377
+ Raised if:
378
+ * Attempting to define a callback `from` a state that has no valid transitions (A terminal state).
379
+ * Attempting to define a callback `to` the `initial` state if that state has no transitions to it.
380
+ * Attempting to define a callback with `from` and `to` where any of the pairs have no transition between them.
381
+
382
+ #### InvalidCallbackError
383
+ Raised if:
384
+ * Attempting to define a callback without a block.
385
+
386
+ #### UnserializedMetadataError
387
+ Raised if:
388
+ * ActiveRecord is configured to not serialize the `metadata` attribute into
389
+ to Database column backing it. See the `Using PostgreSQL JSON column` section.
390
+
391
+ #### IncompatibleSerializationError
392
+ Raised if:
393
+ * There is a mismatch between the column type of the `metadata` in the
394
+ Database and the model. See the `Using PostgreSQL JSON column` section.
395
+
396
+ #### MissingTransitionAssociation
397
+ Raised if:
398
+ * The model that `Statesman::Adapters::ActiveRecordQueries` is included in
399
+ does not have a `has_many` association to the `transition_class`.
400
+
401
+ ### Runtime errors
402
+ These errors are raised by `transition_to!`. Using `transition_to` will
403
+ supress `GuardFailedError` and `TransitionFailedError` and return `false` instead.
404
+
405
+ #### GuardFailedError
406
+ Raised if:
407
+ * A guard callback between `from` and `to` state returned a falsey value.
408
+
409
+ #### TransitionFailedError
410
+ Raised if:
411
+ * A transition is attempted but `current_state -> new_state` is not a valid pair.
412
+
413
+ #### TransitionConflictError
414
+ Raised if:
415
+ * A database conflict affecting the `sort_key` or `most_recent` columns occurs
416
+ when attempting a transition.
417
+ Retried automatically if it occurs wrapped in `retry_conflicts`.
418
+
419
+
354
420
  ## Model scopes
355
421
 
356
422
  A mixin is provided for the ActiveRecord adapter which adds scopes to easily
@@ -209,6 +209,10 @@ module Statesman
209
209
  @storage_adapter.last(force_reload: force_reload)
210
210
  end
211
211
 
212
+ def last_transition_to(state)
213
+ history.reverse.find { |transition| transition.to_state.to_sym == state.to_sym }
214
+ end
215
+
212
216
  def can_transition_to?(new_state, metadata = {})
213
217
  validate_transition(from: current_state,
214
218
  to: new_state,
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Statesman
4
- VERSION = "8.0.2"
4
+ VERSION = "8.0.3"
5
5
  end
@@ -537,6 +537,34 @@ describe Statesman::Machine do
537
537
  end
538
538
  end
539
539
 
540
+ describe "#last_transition_to" do
541
+ subject { instance.last_transition_to(:y) }
542
+
543
+ before do
544
+ machine.class_eval do
545
+ state :x, initial: true
546
+ state :y
547
+ state :z
548
+ transition from: :x, to: :y
549
+ transition from: :y, to: :z
550
+ transition from: :z, to: :y
551
+ end
552
+
553
+ instance.transition_to!(:y)
554
+ instance.transition_to!(:z)
555
+ end
556
+
557
+ let(:instance) { machine.new(my_model) }
558
+
559
+ it { is_expected.to have_attributes(to_state: "y") }
560
+
561
+ context "when there are 2 transitions to the state" do
562
+ before { instance.transition_to!(:y) }
563
+
564
+ it { is_expected.to eq(instance.last_transition) }
565
+ end
566
+ end
567
+
540
568
  describe "#can_transition_to?" do
541
569
  subject(:can_transition_to?) { instance.can_transition_to?(new_state, metadata) }
542
570
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: statesman
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.0.2
4
+ version: 8.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - GoCardless
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-30 00:00:00.000000000 Z
11
+ date: 2021-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ammeter