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 +9 -6
- data/Rakefile +1 -0
- data/VERSION.yml +1 -1
- data/lib/fsm/builder.rb +9 -3
- data/lib/fsm/machine.rb +1 -2
- data/lib/fsm/state.rb +3 -4
- data/lib/fsm/state_attribute_interceptor.rb +35 -0
- data/lib/fsm.rb +10 -8
- data/test/ar_test.rb +10 -0
- data/test/test_helper.rb +0 -3
- data/test/test_helper_ar.rb +29 -0
- data/test/water_sample_test.rb +1 -4
- metadata +7 -2
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
|
-
|
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.
|
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
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,
|
31
|
-
|
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
|
-
|
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
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
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
data/test/test_helper.rb
CHANGED
@@ -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 :-)
|
data/test/water_sample_test.rb
CHANGED
@@ -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
|
-
|
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.
|
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-
|
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
|