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 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
-