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.
@@ -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 :Builder, 'state_flow/builder'
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 :NamedAction, 'state_flow/named_action'
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 :Entry, 'state_flow/entry'
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 ::StateFlow::Builder::ClientClassMethods
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
 
data/spec/database.yml CHANGED
@@ -21,5 +21,3 @@ postgresql:
21
21
  host: localhost
22
22
  username: postgres
23
23
  password:
24
- allow_concurrency: true
25
- pool: 30