statemachine 1.0.0 → 1.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +11 -0
- data/README.rdoc +1 -1
- data/lib/statemachine/generate/dot_graph.rb +1 -0
- data/lib/statemachine/generate/dot_graph/dot_graph_statemachine.rb +127 -0
- data/lib/statemachine/generate/java/java_statemachine.rb +6 -4
- data/lib/statemachine/generate/util.rb +14 -1
- data/lib/statemachine/statemachine.rb +33 -33
- data/lib/statemachine/stub_context.rb +3 -1
- data/lib/statemachine/version.rb +2 -2
- data/spec/generate/dot_graph/dot_graph_stagemachine_spec.rb +27 -0
- data/spec/generate/java/java_statemachine_spec.rb +6 -6
- data/spec/spec_helper.rb +14 -6
- metadata +8 -5
data/CHANGES
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
= Statemachine Changelog
|
2
2
|
|
3
|
+
== Version 1.1.0
|
4
|
+
|
5
|
+
DotGraph
|
6
|
+
* DotGraph generator was added to generate graphs of statemachines using Omnigraffle.
|
7
|
+
* Fixed bug in Java generator where Statenames where not formated correctly.
|
8
|
+
|
9
|
+
== Version 1.0.0
|
10
|
+
|
11
|
+
Generator
|
12
|
+
* Java generator was added. Statemachines defined in the Ruby DSL can generate Java code.
|
13
|
+
|
3
14
|
== Version 0.4.2
|
4
15
|
|
5
16
|
Simple Fixes
|
data/README.rdoc
CHANGED
@@ -21,7 +21,7 @@ at http://blog.8thlight.com/articles/2006/11/17/understanding-statemachines-part
|
|
21
21
|
|
22
22
|
== License
|
23
23
|
|
24
|
-
Copyright (C) 2006 Micah Martin
|
24
|
+
Copyright (C) 2006-2010 Micah Martin
|
25
25
|
|
26
26
|
This library is free software; you can redistribute it and/or
|
27
27
|
modify it under the terms of the GNU Lesser General Public
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'statemachine/generate/dot_graph/dot_graph_statemachine'
|
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'statemachine/generate/util'
|
2
|
+
require 'statemachine/generate/src_builder'
|
3
|
+
|
4
|
+
module Statemachine
|
5
|
+
class Statemachine
|
6
|
+
|
7
|
+
attr_reader :states
|
8
|
+
|
9
|
+
def to_dot(options = {})
|
10
|
+
generator = Generate::DotGraph::DotGraphStatemachine.new(self, options)
|
11
|
+
generator.generate!
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module Generate
|
17
|
+
module DotGraph
|
18
|
+
|
19
|
+
class DotGraphStatemachine
|
20
|
+
|
21
|
+
include Generate::Util
|
22
|
+
|
23
|
+
def initialize(sm, options)
|
24
|
+
@sm = sm
|
25
|
+
@output_dir = options[:output]
|
26
|
+
raise "Please specify an output directory. (:output => 'where/you/want/your/code')" if @output_dir.nil?
|
27
|
+
raise "Output dir '#{@output_dir}' doesn't exist." if !File.exist?(@output_dir)
|
28
|
+
end
|
29
|
+
|
30
|
+
def generate!
|
31
|
+
explore_sm
|
32
|
+
save_output(src_file("main"), build_full_graph)
|
33
|
+
@sm.states.values.each do |state|
|
34
|
+
save_output(src_file("#{state.id}"), build_state_graph(state))
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def explore_sm
|
41
|
+
@nodes = []
|
42
|
+
@transitions = []
|
43
|
+
@sm.states.values.each { |state|
|
44
|
+
state.transitions.values.each { |transition|
|
45
|
+
@nodes << transition.origin_id
|
46
|
+
@nodes << transition.destination_id
|
47
|
+
@transitions << transition
|
48
|
+
}
|
49
|
+
}
|
50
|
+
@nodes = @nodes.uniq
|
51
|
+
end
|
52
|
+
|
53
|
+
def build_full_graph
|
54
|
+
builder = Generate::SrcBuilder.new
|
55
|
+
|
56
|
+
add_graph_header(builder, "main")
|
57
|
+
|
58
|
+
@nodes.each { |node| add_node(builder, node) }
|
59
|
+
builder << endl
|
60
|
+
|
61
|
+
@transitions.each do |transition|
|
62
|
+
add_transition(builder, transition)
|
63
|
+
end
|
64
|
+
|
65
|
+
add_graph_footer(builder)
|
66
|
+
|
67
|
+
return builder.to_s
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_state_graph(state)
|
71
|
+
builder = Generate::SrcBuilder.new
|
72
|
+
|
73
|
+
add_graph_header(builder, state.id)
|
74
|
+
|
75
|
+
state.transitions.values.each do |transition|
|
76
|
+
add_transition(builder, transition)
|
77
|
+
end
|
78
|
+
|
79
|
+
add_graph_footer(builder)
|
80
|
+
|
81
|
+
return builder.to_s
|
82
|
+
end
|
83
|
+
|
84
|
+
def add_graph_header(builder, graph_name)
|
85
|
+
builder << "digraph #{graph_name} {" << endl
|
86
|
+
builder.indent!
|
87
|
+
end
|
88
|
+
|
89
|
+
def add_graph_footer(builder)
|
90
|
+
builder.undent!
|
91
|
+
builder << "}" << endl
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_node(builder, node)
|
95
|
+
builder << node
|
96
|
+
builder << " [ href = \"#{node}.svg\"]"
|
97
|
+
builder << endl
|
98
|
+
end
|
99
|
+
|
100
|
+
def add_transition(builder, transition)
|
101
|
+
builder << transition.origin_id
|
102
|
+
builder << " -> "
|
103
|
+
builder << transition.destination_id
|
104
|
+
builder << " [ "
|
105
|
+
builder << "label = #{transition.event} "
|
106
|
+
builder << "]"
|
107
|
+
builder << endl
|
108
|
+
end
|
109
|
+
|
110
|
+
def src_file(name)
|
111
|
+
return name if @output_dir.nil?
|
112
|
+
path = @output_dir
|
113
|
+
answer = File.join(path, "#{name}.dot")
|
114
|
+
return answer
|
115
|
+
end
|
116
|
+
|
117
|
+
def save_output(filename, content)
|
118
|
+
if @output_dir.nil?
|
119
|
+
say "Writing to file: #{filename}"
|
120
|
+
else
|
121
|
+
create_file(filename, content)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -37,6 +37,7 @@ module Statemachine
|
|
37
37
|
explore_sm
|
38
38
|
create_file(src_file(@classname), build_statemachine_src)
|
39
39
|
create_file(src_file(@context_classname), build_context_src)
|
40
|
+
say "Statemachine generated."
|
40
41
|
end
|
41
42
|
|
42
43
|
private ###########################################
|
@@ -88,8 +89,8 @@ module Statemachine
|
|
88
89
|
src << "// Instance variables" << endl
|
89
90
|
concrete_states = @sm.states.values.reject { |state| state.id.nil? || !state.concrete? }.sort { |a, b| a.id <=> b.id }
|
90
91
|
concrete_states.each do |state|
|
91
|
-
name = state.id.to_s
|
92
|
-
src << "public final State #{name.upcase} = new #{name}State(this);" << endl
|
92
|
+
name = state.id.to_s
|
93
|
+
src << "public final State #{name.upcase} = new #{name.camalized}State(this);" << endl
|
93
94
|
end
|
94
95
|
superstates = @sm.states.values.reject { |state| state.concrete? }.sort { |a, b| a.id <=> b.id }
|
95
96
|
superstates.each do |superstate|
|
@@ -193,7 +194,7 @@ module Statemachine
|
|
193
194
|
src << "statemachine.getContext().#{transition.action.to_s.camalized(:lower)}();" << endl if transition.action
|
194
195
|
src << "statemachine.setState(statemachine.#{transition.destination_id.to_s.upcase});" << endl
|
195
196
|
entries.each do |entry|
|
196
|
-
src << "statemachine.getContext().#{entry.entry_action.to_s.camalized(:lower)}();" << endl if entry.entry_action
|
197
|
+
src << "statemachine.getContext().#{entry.entry_action.to_s.camalized(:lower)}();" << endl if entry.entry_action
|
197
198
|
end
|
198
199
|
end
|
199
200
|
end
|
@@ -244,6 +245,7 @@ module Statemachine
|
|
244
245
|
|
245
246
|
def create_file(filename, content)
|
246
247
|
establish_directory(File.dirname(filename))
|
248
|
+
say "Writing to file: #{filename}"
|
247
249
|
File.open(filename, 'w') do |file|
|
248
250
|
file.write(content)
|
249
251
|
end
|
@@ -260,4 +262,4 @@ module Statemachine
|
|
260
262
|
end
|
261
263
|
end
|
262
264
|
end
|
263
|
-
end
|
265
|
+
end
|
@@ -4,6 +4,13 @@ module Statemachine
|
|
4
4
|
module Generate
|
5
5
|
module Util
|
6
6
|
|
7
|
+
def create_file(filename, content)
|
8
|
+
establish_directory(File.dirname(filename))
|
9
|
+
File.open(filename, 'w') do |file|
|
10
|
+
file.write(content)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
7
14
|
def establish_directory(path)
|
8
15
|
return if File.exist?(path)
|
9
16
|
establish_directory(File.dirname(path))
|
@@ -18,6 +25,12 @@ module Statemachine
|
|
18
25
|
return :endl
|
19
26
|
end
|
20
27
|
|
28
|
+
def say(message)
|
29
|
+
if !defined?($IS_TEST)
|
30
|
+
puts message
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
21
34
|
end
|
22
35
|
end
|
23
36
|
end
|
@@ -34,4 +47,4 @@ class Symbol
|
|
34
47
|
def <=>(other)
|
35
48
|
return to_s <=> other.to_s
|
36
49
|
end
|
37
|
-
end
|
50
|
+
end
|
@@ -1,52 +1,52 @@
|
|
1
1
|
module Statemachine
|
2
|
-
|
2
|
+
|
3
3
|
class StatemachineException < Exception
|
4
4
|
end
|
5
|
-
|
6
|
-
class TransitionMissingException < Exception
|
5
|
+
|
6
|
+
class TransitionMissingException < Exception
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
# Used at runtime to execute the behavior of the statemachine.
|
10
10
|
# Should be created by using the Statemachine.build method.
|
11
|
-
#
|
11
|
+
#
|
12
12
|
# sm = Statemachine.build do
|
13
13
|
# trans :locked, :coin, :unlocked
|
14
14
|
# trans :unlocked, :pass, :locked:
|
15
15
|
# end
|
16
|
-
#
|
16
|
+
#
|
17
17
|
# sm.coin
|
18
18
|
# sm.state
|
19
|
-
#
|
19
|
+
#
|
20
20
|
# This class will accept any method that corresponds to an event. If the
|
21
21
|
# current state respons to the event, the appropriate transtion will be invoked.
|
22
22
|
# Otherwise an exception will be raised.
|
23
23
|
class Statemachine
|
24
24
|
include ActionInvokation
|
25
|
-
|
26
|
-
# The tracer is an IO object. The statemachine will write run time execution
|
25
|
+
|
26
|
+
# The tracer is an IO object. The statemachine will write run time execution
|
27
27
|
# information to the +tracer+. Can be helpful in debugging. Defaults to nil.
|
28
28
|
attr_accessor :tracer
|
29
|
-
|
29
|
+
|
30
30
|
# Provides access to the +context+ of the statemachine. The context is a object
|
31
31
|
# where all actions will be invoked. This provides a way to separate logic from
|
32
32
|
# behavior. The statemachine is responsible for all the logic and the context
|
33
|
-
# is responsible for all the behavior.
|
33
|
+
# is responsible for all the behavior.
|
34
34
|
attr_accessor :context
|
35
|
-
|
35
|
+
|
36
36
|
attr_reader :root #:nodoc:
|
37
|
-
|
37
|
+
|
38
38
|
# Should not be called directly. Instances of Statemachine::Statemachine are created
|
39
39
|
# through the Statemachine.build method.
|
40
40
|
def initialize(root = Superstate.new(:root, nil, self))
|
41
41
|
@root = root
|
42
42
|
@states = {}
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
# Returns the id of the startstate of the statemachine.
|
46
46
|
def startstate
|
47
47
|
return @root.startstate_id
|
48
48
|
end
|
49
|
-
|
49
|
+
|
50
50
|
# Resets the statemachine back to its starting state.
|
51
51
|
def reset
|
52
52
|
@state = get_state(@root.startstate_id)
|
@@ -55,15 +55,15 @@ module Statemachine
|
|
55
55
|
end
|
56
56
|
raise StatemachineException.new("The state machine doesn't know where to start. Try setting the startstate.") if @state == nil
|
57
57
|
@state.enter
|
58
|
-
|
58
|
+
|
59
59
|
@states.values.each { |state| state.reset }
|
60
60
|
end
|
61
|
-
|
61
|
+
|
62
62
|
# Return the id of the current state of the statemachine.
|
63
63
|
def state
|
64
64
|
return @state.id
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
# You may change the state of the statemachine by using this method. The parameter should be
|
68
68
|
# the id of the desired state.
|
69
69
|
def state= value
|
@@ -75,10 +75,10 @@ module Statemachine
|
|
75
75
|
@state = @states[value.to_sym]
|
76
76
|
end
|
77
77
|
end
|
78
|
-
|
78
|
+
|
79
79
|
# The key method to exercise the statemachine. Any extra arguments supplied will be passed into
|
80
80
|
# any actions associated with the transition.
|
81
|
-
#
|
81
|
+
#
|
82
82
|
# Alternatively to this method, you may invoke methods, names the same as the event, on the statemachine.
|
83
83
|
# The advantage of using +process_event+ is that errors messages are more informative.
|
84
84
|
def process_event(event, *args)
|
@@ -95,11 +95,11 @@ module Statemachine
|
|
95
95
|
raise StatemachineException.new("The state machine isn't in any state while processing the '#{event}' event.")
|
96
96
|
end
|
97
97
|
end
|
98
|
-
|
98
|
+
|
99
99
|
def trace(message) #:nodoc:
|
100
100
|
@tracer.puts message if @tracer
|
101
101
|
end
|
102
|
-
|
102
|
+
|
103
103
|
def get_state(id) #:nodoc:
|
104
104
|
if @states.has_key? id
|
105
105
|
return @states[id]
|
@@ -114,11 +114,11 @@ module Statemachine
|
|
114
114
|
return state
|
115
115
|
end
|
116
116
|
end
|
117
|
-
|
117
|
+
|
118
118
|
def add_state(state) #:nodoc:
|
119
119
|
@states[state.id] = state
|
120
120
|
end
|
121
|
-
|
121
|
+
|
122
122
|
def has_state(id) #:nodoc:
|
123
123
|
if(is_history_state_id?(id))
|
124
124
|
return @states.has_key?(base_id(id))
|
@@ -126,13 +126,13 @@ module Statemachine
|
|
126
126
|
return @states.has_key?(id)
|
127
127
|
end
|
128
128
|
end
|
129
|
-
|
129
|
+
|
130
130
|
def respond_to?(message)
|
131
131
|
return true if super(message)
|
132
132
|
return true if @state and @state.transition_for(message)
|
133
133
|
return false
|
134
134
|
end
|
135
|
-
|
135
|
+
|
136
136
|
def method_missing(message, *args) #:nodoc:
|
137
137
|
if @state and @state.transition_for(message)
|
138
138
|
process_event(message.to_sym, *args)
|
@@ -147,17 +147,17 @@ module Statemachine
|
|
147
147
|
end
|
148
148
|
end
|
149
149
|
end
|
150
|
-
|
151
|
-
private
|
152
|
-
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
153
|
def is_history_state_id?(id)
|
154
154
|
return id.to_s[-2..-1] == "_H"
|
155
155
|
end
|
156
|
-
|
156
|
+
|
157
157
|
def base_id(history_id)
|
158
158
|
return history_id.to_s[0...-2].to_sym
|
159
159
|
end
|
160
|
-
|
160
|
+
|
161
161
|
def load_history(superstate)
|
162
162
|
100.times do
|
163
163
|
history = superstate.history_id ? get_state(superstate.history_id) : nil
|
@@ -170,6 +170,6 @@ module Statemachine
|
|
170
170
|
end
|
171
171
|
raise StatemachineException.new("No history found within 100 levels of nested superstates.")
|
172
172
|
end
|
173
|
-
|
173
|
+
|
174
174
|
end
|
175
|
-
end
|
175
|
+
end
|
@@ -16,7 +16,9 @@ module Statemachine
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def __generic_method(name, *args)
|
19
|
-
|
19
|
+
if !defined?($IS_TEST)
|
20
|
+
puts "action invoked: #{name}(#{args.join(", ")}) #{block_given? ? "with block" : ""}" if @verbose
|
21
|
+
end
|
20
22
|
end
|
21
23
|
|
22
24
|
end
|
data/lib/statemachine/version.rb
CHANGED
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../spec_helper'
|
2
|
+
require 'statemachine/generate/dot_graph/dot_graph_statemachine'
|
3
|
+
|
4
|
+
describe Statemachine::Statemachine, "(Turn Stile)" do
|
5
|
+
include TurnstileStatemachine
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
remove_test_dir("dot")
|
9
|
+
@output = test_dir("dot")
|
10
|
+
create_turnstile
|
11
|
+
end
|
12
|
+
|
13
|
+
# it "should output to console when no output dir provided" do
|
14
|
+
# # Note - this test doesn't verify output to the console, but it does
|
15
|
+
# # ensure that the to_dot call does not fail if not output is provided.
|
16
|
+
# @sm.to_dot
|
17
|
+
# end
|
18
|
+
|
19
|
+
it "should generate a basic graph declaration" do
|
20
|
+
@sm.to_dot(:output => @output)
|
21
|
+
|
22
|
+
dot = load_lines(@output, "main.dot")
|
23
|
+
|
24
|
+
dot.should_not equal(nil)
|
25
|
+
dot[0].include?("digraph").should == true
|
26
|
+
end
|
27
|
+
end
|
@@ -48,11 +48,11 @@ describe Statemachine::Statemachine, "(Java)" do
|
|
48
48
|
@sm.to_java(:output => @output, :name => "JavaTurnstile", :package => "test.turnstile")
|
49
49
|
end
|
50
50
|
|
51
|
-
def load_lines(*segs)
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
51
|
+
# def load_lines(*segs)
|
52
|
+
# filename = File.join(*segs)
|
53
|
+
# File.should exist( filename)
|
54
|
+
# return IO.read(filename).split("\n")
|
55
|
+
# end
|
56
56
|
|
57
57
|
def empty_sm_lines
|
58
58
|
@sm.to_java(:name => "JavaTest", :output => @output, :package => "test.blank")
|
@@ -345,4 +345,4 @@ describe Statemachine::Statemachine, "(Java)" do
|
|
345
345
|
lines.shift.should == " }"
|
346
346
|
end
|
347
347
|
|
348
|
-
end
|
348
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,6 +3,8 @@ require 'rubygems'
|
|
3
3
|
require 'spec'
|
4
4
|
require 'statemachine'
|
5
5
|
|
6
|
+
$IS_TEST = true
|
7
|
+
|
6
8
|
def check_transition(transition, origin_id, destination_id, event, action)
|
7
9
|
transition.should_not equal(nil)
|
8
10
|
transition.event.should equal(event)
|
@@ -12,20 +14,20 @@ def check_transition(transition, origin_id, destination_id, event, action)
|
|
12
14
|
end
|
13
15
|
|
14
16
|
module SwitchStatemachine
|
15
|
-
|
17
|
+
|
16
18
|
def create_switch
|
17
19
|
@status = "off"
|
18
20
|
@sm = Statemachine.build do
|
19
|
-
trans :off, :toggle, :on, Proc.new { @status = "on" }
|
21
|
+
trans :off, :toggle, :on, Proc.new { @status = "on" }
|
20
22
|
trans :on, :toggle, :off, Proc.new { @status = "off" }
|
21
23
|
end
|
22
24
|
@sm.context = self
|
23
25
|
end
|
24
|
-
|
26
|
+
|
25
27
|
end
|
26
28
|
|
27
29
|
module TurnstileStatemachine
|
28
|
-
|
30
|
+
|
29
31
|
def create_turnstile
|
30
32
|
@locked = true
|
31
33
|
@alarm_status = false
|
@@ -34,7 +36,7 @@ module TurnstileStatemachine
|
|
34
36
|
@unlock = "@locked = false"
|
35
37
|
@alarm = "@alarm_status = true"
|
36
38
|
@thankyou = "@thankyou_status = true"
|
37
|
-
|
39
|
+
|
38
40
|
@sm = Statemachine.build do
|
39
41
|
trans :locked, :coin, :unlocked, "@locked = false"
|
40
42
|
trans :unlocked, :pass, :locked, "@locked = true"
|
@@ -58,4 +60,10 @@ end
|
|
58
60
|
|
59
61
|
def remove_test_dir(name)
|
60
62
|
system "rm -rf #{test_dir(name)}" if File.exist?(test_dir(name))
|
61
|
-
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def load_lines(*segs)
|
66
|
+
filename = File.join(*segs)
|
67
|
+
File.should exist( filename)
|
68
|
+
return IO.read(filename).split("\n")
|
69
|
+
end
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 1.
|
7
|
+
- 1
|
8
|
+
- 1
|
9
|
+
version: 1.1.1
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Micah Martin
|
@@ -14,7 +14,7 @@ autorequire: statemachine
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date: 2010-
|
17
|
+
date: 2010-09-29 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies: []
|
20
20
|
|
@@ -34,6 +34,8 @@ files:
|
|
34
34
|
- TODO
|
35
35
|
- lib/statemachine/action_invokation.rb
|
36
36
|
- lib/statemachine/builder.rb
|
37
|
+
- lib/statemachine/generate/dot_graph/dot_graph_statemachine.rb
|
38
|
+
- lib/statemachine/generate/dot_graph.rb
|
37
39
|
- lib/statemachine/generate/java/java_statemachine.rb
|
38
40
|
- lib/statemachine/generate/java.rb
|
39
41
|
- lib/statemachine/generate/src_builder.rb
|
@@ -48,6 +50,7 @@ files:
|
|
48
50
|
- spec/action_invokation_spec.rb
|
49
51
|
- spec/builder_spec.rb
|
50
52
|
- spec/default_transition_spec.rb
|
53
|
+
- spec/generate/dot_graph/dot_graph_stagemachine_spec.rb
|
51
54
|
- spec/generate/java/java_statemachine_spec.rb
|
52
55
|
- spec/history_spec.rb
|
53
56
|
- spec/sm_action_parameterization_spec.rb
|
@@ -87,7 +90,7 @@ rubyforge_project: statemachine
|
|
87
90
|
rubygems_version: 1.3.6
|
88
91
|
signing_key:
|
89
92
|
specification_version: 3
|
90
|
-
summary: Statemachine-1.
|
93
|
+
summary: Statemachine-1.1.1 - Statemachine Library for Ruby http://slagyr.github.com/statemachine
|
91
94
|
test_files:
|
92
95
|
- spec/action_invokation_spec.rb
|
93
96
|
- spec/builder_spec.rb
|