rails_state_machine 1.1.0 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/test.yml +63 -0
- data/.ruby-version +1 -1
- data/CHANGELOG.md +44 -0
- data/Gemfile.5.1.pg.lock +3 -3
- data/Gemfile.5.2.pg.lock +6 -4
- data/Gemfile.6.0.pg +15 -0
- data/Gemfile.6.0.pg.lock +175 -0
- data/Gemfile.6.1.pg +15 -0
- data/Gemfile.6.1.pg.lock +181 -0
- data/README.md +48 -41
- data/lib/rails_state_machine.rb +2 -0
- data/lib/rails_state_machine/callbacks.rb +68 -0
- data/lib/rails_state_machine/event.rb +1 -1
- data/lib/rails_state_machine/model.rb +57 -7
- data/lib/rails_state_machine/state_machine.rb +42 -134
- data/lib/rails_state_machine/state_manager.rb +47 -0
- data/lib/rails_state_machine/version.rb +1 -1
- metadata +10 -5
- data/.travis.yml +0 -27
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: d01a1094865abf2d8451c41a75bb4da8b5c1f457d46c88580045cf13ac5af2ea
|
4
|
+
data.tar.gz: f7575ce50426110e387c438974a30ed4b3511413506243f50537e15abd1baef6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50ebcbd93b785b373721dc3d5ae6d84108c2a7d7942ce573f454c17198a63654a0782ea5bd4a797fb5220276af17addee8788d240ec4314df505aeced00dee1a
|
7
|
+
data.tar.gz: d28ec9396d3b28a2ba7b4741039e4ff2b9936fec67d0b7f7d3be89e62c4ac68f739871ed422b1b2a1f260f0fb422e55b0bd8b5da8d7cfc6073d973e70a293a90
|
@@ -0,0 +1,63 @@
|
|
1
|
+
---
|
2
|
+
name: Tests
|
3
|
+
'on':
|
4
|
+
push:
|
5
|
+
branches:
|
6
|
+
- master
|
7
|
+
pull_request:
|
8
|
+
branches:
|
9
|
+
- master
|
10
|
+
jobs:
|
11
|
+
test_pg:
|
12
|
+
runs-on: ubuntu-20.04
|
13
|
+
services:
|
14
|
+
postgres:
|
15
|
+
image: postgres:9.3
|
16
|
+
env:
|
17
|
+
POSTGRES_PASSWORD: postgres
|
18
|
+
options: "--health-cmd pg_isready --health-interval 10s --health-timeout 5s
|
19
|
+
--health-retries 5"
|
20
|
+
ports:
|
21
|
+
- 5432:5432
|
22
|
+
strategy:
|
23
|
+
fail-fast: false
|
24
|
+
matrix:
|
25
|
+
include:
|
26
|
+
- ruby: 2.3.8
|
27
|
+
gemfile: Gemfile.5.1.pg
|
28
|
+
- ruby: 2.4.6
|
29
|
+
gemfile: Gemfile.5.1.pg
|
30
|
+
- ruby: 2.3.8
|
31
|
+
gemfile: Gemfile.5.2.pg
|
32
|
+
- ruby: 2.4.6
|
33
|
+
gemfile: Gemfile.5.2.pg
|
34
|
+
- ruby: 2.5.6
|
35
|
+
gemfile: Gemfile.5.2.pg
|
36
|
+
- ruby: 2.5.6
|
37
|
+
gemfile: Gemfile.6.0.pg
|
38
|
+
- ruby: 2.6.6
|
39
|
+
gemfile: Gemfile.6.0.pg
|
40
|
+
- ruby: 2.7.2
|
41
|
+
gemfile: Gemfile.6.0.pg
|
42
|
+
- ruby: 2.7.2
|
43
|
+
gemfile: Gemfile.6.1.pg
|
44
|
+
- ruby: 3.0.0
|
45
|
+
gemfile: Gemfile.6.1.pg
|
46
|
+
env:
|
47
|
+
BUNDLE_GEMFILE: "${{ matrix.gemfile }}"
|
48
|
+
steps:
|
49
|
+
- uses: actions/checkout@v2
|
50
|
+
- name: Install ruby
|
51
|
+
uses: ruby/setup-ruby@v1
|
52
|
+
with:
|
53
|
+
ruby-version: "${{ matrix.ruby }}"
|
54
|
+
- name: Setup database
|
55
|
+
run: |
|
56
|
+
sudo apt-get install -y postgresql-client
|
57
|
+
PGPASSWORD=postgres psql -c 'create database rails_state_machine_test;' -U postgres -p 5432 -h localhost
|
58
|
+
- name: Bundle
|
59
|
+
run: |
|
60
|
+
gem install bundler:2.1.4
|
61
|
+
bundle install --no-deployment
|
62
|
+
- name: Run tests
|
63
|
+
run: bundle exec rspec
|
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.4.
|
1
|
+
2.4.6
|
data/CHANGELOG.md
CHANGED
@@ -3,6 +3,50 @@ All notable changes to this project will be documented in this file.
|
|
3
3
|
|
4
4
|
This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
5
5
|
|
6
|
+
## Unreleased
|
7
|
+
|
8
|
+
### Compatible changes
|
9
|
+
|
10
|
+
### Breaking changes
|
11
|
+
|
12
|
+
|
13
|
+
## 2.1.0 2021-03-30
|
14
|
+
|
15
|
+
### Compatible changes
|
16
|
+
|
17
|
+
- Added support for Ruby 3.0.
|
18
|
+
|
19
|
+
## 2.0.0 2019-09-30
|
20
|
+
|
21
|
+
### Compatible changes
|
22
|
+
|
23
|
+
- Added: State machine can now use an attribute other than `state` to represent the machine's state.
|
24
|
+
- Added: It is now possible to define multiple state machines on the same model. States and event names
|
25
|
+
have to differ, though.
|
26
|
+
|
27
|
+
### Breaking changes
|
28
|
+
|
29
|
+
- Removed: Dropped support for adding a state machine to a model without including `RailsStateMachine::Model`.
|
30
|
+
|
31
|
+
|
32
|
+
## 1.1.3 2019-08-12
|
33
|
+
|
34
|
+
### Compatible changes
|
35
|
+
|
36
|
+
- Fix a bug sometimes causing unsaved changes to be lost on state transitions.
|
37
|
+
|
38
|
+
## 1.1.2 2019-03-22
|
39
|
+
|
40
|
+
### Compatible changes
|
41
|
+
|
42
|
+
- Fix bug where state was set to an older state when making a record invalid after successfully transitioning to a new state.
|
43
|
+
|
44
|
+
## 1.1.1 2019-03-22
|
45
|
+
|
46
|
+
### Compatible changes
|
47
|
+
|
48
|
+
- Fix bug where state was set to `nil` by calling `valid?` on invalid records without making a state transition.
|
49
|
+
|
6
50
|
## 1.1.0 2019-02-07
|
7
51
|
|
8
52
|
### Compatible changes
|
data/Gemfile.5.1.pg.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rails_state_machine (
|
4
|
+
rails_state_machine (2.1.0)
|
5
5
|
activerecord
|
6
6
|
|
7
7
|
GEM
|
@@ -53,7 +53,7 @@ GEM
|
|
53
53
|
database_cleaner (1.6.2)
|
54
54
|
diff-lcs (1.3)
|
55
55
|
erubi (1.7.0)
|
56
|
-
gemika (0.
|
56
|
+
gemika (0.5.0)
|
57
57
|
globalid (0.4.1)
|
58
58
|
activesupport (>= 4.2.0)
|
59
59
|
i18n (0.9.1)
|
@@ -146,4 +146,4 @@ DEPENDENCIES
|
|
146
146
|
rspec (~> 3.5)
|
147
147
|
|
148
148
|
BUNDLED WITH
|
149
|
-
|
149
|
+
2.2.3
|
data/Gemfile.5.2.pg.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rails_state_machine (
|
4
|
+
rails_state_machine (2.1.0)
|
5
5
|
activerecord
|
6
6
|
|
7
7
|
GEM
|
@@ -57,7 +57,7 @@ GEM
|
|
57
57
|
database_cleaner (1.7.0)
|
58
58
|
diff-lcs (1.3)
|
59
59
|
erubi (1.7.1)
|
60
|
-
gemika (0.
|
60
|
+
gemika (0.5.0)
|
61
61
|
globalid (0.4.1)
|
62
62
|
activesupport (>= 4.2.0)
|
63
63
|
i18n (1.1.0)
|
@@ -70,7 +70,9 @@ GEM
|
|
70
70
|
marcel (0.3.2)
|
71
71
|
mimemagic (~> 0.3.2)
|
72
72
|
method_source (0.9.0)
|
73
|
-
mimemagic (0.3.
|
73
|
+
mimemagic (0.3.10)
|
74
|
+
nokogiri (~> 1)
|
75
|
+
rake
|
74
76
|
mini_mime (1.0.1)
|
75
77
|
mini_portile2 (2.3.0)
|
76
78
|
minitest (5.11.3)
|
@@ -154,4 +156,4 @@ DEPENDENCIES
|
|
154
156
|
rspec (~> 3.5)
|
155
157
|
|
156
158
|
BUNDLED WITH
|
157
|
-
|
159
|
+
2.2.15
|
data/Gemfile.6.0.pg
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Runtime dependencies
|
4
|
+
gem 'rails', '~>6.0.0'
|
5
|
+
gem 'pg'
|
6
|
+
|
7
|
+
# Development dependencies
|
8
|
+
gem 'rspec', '~>3.5'
|
9
|
+
gem 'rake'
|
10
|
+
gem 'pry-byebug'
|
11
|
+
gem 'gemika'
|
12
|
+
gem 'database_cleaner'
|
13
|
+
|
14
|
+
# Gem under test
|
15
|
+
gem 'rails_state_machine', :path => '.'
|
data/Gemfile.6.0.pg.lock
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rails_state_machine (2.1.0)
|
5
|
+
activerecord
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actioncable (6.0.0)
|
11
|
+
actionpack (= 6.0.0)
|
12
|
+
nio4r (~> 2.0)
|
13
|
+
websocket-driver (>= 0.6.1)
|
14
|
+
actionmailbox (6.0.0)
|
15
|
+
actionpack (= 6.0.0)
|
16
|
+
activejob (= 6.0.0)
|
17
|
+
activerecord (= 6.0.0)
|
18
|
+
activestorage (= 6.0.0)
|
19
|
+
activesupport (= 6.0.0)
|
20
|
+
mail (>= 2.7.1)
|
21
|
+
actionmailer (6.0.0)
|
22
|
+
actionpack (= 6.0.0)
|
23
|
+
actionview (= 6.0.0)
|
24
|
+
activejob (= 6.0.0)
|
25
|
+
mail (~> 2.5, >= 2.5.4)
|
26
|
+
rails-dom-testing (~> 2.0)
|
27
|
+
actionpack (6.0.0)
|
28
|
+
actionview (= 6.0.0)
|
29
|
+
activesupport (= 6.0.0)
|
30
|
+
rack (~> 2.0)
|
31
|
+
rack-test (>= 0.6.3)
|
32
|
+
rails-dom-testing (~> 2.0)
|
33
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
34
|
+
actiontext (6.0.0)
|
35
|
+
actionpack (= 6.0.0)
|
36
|
+
activerecord (= 6.0.0)
|
37
|
+
activestorage (= 6.0.0)
|
38
|
+
activesupport (= 6.0.0)
|
39
|
+
nokogiri (>= 1.8.5)
|
40
|
+
actionview (6.0.0)
|
41
|
+
activesupport (= 6.0.0)
|
42
|
+
builder (~> 3.1)
|
43
|
+
erubi (~> 1.4)
|
44
|
+
rails-dom-testing (~> 2.0)
|
45
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
46
|
+
activejob (6.0.0)
|
47
|
+
activesupport (= 6.0.0)
|
48
|
+
globalid (>= 0.3.6)
|
49
|
+
activemodel (6.0.0)
|
50
|
+
activesupport (= 6.0.0)
|
51
|
+
activerecord (6.0.0)
|
52
|
+
activemodel (= 6.0.0)
|
53
|
+
activesupport (= 6.0.0)
|
54
|
+
activestorage (6.0.0)
|
55
|
+
actionpack (= 6.0.0)
|
56
|
+
activejob (= 6.0.0)
|
57
|
+
activerecord (= 6.0.0)
|
58
|
+
marcel (~> 0.3.1)
|
59
|
+
activesupport (6.0.0)
|
60
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
61
|
+
i18n (>= 0.7, < 2)
|
62
|
+
minitest (~> 5.1)
|
63
|
+
tzinfo (~> 1.1)
|
64
|
+
zeitwerk (~> 2.1, >= 2.1.8)
|
65
|
+
builder (3.2.3)
|
66
|
+
byebug (10.0.2)
|
67
|
+
coderay (1.1.2)
|
68
|
+
concurrent-ruby (1.1.5)
|
69
|
+
crass (1.0.4)
|
70
|
+
database_cleaner (1.7.0)
|
71
|
+
diff-lcs (1.3)
|
72
|
+
erubi (1.9.0)
|
73
|
+
gemika (0.5.0)
|
74
|
+
globalid (0.4.2)
|
75
|
+
activesupport (>= 4.2.0)
|
76
|
+
i18n (1.6.0)
|
77
|
+
concurrent-ruby (~> 1.0)
|
78
|
+
loofah (2.3.0)
|
79
|
+
crass (~> 1.0.2)
|
80
|
+
nokogiri (>= 1.5.9)
|
81
|
+
mail (2.7.1)
|
82
|
+
mini_mime (>= 0.1.1)
|
83
|
+
marcel (0.3.3)
|
84
|
+
mimemagic (~> 0.3.2)
|
85
|
+
method_source (0.9.2)
|
86
|
+
mimemagic (0.3.10)
|
87
|
+
nokogiri (~> 1)
|
88
|
+
rake
|
89
|
+
mini_mime (1.0.2)
|
90
|
+
mini_portile2 (2.4.0)
|
91
|
+
minitest (5.12.2)
|
92
|
+
nio4r (2.5.2)
|
93
|
+
nokogiri (1.10.4)
|
94
|
+
mini_portile2 (~> 2.4.0)
|
95
|
+
pg (1.1.2)
|
96
|
+
pry (0.11.3)
|
97
|
+
coderay (~> 1.1.0)
|
98
|
+
method_source (~> 0.9.0)
|
99
|
+
pry-byebug (3.6.0)
|
100
|
+
byebug (~> 10.0)
|
101
|
+
pry (~> 0.10)
|
102
|
+
rack (2.0.7)
|
103
|
+
rack-test (1.1.0)
|
104
|
+
rack (>= 1.0, < 3)
|
105
|
+
rails (6.0.0)
|
106
|
+
actioncable (= 6.0.0)
|
107
|
+
actionmailbox (= 6.0.0)
|
108
|
+
actionmailer (= 6.0.0)
|
109
|
+
actionpack (= 6.0.0)
|
110
|
+
actiontext (= 6.0.0)
|
111
|
+
actionview (= 6.0.0)
|
112
|
+
activejob (= 6.0.0)
|
113
|
+
activemodel (= 6.0.0)
|
114
|
+
activerecord (= 6.0.0)
|
115
|
+
activestorage (= 6.0.0)
|
116
|
+
activesupport (= 6.0.0)
|
117
|
+
bundler (>= 1.3.0)
|
118
|
+
railties (= 6.0.0)
|
119
|
+
sprockets-rails (>= 2.0.0)
|
120
|
+
rails-dom-testing (2.0.3)
|
121
|
+
activesupport (>= 4.2.0)
|
122
|
+
nokogiri (>= 1.6)
|
123
|
+
rails-html-sanitizer (1.2.0)
|
124
|
+
loofah (~> 2.2, >= 2.2.2)
|
125
|
+
railties (6.0.0)
|
126
|
+
actionpack (= 6.0.0)
|
127
|
+
activesupport (= 6.0.0)
|
128
|
+
method_source
|
129
|
+
rake (>= 0.8.7)
|
130
|
+
thor (>= 0.20.3, < 2.0)
|
131
|
+
rake (13.0.0)
|
132
|
+
rspec (3.8.0)
|
133
|
+
rspec-core (~> 3.8.0)
|
134
|
+
rspec-expectations (~> 3.8.0)
|
135
|
+
rspec-mocks (~> 3.8.0)
|
136
|
+
rspec-core (3.8.0)
|
137
|
+
rspec-support (~> 3.8.0)
|
138
|
+
rspec-expectations (3.8.1)
|
139
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
140
|
+
rspec-support (~> 3.8.0)
|
141
|
+
rspec-mocks (3.8.0)
|
142
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
143
|
+
rspec-support (~> 3.8.0)
|
144
|
+
rspec-support (3.8.0)
|
145
|
+
sprockets (3.7.2)
|
146
|
+
concurrent-ruby (~> 1.0)
|
147
|
+
rack (> 1, < 3)
|
148
|
+
sprockets-rails (3.2.1)
|
149
|
+
actionpack (>= 4.0)
|
150
|
+
activesupport (>= 4.0)
|
151
|
+
sprockets (>= 3.0.0)
|
152
|
+
thor (0.20.3)
|
153
|
+
thread_safe (0.3.6)
|
154
|
+
tzinfo (1.2.5)
|
155
|
+
thread_safe (~> 0.1)
|
156
|
+
websocket-driver (0.7.1)
|
157
|
+
websocket-extensions (>= 0.1.0)
|
158
|
+
websocket-extensions (0.1.4)
|
159
|
+
zeitwerk (2.1.10)
|
160
|
+
|
161
|
+
PLATFORMS
|
162
|
+
ruby
|
163
|
+
|
164
|
+
DEPENDENCIES
|
165
|
+
database_cleaner
|
166
|
+
gemika
|
167
|
+
pg
|
168
|
+
pry-byebug
|
169
|
+
rails (~> 6.0.0)
|
170
|
+
rails_state_machine!
|
171
|
+
rake
|
172
|
+
rspec (~> 3.5)
|
173
|
+
|
174
|
+
BUNDLED WITH
|
175
|
+
2.1.4
|
data/Gemfile.6.1.pg
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source 'https://rubygems.org'
|
2
|
+
|
3
|
+
# Runtime dependencies
|
4
|
+
gem 'rails', '~>6.1.0'
|
5
|
+
gem 'pg'
|
6
|
+
|
7
|
+
# Development dependencies
|
8
|
+
gem 'rspec', '~>3.5'
|
9
|
+
gem 'rake'
|
10
|
+
gem 'pry-byebug'
|
11
|
+
gem 'gemika'
|
12
|
+
gem 'database_cleaner'
|
13
|
+
|
14
|
+
# Gem under test
|
15
|
+
gem 'rails_state_machine', :path => '.'
|
data/Gemfile.6.1.pg.lock
ADDED
@@ -0,0 +1,181 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rails_state_machine (2.1.0)
|
5
|
+
activerecord
|
6
|
+
|
7
|
+
GEM
|
8
|
+
remote: https://rubygems.org/
|
9
|
+
specs:
|
10
|
+
actioncable (6.1.3.1)
|
11
|
+
actionpack (= 6.1.3.1)
|
12
|
+
activesupport (= 6.1.3.1)
|
13
|
+
nio4r (~> 2.0)
|
14
|
+
websocket-driver (>= 0.6.1)
|
15
|
+
actionmailbox (6.1.3.1)
|
16
|
+
actionpack (= 6.1.3.1)
|
17
|
+
activejob (= 6.1.3.1)
|
18
|
+
activerecord (= 6.1.3.1)
|
19
|
+
activestorage (= 6.1.3.1)
|
20
|
+
activesupport (= 6.1.3.1)
|
21
|
+
mail (>= 2.7.1)
|
22
|
+
actionmailer (6.1.3.1)
|
23
|
+
actionpack (= 6.1.3.1)
|
24
|
+
actionview (= 6.1.3.1)
|
25
|
+
activejob (= 6.1.3.1)
|
26
|
+
activesupport (= 6.1.3.1)
|
27
|
+
mail (~> 2.5, >= 2.5.4)
|
28
|
+
rails-dom-testing (~> 2.0)
|
29
|
+
actionpack (6.1.3.1)
|
30
|
+
actionview (= 6.1.3.1)
|
31
|
+
activesupport (= 6.1.3.1)
|
32
|
+
rack (~> 2.0, >= 2.0.9)
|
33
|
+
rack-test (>= 0.6.3)
|
34
|
+
rails-dom-testing (~> 2.0)
|
35
|
+
rails-html-sanitizer (~> 1.0, >= 1.2.0)
|
36
|
+
actiontext (6.1.3.1)
|
37
|
+
actionpack (= 6.1.3.1)
|
38
|
+
activerecord (= 6.1.3.1)
|
39
|
+
activestorage (= 6.1.3.1)
|
40
|
+
activesupport (= 6.1.3.1)
|
41
|
+
nokogiri (>= 1.8.5)
|
42
|
+
actionview (6.1.3.1)
|
43
|
+
activesupport (= 6.1.3.1)
|
44
|
+
builder (~> 3.1)
|
45
|
+
erubi (~> 1.4)
|
46
|
+
rails-dom-testing (~> 2.0)
|
47
|
+
rails-html-sanitizer (~> 1.1, >= 1.2.0)
|
48
|
+
activejob (6.1.3.1)
|
49
|
+
activesupport (= 6.1.3.1)
|
50
|
+
globalid (>= 0.3.6)
|
51
|
+
activemodel (6.1.3.1)
|
52
|
+
activesupport (= 6.1.3.1)
|
53
|
+
activerecord (6.1.3.1)
|
54
|
+
activemodel (= 6.1.3.1)
|
55
|
+
activesupport (= 6.1.3.1)
|
56
|
+
activestorage (6.1.3.1)
|
57
|
+
actionpack (= 6.1.3.1)
|
58
|
+
activejob (= 6.1.3.1)
|
59
|
+
activerecord (= 6.1.3.1)
|
60
|
+
activesupport (= 6.1.3.1)
|
61
|
+
marcel (~> 1.0.0)
|
62
|
+
mini_mime (~> 1.0.2)
|
63
|
+
activesupport (6.1.3.1)
|
64
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
65
|
+
i18n (>= 1.6, < 2)
|
66
|
+
minitest (>= 5.1)
|
67
|
+
tzinfo (~> 2.0)
|
68
|
+
zeitwerk (~> 2.3)
|
69
|
+
builder (3.2.4)
|
70
|
+
byebug (11.1.3)
|
71
|
+
coderay (1.1.3)
|
72
|
+
concurrent-ruby (1.1.8)
|
73
|
+
crass (1.0.6)
|
74
|
+
database_cleaner (2.0.1)
|
75
|
+
database_cleaner-active_record (~> 2.0.0)
|
76
|
+
database_cleaner-active_record (2.0.0)
|
77
|
+
activerecord (>= 5.a)
|
78
|
+
database_cleaner-core (~> 2.0.0)
|
79
|
+
database_cleaner-core (2.0.1)
|
80
|
+
diff-lcs (1.4.4)
|
81
|
+
erubi (1.10.0)
|
82
|
+
gemika (0.5.0)
|
83
|
+
globalid (0.4.2)
|
84
|
+
activesupport (>= 4.2.0)
|
85
|
+
i18n (1.8.9)
|
86
|
+
concurrent-ruby (~> 1.0)
|
87
|
+
loofah (2.9.0)
|
88
|
+
crass (~> 1.0.2)
|
89
|
+
nokogiri (>= 1.5.9)
|
90
|
+
mail (2.7.1)
|
91
|
+
mini_mime (>= 0.1.1)
|
92
|
+
marcel (1.0.0)
|
93
|
+
method_source (1.0.0)
|
94
|
+
mini_mime (1.0.3)
|
95
|
+
mini_portile2 (2.5.0)
|
96
|
+
minitest (5.14.4)
|
97
|
+
nio4r (2.5.7)
|
98
|
+
nokogiri (1.11.2)
|
99
|
+
mini_portile2 (~> 2.5.0)
|
100
|
+
racc (~> 1.4)
|
101
|
+
pg (1.2.3)
|
102
|
+
pry (0.13.1)
|
103
|
+
coderay (~> 1.1)
|
104
|
+
method_source (~> 1.0)
|
105
|
+
pry-byebug (3.9.0)
|
106
|
+
byebug (~> 11.0)
|
107
|
+
pry (~> 0.13.0)
|
108
|
+
racc (1.5.2)
|
109
|
+
rack (2.2.3)
|
110
|
+
rack-test (1.1.0)
|
111
|
+
rack (>= 1.0, < 3)
|
112
|
+
rails (6.1.3.1)
|
113
|
+
actioncable (= 6.1.3.1)
|
114
|
+
actionmailbox (= 6.1.3.1)
|
115
|
+
actionmailer (= 6.1.3.1)
|
116
|
+
actionpack (= 6.1.3.1)
|
117
|
+
actiontext (= 6.1.3.1)
|
118
|
+
actionview (= 6.1.3.1)
|
119
|
+
activejob (= 6.1.3.1)
|
120
|
+
activemodel (= 6.1.3.1)
|
121
|
+
activerecord (= 6.1.3.1)
|
122
|
+
activestorage (= 6.1.3.1)
|
123
|
+
activesupport (= 6.1.3.1)
|
124
|
+
bundler (>= 1.15.0)
|
125
|
+
railties (= 6.1.3.1)
|
126
|
+
sprockets-rails (>= 2.0.0)
|
127
|
+
rails-dom-testing (2.0.3)
|
128
|
+
activesupport (>= 4.2.0)
|
129
|
+
nokogiri (>= 1.6)
|
130
|
+
rails-html-sanitizer (1.3.0)
|
131
|
+
loofah (~> 2.3)
|
132
|
+
railties (6.1.3.1)
|
133
|
+
actionpack (= 6.1.3.1)
|
134
|
+
activesupport (= 6.1.3.1)
|
135
|
+
method_source
|
136
|
+
rake (>= 0.8.7)
|
137
|
+
thor (~> 1.0)
|
138
|
+
rake (13.0.3)
|
139
|
+
rspec (3.10.0)
|
140
|
+
rspec-core (~> 3.10.0)
|
141
|
+
rspec-expectations (~> 3.10.0)
|
142
|
+
rspec-mocks (~> 3.10.0)
|
143
|
+
rspec-core (3.10.1)
|
144
|
+
rspec-support (~> 3.10.0)
|
145
|
+
rspec-expectations (3.10.1)
|
146
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
147
|
+
rspec-support (~> 3.10.0)
|
148
|
+
rspec-mocks (3.10.2)
|
149
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
150
|
+
rspec-support (~> 3.10.0)
|
151
|
+
rspec-support (3.10.2)
|
152
|
+
sprockets (4.0.2)
|
153
|
+
concurrent-ruby (~> 1.0)
|
154
|
+
rack (> 1, < 3)
|
155
|
+
sprockets-rails (3.2.2)
|
156
|
+
actionpack (>= 4.0)
|
157
|
+
activesupport (>= 4.0)
|
158
|
+
sprockets (>= 3.0.0)
|
159
|
+
thor (1.1.0)
|
160
|
+
tzinfo (2.0.4)
|
161
|
+
concurrent-ruby (~> 1.0)
|
162
|
+
websocket-driver (0.7.3)
|
163
|
+
websocket-extensions (>= 0.1.0)
|
164
|
+
websocket-extensions (0.1.5)
|
165
|
+
zeitwerk (2.4.2)
|
166
|
+
|
167
|
+
PLATFORMS
|
168
|
+
ruby
|
169
|
+
|
170
|
+
DEPENDENCIES
|
171
|
+
database_cleaner
|
172
|
+
gemika
|
173
|
+
pg
|
174
|
+
pry-byebug
|
175
|
+
rails (~> 6.1.0)
|
176
|
+
rails_state_machine!
|
177
|
+
rake
|
178
|
+
rspec (~> 3.5)
|
179
|
+
|
180
|
+
BUNDLED WITH
|
181
|
+
2.2.3
|
data/README.md
CHANGED
@@ -1,5 +1,4 @@
|
|
1
|
-
# Rails State Machine
|
2
|
-
[![Build Status](https://travis-ci.org/makandra/rails_state_machine.svg?branch=master)](https://travis-ci.org/makandra/rails_state_machine)
|
1
|
+
# Rails State Machine [![Tests](https://github.com/makandra/rails_state_machine/workflows/Tests/badge.svg)](https://github.com/makandra/rails_state_machine/actions?query=branch:master)
|
3
2
|
|
4
3
|
Rails State Machine is a ActiveRecord-bound state machine.
|
5
4
|
|
@@ -71,39 +70,6 @@ event :request_feedback do
|
|
71
70
|
end
|
72
71
|
```
|
73
72
|
|
74
|
-
As an alternative to using `RailsStateMachine::Model` and `state_machine do`, configure the state machine manually. This only adds the `state_machine` to your model, but no `states` or `state_events`.
|
75
|
-
|
76
|
-
```ruby
|
77
|
-
class YourModel < ApplicationRecord
|
78
|
-
RailsStateMachine::StateMachine.new(self).configure do
|
79
|
-
state :draft, initial: true
|
80
|
-
state :review_pending
|
81
|
-
state :approved
|
82
|
-
state :rejected
|
83
|
-
|
84
|
-
event :request_review do
|
85
|
-
transitions from: [:draft, :rejected], to: :review_pending
|
86
|
-
end
|
87
|
-
|
88
|
-
event :approve do
|
89
|
-
transitions from: :review_pending, to: :approved
|
90
|
-
end
|
91
|
-
|
92
|
-
event :reject do
|
93
|
-
transitions from: :review_pending, to: :rejected
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
def self.states
|
98
|
-
state_machine.state_names
|
99
|
-
end
|
100
|
-
|
101
|
-
def self.state_events
|
102
|
-
state_machine.event_names
|
103
|
-
end
|
104
|
-
end
|
105
|
-
```
|
106
|
-
|
107
73
|
## Event callbacks
|
108
74
|
|
109
75
|
Here is a list with all the available callbacks, listed in the same order in which they will get called during the respective operations. The callbacks are chained with the existing active record callbacks on the model.
|
@@ -137,19 +103,60 @@ event :request_review do
|
|
137
103
|
end
|
138
104
|
```
|
139
105
|
|
106
|
+
## Other state attributes and multiple state machines on the same model
|
107
|
+
|
108
|
+
To use a state attribute other than the default `state`, pass it to the `.state_machine` method:
|
109
|
+
|
110
|
+
```
|
111
|
+
state_machine :review_state do
|
112
|
+
# ...
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
This also allows you to define multiple state machines on the same model. Note that event
|
117
|
+
and state names still have to be unique for the whole model.
|
118
|
+
|
119
|
+
|
120
|
+
## Taking multiple transitions
|
121
|
+
|
122
|
+
You can safely take a second transition inside an after_save callback. All relevant
|
123
|
+
callbacks will be run.
|
124
|
+
|
125
|
+
```
|
126
|
+
state_machine do
|
127
|
+
state :draft, initial: true
|
128
|
+
state :review_pending
|
129
|
+
state :approved
|
130
|
+
|
131
|
+
event :request_review do
|
132
|
+
transitions from: [:draft, :rejected], to: :review_pending
|
133
|
+
|
134
|
+
after_save do
|
135
|
+
if auto_approve?
|
136
|
+
approve
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
event :approve do
|
142
|
+
transitions from: :review_pending, to: :approved
|
143
|
+
end
|
144
|
+
end
|
145
|
+
```
|
146
|
+
|
140
147
|
## Development
|
141
148
|
|
142
149
|
There are tests in `spec`. We only accept PRs with tests. To run tests:
|
143
150
|
|
144
|
-
- Install Ruby 2.4.
|
151
|
+
- Install Ruby 2.4.6
|
145
152
|
- Copy the file `spec/support/database.sample.yml` to `spec/support/database.yml` and enter your PostgreSQL credentials. You can create the database afterwards with `createdb rails_state_machine_test`.
|
146
153
|
- Run `bin/setup` to install development dependencies.
|
147
154
|
- Run tests using `bundle exec rspec`
|
148
155
|
|
149
|
-
We recommend to test large changes against multiple versions of Ruby and multiple dependency sets. Supported combinations are configured in `.
|
156
|
+
We recommend to test large changes against multiple versions of Ruby and multiple dependency sets. Supported combinations are configured in `.github/workflows/test.yml`. We provide some rake tasks to help with this:
|
150
157
|
|
151
|
-
- Install development dependencies using `
|
152
|
-
- Run tests using `
|
158
|
+
- Install development dependencies using `rake matrix:install`
|
159
|
+
- Run tests using `rake matrix:spec`
|
153
160
|
|
154
161
|
You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
155
162
|
|
@@ -167,7 +174,7 @@ If you would like to contribute:
|
|
167
174
|
|
168
175
|
We want to keep this gem leightweight and on topic. If you are unsure whether a change would make it into the gem, open an issue and discuss.
|
169
176
|
|
170
|
-
Note that we have configured
|
177
|
+
Note that we have configured GitHub Actions to automatically run tests in all supported Ruby versions and dependency sets after each push. We will only merge pull requests after a green workflow build.
|
171
178
|
|
172
179
|
## License
|
173
180
|
|
@@ -175,4 +182,4 @@ The gem is available as open source under the terms of the [MIT License](https:/
|
|
175
182
|
|
176
183
|
## Credits
|
177
184
|
|
178
|
-
Arne Hartherz
|
185
|
+
Arne Hartherz, Emanuel Denzel, Tobias Kraze from [makandra](https://makandra.de/).
|
data/lib/rails_state_machine.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
require 'rails_state_machine/version'
|
2
2
|
require 'rails_state_machine/state'
|
3
3
|
require 'rails_state_machine/event'
|
4
|
+
require 'rails_state_machine/callbacks'
|
4
5
|
require 'rails_state_machine/state_machine'
|
6
|
+
require 'rails_state_machine/state_manager'
|
5
7
|
require 'rails_state_machine/model'
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module RailsStateMachine
|
2
|
+
module Callbacks
|
3
|
+
class << self
|
4
|
+
def included(model)
|
5
|
+
register_callbacks(model)
|
6
|
+
register_validations(model)
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
def register_callbacks(model)
|
12
|
+
model.class_eval do
|
13
|
+
before_validation :run_state_events_before_validation
|
14
|
+
before_save :register_state_events_for_callbacks
|
15
|
+
before_save { flush_state_event_callbacks(:before_save) }
|
16
|
+
after_save { flush_state_event_callbacks(:after_save) }
|
17
|
+
after_commit { flush_state_event_callbacks(:after_commit) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def register_validations(model)
|
22
|
+
model.class_eval do
|
23
|
+
after_validation :revert_states, if: -> { errors.any? }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def run_state_events_before_validation
|
29
|
+
# Since validations may be skipped, we will not register validation callbacks in @state_event_callbacks,
|
30
|
+
# but call them explicitly when before_validation callbacks are triggered.
|
31
|
+
state_machine_state_managers.each do |state_manager|
|
32
|
+
state_manager.next_event&.run_before_validation(self)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def register_state_events_for_callbacks
|
37
|
+
@state_event_callbacks ||= {
|
38
|
+
before_save: [],
|
39
|
+
after_save: [],
|
40
|
+
after_commit: []
|
41
|
+
}
|
42
|
+
state_machine_state_managers.each do |state_manager|
|
43
|
+
if (next_event = state_manager.next_event)
|
44
|
+
@state_event_callbacks[:before_save] << next_event
|
45
|
+
@state_event_callbacks[:after_save] << next_event
|
46
|
+
@state_event_callbacks[:after_commit] << next_event
|
47
|
+
state_manager.next_event = nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
true
|
52
|
+
end
|
53
|
+
|
54
|
+
def flush_state_event_callbacks(name)
|
55
|
+
if @state_event_callbacks
|
56
|
+
while (event = @state_event_callbacks[name].shift)
|
57
|
+
event.public_send("run_#{name}", self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def revert_states
|
63
|
+
state_machine_state_managers.each do |state_manager|
|
64
|
+
state_manager.revert
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -1,21 +1,71 @@
|
|
1
1
|
module RailsStateMachine
|
2
|
+
DEFAULT_STATE_ATTRIBUTE = :state
|
3
|
+
|
2
4
|
module Model
|
3
5
|
def self.included(base)
|
4
|
-
base.
|
6
|
+
base.class_eval do
|
7
|
+
extend ClassMethods
|
8
|
+
|
9
|
+
cattr_accessor :state_machines
|
10
|
+
self.state_machines = {}
|
11
|
+
|
12
|
+
delegate :state_machine, to: :class
|
13
|
+
end
|
5
14
|
end
|
6
15
|
|
7
16
|
module ClassMethods
|
8
|
-
def state_machine(&block)
|
9
|
-
StateMachine.new(self)
|
17
|
+
def state_machine(state_attribute = DEFAULT_STATE_ATTRIBUTE, &block)
|
18
|
+
state_machine = state_machines[state_attribute] ||= StateMachine.new(self, state_attribute)
|
19
|
+
if block
|
20
|
+
include(Callbacks) unless self < Callbacks
|
21
|
+
state_machine.configure(&block)
|
22
|
+
end
|
23
|
+
state_machine
|
24
|
+
end
|
25
|
+
|
26
|
+
def states(state_attribute = DEFAULT_STATE_ATTRIBUTE)
|
27
|
+
state_machine(state_attribute).state_names
|
28
|
+
end
|
29
|
+
|
30
|
+
def state_events(state_attribute = DEFAULT_STATE_ATTRIBUTE)
|
31
|
+
state_machine(state_attribute).event_names
|
10
32
|
end
|
33
|
+
end
|
34
|
+
|
35
|
+
|
36
|
+
private
|
11
37
|
|
12
|
-
|
13
|
-
|
38
|
+
def state_machine_state_manager(state_attribute)
|
39
|
+
@state_machine_state_managers ||= {}
|
40
|
+
@state_machine_state_managers[state_attribute] ||= StateManager.new(self, state_machine(state_attribute), state_attribute)
|
41
|
+
end
|
42
|
+
|
43
|
+
def state_machine_state_managers
|
44
|
+
self.state_machines.keys.collect do |state_attribute|
|
45
|
+
state_machine_state_manager(state_attribute)
|
14
46
|
end
|
47
|
+
end
|
15
48
|
|
16
|
-
|
17
|
-
|
49
|
+
def prepare_state_event_change(attributes)
|
50
|
+
if ActiveRecord::VERSION::STRING < '5.2' && saved_changes?
|
51
|
+
# After calling `save`, ActiveRecord 5.1 will flag the changes that it just stored as saved.
|
52
|
+
# https://github.com/rails/rails/blob/v5.1.4/activerecord/lib/active_record/attribute_methods/dirty.rb#L33-L46
|
53
|
+
#
|
54
|
+
# When taking multiple state events (e.g. a second event called inside an `after_save` callback) and thus
|
55
|
+
# saving after other changes were just saved, we need to mimic that behavior. Otherwise, ActiveRecord will
|
56
|
+
# print deprecation warnings like these:
|
57
|
+
#
|
58
|
+
# DEPRECATION WARNING: The behavior of `attribute_was` inside of after callbacks will be changing in the
|
59
|
+
# next version of Rails. The new return value will reflect the behavior of calling the method after
|
60
|
+
# `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use
|
61
|
+
# `attribute_before_last_save` instead.
|
62
|
+
#
|
63
|
+
# These actually originate from ActiveRecord internals which try to determine the changes that should be
|
64
|
+
# stored for the second save. It is probably a shortcoming of ActiveRecord 5.1.x that will be fixed, but
|
65
|
+
# since the current/previous save was already successful, the right action is to just call `changes_applied`.
|
66
|
+
changes_applied
|
18
67
|
end
|
68
|
+
self.attributes = attributes
|
19
69
|
end
|
20
70
|
end
|
21
71
|
end
|
@@ -1,13 +1,13 @@
|
|
1
1
|
module RailsStateMachine
|
2
2
|
class StateMachine
|
3
|
-
|
4
|
-
@model = model
|
5
|
-
|
6
|
-
model_constant('StateMachineMethods', Module.new)
|
7
|
-
@model.include(@model::StateMachineMethods)
|
3
|
+
attr_reader :model
|
8
4
|
|
5
|
+
def initialize(model, state_attribute)
|
6
|
+
@model = model
|
7
|
+
@state_attribute = state_attribute
|
9
8
|
@states_by_name = {}
|
10
9
|
@events_by_name = {}
|
10
|
+
build_model_module
|
11
11
|
end
|
12
12
|
|
13
13
|
def configure(&block)
|
@@ -18,11 +18,6 @@ module RailsStateMachine
|
|
18
18
|
register_initial_state
|
19
19
|
|
20
20
|
define_event_methods
|
21
|
-
|
22
|
-
register_callbacks
|
23
|
-
register_validations
|
24
|
-
register_state_machine
|
25
|
-
|
26
21
|
define_model_methods
|
27
22
|
end
|
28
23
|
|
@@ -53,25 +48,13 @@ module RailsStateMachine
|
|
53
48
|
private
|
54
49
|
|
55
50
|
def state(name, **options)
|
56
|
-
@states_by_name[name] = State.new(name, options)
|
51
|
+
@states_by_name[name] = State.new(name, **options)
|
57
52
|
end
|
58
53
|
|
59
54
|
def event(name, &block)
|
60
55
|
event = Event.new(name, self)
|
61
56
|
event.configure(&block)
|
62
57
|
|
63
|
-
model_methods do
|
64
|
-
define_method "#{event.name}" do |**attributes|
|
65
|
-
prepare_state_event_change(attributes.merge(state_event: event.name))
|
66
|
-
save
|
67
|
-
end
|
68
|
-
|
69
|
-
define_method "#{event.name}!" do |**attributes|
|
70
|
-
prepare_state_event_change(attributes.merge(state_event: event.name))
|
71
|
-
save!
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
58
|
@events_by_name[name] = event
|
76
59
|
end
|
77
60
|
|
@@ -79,18 +62,24 @@ module RailsStateMachine
|
|
79
62
|
@model.const_set(name, value)
|
80
63
|
end
|
81
64
|
|
82
|
-
def
|
65
|
+
def build_model_module
|
83
66
|
# Using a state machine defines several methods on the model.
|
84
67
|
# The model should be able to re-define them and `super` into the original method, if necessary.
|
85
68
|
# For that, we use a module to store all methods. The module is loaded into the model class.
|
86
|
-
@
|
69
|
+
@model_module = Module.new
|
70
|
+
@model.include(@model_module)
|
71
|
+
end
|
72
|
+
|
73
|
+
def model_module_eval(&block)
|
74
|
+
@model_module.module_eval(&block)
|
87
75
|
end
|
88
76
|
|
89
77
|
def define_state_methods
|
78
|
+
state_attribute = @state_attribute
|
90
79
|
state_names.each do |state_name|
|
91
|
-
|
80
|
+
model_module_eval do
|
92
81
|
define_method "#{state_name}?" do
|
93
|
-
|
82
|
+
state_machine_state_manager(state_attribute).state == state_name.to_s
|
94
83
|
end
|
95
84
|
end
|
96
85
|
end
|
@@ -103,130 +92,49 @@ module RailsStateMachine
|
|
103
92
|
end
|
104
93
|
|
105
94
|
def register_initial_state
|
106
|
-
|
107
|
-
|
95
|
+
state_attribute = @state_attribute
|
96
|
+
initial_state_name = states.detect(&:initial?)&.name
|
97
|
+
return unless initial_state_name
|
108
98
|
|
109
99
|
@model.after_initialize do
|
110
|
-
|
100
|
+
manager = state_machine_state_manager(state_attribute)
|
101
|
+
if new_record? && !manager.state
|
102
|
+
manager.state = initial_state_name
|
103
|
+
end
|
111
104
|
end
|
112
105
|
end
|
113
106
|
|
114
107
|
def define_event_methods
|
108
|
+
state_attribute = @state_attribute
|
115
109
|
event_names.each do |event_name, event|
|
116
|
-
|
117
|
-
define_method "
|
118
|
-
|
110
|
+
model_module_eval do
|
111
|
+
define_method "#{event_name}" do |**attributes|
|
112
|
+
prepare_state_event_change(attributes.merge("#{state_attribute}_event": event_name))
|
113
|
+
save
|
119
114
|
end
|
120
|
-
end
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
def register_callbacks
|
125
|
-
@model.class_eval do
|
126
|
-
before_validation :run_state_event_before_validation
|
127
|
-
before_save :register_state_events_for_callbacks
|
128
|
-
before_save { flush_state_event_callbacks(:before_save) }
|
129
|
-
after_save { flush_state_event_callbacks(:after_save) }
|
130
|
-
after_commit { flush_state_event_callbacks(:after_commit) }
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def register_validations
|
135
|
-
@model.class_eval do
|
136
|
-
after_validation :revert_state, if: -> { errors.any? }
|
137
|
-
end
|
138
|
-
end
|
139
115
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
delegate :state_machine, to: :class
|
144
|
-
end
|
145
|
-
|
146
|
-
@model.state_machine = self
|
147
|
-
end
|
148
|
-
|
149
|
-
def define_model_methods
|
150
|
-
model_methods do
|
151
|
-
def state_event=(event_name)
|
152
|
-
@next_state_machine_event = state_machine.find_event(event_name)
|
153
|
-
@state_before_state_event = source_state
|
154
|
-
|
155
|
-
# If the event can not transition from source_state, a TransitionNotFoundError will be raised
|
156
|
-
self.state = @next_state_machine_event.future_state_name(source_state).to_s
|
157
|
-
end
|
158
|
-
|
159
|
-
def state_event
|
160
|
-
@next_state_machine_event&.name
|
161
|
-
end
|
162
|
-
|
163
|
-
def source_state
|
164
|
-
if new_record?
|
165
|
-
state
|
166
|
-
else
|
167
|
-
state_in_database
|
116
|
+
define_method "#{event_name}!" do |**attributes|
|
117
|
+
prepare_state_event_change(attributes.merge("#{state_attribute}_event": event_name))
|
118
|
+
save!
|
168
119
|
end
|
169
|
-
end
|
170
|
-
|
171
|
-
private
|
172
120
|
|
173
|
-
|
174
|
-
|
175
|
-
# but call them explicitly when before_validation callbacks are triggered.
|
176
|
-
@next_state_machine_event&.run_before_validation(self)
|
177
|
-
end
|
178
|
-
|
179
|
-
def register_state_events_for_callbacks
|
180
|
-
@state_event_callbacks ||= {
|
181
|
-
before_save: [],
|
182
|
-
after_save: [],
|
183
|
-
after_commit: []
|
184
|
-
}
|
185
|
-
if @next_state_machine_event
|
186
|
-
@state_event_callbacks[:before_save] << @next_state_machine_event
|
187
|
-
@state_event_callbacks[:after_save] << @next_state_machine_event
|
188
|
-
@state_event_callbacks[:after_commit] << @next_state_machine_event
|
189
|
-
end
|
190
|
-
|
191
|
-
true
|
192
|
-
end
|
193
|
-
|
194
|
-
def flush_state_event_callbacks(name)
|
195
|
-
if @state_event_callbacks
|
196
|
-
while (event = @state_event_callbacks[name].shift)
|
197
|
-
event.public_send("run_#{name}", self)
|
198
|
-
end
|
121
|
+
define_method "may_#{event_name}?" do
|
122
|
+
state_machine_state_manager(state_attribute).transition_allowed_for?(event_name)
|
199
123
|
end
|
200
124
|
end
|
125
|
+
end
|
126
|
+
end
|
201
127
|
|
202
|
-
|
203
|
-
|
204
|
-
end
|
128
|
+
def define_model_methods
|
129
|
+
state_attribute = @state_attribute
|
205
130
|
|
206
|
-
|
207
|
-
|
131
|
+
model_module_eval do
|
132
|
+
define_method :"#{state_attribute}_event=" do |event_name|
|
133
|
+
state_machine_state_manager(state_attribute).transition_to(event_name)
|
208
134
|
end
|
209
135
|
|
210
|
-
|
211
|
-
|
212
|
-
# After calling `save`, ActiveRecord will flag the changes that it just stored as saved.
|
213
|
-
# https://github.com/rails/rails/blob/v5.1.4/activerecord/lib/active_record/attribute_methods/dirty.rb#L33-L46
|
214
|
-
#
|
215
|
-
# When taking multiple state events (e.g. a second event called inside an `after_save` callback) and thus
|
216
|
-
# saving after other changes were just saved, we need to mimic that behavior. Otherwise, ActiveRecord will
|
217
|
-
# print deprecation warnings like these:
|
218
|
-
#
|
219
|
-
# DEPRECATION WARNING: The behavior of `attribute_was` inside of after callbacks will be changing in the
|
220
|
-
# next version of Rails. The new return value will reflect the behavior of calling the method after
|
221
|
-
# `save` returned (e.g. the opposite of what it returns now). To maintain the current behavior, use
|
222
|
-
# `attribute_before_last_save` instead.
|
223
|
-
#
|
224
|
-
# These actually originate from ActiveRecord internals which try to determine the changes that should be
|
225
|
-
# stored for the second save. It is probably a shortcoming of ActiveRecord 5.1.x that will be fixed, but
|
226
|
-
# since the current/previous save was already successful, the right action is to just call `changes_applied`.
|
227
|
-
changes_applied
|
228
|
-
end
|
229
|
-
self.attributes = attributes
|
136
|
+
define_method :"#{state_attribute}_event" do
|
137
|
+
state_machine_state_manager(state_attribute).next_event&.name
|
230
138
|
end
|
231
139
|
end
|
232
140
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module RailsStateMachine
|
2
|
+
class StateManager
|
3
|
+
attr_accessor :next_event, :state_before_state_event
|
4
|
+
|
5
|
+
def initialize(record, state_machine, state_attribute)
|
6
|
+
@record = record
|
7
|
+
@state_machine = state_machine
|
8
|
+
@state_attribute = state_attribute
|
9
|
+
end
|
10
|
+
|
11
|
+
def state
|
12
|
+
@record.public_send(@state_attribute)
|
13
|
+
end
|
14
|
+
|
15
|
+
def state_in_database
|
16
|
+
@record.public_send(:"#{@state_attribute}_in_database").to_s
|
17
|
+
end
|
18
|
+
|
19
|
+
def state=(value)
|
20
|
+
@record.public_send(:"#{@state_attribute}=", value)
|
21
|
+
end
|
22
|
+
|
23
|
+
def revert
|
24
|
+
self.state = @state_before_state_event if @next_event
|
25
|
+
end
|
26
|
+
|
27
|
+
def source_state
|
28
|
+
if @record.new_record?
|
29
|
+
state
|
30
|
+
else
|
31
|
+
state_in_database
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def transition_to(event_name)
|
36
|
+
@next_event = @state_machine.find_event(event_name)
|
37
|
+
@state_before_state_event = source_state
|
38
|
+
|
39
|
+
# If the event can not transition from source_state, a TransitionNotFoundError will be raised
|
40
|
+
self.state = @next_event.future_state_name(source_state).to_s
|
41
|
+
end
|
42
|
+
|
43
|
+
def transition_allowed_for?(event_name)
|
44
|
+
@state_machine.find_event(event_name).allowed_from?(source_state)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rails_state_machine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Arne Hartherz
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2021-03-30 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activerecord
|
@@ -74,16 +74,20 @@ executables: []
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
+
- ".github/workflows/test.yml"
|
77
78
|
- ".gitignore"
|
78
79
|
- ".rspec"
|
79
80
|
- ".ruby-version"
|
80
|
-
- ".travis.yml"
|
81
81
|
- CHANGELOG.md
|
82
82
|
- Gemfile
|
83
83
|
- Gemfile.5.1.pg
|
84
84
|
- Gemfile.5.1.pg.lock
|
85
85
|
- Gemfile.5.2.pg
|
86
86
|
- Gemfile.5.2.pg.lock
|
87
|
+
- Gemfile.6.0.pg
|
88
|
+
- Gemfile.6.0.pg.lock
|
89
|
+
- Gemfile.6.1.pg
|
90
|
+
- Gemfile.6.1.pg.lock
|
87
91
|
- Gemfile.lock
|
88
92
|
- LICENSE
|
89
93
|
- LICENSE.txt
|
@@ -92,10 +96,12 @@ files:
|
|
92
96
|
- bin/console
|
93
97
|
- bin/setup
|
94
98
|
- lib/rails_state_machine.rb
|
99
|
+
- lib/rails_state_machine/callbacks.rb
|
95
100
|
- lib/rails_state_machine/event.rb
|
96
101
|
- lib/rails_state_machine/model.rb
|
97
102
|
- lib/rails_state_machine/state.rb
|
98
103
|
- lib/rails_state_machine/state_machine.rb
|
104
|
+
- lib/rails_state_machine/state_manager.rb
|
99
105
|
- lib/rails_state_machine/version.rb
|
100
106
|
- rails_state_machine.gemspec
|
101
107
|
homepage: https://github.com/makandra/rails_state_machine
|
@@ -117,8 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
117
123
|
- !ruby/object:Gem::Version
|
118
124
|
version: '0'
|
119
125
|
requirements: []
|
120
|
-
|
121
|
-
rubygems_version: 2.6.14.1
|
126
|
+
rubygems_version: 3.2.3
|
122
127
|
signing_key:
|
123
128
|
specification_version: 4
|
124
129
|
summary: ActiveRecord-bound state machine
|
data/.travis.yml
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
sudo: false
|
2
|
-
language: ruby
|
3
|
-
cache: bundler
|
4
|
-
|
5
|
-
before_script:
|
6
|
-
- psql -c 'create database rails_state_machine_test;' -U postgres
|
7
|
-
- mysql -e 'create database IF NOT EXISTS rails_state_machine_test;'
|
8
|
-
|
9
|
-
notifications:
|
10
|
-
email:
|
11
|
-
- fail@makandra.de
|
12
|
-
|
13
|
-
install:
|
14
|
-
# Replace default Travis CI bundler script with a version that doesn't
|
15
|
-
# explode when lockfile doesn't match recently bumped version
|
16
|
-
- bundle install --no-deployment --jobs=3 --retry=3 --path=${BUNDLE_PATH:-vendor/bundle}
|
17
|
-
|
18
|
-
script: bundle exec rake current_rspec
|
19
|
-
|
20
|
-
rvm:
|
21
|
-
- 2.3.7
|
22
|
-
- 2.4.4
|
23
|
-
- 2.5.1
|
24
|
-
|
25
|
-
gemfile:
|
26
|
-
- Gemfile.5.1.pg
|
27
|
-
- Gemfile.5.2.pg
|