statemachine 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
|