statemachine 0.4.1 → 0.5.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/CHANGES +6 -0
- data/{README → README.rdoc} +0 -0
- data/Rakefile +13 -40
- data/lib/statemachine/builder.rb +9 -1
- data/lib/statemachine/generate/java.rb +1 -0
- data/lib/statemachine/generate/java/java_statemachine.rb +237 -0
- data/lib/statemachine/generate/src_builder.rb +48 -0
- data/lib/statemachine/generate/util.rb +37 -0
- data/lib/statemachine/statemachine.rb +2 -2
- data/lib/statemachine/superstate.rb +6 -6
- data/lib/statemachine/version.rb +3 -3
- data/spec/action_invokation_spec.rb +0 -2
- data/spec/builder_spec.rb +44 -0
- data/spec/default_transition_spec.rb +6 -0
- data/spec/generate/java/java_statemachine_spec.rb +259 -0
- data/spec/history_spec.rb +4 -4
- data/spec/sm_odds_n_ends_spec.rb +6 -0
- data/spec/spec_helper.rb +15 -1
- metadata +52 -38
data/CHANGES
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
= Statemachine Changelog
|
2
2
|
|
3
|
+
== Version 0.4.2
|
4
|
+
|
5
|
+
Simple Fixes
|
6
|
+
* Fixed respond_to? to handle the, what should be impossible, case when the state is nil
|
7
|
+
* Switch history members variable to store id rather than object.
|
8
|
+
|
3
9
|
== Version 0.4.1
|
4
10
|
|
5
11
|
Simple Fixes
|
data/{README → README.rdoc}
RENAMED
File without changes
|
data/Rakefile
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
$:.unshift('lib')
|
2
2
|
require 'rubygems'
|
3
3
|
require 'rake/gempackagetask'
|
4
|
-
require 'rake/contrib/rubyforgepublisher'
|
5
4
|
require 'rake/clean'
|
6
5
|
require 'rake/rdoctask'
|
7
6
|
require 'spec/rake/spectask'
|
@@ -24,11 +23,13 @@ Spec::Rake::SpecTask.new do |t|
|
|
24
23
|
t.spec_files = FileList['spec/**/*_spec.rb']
|
25
24
|
end
|
26
25
|
|
26
|
+
WEB_ROOT = File.expand_path('~/Projects/slagyr.github.com/statemachine/')
|
27
|
+
|
27
28
|
desc 'Generate RDoc'
|
28
29
|
rd = Rake::RDocTask.new do |rdoc|
|
29
|
-
rdoc.rdoc_dir =
|
30
|
-
rdoc.options << '--title' << 'Statemachine' << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
31
|
-
rdoc.rdoc_files.include('README', 'CHANGES', 'lib/**/*.rb')
|
30
|
+
rdoc.rdoc_dir = "#{WEB_ROOT}/rdoc"
|
31
|
+
rdoc.options << '--title' << 'Statemachine' << '--line-numbers' << '--inline-source' << '--main' << 'README.rdoc'
|
32
|
+
rdoc.rdoc_files.include('README.rdoc', 'CHANGES', 'lib/**/*.rb')
|
32
33
|
end
|
33
34
|
task :rdoc
|
34
35
|
|
@@ -72,10 +73,6 @@ task :todo do
|
|
72
73
|
egrep /(FIXME|TODO|TBD)/
|
73
74
|
end
|
74
75
|
|
75
|
-
task :clobber do
|
76
|
-
rm_rf 'doc/output'
|
77
|
-
end
|
78
|
-
|
79
76
|
task :release => [:clobber, :verify_committed, :verify_user, :verify_password, :spec, :publish_packages, :tag, :publish_website, :publish_news]
|
80
77
|
|
81
78
|
desc "Verifies that there is no uncommitted code"
|
@@ -94,20 +91,15 @@ task :tag do
|
|
94
91
|
puts "Done!"
|
95
92
|
end
|
96
93
|
|
94
|
+
desc 'Generate HTML documentation for website'
|
95
|
+
task :webgen do
|
96
|
+
system "rm -rf doc/website/out"
|
97
|
+
system "rm -rf doc/website/webgen.cache"
|
98
|
+
system "cd doc/website; webgen -v render; cp -rf out/* #{WEB_ROOT}"
|
99
|
+
end
|
97
100
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
#desc "Upload Website to RubyForge"
|
102
|
-
#task :publish_website => [:verify_user, :website] do
|
103
|
-
# publisher = Rake::SshDirPublisher.new(
|
104
|
-
# "rspec-website@rubyforge.org",
|
105
|
-
# "/var/www/gforge-projects/#{PKG_NAME}",
|
106
|
-
# "doc/output"
|
107
|
-
# )
|
108
|
-
|
109
|
-
# publisher.upload
|
110
|
-
#end
|
101
|
+
desc "Build the website, but do not publish it"
|
102
|
+
task :website => [:webgen, :rdoc]
|
111
103
|
|
112
104
|
task :verify_user do
|
113
105
|
raise "RUBYFORGE_USER environment variable not set!" unless ENV['RUBYFORGE_USER']
|
@@ -115,23 +107,4 @@ end
|
|
115
107
|
|
116
108
|
task :verify_password do
|
117
109
|
raise "RUBYFORGE_PASSWORD environment variable not set!" unless ENV['RUBYFORGE_PASSWORD']
|
118
|
-
end
|
119
|
-
|
120
|
-
desc "Publish gem+tgz+zip on RubyForge. You must make sure lib/version.rb is aligned with the CHANGELOG file"
|
121
|
-
task :publish_packages => [:verify_user, :verify_password, :package] do
|
122
|
-
require 'meta_project'
|
123
|
-
require 'rake/contrib/xforge'
|
124
|
-
release_files = FileList[
|
125
|
-
"pkg/#{PKG_FILE_NAME}.gem",
|
126
|
-
"pkg/#{PKG_FILE_NAME}.tgz",
|
127
|
-
"pkg/#{PKG_FILE_NAME}.zip"
|
128
|
-
]
|
129
|
-
|
130
|
-
Rake::XForge::Release.new(MetaProject::Project::XForge::RubyForge.new(PKG_NAME)) do |xf|
|
131
|
-
# Never hardcode user name and password in the Rakefile!
|
132
|
-
xf.user_name = ENV['RUBYFORGE_USER']
|
133
|
-
xf.password = ENV['RUBYFORGE_PASSWORD']
|
134
|
-
xf.files = release_files.to_a
|
135
|
-
xf.release_name = "statemachine #{PKG_VERSION}"
|
136
|
-
end
|
137
110
|
end
|
data/lib/statemachine/builder.rb
CHANGED
@@ -74,6 +74,10 @@ module Statemachine
|
|
74
74
|
@subject.add(Transition.new(@subject.id, destination_id, event, action))
|
75
75
|
end
|
76
76
|
|
77
|
+
def on_event(event, options)
|
78
|
+
self.event(event, options[:transition_to], options[:and_perform])
|
79
|
+
end
|
80
|
+
|
77
81
|
# Declare the entry action for the state.
|
78
82
|
#
|
79
83
|
# sm = Statemachine.build do
|
@@ -160,6 +164,10 @@ module Statemachine
|
|
160
164
|
origin = acquire_state_in(origin_id, @subject)
|
161
165
|
origin.add(Transition.new(origin_id, destination_id, event, action))
|
162
166
|
end
|
167
|
+
|
168
|
+
def transition_from(origin_id, options)
|
169
|
+
trans(origin_id, options[:on_event], options[:transition_to], options[:and_perform])
|
170
|
+
end
|
163
171
|
|
164
172
|
# Specifies the startstate for the statemachine or superstate. The state must
|
165
173
|
# exist within the scope.
|
@@ -205,7 +213,7 @@ module Statemachine
|
|
205
213
|
# end
|
206
214
|
#
|
207
215
|
def default_history(id)
|
208
|
-
@subject.default_history =
|
216
|
+
@subject.default_history = id
|
209
217
|
end
|
210
218
|
end
|
211
219
|
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'statemachine/generate/java/java_statemachine'
|
@@ -0,0 +1,237 @@
|
|
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_java(options = {})
|
10
|
+
generator = Generate::Java::JavaStatemachine.new(self, options)
|
11
|
+
generator.generate!
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module Generate
|
17
|
+
module Java
|
18
|
+
class JavaStatemachine
|
19
|
+
|
20
|
+
include Generate::Util
|
21
|
+
|
22
|
+
HEADER1 = "// This file was generated by the Ruby Statemachine Library (http://slagyr.github.com/statemachine)."
|
23
|
+
HEADER2 = "// Generated at "
|
24
|
+
|
25
|
+
def initialize(sm, options)
|
26
|
+
@sm = sm
|
27
|
+
@output_dir = options[:output]
|
28
|
+
@classname = options[:name]
|
29
|
+
@context_classname = "#{@classname}Context"
|
30
|
+
@package = options[:package]
|
31
|
+
raise "Please specify an output directory. (:output => 'where/you/want/your/code')" if @output_dir.nil?
|
32
|
+
raise "Output dir '#{@output_dir}' doesn't exist." if !File.exist?(@output_dir)
|
33
|
+
raise "Please specify a name for the statemachine. (:name => 'SomeName')" if @classname.nil?
|
34
|
+
end
|
35
|
+
|
36
|
+
def generate!
|
37
|
+
explore_sm
|
38
|
+
create_file(src_file(@classname), build_statemachine_src)
|
39
|
+
create_file(src_file(@context_classname), build_context_src)
|
40
|
+
end
|
41
|
+
|
42
|
+
private ###########################################
|
43
|
+
|
44
|
+
def explore_sm
|
45
|
+
@state_names = @sm.states.keys.reject{|k| k.nil? }.map { |id| id.to_s.camalized }.sort
|
46
|
+
|
47
|
+
events = []
|
48
|
+
actions = []
|
49
|
+
@sm.states.values.each do |state|
|
50
|
+
state.transitions.values.each do |transition|
|
51
|
+
events << transition.event
|
52
|
+
add_action(actions, transition.action)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
@event_names = events.uniq.map {|e| e.to_s.camalized(:lower)}.sort
|
56
|
+
|
57
|
+
@sm.states.values.each do |state|
|
58
|
+
add_action(actions, state.entry_action)
|
59
|
+
add_action(actions, state.exit_action)
|
60
|
+
end
|
61
|
+
@action_names = actions.uniq.map {|e| e.to_s.camalized(:lower)}.sort
|
62
|
+
end
|
63
|
+
|
64
|
+
def add_action(actions, action)
|
65
|
+
return if action.nil?
|
66
|
+
raise "Actions must be symbols in order to generation Java code. (#{action})" unless action.is_a?(Symbol)
|
67
|
+
actions << action
|
68
|
+
end
|
69
|
+
|
70
|
+
def build_statemachine_src
|
71
|
+
src = begin_src
|
72
|
+
src << "public class #{@classname}" << endl
|
73
|
+
begin_scope(src)
|
74
|
+
|
75
|
+
add_state_instances(src)
|
76
|
+
add_statemachine_boilerplate_code(src)
|
77
|
+
add_event_delegation(src)
|
78
|
+
add_statemachine_exception(src)
|
79
|
+
add_base_state(src)
|
80
|
+
add_state_implementations(src)
|
81
|
+
|
82
|
+
end_scope(src)
|
83
|
+
return src.to_s
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_state_instances(src)
|
87
|
+
src << "// State instances" << endl
|
88
|
+
@state_names.each do |state|
|
89
|
+
src << "public final State #{state.upcase} = new #{state}State(this);" << endl
|
90
|
+
end
|
91
|
+
src << "private State state = #{default_state_name};" << endl
|
92
|
+
src << endl
|
93
|
+
end
|
94
|
+
|
95
|
+
def add_statemachine_boilerplate_code(src)
|
96
|
+
src << "// The following is boiler plate code standard to all statemachines" << endl
|
97
|
+
src << "private #{@context_classname} context;" << endl
|
98
|
+
src << endl
|
99
|
+
add_one_liner(src, nil, @classname, "#{@context_classname} context", "this.context = context")
|
100
|
+
add_one_liner(src, @context_classname, "getContext", nil, "return context")
|
101
|
+
add_one_liner(src, "State", "getState", nil, "return state")
|
102
|
+
add_one_liner(src, "void", "setState", "State newState", "state = newState")
|
103
|
+
end
|
104
|
+
|
105
|
+
def add_event_delegation(src)
|
106
|
+
src << "// Event delegation" << endl
|
107
|
+
@event_names.each do |event|
|
108
|
+
add_one_liner(src, "void", event, nil, "state.#{event}()")
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def add_statemachine_exception(src)
|
113
|
+
src << "// Standard exception class added to all statemachines." << endl
|
114
|
+
src << "public static class StatemachineException extends RuntimeException" << endl
|
115
|
+
begin_scope(src)
|
116
|
+
src << "public StatemachineException(State state, String event)" << endl
|
117
|
+
begin_scope(src)
|
118
|
+
src << "super(\"Missing transition from the '\" + state.getClass().getName() + \"' state with the '\" + event + \"' event.\");" << endl
|
119
|
+
end_scope(src)
|
120
|
+
end_scope(src)
|
121
|
+
src << endl
|
122
|
+
end
|
123
|
+
|
124
|
+
def add_base_state(src)
|
125
|
+
src << "// The base state" << endl
|
126
|
+
src << "public static abstract class State" << endl
|
127
|
+
begin_scope(src)
|
128
|
+
src << "protected #{@classname} statemachine;" << endl
|
129
|
+
src << endl
|
130
|
+
add_one_liner(src, nil, "State", "#{@classname} statemachine", "this.statemachine = statemachine")
|
131
|
+
@event_names.each do |event|
|
132
|
+
add_one_liner(src, "void", event, nil, "throw new StatemachineException(this, \"#{event}\")")
|
133
|
+
end
|
134
|
+
end_scope(src)
|
135
|
+
src << endl
|
136
|
+
end
|
137
|
+
|
138
|
+
def add_state_implementations(src)
|
139
|
+
src << "// State implementations" << endl
|
140
|
+
@sm.states.keys.sort.each do |state_id|
|
141
|
+
add_state_class(src, state_id) if state_id
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def add_state_class(src, state_id)
|
146
|
+
state = @sm.states[state_id]
|
147
|
+
state_name = state_id.to_s.camalized
|
148
|
+
src << "public static class #{state_name}State extends State" << endl
|
149
|
+
src << "{" << endl
|
150
|
+
src.indent!
|
151
|
+
add_one_liner(src, nil, "#{state_name}State", "#{@classname} statemachine", "super(statemachine)")
|
152
|
+
state.transitions.keys.sort.each do |event_id|
|
153
|
+
add_state_event_handler(event_id, src, state)
|
154
|
+
end
|
155
|
+
src.undent!
|
156
|
+
src << "}" << endl
|
157
|
+
src << endl
|
158
|
+
end
|
159
|
+
|
160
|
+
def add_state_event_handler(event_id, src, state)
|
161
|
+
transition = state.transitions[event_id]
|
162
|
+
event_name = event_id.to_s.camalized(:lower)
|
163
|
+
add_method(src, "void", event_name, nil) do
|
164
|
+
if transition.action
|
165
|
+
src << "statemachine.getContext().#{transition.action.to_s.camalized(:lower)}();" << endl
|
166
|
+
end
|
167
|
+
src << "statemachine.setState(statemachine.#{transition.destination_id.to_s.upcase});" << endl
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
def add_one_liner(src, return_type, name, params, body)
|
172
|
+
add_method(src, return_type, name, params) do
|
173
|
+
src << "#{body};" << endl
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
def add_method(src, return_type, name, params)
|
178
|
+
src << "public #{return_type} #{name}(#{params})".sub(' ', ' ') << endl
|
179
|
+
begin_scope(src)
|
180
|
+
yield
|
181
|
+
end_scope(src)
|
182
|
+
src << endl
|
183
|
+
end
|
184
|
+
|
185
|
+
def begin_scope(src)
|
186
|
+
src << "{" << endl
|
187
|
+
src.indent!
|
188
|
+
end
|
189
|
+
|
190
|
+
def end_scope(src)
|
191
|
+
src.undent! << "}" << endl
|
192
|
+
end
|
193
|
+
|
194
|
+
def default_state_name
|
195
|
+
@sm.startstate.nil? ? "null" : @sm.startstate.to_s.upcase
|
196
|
+
end
|
197
|
+
|
198
|
+
def build_context_src
|
199
|
+
src = begin_src
|
200
|
+
src << "public interface #{@context_classname}" << endl
|
201
|
+
begin_scope(src)
|
202
|
+
src << "// Actions" << endl
|
203
|
+
@action_names.each do |event|
|
204
|
+
src << "void #{event}();" << endl
|
205
|
+
end
|
206
|
+
end_scope(src)
|
207
|
+
return src.to_s
|
208
|
+
end
|
209
|
+
|
210
|
+
def begin_src
|
211
|
+
src = SrcBuilder.new
|
212
|
+
src << HEADER1 << endl
|
213
|
+
src << HEADER2 << timestamp << endl
|
214
|
+
src << "package #{@package};" << endl
|
215
|
+
src << endl
|
216
|
+
return src
|
217
|
+
end
|
218
|
+
|
219
|
+
def create_file(filename, content)
|
220
|
+
establish_directory(File.dirname(filename))
|
221
|
+
File.open(filename, 'w') do |file|
|
222
|
+
file.write(content)
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
def src_file(name)
|
227
|
+
path = @output_dir
|
228
|
+
if @package
|
229
|
+
@package.split(".").each { |segment| path = File.join(path, segment) }
|
230
|
+
end
|
231
|
+
return File.join(path, "#{name}.java")
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Statemachine
|
2
|
+
module Generate
|
3
|
+
class SrcBuilder
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@src = ""
|
7
|
+
@is_newline = true
|
8
|
+
@indents = 0
|
9
|
+
@indent_size = 2
|
10
|
+
end
|
11
|
+
|
12
|
+
def <<(content)
|
13
|
+
if content == :endl
|
14
|
+
newline!
|
15
|
+
else
|
16
|
+
add_indents if @is_newline
|
17
|
+
@src += content.to_s
|
18
|
+
end
|
19
|
+
return self
|
20
|
+
end
|
21
|
+
|
22
|
+
def newline!
|
23
|
+
@src += "\n"
|
24
|
+
@is_newline = true
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_s
|
28
|
+
return @src
|
29
|
+
end
|
30
|
+
|
31
|
+
def indent!
|
32
|
+
@indents += 1
|
33
|
+
return self
|
34
|
+
end
|
35
|
+
|
36
|
+
def undent!
|
37
|
+
@indents -= 1
|
38
|
+
return self
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_indents
|
42
|
+
@src += (" " * (@indent_size * @indents))
|
43
|
+
@is_newline = false
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
module Statemachine
|
4
|
+
module Generate
|
5
|
+
module Util
|
6
|
+
|
7
|
+
def establish_directory(path)
|
8
|
+
return if File.exist?(path)
|
9
|
+
establish_directory(File.dirname(path))
|
10
|
+
Dir.mkdir(path)
|
11
|
+
end
|
12
|
+
|
13
|
+
def timestamp
|
14
|
+
return DateTime.now.strftime("%H:%M:%S %B %d, %Y")
|
15
|
+
end
|
16
|
+
|
17
|
+
def endl
|
18
|
+
return :endl
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class String
|
26
|
+
def camalized(starting_case = :upper)
|
27
|
+
value = self.downcase.gsub(/[_| |\-][a-z]/) { |match| match[-1..-1].upcase }
|
28
|
+
value = value[0..0].upcase + value[1..-1] if starting_case == :upper
|
29
|
+
return value
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class Symbol
|
34
|
+
def <=>(other)
|
35
|
+
return to_s <=> other.to_s
|
36
|
+
end
|
37
|
+
end
|
@@ -129,7 +129,7 @@ module Statemachine
|
|
129
129
|
|
130
130
|
def respond_to?(message)
|
131
131
|
return true if super(message)
|
132
|
-
return true if @state.transition_for(message)
|
132
|
+
return true if @state and @state.transition_for(message)
|
133
133
|
return false
|
134
134
|
end
|
135
135
|
|
@@ -160,7 +160,7 @@ module Statemachine
|
|
160
160
|
|
161
161
|
def load_history(superstate)
|
162
162
|
100.times do
|
163
|
-
history = superstate.
|
163
|
+
history = superstate.history_id ? get_state(superstate.history_id) : nil
|
164
164
|
raise StatemachineException.new("#{superstate} doesn't have any history yet.") if not history
|
165
165
|
if history.is_concrete?
|
166
166
|
return history
|
@@ -3,12 +3,12 @@ module Statemachine
|
|
3
3
|
class Superstate < State #:nodoc:
|
4
4
|
|
5
5
|
attr_accessor :startstate_id
|
6
|
-
attr_reader :
|
6
|
+
attr_reader :history_id
|
7
7
|
|
8
8
|
def initialize(id, superstate, statemachine)
|
9
9
|
super(id, superstate, statemachine)
|
10
10
|
@startstate = nil
|
11
|
-
@
|
11
|
+
@history_id = nil
|
12
12
|
end
|
13
13
|
|
14
14
|
def is_concrete?
|
@@ -16,19 +16,19 @@ module Statemachine
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def substate_exiting(substate)
|
19
|
-
@
|
19
|
+
@history_id = substate.id
|
20
20
|
end
|
21
21
|
|
22
22
|
def add_substates(*substate_ids)
|
23
23
|
do_substate_adding(substate_ids)
|
24
24
|
end
|
25
25
|
|
26
|
-
def default_history=(
|
27
|
-
@
|
26
|
+
def default_history=(state_id)
|
27
|
+
@history_id = @default_history_id = state_id
|
28
28
|
end
|
29
29
|
|
30
30
|
def reset
|
31
|
-
@
|
31
|
+
@history_id = @default_history_id
|
32
32
|
end
|
33
33
|
|
34
34
|
def to_s
|
data/lib/statemachine/version.rb
CHANGED
@@ -2,14 +2,14 @@ module Statemachine
|
|
2
2
|
module VERSION #:nodoc:
|
3
3
|
unless defined? MAJOR
|
4
4
|
MAJOR = 0
|
5
|
-
MINOR =
|
6
|
-
TINY =
|
5
|
+
MINOR = 5
|
6
|
+
TINY = 0
|
7
7
|
|
8
8
|
STRING = [MAJOR, MINOR, TINY].join('.')
|
9
9
|
TAG = "REL_" + [MAJOR, MINOR, TINY].join('_')
|
10
10
|
|
11
11
|
NAME = "Statemachine"
|
12
|
-
URL = "http://
|
12
|
+
URL = "http://slagyr.github.com/statemachine"
|
13
13
|
|
14
14
|
DESCRIPTION = "#{NAME}-#{STRING} - Statemachine Library for Ruby\n#{URL}"
|
15
15
|
end
|
data/spec/builder_spec.rb
CHANGED
@@ -193,5 +193,49 @@ describe "Builder" do
|
|
193
193
|
widget.statemachine.should be(sm)
|
194
194
|
end
|
195
195
|
|
196
|
+
it "should have an on_event" do
|
197
|
+
sm = Statemachine.build do
|
198
|
+
startstate :start
|
199
|
+
state :start do
|
200
|
+
on_event :go, :transition_to => :new_state
|
201
|
+
end
|
202
|
+
end
|
203
|
+
sm.go
|
204
|
+
sm.state.should == :new_state
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should trigger actions using on_event" do
|
208
|
+
sm = Statemachine.build do
|
209
|
+
startstate :start
|
210
|
+
state :start do
|
211
|
+
on_event :go, :transition_to => :new_state, :and_perform => :action
|
212
|
+
end
|
213
|
+
end
|
214
|
+
object = mock("context")
|
215
|
+
sm.context = object
|
216
|
+
object.should_receive(:action)
|
217
|
+
|
218
|
+
sm.go
|
219
|
+
end
|
220
|
+
|
221
|
+
it "should have a transition_from" do
|
222
|
+
sm = Statemachine.build do
|
223
|
+
transition_from :start, :on_event => :go, :transition_to => :new_state
|
224
|
+
end
|
225
|
+
|
226
|
+
sm.go
|
227
|
+
sm.state.should == :new_state
|
228
|
+
end
|
229
|
+
|
230
|
+
it "should trigger actions on transition_from" do
|
231
|
+
sm = Statemachine.build do
|
232
|
+
transition_from :start, :on_event => :go, :transition_to => :new_state, :and_perform => :action
|
233
|
+
end
|
234
|
+
object = mock("context")
|
235
|
+
sm.context = object
|
236
|
+
object.should_receive(:action)
|
237
|
+
|
238
|
+
sm.go
|
239
|
+
end
|
196
240
|
end
|
197
241
|
|
@@ -100,6 +100,12 @@ describe "Default Transition" do
|
|
100
100
|
@sm.state.should eql(:base_default)
|
101
101
|
end
|
102
102
|
|
103
|
+
it "should be marshalable" do
|
104
|
+
dump = Marshal.dump(@sm)
|
105
|
+
loaded = Marshal.load(dump)
|
106
|
+
loaded.state.should eql(:default_state)
|
107
|
+
end
|
108
|
+
|
103
109
|
|
104
110
|
|
105
111
|
end
|
@@ -0,0 +1,259 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
|
2
|
+
require 'statemachine/generate/java/java_statemachine'
|
3
|
+
|
4
|
+
describe Statemachine::Statemachine, "(Java)" do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
remove_test_dir("java")
|
8
|
+
@output = test_dir("java")
|
9
|
+
@sm = Statemachine.build {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def generate_turnstile_sm
|
13
|
+
@sm = Statemachine.build do
|
14
|
+
trans :locked, :coin, :unlocked, :unlock
|
15
|
+
trans :unlocked, :pass, :locked, :lock
|
16
|
+
trans :locked, :pass, :locked, :alarm
|
17
|
+
trans :unlocked, :coin, :locked, :thanks
|
18
|
+
end
|
19
|
+
|
20
|
+
@sm.to_java(:output => @output, :name => "JavaTurnstile", :package => "test.turnstile")
|
21
|
+
end
|
22
|
+
|
23
|
+
def empty_sm_lines
|
24
|
+
@sm.to_java(:name => "JavaTest", :output => @output, :package => "test.blank")
|
25
|
+
filename = File.join(@output, "test", "blank", "JavaTest.java")
|
26
|
+
File.should exist( filename)
|
27
|
+
lines = IO.read(filename).split("\n")
|
28
|
+
return lines
|
29
|
+
end
|
30
|
+
|
31
|
+
def sm_lines
|
32
|
+
generate_turnstile_sm
|
33
|
+
filename = File.join(@output, "test", "turnstile", "JavaTurnstile.java")
|
34
|
+
File.should exist( filename)
|
35
|
+
return IO.read(filename).split("\n")
|
36
|
+
end
|
37
|
+
|
38
|
+
def context_lines
|
39
|
+
generate_turnstile_sm
|
40
|
+
filename = File.join(@output, "test", "turnstile", "JavaTurnstileContext.java")
|
41
|
+
File.should exist( filename)
|
42
|
+
return IO.read(filename).split("\n")
|
43
|
+
end
|
44
|
+
|
45
|
+
def find_lines_after(lines, goose)
|
46
|
+
while !lines.empty?
|
47
|
+
return lines if lines.shift.strip == goose
|
48
|
+
end
|
49
|
+
raise "Can't find desired line: #{goose}"
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should error if no output dir is specified" do
|
53
|
+
lambda { @sm.to_java }.should raise_error("Please specify an output directory. (:output => 'where/you/want/your/code')")
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should error if output dir doesn't exist" do
|
57
|
+
lambda { @sm.to_java(:output => "/blah") }.should raise_error("Output dir '/blah' doesn't exist.")
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should error if no class name is specified" do
|
61
|
+
lambda { @sm.to_java(:output => @output) }.should raise_error("Please specify a name for the statemachine. (:name => 'SomeName')")
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should generate a class" do
|
65
|
+
@sm.to_java(:name => "JavaTest", :output => @output)
|
66
|
+
File.should exist(File.join(@output, "JavaTest.java"))
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should generate a class in a package" do
|
70
|
+
lines = empty_sm_lines
|
71
|
+
|
72
|
+
lines.shift.should == "// This file was generated by the Ruby Statemachine Library (http://slagyr.github.com/statemachine)."
|
73
|
+
lines.shift.include?("// Generated at ").should == true
|
74
|
+
lines.shift.should == "package test.blank;"
|
75
|
+
lines.shift.should == ""
|
76
|
+
lines.shift.should == "public class JavaTest"
|
77
|
+
lines.shift.should == "{"
|
78
|
+
lines.last.should == "}"
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should generate sm boilerplate code" do
|
82
|
+
lines = empty_sm_lines
|
83
|
+
lines = find_lines_after(lines, "// The following is boiler plate code standard to all statemachines")
|
84
|
+
|
85
|
+
lines.shift.should == " private JavaTestContext context;"
|
86
|
+
lines.shift.should == ""
|
87
|
+
lines.shift.should == " public JavaTest(JavaTestContext context)"
|
88
|
+
lines.shift.should == " {"
|
89
|
+
lines.shift.should == " this.context = context;"
|
90
|
+
lines.shift.should == " }"
|
91
|
+
lines.shift.should == ""
|
92
|
+
lines.shift.should == " public JavaTestContext getContext()"
|
93
|
+
lines.shift.should == " {"
|
94
|
+
lines.shift.should == " return context;"
|
95
|
+
lines.shift.should == " }"
|
96
|
+
lines.shift.should == ""
|
97
|
+
lines.shift.should == " public State getState()"
|
98
|
+
lines.shift.should == " {"
|
99
|
+
lines.shift.should == " return state;"
|
100
|
+
lines.shift.should == " }"
|
101
|
+
lines.shift.should == ""
|
102
|
+
lines.shift.should == " public void setState(State newState)"
|
103
|
+
lines.shift.should == " {"
|
104
|
+
lines.shift.should == " state = newState;"
|
105
|
+
lines.shift.should == " }"
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should generate the sm exception" do
|
109
|
+
lines = empty_sm_lines
|
110
|
+
lines = find_lines_after(lines, "// Standard exception class added to all statemachines.")
|
111
|
+
|
112
|
+
lines.shift.should == " public static class StatemachineException extends RuntimeException"
|
113
|
+
lines.shift.should == " {"
|
114
|
+
lines.shift.should == " public StatemachineException(State state, String event)"
|
115
|
+
lines.shift.should == " {"
|
116
|
+
lines.shift.should == " super(\"Missing transition from the '\" + state.getClass().getName() + \"' state with the '\" + event + \"' event.\");"
|
117
|
+
lines.shift.should == " }"
|
118
|
+
lines.shift.should == " }"
|
119
|
+
end
|
120
|
+
|
121
|
+
it "should generate base state" do
|
122
|
+
lines = empty_sm_lines
|
123
|
+
lines = find_lines_after(lines, "// The base state")
|
124
|
+
|
125
|
+
lines.shift.should == " public static abstract class State"
|
126
|
+
lines.shift.should == " {"
|
127
|
+
lines.shift.should == " protected JavaTest statemachine;"
|
128
|
+
lines.shift.should == ""
|
129
|
+
lines.shift.should == " public State(JavaTest statemachine)"
|
130
|
+
lines.shift.should == " {"
|
131
|
+
lines.shift.should == " this.statemachine = statemachine;"
|
132
|
+
lines.shift.should == " }"
|
133
|
+
lines.shift.should == ""
|
134
|
+
lines.shift.should == " }"
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should generate the context" do
|
138
|
+
@sm.to_java(:name => "JavaTest", :output => @output, :package => "com.blah")
|
139
|
+
|
140
|
+
filename = File.join(@output, "com", "blah", "JavaTestContext.java")
|
141
|
+
File.should exist(filename)
|
142
|
+
lines = IO.read(filename).split("\n")
|
143
|
+
|
144
|
+
lines.shift.should == "// This file was generated by the Ruby Statemachine Library (http://slagyr.github.com/statemachine)."
|
145
|
+
lines.shift.include?("// Generated at ").should == true
|
146
|
+
lines.shift.should == "package com.blah;"
|
147
|
+
lines.shift.should == ""
|
148
|
+
lines.shift.should == "public interface JavaTestContext"
|
149
|
+
lines.shift.should == "{"
|
150
|
+
lines.shift.should == " // Actions"
|
151
|
+
lines.shift.should == "}"
|
152
|
+
lines.should be_empty
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should generate the state instance variables" do
|
156
|
+
lines = find_lines_after(sm_lines, "// State instances")
|
157
|
+
|
158
|
+
lines.shift.should == " public final State LOCKED = new LockedState(this);"
|
159
|
+
lines.shift.should == " public final State UNLOCKED = new UnlockedState(this);"
|
160
|
+
lines.shift.should == " private State state = LOCKED;"
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should generate all the sm event handlers" do
|
164
|
+
lines = find_lines_after(sm_lines, "// Event delegation")
|
165
|
+
|
166
|
+
lines.shift.should == " public void coin()"
|
167
|
+
lines.shift.should == " {"
|
168
|
+
lines.shift.should == " state.coin();"
|
169
|
+
lines.shift.should == " }"
|
170
|
+
lines.shift.should == ""
|
171
|
+
lines.shift.should == " public void pass()"
|
172
|
+
lines.shift.should == " {"
|
173
|
+
lines.shift.should == " state.pass();"
|
174
|
+
lines.shift.should == " }"
|
175
|
+
lines.shift.should == ""
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should generate all the default event handlers" do
|
179
|
+
lines = find_lines_after(sm_lines, "// The base state")
|
180
|
+
|
181
|
+
lines.shift.should == " public static abstract class State"
|
182
|
+
lines.shift.should == " {"
|
183
|
+
lines.shift.should == " protected JavaTurnstile statemachine;"
|
184
|
+
lines.shift.should == ""
|
185
|
+
lines.shift.should == " public State(JavaTurnstile statemachine)"
|
186
|
+
lines.shift.should == " {"
|
187
|
+
lines.shift.should == " this.statemachine = statemachine;"
|
188
|
+
lines.shift.should == " }"
|
189
|
+
lines.shift.should == ""
|
190
|
+
lines.shift.should == " public void coin()"
|
191
|
+
lines.shift.should == " {"
|
192
|
+
lines.shift.should == " throw new StatemachineException(this, \"coin\");"
|
193
|
+
lines.shift.should == " }"
|
194
|
+
lines.shift.should == ""
|
195
|
+
lines.shift.should == " public void pass()"
|
196
|
+
lines.shift.should == " {"
|
197
|
+
lines.shift.should == " throw new StatemachineException(this, \"pass\");"
|
198
|
+
lines.shift.should == " }"
|
199
|
+
lines.shift.should == ""
|
200
|
+
lines.shift.should == " }"
|
201
|
+
end
|
202
|
+
|
203
|
+
it "should generate state classes" do
|
204
|
+
lines = find_lines_after(sm_lines, "// State implementations")
|
205
|
+
|
206
|
+
lines.shift.should == " public static class LockedState extends State"
|
207
|
+
lines.shift.should == " {"
|
208
|
+
lines.shift.should == " public LockedState(JavaTurnstile statemachine)"
|
209
|
+
lines.shift.should == " {"
|
210
|
+
lines.shift.should == " super(statemachine);"
|
211
|
+
lines.shift.should == " }"
|
212
|
+
lines.shift.should == ""
|
213
|
+
lines.shift.should == " public void coin()"
|
214
|
+
lines.shift.should == " {"
|
215
|
+
lines.shift.should == " statemachine.getContext().unlock();"
|
216
|
+
lines.shift.should == " statemachine.setState(statemachine.UNLOCKED);"
|
217
|
+
lines.shift.should == " }"
|
218
|
+
lines.shift.should == ""
|
219
|
+
lines.shift.should == " public void pass()"
|
220
|
+
lines.shift.should == " {"
|
221
|
+
lines.shift.should == " statemachine.getContext().alarm();"
|
222
|
+
lines.shift.should == " statemachine.setState(statemachine.LOCKED);"
|
223
|
+
lines.shift.should == " }"
|
224
|
+
lines.shift.should == ""
|
225
|
+
lines.shift.should == " }"
|
226
|
+
lines.shift.should == ""
|
227
|
+
lines.shift.should == " public static class UnlockedState extends State"
|
228
|
+
lines.shift.should == " {"
|
229
|
+
lines.shift.should == " public UnlockedState(JavaTurnstile statemachine)"
|
230
|
+
lines.shift.should == " {"
|
231
|
+
lines.shift.should == " super(statemachine);"
|
232
|
+
lines.shift.should == " }"
|
233
|
+
lines.shift.should == ""
|
234
|
+
lines.shift.should == " public void coin()"
|
235
|
+
lines.shift.should == " {"
|
236
|
+
lines.shift.should == " statemachine.getContext().thanks();"
|
237
|
+
lines.shift.should == " statemachine.setState(statemachine.LOCKED);"
|
238
|
+
lines.shift.should == " }"
|
239
|
+
lines.shift.should == ""
|
240
|
+
lines.shift.should == " public void pass()"
|
241
|
+
lines.shift.should == " {"
|
242
|
+
lines.shift.should == " statemachine.getContext().lock();"
|
243
|
+
lines.shift.should == " statemachine.setState(statemachine.LOCKED);"
|
244
|
+
lines.shift.should == " }"
|
245
|
+
lines.shift.should == ""
|
246
|
+
lines.shift.should == " }"
|
247
|
+
lines.shift.should == ""
|
248
|
+
end
|
249
|
+
|
250
|
+
it "should generate all the context methods" do
|
251
|
+
lines = find_lines_after(context_lines, "// Actions")
|
252
|
+
|
253
|
+
lines.shift.should == " void alarm();"
|
254
|
+
lines.shift.should == " void lock();"
|
255
|
+
lines.shift.should == " void thanks();"
|
256
|
+
lines.shift.should == " void unlock();"
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
data/spec/history_spec.rb
CHANGED
@@ -31,10 +31,10 @@ describe "History States" do
|
|
31
31
|
it "reseting the statemachine resets history" do
|
32
32
|
@sm.faddle
|
33
33
|
@sm.fiddle
|
34
|
-
@sm.get_state(:operate).
|
34
|
+
@sm.get_state(:operate).history_id.should eql(:on)
|
35
35
|
|
36
36
|
@sm.reset
|
37
|
-
@sm.get_state(:operate).
|
37
|
+
@sm.get_state(:operate).history_id.should eql(nil)
|
38
38
|
end
|
39
39
|
|
40
40
|
end
|
@@ -64,10 +64,10 @@ describe "History Default" do
|
|
64
64
|
@sm.faddle
|
65
65
|
@sm.toggle
|
66
66
|
@sm.fiddle
|
67
|
-
@sm.get_state(:operate).
|
67
|
+
@sm.get_state(:operate).history_id.should eql(:off)
|
68
68
|
|
69
69
|
@sm.reset
|
70
|
-
@sm.get_state(:operate).
|
70
|
+
@sm.get_state(:operate).history_id.should eql(:on)
|
71
71
|
end
|
72
72
|
|
73
73
|
end
|
data/spec/sm_odds_n_ends_spec.rb
CHANGED
@@ -28,6 +28,12 @@ describe "State Machine Odds And Ends" do
|
|
28
28
|
@sm.respond_to?(:blah).should eql(false)
|
29
29
|
end
|
30
30
|
|
31
|
+
it "should not crash when respond_to? called when the statemachine is not in a state" do
|
32
|
+
@sm.instance_eval { @state = nil }
|
33
|
+
lambda { @sm.respond_to?(:toggle) }.should_not raise_error
|
34
|
+
@sm.respond_to?(:toggle).should eql(false)
|
35
|
+
end
|
36
|
+
|
31
37
|
it "set state with string" do
|
32
38
|
@sm.state.should equal(:off)
|
33
39
|
@sm.state = "on"
|
data/spec/spec_helper.rb
CHANGED
@@ -43,5 +43,19 @@ module TurnstileStatemachine
|
|
43
43
|
end
|
44
44
|
@sm.context = self
|
45
45
|
end
|
46
|
-
|
46
|
+
|
47
|
+
end
|
48
|
+
|
49
|
+
TEST_DIR = File.expand_path(File.dirname(__FILE__) + "/../test_dir/")
|
50
|
+
|
51
|
+
def test_dir(name = nil)
|
52
|
+
Dir.mkdir(TEST_DIR) if !File.exist?(TEST_DIR)
|
53
|
+
return TEST_DIR if name.nil?
|
54
|
+
dir = File.join(TEST_DIR, name)
|
55
|
+
Dir.mkdir(dir) if !File.exist?(dir)
|
56
|
+
return dir
|
57
|
+
end
|
58
|
+
|
59
|
+
def remove_test_dir(name)
|
60
|
+
system "rm -rf #{test_dir(name)}" if File.exist?(test_dir(name))
|
47
61
|
end
|
metadata
CHANGED
@@ -1,50 +1,48 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
|
-
rubygems_version: 0.9.1
|
3
|
-
specification_version: 1
|
4
2
|
name: statemachine
|
5
3
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2007-05-20 00:00:00 -07:00
|
8
|
-
summary: Statemachine-0.4.1 - Statemachine Library for Ruby http://statemachine.rubyforge.org/
|
9
|
-
require_paths:
|
10
|
-
- lib
|
11
|
-
email: statemachine-devel@rubyforge.org
|
12
|
-
homepage: http://statemachine.rubyforge.org
|
13
|
-
rubyforge_project: statemachine
|
14
|
-
description: Statemachine is a ruby library for building Finite State Machines (FSM), also known as Finite State Automata (FSA).
|
15
|
-
autorequire: statemachine
|
16
|
-
default_executable:
|
17
|
-
bindir: bin
|
18
|
-
has_rdoc: false
|
19
|
-
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
-
requirements:
|
21
|
-
- - ">"
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.0.0
|
24
|
-
version:
|
4
|
+
version: 0.5.0
|
25
5
|
platform: ruby
|
26
|
-
signing_key:
|
27
|
-
cert_chain:
|
28
|
-
post_install_message:
|
29
6
|
authors:
|
30
7
|
- Micah Martin
|
8
|
+
autorequire: statemachine
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-03-24 00:00:00 -05:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Statemachine is a ruby library for building Finite State Machines (FSM), also known as Finite State Automata (FSA).
|
17
|
+
email: statemachine-devel@rubyforge.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files: []
|
23
|
+
|
31
24
|
files:
|
32
25
|
- CHANGES
|
33
26
|
- LICENSE
|
34
27
|
- Rakefile
|
35
|
-
- README
|
28
|
+
- README.rdoc
|
36
29
|
- TODO
|
37
|
-
- lib/statemachine.rb
|
38
30
|
- lib/statemachine/action_invokation.rb
|
39
31
|
- lib/statemachine/builder.rb
|
32
|
+
- lib/statemachine/generate/java/java_statemachine.rb
|
33
|
+
- lib/statemachine/generate/java.rb
|
34
|
+
- lib/statemachine/generate/src_builder.rb
|
35
|
+
- lib/statemachine/generate/util.rb
|
40
36
|
- lib/statemachine/state.rb
|
41
37
|
- lib/statemachine/statemachine.rb
|
42
38
|
- lib/statemachine/superstate.rb
|
43
39
|
- lib/statemachine/transition.rb
|
44
40
|
- lib/statemachine/version.rb
|
41
|
+
- lib/statemachine.rb
|
45
42
|
- spec/action_invokation_spec.rb
|
46
43
|
- spec/builder_spec.rb
|
47
44
|
- spec/default_transition_spec.rb
|
45
|
+
- spec/generate/java/java_statemachine_spec.rb
|
48
46
|
- spec/history_spec.rb
|
49
47
|
- spec/sm_action_parameterization_spec.rb
|
50
48
|
- spec/sm_entry_exit_actions_spec.rb
|
@@ -54,6 +52,34 @@ files:
|
|
54
52
|
- spec/sm_turnstile_spec.rb
|
55
53
|
- spec/spec_helper.rb
|
56
54
|
- spec/transition_spec.rb
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://statemachine.rubyforge.org
|
57
|
+
licenses: []
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project: statemachine
|
79
|
+
rubygems_version: 1.3.5
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Statemachine-0.5.0 - Statemachine Library for Ruby http://slagyr.github.com/statemachine
|
57
83
|
test_files:
|
58
84
|
- spec/action_invokation_spec.rb
|
59
85
|
- spec/builder_spec.rb
|
@@ -66,15 +92,3 @@ test_files:
|
|
66
92
|
- spec/sm_super_state_spec.rb
|
67
93
|
- spec/sm_turnstile_spec.rb
|
68
94
|
- spec/transition_spec.rb
|
69
|
-
rdoc_options: []
|
70
|
-
|
71
|
-
extra_rdoc_files: []
|
72
|
-
|
73
|
-
executables: []
|
74
|
-
|
75
|
-
extensions: []
|
76
|
-
|
77
|
-
requirements: []
|
78
|
-
|
79
|
-
dependencies: []
|
80
|
-
|