stator 0.4.0 → 0.8.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 +4 -4
- data/.github/CODEOWNERS +1 -0
- data/.github/dependabot.yml +24 -0
- data/.github/workflows/build.yml +23 -0
- data/.ruby-version +1 -1
- data/Appraisals +26 -0
- data/Gemfile +5 -5
- data/README.md +20 -4
- data/gemfiles/activerecord_6.0.gemfile +12 -0
- data/gemfiles/activerecord_6.0.gemfile.lock +102 -0
- data/gemfiles/activerecord_6.1.gemfile +12 -0
- data/gemfiles/activerecord_6.1.gemfile.lock +100 -0
- data/gemfiles/activerecord_7.0.gemfile +12 -0
- data/gemfiles/activerecord_7.0.gemfile.lock +97 -0
- data/gemfiles/activerecord_7.1.gemfile +11 -0
- data/gemfiles/activerecord_7.1.gemfile.lock +114 -0
- data/gemfiles/activerecord_7.2.gemfile +11 -0
- data/gemfiles/activerecord_7.2.gemfile.lock +113 -0
- data/gemfiles/activerecord_8.0.gemfile +11 -0
- data/gemfiles/activerecord_8.0.gemfile.lock +116 -0
- data/lib/stator/integration.rb +5 -5
- data/lib/stator/version.rb +1 -1
- data/spec/model_spec.rb +117 -3
- data/spec/spec_helper.rb +0 -1
- data/spec/support/models.rb +13 -0
- data/stator.gemspec +10 -2
- metadata +94 -15
- data/.travis.yml +0 -41
- data/gemfiles/ar40.gemfile +0 -10
- data/gemfiles/ar41.gemfile +0 -10
- data/gemfiles/ar42.gemfile +0 -10
- data/gemfiles/ar52.gemfile +0 -10
@@ -0,0 +1,113 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
stator (0.8.0)
|
5
|
+
activerecord (>= 6.0)
|
6
|
+
base64
|
7
|
+
benchmark
|
8
|
+
bigdecimal
|
9
|
+
logger
|
10
|
+
mutex_m
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: https://rubygems.org/
|
14
|
+
specs:
|
15
|
+
activemodel (7.2.2.1)
|
16
|
+
activesupport (= 7.2.2.1)
|
17
|
+
activerecord (7.2.2.1)
|
18
|
+
activemodel (= 7.2.2.1)
|
19
|
+
activesupport (= 7.2.2.1)
|
20
|
+
timeout (>= 0.4.0)
|
21
|
+
activerecord-nulldb-adapter (1.1.1)
|
22
|
+
activerecord (>= 6.0, < 8.1)
|
23
|
+
activesupport (7.2.2.1)
|
24
|
+
base64
|
25
|
+
benchmark (>= 0.3)
|
26
|
+
bigdecimal
|
27
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
28
|
+
connection_pool (>= 2.2.5)
|
29
|
+
drb
|
30
|
+
i18n (>= 1.6, < 2)
|
31
|
+
logger (>= 1.4.2)
|
32
|
+
minitest (>= 5.1)
|
33
|
+
securerandom (>= 0.3)
|
34
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
35
|
+
appraisal (2.5.0)
|
36
|
+
bundler
|
37
|
+
rake
|
38
|
+
thor (>= 0.14.0)
|
39
|
+
base64 (0.2.0)
|
40
|
+
benchmark (0.4.0)
|
41
|
+
bigdecimal (3.1.9)
|
42
|
+
concurrent-ruby (1.3.5)
|
43
|
+
connection_pool (2.5.0)
|
44
|
+
diff-lcs (1.6.1)
|
45
|
+
drb (2.2.1)
|
46
|
+
i18n (1.14.7)
|
47
|
+
concurrent-ruby (~> 1.0)
|
48
|
+
logger (1.7.0)
|
49
|
+
minitest (5.25.5)
|
50
|
+
mutex_m (0.3.0)
|
51
|
+
rake (13.2.1)
|
52
|
+
rspec (3.13.0)
|
53
|
+
rspec-core (~> 3.13.0)
|
54
|
+
rspec-expectations (~> 3.13.0)
|
55
|
+
rspec-mocks (~> 3.13.0)
|
56
|
+
rspec-core (3.13.3)
|
57
|
+
rspec-support (~> 3.13.0)
|
58
|
+
rspec-expectations (3.13.3)
|
59
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
60
|
+
rspec-support (~> 3.13.0)
|
61
|
+
rspec-mocks (3.13.2)
|
62
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
63
|
+
rspec-support (~> 3.13.0)
|
64
|
+
rspec-support (3.13.2)
|
65
|
+
securerandom (0.4.1)
|
66
|
+
thor (1.3.2)
|
67
|
+
timeout (0.4.3)
|
68
|
+
tzinfo (2.0.6)
|
69
|
+
concurrent-ruby (~> 1.0)
|
70
|
+
|
71
|
+
PLATFORMS
|
72
|
+
arm64-darwin
|
73
|
+
ruby
|
74
|
+
|
75
|
+
DEPENDENCIES
|
76
|
+
activerecord (~> 7.2.0)
|
77
|
+
activerecord-nulldb-adapter
|
78
|
+
appraisal
|
79
|
+
rake
|
80
|
+
rspec
|
81
|
+
stator!
|
82
|
+
|
83
|
+
CHECKSUMS
|
84
|
+
activemodel (7.2.2.1) sha256=8398861f9ee2c4671a8357ab39e9b38a045fd656f6685a3dd5890c2419dbfdaf
|
85
|
+
activerecord (7.2.2.1) sha256=79a31f71c32d5138717c2104e0ff105f5d82922247c85bdca144f2720e67fab9
|
86
|
+
activerecord-nulldb-adapter (1.1.1) sha256=034c91106183b954b072fba14c2786adf1a2b9e852ce04f85f823afaf03e9820
|
87
|
+
activesupport (7.2.2.1) sha256=842bcbf8a92977f80fb4750661a237cf5dd4fdd442066b3c35e88afb488647f5
|
88
|
+
appraisal (2.5.0) sha256=36989221be127913b0dba8d114da2001e6b2dceea7bd4951200eaba764eed3ce
|
89
|
+
base64 (0.2.0) sha256=0f25e9b21a02a0cc0cea8ef92b2041035d39350946e8789c562b2d1a3da01507
|
90
|
+
benchmark (0.4.0) sha256=0f12f8c495545e3710c3e4f0480f63f06b4c842cc94cec7f33a956f5180e874a
|
91
|
+
bigdecimal (3.1.9) sha256=2ffc742031521ad69c2dfc815a98e426a230a3d22aeac1995826a75dabfad8cc
|
92
|
+
concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6
|
93
|
+
connection_pool (2.5.0) sha256=233b92f8d38e038c1349ccea65dd3772727d669d6d2e71f9897c8bf5cd53ebfc
|
94
|
+
diff-lcs (1.6.1) sha256=12a5a83f3e37a8e2f4427268e305914d5f1879f22b4e73bb1a09f76a3dd86cd4
|
95
|
+
drb (2.2.1) sha256=e9d472bf785f558b96b25358bae115646da0dbfd45107ad858b0bc0d935cb340
|
96
|
+
i18n (1.14.7) sha256=ceba573f8138ff2c0915427f1fc5bdf4aa3ab8ae88c8ce255eb3ecf0a11a5d0f
|
97
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
98
|
+
minitest (5.25.5) sha256=391b6c6cb43a4802bfb7c93af1ebe2ac66a210293f4a3fb7db36f2fc7dc2c756
|
99
|
+
mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751
|
100
|
+
rake (13.2.1) sha256=46cb38dae65d7d74b6020a4ac9d48afed8eb8149c040eccf0523bec91907059d
|
101
|
+
rspec (3.13.0) sha256=d490914ac1d5a5a64a0e1400c1d54ddd2a501324d703b8cfe83f458337bab993
|
102
|
+
rspec-core (3.13.3) sha256=25136507f4f9cf2e8977a2851e64e438b4331646054e345998714108745cdfe4
|
103
|
+
rspec-expectations (3.13.3) sha256=0e6b5af59b900147698ea0ff80456c4f2e69cac4394fbd392fbd1ca561f66c58
|
104
|
+
rspec-mocks (3.13.2) sha256=2327335def0e1665325a9b617e3af9ae20272741d80ac550336309a7c59abdef
|
105
|
+
rspec-support (3.13.2) sha256=cea3a2463fd9b84b9dcc9685efd80ea701aa8f7b3decb3b3ce795ed67737dbec
|
106
|
+
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
107
|
+
stator (0.8.0)
|
108
|
+
thor (1.3.2) sha256=eef0293b9e24158ccad7ab383ae83534b7ad4ed99c09f96f1a6b036550abbeda
|
109
|
+
timeout (0.4.3) sha256=9509f079b2b55fe4236d79633bd75e34c1c1e7e3fb4b56cb5fda61f80a0fe30e
|
110
|
+
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
|
111
|
+
|
112
|
+
BUNDLED WITH
|
113
|
+
2.6.7
|
@@ -0,0 +1,116 @@
|
|
1
|
+
PATH
|
2
|
+
remote: ..
|
3
|
+
specs:
|
4
|
+
stator (0.8.0)
|
5
|
+
activerecord (>= 6.0)
|
6
|
+
base64
|
7
|
+
benchmark
|
8
|
+
bigdecimal
|
9
|
+
logger
|
10
|
+
mutex_m
|
11
|
+
|
12
|
+
GEM
|
13
|
+
remote: https://rubygems.org/
|
14
|
+
specs:
|
15
|
+
activemodel (8.0.2)
|
16
|
+
activesupport (= 8.0.2)
|
17
|
+
activerecord (8.0.2)
|
18
|
+
activemodel (= 8.0.2)
|
19
|
+
activesupport (= 8.0.2)
|
20
|
+
timeout (>= 0.4.0)
|
21
|
+
activerecord-nulldb-adapter (1.1.1)
|
22
|
+
activerecord (>= 6.0, < 8.1)
|
23
|
+
activesupport (8.0.2)
|
24
|
+
base64
|
25
|
+
benchmark (>= 0.3)
|
26
|
+
bigdecimal
|
27
|
+
concurrent-ruby (~> 1.0, >= 1.3.1)
|
28
|
+
connection_pool (>= 2.2.5)
|
29
|
+
drb
|
30
|
+
i18n (>= 1.6, < 2)
|
31
|
+
logger (>= 1.4.2)
|
32
|
+
minitest (>= 5.1)
|
33
|
+
securerandom (>= 0.3)
|
34
|
+
tzinfo (~> 2.0, >= 2.0.5)
|
35
|
+
uri (>= 0.13.1)
|
36
|
+
appraisal (2.5.0)
|
37
|
+
bundler
|
38
|
+
rake
|
39
|
+
thor (>= 0.14.0)
|
40
|
+
base64 (0.2.0)
|
41
|
+
benchmark (0.4.0)
|
42
|
+
bigdecimal (3.1.9)
|
43
|
+
concurrent-ruby (1.3.5)
|
44
|
+
connection_pool (2.5.0)
|
45
|
+
diff-lcs (1.6.1)
|
46
|
+
drb (2.2.1)
|
47
|
+
i18n (1.14.7)
|
48
|
+
concurrent-ruby (~> 1.0)
|
49
|
+
logger (1.7.0)
|
50
|
+
minitest (5.25.5)
|
51
|
+
mutex_m (0.3.0)
|
52
|
+
rake (13.2.1)
|
53
|
+
rspec (3.13.0)
|
54
|
+
rspec-core (~> 3.13.0)
|
55
|
+
rspec-expectations (~> 3.13.0)
|
56
|
+
rspec-mocks (~> 3.13.0)
|
57
|
+
rspec-core (3.13.3)
|
58
|
+
rspec-support (~> 3.13.0)
|
59
|
+
rspec-expectations (3.13.3)
|
60
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
61
|
+
rspec-support (~> 3.13.0)
|
62
|
+
rspec-mocks (3.13.2)
|
63
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
64
|
+
rspec-support (~> 3.13.0)
|
65
|
+
rspec-support (3.13.2)
|
66
|
+
securerandom (0.4.1)
|
67
|
+
thor (1.3.2)
|
68
|
+
timeout (0.4.3)
|
69
|
+
tzinfo (2.0.6)
|
70
|
+
concurrent-ruby (~> 1.0)
|
71
|
+
uri (1.0.3)
|
72
|
+
|
73
|
+
PLATFORMS
|
74
|
+
arm64-darwin
|
75
|
+
ruby
|
76
|
+
|
77
|
+
DEPENDENCIES
|
78
|
+
activerecord (~> 8.0.0)
|
79
|
+
activerecord-nulldb-adapter
|
80
|
+
appraisal
|
81
|
+
rake
|
82
|
+
rspec
|
83
|
+
stator!
|
84
|
+
|
85
|
+
CHECKSUMS
|
86
|
+
activemodel (8.0.2) sha256=0ae1fb7fa1fae0699ba041a9e97702df42ea3b13f2d39f2d0fde51fca5f0656c
|
87
|
+
activerecord (8.0.2) sha256=793470b92c44e4198d0262ac60086b7822f0ea585079ad67e32a6e4c86f2d90a
|
88
|
+
activerecord-nulldb-adapter (1.1.1) sha256=034c91106183b954b072fba14c2786adf1a2b9e852ce04f85f823afaf03e9820
|
89
|
+
activesupport (8.0.2) sha256=8565cddba31b900cdc17682fd66ecd020441e3eef320a9930285394e8c07a45e
|
90
|
+
appraisal (2.5.0) sha256=36989221be127913b0dba8d114da2001e6b2dceea7bd4951200eaba764eed3ce
|
91
|
+
base64 (0.2.0) sha256=0f25e9b21a02a0cc0cea8ef92b2041035d39350946e8789c562b2d1a3da01507
|
92
|
+
benchmark (0.4.0) sha256=0f12f8c495545e3710c3e4f0480f63f06b4c842cc94cec7f33a956f5180e874a
|
93
|
+
bigdecimal (3.1.9) sha256=2ffc742031521ad69c2dfc815a98e426a230a3d22aeac1995826a75dabfad8cc
|
94
|
+
concurrent-ruby (1.3.5) sha256=813b3e37aca6df2a21a3b9f1d497f8cbab24a2b94cab325bffe65ee0f6cbebc6
|
95
|
+
connection_pool (2.5.0) sha256=233b92f8d38e038c1349ccea65dd3772727d669d6d2e71f9897c8bf5cd53ebfc
|
96
|
+
diff-lcs (1.6.1) sha256=12a5a83f3e37a8e2f4427268e305914d5f1879f22b4e73bb1a09f76a3dd86cd4
|
97
|
+
drb (2.2.1) sha256=e9d472bf785f558b96b25358bae115646da0dbfd45107ad858b0bc0d935cb340
|
98
|
+
i18n (1.14.7) sha256=ceba573f8138ff2c0915427f1fc5bdf4aa3ab8ae88c8ce255eb3ecf0a11a5d0f
|
99
|
+
logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
|
100
|
+
minitest (5.25.5) sha256=391b6c6cb43a4802bfb7c93af1ebe2ac66a210293f4a3fb7db36f2fc7dc2c756
|
101
|
+
mutex_m (0.3.0) sha256=cfcb04ac16b69c4813777022fdceda24e9f798e48092a2b817eb4c0a782b0751
|
102
|
+
rake (13.2.1) sha256=46cb38dae65d7d74b6020a4ac9d48afed8eb8149c040eccf0523bec91907059d
|
103
|
+
rspec (3.13.0) sha256=d490914ac1d5a5a64a0e1400c1d54ddd2a501324d703b8cfe83f458337bab993
|
104
|
+
rspec-core (3.13.3) sha256=25136507f4f9cf2e8977a2851e64e438b4331646054e345998714108745cdfe4
|
105
|
+
rspec-expectations (3.13.3) sha256=0e6b5af59b900147698ea0ff80456c4f2e69cac4394fbd392fbd1ca561f66c58
|
106
|
+
rspec-mocks (3.13.2) sha256=2327335def0e1665325a9b617e3af9ae20272741d80ac550336309a7c59abdef
|
107
|
+
rspec-support (3.13.2) sha256=cea3a2463fd9b84b9dcc9685efd80ea701aa8f7b3decb3b3ce795ed67737dbec
|
108
|
+
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
|
109
|
+
stator (0.8.0)
|
110
|
+
thor (1.3.2) sha256=eef0293b9e24158ccad7ab383ae83534b7ad4ed99c09f96f1a6b036550abbeda
|
111
|
+
timeout (0.4.3) sha256=9509f079b2b55fe4236d79633bd75e34c1c1e7e3fb4b56cb5fda61f80a0fe30e
|
112
|
+
tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
|
113
|
+
uri (1.0.3) sha256=e9f2244608eea2f7bc357d954c65c910ce0399ca5e18a7a29207ac22d8767011
|
114
|
+
|
115
|
+
BUNDLED WITH
|
116
|
+
2.6.7
|
data/lib/stator/integration.rb
CHANGED
@@ -25,9 +25,9 @@ module Stator
|
|
25
25
|
|
26
26
|
def state_was(use_previous = false)
|
27
27
|
if use_previous
|
28
|
-
@record.
|
28
|
+
@record.attribute_before_last_save(@machine.field)
|
29
29
|
else
|
30
|
-
@record.
|
30
|
+
@record.attribute_in_database(@machine.field)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
@@ -41,9 +41,9 @@ module Stator
|
|
41
41
|
|
42
42
|
def state_changed?(use_previous = false)
|
43
43
|
if use_previous
|
44
|
-
|
44
|
+
@record.saved_change_to_attribute?(@machine.field)
|
45
45
|
else
|
46
|
-
@record.
|
46
|
+
@record.will_save_change_to_attribute?(@machine.field)
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
@@ -158,7 +158,7 @@ module Stator
|
|
158
158
|
return unless @record.respond_to?(field_name)
|
159
159
|
return unless @record.respond_to?("#{field_name}=")
|
160
160
|
return unless @record.send(field_name.to_s).nil? || state_changed?
|
161
|
-
return if @record.
|
161
|
+
return if @record.will_save_change_to_attribute?(field_name)
|
162
162
|
|
163
163
|
@record.send("#{field_name}=", (Time.zone || Time).now)
|
164
164
|
end
|
data/lib/stator/version.rb
CHANGED
data/spec/model_spec.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "spec_helper"
|
4
4
|
|
5
5
|
describe Stator::Model do
|
6
|
+
|
6
7
|
it "should set the default state after initialization" do
|
7
8
|
u = User.new
|
8
9
|
u.state.should eql("pending")
|
@@ -11,7 +12,7 @@ describe Stator::Model do
|
|
11
12
|
it "should see the initial setting of the state as a change with the initial state as the previous value" do
|
12
13
|
u = User.new
|
13
14
|
u.state = "activated"
|
14
|
-
u.
|
15
|
+
u.state_in_database.should eql("pending")
|
15
16
|
end
|
16
17
|
|
17
18
|
it "should not obstruct normal validations" do
|
@@ -35,6 +36,26 @@ describe Stator::Model do
|
|
35
36
|
u.should be_valid
|
36
37
|
end
|
37
38
|
|
39
|
+
it "should work normally with active_record dirty methods" do
|
40
|
+
u = User.new(email: "doug@example.com")
|
41
|
+
|
42
|
+
u.will_save_change_to_state?.should_not be true
|
43
|
+
u.state_in_database.should eq("pending")
|
44
|
+
u.state_before_last_save.should be nil
|
45
|
+
|
46
|
+
u.state = "hyperactivated"
|
47
|
+
|
48
|
+
u.will_save_change_to_state?.should be true
|
49
|
+
u.state_in_database.should eq("pending")
|
50
|
+
u.state_before_last_save.should be nil
|
51
|
+
|
52
|
+
u.save!
|
53
|
+
|
54
|
+
u.will_save_change_to_state?.should_not be true
|
55
|
+
u.state_in_database.should eq("hyperactivated")
|
56
|
+
u.state_before_last_save.should eq("pending")
|
57
|
+
end
|
58
|
+
|
38
59
|
it "should ensure a valid state transition when given an illegal state based on the current state" do
|
39
60
|
u = User.new
|
40
61
|
|
@@ -77,6 +98,21 @@ describe Stator::Model do
|
|
77
98
|
u.should be_persisted
|
78
99
|
end
|
79
100
|
|
101
|
+
it "should conditionally invoke after_save callbacks when use_previous is true" do
|
102
|
+
u = User.new
|
103
|
+
u.email = "doug@example.com"
|
104
|
+
|
105
|
+
u.semiactivate!
|
106
|
+
|
107
|
+
u.state.should eql("semiactivated")
|
108
|
+
u.activation_notification_published.should_not be true
|
109
|
+
|
110
|
+
u.activate!
|
111
|
+
|
112
|
+
u.state.should eql("activated")
|
113
|
+
u.activation_notification_published.should be true
|
114
|
+
end
|
115
|
+
|
80
116
|
it "should blow up if the record is invalid and a bang method is used" do
|
81
117
|
u = User.new(email: "doug@other.com", name: "doug")
|
82
118
|
lambda {
|
@@ -218,6 +254,75 @@ describe Stator::Model do
|
|
218
254
|
end
|
219
255
|
end
|
220
256
|
|
257
|
+
it "should validate state transitions using the db state after a transaction rollback" do
|
258
|
+
is_active_record_6_or_higher = Gem::Requirement.new(">= 6.0").satisfied_by?(ActiveRecord.version)
|
259
|
+
|
260
|
+
u = User.create!(email: 'doug@example.com')
|
261
|
+
u.state.should eql('pending')
|
262
|
+
|
263
|
+
lambda {
|
264
|
+
ActiveRecord::Base.transaction do
|
265
|
+
# The state change will be applied to the model object in memory.
|
266
|
+
# An UPDATE query will be sent to the db, but it will be rolled back
|
267
|
+
# when the error is raised below.
|
268
|
+
u.activate!
|
269
|
+
raise "Some error"
|
270
|
+
end
|
271
|
+
}.should raise_error("Some error")
|
272
|
+
|
273
|
+
u.state.should eql("activated")
|
274
|
+
|
275
|
+
# Rails 6.0 fixed a bug where a model's dirty state would be incorrect
|
276
|
+
# in a scenario like this one, where a model is updated within a transaction,
|
277
|
+
# and the transaction is then rolled back:
|
278
|
+
#
|
279
|
+
# https://github.com/rails/rails/pull/35987
|
280
|
+
#
|
281
|
+
# We show this dirty behavior change in Rails 6.0 below to clarify why stator itself
|
282
|
+
# behaves differently starting in Rails 6.0.
|
283
|
+
if is_active_record_6_or_higher
|
284
|
+
# On Rails 6.0 or higher, attribute_in_database is "pending" — which is correct,
|
285
|
+
# because the db transaction was rolled back.
|
286
|
+
u.attribute_in_database("state").should eql("pending")
|
287
|
+
|
288
|
+
# Attempting a state change to "hyperactivated" fails, which is correct,
|
289
|
+
# because the previous state change to "activated" did not succeed.
|
290
|
+
lambda {
|
291
|
+
u.hyperactivate!
|
292
|
+
}.should raise_error(ActiveRecord::RecordInvalid, 'Validation failed: State cannot transition to "hyperactivated" from "pending"')
|
293
|
+
else
|
294
|
+
# On Rails < 6.0, attribute_in_database is "activated" — which is incorrect,
|
295
|
+
# because the db transaction was rolled back.
|
296
|
+
u.attribute_in_database("state").should eql("activated")
|
297
|
+
|
298
|
+
# On Rails < 6.0, stator incorrectly allows the state change to "hyperactivated"
|
299
|
+
# because it incorrectly thinks the state has been successfully updated to "activated".
|
300
|
+
lambda {
|
301
|
+
u.hyperactivate!
|
302
|
+
}.should_not raise_error
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
it "should not support multiple state changes made between saves" do
|
307
|
+
u = User.create!(email: "doug@example.com")
|
308
|
+
u.state.should eql("pending")
|
309
|
+
|
310
|
+
u.activate(false) # change state, but do not save to db
|
311
|
+
u.state.should eql("activated")
|
312
|
+
|
313
|
+
lambda {
|
314
|
+
# Fails because the db state is still "pending", and
|
315
|
+
# the db state is what stator uses for the "previous" state
|
316
|
+
# to check that the state transition is valid.
|
317
|
+
u.hyperactivate!
|
318
|
+
}.should raise_error(ActiveRecord::RecordInvalid, 'Validation failed: State cannot transition to "hyperactivated" from "pending"')
|
319
|
+
|
320
|
+
# The model is updated in-memory with the new state value,
|
321
|
+
# but remains invalid.
|
322
|
+
u.state.should eql("hyperactivated")
|
323
|
+
u.valid?.should be false
|
324
|
+
end
|
325
|
+
|
221
326
|
describe "tracker methods" do
|
222
327
|
before do
|
223
328
|
Time.zone = "Eastern Time (US & Canada)"
|
@@ -371,8 +476,16 @@ describe Stator::Model do
|
|
371
476
|
User::ACTIVE_STATES.should eql(%w[activated hyperactivated])
|
372
477
|
User::INACTIVE_STATES.should eql(%w[pending deactivated semiactivated])
|
373
478
|
|
374
|
-
|
375
|
-
|
479
|
+
is_active_record_72_or_higher = Gem::Requirement.new(">= 7.2").satisfied_by?(ActiveRecord.version)
|
480
|
+
|
481
|
+
if (is_active_record_72_or_higher)
|
482
|
+
User.active.to_sql.gsub(" ", " ").should eq("SELECT 'users'.* FROM 'users' WHERE 'users'.'state' IN ('activated', 'hyperactivated')")
|
483
|
+
User.inactive.to_sql.gsub(" ", " ").should eq("SELECT 'users'.* FROM 'users' WHERE 'users'.'state' IN ('pending', 'deactivated', 'semiactivated')")
|
484
|
+
else
|
485
|
+
User.active.to_sql.gsub(" ", " ").should eq("SELECT users.* FROM users WHERE users.state IN ('activated', 'hyperactivated')")
|
486
|
+
User.inactive.to_sql.gsub(" ", " ").should eq("SELECT users.* FROM users WHERE users.state IN ('pending', 'deactivated', 'semiactivated')")
|
487
|
+
end
|
488
|
+
|
376
489
|
end
|
377
490
|
|
378
491
|
it "should evaluate inverses correctly" do
|
@@ -416,4 +529,5 @@ describe Stator::Model do
|
|
416
529
|
states.should eql(%w[pending activated deactivated semiactivated hyperactivated])
|
417
530
|
end
|
418
531
|
end
|
532
|
+
|
419
533
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -11,7 +11,6 @@ require 'active_support/core_ext'
|
|
11
11
|
require 'stator'
|
12
12
|
|
13
13
|
RSpec.configure do |config|
|
14
|
-
config.treat_symbols_as_metadata_keys_with_true_values = true
|
15
14
|
config.expect_with(:rspec) { |c| c.syntax = :should }
|
16
15
|
config.run_all_when_everything_filtered = true
|
17
16
|
config.filter_run :focus
|
data/spec/support/models.rb
CHANGED
@@ -3,11 +3,17 @@ class User < ActiveRecord::Base
|
|
3
3
|
|
4
4
|
before_save :set_tagged_at
|
5
5
|
|
6
|
+
attr_reader :activation_notification_published
|
7
|
+
|
6
8
|
stator track: true, initial: :pending do
|
7
9
|
|
8
10
|
transition :activate do
|
9
11
|
from :pending, :semiactivated
|
10
12
|
to :activated
|
13
|
+
|
14
|
+
conditional(use_previous: true) do |condition|
|
15
|
+
after_save :publish_activation_notification, :if => condition
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
19
|
transition :deactivate do
|
@@ -88,6 +94,13 @@ class User < ActiveRecord::Base
|
|
88
94
|
def set_tagged_at
|
89
95
|
self.tagged_at = self.semiactivated_state_at
|
90
96
|
end
|
97
|
+
|
98
|
+
private
|
99
|
+
|
100
|
+
def publish_activation_notification
|
101
|
+
@activation_notification_published = true
|
102
|
+
end
|
103
|
+
|
91
104
|
end
|
92
105
|
|
93
106
|
class Animal < ActiveRecord::Base
|
data/stator.gemspec
CHANGED
@@ -10,12 +10,20 @@ Gem::Specification.new do |gem|
|
|
10
10
|
gem.email = ["mike@mikeonrails.com"]
|
11
11
|
gem.description = %q{The simplest of ActiveRecord state machines. Intended to be lightweight and minimalistic.}
|
12
12
|
gem.summary = %q{The simplest of ActiveRecord state machines}
|
13
|
-
gem.homepage = "https://
|
13
|
+
gem.homepage = "https://github.com/guideline-tech/stator"
|
14
|
+
gem.license = "MIT"
|
14
15
|
|
15
16
|
gem.files = `git ls-files`.split($/)
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
18
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
19
|
gem.require_paths = ["lib"]
|
19
20
|
|
20
|
-
gem.add_dependency '
|
21
|
+
gem.add_dependency 'base64'
|
22
|
+
gem.add_dependency 'benchmark'
|
23
|
+
gem.add_dependency 'bigdecimal'
|
24
|
+
gem.add_dependency 'logger'
|
25
|
+
gem.add_dependency 'mutex_m'
|
26
|
+
gem.add_dependency 'activerecord', ">= 6.0"
|
27
|
+
|
28
|
+
gem.required_ruby_version = ">= 3.2.0"
|
21
29
|
end
|
metadata
CHANGED
@@ -1,17 +1,58 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Nelson
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
|
-
name:
|
13
|
+
name: base64
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: benchmark
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: bigdecimal
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
- !ruby/object:Gem::Dependency
|
55
|
+
name: logger
|
15
56
|
requirement: !ruby/object:Gem::Requirement
|
16
57
|
requirements:
|
17
58
|
- - ">="
|
@@ -24,6 +65,34 @@ dependencies:
|
|
24
65
|
- - ">="
|
25
66
|
- !ruby/object:Gem::Version
|
26
67
|
version: '0'
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: mutex_m
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: '0'
|
75
|
+
type: :runtime
|
76
|
+
prerelease: false
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
version: '0'
|
82
|
+
- !ruby/object:Gem::Dependency
|
83
|
+
name: activerecord
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '6.0'
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '6.0'
|
27
96
|
description: The simplest of ActiveRecord state machines. Intended to be lightweight
|
28
97
|
and minimalistic.
|
29
98
|
email:
|
@@ -32,19 +101,30 @@ executables: []
|
|
32
101
|
extensions: []
|
33
102
|
extra_rdoc_files: []
|
34
103
|
files:
|
104
|
+
- ".github/CODEOWNERS"
|
105
|
+
- ".github/dependabot.yml"
|
106
|
+
- ".github/workflows/build.yml"
|
35
107
|
- ".gitignore"
|
36
108
|
- ".rspec"
|
37
109
|
- ".ruby-gemset"
|
38
110
|
- ".ruby-version"
|
39
|
-
-
|
111
|
+
- Appraisals
|
40
112
|
- Gemfile
|
41
113
|
- LICENSE.txt
|
42
114
|
- README.md
|
43
115
|
- Rakefile
|
44
|
-
- gemfiles/
|
45
|
-
- gemfiles/
|
46
|
-
- gemfiles/
|
47
|
-
- gemfiles/
|
116
|
+
- gemfiles/activerecord_6.0.gemfile
|
117
|
+
- gemfiles/activerecord_6.0.gemfile.lock
|
118
|
+
- gemfiles/activerecord_6.1.gemfile
|
119
|
+
- gemfiles/activerecord_6.1.gemfile.lock
|
120
|
+
- gemfiles/activerecord_7.0.gemfile
|
121
|
+
- gemfiles/activerecord_7.0.gemfile.lock
|
122
|
+
- gemfiles/activerecord_7.1.gemfile
|
123
|
+
- gemfiles/activerecord_7.1.gemfile.lock
|
124
|
+
- gemfiles/activerecord_7.2.gemfile
|
125
|
+
- gemfiles/activerecord_7.2.gemfile.lock
|
126
|
+
- gemfiles/activerecord_8.0.gemfile
|
127
|
+
- gemfiles/activerecord_8.0.gemfile.lock
|
48
128
|
- lib/stator.rb
|
49
129
|
- lib/stator/alias.rb
|
50
130
|
- lib/stator/integration.rb
|
@@ -57,10 +137,10 @@ files:
|
|
57
137
|
- spec/support/models.rb
|
58
138
|
- spec/support/schema.rb
|
59
139
|
- stator.gemspec
|
60
|
-
homepage: https://
|
61
|
-
licenses:
|
140
|
+
homepage: https://github.com/guideline-tech/stator
|
141
|
+
licenses:
|
142
|
+
- MIT
|
62
143
|
metadata: {}
|
63
|
-
post_install_message:
|
64
144
|
rdoc_options: []
|
65
145
|
require_paths:
|
66
146
|
- lib
|
@@ -68,15 +148,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
148
|
requirements:
|
69
149
|
- - ">="
|
70
150
|
- !ruby/object:Gem::Version
|
71
|
-
version:
|
151
|
+
version: 3.2.0
|
72
152
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
153
|
requirements:
|
74
154
|
- - ">="
|
75
155
|
- !ruby/object:Gem::Version
|
76
156
|
version: '0'
|
77
157
|
requirements: []
|
78
|
-
rubygems_version: 3.
|
79
|
-
signing_key:
|
158
|
+
rubygems_version: 3.6.7
|
80
159
|
specification_version: 4
|
81
160
|
summary: The simplest of ActiveRecord state machines
|
82
161
|
test_files:
|