state_flow 0.1.0 → 0.2.0
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.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
|
|