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.
@@ -0,0 +1,149 @@
1
+ # -*- coding: utf-8 -*-
2
+ require File.join(File.dirname(__FILE__), '../spec_helper')
3
+
4
+ describe StateFlow::State do
5
+
6
+ describe "with Order" do
7
+ describe "from waiting_settling" do
8
+ describe ":cash_on_delivery" do
9
+ before do
10
+ StateFlow::Log.delete_all
11
+ Order.delete_all
12
+ @order = Order.new
13
+ @order.product_name = "Beautiful Code"
14
+ @order.payment_type = :cash_on_delivery
15
+ @order.status_key = :waiting_settling
16
+ @order.save!
17
+ Order.count == 1
18
+
19
+ @flow = Order.state_flow_for(:status_cd)
20
+ @state = @flow.origin
21
+ @state.name.should == :waiting_settling
22
+
23
+ @context = @flow.prepare_context(@order)
24
+ end
25
+
26
+ it "should recieve guard process" do
27
+ g0 = @state.guards[0] # :pay_cash_on_delivery?
28
+ g0.should_receive(:process).with(@context).and_return{|order| g0.action.process(order)}
29
+ @order.process_status_cd(@context)
30
+ @order.status_key.should == :stock_error
31
+ end
32
+
33
+ it "should recieve action process #0" do
34
+ g0 = @state.guards[0] # :pay_cash_on_delivery?
35
+ a0 = g0.action # :reserve_point
36
+ a0.should_receive(:process).with(@context).and_return{|order| a0.action.process(order)}
37
+ @order.process_status_cd(@context)
38
+ @order.status_key.should == :stock_error
39
+ end
40
+
41
+ it "should recieve action process #1" do
42
+ g0 = @state.guards[0] # :pay_cash_on_delivery?
43
+ a0 = g0.action # :reserve_point
44
+ a1 = a0.action # :reserve_stock
45
+ a1.should_receive(:process).with(@context).and_return{|order| a1.events[0].process(order)}
46
+ @order.process_status_cd(@context)
47
+ @order.status_key.should == :deliver_preparing
48
+ end
49
+
50
+ describe "action event invocation" do
51
+ before do
52
+ g0 = @state.guards[0] # :pay_cash_on_delivery?
53
+ a0 = g0.action # :reserve_point
54
+ @a1 = a0.action # :reserve_stock
55
+ end
56
+
57
+ it "should recieve action events_for_action" do
58
+ @order.should_receive(:reserve_stock).and_return(nil)
59
+ @a1.should_receive(:event_for_action_result).and_return(@a1.events[0])
60
+ @order.process_status_cd(@context)
61
+ @order.status_key.should == :deliver_preparing
62
+ end
63
+
64
+ it "should recieve action events_for_action" do
65
+ @order.should_receive(:reserve_stock).and_return(:reserve_stock_ok)
66
+ @a1.should_receive(:event_for_action_result).and_return(@a1.events[1])
67
+ @order.process_status_cd(@context)
68
+ @order.status_key.should == :stock_error
69
+ end
70
+ end
71
+
72
+ it "should recieve action event update_to_destination" do
73
+ @order.should_receive(:reserve_stock).and_return(:reserve_stock_ok)
74
+ @order.process_status_cd(@context)
75
+ @order.status_key.should == :deliver_preparing
76
+ end
77
+
78
+ it "should recieve action event update_to_destination #2" do
79
+ @order.should_receive(:reserve_stock).and_return(nil)
80
+ @order.process_status_cd(@context)
81
+ @order.status_key.should == :stock_error
82
+ end
83
+ end
84
+
85
+ describe ":credit_card" do
86
+ before do
87
+ StateFlow::Log.delete_all
88
+ Order.delete_all
89
+ @order = Order.new
90
+ @order.product_name = "Beautiful Code"
91
+ @order.payment_type = :credit_card
92
+ @order.status_key = :waiting_settling
93
+ @order.save!
94
+ Order.count == 1
95
+
96
+ @flow = Order.state_flow_for(:status_cd)
97
+ @state = @flow.origin
98
+ @state.name.should == :waiting_settling
99
+
100
+ @context = @flow.prepare_context(@order)
101
+ end
102
+
103
+ describe ":reserve_stock_ok" do
104
+ before do
105
+ @order.should_receive(:reserve_stock).and_return(:reserve_stock_ok)
106
+ end
107
+
108
+ it "should recieve guard process" do
109
+ g1 = @state.guards[1]
110
+ g1.should_receive(:process).with(@context).and_return{|order| g1.action.process(order)}
111
+ @order.process_status_cd(@context)
112
+ end
113
+
114
+ it "should recieve action event_for_action_result" do
115
+ g1 = @state.guards[1]
116
+ a0 = g1.action
117
+ a1 = a0.action
118
+ a1.event_for_action_result(:reserve_stock_ok).should == a1.events[0]
119
+ a1.should_receive(:event_for_action_result).and_return(a1.events[0])
120
+ @order.process_status_cd(@context)
121
+ end
122
+
123
+ it "should recieve evnet process" do
124
+ g1 = @state.guards[1]
125
+ a0 = g1.action
126
+ a1 = a0.action
127
+ e0 = a1.events[0]
128
+ e0.should_receive(:process).with(@context).and_return{|order| e0.guards[1].process(order)}
129
+ @order.process_status_cd(@context)
130
+ end
131
+
132
+ it "should recieve evnet process" do
133
+ g1 = @state.guards[1]
134
+ a0 = g1.action
135
+ a1 = a0.action
136
+ e0 = a1.events[0]
137
+ g1 = e0.guards[1]
138
+ g1.should_receive(:process).with(@context).and_return{|order| g1.update_to_destination(order)}
139
+ @order.process_status_cd(@context)
140
+ end
141
+ end
142
+
143
+
144
+ end
145
+
146
+ end
147
+ end
148
+
149
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: state_flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Takeshi Akima
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-10-27 00:00:00 +09:00
12
+ date: 2009-11-04 00:00:00 +09:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -31,21 +31,30 @@ files:
31
31
  - install.rb
32
32
  - lib/state_flow.rb
33
33
  - lib/state_flow/action.rb
34
+ - lib/state_flow/action_client.rb
35
+ - lib/state_flow/action_event.rb
34
36
  - lib/state_flow/base.rb
35
- - lib/state_flow/builder.rb
36
- - lib/state_flow/entry.rb
37
+ - lib/state_flow/context.rb
38
+ - lib/state_flow/element.rb
39
+ - lib/state_flow/element_visitable.rb
37
40
  - lib/state_flow/event.rb
41
+ - lib/state_flow/event_client.rb
42
+ - lib/state_flow/exception_handler.rb
43
+ - lib/state_flow/exception_handler_client.rb
44
+ - lib/state_flow/guard.rb
45
+ - lib/state_flow/guard_client.rb
38
46
  - lib/state_flow/log.rb
39
- - lib/state_flow/named_action.rb
47
+ - lib/state_flow/named_event.rb
48
+ - lib/state_flow/named_guard.rb
49
+ - lib/state_flow/state.rb
40
50
  - spec/.gitignore
41
- - spec/base_spec.rb
42
- - spec/concurrency_spec.rb
43
51
  - spec/database.yml
44
- - spec/page_spec.rb
45
- - spec/resources/models/page.rb
52
+ - spec/order_spec.rb
53
+ - spec/resources/models/order.rb
46
54
  - spec/schema.rb
47
55
  - spec/spec.opts
48
56
  - spec/spec_helper.rb
57
+ - spec/state_flow/state_spec.rb
49
58
  - tasks/state_flow_tasks.rake
50
59
  - uninstall.rb
51
60
  has_rdoc: true
@@ -77,9 +86,8 @@ signing_key:
77
86
  specification_version: 3
78
87
  summary: state_flow provides a DSL for State Transition
79
88
  test_files:
80
- - spec/base_spec.rb
81
- - spec/concurrency_spec.rb
82
- - spec/page_spec.rb
83
- - spec/resources/models/page.rb
89
+ - spec/order_spec.rb
90
+ - spec/resources/models/order.rb
84
91
  - spec/schema.rb
85
92
  - spec/spec_helper.rb
93
+ - spec/state_flow/state_spec.rb
@@ -1,193 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- require 'state_flow'
3
- module StateFlow
4
-
5
- module Builder
6
- module ClientClassMethods
7
- def state_flow_for(selectable_attr)
8
- return nil unless @state_flows
9
- @state_flows.detect{|flow| flow.attr_name == selectable_attr}
10
- end
11
-
12
- def state_flow(selectable_attr, options = nil, &block)
13
- options = {
14
- :attr_key_name => "#{self.enum_base_name(selectable_attr)}_key".to_sym
15
- }.update(options || {})
16
- flow = Base.new(self, selectable_attr, options[:attr_key_name])
17
- flow.instance_eval(&block)
18
- flow.setup_events
19
- @state_flows ||= []
20
- @state_flows << flow
21
- flow
22
- end
23
- end
24
-
25
- def new_entry(key)
26
- @entry_hash = nil
27
- result = Entry.new(self, key)
28
- entries << result
29
- result
30
- end
31
-
32
- def state(*args)
33
- raise_invalid_state_argument if args.length > 2
34
- if args.length == 2
35
- # 引数が2個ならできるだけ一つのHashにまとめます。
36
- case args.first
37
- when Hash then
38
- return state(args.first.update(args.last))
39
- when Symbol, String
40
- return state({args.first => nil}.update(args.last))
41
- else
42
- raise_invalid_state_argument
43
- end
44
- end
45
- # 引数が一つだけになってます
46
- arg = args.first
47
- case arg
48
- when Symbol, String
49
- return state({args.first => nil})
50
- when Hash then
51
- # through
52
- else
53
- raise_invalid_state_argument
54
- end
55
- # 引数がHash一つだけの場合
56
- base_options = extract_common_options(arg)
57
- arg.each do |key, value|
58
- entry = new_entry(key)
59
- build_entry(entry, value, base_options)
60
- end
61
- end
62
-
63
- def action(name)
64
- NamedAction.new(self, name)
65
- end
66
-
67
- def event(name)
68
- Event.new(self, name)
69
- end
70
-
71
- def setup_events
72
- event_defs = {}
73
- entries.each do |entry|
74
- origin_key = entry.key
75
- entry.events.each do |event|
76
- event_trans = event_defs[event.name] ||= {}
77
- event_trans[origin_key] = [event.success_key, event.failure_key]
78
- end
79
- end
80
- event_defs.each do |event_name, trans|
81
- method_def = <<-"EOS"
82
- def #{event_name}
83
- @state_flow ||= self.class.state_flow_for(:#{attr_name})
84
- @#{event_name}_transitions ||= #{trans.inspect}
85
- @state_flow.process_with_log(self,
86
- *@#{event_name}_transitions[#{attr_key_name}])
87
- end
88
- EOS
89
- if klass.instance_methods.include?(event_name)
90
- klass.logger.warn("state_flow plugin was going to define #{event_name} but didn't do it because #{event_name} does exist.")
91
- else
92
- klass.module_eval(method_def, __FILE__, __LINE__)
93
- end
94
- end
95
- end
96
-
97
- private
98
-
99
- def build_entry(entry, options_or_success_key, base_options)
100
- if options_or_success_key.nil?
101
- return null_action_entry(entry, base_options)
102
- end
103
- case options_or_success_key
104
- when String, Symbol then
105
- return null_action_entry(entry, base_options, options_or_success_key.to_sym)
106
- when Action then
107
- entry.action = setup_action(options_or_success_key, base_options)
108
- return entry
109
- when Hash then
110
- options = options_or_success_key
111
- prior_options = extract_common_options(options)
112
- options_keys = options.keys
113
- if options_keys.all?{|key| key.is_a?(Action)}
114
- build_action(entry, options, base_options.merge(prior_options))
115
- elsif options_keys.all?{|key| key.is_a?(Event)}
116
- raise_invalid_state_argument unless entry.is_a?(Entry)
117
- build_events(entry, options, base_options.merge(prior_options))
118
- else
119
- raise_invalid_state_argument
120
- end
121
- when Array then
122
- options_or_success_key.each do |options|
123
- each_base_options = base_options.merge(extract_common_options(options))
124
- build_entry(entry, options, each_base_options)
125
- end
126
- else
127
- raise_invalid_state_argument
128
- end
129
- end
130
-
131
- def null_action_entry(entry, options, success_key = nil)
132
- action = Action.new(self)
133
- options = options.dup
134
- entry.action = setup_action(action, options, success_key)
135
- entry.options = options
136
- entry
137
- end
138
-
139
- def build_action(entry, action_hash, options)
140
- raise_invalid_state_argument unless action_hash.length == 1
141
- options = options.dup
142
- entry.action = setup_action(action_hash.keys.first, options, action_hash.values.first)
143
- entry.options = options
144
- end
145
-
146
- def setup_action(action, options, success_key = nil)
147
- action.success_key = success_key.to_sym if success_key
148
- action.failure_key = options.delete(:failure)
149
- action.lock = options.delete(:lock)
150
- action.if = options.delete(:if)
151
- action.unless = options.delete(:unless)
152
- action
153
- end
154
-
155
-
156
- def build_events(entry, event_hash, options)
157
- event_hash.each do |event, value|
158
- build_entry(event, value, options)
159
- entry.events << event
160
- end
161
- end
162
-
163
- COMMON_OPTION_NAMES = [:lock, :if, :unless, :failure]
164
-
165
- def extract_common_options(hash)
166
- COMMON_OPTION_NAMES.inject({}) do |dest, name|
167
- value = hash.delete(name)
168
- dest[name] = value if value
169
- dest
170
- end
171
- end
172
-
173
- def raise_invalid_state_argument
174
- raise ArgumentError, state_argument_pattern
175
- end
176
-
177
- def state_argument_pattern
178
- descriptions = <<-"EOS"
179
- state arguments pattern:
180
- * state :<state_name>
181
- * state :<state_name> => :<new_state_name>
182
- * state :<state_name> => action(:<method_name>)
183
- * state :<state_name> => { action(:<method_name>) => :<new_state_name>}
184
- * state :<state_name> => { event(:<event_name1>) => :<new_state_name>, event(:<event_name2>) => :<new_state_name>}
185
- * state :<state_name> => { event(:<event_name1>) => { action(:<method_name1>) => :<new_state_name>}, event(:<event_name2>) => {action(:<method_name1>) => :<new_state_name>} }
186
- And you can append :lock, :if, :unless option in Hash
187
- EOS
188
- descriptions.split(/$/).map{|s| s.sub(/^\s{8}/, '')}.join("\n")
189
- end
190
-
191
- end
192
-
193
- end
@@ -1,44 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- require 'state_flow'
3
- module StateFlow
4
-
5
- class Entry
6
- include Action::Executable
7
- attr_reader :flow, :key
8
-
9
- def initialize(flow, key)
10
- @flow = flow
11
- @key = key.to_s.to_sym
12
- end
13
-
14
- def events
15
- @events ||= [];
16
- end
17
-
18
- def event_for(name)
19
- events.detect{|event| event.name == name}
20
- end
21
-
22
- def process(&block)
23
- value = flow.state_cd_by_key(key)
24
- find_options = {
25
- :order => "id asc",
26
- :conditions => ["#{flow.attr_name} = ?", value]
27
- }
28
- find_options[:lock] = action.lock if action.lock
29
- if record = flow.klass.find(:first, find_options)
30
- action.process(record, &block) if action
31
- end
32
- end
33
-
34
- def inspect
35
- result = "<#{self.class.name} @key=#{@key.inspect}"
36
- result << " @action=#{@action.inspect}" if @action
37
- if @events && !@events.empty?
38
- result << " @events=#{@events.sort_by{|event|event.name.to_s}.inspect}"
39
- end
40
- result << ">"
41
- end
42
- end
43
-
44
- end
@@ -1,19 +0,0 @@
1
- # -*- coding: utf-8 -*-
2
- require 'state_flow'
3
- module StateFlow
4
-
5
- class NamedAction < Action
6
- attr_reader :name
7
- def initialize(flow, name)
8
- super(flow)
9
- @name = name.to_s.to_sym
10
- end
11
-
12
- def proceed
13
- flow.process_with_log(self.record, success_key, failure_key) do
14
- self.record.send(name)
15
- end
16
- end
17
- end
18
-
19
- end