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 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
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 = 'doc/website/output/rdoc'
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
- #desc "Build the website, but do not publish it"
99
- #task :website => [:clobber, :verify_rcov, :webgen, :failing_examples_with_html, :spec, :examples_specdoc, :rdoc]
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
@@ -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 = @statemachine.get_state(id)
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.history
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 :history
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
- @history = nil
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
- @history = substate
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=(state)
27
- @history = @default_history = state
26
+ def default_history=(state_id)
27
+ @history_id = @default_history_id = state_id
28
28
  end
29
29
 
30
30
  def reset
31
- @history = @default_history
31
+ @history_id = @default_history_id
32
32
  end
33
33
 
34
34
  def to_s
@@ -2,14 +2,14 @@ module Statemachine
2
2
  module VERSION #:nodoc:
3
3
  unless defined? MAJOR
4
4
  MAJOR = 0
5
- MINOR = 4
6
- TINY = 1
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://statemachine.rubyforge.org/"
12
+ URL = "http://slagyr.github.com/statemachine"
13
13
 
14
14
  DESCRIPTION = "#{NAME}-#{STRING} - Statemachine Library for Ruby\n#{URL}"
15
15
  end
@@ -63,6 +63,4 @@ describe "Action Invokation" do
63
63
  @noodle.cooked.should equal(true)
64
64
  end
65
65
 
66
-
67
-
68
66
  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).history.id.should eql(:on)
34
+ @sm.get_state(:operate).history_id.should eql(:on)
35
35
 
36
36
  @sm.reset
37
- @sm.get_state(:operate).history.should eql(nil)
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).history.id.should eql(:off)
67
+ @sm.get_state(:operate).history_id.should eql(:off)
68
68
 
69
69
  @sm.reset
70
- @sm.get_state(:operate).history.id.should eql(:on)
70
+ @sm.get_state(:operate).history_id.should eql(:on)
71
71
  end
72
72
 
73
73
  end
@@ -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.4.1
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
-