state_machines-activemodel 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,33 +0,0 @@
1
- module StateMachines
2
- module Integrations #:nodoc:
3
- module ActiveModel
4
- # Adds support for invoking callbacks on ActiveModel observers with more
5
- # than one argument (e.g. the record *and* the state transition). By
6
- # default, ActiveModel only supports passing the record into the
7
- # callbacks.
8
- #
9
- # For example:
10
- #
11
- # class VehicleObserver < ActiveModel::Observer
12
- # # The default behavior: only pass in the record
13
- # def after_save(vehicle)
14
- # end
15
- #
16
- # # Custom behavior: allow the transition to be passed in as well
17
- # def after_transition(vehicle, transition)
18
- # Audit.log(vehicle, transition)
19
- # end
20
- # end
21
- module Observer
22
- def update_with_transition(observer_update)
23
- method = observer_update.method
24
- send(method, *observer_update.args) if respond_to?(method)
25
- end
26
- end
27
- end
28
- end
29
- end
30
-
31
- ActiveModel::Observer.class_eval do
32
- include StateMachines::Integrations::ActiveModel::Observer
33
- end
@@ -1,42 +0,0 @@
1
- module StateMachines
2
- module Integrations #:nodoc:
3
- module ActiveModel
4
- # Represents the encapsulation of all of the details to be included in an
5
- # update to state machine observers. This allows multiple arguments to
6
- # get passed to an observer method (instead of just a single +object+)
7
- # while still respecting the way in which ActiveModel checks for the
8
- # object's list of observers.
9
- class ObserverUpdate
10
- # The method to invoke on the observer
11
- attr_reader :method
12
-
13
- # The object being transitioned
14
- attr_reader :object
15
-
16
- # The transition being run
17
- attr_reader :transition
18
-
19
- def initialize(method, object, transition) #:nodoc:
20
- @method, @object, @transition = method, object, transition
21
- end
22
-
23
- # The arguments to pass into the method
24
- def args
25
- [object, transition]
26
- end
27
-
28
- # The class of the object being transitioned. Normally the object
29
- # getting passed into observer methods is the actual instance of the
30
- # ActiveModel class. ActiveModel uses that instance's class to check
31
- # for enabled / disabled observers.
32
- #
33
- # Since state_machine is passing an ObserverUpdate instance into observer
34
- # methods, +class+ needs to be overridden so that ActiveModel can still
35
- # get access to the enabled / disabled observers.
36
- def class
37
- object.class
38
- end
39
- end
40
- end
41
- end
42
- end
@@ -1,519 +0,0 @@
1
- if ActiveModel::VERSION::MAJOR >= 4
2
- begin
3
- require 'rails/observers/active_model'
4
- # require 'active_model/mass_assignment_security'
5
- rescue LoadError
6
- end
7
- else
8
- require 'active_model/observing'
9
- end
10
- require 'active_support/all'
11
-
12
- if defined?(ActiveModel::Observing)
13
-
14
- context 'ObserverUpdate' do
15
- before(:each) do
16
- @model = new_model { include ActiveModel::Observing }
17
- @machine = StateMachines::Machine.new(@model)
18
- @machine.state :parked, :idling
19
- @machine.event :ignite
20
-
21
- @record = @model.new(:state => 'parked')
22
- @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
23
-
24
- @observer_update = StateMachines::Integrations::ActiveModel::ObserverUpdate.new(:before_transition, @record, @transition)
25
- end
26
-
27
- it 'should_have_method' do
28
- assert_equal :before_transition, @observer_update.method
29
- end
30
-
31
- it 'should_have_object' do
32
- assert_equal @record, @observer_update.object
33
- end
34
-
35
- it 'should_have_transition' do
36
- assert_equal @transition, @observer_update.transition
37
- end
38
-
39
- it 'should_include_object_and_transition_in_args' do
40
- assert_equal [@record, @transition], @observer_update.args
41
- end
42
-
43
- it 'should_use_record_class_as_class' do
44
- assert_equal @model, @observer_update.class
45
- end
46
- end
47
-
48
- context 'WithObservers' do
49
- before(:each) do
50
- @model = new_model { include ActiveModel::Observing }
51
- @machine = StateMachines::Machine.new(@model)
52
- @machine.state :parked, :idling
53
- @machine.event :ignite
54
- @record = @model.new(:state => 'parked')
55
- @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
56
- end
57
-
58
- it 'should_call_all_transition_callback_permutations' do
59
- callbacks = [
60
- :before_ignite_from_parked_to_idling,
61
- :before_ignite_from_parked,
62
- :before_ignite_to_idling,
63
- :before_ignite,
64
- :before_transition_state_from_parked_to_idling,
65
- :before_transition_state_from_parked,
66
- :before_transition_state_to_idling,
67
- :before_transition_state,
68
- :before_transition
69
- ]
70
-
71
- observer = new_observer(@model) do
72
- callbacks.each do |callback|
73
- define_method(callback) do |*args|
74
- notifications << callback
75
- end
76
- end
77
- end
78
-
79
- instance = observer.instance
80
-
81
- @transition.perform
82
- assert_equal callbacks, instance.notifications
83
- end
84
-
85
- it 'should_call_no_transition_callbacks_when_observers_disabled' do
86
- callbacks = [
87
- :before_ignite,
88
- :before_transition
89
- ]
90
-
91
- observer = new_observer(@model) do
92
- callbacks.each do |callback|
93
- define_method(callback) do |*args|
94
- notifications << callback
95
- end
96
- end
97
- end
98
-
99
- instance = observer.instance
100
-
101
- @model.observers.disable(observer) do
102
- @transition.perform
103
- end
104
-
105
- assert_equal [], instance.notifications
106
- end
107
-
108
- it 'should_pass_record_and_transition_to_before_callbacks' do
109
- observer = new_observer(@model) do
110
- def before_transition(*args)
111
- notifications << args
112
- end
113
- end
114
- instance = observer.instance
115
-
116
- @transition.perform
117
- assert_equal [[@record, @transition]], instance.notifications
118
- end
119
-
120
- it 'should_pass_record_and_transition_to_after_callbacks' do
121
- observer = new_observer(@model) do
122
- def after_transition(*args)
123
- notifications << args
124
- end
125
- end
126
- instance = observer.instance
127
-
128
- @transition.perform
129
- assert_equal [[@record, @transition]], instance.notifications
130
- end
131
-
132
- it 'should_call_methods_outside_the_context_of_the_record' do
133
- observer = new_observer(@model) do
134
- def before_ignite(*args)
135
- notifications << self
136
- end
137
- end
138
- instance = observer.instance
139
-
140
- @transition.perform
141
- assert_equal [instance], instance.notifications
142
- end
143
-
144
- it 'should_support_nil_from_states' do
145
- callbacks = [
146
- :before_ignite_from_nil_to_idling,
147
- :before_ignite_from_nil,
148
- :before_transition_state_from_nil_to_idling,
149
- :before_transition_state_from_nil
150
- ]
151
-
152
- observer = new_observer(@model) do
153
- callbacks.each do |callback|
154
- define_method(callback) do |*args|
155
- notifications << callback
156
- end
157
- end
158
- end
159
-
160
- instance = observer.instance
161
-
162
- transition = StateMachines::Transition.new(@record, @machine, :ignite, nil, :idling)
163
- transition.perform
164
- assert_equal callbacks, instance.notifications
165
- end
166
-
167
- it 'should_support_nil_to_states' do
168
- callbacks = [
169
- :before_ignite_from_parked_to_nil,
170
- :before_ignite_to_nil,
171
- :before_transition_state_from_parked_to_nil,
172
- :before_transition_state_to_nil
173
- ]
174
-
175
- observer = new_observer(@model) do
176
- callbacks.each do |callback|
177
- define_method(callback) do |*args|
178
- notifications << callback
179
- end
180
- end
181
- end
182
-
183
- instance = observer.instance
184
-
185
- transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, nil)
186
- transition.perform
187
- assert_equal callbacks, instance.notifications
188
- end
189
- end
190
-
191
- context 'WithNamespacedObservers' do
192
- before(:each) do
193
- @model = new_model { include ActiveModel::Observing }
194
- @machine = StateMachines::Machine.new(@model, :state, :namespace => 'alarm')
195
- @machine.state :active, :off
196
- @machine.event :enable
197
- @record = @model.new(:state => 'off')
198
- @transition = StateMachines::Transition.new(@record, @machine, :enable, :off, :active)
199
- end
200
-
201
- it 'should_call_namespaced_before_event_method' do
202
- observer = new_observer(@model) do
203
- def before_enable_alarm(*args)
204
- notifications << args
205
- end
206
- end
207
- instance = observer.instance
208
-
209
- @transition.perform
210
- assert_equal [[@record, @transition]], instance.notifications
211
- end
212
-
213
- it 'should_call_namespaced_after_event_method' do
214
- observer = new_observer(@model) do
215
- def after_enable_alarm(*args)
216
- notifications << args
217
- end
218
- end
219
- instance = observer.instance
220
-
221
- @transition.perform
222
- assert_equal [[@record, @transition]], instance.notifications
223
- end
224
- end
225
-
226
- context 'WithFailureCallbacks' do
227
- before(:each) do
228
- @model = new_model { include ActiveModel::Observing }
229
- @machine = StateMachines::Machine.new(@model)
230
- @machine.state :parked, :idling
231
- @machine.event :ignite
232
- @record = @model.new(:state => 'parked')
233
- @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
234
-
235
- @notifications = []
236
-
237
- # Create callbacks
238
- @machine.before_transition { false }
239
- @machine.after_failure { @notifications << :callback_after_failure }
240
-
241
- # Create observer callbacks
242
- observer = new_observer(@model) do
243
- def after_failure_to_ignite(*args)
244
- notifications << :observer_after_failure_ignite
245
- end
246
-
247
- def after_failure_to_transition(*args)
248
- notifications << :observer_after_failure_transition
249
- end
250
- end
251
- instance = observer.instance
252
- instance.notifications = @notifications
253
-
254
- @transition.perform
255
- end
256
-
257
- it 'should_invoke_callbacks_in_specific_order' do
258
- expected = [
259
- :callback_after_failure,
260
- :observer_after_failure_ignite,
261
- :observer_after_failure_transition
262
- ]
263
-
264
- assert_equal expected, @notifications
265
- end
266
- end
267
-
268
- context 'WithMixedCallbacks' do
269
- before(:each) do
270
- @model = new_model { include ActiveModel::Observing }
271
- @machine = StateMachines::Machine.new(@model)
272
- @machine.state :parked, :idling
273
- @machine.event :ignite
274
- @record = @model.new(:state => 'parked')
275
- @transition = StateMachines::Transition.new(@record, @machine, :ignite, :parked, :idling)
276
-
277
- @notifications = []
278
-
279
- # Create callbacks
280
- @machine.before_transition { @notifications << :callback_before_transition }
281
- @machine.after_transition { @notifications << :callback_after_transition }
282
- @machine.around_transition { |block| @notifications << :callback_around_before_transition; block.call; @notifications << :callback_around_after_transition }
283
-
284
- # Create observer callbacks
285
- observer = new_observer(@model) do
286
- def before_ignite(*args)
287
- notifications << :observer_before_ignite
288
- end
289
-
290
- def before_transition(*args)
291
- notifications << :observer_before_transition
292
- end
293
-
294
- def after_ignite(*args)
295
- notifications << :observer_after_ignite
296
- end
297
-
298
- def after_transition(*args)
299
- notifications << :observer_after_transition
300
- end
301
- end
302
- instance = observer.instance
303
- instance.notifications = @notifications
304
-
305
- @transition.perform
306
- end
307
-
308
- it 'should_invoke_callbacks_in_specific_order' do
309
- expected = [
310
- :callback_before_transition,
311
- :callback_around_before_transition,
312
- :observer_before_ignite,
313
- :observer_before_transition,
314
- :callback_around_after_transition,
315
- :callback_after_transition,
316
- :observer_after_ignite,
317
- :observer_after_transition
318
- ]
319
-
320
- assert_equal expected, @notifications
321
- end
322
- end
323
-
324
- context 'WithInternationalization' do
325
- before(:each) do
326
- I18n.backend = I18n::Backend::Simple.new
327
-
328
- # Initialize the backend
329
- I18n.backend.translate(:en, 'activemodel.errors.messages.invalid_transition', :event => 'ignite', :value => 'idling')
330
-
331
- @model = new_model { include ActiveModel::Validations }
332
- end
333
-
334
- it 'should_use_defaults' do
335
- I18n.backend.store_translations(:en, {
336
- :activemodel => {:errors => {:messages => {:invalid_transition => 'cannot %{event}'}}}
337
- })
338
-
339
- machine = StateMachines::Machine.new(@model, :action => :save)
340
- machine.state :parked, :idling
341
- machine.event :ignite
342
-
343
- record = @model.new(:state => 'idling')
344
-
345
- machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
346
- assert_equal ['State cannot ignite'], record.errors.full_messages
347
- end
348
-
349
- it 'should_allow_customized_error_key' do
350
- I18n.backend.store_translations(:en, {
351
- :activemodel => {:errors => {:messages => {:bad_transition => 'cannot %{event}'}}}
352
- })
353
-
354
- machine = StateMachines::Machine.new(@model, :action => :save, :messages => {:invalid_transition => :bad_transition})
355
- machine.state :parked, :idling
356
-
357
- record = @model.new
358
- record.state = 'idling'
359
-
360
- machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
361
- assert_equal ['State cannot ignite'], record.errors.full_messages
362
- end
363
-
364
- it 'should_allow_customized_error_string' do
365
- machine = StateMachines::Machine.new(@model, :action => :save, :messages => {:invalid_transition => 'cannot %{event}'})
366
- machine.state :parked, :idling
367
-
368
- record = @model.new(:state => 'idling')
369
-
370
- machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
371
- assert_equal ['State cannot ignite'], record.errors.full_messages
372
- end
373
-
374
- it 'should_allow_customized_state_key_scoped_to_class_and_machine' do
375
- I18n.backend.store_translations(:en, {
376
- :activemodel => {:state_machines => {:'bar/foo' => {:state => {:states => {:parked => 'shutdown'}}}}}
377
- })
378
-
379
- machine = StateMachines::Machine.new(@model)
380
- machine.state :parked
381
-
382
- assert_equal 'shutdown', machine.state(:parked).human_name
383
- end
384
-
385
- it 'should_allow_customized_state_key_scoped_to_class' do
386
- I18n.backend.store_translations(:en, {
387
- :activemodel => {:state_machines => {:'bar/foo' => {:states => {:parked => 'shutdown'}}}}
388
- })
389
-
390
- machine = StateMachines::Machine.new(@model)
391
- machine.state :parked
392
-
393
- assert_equal 'shutdown', machine.state(:parked).human_name
394
- end
395
-
396
- it 'should_allow_customized_state_key_scoped_to_machine' do
397
- I18n.backend.store_translations(:en, {
398
- :activemodel => {:state_machines => {:state => {:states => {:parked => 'shutdown'}}}}
399
- })
400
-
401
- machine = StateMachines::Machine.new(@model)
402
- machine.state :parked
403
-
404
- assert_equal 'shutdown', machine.state(:parked).human_name
405
- end
406
-
407
- it 'should_allow_customized_state_key_unscoped' do
408
- I18n.backend.store_translations(:en, {
409
- :activemodel => {:state_machines => {:states => {:parked => 'shutdown'}}}
410
- })
411
-
412
- machine = StateMachines::Machine.new(@model)
413
- machine.state :parked
414
-
415
- assert_equal 'shutdown', machine.state(:parked).human_name
416
- end
417
-
418
- it 'should_support_nil_state_key' do
419
- I18n.backend.store_translations(:en, {
420
- :activemodel => {:state_machines => {:states => {:nil => 'empty'}}}
421
- })
422
-
423
- machine = StateMachines::Machine.new(@model)
424
-
425
- assert_equal 'empty', machine.state(nil).human_name
426
- end
427
-
428
- it 'should_allow_customized_event_key_scoped_to_class_and_machine' do
429
- I18n.backend.store_translations(:en, {
430
- :activemodel => {:state_machines => {:'bar/foo' => {:state => {:events => {:park => 'stop'}}}}}
431
- })
432
-
433
- machine = StateMachines::Machine.new(@model)
434
- machine.event :park
435
-
436
- assert_equal 'stop', machine.event(:park).human_name
437
- end
438
-
439
- it 'should_allow_customized_event_key_scoped_to_class' do
440
- I18n.backend.store_translations(:en, {
441
- :activemodel => {:state_machines => {:'bar/foo' => {:events => {:park => 'stop'}}}}
442
- })
443
-
444
- machine = StateMachines::Machine.new(@model)
445
- machine.event :park
446
-
447
- assert_equal 'stop', machine.event(:park).human_name
448
- end
449
-
450
- it 'should_allow_customized_event_key_scoped_to_machine' do
451
- I18n.backend.store_translations(:en, {
452
- :activemodel => {:state_machines => {:state => {:events => {:park => 'stop'}}}}
453
- })
454
-
455
- machine = StateMachines::Machine.new(@model)
456
- machine.event :park
457
-
458
- assert_equal 'stop', machine.event(:park).human_name
459
- end
460
-
461
- it 'should_allow_customized_event_key_unscoped' do
462
- I18n.backend.store_translations(:en, {
463
- :activemodel => {:state_machines => {:events => {:park => 'stop'}}}
464
- })
465
-
466
- machine = StateMachines::Machine.new(@model)
467
- machine.event :park
468
-
469
- assert_equal 'stop', machine.event(:park).human_name
470
- end
471
-
472
- it 'should_only_add_locale_once_in_load_path' do
473
- assert_equal 1, I18n.load_path.select { |path| path =~ %r{active_model/locale\.rb$} }.length
474
-
475
- # Create another ActiveModel model that will triger the i18n feature
476
- new_model
477
-
478
- assert_equal 1, I18n.load_path.select { |path| path =~ %r{active_model/locale\.rb$} }.length
479
- end
480
-
481
- context 'loading locale' do
482
- before(:each) do
483
- @original_load_path = I18n.load_path
484
- I18n.backend = I18n::Backend::Simple.new
485
- end
486
- it 'should_add_locale_to_beginning_of_load_path' do
487
- app_locale = File.dirname(__FILE__) + '/support/en.yml'
488
- default_locale = File.dirname(__FILE__) + '/../lib/state_machines/integrations/active_model/locale.rb'
489
- I18n.load_path = [app_locale]
490
-
491
- StateMachines::Machine.new(@model)
492
-
493
- assert_equal [default_locale, app_locale].map { |path| File.expand_path(path) }, I18n.load_path.map { |path| File.expand_path(path) }
494
-
495
- end
496
-
497
- it 'should_prefer_other_locales_first' do
498
-
499
- I18n.load_path = [File.dirname(__FILE__) + '/support/en.yml']
500
-
501
- machine = StateMachines::Machine.new(@model)
502
- machine.state :parked, :idling
503
- machine.event :ignite
504
-
505
- record = @model.new(:state => 'idling')
506
-
507
- machine.invalidate(record, :state, :invalid_transition, [[:event, 'ignite']])
508
- assert_equal ['State cannot ignite'], record.errors.full_messages
509
-
510
-
511
- end
512
-
513
- after(:each) do
514
- I18n.load_path = @original_load_path
515
- end
516
- end
517
- end
518
-
519
- end