state_shifter 0.7.2 → 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.
- data/Gemfile +1 -0
- data/Gemfile.lock +2 -0
- data/VERSION +1 -1
- data/examples/malformed_persistence.rb +1 -1
- data/examples/missing_persistence.rb +98 -0
- data/examples/review.rb +33 -0
- data/examples/review_custom_persistence.rb +35 -0
- data/lib/state_shifter/definition/active_record_integration_methods.rb +24 -0
- data/lib/state_shifter/definition/instance_methods.rb +6 -6
- data/lib/state_shifter/definition.rb +7 -0
- data/lib/state_shifter.rb +1 -0
- data/spec/state_shifter_spec.rb +61 -0
- data/state_shifter.gemspec +8 -1
- metadata +22 -2
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.8.0
|
@@ -0,0 +1,98 @@
|
|
1
|
+
class MissingPersistence < ActiveRecord::Base
|
2
|
+
include StateShifter::Definition
|
3
|
+
|
4
|
+
###
|
5
|
+
|
6
|
+
persist_attribute :lollies
|
7
|
+
|
8
|
+
state_machine do
|
9
|
+
|
10
|
+
state :initialized do
|
11
|
+
|
12
|
+
event :start_date_changed, :call => :handle_start_date_changed
|
13
|
+
event :forced_start => :running
|
14
|
+
event :start_date_reached => :running, :if => :start_date_reached?
|
15
|
+
event :abort_initialized_contest => :finalized
|
16
|
+
end
|
17
|
+
|
18
|
+
state :running do
|
19
|
+
|
20
|
+
on_entry do |previous_state, trigger_event|
|
21
|
+
running_entry previous_state, trigger_event
|
22
|
+
end
|
23
|
+
|
24
|
+
event :abort_running_contest => :notify_stakeholders
|
25
|
+
event :changed_properties
|
26
|
+
event :keep_users_engaged
|
27
|
+
event :deadline_reached => :notify_organizers, :if => :entries_deadline_reached?
|
28
|
+
event :spots_filled => :notify_organizers, :if => :spots_filled?
|
29
|
+
event :deadline_reached_without_approvals => :notify_pending_users, :if => :entries_deadline_reached_without_approvals?
|
30
|
+
event :deadline_reached_without_entries => :finalized, :if => :entries_deadline_reached_without_entries?
|
31
|
+
end
|
32
|
+
|
33
|
+
state :notify_organizers do
|
34
|
+
on_entry :send_notification_to_organizers
|
35
|
+
event :organizers_notified => :awaiting_organizer_reply
|
36
|
+
end
|
37
|
+
|
38
|
+
state :awaiting_organizer_reply do
|
39
|
+
event :organizer_confirmation_missing => :notify_stakeholders, :if => :organizer_confirmation_deadline_reached?
|
40
|
+
event :keep_organizers_engaged
|
41
|
+
event :organizer_confirmation_received => :notify_approved_users
|
42
|
+
event :organizer_has_more_tickets => :running
|
43
|
+
end
|
44
|
+
|
45
|
+
state :notify_stakeholders do
|
46
|
+
on_entry :send_notification_to_stakeholders
|
47
|
+
event :stakeholders_notified => :cancelled
|
48
|
+
end
|
49
|
+
|
50
|
+
state :cancelled
|
51
|
+
|
52
|
+
state :notify_pending_users do
|
53
|
+
on_entry :send_notification_to_pending_users
|
54
|
+
event :pending_users_notified => :finalized
|
55
|
+
end
|
56
|
+
|
57
|
+
state :notify_approved_users do
|
58
|
+
on_entry :send_notification_to_approved_users
|
59
|
+
event :approved_users_notified => :send_list_to_organizers
|
60
|
+
end
|
61
|
+
|
62
|
+
state :send_list_to_organizers do
|
63
|
+
on_entry :send_guestlist_to_organizers
|
64
|
+
event :list_sent_to_organizers => :awaiting_attendance
|
65
|
+
end
|
66
|
+
|
67
|
+
state :awaiting_attendance do
|
68
|
+
event :remind_to_fill_in_report => :create_report_filling_requests
|
69
|
+
end
|
70
|
+
|
71
|
+
state :create_report_filling_requests do
|
72
|
+
on_entry :send_report_filling_requests
|
73
|
+
event :finalize => :finalized
|
74
|
+
end
|
75
|
+
|
76
|
+
state :finalized
|
77
|
+
|
78
|
+
on_transition do |from,to,trigger_event, duration|
|
79
|
+
benchmark from, to, trigger_event, duration
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
###
|
85
|
+
|
86
|
+
def entries_deadline_reached?
|
87
|
+
true
|
88
|
+
end
|
89
|
+
|
90
|
+
def running_entry previous_state, trigger_event
|
91
|
+
#
|
92
|
+
end
|
93
|
+
|
94
|
+
def benchmark from, to, trigger_event, duration
|
95
|
+
#
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
data/examples/review.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
class Review < ActiveRecord::Base
|
2
|
+
include StateShifter::Definition
|
3
|
+
|
4
|
+
state_machine do
|
5
|
+
|
6
|
+
# first state to be defined is the initial one
|
7
|
+
state :new do
|
8
|
+
event :submit => :awaiting_review
|
9
|
+
end
|
10
|
+
|
11
|
+
state :awaiting_review do
|
12
|
+
event :review => :being_reviewed
|
13
|
+
end
|
14
|
+
|
15
|
+
state :being_reviewed do
|
16
|
+
event :accept => :accepted, :if => :cool_article?
|
17
|
+
event :reject => :rejected, :if => :bad_article?
|
18
|
+
end
|
19
|
+
|
20
|
+
state :accepted
|
21
|
+
state :rejected
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
def cool_article?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
|
29
|
+
def bad_article?
|
30
|
+
false
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
class ReviewCustomPersistence < ActiveRecord::Base
|
2
|
+
include StateShifter::Definition
|
3
|
+
|
4
|
+
persist_attribute :stamp
|
5
|
+
|
6
|
+
state_machine do
|
7
|
+
|
8
|
+
# first state to be defined is the initial one
|
9
|
+
state :new do
|
10
|
+
event :submit => :awaiting_review
|
11
|
+
end
|
12
|
+
|
13
|
+
state :awaiting_review do
|
14
|
+
event :review => :being_reviewed
|
15
|
+
end
|
16
|
+
|
17
|
+
state :being_reviewed do
|
18
|
+
event :accept => :accepted, :if => :cool_article?
|
19
|
+
event :reject => :rejected, :if => :bad_article?
|
20
|
+
end
|
21
|
+
|
22
|
+
state :accepted
|
23
|
+
state :rejected
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
def cool_article?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
|
31
|
+
def bad_article?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module StateShifter
|
2
|
+
module Definition
|
3
|
+
module ActiveRecordIntegrationMethods
|
4
|
+
|
5
|
+
class ::StateShifter::Definition::StatePersistenceAttributeNotPresent < RuntimeError; end
|
6
|
+
|
7
|
+
def get_current_state
|
8
|
+
raise StatePersistenceAttributeNotPresent unless self.attribute_names.include? self.class.persist_attr_name.to_s
|
9
|
+
read_attribute self.class.persist_attr_name
|
10
|
+
end
|
11
|
+
|
12
|
+
def set_current_state value
|
13
|
+
raise StatePersistenceAttributeNotPresent unless self.attribute_names.include? self.class.persist_attr_name.to_s
|
14
|
+
update_attribute self.class.persist_attr_name, value
|
15
|
+
end
|
16
|
+
|
17
|
+
def write_initial_state
|
18
|
+
raise StatePersistenceAttributeNotPresent unless self.attribute_names.include? self.class.persist_attr_name.to_s
|
19
|
+
write_attribute self.class.persist_attr_name, self.class.state_machine_definition.initial_state.name.to_sym
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -3,7 +3,7 @@ module StateShifter
|
|
3
3
|
module InstanceMethods
|
4
4
|
|
5
5
|
def current_state
|
6
|
-
|
6
|
+
get_current_state
|
7
7
|
end
|
8
8
|
|
9
9
|
def get_current_state
|
@@ -19,11 +19,11 @@ module StateShifter
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def next_states
|
22
|
-
_next_states
|
22
|
+
_next_states get_current_state
|
23
23
|
end
|
24
24
|
|
25
25
|
def transitionable_states
|
26
|
-
_next_states
|
26
|
+
_next_states get_current_state, {:check_guards => true}
|
27
27
|
end
|
28
28
|
|
29
29
|
def state_names
|
@@ -52,7 +52,7 @@ module StateShifter
|
|
52
52
|
end
|
53
53
|
|
54
54
|
def current_state_def
|
55
|
-
state_machine_definition.get(:state,
|
55
|
+
state_machine_definition.get(:state, get_current_state)
|
56
56
|
end
|
57
57
|
|
58
58
|
def call_state_entry_callback trigger, old_state
|
@@ -73,7 +73,7 @@ module StateShifter
|
|
73
73
|
_start = Time.now
|
74
74
|
|
75
75
|
# BOOP!
|
76
|
-
old_state =
|
76
|
+
old_state = get_current_state
|
77
77
|
set_current_state args[:to].to_sym
|
78
78
|
#
|
79
79
|
|
@@ -81,7 +81,7 @@ module StateShifter
|
|
81
81
|
|
82
82
|
call_state_entry_callback(args[:trigger], old_state) if current_state_def.has_entry_callback?
|
83
83
|
|
84
|
-
self.instance_exec(old_state,
|
84
|
+
self.instance_exec(old_state, get_current_state, args[:trigger].to_sym, (Time.now - _start), &state_machine_definition.on_transition_proc) if state_machine_definition.has_on_transition_proc?
|
85
85
|
|
86
86
|
true
|
87
87
|
end
|
@@ -4,6 +4,13 @@ module StateShifter
|
|
4
4
|
def self.included klass
|
5
5
|
klass.send :include, InstanceMethods
|
6
6
|
klass.extend ClassMethods
|
7
|
+
|
8
|
+
if Object.const_defined?(:ActiveRecord)
|
9
|
+
if klass < ActiveRecord::Base
|
10
|
+
klass.send :include, ActiveRecordIntegrationMethods
|
11
|
+
klass.before_validation :write_initial_state
|
12
|
+
end
|
13
|
+
end
|
7
14
|
end
|
8
15
|
|
9
16
|
end
|
data/lib/state_shifter.rb
CHANGED
@@ -4,6 +4,7 @@ require 'state_shifter/definition'
|
|
4
4
|
require 'state_shifter/definition/contents'
|
5
5
|
require 'state_shifter/definition/class_methods'
|
6
6
|
require 'state_shifter/definition/instance_methods'
|
7
|
+
require 'state_shifter/definition/active_record_integration_methods'
|
7
8
|
|
8
9
|
class ::StateShifter::TransitionHalted < Exception ; end
|
9
10
|
class ::StateShifter::GuardMethodUndefined < Exception ; end
|
data/spec/state_shifter_spec.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
2
|
require_relative '../examples/simple'
|
3
3
|
require_relative '../examples/advanced'
|
4
|
+
require_relative '../examples/review'
|
5
|
+
require_relative '../examples/review_custom_persistence'
|
4
6
|
|
5
7
|
shared_examples_for 'a simple state machine' do
|
6
8
|
|
7
9
|
before(:each) do
|
8
10
|
@state_machine = described_class.new
|
11
|
+
@state_machine.save if @state_machine.class < ActiveRecord::Base
|
9
12
|
end
|
10
13
|
|
11
14
|
it 'should have all states and events defined' do
|
@@ -19,6 +22,7 @@ shared_examples_for 'a simple state machine' do
|
|
19
22
|
|
20
23
|
@state_machine.submit
|
21
24
|
@state_machine.initial_state.should == :new
|
25
|
+
|
22
26
|
@state_machine.current_state.should == :awaiting_review
|
23
27
|
end
|
24
28
|
|
@@ -162,3 +166,60 @@ describe 'Advanced state machine functionality' do
|
|
162
166
|
|
163
167
|
end
|
164
168
|
|
169
|
+
describe Review do
|
170
|
+
|
171
|
+
before(:all) do
|
172
|
+
ActiveRecord::Migration.verbose = false
|
173
|
+
|
174
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
175
|
+
|
176
|
+
ActiveRecord::Schema.define do
|
177
|
+
create_table :reviews do |t|
|
178
|
+
t.string :current_state
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
it_should_behave_like 'a simple state machine'
|
184
|
+
|
185
|
+
end
|
186
|
+
|
187
|
+
describe ReviewCustomPersistence do
|
188
|
+
|
189
|
+
before(:all) do
|
190
|
+
ActiveRecord::Migration.verbose = false
|
191
|
+
|
192
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
193
|
+
|
194
|
+
ActiveRecord::Schema.define do
|
195
|
+
create_table :review_custom_persistences do |t|
|
196
|
+
t.string :stamp
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
it_should_behave_like 'a simple state machine'
|
202
|
+
|
203
|
+
end
|
204
|
+
|
205
|
+
describe 'Malformed persistence definition' do
|
206
|
+
|
207
|
+
before(:all) do
|
208
|
+
ActiveRecord::Migration.verbose = false
|
209
|
+
|
210
|
+
ActiveRecord::Base.establish_connection :adapter => 'sqlite3', :database => ':memory:'
|
211
|
+
|
212
|
+
ActiveRecord::Schema.define do
|
213
|
+
create_table :missing_persistences do |t|
|
214
|
+
t.string :xyz
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'should complain when the persist attribute is not present' do
|
220
|
+
require_relative '../examples/missing_persistence'
|
221
|
+
review_custom_persistence = MissingPersistence.new
|
222
|
+
lambda { review_custom_persistence.save }.should raise_error(StateShifter::Definition::StatePersistenceAttributeNotPresent)
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
data/state_shifter.gemspec
CHANGED
@@ -5,7 +5,7 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "state_shifter"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.8.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Bruno Antunes"]
|
@@ -29,9 +29,13 @@ Gem::Specification.new do |s|
|
|
29
29
|
"examples/malformed_events.rb",
|
30
30
|
"examples/malformed_persistence.rb",
|
31
31
|
"examples/malformed_states.rb",
|
32
|
+
"examples/missing_persistence.rb",
|
33
|
+
"examples/review.rb",
|
34
|
+
"examples/review_custom_persistence.rb",
|
32
35
|
"examples/simple.rb",
|
33
36
|
"lib/state_shifter.rb",
|
34
37
|
"lib/state_shifter/definition.rb",
|
38
|
+
"lib/state_shifter/definition/active_record_integration_methods.rb",
|
35
39
|
"lib/state_shifter/definition/class_methods.rb",
|
36
40
|
"lib/state_shifter/definition/contents.rb",
|
37
41
|
"lib/state_shifter/definition/instance_methods.rb",
|
@@ -52,6 +56,7 @@ Gem::Specification.new do |s|
|
|
52
56
|
|
53
57
|
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
54
58
|
s.add_development_dependency(%q<activerecord>, ["~> 3.2.x"])
|
59
|
+
s.add_development_dependency(%q<sqlite3>, [">= 0"])
|
55
60
|
s.add_development_dependency(%q<rspec>, ["~> 2.8.0"])
|
56
61
|
s.add_development_dependency(%q<yard>, ["~> 0.7"])
|
57
62
|
s.add_development_dependency(%q<redcarpet>, [">= 0"])
|
@@ -61,6 +66,7 @@ Gem::Specification.new do |s|
|
|
61
66
|
s.add_development_dependency(%q<simplecov>, [">= 0"])
|
62
67
|
else
|
63
68
|
s.add_dependency(%q<activerecord>, ["~> 3.2.x"])
|
69
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
64
70
|
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
65
71
|
s.add_dependency(%q<yard>, ["~> 0.7"])
|
66
72
|
s.add_dependency(%q<redcarpet>, [">= 0"])
|
@@ -71,6 +77,7 @@ Gem::Specification.new do |s|
|
|
71
77
|
end
|
72
78
|
else
|
73
79
|
s.add_dependency(%q<activerecord>, ["~> 3.2.x"])
|
80
|
+
s.add_dependency(%q<sqlite3>, [">= 0"])
|
74
81
|
s.add_dependency(%q<rspec>, ["~> 2.8.0"])
|
75
82
|
s.add_dependency(%q<yard>, ["~> 0.7"])
|
76
83
|
s.add_dependency(%q<redcarpet>, [">= 0"])
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: state_shifter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.8.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ~>
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: 3.2.x
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: sqlite3
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: rspec
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -159,9 +175,13 @@ files:
|
|
159
175
|
- examples/malformed_events.rb
|
160
176
|
- examples/malformed_persistence.rb
|
161
177
|
- examples/malformed_states.rb
|
178
|
+
- examples/missing_persistence.rb
|
179
|
+
- examples/review.rb
|
180
|
+
- examples/review_custom_persistence.rb
|
162
181
|
- examples/simple.rb
|
163
182
|
- lib/state_shifter.rb
|
164
183
|
- lib/state_shifter/definition.rb
|
184
|
+
- lib/state_shifter/definition/active_record_integration_methods.rb
|
165
185
|
- lib/state_shifter/definition/class_methods.rb
|
166
186
|
- lib/state_shifter/definition/contents.rb
|
167
187
|
- lib/state_shifter/definition/instance_methods.rb
|
@@ -185,7 +205,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
185
205
|
version: '0'
|
186
206
|
segments:
|
187
207
|
- 0
|
188
|
-
hash:
|
208
|
+
hash: 3920605756095371187
|
189
209
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
190
210
|
none: false
|
191
211
|
requirements:
|