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
data/spec/order_spec.rb
ADDED
@@ -0,0 +1,415 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
require File.join(File.dirname(__FILE__), 'spec_helper')
|
3
|
+
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
describe Order do
|
7
|
+
before(:each) do
|
8
|
+
StateFlow::Log.delete_all
|
9
|
+
Order.delete_all
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "from waiting_settling" do
|
13
|
+
describe "cach_on_delivery" do
|
14
|
+
before do
|
15
|
+
@order = Order.new
|
16
|
+
@order.product_name = "Beautiful Code"
|
17
|
+
@order.payment_type = :cash_on_delivery
|
18
|
+
@order.status_key = :waiting_settling
|
19
|
+
@order.save!
|
20
|
+
Order.count.should == 1
|
21
|
+
end
|
22
|
+
|
23
|
+
it "reserve_stock succeed" do
|
24
|
+
@order.should_receive(:reserve_point)
|
25
|
+
@order.should_receive(:reserve_stock).once.and_return(:reserve_stock_ok)
|
26
|
+
@order.process_status_cd(:save => false)
|
27
|
+
@order.status_key.should == :deliver_preparing
|
28
|
+
Order.count.should == 1
|
29
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 1
|
30
|
+
end
|
31
|
+
|
32
|
+
it "reserve_stock fails" do
|
33
|
+
@order.should_receive(:reserve_point)
|
34
|
+
@order.should_receive(:reserve_stock).once.and_return(nil)
|
35
|
+
@order.process_status_cd(:save => false)
|
36
|
+
@order.status_key.should == :stock_error
|
37
|
+
Order.count.should == 1
|
38
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it "reserve_stock failed by Net::ProtoServerError" do
|
42
|
+
@order.should_receive(:reserve_point)
|
43
|
+
@order.should_receive(:reserve_stock).once.and_raise(Net::ProtoServerError)
|
44
|
+
@order.process_status_cd
|
45
|
+
@order.status_key.should == :external_error
|
46
|
+
# saveされてます。
|
47
|
+
Order.count.should == 1
|
48
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 0
|
49
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:external_error)}).should == 1
|
50
|
+
StateFlow::Log.count.should == 0
|
51
|
+
end
|
52
|
+
|
53
|
+
it "reserve_stock failed by IOError" do
|
54
|
+
@order.should_receive(:reserve_point)
|
55
|
+
@order.should_receive(:reserve_stock).once.and_raise(IOError)
|
56
|
+
@order.process_status_cd
|
57
|
+
@order.status_key.should == :internal_error
|
58
|
+
# saveされてます。
|
59
|
+
Order.count.should == 1
|
60
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 0
|
61
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:internal_error)}).should == 1
|
62
|
+
StateFlow::Log.count.should == 0
|
63
|
+
end
|
64
|
+
|
65
|
+
it "cancel_request" do
|
66
|
+
@order.should_receive(:send_mail_cancel_requested)
|
67
|
+
@order.cancel_request
|
68
|
+
@order.status_key.should == :cancel_requested
|
69
|
+
# saveされてます。
|
70
|
+
Order.count.should == 1
|
71
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:cancel_requested)}).should == 1
|
72
|
+
StateFlow::Log.count.should == 0
|
73
|
+
end
|
74
|
+
|
75
|
+
it "cancel_request but raised error" do
|
76
|
+
@order.should_receive(:send_mail_cancel_requested).and_raise(IOError)
|
77
|
+
@order.cancel_request
|
78
|
+
@order.status_key.should == :internal_error
|
79
|
+
# saveされてます。
|
80
|
+
Order.count.should == 1
|
81
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:internal_error)}).should == 1
|
82
|
+
StateFlow::Log.count.should == 0
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "credit_card" do
|
87
|
+
before do
|
88
|
+
@order = Order.new
|
89
|
+
@order.product_name = "Beautiful Code"
|
90
|
+
@order.payment_type = :credit_card
|
91
|
+
@order.status_key = :waiting_settling
|
92
|
+
@order.save!
|
93
|
+
Order.count.should == 1
|
94
|
+
end
|
95
|
+
|
96
|
+
it "reserve_stock succeed step by step" do
|
97
|
+
@order.should_receive(:reserve_point)
|
98
|
+
@order.should_receive(:reserve_stock).with(:temporary => true).once.and_return(:reserve_stock_ok)
|
99
|
+
@order.process_status_cd(:save => false, :keep_process => false)
|
100
|
+
@order.status_key.should == :online_settling
|
101
|
+
# saveされてません。
|
102
|
+
Order.count.should == 1
|
103
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 1
|
104
|
+
@order.save!
|
105
|
+
Order.count.should == 1
|
106
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:online_settling)}).should == 1
|
107
|
+
end
|
108
|
+
|
109
|
+
it "reserve_stock succeed keep_process" do
|
110
|
+
@order.should_receive(:reserve_point)
|
111
|
+
@order.should_receive(:reserve_stock).with(:temporary => true).once.and_return(:reserve_stock_ok)
|
112
|
+
@order.should_receive(:reserve_stock).once.and_return(:reserve_stock_ok)
|
113
|
+
@order.should_receive(:settle).once.and_return(:ok)
|
114
|
+
@order.process_status_cd
|
115
|
+
@order.status_key.should == :deliver_preparing
|
116
|
+
# saveされてます。
|
117
|
+
Order.count.should == 1
|
118
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:deliver_preparing)}).should == 1
|
119
|
+
end
|
120
|
+
|
121
|
+
it "reserve_stock succeed keep_process but failed" do
|
122
|
+
@order.should_receive(:reserve_point)
|
123
|
+
@order.should_receive(:reserve_stock).with(:temporary => true).once.and_return(:reserve_stock_ok)
|
124
|
+
@order.should_receive(:reserve_stock).once.and_raise(IOError)
|
125
|
+
@order.should_receive(:settle).once.and_return(:ok)
|
126
|
+
@order.process_status_cd
|
127
|
+
@order.status_key.should == :settlement_error # 決済時の例外はすべて:settlement_errorにします。
|
128
|
+
# saveされてます。
|
129
|
+
Order.count.should == 1
|
130
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:settlement_error)}).should == 1
|
131
|
+
end
|
132
|
+
|
133
|
+
it "reserve_stock fails" do
|
134
|
+
@order.should_receive(:reserve_point)
|
135
|
+
@order.should_receive(:reserve_stock).with(:temporary => true).once.and_return(nil)
|
136
|
+
@order.process_status_cd
|
137
|
+
@order.status_key.should == :stock_error
|
138
|
+
# saveされてます。
|
139
|
+
Order.count.should == 1
|
140
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 0
|
141
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:stock_error)}).should == 1
|
142
|
+
StateFlow::Log.count.should == 0
|
143
|
+
end
|
144
|
+
|
145
|
+
it "StockShortageError raised" do
|
146
|
+
@order.product_name = "Refactoring"
|
147
|
+
@order.should_receive(:reserve_point)
|
148
|
+
@order.should_receive(:reserve_stock).with(:temporary => true).once.and_raise(Order::StockShortageError)
|
149
|
+
Order.transaction do
|
150
|
+
@order.process_status_cd
|
151
|
+
end
|
152
|
+
@order.status_key.should == :stock_error
|
153
|
+
Order.count.should == 1
|
154
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:stock_error)}).should == 1
|
155
|
+
# ステータスはちゃんと変わっているけど、他のデータはロールバックされていなければならない
|
156
|
+
Order.count(:conditions => {:product_name => "Refactoring"}).should == 0
|
157
|
+
Order.count(:conditions => {:product_name => "Beautiful Code"}).should == 1
|
158
|
+
# StateFlow::Log.count.should == 1
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "bank_deposit" do
|
163
|
+
before do
|
164
|
+
@order = Order.new
|
165
|
+
@order.product_name = "Beautiful Code"
|
166
|
+
@order.payment_type = :bank_deposit
|
167
|
+
@order.status_key = :waiting_settling
|
168
|
+
@order.save!
|
169
|
+
Order.count.should == 1
|
170
|
+
end
|
171
|
+
|
172
|
+
it "reserve_stock succeed" do
|
173
|
+
@order.should_receive(:reserve_point)
|
174
|
+
@order.should_receive(:reserve_stock).with(:temporary => true).once.and_return(:reserve_stock_ok)
|
175
|
+
@order.should_receive(:send_mail_thanks)
|
176
|
+
@order.process_status_cd(:save => false)
|
177
|
+
@order.status_key.should == :receiving
|
178
|
+
Order.count.should == 1
|
179
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 1
|
180
|
+
end
|
181
|
+
|
182
|
+
it "reserve_stock fails" do
|
183
|
+
@order.should_receive(:reserve_point)
|
184
|
+
@order.should_receive(:reserve_stock).with(:temporary => true).once.and_return(nil)
|
185
|
+
@order.process_status_cd(:save => false)
|
186
|
+
@order.status_key.should == :stock_error
|
187
|
+
Order.count.should == 1
|
188
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 1
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
describe "foreign_payment" do
|
193
|
+
before do
|
194
|
+
@order = Order.new
|
195
|
+
@order.product_name = "Beautiful Code"
|
196
|
+
@order.payment_type = :foreign_payment
|
197
|
+
@order.status_key = :waiting_settling
|
198
|
+
@order.save!
|
199
|
+
Order.count.should == 1
|
200
|
+
end
|
201
|
+
|
202
|
+
it "reserve_stock succeed" do
|
203
|
+
@order.should_receive(:reserve_point)
|
204
|
+
@order.should_receive(:reserve_stock).with(:temporary => true).once.and_return(:reserve_stock_ok)
|
205
|
+
@order.should_receive(:settle)
|
206
|
+
@order.process_status_cd(:save => false)
|
207
|
+
@order.status_key.should == :online_settling
|
208
|
+
Order.count.should == 1
|
209
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 1
|
210
|
+
end
|
211
|
+
|
212
|
+
it "reserve_stock fails" do
|
213
|
+
@order.should_receive(:reserve_point)
|
214
|
+
@order.should_receive(:reserve_stock).with(:temporary => true).once.and_return(nil)
|
215
|
+
@order.should_receive(:send_mail_stock_shortage)
|
216
|
+
@order.process_status_cd(:save => false)
|
217
|
+
@order.status_key.should == :stock_error
|
218
|
+
Order.count.should == 1
|
219
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:waiting_settling)}).should == 1
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe "from online_settling" do
|
225
|
+
describe "credit_card" do
|
226
|
+
before do
|
227
|
+
@order = Order.new
|
228
|
+
@order.product_name = "Beautiful Code"
|
229
|
+
@order.payment_type = :credit_card
|
230
|
+
@order.status_key = :online_settling
|
231
|
+
@order.save!
|
232
|
+
Order.count.should == 1
|
233
|
+
end
|
234
|
+
|
235
|
+
it "settle succeed" do
|
236
|
+
@order.should_receive(:settle).and_return(:ok)
|
237
|
+
@order.should_receive(:reserve_stock)
|
238
|
+
@order.should_receive(:send_mail_thanks)
|
239
|
+
@order.process_status_cd
|
240
|
+
@order.status_key.should == :deliver_preparing
|
241
|
+
Order.count.should == 1
|
242
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:deliver_preparing)}).should == 1
|
243
|
+
end
|
244
|
+
|
245
|
+
it "settle failed" do
|
246
|
+
@order.should_receive(:settle).and_return(nil)
|
247
|
+
@order.should_receive(:release_stock)
|
248
|
+
@order.should_receive(:delete_point)
|
249
|
+
@order.process_status_cd
|
250
|
+
@order.status_key.should == :settlement_error
|
251
|
+
Order.count.should == 1
|
252
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:settlement_error)}).should == 1
|
253
|
+
end
|
254
|
+
|
255
|
+
it "settle failed by IOError" do
|
256
|
+
@order.should_receive(:settle).and_raise(IOError)
|
257
|
+
@order.should_receive(:release_stock)
|
258
|
+
@order.should_receive(:delete_point)
|
259
|
+
@order.process_status_cd
|
260
|
+
@order.status_key.should == :settlement_error
|
261
|
+
Order.count.should == 1
|
262
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:settlement_error)}).should == 1
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
describe "foreign_payment" do
|
267
|
+
before do
|
268
|
+
@order = Order.new
|
269
|
+
@order.product_name = "Beautiful Code"
|
270
|
+
@order.payment_type = :foreign_payment
|
271
|
+
@order.status_key = :online_settling
|
272
|
+
@order.save!
|
273
|
+
Order.count.should == 1
|
274
|
+
end
|
275
|
+
|
276
|
+
it "event must be called as a method" do
|
277
|
+
@order.should_not_receive(:settle)
|
278
|
+
@order.should_not_receive(:reserve_stock)
|
279
|
+
@order.should_not_receive(:send_mail_thanks)
|
280
|
+
@order.process_status_cd
|
281
|
+
@order.status_key.should == :online_settling
|
282
|
+
Order.count.should == 1
|
283
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:online_settling)}).should == 1
|
284
|
+
end
|
285
|
+
|
286
|
+
it "event must be called as a method" do
|
287
|
+
@order.should_not_receive(:settle)
|
288
|
+
@order.should_not_receive(:reserve_stock)
|
289
|
+
@order.should_not_receive(:send_mail_thanks)
|
290
|
+
@order.settlement_ok # イベント実行
|
291
|
+
@order.status_key.should == :deliver_preparing
|
292
|
+
Order.count.should == 1
|
293
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:deliver_preparing)}).should == 1
|
294
|
+
end
|
295
|
+
|
296
|
+
it "settle failed" do
|
297
|
+
@order.should_receive(:release_stock)
|
298
|
+
@order.should_receive(:delete_point)
|
299
|
+
@order.should_receive(:send_mail_invalid_purchage)
|
300
|
+
@order.settlement_ng # イベント実行
|
301
|
+
@order.status_key.should == :settlement_error
|
302
|
+
Order.count.should == 1
|
303
|
+
Order.count(:conditions => {:status_cd => Order.status_id_by_key(:settlement_error)}).should == 1
|
304
|
+
end
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
describe "structure" do
|
309
|
+
it "states" do
|
310
|
+
flow = Order.state_flow_for(:status_cd)
|
311
|
+
flow.states.length.should == 2
|
312
|
+
end
|
313
|
+
|
314
|
+
it "all_states" do
|
315
|
+
flow = Order.state_flow_for(:status_cd)
|
316
|
+
flow.all_states.values.map{|s| s.name.to_s}.sort.should == [
|
317
|
+
:valid, :auto_cancelable, :waiting_settling, :online_settling,
|
318
|
+
:receiving, :deliver_preparing, :deliver_requested, :delivered,
|
319
|
+
:deliver_notified, :canceling, :cancel_requested, :canceled,
|
320
|
+
:error, :settlement_error, :stock_error, :internal_error, :external_error
|
321
|
+
].map{|s| s.to_s}.sort
|
322
|
+
flow.all_states.length.should == 17
|
323
|
+
end
|
324
|
+
|
325
|
+
it "concrete_states" do
|
326
|
+
flow = Order.state_flow_for(:status_cd)
|
327
|
+
flow.concrete_states.values.map{|s| s.name.to_s}.sort.should == [
|
328
|
+
:waiting_settling, :online_settling,
|
329
|
+
:receiving, :deliver_preparing, :deliver_requested, :delivered,
|
330
|
+
:deliver_notified, :cancel_requested, :canceled,
|
331
|
+
:settlement_error, :stock_error, :internal_error, :external_error
|
332
|
+
].map{|s| s.to_s}.sort
|
333
|
+
flow.concrete_states.length.should == 13
|
334
|
+
end
|
335
|
+
|
336
|
+
it "origin" do
|
337
|
+
flow = Order.state_flow_for(:status_cd)
|
338
|
+
flow.origin.class.should == StateFlow::State
|
339
|
+
flow.origin.name == :waiting_settling
|
340
|
+
end
|
341
|
+
|
342
|
+
it "waiting_settling" do
|
343
|
+
flow = Order.state_flow_for(:status_cd)
|
344
|
+
state = flow.origin
|
345
|
+
state.guards.length.should == 2
|
346
|
+
state.events.length.should == 1
|
347
|
+
state.action.should be_nil
|
348
|
+
g0 = state.guards[0]
|
349
|
+
g0.name.should == :pay_cash_on_delivery?
|
350
|
+
g0.action.method_name.should == :reserve_point
|
351
|
+
g0.action.action.method_name.should == :reserve_stock
|
352
|
+
g0.action.action.events.length.should == 2
|
353
|
+
g0.action.action.events[0].should be_a(StateFlow::ActionEvent)
|
354
|
+
g0.action.action.events[1].should be_a(StateFlow::ActionEvent)
|
355
|
+
g0.action.action.events[0].destination.should == :deliver_preparing
|
356
|
+
g0.action.action.events[1].action.method_name.should == :delete_point
|
357
|
+
g0.action.action.events[1].action.destination.should == :stock_error
|
358
|
+
g1 = state.guards[1]
|
359
|
+
g1.class.should == StateFlow::Guard
|
360
|
+
g1.action.method_name.should == :reserve_point
|
361
|
+
a1 = g1.action.action
|
362
|
+
a1.method_name.should == :reserve_stock
|
363
|
+
a1.method_args.should == [:temporary => true]
|
364
|
+
a1.events[0].should be_a(StateFlow::ActionEvent)
|
365
|
+
a1.events[1].should be_a(StateFlow::ActionEvent)
|
366
|
+
a1.events[0].matcher.should == :reserve_stock_ok
|
367
|
+
g00 = a1.events[0].guards[0]
|
368
|
+
g00.name.should == :bank_deposit?
|
369
|
+
g00.action.method_name.should == :send_mail_thanks
|
370
|
+
g00.action.destination.should ==:receiving
|
371
|
+
g01 = a1.events[0].guards[1]
|
372
|
+
g01.name.should == :credit_card?
|
373
|
+
g01.destination.should ==:online_settling
|
374
|
+
g02 = a1.events[0].guards[2]
|
375
|
+
g02.name.should == :foreign_payment?
|
376
|
+
g02.action.method_name.should == :settle
|
377
|
+
g02.action.destination.should ==:online_settling
|
378
|
+
e1 = a1.events[1]
|
379
|
+
e1.should be_a(StateFlow::ActionEvent)
|
380
|
+
e1.guards[0].action.method_name.should == :delete_point
|
381
|
+
e1.guards[0].action.action.method_name.should == :send_mail_stock_shortage
|
382
|
+
e1.destination.should == :stock_error
|
383
|
+
end
|
384
|
+
|
385
|
+
it ":online_settling" do
|
386
|
+
flow = Order.state_flow_for(:status_cd)
|
387
|
+
state = flow.concrete_states[:online_settling]
|
388
|
+
g0 = state.guards[0]
|
389
|
+
g0.name.should == :credit_card?
|
390
|
+
g0.action.method_name.should == :settle
|
391
|
+
e0 = g0.action.events[0]
|
392
|
+
e0.should be_a(StateFlow::ActionEvent)
|
393
|
+
e0.action.method_name.should == :reserve_stock
|
394
|
+
e0.action.action.method_name.should == :send_mail_thanks
|
395
|
+
e0.action.action.destination.should == :deliver_preparing
|
396
|
+
e1 = g0.action.events[1]
|
397
|
+
e1.should be_a(StateFlow::ActionEvent)
|
398
|
+
e1.action.method_name.should == :release_stock
|
399
|
+
e1.action.action.method_name.should == :delete_point
|
400
|
+
e1.action.action.destination.should == :settlement_error
|
401
|
+
g1 = state.guards[1]
|
402
|
+
g1.name.should == :foreign_payment?
|
403
|
+
e2 = g1.events[0]
|
404
|
+
e2.should be_a(StateFlow::NamedEvent)
|
405
|
+
e2.destination.should == :deliver_preparing
|
406
|
+
e3 = g1.events[1]
|
407
|
+
e3.should be_a(StateFlow::NamedEvent)
|
408
|
+
e3.action.method_name.should == :release_stock
|
409
|
+
e3.action.action.method_name.should == :delete_point
|
410
|
+
e3.action.action.action.method_name.should == :send_mail_invalid_purchage
|
411
|
+
e3.action.action.action.destination.should == :settlement_error
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
end
|
@@ -0,0 +1,171 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
class Order < ActiveRecord::Base
|
3
|
+
|
4
|
+
class StockShortageError < StandardError
|
5
|
+
end
|
6
|
+
|
7
|
+
selectable_attr :status_cd do
|
8
|
+
entry '00', :waiting_settling , '決済前'
|
9
|
+
entry '01', :online_settling , '決済中'
|
10
|
+
entry '02', :receiving , '入金待ち'
|
11
|
+
entry '03', :deliver_preparing, '配送準備中'
|
12
|
+
entry '04', :deliver_requested, '配送指示済'
|
13
|
+
entry '05', :delivered , '配送済'
|
14
|
+
entry '06', :deliver_notified , '配送案内済'
|
15
|
+
entry '07', :cancel_requested , 'キャンセル依頼中'
|
16
|
+
entry '08', :canceled , 'キャンセル完了'
|
17
|
+
entry '19', :settlement_error , '決済NG'
|
18
|
+
entry '10', :stock_error , '在庫不足エラー'
|
19
|
+
entry '11', :internal_error , '内部エラー'
|
20
|
+
entry '12', :external_error , '外部エラー'
|
21
|
+
end
|
22
|
+
|
23
|
+
state_flow(:status_cd) do
|
24
|
+
origin(:waiting_settling)
|
25
|
+
|
26
|
+
group(:valid) do # 正常系
|
27
|
+
# recoverの順番は重要。Exceptionを先に書くと全ての例外は:internal_errorになってしまいます。
|
28
|
+
recover(:'Net::HTTPHeaderSyntaxError').to(:external_error)
|
29
|
+
recover(:'Net::ProtocolError').to(:external_error)
|
30
|
+
recover(:'Exception').to(:internal_error)
|
31
|
+
|
32
|
+
group(:auto_cancelable) do # 自動キャンセル可
|
33
|
+
|
34
|
+
from(:waiting_settling) do
|
35
|
+
# actionは続けて書くこともできます。
|
36
|
+
guard(:pay_cash_on_delivery?).action(:reserve_point).action(:reserve_stock){
|
37
|
+
event(:reserve_stock_ok).to(:deliver_preparing)
|
38
|
+
event_else.action(:delete_point).to(:stock_error)
|
39
|
+
}
|
40
|
+
# guard_elseは他のガード(群)に該当しなかった場合のガードです。
|
41
|
+
guard_else.action(:reserve_point).action(:reserve_stock, :temporary => true){
|
42
|
+
# 一行で書けない処理群はブロックを用いて書くことができます。
|
43
|
+
event(:reserve_stock_ok){
|
44
|
+
guard(:bank_deposit?).action(:send_mail_thanks).to(:receiving)
|
45
|
+
guard(:credit_card?).to(:online_settling)
|
46
|
+
guard(:foreign_payment?).action(:settle).to(:online_settling)
|
47
|
+
}
|
48
|
+
event_else{
|
49
|
+
guard(:foreign_payment?).action(:delete_point).action(:send_mail_stock_shortage)
|
50
|
+
}.to(:stock_error)
|
51
|
+
|
52
|
+
# この上の書き方は以下と同じ意味を持ちます。
|
53
|
+
# event_else{
|
54
|
+
# guard(:foreign_payment?).action(:send_mail_stock_shortage).to(:stock_error)
|
55
|
+
# guard_else.to(:stock_error)
|
56
|
+
# }
|
57
|
+
}
|
58
|
+
|
59
|
+
# sqlite3ではテスト中にロールバックすると、transactionの中なのに
|
60
|
+
# cannot rollback - no transaction is active
|
61
|
+
# とか言われちゃうので、rolling_backをfalseにしてます。
|
62
|
+
recover(StockShortageError, :rolling_back => (ENV['DB'] || 'sqlite3') != 'sqlite3').to(:stock_error)
|
63
|
+
end
|
64
|
+
|
65
|
+
from(:online_settling) do
|
66
|
+
guard(:credit_card?).action(:settle){
|
67
|
+
event(:ok).action(:reserve_stock).action(:send_mail_thanks).to(:deliver_preparing)
|
68
|
+
event_else.action(:release_stock).action(:delete_point).to(:settlement_error)
|
69
|
+
}
|
70
|
+
guard(:foreign_payment?){
|
71
|
+
event(:settlement_ok).to(:deliver_preparing)
|
72
|
+
event(:settlement_ng).action(:release_stock).action(:delete_point).action(:send_mail_invalid_purchage).to(:settlement_error)
|
73
|
+
}
|
74
|
+
recover(Exception).action(:release_stock).action(:delete_point).to(:settlement_error)
|
75
|
+
end
|
76
|
+
|
77
|
+
from :receiving do
|
78
|
+
event(:confirm_receiving) {
|
79
|
+
action :reserve_stock
|
80
|
+
action :send_mail_payment_complete
|
81
|
+
to :deliver_preparing
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
from :deliver_preparing do
|
86
|
+
event(:deliver_request).
|
87
|
+
action(:reduce_stock).
|
88
|
+
action(:send_mail_deliver_request).
|
89
|
+
to(:deliver_requested)
|
90
|
+
end
|
91
|
+
|
92
|
+
event(:cancel_request) {
|
93
|
+
action(:send_mail_cancel_requested)
|
94
|
+
}.to(:cancel_requested)
|
95
|
+
|
96
|
+
event(:cancel) {
|
97
|
+
action(:release_reserve)
|
98
|
+
action(:delete_point)
|
99
|
+
action(:send_mail_cancel_complete)
|
100
|
+
}.to(:canceled)
|
101
|
+
end
|
102
|
+
|
103
|
+
from(:deliver_requested) do
|
104
|
+
event(:deliver).action(:send_mail_shipping_for_shop).action(:send_mail_shipping_for_customer).to(:delivered)
|
105
|
+
event(:cancel_request).action(:send_mail_cancel_requested).to(:cancel_requested)
|
106
|
+
end
|
107
|
+
|
108
|
+
from(:delivered) do
|
109
|
+
event(:deliver_notify).action(:send_mail_deliver_notification).to(:deliver_notified)
|
110
|
+
end
|
111
|
+
|
112
|
+
termination(:deliver_notified) # 終端
|
113
|
+
|
114
|
+
state_group(:canceling) do # 正常なキャンセル系
|
115
|
+
from :cancel_requested do
|
116
|
+
event(:accept_cancel) {
|
117
|
+
action(:release_reserve)
|
118
|
+
action(:delete_point)
|
119
|
+
action(:send_mail_cancel_complete)
|
120
|
+
}.to(:canceled)
|
121
|
+
end
|
122
|
+
|
123
|
+
termination(:canceled) # 終端
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
group(:error) do # 異常系
|
128
|
+
from(:settlement_error) do
|
129
|
+
action(:send_mail_settlement_error)
|
130
|
+
# to(:settlement_error) # 遷移しない
|
131
|
+
end
|
132
|
+
|
133
|
+
state(:stock_error)
|
134
|
+
state(:internal_error)
|
135
|
+
state(:external_error)
|
136
|
+
|
137
|
+
termination # 終端
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
validates_presence_of :product_name
|
142
|
+
|
143
|
+
attr_accessor :payment_type
|
144
|
+
def pay_cash_on_delivery?; payment_type == :cash_on_delivery; end
|
145
|
+
def bank_deposit? ; payment_type == :bank_deposit ; end
|
146
|
+
def credit_card? ; payment_type == :credit_card ; end
|
147
|
+
def foreign_payment? ; payment_type == :foreign_payment ; end
|
148
|
+
|
149
|
+
attr_accessor :reserve_stock_result
|
150
|
+
|
151
|
+
def reserve_point; end
|
152
|
+
def delete_point; end
|
153
|
+
|
154
|
+
def reserve_stock(*args); end
|
155
|
+
def release_stock; end
|
156
|
+
def reduce_stock; end
|
157
|
+
|
158
|
+
def settle; end
|
159
|
+
|
160
|
+
def send_mail_thanks; end
|
161
|
+
def send_mail_stock_shortage; end
|
162
|
+
def send_mail_invalid_purchage; end
|
163
|
+
def send_mail_deliver_request; end
|
164
|
+
def send_mail_cancel_requested; end
|
165
|
+
def send_mail_cancel_complete; end
|
166
|
+
def send_mail_shipping_for_shop; end
|
167
|
+
def send_mail_shipping_for_customer; end
|
168
|
+
def send_mail_deliver_notification; end
|
169
|
+
def send_mail_settlement_error; end
|
170
|
+
|
171
|
+
end
|
data/spec/schema.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
ActiveRecord::Schema.define(:version => 0) do
|
2
2
|
# Users are created and updated by other Users
|
3
|
-
create_table :
|
4
|
-
t.column :
|
3
|
+
create_table :orders, :force => true do |t|
|
4
|
+
t.column :product_name, :string
|
5
5
|
t.column :status_cd , :string, :limit => 2, :null => false, :default => '00'
|
6
6
|
t.column :created_on, :datetime
|
7
7
|
t.column :updated_at, :datetime
|
@@ -10,15 +10,10 @@ ActiveRecord::Schema.define(:version => 0) do
|
|
10
10
|
create_table :state_flow_logs, :force => true do |t|
|
11
11
|
t.string :target_type
|
12
12
|
t.integer :target_id
|
13
|
-
t.string :origin_state
|
14
|
-
t.string :origin_state_key
|
15
|
-
t.string :dest_state
|
16
|
-
t.string :dest_state_key
|
17
13
|
t.string :level, :limit => 5, :null => false, :default => 'debug'
|
18
14
|
t.text :descriptions
|
19
15
|
t.datetime :created_on
|
20
16
|
t.datetime :updated_at
|
21
17
|
end
|
22
|
-
|
23
18
|
|
24
19
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -33,9 +33,7 @@ unless defined?(RAILS_ENV)
|
|
33
33
|
ActiveRecord::Base.logger.formatter = FormatterWithThread.new
|
34
34
|
# ActionController::Base.logger = ActiveRecord::Base.logger
|
35
35
|
|
36
|
-
ActiveRecord::Base.establish_connection(
|
37
|
-
{:pool => 10}.update(config[ENV['DB'] || 'sqlite3'])
|
38
|
-
)
|
36
|
+
ActiveRecord::Base.establish_connection(config[ENV['DB'] || 'sqlite3'])
|
39
37
|
|
40
38
|
load(File.join(File.dirname(__FILE__), 'schema.rb'))
|
41
39
|
|