has_state_machine 0.2.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: 825270788dd41e2ffaee9573eea5e6bda6b63a21d9520cfd7805070feff0fac3
4
- data.tar.gz: fd7679aafbd2aa7a21b522b99b13d7481d3b7bb8a345c4602380fdab581215d8
3
+ metadata.gz: eb504bbd9b6e978b3a62551273a74624cdb4d189545cb32bf45a7f060983e888
4
+ data.tar.gz: ac89964cd89cec827ac60c8fae3970d94329c0141c0078da7a409ff2f63547ff
5
5
  SHA512:
6
- metadata.gz: 5a6251974a28867af9fd6f589f6680e231ad3e6e864e44e0f7303677e3d82ea492956c2bb48adc9c04ed56308158f13bd54ab63cdb11f6f995525adc42b0da27
7
- data.tar.gz: 0a5db9a428f4c8f71f129c1c3133f50874f81be8933f5bc1177e0a2fb268c7ad1cd38427a2f2a398f91e721f71fdbf3ceaab094972953f3d2da4e16bb887e3d2
6
+ metadata.gz: e62938a674ba7e8bea155e536724220280458a32bfa4dc3bdeed402bba4067a54b253b76f017fec7a25b361e349b92d88daf00d8071464bc72814f96e1ae152d
7
+ data.tar.gz: cfc4cf9a5b21dc548cc4207979ac1325e5134dfe6d4fd3114ca6d623ac22ca8afcdc899cfea192030986f7ab42268405ebd6edc7201fab976752d9d6447879f9
data/README.md CHANGED
@@ -5,7 +5,18 @@
5
5
 
6
6
  HasStateMachine uses ruby classes to make creating a finite state machine for your ActiveRecord models a breeze.
7
7
 
8
+ ## Contents
9
+
10
+ - [HasStateMachine](#hasstatemachine)
11
+ - [Contents](#contents)
12
+ - [Installation](#installation)
13
+ - [Usage](#usage)
14
+ - [Advanced Usage](#advanced-usage)
15
+ - [Contributing](#contributing)
16
+ - [License](#license)
17
+
8
18
  ## Installation
19
+
9
20
  Add this line to your application's Gemfile:
10
21
 
11
22
  ```ruby
@@ -13,11 +24,13 @@ gem 'has_state_machine'
13
24
  ```
14
25
 
15
26
  And then execute:
27
+
16
28
  ```bash
17
29
  $ bundle
18
30
  ```
19
31
 
20
32
  Or install it yourself as:
33
+
21
34
  ```bash
22
35
  $ gem install has_state_machine
23
36
  ```
@@ -28,6 +41,7 @@ You must first use the `has_state_machine` macro to define your state machine at
28
41
  a high level. This includes defining the possible states for your object as well
29
42
  as some optional configuration should you want to change the default behavior of
30
43
  the state machine.
44
+
31
45
  ```ruby
32
46
  # By default, it is assumed that the "state" of the object is
33
47
  # stored in a string column named "status".
@@ -45,13 +59,13 @@ from `HasStateMachine::State`.
45
59
  module Workflow
46
60
  class Post::Draft < HasStateMachine::State
47
61
  # Define the possible transitions from the "draft" state
48
- transitions_to %i[published archived]
62
+ state_options transitions_to: %i[published archived]
49
63
  end
50
64
  end
51
65
 
52
66
  module Workflow
53
67
  class Post::Published < HasStateMachine::State
54
- transitions_to %i[archived]
68
+ state_options transitions_to: %i[archived]
55
69
 
56
70
  # Custom validations can be added to the state to ensure a transition is "valid"
57
71
  validate :title_exists?
@@ -70,15 +84,15 @@ module Workflow
70
84
  # There are callbacks for running logic before and after
71
85
  # a transition occurs.
72
86
  before_transition do
73
- Rails.logger.info "Post is being archived\n"
87
+ Rails.logger.info "== Post is being archived ==\n"
74
88
  end
75
89
 
76
90
  after_transition do
77
- Rails.logger.info "Post has been archived\n"
91
+ Rails.logger.info "== Post has been archived ==\n"
78
92
 
79
93
  # You can access the previous state of the object in
80
94
  # after_transition callbacks as well.
81
- Rails.logger.info "Transitioned from #{previous_state}\n"
95
+ Rails.logger.info "== Transitioned from #{previous_state} ==\n"
82
96
  end
83
97
  end
84
98
  end
@@ -95,10 +109,54 @@ post.status # => "draft"
95
109
  post.title = "Foobar"
96
110
  post.status.transition_to(:published) # => true
97
111
  post.status # => "published"
112
+
113
+ post.status.transition_to(:archived)
114
+ # == Post is being archived ==
115
+ # == Post has been archived ==
116
+ # == Transitioned from published ==
117
+ # => true
118
+ ```
119
+
120
+ ### Advanced Usage
121
+
122
+ Sometimes there may be a situation where you want to manually roll back a state change in one of the provided callbacks. To do this, add the `transactional: true` option to the `state_options` declaration and use the `rollback_transition` method in your callback. This will allow you to prevent the transition from persisting if something further down the line fails.
123
+
124
+ ```ruby
125
+ module Workflow
126
+ class Post::Archived < HasStateMachine::State
127
+ state_options transactional: true
128
+
129
+ after_transition do
130
+ rollback_transition unless notified_watchers?
131
+ end
132
+
133
+ private
134
+
135
+ def notified_watchers?
136
+ #...
137
+ end
138
+ end
139
+ end
98
140
  ```
99
141
 
100
142
  ## Contributing
101
- Coming Soon
143
+
144
+ Anyone is encouraged to help improve this project. Here are a few ways you can help:
145
+
146
+ - [Report bugs](https://github.com/encampment/has_state_machine/issues)
147
+ - Fix bugs and [submit pull requests](https://github.com/encampment/has_state_machine/pulls)
148
+ - Write, clarify, or fix documentation
149
+ - Suggest or add new features
150
+
151
+ To get started with development:
152
+
153
+ ```
154
+ git clone https://github.com/encampment/has_state_machine.git
155
+ cd has_state_machine
156
+ bundle install
157
+ bundle exec rake test
158
+ ```
102
159
 
103
160
  ## License
161
+
104
162
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -3,6 +3,7 @@
3
3
  require "has_state_machine/railtie"
4
4
  require "has_state_machine/core_ext/string"
5
5
  require "has_state_machine/definition"
6
+ require "has_state_machine/deprecation"
6
7
 
7
8
  module HasStateMachine
8
9
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/deprecation"
4
+
5
+ module HasStateMachine
6
+ Deprecation = ActiveSupport::Deprecation.new("1.0", "HasStateMachine")
7
+ end
@@ -16,8 +16,9 @@ module HasStateMachine
16
16
  define_model_callbacks :transition, only: %i[before after]
17
17
 
18
18
  ##
19
- # Retrieves the next available transitions for a given state.
20
- delegate :possible_transitions, :state, to: "self.class"
19
+ # possible_transitions - Retrieves the next available transitions for a given state.
20
+ # transactional? - Determines whether or not the transition should happen with a transactional block.
21
+ delegate :possible_transitions, :transactional?, :state, to: "self.class"
21
22
 
22
23
  ##
23
24
  # Add errors to the ActiveRecord object rather than the HasStateMachine::State
@@ -51,7 +52,13 @@ module HasStateMachine
51
52
  with_transition_options(options) do
52
53
  return false unless valid_transition?(desired_state.to_s)
53
54
 
54
- transitioned = state_instance(desired_state.to_s).perform_transition!
55
+ desired_state = state_instance(desired_state.to_s)
56
+
57
+ transitioned = if desired_state.transactional?
58
+ desired_state.perform_transactional_transition!
59
+ else
60
+ desired_state.perform_transition!
61
+ end
55
62
  end
56
63
 
57
64
  transitioned
@@ -66,8 +73,24 @@ module HasStateMachine
66
73
  end
67
74
  end
68
75
 
76
+ ##
77
+ # Makes the actual transition from one state to the next and
78
+ # runs the before and after transition callbacks in a transaction
79
+ # to allow for roll backs.
80
+ def perform_transactional_transition!
81
+ ActiveRecord::Base.transaction(requires_new: true, joinable: false) do
82
+ run_callbacks :transition do
83
+ rollback_transition unless object.update("#{object.state_attribute}": state)
84
+ end
85
+ end
86
+ end
87
+
69
88
  private
70
89
 
90
+ def rollback_transition
91
+ raise ActiveRecord::Rollback
92
+ end
93
+
71
94
  ##
72
95
  # Determines if the given desired state exists in the predetermined
73
96
  # list of allowed transitions.
@@ -111,11 +134,25 @@ module HasStateMachine
111
134
  to_s.demodulize.underscore
112
135
  end
113
136
 
137
+ def transactional?
138
+ @transactional || false
139
+ end
140
+
114
141
  ##
115
142
  # Setter for the HasStateMachine::State classes to define the possible
116
143
  # states the current state can transition to.
117
144
  def transitions_to(states)
118
- @possible_transitions = states.map(&:to_s)
145
+ state_options(transitions_to: states)
146
+ HasStateMachine::Deprecation.deprecation_warning(:transitions_to, "use state_options instead")
147
+ end
148
+
149
+ ##
150
+ # Set the options for the HasStateMachine::State classes to define the possible
151
+ # states the current state can transition to and whether or not transitioning
152
+ # to the state should be performed within a transaction.
153
+ def state_options(transitions_to: [], transactional: false)
154
+ @possible_transitions = transitions_to.map(&:to_s)
155
+ @transactional = transactional
119
156
  end
120
157
  end
121
158
  end
@@ -55,7 +55,9 @@ module HasStateMachine
55
55
  # @example Retreiving a users published posts
56
56
  # > Post.published.where(user: user)
57
57
  # #=> [#<Post>]
58
- scope state, -> { where("#{state_attribute} = ?", state) }
58
+ if defined?(ActiveRecord) && (self < ActiveRecord::Base)
59
+ scope state, -> { where("#{state_attribute} = ?", state) }
60
+ end
59
61
 
60
62
  ##
61
63
  # Defines boolean helpers to determine if the active state matches
@@ -76,7 +78,7 @@ module HasStateMachine
76
78
  # Getter for the current state of the model based on the configured state
77
79
  # attribute.
78
80
  def current_state
79
- self[state_attribute]
81
+ attributes.with_indifferent_access[state_attribute]
80
82
  end
81
83
 
82
84
  ##
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module HasStateMachine
4
- VERSION = "0.2.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has_state_machine
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Benjamin Hargett
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2020-11-05 00:00:00.000000000 Z
12
+ date: 2021-04-03 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -110,6 +110,7 @@ files:
110
110
  - lib/has_state_machine.rb
111
111
  - lib/has_state_machine/core_ext/string.rb
112
112
  - lib/has_state_machine/definition.rb
113
+ - lib/has_state_machine/deprecation.rb
113
114
  - lib/has_state_machine/railtie.rb
114
115
  - lib/has_state_machine/state.rb
115
116
  - lib/has_state_machine/state_helpers.rb