state_flow 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +61 -23
- data/VERSION +1 -1
- data/lib/state_flow/action.rb +22 -56
- data/lib/state_flow/action_client.rb +16 -0
- data/lib/state_flow/action_event.rb +21 -0
- data/lib/state_flow/base.rb +71 -28
- data/lib/state_flow/context.rb +82 -0
- data/lib/state_flow/element.rb +60 -0
- data/lib/state_flow/element_visitable.rb +19 -0
- data/lib/state_flow/event.rb +13 -13
- data/lib/state_flow/event_client.rb +88 -0
- data/lib/state_flow/exception_handler.rb +39 -0
- data/lib/state_flow/exception_handler_client.rb +25 -0
- data/lib/state_flow/guard.rb +21 -0
- data/lib/state_flow/guard_client.rb +28 -0
- data/lib/state_flow/named_event.rb +56 -0
- data/lib/state_flow/named_guard.rb +17 -0
- data/lib/state_flow/state.rb +91 -0
- data/lib/state_flow.rb +20 -6
- data/spec/database.yml +0 -2
- data/spec/order_spec.rb +415 -0
- data/spec/resources/models/order.rb +171 -0
- data/spec/schema.rb +2 -7
- data/spec/spec_helper.rb +1 -3
- data/spec/state_flow/state_spec.rb +149 -0
- metadata +21 -13
- data/lib/state_flow/builder.rb +0 -193
- data/lib/state_flow/entry.rb +0 -44
- data/lib/state_flow/named_action.rb +0 -19
- data/spec/base_spec.rb +0 -457
- data/spec/concurrency_spec.rb +0 -275
- data/spec/page_spec.rb +0 -554
- data/spec/resources/models/page.rb +0 -70
@@ -0,0 +1,39 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'state_flow'
|
3
|
+
module StateFlow
|
4
|
+
|
5
|
+
class ExceptionHandler < Event
|
6
|
+
attr_reader :exceptions
|
7
|
+
attr_reader :recovering, :rolling_back, :logging_error
|
8
|
+
|
9
|
+
def initialize(origin, *exceptions, &block)
|
10
|
+
options = exceptions.extract_options!
|
11
|
+
@exceptions = exceptions
|
12
|
+
super(origin, &block)
|
13
|
+
@recovering = options[:recovering] || false
|
14
|
+
@rolling_back = options[:rolling_back] || options[:rollback] || false
|
15
|
+
@logging_error = options[:logging]
|
16
|
+
end
|
17
|
+
|
18
|
+
def match?(exception)
|
19
|
+
prepare_exceptions
|
20
|
+
exceptions.any?{|klass| exception.is_a?(klass)}
|
21
|
+
end
|
22
|
+
|
23
|
+
def process(context)
|
24
|
+
context.recovered_exceptions << context.exceptions.last if recovering
|
25
|
+
context.transaction_rollback if rolling_back
|
26
|
+
context.record_reload_if_possible
|
27
|
+
super
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def prepare_exceptions
|
32
|
+
return if exceptions.all?{|ex| ex.is_a?(Class)}
|
33
|
+
@exceptions = exceptions.map do |ex|
|
34
|
+
ex.is_a?(Class) ? ex : ex.to_s.constantize
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'state_flow'
|
3
|
+
module StateFlow
|
4
|
+
|
5
|
+
module ExceptionHandlerClient
|
6
|
+
def exception_handling(context)
|
7
|
+
begin
|
8
|
+
yield
|
9
|
+
rescue Exception => exception
|
10
|
+
context.exceptions << exception
|
11
|
+
context.trace(exception)
|
12
|
+
handlers = events.select{|ev| ev.is_a?(ExceptionHandler)}
|
13
|
+
handlers.each do |handler|
|
14
|
+
next unless handler.match?(exception)
|
15
|
+
handler.process(context)
|
16
|
+
break
|
17
|
+
end
|
18
|
+
raise exception unless context.recovered?(exception)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'state_flow'
|
2
|
+
module StateFlow
|
3
|
+
|
4
|
+
class Guard < Element
|
5
|
+
include EventClient
|
6
|
+
include ActionClient
|
7
|
+
|
8
|
+
def match?(context)
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def process(context)
|
13
|
+
exception_handling(context) do
|
14
|
+
action.process(context) if action
|
15
|
+
update_to_destination(context)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'state_flow'
|
2
|
+
module StateFlow
|
3
|
+
|
4
|
+
module GuardClient
|
5
|
+
def guards
|
6
|
+
@guards ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def guard(method_name, &block)
|
10
|
+
result = NamedGuard.new(self, method_name, &block)
|
11
|
+
guards << result
|
12
|
+
result
|
13
|
+
end
|
14
|
+
|
15
|
+
def guard_else(&block)
|
16
|
+
result = Guard.new(self, &block)
|
17
|
+
guards << result
|
18
|
+
result
|
19
|
+
end
|
20
|
+
|
21
|
+
def guard_for(context)
|
22
|
+
guards.detect{|guard| guard.match?(context)}
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'state_flow'
|
3
|
+
module StateFlow
|
4
|
+
|
5
|
+
class NamedEvent < Event
|
6
|
+
attr_reader :name
|
7
|
+
def initialize(origin, name, &block)
|
8
|
+
@name = name
|
9
|
+
super(origin, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def process(context)
|
13
|
+
block = state.ancestors_exception_handled_proc(context) do
|
14
|
+
super
|
15
|
+
end
|
16
|
+
block.call
|
17
|
+
end
|
18
|
+
|
19
|
+
class << self
|
20
|
+
def build_event_methods(flow)
|
21
|
+
named_events = []
|
22
|
+
flow.all_states.each do |state_name, state|
|
23
|
+
state.visit do |event|
|
24
|
+
named_events << event if event.is_a?(NamedEvent)
|
25
|
+
[:events, :guards, :action]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
method_name_to_events = {}
|
29
|
+
named_events.each do |ev|
|
30
|
+
method_name_to_events[ev.name] ||= []
|
31
|
+
method_name_to_events[ev.name] << ev
|
32
|
+
end
|
33
|
+
method_name_to_events.each do |name, events|
|
34
|
+
flow.klass.module_eval do
|
35
|
+
# イベントを通知するときに呼び出されるメソッド
|
36
|
+
define_method(name) do |*args|
|
37
|
+
result = nil
|
38
|
+
events.each do |event|
|
39
|
+
if event.state.include?(self.send(flow.attr_key_name))
|
40
|
+
context = flow.prepare_context(self, args.first)
|
41
|
+
result = context.process(event)
|
42
|
+
break
|
43
|
+
end
|
44
|
+
end
|
45
|
+
result # return
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'state_flow'
|
3
|
+
module StateFlow
|
4
|
+
|
5
|
+
class NamedGuard < Guard
|
6
|
+
attr_reader :name
|
7
|
+
def initialize(origin, name, &block)
|
8
|
+
@name = name
|
9
|
+
super(origin, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
def match?(context)
|
13
|
+
context.record_send(name)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require 'state_flow'
|
3
|
+
module StateFlow
|
4
|
+
|
5
|
+
class State
|
6
|
+
include EventClient
|
7
|
+
include GuardClient
|
8
|
+
include ActionClient
|
9
|
+
include ElementVisitable
|
10
|
+
|
11
|
+
attr_reader :name, :flow, :parent
|
12
|
+
attr_accessor :termination
|
13
|
+
def initialize(flow_or_parent, name, &block)
|
14
|
+
@name = name
|
15
|
+
@parent = flow_or_parent if flow_or_parent.is_a?(State)
|
16
|
+
@flow = flow_or_parent.is_a?(State) ? flow_or_parent.flow : flow_or_parent
|
17
|
+
@concreate = @flow.state_cd_by_key(@name)
|
18
|
+
instance_eval(&block) if block
|
19
|
+
end
|
20
|
+
|
21
|
+
def state(name, &block)
|
22
|
+
result = State.new(self, name, &block)
|
23
|
+
children << result
|
24
|
+
result
|
25
|
+
end
|
26
|
+
alias_method :from, :state
|
27
|
+
alias_method :group, :state
|
28
|
+
alias_method :state_group, :state
|
29
|
+
|
30
|
+
def termination(name = nil)
|
31
|
+
result = name ? state(name) : self
|
32
|
+
result.termination = true
|
33
|
+
result
|
34
|
+
end
|
35
|
+
|
36
|
+
def children
|
37
|
+
@children ||= []
|
38
|
+
end
|
39
|
+
|
40
|
+
def descendants
|
41
|
+
[self, children.map{|c|c.descendants}].flatten
|
42
|
+
end
|
43
|
+
|
44
|
+
def include?(state_or_name)
|
45
|
+
name = state_or_name.is_a?(State) ? state_or_name.name : state_or_name
|
46
|
+
descendants.any?{|state| state.name == name}
|
47
|
+
end
|
48
|
+
|
49
|
+
def concrete?
|
50
|
+
@concreate
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
# for EventClient
|
55
|
+
def origin
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
public
|
60
|
+
def process(context)
|
61
|
+
context.trace(self)
|
62
|
+
block = ancestors_exception_handled_proc(context) do
|
63
|
+
guard = guard_for(context)
|
64
|
+
return guard.process(context) if guard
|
65
|
+
return action.process(context) if action
|
66
|
+
end
|
67
|
+
block.call
|
68
|
+
end
|
69
|
+
|
70
|
+
def ancestors_exception_handled_proc(context, &block)
|
71
|
+
result = Proc.new{ exception_handling(context, &block) }
|
72
|
+
parent ? parent.ancestors_exception_handled_proc(context, &result) : result
|
73
|
+
end
|
74
|
+
|
75
|
+
def name_path(separator = '>')
|
76
|
+
result = []
|
77
|
+
current = self
|
78
|
+
while current
|
79
|
+
result << current.name
|
80
|
+
current = current.parent
|
81
|
+
end
|
82
|
+
result.reverse.join(separator)
|
83
|
+
end
|
84
|
+
|
85
|
+
def inspect
|
86
|
+
"#<%s:%#x @name=%s>" % [self.class.name, self.object_id, name_path.inspect]
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/lib/state_flow.rb
CHANGED
@@ -1,19 +1,33 @@
|
|
1
1
|
module StateFlow
|
2
2
|
autoload :Base, 'state_flow/base'
|
3
|
-
autoload :
|
3
|
+
autoload :Context, 'state_flow/context'
|
4
|
+
|
5
|
+
autoload :State, 'state_flow/state'
|
6
|
+
|
7
|
+
autoload :Element, 'state_flow/element'
|
8
|
+
autoload :ElementVisitable, 'state_flow/element_visitable'
|
9
|
+
|
4
10
|
autoload :Action, 'state_flow/action'
|
5
|
-
autoload :
|
11
|
+
autoload :ActionClient, 'state_flow/action_client'
|
12
|
+
|
13
|
+
autoload :Guard, 'state_flow/guard'
|
14
|
+
autoload :GuardClient, 'state_flow/guard_client'
|
15
|
+
autoload :NamedGuard, 'state_flow/named_guard'
|
16
|
+
|
6
17
|
autoload :Event, 'state_flow/event'
|
7
|
-
autoload :
|
18
|
+
autoload :EventClient, 'state_flow/event_client'
|
19
|
+
autoload :NamedEvent, 'state_flow/named_event'
|
20
|
+
autoload :ActionEvent, 'state_flow/action_event'
|
21
|
+
autoload :ExceptionHandler, 'state_flow/exception_handler'
|
22
|
+
autoload :ExceptionHandlerClient, 'state_flow/exception_handler_client'
|
23
|
+
|
8
24
|
autoload :Log, 'state_flow/log'
|
9
25
|
|
10
26
|
# autoload :ActiveRecord, 'state_flow/active_record'
|
11
27
|
|
12
28
|
def self.included(mod)
|
13
29
|
mod.module_eval do
|
14
|
-
extend
|
15
|
-
extend ::StateFlow::Base::ClassMethods
|
16
|
-
# include ::StateFlow::ActiveRecord if mod.ancestors.map{|m| m.name}.include?('ActiveRecord::Base')
|
30
|
+
extend(Base::ClientClassMethods)
|
17
31
|
end
|
18
32
|
end
|
19
33
|
|