simple_state_machine 0.6.0.pre → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,17 @@
1
+ module SimpleStateMachine
2
+ module Tools
3
+ require 'cgi'
4
+ module Graphviz
5
+ # Graphviz dot format for rendering as a directional graph
6
+ def to_graphviz_dot
7
+ transitions.map { |t| t.to_graphviz_dot }.sort.join(";")
8
+ end
9
+
10
+ # Generates a url that renders states and events as a directional graph.
11
+ # See http://code.google.com/apis/chart/docs/gallery/graphviz.html
12
+ def google_chart_url
13
+ "http://chart.googleapis.com/chart?cht=gv&chl=digraph{#{::CGI.escape to_graphviz_dot}}"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,44 @@
1
+ module SimpleStateMachine
2
+ module Tools
3
+ module Inspector
4
+ def begin_states
5
+ from_states - to_states
6
+ end
7
+
8
+ def end_states
9
+ to_states - from_states
10
+ end
11
+
12
+ def states
13
+ (to_states + from_states).uniq
14
+ end
15
+
16
+ private
17
+
18
+ def from_states
19
+ to_uniq_sym(sample_transitions.map(&:from))
20
+ end
21
+
22
+ def to_states
23
+ to_uniq_sym(sample_transitions.map(&:to))
24
+ end
25
+
26
+ def to_uniq_sym(array)
27
+ array.map { |state| state.is_a?(String) ? state.to_sym : state }.uniq
28
+ end
29
+
30
+ def sample_transitions
31
+ (@subject || sample_subject).state_machine_definition.send :transitions
32
+ end
33
+
34
+ def sample_subject
35
+ self_class = self.class
36
+ sample = Class.new do
37
+ extend SimpleStateMachine::Mountable
38
+ mount_state_machine self_class
39
+ end
40
+ sample
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,40 @@
1
+ module SimpleStateMachine
2
+ # Defines transitions for events
3
+ class Transition
4
+ attr_reader :event_name, :from, :to
5
+ def initialize(event_name, from, to)
6
+ @event_name = event_name.to_s
7
+ @from = from.is_a?(Class) ? from : from.to_s
8
+ @to = to.to_s
9
+ end
10
+
11
+ # returns true if it's a transition for event_name and subject_state
12
+ def is_transition_for?(event_name, subject_state)
13
+ is_same_event?(event_name) && is_same_from?(subject_state)
14
+ end
15
+
16
+ # returns true if it's a error transition for event_name and error
17
+ def is_error_transition_for?(event_name, error)
18
+ is_same_event?(event_name) && from.is_a?(Class) && error.is_a?(from)
19
+ end
20
+
21
+ def to_s
22
+ "#{from}.#{event_name}! => #{to}"
23
+ end
24
+
25
+ # TODO move to Graphiz module
26
+ def to_graphviz_dot
27
+ %("#{from}"->"#{to}"[label=#{event_name}])
28
+ end
29
+
30
+ private
31
+
32
+ def is_same_event?(event_name)
33
+ self.event_name == event_name.to_s
34
+ end
35
+
36
+ def is_same_from?(subject_from)
37
+ from.to_s == 'all' || subject_from.to_s == from.to_s
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module SimpleStateMachine
2
- VERSION = "0.6.0.pre"
2
+ VERSION = "0.6.0"
3
3
  end
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.platform = Gem::Platform::RUBY
9
9
 
10
10
  s.authors = ["Marek de Heus", "Petrik de Heus"]
11
- s.description = %q{Simple State Machine is a state machine that focuses on events instead of states}
11
+ s.description = %q{A simple DSL to decorate existing methods with state transition guards.}
12
12
  s.email = ["FIX@example.com"]
13
13
  s.homepage = %q{http://github.com/mdh/ssm}
14
14
  s.extra_rdoc_files = [
Binary file
@@ -1,11 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
-
3
- require "rubygems"
4
- require "bundler"
5
- Bundler.require
6
- #Bundler.setup(:test)#, :activerecord)
7
- require 'active_record'
8
- require 'examples/user'
1
+ require 'spec_helper'
9
2
 
10
3
  ActiveRecord::Base.logger = Logger.new "test.log"
11
4
  ActiveRecord::Base.establish_connection(:adapter => "sqlite3",
@@ -114,7 +107,7 @@ describe ActiveRecord do
114
107
  it "rollsback if an exception is raised" do
115
108
  user_class = Class.new(User)
116
109
  user_class.instance_eval do
117
- define_method :without_managed_state_invite do
110
+ define_method :invite_without_managed_state do
118
111
  User.create!(:name => 'name2') #this shouldn't be persisted
119
112
  User.create! #this should raise an error
120
113
  end
@@ -182,7 +175,7 @@ describe ActiveRecord do
182
175
  it "rollsback if an exception is raised" do
183
176
  user_class = Class.new(User)
184
177
  user_class.instance_eval do
185
- define_method :without_managed_state_invite do
178
+ define_method :invite_without_managed_state do
186
179
  User.create!(:name => 'name2') #this shouldn't be persisted
187
180
  User.create! #this should raise an error
188
181
  end
@@ -242,16 +235,16 @@ describe ActiveRecord do
242
235
 
243
236
  it "persists transitions" do
244
237
  ticket = Ticket.create!
245
- ticket.should be_open
238
+ ticket.ssm_state.should == 'open'
246
239
  ticket.close!.should == true
247
- ticket.should be_closed
240
+ ticket.ssm_state.should == 'closed'
248
241
  end
249
242
 
250
243
  it "persists transitions with !" do
251
244
  ticket = Ticket.create!
252
- ticket.should be_open
245
+ ticket.ssm_state.should == 'open'
253
246
  ticket.close!
254
- ticket.should be_closed
247
+ ticket.ssm_state.should == 'closed'
255
248
  end
256
249
 
257
250
  end
@@ -1,6 +1,6 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
- describe SimpleStateMachine::Decorator do
3
+ describe SimpleStateMachine::Decorator::Default do
4
4
 
5
5
  context "given a class" do
6
6
  before do
@@ -10,7 +10,7 @@ describe SimpleStateMachine::Decorator do
10
10
  @state_machine_definition ||= SimpleStateMachine::StateMachineDefinition.new
11
11
  end
12
12
  end
13
- decorator = SimpleStateMachine::Decorator.new klass
13
+ decorator = SimpleStateMachine::Decorator::Default.new klass
14
14
  decorator.decorate SimpleStateMachine::Transition.new(:event, :state1, :state2)
15
15
  @instance = klass.new
16
16
  @instance.state = 'state1'
@@ -35,7 +35,7 @@ describe SimpleStateMachine::Decorator do
35
35
  @instance.state1?.should == true
36
36
  @instance.state2?.should == false
37
37
  end
38
-
38
+
39
39
  it "defines an event method" do
40
40
  @instance.should respond_to(:event)
41
41
  end
@@ -55,7 +55,7 @@ describe SimpleStateMachine::Decorator do
55
55
  def event() "predefined method" end
56
56
  end
57
57
  transition = SimpleStateMachine::Transition.new(:event, :state1, :state2)
58
- decorator = SimpleStateMachine::Decorator.new klass
58
+ decorator = SimpleStateMachine::Decorator::Default.new klass
59
59
  decorator.decorate transition
60
60
  klass.state_machine_definition.transitions << transition
61
61
  @instance = klass.new
@@ -87,7 +87,7 @@ describe SimpleStateMachine::Decorator do
87
87
  end
88
88
  end
89
89
  end
90
-
90
+
91
91
  context "given a class with predefined protected methods" do
92
92
  before do
93
93
  klass = Class.new do
@@ -102,7 +102,7 @@ describe SimpleStateMachine::Decorator do
102
102
  def event() "predefined method" end
103
103
  end
104
104
  transition = SimpleStateMachine::Transition.new(:event, :state1, :state2)
105
- decorator = SimpleStateMachine::Decorator.new klass
105
+ decorator = SimpleStateMachine::Decorator::Default.new klass
106
106
  decorator.decorate transition
107
107
  klass.state_machine_definition.transitions << transition
108
108
  @instance = klass.new
@@ -154,7 +154,7 @@ describe SimpleStateMachine::Decorator do
154
154
  def event() "predefined method" end
155
155
  end
156
156
  transition = SimpleStateMachine::Transition.new(:event, :state1, :state2)
157
- decorator = SimpleStateMachine::Decorator.new klass
157
+ decorator = SimpleStateMachine::Decorator::Default.new klass
158
158
  decorator.decorate transition
159
159
  klass.state_machine_definition.transitions << transition
160
160
  @instance = klass.new
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe "Examples" do
4
4
  describe "TrafficLight" do
@@ -13,7 +13,7 @@ describe "Examples" do
13
13
  tl.should be_green
14
14
  end
15
15
  end
16
-
16
+
17
17
  describe "Lamp" do
18
18
  it "changes between :on and :off" do
19
19
  lamp = Lamp.new
@@ -28,13 +28,13 @@ describe "Examples" do
28
28
  lamp.should be_off
29
29
  end
30
30
  end
31
-
31
+
32
32
  describe "Conversation" do
33
33
  it "is :unread by default" do
34
34
  conversation = Conversation.new
35
35
  conversation.should be_unread
36
36
  end
37
-
37
+
38
38
  it "changes to read on view" do
39
39
  conversation = Conversation.new
40
40
  conversation.view
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe "Mountable" do
4
4
  before do
@@ -6,14 +6,14 @@ describe "Mountable" do
6
6
  event(:event, :state1 => :state2)
7
7
 
8
8
  def decorator_class
9
- SimpleStateMachine::Decorator
9
+ SimpleStateMachine::Decorator::Default
10
10
  end
11
11
  end
12
12
  klass = Class.new do
13
13
  attr_accessor :event_called
14
14
  extend SimpleStateMachine::Mountable
15
15
  mount_state_machine mountable_class
16
- def without_managed_state_event
16
+ def event_without_managed_state
17
17
  @event_called = true
18
18
  end
19
19
  end
@@ -1,5 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
- require 'cgi'
1
+ require 'spec_helper'
3
2
 
4
3
  describe SimpleStateMachine do
5
4
 
@@ -1,7 +1,13 @@
1
+ require "rubygems"
2
+ require "bundler"
3
+ Bundler.require
4
+ #Bundler.setup(:test)#, :activerecord)
5
+ require 'active_record'
1
6
  $LOAD_PATH.unshift(File.dirname(__FILE__))
2
7
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
8
  require 'simple_state_machine'
4
9
  require 'examples/conversation'
5
10
  require 'examples/lamp'
6
11
  require 'examples/traffic_light'
12
+ require 'examples/user'
7
13
 
@@ -1,4 +1,4 @@
1
- require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
1
+ require 'spec_helper'
2
2
 
3
3
  describe SimpleStateMachine::StateMachineDefinition do
4
4
 
@@ -86,70 +86,6 @@ describe SimpleStateMachine::StateMachineDefinition do
86
86
  end
87
87
  end
88
88
 
89
- describe "#begin_states" do
90
- before do
91
- @klass = Class.new(SimpleStateMachine::StateMachineDefinition) do
92
- def add_events
93
- define_event(:event_a, :state1 => :state2)
94
- define_event(:event_b, :state2 => :state3)
95
- define_event(:event_c, :state1 => :state3)
96
- define_event(:event_c, RuntimeError => :state3)
97
- end
98
-
99
- def decorator_class
100
- SimpleStateMachine::Decorator
101
- end
102
- end
103
- end
104
-
105
- it "returns all 'from' states that aren't 'to' states" do
106
- @klass.new.begin_states.should == [:state1, RuntimeError]
107
- end
108
- end
109
-
110
- describe "#end_states" do
111
- before do
112
- @klass = Class.new(SimpleStateMachine::StateMachineDefinition) do
113
- def add_events
114
- define_event(:event_a, :state1 => :state2)
115
- define_event(:event_b, :state2 => :state3)
116
- define_event(:event_c, :state1 => :state3)
117
- end
118
-
119
- def decorator_class
120
- SimpleStateMachine::Decorator
121
- end
122
-
123
- end
124
- end
125
-
126
- it "returns all 'to' states that aren't 'from' states" do
127
- @klass.new.end_states.should == [:state3]
128
- end
129
- end
130
-
131
- describe "#states" do
132
- before do
133
- @klass = Class.new(SimpleStateMachine::StateMachineDefinition) do
134
- def add_events
135
- define_event(:event_a, :state1 => :state2)
136
- define_event(:event_b, :state2 => :state3)
137
- define_event(:event_c, :state1 => :state3)
138
- end
139
-
140
- def decorator_class
141
- SimpleStateMachine::Decorator
142
- end
143
-
144
- end
145
- end
146
-
147
- it "returns all states" do
148
- @klass.new.states.map(&:to_s).sort.should == %w{state1 state2 state3}
149
- end
150
- end
151
-
152
-
153
89
  describe "#transitions" do
154
90
  it "has a list of transitions" do
155
91
  @smd.transitions.should be_a(Array)
@@ -163,16 +99,5 @@ describe SimpleStateMachine::StateMachineDefinition do
163
99
  end
164
100
  end
165
101
 
166
- describe "#to_graphviz_dot" do
167
- it "converts to graphviz dot format" do
168
- @smd.to_graphviz_dot.should == %("state1"->"state2"[label=event1];"state2"->"state3"[label=event1])
169
- end
170
- end
171
-
172
- describe "#google_chart_url" do
173
- it "shows the state and event dependencies as a Google chart" do
174
- @smd.google_chart_url.should == "http://chart.googleapis.com/chart?cht=gv&chl=digraph{#{::CGI.escape @smd.to_graphviz_dot}}"
175
- end
176
- end
177
102
  end
178
103
 
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe SimpleStateMachine::Tools::Graphviz do
4
+
5
+ before do
6
+ @klass = Class.new do
7
+ extend SimpleStateMachine
8
+ def initialize(state = 'state1')
9
+ @state = state
10
+ end
11
+ event :event1, :state1 => :state2, :state2 => :state3
12
+ end
13
+ @smd = @klass.state_machine_definition
14
+ end
15
+
16
+ describe "#to_graphviz_dot" do
17
+ it "converts to graphviz dot format" do
18
+ @smd.to_graphviz_dot.should == %("state1"->"state2"[label=event1];"state2"->"state3"[label=event1])
19
+ end
20
+ end
21
+
22
+ describe "#google_chart_url" do
23
+ it "shows the state and event dependencies as a Google chart" do
24
+ @smd.google_chart_url.should == "http://chart.googleapis.com/chart?cht=gv&chl=digraph{#{::CGI.escape @smd.to_graphviz_dot}}"
25
+ end
26
+ end
27
+ end
28
+
29
+
@@ -0,0 +1,70 @@
1
+ require 'spec_helper'
2
+
3
+ describe SimpleStateMachine::Tools::Inspector do
4
+
5
+ describe "#begin_states" do
6
+ before do
7
+ @klass = Class.new(SimpleStateMachine::StateMachineDefinition) do
8
+ def add_events
9
+ define_event(:event_a, :state1 => :state2)
10
+ define_event(:event_b, :state2 => :state3)
11
+ define_event(:event_c, :state1 => :state3)
12
+ define_event(:event_c, RuntimeError => :state3)
13
+ end
14
+
15
+ def decorator_class
16
+ SimpleStateMachine::Decorator::Default
17
+ end
18
+ end
19
+ end
20
+
21
+ it "returns all 'from' states that aren't 'to' states" do
22
+ @klass.new.begin_states.should == [:state1, RuntimeError]
23
+ end
24
+ end
25
+
26
+ describe "#end_states" do
27
+ before do
28
+ @klass = Class.new(SimpleStateMachine::StateMachineDefinition) do
29
+ def add_events
30
+ define_event(:event_a, :state1 => :state2)
31
+ define_event(:event_b, :state2 => :state3)
32
+ define_event(:event_c, :state1 => :state3)
33
+ end
34
+
35
+ def decorator_class
36
+ SimpleStateMachine::Decorator::Default
37
+ end
38
+
39
+ end
40
+ end
41
+
42
+ it "returns all 'to' states that aren't 'from' states" do
43
+ @klass.new.end_states.should == [:state3]
44
+ end
45
+ end
46
+
47
+ describe "#states" do
48
+ before do
49
+ @klass = Class.new(SimpleStateMachine::StateMachineDefinition) do
50
+ def add_events
51
+ define_event(:event_a, :state1 => :state2)
52
+ define_event(:event_b, :state2 => :state3)
53
+ define_event(:event_c, :state1 => :state3)
54
+ end
55
+
56
+ def decorator_class
57
+ SimpleStateMachine::Decorator::Default
58
+ end
59
+
60
+ end
61
+ end
62
+
63
+ it "returns all states" do
64
+ @klass.new.states.map(&:to_s).sort.should == %w{state1 state2 state3}
65
+ end
66
+ end
67
+
68
+ end
69
+
70
+