simplificator-fsm 0.3.0 → 0.3.2

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