state_machines-activemodel 0.0.1 → 0.0.2

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.
@@ -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