stator 0.3.1 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/lib/stator/integration.rb +21 -2
- data/lib/stator/machine.rb +9 -25
- data/lib/stator/model.rb +24 -14
- data/lib/stator/transition.rb +5 -5
- data/lib/stator/version.rb +1 -1
- data/spec/model_spec.rb +51 -0
- data/spec/support/models.rb +27 -0
- data/spec/support/schema.rb +10 -0
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c4db54f443ba466ae3fc6f79857816b8719e0cbacf0615f8c014ae0c71c32e81
|
4
|
+
data.tar.gz: 53d72d27aafa1a340c02d2d5459235488bf5e12df9198312ffe1d5121fc24912
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a2e76eb8e8bbe0e1dab6d8fbdbb6516f12cff56c26c3d10d974d4aadce08fbcabdcd49dbba710d0f904a1a04ea4c489125b18475a119de5e625a9cbb6e9406a4
|
7
|
+
data.tar.gz: 0fef985f07d95f3d8801f4e3534f478feb516fea223532e1e562cc85bebdc54c09222ee0087517d4c3f396f4d094ad80a9404f7e0cb69829ffa6c5ddc40bb297
|
data/Gemfile
CHANGED
data/lib/stator/integration.rb
CHANGED
@@ -7,6 +7,9 @@ module Stator
|
|
7
7
|
delegate :transitions, to: :@machine
|
8
8
|
delegate :namespace, to: :@machine
|
9
9
|
|
10
|
+
attr_reader :skip_validations
|
11
|
+
attr_reader :skip_transition_tracking
|
12
|
+
|
10
13
|
def initialize(machine, record)
|
11
14
|
@machine = machine
|
12
15
|
@record = record
|
@@ -38,7 +41,7 @@ module Stator
|
|
38
41
|
|
39
42
|
def validate_transition
|
40
43
|
return unless state_changed?
|
41
|
-
return if
|
44
|
+
return if skip_validations
|
42
45
|
|
43
46
|
was = state_was
|
44
47
|
is = state
|
@@ -60,7 +63,7 @@ module Stator
|
|
60
63
|
end
|
61
64
|
|
62
65
|
def track_transition
|
63
|
-
return if
|
66
|
+
return if skip_transition_tracking
|
64
67
|
|
65
68
|
attempt_to_track_state(state)
|
66
69
|
attempt_to_track_state_changed_timestamp
|
@@ -115,6 +118,22 @@ module Stator
|
|
115
118
|
@machine.states.reverse.detect { |s| in_state_at?(s, t) }
|
116
119
|
end
|
117
120
|
|
121
|
+
def without_validation
|
122
|
+
was = @skip_validations
|
123
|
+
@skip_validations = true
|
124
|
+
yield @record
|
125
|
+
ensure
|
126
|
+
@skip_validations = was
|
127
|
+
end
|
128
|
+
|
129
|
+
def without_transition_tracking
|
130
|
+
was = @skip_transition_tracking
|
131
|
+
@skip_transition_tracking = true
|
132
|
+
yield @record
|
133
|
+
ensure
|
134
|
+
@skip_transition_tracking = was
|
135
|
+
end
|
136
|
+
|
118
137
|
protected
|
119
138
|
|
120
139
|
def attempt_to_track_state(state_to_track)
|
data/lib/stator/machine.rb
CHANGED
@@ -7,16 +7,14 @@ module Stator
|
|
7
7
|
attr_reader :transitions
|
8
8
|
attr_reader :states
|
9
9
|
attr_reader :namespace
|
10
|
-
attr_reader :skip_validations
|
11
|
-
attr_reader :skip_transition_tracking
|
12
|
-
|
13
10
|
|
14
11
|
def initialize(klass, options = {})
|
15
|
-
@class_name
|
16
|
-
@field
|
17
|
-
@namespace
|
12
|
+
@class_name = klass.name
|
13
|
+
@field = options[:field] || :state
|
14
|
+
@namespace = options[:namespace]
|
18
15
|
|
19
|
-
@initial_state
|
16
|
+
@initial_state = options[:initial] && options[:initial].to_s
|
17
|
+
@tracking_enabled = options[:track] || false
|
20
18
|
|
21
19
|
@transitions = []
|
22
20
|
@aliases = []
|
@@ -26,7 +24,6 @@ module Stator
|
|
26
24
|
@states = [@initial_state].compact
|
27
25
|
|
28
26
|
@options = options
|
29
|
-
|
30
27
|
end
|
31
28
|
|
32
29
|
def integration(record)
|
@@ -38,7 +35,6 @@ module Stator
|
|
38
35
|
end
|
39
36
|
|
40
37
|
def transition(name, &block)
|
41
|
-
|
42
38
|
t = ::Stator::Transition.new(@class_name, name, @namespace)
|
43
39
|
t.instance_eval(&block) if block_given?
|
44
40
|
|
@@ -66,6 +62,10 @@ module Stator
|
|
66
62
|
end
|
67
63
|
end
|
68
64
|
|
65
|
+
def tracking_enabled?
|
66
|
+
@tracking_enabled
|
67
|
+
end
|
68
|
+
|
69
69
|
def conditional(*states, &block)
|
70
70
|
_namespace = @namespace
|
71
71
|
|
@@ -88,22 +88,6 @@ module Stator
|
|
88
88
|
@class_name.constantize
|
89
89
|
end
|
90
90
|
|
91
|
-
def without_validation
|
92
|
-
was = @skip_validations
|
93
|
-
@skip_validations = true
|
94
|
-
yield
|
95
|
-
ensure
|
96
|
-
@skip_validations = was
|
97
|
-
end
|
98
|
-
|
99
|
-
def without_transition_tracking
|
100
|
-
was = @skip_transition_tracking
|
101
|
-
@skip_transition_tracking = true
|
102
|
-
yield
|
103
|
-
ensure
|
104
|
-
@skip_transition_tracking = was
|
105
|
-
end
|
106
|
-
|
107
91
|
protected
|
108
92
|
|
109
93
|
def verify_transition_validity(transition)
|
data/lib/stator/model.rb
CHANGED
@@ -35,26 +35,25 @@ module Stator
|
|
35
35
|
|
36
36
|
def self.included(base)
|
37
37
|
base.class_eval do
|
38
|
-
before_save :
|
38
|
+
before_save :_stator_maybe_track_transition, prepend: true
|
39
39
|
end
|
40
40
|
end
|
41
41
|
|
42
42
|
def in_state_at?(state, t, namespace = '')
|
43
|
-
|
44
|
-
machine.integration(self).in_state_at?(state, t)
|
43
|
+
_integration(namespace).in_state_at?(state, t)
|
45
44
|
end
|
46
45
|
|
47
46
|
def likely_state_at(t, namespace = '')
|
48
|
-
|
49
|
-
machine.integration(self).likely_state_at(t)
|
47
|
+
_integration(namespace).likely_state_at(t)
|
50
48
|
end
|
51
49
|
|
52
50
|
protected
|
53
51
|
|
54
|
-
|
55
|
-
def _stator_track_transition
|
52
|
+
def _stator_maybe_track_transition
|
56
53
|
self._stators.each do |namespace, machine|
|
57
|
-
machine.
|
54
|
+
next unless machine.tracking_enabled?
|
55
|
+
|
56
|
+
_integration(namespace).track_transition
|
58
57
|
end
|
59
58
|
|
60
59
|
true
|
@@ -70,23 +69,28 @@ module Stator
|
|
70
69
|
end
|
71
70
|
end
|
72
71
|
|
72
|
+
def initialize_dup(other)
|
73
|
+
@_integrations = {}
|
74
|
+
super
|
75
|
+
end
|
76
|
+
|
73
77
|
def without_state_transition_validations(namespace = '')
|
74
|
-
|
75
|
-
yield
|
78
|
+
_integration(namespace).without_validation do
|
79
|
+
yield self
|
76
80
|
end
|
77
81
|
end
|
78
82
|
|
79
83
|
def without_state_transition_tracking(namespace = '')
|
80
|
-
|
81
|
-
yield
|
84
|
+
_integration(namespace).without_transition_tracking do
|
85
|
+
yield self
|
82
86
|
end
|
83
87
|
end
|
84
88
|
|
85
89
|
protected
|
86
90
|
|
87
91
|
def _stator_validate_transition
|
88
|
-
self._stators.
|
89
|
-
|
92
|
+
self._stators.each_key do |namespace|
|
93
|
+
_integration(namespace).validate_transition
|
90
94
|
end
|
91
95
|
end
|
92
96
|
|
@@ -94,6 +98,12 @@ module Stator
|
|
94
98
|
self.class._stator(namespace)
|
95
99
|
end
|
96
100
|
|
101
|
+
def _integration(namespace = '')
|
102
|
+
@_integrations ||= {}
|
103
|
+
@_integrations[namespace] ||= _stator(namespace).integration(self)
|
104
|
+
@_integrations[namespace]
|
105
|
+
end
|
106
|
+
|
97
107
|
end
|
98
108
|
end
|
99
109
|
end
|
data/lib/stator/transition.rb
CHANGED
@@ -86,7 +86,7 @@ module Stator
|
|
86
86
|
def generate_methods
|
87
87
|
klass.class_eval <<-EV, __FILE__, __LINE__ + 1
|
88
88
|
def #{@full_name}(should_save = true)
|
89
|
-
integration =
|
89
|
+
integration = _integration(#{@namespace.to_s.inspect})
|
90
90
|
|
91
91
|
unless can_#{@full_name}?
|
92
92
|
integration.invalid_transition!(integration.state, #{@to.inspect}) if should_save
|
@@ -98,7 +98,7 @@ module Stator
|
|
98
98
|
end
|
99
99
|
|
100
100
|
def #{@full_name}!
|
101
|
-
integration =
|
101
|
+
integration = _integration(#{@namespace.to_s.inspect})
|
102
102
|
|
103
103
|
unless can_#{@full_name}?
|
104
104
|
integration.invalid_transition!(integration.state, #{@to.inspect})
|
@@ -110,10 +110,10 @@ module Stator
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def can_#{@full_name}?
|
113
|
-
|
114
|
-
return true if
|
113
|
+
integration = _integration(#{@namespace.to_s.inspect})
|
114
|
+
return true if integration.skip_validations
|
115
115
|
|
116
|
-
|
116
|
+
machine = self._stator(#{@namespace.to_s.inspect})
|
117
117
|
transition = machine.transitions.detect{|t| t.full_name.to_s == #{@full_name.inspect}.to_s }
|
118
118
|
transition.can?(integration.state)
|
119
119
|
end
|
data/lib/stator/version.rb
CHANGED
data/spec/model_spec.rb
CHANGED
@@ -160,6 +160,42 @@ describe Stator::Model do
|
|
160
160
|
u.activated_state_at.should_not be_nil
|
161
161
|
end
|
162
162
|
|
163
|
+
it "should skip tracking timestamps if opted out of with thread safety" do
|
164
|
+
threads = []
|
165
|
+
skip = User.new(email: "skip@example.com")
|
166
|
+
nope = User.new(email: "nope@example.com")
|
167
|
+
|
168
|
+
threads << Thread.new do
|
169
|
+
sleep 0.5
|
170
|
+
nope.semiactivate!
|
171
|
+
end
|
172
|
+
threads << Thread.new do
|
173
|
+
skip.without_state_transition_tracking do
|
174
|
+
sleep 1
|
175
|
+
skip.semiactivate!
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
threads.each(&:join)
|
180
|
+
|
181
|
+
nope.semiactivated_state_at.should_not be_nil
|
182
|
+
skip.semiactivated_state_at.should be_nil
|
183
|
+
end
|
184
|
+
|
185
|
+
it "should not inherit _integration cache on dup" do
|
186
|
+
u = User.new(email: "user@example.com")
|
187
|
+
u.save!
|
188
|
+
|
189
|
+
u_duped = u.dup
|
190
|
+
|
191
|
+
u.semiactivate!
|
192
|
+
|
193
|
+
u_duped_integration = u_duped.send(:_integration)
|
194
|
+
|
195
|
+
u_duped_integration.state.should_not eql(u.state)
|
196
|
+
u_duped_integration.instance_values["record"].should eq(u_duped)
|
197
|
+
end
|
198
|
+
|
163
199
|
describe "helper methods" do
|
164
200
|
it "should answer the question of whether the state is currently the one invoked" do
|
165
201
|
a = Animal.new
|
@@ -254,6 +290,21 @@ describe Stator::Model do
|
|
254
290
|
u.state.should eql("semiactivated")
|
255
291
|
u.semiactivated_state_at.should eql(t)
|
256
292
|
end
|
293
|
+
|
294
|
+
it "should allow opting into track by namespace" do
|
295
|
+
z = ZooKeeper.new(name: "Doug")
|
296
|
+
z.employment_state.should eql("hired")
|
297
|
+
z.employment_fire!
|
298
|
+
z.fired_employment_state_at.should_not be_nil
|
299
|
+
|
300
|
+
z.employment_hire!
|
301
|
+
z.hired_employment_state_at.should_not be_nil
|
302
|
+
|
303
|
+
z.working_start!
|
304
|
+
z.started_working_state_at.should be_nil
|
305
|
+
z.working_end!
|
306
|
+
z.ended_working_state_at.should be_nil
|
307
|
+
end
|
257
308
|
end
|
258
309
|
|
259
310
|
describe "aliasing" do
|
data/spec/support/models.rb
CHANGED
@@ -134,6 +134,33 @@ class Zoo < ActiveRecord::Base
|
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
137
|
+
class ZooKeeper < ActiveRecord::Base
|
138
|
+
extend Stator::Model
|
139
|
+
|
140
|
+
stator namespace: 'employment', field: 'employment_state', track: true do
|
141
|
+
transition :hire do
|
142
|
+
from nil, :fired
|
143
|
+
to :hired
|
144
|
+
end
|
145
|
+
|
146
|
+
transition :fire do
|
147
|
+
from :hired
|
148
|
+
to :fired
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
stator namespace: 'working', field: 'working_state', track: false do
|
153
|
+
transition :start do
|
154
|
+
from nil, :ended
|
155
|
+
to :started
|
156
|
+
end
|
157
|
+
|
158
|
+
transition :end do
|
159
|
+
from :started
|
160
|
+
to :ended
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
137
164
|
|
138
165
|
class Farm < ActiveRecord::Base
|
139
166
|
extend Stator::Model
|
data/spec/support/schema.rb
CHANGED
@@ -22,6 +22,16 @@ ActiveRecord::Schema.define(:version => 20130628161227) do
|
|
22
22
|
t.datetime "born_status_at"
|
23
23
|
end
|
24
24
|
|
25
|
+
create_table "zoo_keepers", :force => true do |t|
|
26
|
+
t.string "name"
|
27
|
+
t.string "employment_state", :default => 'hired'
|
28
|
+
t.datetime "hired_employment_state_at"
|
29
|
+
t.datetime "fired_employment_state_at"
|
30
|
+
t.string "working_state"
|
31
|
+
t.datetime "started_working_state_at"
|
32
|
+
t.datetime "ended_working_state_at"
|
33
|
+
end
|
34
|
+
|
25
35
|
create_table "zoos", :force => true do |t|
|
26
36
|
t.string "name"
|
27
37
|
t.string "state", :default => 'closed'
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: stator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Nelson
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-03-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -75,8 +75,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
75
75
|
- !ruby/object:Gem::Version
|
76
76
|
version: '0'
|
77
77
|
requirements: []
|
78
|
-
|
79
|
-
rubygems_version: 2.7.7
|
78
|
+
rubygems_version: 3.0.6
|
80
79
|
signing_key:
|
81
80
|
specification_version: 4
|
82
81
|
summary: The simplest of ActiveRecord state machines
|