simple_state_machine 0.6.0.pre → 0.6.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.
@@ -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
+