simplificator-fsm 0.3.0 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -1,6 +1,11 @@
1
1
  # fsm
2
2
 
3
- FSM is a simple finite state machine
3
+ FSM is a simple finite state machine gem. You can define your State Machine with a "DSL".
4
+
5
+ ## Status
6
+ FSM is still under development so the interface can/might/will change, features will be added (and perhaps removed again)
7
+ But if you are interested in this project, then use it and tell us what you think/need/want/like/don't like. We are open
8
+ for suggestions!
4
9
 
5
10
  ## Usage
6
11
  class Water
@@ -11,8 +16,7 @@ FSM is a simple finite state machine
11
16
  # you can add :enter / :exit callbacks (callback can be a String, Symbol or Proc)
12
17
  # these callbacks are triggered on any transition from/to this state.
13
18
 
14
- state(:gas)
15
- state(:liquid)
19
+ states(:gas, :liquid) # shortcut to define several states but you can not specify callbacks
16
20
  state(:solid, :enter => :on_enter_solid, :exit => :on_exit_solid)
17
21
 
18
22
  # define all valid transitions (arguments are name of transition, from state name, to state name)
@@ -20,8 +24,7 @@ FSM is a simple finite state machine
20
24
  # guards prevent transition when they return nil/false
21
25
  transition(:heat_up, :solid, :liquid, :event => :on_heat, :guard => :guard_something)
22
26
  transition(:heat_up, :liquid, :gas, :event => :on_heat) # look mam.... two transitions with same name
23
- transition(:cool_down, :gas, :liquid, :event => :on_cool)
24
- transition(:cool_down, :liquid, :solid, :event => :on_cool)
27
+ transition(:cool_down, [:gas, :liquid], :liquid, :event => :on_cool)
25
28
 
26
29
  # define the attribute which is used to store the state (defaults to :state)
27
30
  state(:state_of_material)
@@ -79,7 +82,7 @@ FSM supports the dot format of graphviz (http://www.graphviz.org/).
79
82
  If you have the graphviz tools installed (the dot executable must be on the path) then
80
83
  you can export a graph to png like this
81
84
  # Export to water.png in the current dir
82
- Water.graph
85
+ Water.draw_graph
83
86
  # Export in another format. (see graphviz documentation for supported file formats)
84
87
  Water.draw_graph(:format => :foo)
85
88
  # Change the extension (defaults to the format)
data/Rakefile CHANGED
@@ -9,6 +9,7 @@ begin
9
9
  gem.email = "info@simplificator.com"
10
10
  gem.homepage = "http://github.com/simplificator/fsm"
11
11
  gem.authors = ["simplificator"]
12
+ gem.files.exclude '**/*.sqlite3', '*.sqlite3'
12
13
 
13
14
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
15
  end
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 3
4
- :patch: 0
4
+ :patch: 2
data/lib/fsm/builder.rb CHANGED
@@ -27,8 +27,10 @@ module FSM
27
27
  # * to_name: name of the target state (symbol)
28
28
  # * options
29
29
  #
30
- def transition(name, from_name, to_name, options = {})
31
- @machine.transition(name, from_name, to_name, options)
30
+ def transition(name, from_names, to_name, options = {})
31
+ Array(from_names).each do |from_name|
32
+ @machine.transition(name, from_name, to_name, options)
33
+ end
32
34
  nil # do not expose FSM details
33
35
  end
34
36
 
@@ -48,6 +50,10 @@ module FSM
48
50
  nil # do not expose FSM details
49
51
  end
50
52
 
51
-
53
+ def states(*names)
54
+ names.each do |name|
55
+ state(name)
56
+ end
57
+ end
52
58
  end
53
59
  end
data/lib/fsm/machine.rb CHANGED
@@ -39,8 +39,7 @@ module FSM
39
39
  end
40
40
 
41
41
  def self.get_current_state_name(target)
42
- value = target.send(Machine[target.class].current_state_attribute_name)
43
- (value && value.is_a?(String)) ? value.intern : value
42
+ target.send(Machine[target.class].current_state_attribute_name) || self.initial_state_name
44
43
  end
45
44
 
46
45
  def self.set_current_state_name(target, value)
data/lib/fsm/state.rb CHANGED
@@ -54,11 +54,10 @@ module FSM
54
54
  end
55
55
 
56
56
  def to_dot(options = {})
57
-
58
- if final?
59
- attrs = "style=bold"
60
- elsif initial?
57
+ if initial?
61
58
  attrs = "style=bold, label=\"#{self.name}\\n(initial)\""
59
+ elsif final?
60
+ attrs = "style=bold"
62
61
  else
63
62
  attrs = ""
64
63
  end
@@ -0,0 +1,35 @@
1
+ module FSM
2
+ class StateAttributeInterceptor
3
+ def self.add_interceptor(klass)
4
+ state_attribute_name = Machine[klass].current_state_attribute_name
5
+ hierarchy = klass.ancestors.map {|ancestor| ancestor.to_s}
6
+
7
+ if hierarchy.include?("ActiveRecord::Base")
8
+ bar(klass)
9
+ else
10
+ foo(klass)
11
+ end
12
+
13
+ end
14
+
15
+ private
16
+ def self.bar(klass)
17
+ klass.instance_eval() do
18
+ define_method(Machine[klass].current_state_attribute_name) do
19
+ value = read_attribute(Machine[self.class].current_state_attribute_name) || Machine[self.class].initial_state_name
20
+ value.is_a?(String) ? value.intern : value
21
+ end
22
+ end
23
+ end
24
+
25
+ def self.foo(klass)
26
+ klass.instance_eval() do
27
+ alias_method "fsm_state_attribute", Machine[klass].current_state_attribute_name
28
+ define_method(Machine[klass].current_state_attribute_name) do
29
+ value = fsm_state_attribute || Machine[self.class].initial_state_name
30
+ value.is_a?(String) ? value.intern : value
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
data/lib/fsm.rb CHANGED
@@ -1,4 +1,4 @@
1
- %w[options errors machine state transition executable builder].each do |item|
1
+ %w[options errors machine state transition executable builder state_attribute_interceptor].each do |item|
2
2
  require File.join(File.dirname(__FILE__), 'fsm', item)
3
3
  end
4
4
 
@@ -8,13 +8,15 @@ module FSM
8
8
  raise 'FSM is already defined. Call define_fsm only once' if Machine[self]
9
9
  builder = Builder.new(self)
10
10
  Machine[self] = builder.process(&block)
11
- self.instance_eval() do
12
- alias_method "fsm_state_attribute", Machine[self].current_state_attribute_name
13
- define_method(Machine[self].current_state_attribute_name) do
14
- value = fsm_state_attribute
15
- value ? value : Machine[self.class].initial_state_name
16
- end
17
- end
11
+
12
+ # TODO: check if all states are reachable
13
+ # TODO: other checks? islands?
14
+
15
+ # create alias for state attribute method to intercept it
16
+ # intercept
17
+ FSM::StateAttributeInterceptor.add_interceptor(self)
18
+
19
+
18
20
  end
19
21
 
20
22
  def draw_graph(options = {})
data/test/ar_test.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'test_helper_ar'
2
+ class ArTest < Test::Unit::TestCase
3
+ context 'AR' do
4
+
5
+ should 'bla' do
6
+ o = Order.new
7
+ assert_equal :open, o.state
8
+ end
9
+ end
10
+ end
data/test/test_helper.rb CHANGED
@@ -5,6 +5,3 @@ require 'shoulda'
5
5
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
6
  $LOAD_PATH.unshift(File.dirname(__FILE__))
7
7
  require 'fsm'
8
-
9
- class Test::Unit::TestCase
10
- end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+ require 'rubygems'
3
+ require 'active_record'
4
+
5
+ ActiveRecord::Base.establish_connection(
6
+ :adapter => 'sqlite3',
7
+ :database => 'test.sqlite3',
8
+ :timeout => 5000
9
+ )
10
+
11
+
12
+ begin
13
+ ActiveRecord::Base.connection.drop_table(:orders)
14
+ rescue
15
+ # no such table
16
+ end
17
+
18
+ ActiveRecord::Base.connection.create_table(:orders) do |table|
19
+ table.string(:state, :null => false, :limit => 10, :default => 'open')
20
+ end
21
+ class Order < ActiveRecord::Base
22
+ include FSM
23
+ define_fsm do
24
+ states :open, :closed, :delivered
25
+ end
26
+ end
27
+
28
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
29
+ ActiveRecord::Base.logger.level = Logger::DEBUG # change to DEBUG if you want to see something :-)
@@ -8,8 +8,7 @@ class Water
8
8
  # now define all the states
9
9
  # you can add :enter / :exit callbacks (callback can be a String, Symbol or Proc)
10
10
  # these callbacks are triggered on any transition from/to this state and do not receive any arguments
11
- state(:gas)
12
- state(:liquid)
11
+ states(:gas, :liquid)
13
12
  state(:solid, :enter => :on_enter_solid, :exit => :on_exit_solid)
14
13
 
15
14
  # define all valid transitions (name, from, to). This will define a method with the given name.
@@ -60,8 +59,6 @@ class Water
60
59
  self.temperature -= delta
61
60
  end
62
61
  end
63
- # Draw the the state graph
64
- #Water.draw_graph(:format => :svg)
65
62
 
66
63
  class WaterSampleTest < Test::Unit::TestCase
67
64
  context 'Water' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simplificator-fsm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - simplificator
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-05-25 00:00:00 -07:00
12
+ date: 2009-05-28 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -34,12 +34,15 @@ files:
34
34
  - lib/fsm/machine.rb
35
35
  - lib/fsm/options.rb
36
36
  - lib/fsm/state.rb
37
+ - lib/fsm/state_attribute_interceptor.rb
37
38
  - lib/fsm/transition.rb
39
+ - test/ar_test.rb
38
40
  - test/executable_test.rb
39
41
  - test/invoice_sample_test.rb
40
42
  - test/options_test.rb
41
43
  - test/state_test.rb
42
44
  - test/test_helper.rb
45
+ - test/test_helper_ar.rb
43
46
  - test/transition_test.rb
44
47
  - test/water_sample_test.rb
45
48
  has_rdoc: true
@@ -69,10 +72,12 @@ signing_key:
69
72
  specification_version: 3
70
73
  summary: A simple finite state machine (FSM) gem.
71
74
  test_files:
75
+ - test/ar_test.rb
72
76
  - test/executable_test.rb
73
77
  - test/invoice_sample_test.rb
74
78
  - test/options_test.rb
75
79
  - test/state_test.rb
76
80
  - test/test_helper.rb
81
+ - test/test_helper_ar.rb
77
82
  - test/transition_test.rb
78
83
  - test/water_sample_test.rb