enum_state_machine 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +12 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. metadata +83 -130
  6. data/.rvmrc +0 -1
  7. data/enum_state_machine.gemspec +0 -25
  8. data/lib/enum_state_machine.rb +0 -9
  9. data/lib/enum_state_machine/assertions.rb +0 -36
  10. data/lib/enum_state_machine/branch.rb +0 -225
  11. data/lib/enum_state_machine/callback.rb +0 -232
  12. data/lib/enum_state_machine/core.rb +0 -12
  13. data/lib/enum_state_machine/core_ext.rb +0 -2
  14. data/lib/enum_state_machine/core_ext/class/state_machine.rb +0 -5
  15. data/lib/enum_state_machine/error.rb +0 -13
  16. data/lib/enum_state_machine/eval_helpers.rb +0 -87
  17. data/lib/enum_state_machine/event.rb +0 -257
  18. data/lib/enum_state_machine/event_collection.rb +0 -141
  19. data/lib/enum_state_machine/extensions.rb +0 -149
  20. data/lib/enum_state_machine/graph.rb +0 -92
  21. data/lib/enum_state_machine/helper_module.rb +0 -17
  22. data/lib/enum_state_machine/initializers.rb +0 -4
  23. data/lib/enum_state_machine/initializers/rails.rb +0 -22
  24. data/lib/enum_state_machine/integrations.rb +0 -97
  25. data/lib/enum_state_machine/integrations/active_model.rb +0 -585
  26. data/lib/enum_state_machine/integrations/active_model/locale.rb +0 -11
  27. data/lib/enum_state_machine/integrations/active_model/observer.rb +0 -33
  28. data/lib/enum_state_machine/integrations/active_model/observer_update.rb +0 -42
  29. data/lib/enum_state_machine/integrations/active_model/versions.rb +0 -31
  30. data/lib/enum_state_machine/integrations/active_record.rb +0 -548
  31. data/lib/enum_state_machine/integrations/active_record/locale.rb +0 -20
  32. data/lib/enum_state_machine/integrations/active_record/versions.rb +0 -123
  33. data/lib/enum_state_machine/integrations/base.rb +0 -100
  34. data/lib/enum_state_machine/machine.rb +0 -2292
  35. data/lib/enum_state_machine/machine_collection.rb +0 -86
  36. data/lib/enum_state_machine/macro_methods.rb +0 -518
  37. data/lib/enum_state_machine/matcher.rb +0 -123
  38. data/lib/enum_state_machine/matcher_helpers.rb +0 -54
  39. data/lib/enum_state_machine/node_collection.rb +0 -222
  40. data/lib/enum_state_machine/path.rb +0 -120
  41. data/lib/enum_state_machine/path_collection.rb +0 -90
  42. data/lib/enum_state_machine/state.rb +0 -297
  43. data/lib/enum_state_machine/state_collection.rb +0 -112
  44. data/lib/enum_state_machine/state_context.rb +0 -138
  45. data/lib/enum_state_machine/state_enum.rb +0 -23
  46. data/lib/enum_state_machine/transition.rb +0 -470
  47. data/lib/enum_state_machine/transition_collection.rb +0 -245
  48. data/lib/enum_state_machine/version.rb +0 -3
  49. data/lib/enum_state_machine/yard.rb +0 -8
  50. data/lib/enum_state_machine/yard/handlers.rb +0 -12
  51. data/lib/enum_state_machine/yard/handlers/base.rb +0 -32
  52. data/lib/enum_state_machine/yard/handlers/event.rb +0 -25
  53. data/lib/enum_state_machine/yard/handlers/machine.rb +0 -344
  54. data/lib/enum_state_machine/yard/handlers/state.rb +0 -25
  55. data/lib/enum_state_machine/yard/handlers/transition.rb +0 -47
  56. data/lib/enum_state_machine/yard/templates.rb +0 -3
  57. data/lib/enum_state_machine/yard/templates/default/class/html/setup.rb +0 -30
  58. data/lib/enum_state_machine/yard/templates/default/class/html/state_machines.erb +0 -12
  59. data/lib/tasks/enum_state_machine.rake +0 -1
  60. data/lib/tasks/enum_state_machine.rb +0 -24
  61. data/lib/yard-enum_state_machine.rb +0 -2
  62. data/test/functional/state_machine_test.rb +0 -1066
  63. data/test/unit/integrations/active_model_test.rb +0 -1245
  64. data/test/unit/integrations/active_record_test.rb +0 -2551
  65. data/test/unit/integrations/base_test.rb +0 -104
  66. data/test/unit/integrations_test.rb +0 -71
  67. data/test/unit/invalid_event_test.rb +0 -20
  68. data/test/unit/invalid_parallel_transition_test.rb +0 -18
  69. data/test/unit/invalid_transition_test.rb +0 -115
  70. data/test/unit/machine_collection_test.rb +0 -603
  71. data/test/unit/machine_test.rb +0 -3395
  72. data/test/unit/state_machine_test.rb +0 -31
@@ -1,585 +0,0 @@
1
- module EnumStateMachine
2
- module Integrations #:nodoc:
3
- # Adds support for integrating state machines with ActiveModel classes.
4
- #
5
- # == Examples
6
- #
7
- # If using ActiveModel directly within your class, then any one of the
8
- # following features need to be included in order for the integration to be
9
- # detected:
10
- # * ActiveModel::Observing
11
- # * ActiveModel::Validations
12
- #
13
- # Below is an example of a simple state machine defined within an
14
- # ActiveModel class:
15
- #
16
- # class Vehicle
17
- # include ActiveModel::Observing
18
- # include ActiveModel::Validations
19
- #
20
- # attr_accessor :state
21
- # define_attribute_methods [:state]
22
- #
23
- # state_machine :initial => :parked do
24
- # event :ignite do
25
- # transition :parked => :idling
26
- # end
27
- # end
28
- # end
29
- #
30
- # The examples in the sections below will use the above class as a
31
- # reference.
32
- #
33
- # == Actions
34
- #
35
- # By default, no action will be invoked when a state is transitioned. This
36
- # means that if you want to save changes when transitioning, you must
37
- # define the action yourself like so:
38
- #
39
- # class Vehicle
40
- # include ActiveModel::Validations
41
- # attr_accessor :state
42
- #
43
- # state_machine :action => :save do
44
- # ...
45
- # end
46
- #
47
- # def save
48
- # # Save changes
49
- # end
50
- # end
51
- #
52
- # == Validations
53
- #
54
- # As mentioned in EnumStateMachine::Machine#state, you can define behaviors,
55
- # like validations, that only execute for certain states. One *important*
56
- # caveat here is that, due to a constraint in ActiveModel's validation
57
- # framework, custom validators will not work as expected when defined to run
58
- # in multiple states. For example:
59
- #
60
- # class Vehicle
61
- # include ActiveModel::Validations
62
- #
63
- # state_machine do
64
- # ...
65
- # state :first_gear, :second_gear do
66
- # validate :speed_is_legal
67
- # end
68
- # end
69
- # end
70
- #
71
- # In this case, the <tt>:speed_is_legal</tt> validation will only get run
72
- # for the <tt>:second_gear</tt> state. To avoid this, you can define your
73
- # custom validation like so:
74
- #
75
- # class Vehicle
76
- # include ActiveModel::Validations
77
- #
78
- # state_machine do
79
- # ...
80
- # state :first_gear, :second_gear do
81
- # validate {|vehicle| vehicle.speed_is_legal}
82
- # end
83
- # end
84
- # end
85
- #
86
- # == Validation errors
87
- #
88
- # In order to hook in validation support for your model, the
89
- # ActiveModel::Validations feature must be included. If this is included
90
- # and an event fails to successfully fire because there are no matching
91
- # transitions for the object, a validation error is added to the object's
92
- # state attribute to help in determining why it failed.
93
- #
94
- # For example,
95
- #
96
- # vehicle = Vehicle.new
97
- # vehicle.ignite # => false
98
- # vehicle.errors.full_messages # => ["State cannot transition via \"ignite\""]
99
- #
100
- # In addition, if you're using the <tt>ignite!</tt> version of the event,
101
- # then the failure reason (such as the current validation errors) will be
102
- # included in the exception that gets raised when the event fails. For
103
- # example, assuming there's a validation on a field called +name+ on the class:
104
- #
105
- # vehicle = Vehicle.new
106
- # vehicle.ignite! # => EnumStateMachine::InvalidTransition: Cannot transition state via :ignite from :parked (Reason(s): Name cannot be blank)
107
- #
108
- # === Security implications
109
- #
110
- # Beware that public event attributes mean that events can be fired
111
- # whenever mass-assignment is being used. If you want to prevent malicious
112
- # users from tampering with events through URLs / forms, the attribute
113
- # should be protected like so:
114
- #
115
- # class Vehicle
116
- # include ActiveModel::MassAssignmentSecurity
117
- # attr_accessor :state
118
- #
119
- # attr_protected :state_event
120
- # # attr_accessible ... # Alternative technique
121
- #
122
- # state_machine do
123
- # ...
124
- # end
125
- # end
126
- #
127
- # If you want to only have *some* events be able to fire via mass-assignment,
128
- # you can build two state machines (one public and one protected) like so:
129
- #
130
- # class Vehicle
131
- # include ActiveModel::MassAssignmentSecurity
132
- # attr_accessor :state
133
- #
134
- # attr_protected :state_event # Prevent access to events in the first machine
135
- #
136
- # state_machine do
137
- # # Define private events here
138
- # end
139
- #
140
- # # Public machine targets the same state as the private machine
141
- # state_machine :public_state, :attribute => :state do
142
- # # Define public events here
143
- # end
144
- # end
145
- #
146
- # == Callbacks
147
- #
148
- # All before/after transition callbacks defined for ActiveModel models
149
- # behave in the same way that other ActiveSupport callbacks behave. The
150
- # object involved in the transition is passed in as an argument.
151
- #
152
- # For example,
153
- #
154
- # class Vehicle
155
- # include ActiveModel::Validations
156
- # attr_accessor :state
157
- #
158
- # state_machine :initial => :parked do
159
- # before_transition any => :idling do |vehicle|
160
- # vehicle.put_on_seatbelt
161
- # end
162
- #
163
- # before_transition do |vehicle, transition|
164
- # # log message
165
- # end
166
- #
167
- # event :ignite do
168
- # transition :parked => :idling
169
- # end
170
- # end
171
- #
172
- # def put_on_seatbelt
173
- # ...
174
- # end
175
- # end
176
- #
177
- # Note, also, that the transition can be accessed by simply defining
178
- # additional arguments in the callback block.
179
- #
180
- # == Observers
181
- #
182
- # In order to hook in observer support for your application, the
183
- # ActiveModel::Observing feature must be included. Because of the way
184
- # ActiveModel observers are designed, there is less flexibility around the
185
- # specific transitions that can be hooked in. However, a large number of
186
- # hooks *are* supported. For example, if a transition for a object's
187
- # +state+ attribute changes the state from +parked+ to +idling+ via the
188
- # +ignite+ event, the following observer methods are supported:
189
- # * before/after/after_failure_to-_ignite_from_parked_to_idling
190
- # * before/after/after_failure_to-_ignite_from_parked
191
- # * before/after/after_failure_to-_ignite_to_idling
192
- # * before/after/after_failure_to-_ignite
193
- # * before/after/after_failure_to-_transition_state_from_parked_to_idling
194
- # * before/after/after_failure_to-_transition_state_from_parked
195
- # * before/after/after_failure_to-_transition_state_to_idling
196
- # * before/after/after_failure_to-_transition_state
197
- # * before/after/after_failure_to-_transition
198
- #
199
- # The following class shows an example of some of these hooks:
200
- #
201
- # class VehicleObserver < ActiveModel::Observer
202
- # # Callback for :ignite event *before* the transition is performed
203
- # def before_ignite(vehicle, transition)
204
- # # log message
205
- # end
206
- #
207
- # # Callback for :ignite event *after* the transition has been performed
208
- # def after_ignite(vehicle, transition)
209
- # # put on seatbelt
210
- # end
211
- #
212
- # # Generic transition callback *before* the transition is performed
213
- # def after_transition(vehicle, transition)
214
- # Audit.log(vehicle, transition)
215
- # end
216
- #
217
- # def after_failure_to_transition(vehicle, transition)
218
- # Audit.error(vehicle, transition)
219
- # end
220
- # end
221
- #
222
- # More flexible transition callbacks can be defined directly within the
223
- # model as described in EnumStateMachine::Machine#before_transition
224
- # and EnumStateMachine::Machine#after_transition.
225
- #
226
- # To define a single observer for multiple state machines:
227
- #
228
- # class EnumStateMachineObserver < ActiveModel::Observer
229
- # observe Vehicle, Switch, Project
230
- #
231
- # def after_transition(object, transition)
232
- # Audit.log(object, transition)
233
- # end
234
- # end
235
- #
236
- # == Internationalization
237
- #
238
- # Any error message that is generated from performing invalid transitions
239
- # can be localized. The following default translations are used:
240
- #
241
- # en:
242
- # activemodel:
243
- # errors:
244
- # messages:
245
- # invalid: "is invalid"
246
- # # %{value} = attribute value, %{state} = Human state name
247
- # invalid_event: "cannot transition when %{state}"
248
- # # %{value} = attribute value, %{event} = Human event name, %{state} = Human current state name
249
- # invalid_transition: "cannot transition via %{event}"
250
- #
251
- # You can override these for a specific model like so:
252
- #
253
- # en:
254
- # activemodel:
255
- # errors:
256
- # models:
257
- # user:
258
- # invalid: "is not valid"
259
- #
260
- # In addition to the above, you can also provide translations for the
261
- # various states / events in each state machine. Using the Vehicle example,
262
- # state translations will be looked for using the following keys, where
263
- # +model_name+ = "vehicle", +machine_name+ = "state" and +state_name+ = "parked":
264
- # * <tt>activemodel.state_machines.#{model_name}.#{machine_name}.states.#{state_name}</tt>
265
- # * <tt>activemodel.state_machines.#{model_name}.states.#{state_name}</tt>
266
- # * <tt>activemodel.state_machines.#{machine_name}.states.#{state_name}</tt>
267
- # * <tt>activemodel.state_machines.states.#{state_name}</tt>
268
- #
269
- # Event translations will be looked for using the following keys, where
270
- # +model_name+ = "vehicle", +machine_name+ = "state" and +event_name+ = "ignite":
271
- # * <tt>activemodel.state_machines.#{model_name}.#{machine_name}.events.#{event_name}</tt>
272
- # * <tt>activemodel.state_machines.#{model_name}.events.#{event_name}</tt>
273
- # * <tt>activemodel.state_machines.#{machine_name}.events.#{event_name}</tt>
274
- # * <tt>activemodel.state_machines.events.#{event_name}</tt>
275
- #
276
- # An example translation configuration might look like so:
277
- #
278
- # es:
279
- # activemodel:
280
- # state_machines:
281
- # states:
282
- # parked: 'estacionado'
283
- # events:
284
- # park: 'estacionarse'
285
- #
286
- # == Dirty Attribute Tracking
287
- #
288
- # When using the ActiveModel::Dirty extension, your model will keep track of
289
- # any changes that are made to attributes. Depending on your ORM, an object
290
- # will only be saved when there are attributes that have changed on the
291
- # object. When integrating with state_machine, typically the +state+ field
292
- # will be marked as dirty after a transition occurs. In some situations,
293
- # however, this isn't the case.
294
- #
295
- # If you define loopback transitions in your state machine, the value for
296
- # the machine's attribute (e.g. state) will not change. Unless you explicitly
297
- # indicate so, this means that your object won't persist anything on a
298
- # loopback. For example:
299
- #
300
- # class Vehicle
301
- # include ActiveModel::Validations
302
- # include ActiveModel::Dirty
303
- # attr_accessor :state
304
- #
305
- # state_machine :initial => :parked do
306
- # event :park do
307
- # transition :parked => :parked, ...
308
- # end
309
- # end
310
- # end
311
- #
312
- # If, instead, you'd like your object to always persist regardless of
313
- # whether the value actually changed, you can do so by using the
314
- # <tt>#{attribute}_will_change!</tt> helpers or defining a +before_transition+
315
- # callback that actually changes an attribute on the model. For example:
316
- #
317
- # class Vehicle
318
- # ...
319
- # state_machine :initial => :parked do
320
- # before_transition all => same do |vehicle|
321
- # vehicle.state_will_change!
322
- #
323
- # # Alternative solution, updating timestamp
324
- # # vehicle.updated_at = Time.curent
325
- # end
326
- # end
327
- # end
328
- #
329
- # == Creating new integrations
330
- #
331
- # If you want to integrate state_machine with an ORM that implements parts
332
- # or all of the ActiveModel API, only the machine defaults need to be
333
- # specified. Otherwise, the implementation is similar to any other
334
- # integration.
335
- #
336
- # For example,
337
- #
338
- # module EnumStateMachine::Integrations::MyORM
339
- # include EnumStateMachine::Integrations::ActiveModel
340
- #
341
- # @defaults = {:action = > :persist}
342
- #
343
- # def self.matches?(klass)
344
- # defined?(::MyORM::Base) && klass <= ::MyORM::Base
345
- # end
346
- #
347
- # protected
348
- # def runs_validations_on_action?
349
- # action == :persist
350
- # end
351
- # end
352
- #
353
- # If you wish to implement other features, such as attribute initialization
354
- # with protected attributes, named scopes, or database transactions, you
355
- # must add these independent of the ActiveModel integration. See the
356
- # ActiveRecord implementation for examples of these customizations.
357
- module ActiveModel
358
- def self.included(base) #:nodoc:
359
- base.versions.unshift(*versions)
360
- end
361
-
362
- include Base
363
- extend ClassMethods
364
-
365
- require 'enum_state_machine/integrations/active_model/versions'
366
-
367
- @defaults = {}
368
-
369
- # Classes that include ActiveModel::Observing or ActiveModel::Validations
370
- # will automatically use the ActiveModel integration.
371
- def self.matching_ancestors
372
- %w(ActiveModel ActiveModel::Observing ActiveModel::Validations)
373
- end
374
-
375
- # Adds a validation error to the given object
376
- def invalidate(object, attribute, message, values = [])
377
- if supports_validations?
378
- attribute = self.attribute(attribute)
379
- options = values.inject({}) do |h, (key, value)|
380
- h[key] = value
381
- h
382
- end
383
-
384
- default_options = default_error_message_options(object, attribute, message)
385
- object.errors.add(attribute, message, options.merge(default_options))
386
- end
387
- end
388
-
389
- # Describes the current validation errors on the given object. If none
390
- # are specific, then the default error is interpeted as a "halt".
391
- def errors_for(object)
392
- object.errors.empty? ? 'Transition halted' : object.errors.full_messages * ', '
393
- end
394
-
395
- # Resets any errors previously added when invalidating the given object
396
- def reset(object)
397
- object.errors.clear if supports_validations?
398
- end
399
-
400
- protected
401
- # Whether observers are supported in the integration. Only true if
402
- # ActiveModel::Observer is available.
403
- def supports_observers?
404
- defined?(::ActiveModel::Observing) && owner_class <= ::ActiveModel::Observing
405
- end
406
-
407
- # Whether validations are supported in the integration. Only true if
408
- # the ActiveModel feature is enabled on the owner class.
409
- def supports_validations?
410
- defined?(::ActiveModel::Validations) && owner_class <= ::ActiveModel::Validations
411
- end
412
-
413
- # Do validations run when the action configured this machine is
414
- # invoked? This is used to determine whether to fire off attribute-based
415
- # event transitions when the action is run.
416
- def runs_validations_on_action?
417
- false
418
- end
419
-
420
- # Gets the terminator to use for callbacks
421
- def callback_terminator
422
- @terminator ||= lambda {|result| result == false}
423
- end
424
-
425
- # Determines the base scope to use when looking up translations
426
- def i18n_scope(klass)
427
- klass.i18n_scope
428
- end
429
-
430
- # The default options to use when generating messages for validation
431
- # errors
432
- def default_error_message_options(object, attribute, message)
433
- {:message => @messages[message]}
434
- end
435
-
436
- # Translates the given key / value combo. Translation keys are looked
437
- # up in the following order:
438
- # * <tt>#{i18n_scope}.state_machines.#{model_name}.#{machine_name}.#{plural_key}.#{value}</tt>
439
- # * <tt>#{i18n_scope}.state_machines.#{model_name}.#{plural_key}.#{value}</tt>
440
- # * <tt>#{i18n_scope}.state_machines.#{machine_name}.#{plural_key}.#{value}</tt>
441
- # * <tt>#{i18n_scope}.state_machines.#{plural_key}.#{value}</tt>
442
- #
443
- # If no keys are found, then the humanized value will be the fallback.
444
- def translate(klass, key, value)
445
- ancestors = ancestors_for(klass)
446
- group = key.to_s.pluralize
447
- value = value ? value.to_s : 'nil'
448
-
449
- # Generate all possible translation keys
450
- translations = ancestors.map {|ancestor| :"#{ancestor.model_name.to_s.underscore}.#{name}.#{group}.#{value}"}
451
- translations.concat(ancestors.map {|ancestor| :"#{ancestor.model_name.to_s.underscore}.#{group}.#{value}"})
452
- translations.concat([:"#{name}.#{group}.#{value}", :"#{group}.#{value}", value.humanize.downcase])
453
- I18n.translate(translations.shift, :default => translations, :scope => [i18n_scope(klass), :state_machines])
454
- end
455
-
456
- # Build a list of ancestors for the given class to use when
457
- # determining which localization key to use for a particular string.
458
- def ancestors_for(klass)
459
- klass.lookup_ancestors
460
- end
461
-
462
- # Initializes class-level extensions and defaults for this machine
463
- def after_initialize
464
- super
465
- load_locale
466
- load_observer_extensions
467
- add_default_callbacks
468
- end
469
-
470
- # Loads any locale files needed for translating validation errors
471
- def load_locale
472
- I18n.load_path.unshift(@integration.locale_path) unless I18n.load_path.include?(@integration.locale_path)
473
- end
474
-
475
- # Loads extensions to ActiveModel's Observers
476
- def load_observer_extensions
477
- require 'enum_state_machine/integrations/active_model/observer'
478
- require 'enum_state_machine/integrations/active_model/observer_update'
479
- end
480
-
481
- # Adds a set of default callbacks that utilize the Observer extensions
482
- def add_default_callbacks
483
- if supports_observers?
484
- callbacks[:before] << Callback.new(:before) {|object, transition| notify(:before, object, transition)}
485
- callbacks[:after] << Callback.new(:after) {|object, transition| notify(:after, object, transition)}
486
- callbacks[:failure] << Callback.new(:failure) {|object, transition| notify(:after_failure_to, object, transition)}
487
- end
488
- end
489
-
490
- # Skips defining reader/writer methods since this is done automatically
491
- def define_state_accessor
492
- name = self.name
493
-
494
- owner_class.validates_each(attribute) do |object, attr, value|
495
- machine = object.class.state_machine(name)
496
- machine.invalidate(object, :state, :invalid) unless machine.states.match(object)
497
- end if supports_validations?
498
- end
499
-
500
- # Adds hooks into validation for automatically firing events
501
- def define_action_helpers
502
- super
503
- define_validation_hook if runs_validations_on_action?
504
- end
505
-
506
- # Hooks into validations by defining around callbacks for the
507
- # :validation event
508
- def define_validation_hook
509
- owner_class.set_callback(:validation, :around, self, :prepend => true)
510
- end
511
-
512
- # Runs state events around the object's validation process
513
- def around_validation(object)
514
- object.class.state_machines.transitions(object, action, :after => false).perform { yield }
515
- end
516
-
517
- # Creates a new callback in the callback chain, always inserting it
518
- # before the default Observer callbacks that were created after
519
- # initialization.
520
- def add_callback(type, options, &block)
521
- options[:terminator] = callback_terminator
522
-
523
- if supports_observers?
524
- @callbacks[type == :around ? :before : type].insert(-2, callback = Callback.new(type, options, &block))
525
- add_states(callback.known_states)
526
- callback
527
- else
528
- super
529
- end
530
- end
531
-
532
- # Configures new states with the built-in humanize scheme
533
- def add_states(new_states)
534
- super.each do |new_state|
535
- new_state.human_name = lambda {|state, klass| translate(klass, :state, state.name)}
536
- end
537
- end
538
-
539
- # Configures new event with the built-in humanize scheme
540
- def add_events(new_events)
541
- super.each do |new_event|
542
- new_event.human_name = lambda {|event, klass| translate(klass, :event, event.name)}
543
- end
544
- end
545
-
546
- # Notifies observers on the given object that a callback occurred
547
- # involving the given transition. This will attempt to call the
548
- # following methods on observers:
549
- # * <tt>#{type}_#{qualified_event}_from_#{from}_to_#{to}</tt>
550
- # * <tt>#{type}_#{qualified_event}_from_#{from}</tt>
551
- # * <tt>#{type}_#{qualified_event}_to_#{to}</tt>
552
- # * <tt>#{type}_#{qualified_event}</tt>
553
- # * <tt>#{type}_transition_#{machine_name}_from_#{from}_to_#{to}</tt>
554
- # * <tt>#{type}_transition_#{machine_name}_from_#{from}</tt>
555
- # * <tt>#{type}_transition_#{machine_name}_to_#{to}</tt>
556
- # * <tt>#{type}_transition_#{machine_name}</tt>
557
- # * <tt>#{type}_transition</tt>
558
- #
559
- # This will always return true regardless of the results of the
560
- # callbacks.
561
- def notify(type, object, transition)
562
- name = self.name
563
- event = transition.qualified_event
564
- from = transition.from_name || 'nil'
565
- to = transition.to_name || 'nil'
566
-
567
- # Machine-specific updates
568
- ["#{type}_#{event}", "#{type}_transition_#{name}"].each do |event_segment|
569
- ["_from_#{from}", nil].each do |from_segment|
570
- ["_to_#{to}", nil].each do |to_segment|
571
- object.class.changed if object.class.respond_to?(:changed)
572
- object.class.notify_observers('update_with_transition', ObserverUpdate.new([event_segment, from_segment, to_segment].join, object, transition))
573
- end
574
- end
575
- end
576
-
577
- # Generic updates
578
- object.class.changed if object.class.respond_to?(:changed)
579
- object.class.notify_observers('update_with_transition', ObserverUpdate.new("#{type}_transition", object, transition))
580
-
581
- true
582
- end
583
- end
584
- end
585
- end