88require 'state_machines/integrations/active_model/version'
99
1010module StateMachines
11- module Integrations #:nodoc:
11+ module Integrations # :nodoc:
1212 # Adds support for integrating state machines with ActiveModel classes.
1313 #
1414 # == Examples
@@ -189,64 +189,6 @@ module Integrations #:nodoc:
189189 # Note, also, that the transition can be accessed by simply defining
190190 # additional arguments in the callback block.
191191 #
192- # == Observers
193- #
194- # In order to hook in observer support for your application, the
195- # ActiveModel::Observing feature must be included. This can be added by including the
196- # https://github.com/state-machines/state_machines-activemodel-observers gem in your
197- # Gemfile. Because of the way
198- # ActiveModel observers are designed, there is less flexibility around the
199- # specific transitions that can be hooked in. However, a large number of
200- # hooks *are* supported. For example, if a transition for a object's
201- # +state+ attribute changes the state from +parked+ to +idling+ via the
202- # +ignite+ event, the following observer methods are supported:
203- # * before/after/after_failure_to-_ignite_from_parked_to_idling
204- # * before/after/after_failure_to-_ignite_from_parked
205- # * before/after/after_failure_to-_ignite_to_idling
206- # * before/after/after_failure_to-_ignite
207- # * before/after/after_failure_to-_transition_state_from_parked_to_idling
208- # * before/after/after_failure_to-_transition_state_from_parked
209- # * before/after/after_failure_to-_transition_state_to_idling
210- # * before/after/after_failure_to-_transition_state
211- # * before/after/after_failure_to-_transition
212- #
213- # The following class shows an example of some of these hooks:
214- #
215- # class VehicleObserver < ActiveModel::Observer
216- # # Callback for :ignite event *before* the transition is performed
217- # def before_ignite(vehicle, transition)
218- # # log message
219- # end
220- #
221- # # Callback for :ignite event *after* the transition has been performed
222- # def after_ignite(vehicle, transition)
223- # # put on seatbelt
224- # end
225- #
226- # # Generic transition callback *before* the transition is performed
227- # def after_transition(vehicle, transition)
228- # Audit.log(vehicle, transition)
229- # end
230- #
231- # def after_failure_to_transition(vehicle, transition)
232- # Audit.error(vehicle, transition)
233- # end
234- # end
235- #
236- # More flexible transition callbacks can be defined directly within the
237- # model as described in StateMachine::Machine#before_transition
238- # and StateMachine::Machine#after_transition.
239- #
240- # To define a single observer for multiple state machines:
241- #
242- # class StateMachineObserver < ActiveModel::Observer
243- # observe Vehicle, Switch, Project
244- #
245- # def after_transition(object, transition)
246- # Audit.log(object, transition)
247- # end
248- # end
249- #
250192 # == Internationalization
251193 #
252194 # Any error message that is generated from performing invalid transitions
@@ -403,14 +345,14 @@ def reset(object)
403345 end
404346
405347 # Runs state events around the object's validation process
406- def around_validation ( object )
407- object . class . state_machines . transitions ( object , action , after : false ) . perform { yield }
348+ def around_validation ( object , & )
349+ object . class . state_machines . transitions ( object , action , after : false ) . perform ( & )
408350 end
409351
410352 protected
411353
412354 def define_state_initializer
413- define_helper :instance , <<-end_eval , __FILE__ , __LINE__ + 1
355+ define_helper :instance , <<-END_EVAL , __FILE__ , __LINE__ + 1
414356 def initialize(params = nil, **kwargs)
415357 # Support both positional hash and keyword arguments
416358 attrs = params.nil? ? kwargs : params
@@ -428,7 +370,7 @@ def initialize(params = nil, **kwargs)
428370 end
429371 end
430372 end
431- end_eval
373+ END_EVAL
432374 end
433375
434376 # Whether validations are supported in the integration. Only true if
@@ -446,7 +388,7 @@ def runs_validations_on_action?
446388
447389 # Gets the terminator to use for callbacks
448390 def callback_terminator
449- @terminator ||= -> ( result ) { result == false }
391+ @callback_terminator ||= -> ( result ) { result == false }
450392 end
451393
452394 # Determines the base scope to use when looking up translations
@@ -476,7 +418,7 @@ def translate(klass, key, value)
476418 # Generate all possible translation keys
477419 translations = ancestors . map { |ancestor | :"#{ ancestor . model_name . to_s . underscore } .#{ name } .#{ group } .#{ value } " }
478420 translations . concat ( ancestors . map { |ancestor | :"#{ ancestor . model_name . to_s . underscore } .#{ group } .#{ value } " } )
479- translations . concat ( [ :"#{ name } .#{ group } .#{ value } " , :"#{ group } .#{ value } " , value . humanize . downcase ] )
421+ translations . push ( :"#{ name } .#{ group } .#{ value } " , :"#{ group } .#{ value } " , value . humanize . downcase )
480422 I18n . translate ( translations . shift , default : translations , scope : [ i18n_scope ( klass ) , :state_machines ] )
481423 end
482424
@@ -490,10 +432,12 @@ def ancestors_for(klass)
490432 def define_state_accessor
491433 name = self . name
492434
435+ return unless supports_validations?
436+
493437 owner_class . validates_each ( attribute ) do |object |
494438 machine = object . class . state_machine ( name )
495439 machine . invalidate ( object , :state , :invalid ) unless machine . states . match ( object )
496- end if supports_validations?
440+ end
497441 end
498442
499443 # Adds hooks into validation for automatically firing events
@@ -511,22 +455,32 @@ def define_validation_hook
511455 # Creates a new callback in the callback chain, always inserting it
512456 # before the default Observer callbacks that were created after
513457 # initialization.
514- def add_callback ( type , options , &block )
458+ def add_callback ( type , options , &)
515459 options [ :terminator ] = callback_terminator
516460 super
517461 end
518462
519463 # Configures new states with the built-in humanize scheme
520464 def add_states ( *)
521465 super . each do |new_state |
522- new_state . human_name ||= -> ( state , klass ) { translate ( klass , :state , state . name ) }
466+ # Only set the translation lambda if human_name is the default auto-generated value
467+ # This preserves user-specified human names while still applying translations for defaults
468+ default_human_name = new_state . name ? new_state . name . to_s . tr ( '_' , ' ' ) : 'nil'
469+ if new_state . human_name == default_human_name
470+ new_state . human_name = -> ( state , klass ) { translate ( klass , :state , state . name ) }
471+ end
523472 end
524473 end
525474
526475 # Configures new event with the built-in humanize scheme
527476 def add_events ( *)
528477 super . each do |new_event |
529- new_event . human_name = -> ( event , klass ) { translate ( klass , :event , event . name ) }
478+ # Only set the translation lambda if human_name is the default auto-generated value
479+ # This preserves user-specified human names while still applying translations for defaults
480+ default_human_name = new_event . name ? new_event . name . to_s . tr ( '_' , ' ' ) : 'nil'
481+ if new_event . human_name == default_human_name
482+ new_event . human_name = -> ( event , klass ) { translate ( klass , :event , event . name ) }
483+ end
530484 end
531485 end
532486 end
0 commit comments