hifsm 0.3.0 → 0.3.1
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.
- checksums.yaml +4 -4
- data/README.md +12 -4
- data/lib/hifsm/adapters/active_record_adapter.rb +10 -0
- data/lib/hifsm/fsm.rb +38 -21
- data/lib/hifsm/state.rb +32 -28
- data/lib/hifsm/version.rb +1 -1
- data/test/test_activerecord_adapter.rb +8 -0
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 313a9e688988c6dac1ee778973a602db61af4ca2
|
4
|
+
data.tar.gz: cf7dc5e882dbf4f8dfaf34f211aa04d136474b01
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b585b14728b541d4d2863d71e7ac55a59699f71d33ed48ef8b9a366b8f2f2440b8426fbad9287564748e6acb0fb05308f476b66aa72cd61d70877d9691db30d9
|
7
|
+
data.tar.gz: 53df6c486ca83c6745fca49e9cec581348f5b6919d3efcfd76aa6316682d4d3c6dafb31342777d3530838852a9c0665cebfa3b3bdcfd3f0bc319b8e221854f1a
|
data/README.md
CHANGED
@@ -21,7 +21,7 @@ Or install it yourself as:
|
|
21
21
|
|
22
22
|
$ gem install hifsm
|
23
23
|
|
24
|
-
|
24
|
+
Written in Ruby 1.8-style (hashes, lambdas), but few non-essential 1.9 niceties used, tested in 2+.
|
25
25
|
|
26
26
|
__This is in early development, so be careful.__
|
27
27
|
|
@@ -196,20 +196,28 @@ Add column to your database which would hold the state, and then:
|
|
196
196
|
|
197
197
|
```ruby
|
198
198
|
class Order < ActiveRecord::Base
|
199
|
-
hifsm do
|
199
|
+
hifsm :status do
|
200
200
|
state :draft, :initial => true
|
201
201
|
state :processing do
|
202
202
|
state :packaging, :initial => true
|
203
203
|
state :delivering
|
204
|
+
|
205
|
+
event :start_delivery, :from => :packaging, :to => :delivering
|
204
206
|
end
|
205
207
|
state :done
|
206
208
|
state :cancelled
|
207
209
|
|
208
210
|
event :start_processing, :from => :draft, :to => :processing
|
209
|
-
event :cancel
|
211
|
+
event :cancel!, :to => :cancelled
|
210
212
|
end
|
211
213
|
end
|
212
|
-
Order.
|
214
|
+
order = Order.create # draft
|
215
|
+
order.start_processing.save # 'processing.packaging'
|
216
|
+
|
217
|
+
# scopes defined automatically. parent scopes looked up via like "processing.%"
|
218
|
+
Order.processing.first.start_delivery.save
|
219
|
+
Order.processing_packaging.first # nil
|
220
|
+
Order.processing_delivering.first.cancel!.save # save is never called inisde hifsm
|
213
221
|
|
214
222
|
```
|
215
223
|
|
@@ -12,6 +12,16 @@ module Hifsm
|
|
12
12
|
include Hifsm.fsm_module(column, &block)
|
13
13
|
before_save "hifsm_write_#{column}_attribute"
|
14
14
|
|
15
|
+
send("#{column}_machine_definition").all_states.each do |st|
|
16
|
+
state_name = st.to_s
|
17
|
+
scope_name = state_name.gsub('.', '_')
|
18
|
+
if st.sub_fsm
|
19
|
+
scope scope_name, lambda { where(arel_table[column].matches(state_name + '.%')) }
|
20
|
+
else
|
21
|
+
scope scope_name, lambda { where(column => state_name) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
15
25
|
define_method "#{column}=" do |value|
|
16
26
|
raise 'not (sure will be) implemented'
|
17
27
|
end
|
data/lib/hifsm/fsm.rb
CHANGED
@@ -23,24 +23,7 @@ module Hifsm
|
|
23
23
|
@machine_class.new(self, target, initial_state)
|
24
24
|
end
|
25
25
|
|
26
|
-
|
27
|
-
@states.collect {|name, st| st.events }.flatten.uniq
|
28
|
-
end
|
29
|
-
|
30
|
-
def initial_state!
|
31
|
-
@initial_state || raise(Hifsm::MissingState.new("<initial>"))
|
32
|
-
end
|
33
|
-
|
34
|
-
def get_state!(name)
|
35
|
-
top_level_state, rest = name.to_s.split('.', 2)
|
36
|
-
st = @states[top_level_state] || raise(Hifsm::MissingState.new(name.to_s))
|
37
|
-
if rest
|
38
|
-
st.get_substate!(rest)
|
39
|
-
else
|
40
|
-
st
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
26
|
+
#DSL
|
44
27
|
def event(name, options, &block)
|
45
28
|
ev = Hifsm::Event.new(name, get_state!(options[:to]), array_wrap(options[:guard]))
|
46
29
|
from_states = array_wrap(options[:from])
|
@@ -58,6 +41,32 @@ module Hifsm
|
|
58
41
|
st.instance_eval(&block) if block
|
59
42
|
end
|
60
43
|
|
44
|
+
# internals
|
45
|
+
def all_events
|
46
|
+
@states.flat_map {|name, st| st.events }.uniq
|
47
|
+
end
|
48
|
+
|
49
|
+
def all_states
|
50
|
+
@states.flat_map do |state_name, st|
|
51
|
+
# state should delegate to sub_fsm :)
|
52
|
+
[st] + (st.sub_fsm && st.sub_fsm.all_states || [])
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def initial_state!
|
57
|
+
@initial_state || raise(Hifsm::MissingState.new("<initial>"))
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_state!(name)
|
61
|
+
top_level_state, rest = name.to_s.split('.', 2)
|
62
|
+
st = @states[top_level_state] || raise(Hifsm::MissingState.new(name.to_s))
|
63
|
+
if rest
|
64
|
+
st.get_substate!(rest)
|
65
|
+
else
|
66
|
+
st
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
61
70
|
def to_module
|
62
71
|
@fsm_module
|
63
72
|
end
|
@@ -73,7 +82,17 @@ module Hifsm
|
|
73
82
|
machine_var = "@#{name}_machine"
|
74
83
|
machine_name = "#{name}_machine"
|
75
84
|
|
76
|
-
Module.new
|
85
|
+
module_class_methods = Module.new do
|
86
|
+
define_method("#{machine_name}_definition") { fsm }
|
87
|
+
end
|
88
|
+
|
89
|
+
Module.new do
|
90
|
+
const_set('ClassMethods', module_class_methods)
|
91
|
+
def self.included(base)
|
92
|
+
base.class_exec do
|
93
|
+
extend const_get('ClassMethods')
|
94
|
+
end
|
95
|
+
end
|
77
96
|
|
78
97
|
# <state>_machine returns machine instance
|
79
98
|
define_method(machine_name) do
|
@@ -92,8 +111,6 @@ module Hifsm
|
|
92
111
|
fsm.all_events.each do |event_name, event|
|
93
112
|
define_method(event_name) {|*args| send(machine_name).fire(event_name, *args) }
|
94
113
|
end
|
95
|
-
|
96
|
-
self # return module
|
97
114
|
end
|
98
115
|
end
|
99
116
|
end
|
data/lib/hifsm/state.rb
CHANGED
@@ -2,6 +2,8 @@ module Hifsm
|
|
2
2
|
class State
|
3
3
|
CALLBACKS = [:before_enter, :before_exit, :after_enter, :after_exit].freeze
|
4
4
|
|
5
|
+
attr_reader :sub_fsm
|
6
|
+
|
5
7
|
def initialize(fsm, name, parent = nil)
|
6
8
|
@fsm = fsm
|
7
9
|
@name = name
|
@@ -13,11 +15,7 @@ module Hifsm
|
|
13
15
|
@transitions = Hash.new {|h, key| h[key] = Array.new }
|
14
16
|
end
|
15
17
|
|
16
|
-
|
17
|
-
name = ev.name.to_s
|
18
|
-
@transitions[name].push ev
|
19
|
-
end
|
20
|
-
|
18
|
+
# DSL
|
21
19
|
def action(&block)
|
22
20
|
@action = block
|
23
21
|
end
|
@@ -30,10 +28,38 @@ module Hifsm
|
|
30
28
|
sub_fsm!.event(*args, &block)
|
31
29
|
end
|
32
30
|
|
31
|
+
CALLBACKS.each do |cb|
|
32
|
+
define_method(cb) { |&block| @callbacks[cb].add(&block) }
|
33
|
+
end
|
34
|
+
|
35
|
+
# internals
|
36
|
+
def act!(target, *args)
|
37
|
+
@parent.act!(target, *args) if @parent
|
38
|
+
@action && Callbacks.invoke(target, @action, *args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_transition(ev)
|
42
|
+
name = ev.name.to_s
|
43
|
+
@transitions[name].push ev
|
44
|
+
end
|
45
|
+
|
46
|
+
def enter!
|
47
|
+
if @sub_fsm
|
48
|
+
@sub_fsm.initial_state!
|
49
|
+
else
|
50
|
+
self
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
33
54
|
def events
|
34
55
|
@transitions.keys + (@sub_fsm && @sub_fsm.all_events || [])
|
35
56
|
end
|
36
57
|
|
58
|
+
def get_substate!(name)
|
59
|
+
raise Hifsm::MissingState.new(name.to_s) unless @sub_fsm
|
60
|
+
@sub_fsm.get_state!(name)
|
61
|
+
end
|
62
|
+
|
37
63
|
def fire(target, event_name, *args, &new_state_callback)
|
38
64
|
event_name = event_name.to_s
|
39
65
|
@transitions[event_name].each do |ev|
|
@@ -57,32 +83,10 @@ module Hifsm
|
|
57
83
|
raise Hifsm::MissingTransition.new(to_s, event_name)
|
58
84
|
end
|
59
85
|
|
60
|
-
CALLBACKS.each do |cb|
|
61
|
-
define_method(cb) { |&block| @callbacks[cb].add(&block) }
|
62
|
-
end
|
63
|
-
|
64
86
|
def trigger(target, cb, *args)
|
65
87
|
@callbacks[cb].trigger(target, *args)
|
66
88
|
end
|
67
89
|
|
68
|
-
def act!(target, *args)
|
69
|
-
@parent.act!(target, *args) if @parent
|
70
|
-
@action && Callbacks.invoke(target, @action, *args)
|
71
|
-
end
|
72
|
-
|
73
|
-
def enter!
|
74
|
-
if @sub_fsm
|
75
|
-
@sub_fsm.initial_state!
|
76
|
-
else
|
77
|
-
self
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def get_substate!(name)
|
82
|
-
raise Hifsm::MissingState.new(name.to_s) unless @sub_fsm
|
83
|
-
@sub_fsm.get_state!(name)
|
84
|
-
end
|
85
|
-
|
86
90
|
def to_s
|
87
91
|
if @parent
|
88
92
|
"#{@parent.to_s}.#{@name.to_s}"
|
@@ -93,7 +97,7 @@ module Hifsm
|
|
93
97
|
|
94
98
|
private
|
95
99
|
def sub_fsm!
|
96
|
-
# FIXME too much coupling
|
100
|
+
# FIXME .name = too much coupling
|
97
101
|
@sub_fsm ||= Hifsm::FSM.new(@fsm.name, self)
|
98
102
|
end
|
99
103
|
end
|
data/lib/hifsm/version.rb
CHANGED
@@ -69,6 +69,14 @@ class TestActiverecrodAdapter < Minitest::Test
|
|
69
69
|
assert_equal 'off', SodaMachine.where(:id => @machine.id).pluck(:state).first
|
70
70
|
end
|
71
71
|
|
72
|
+
def test_leaf_scope
|
73
|
+
refute_nil SodaMachine.broken.first
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_parent_scope
|
77
|
+
refute_nil SodaMachine.on.first
|
78
|
+
end
|
79
|
+
|
72
80
|
private
|
73
81
|
def insert_record(address, state)
|
74
82
|
insert_manager = Arel::InsertManager.new(SodaMachine)
|