class AASM::Base
Attributes
klass[R]
state_machine[R]
Public Class Methods
new(klass, name, state_machine, options={}, &block)
click to toggle source
# File lib/aasm/base.rb, line 8 def initialize(klass, name, state_machine, options={}, &block) @klass = klass @name = name # @state_machine = klass.aasm(@name).state_machine @state_machine = state_machine @state_machine.config.column ||= (options[:column] || default_column).to_sym # @state_machine.config.column = options[:column].to_sym if options[:column] # master @options = options # let's cry if the transition is invalid configure :whiny_transitions, true # create named scopes for each state configure :create_scopes, true # don't store any new state if the model is invalid (in ActiveRecord) configure :skip_validation_on_save, false # raise if the model is invalid (in ActiveRecord) configure :whiny_persistence, false # Use transactions (in ActiveRecord) configure :use_transactions, true # use requires_new for nested transactions (in ActiveRecord) configure :requires_new_transaction, true # use pessimistic locking (in ActiveRecord) # true for FOR UPDATE lock # string for a specific lock type i.e. FOR UPDATE NOWAIT configure :requires_lock, false # automatically set `"#{state_name}_at" = ::Time.now` on state changes configure :timestamps, false # set to true to forbid direct assignment of aasm_state column (in ActiveRecord) configure :no_direct_assignment, false # allow a AASM::Base sub-class to be used for state machine configure :with_klass, AASM::Base configure :enum, nil # Set to true to namespace reader methods and constants configure :namespace, false # Configure a logger, with default being a Logger to STDERR configure :logger, Logger.new(STDERR) # setup timestamp-setting callback if enabled setup_timestamps(@name) # make sure to raise an error if no_direct_assignment is enabled # and attribute is directly assigned though setup_no_direct_assignment(@name) end
Public Instance Methods
after_all_events(*callbacks, &block)
click to toggle source
# File lib/aasm/base.rb, line 161 def after_all_events(*callbacks, &block) @state_machine.add_global_callbacks(:after_all_events, *callbacks, &block) end
after_all_transactions(*callbacks, &block)
click to toggle source
# File lib/aasm/base.rb, line 149 def after_all_transactions(*callbacks, &block) @state_machine.add_global_callbacks(:after_all_transactions, *callbacks, &block) end
after_all_transitions(*callbacks, &block)
click to toggle source
# File lib/aasm/base.rb, line 145 def after_all_transitions(*callbacks, &block) @state_machine.add_global_callbacks(:after_all_transitions, *callbacks, &block) end
attribute_name(column_name=nil)
click to toggle source
This method is both a getter and a setter
# File lib/aasm/base.rb, line 66 def attribute_name(column_name=nil) if column_name @state_machine.config.column = column_name.to_sym else @state_machine.config.column ||= :aasm_state end @state_machine.config.column end
before_all_events(*callbacks, &block)
click to toggle source
# File lib/aasm/base.rb, line 157 def before_all_events(*callbacks, &block) @state_machine.add_global_callbacks(:before_all_events, *callbacks, &block) end
before_all_transactions(*callbacks, &block)
click to toggle source
# File lib/aasm/base.rb, line 153 def before_all_transactions(*callbacks, &block) @state_machine.add_global_callbacks(:before_all_transactions, *callbacks, &block) end
ensure_on_all_events(*callbacks, &block)
click to toggle source
# File lib/aasm/base.rb, line 169 def ensure_on_all_events(*callbacks, &block) @state_machine.add_global_callbacks(:ensure_on_all_events, *callbacks, &block) end
error_on_all_events(*callbacks, &block)
click to toggle source
# File lib/aasm/base.rb, line 165 def error_on_all_events(*callbacks, &block) @state_machine.add_global_callbacks(:error_on_all_events, *callbacks, &block) end
event(name, options={}, &block)
click to toggle source
define an event
# File lib/aasm/base.rb, line 111 def event(name, options={}, &block) @state_machine.add_event(name, options, &block) aasm_name = @name.to_sym event = name.to_sym # an addition over standard aasm so that, before firing an event, you can ask # may_event? and get back a boolean that tells you whether the guard method # on the transition will let this happen. safely_define_method klass, "may_#{name}?", ->(*args) do aasm(aasm_name).may_fire_event?(event, *args) end safely_define_method klass, "#{name}!", ->(*args, &block) do aasm(aasm_name).current_event = :"#{name}!" aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block) end safely_define_method klass, name, ->(*args, &block) do aasm(aasm_name).current_event = event aasm_fire_event(aasm_name, event, {:persist => false}, *args, &block) end skip_instance_level_validation(event, name, aasm_name, klass) # Create aliases for the event methods. Keep the old names to maintain backwards compatibility. if namespace? klass.send(:alias_method, "may_#{name}_#{namespace}?", "may_#{name}?") klass.send(:alias_method, "#{name}_#{namespace}!", "#{name}!") klass.send(:alias_method, "#{name}_#{namespace}", name) end end
events()
click to toggle source
# File lib/aasm/base.rb, line 177 def events @state_machine.events.values end
from_states_for_state(state, options={})
click to toggle source
# File lib/aasm/base.rb, line 190 def from_states_for_state(state, options={}) if options[:transition] @state_machine.events[options[:transition]].transitions_to_state(state).flatten.map(&:from).flatten else events.map {|e| e.transitions_to_state(state)}.flatten.map(&:from).flatten end end
human_event_name(event)
click to toggle source
aasm.event(:event_name).human?
# File lib/aasm/base.rb, line 182 def human_event_name(event) # event_name? AASM::Localizer.new.human_event_name(klass, event) end
initial_state(new_initial_state=nil)
click to toggle source
# File lib/aasm/base.rb, line 75 def initial_state(new_initial_state=nil) if new_initial_state @state_machine.initial_state = new_initial_state else @state_machine.initial_state end end
state(*args)
click to toggle source
define a state args
- 0
-
state
- 1
-
options (or nil)
or
- 0
-
state
- 1..
-
state
# File lib/aasm/base.rb, line 90 def state(*args) names, options = interpret_state_args(args) names.each do |name| @state_machine.add_state(name, klass, options) aasm_name = @name.to_sym state = name.to_sym method_name = namespace? ? "#{namespace}_#{name}" : name safely_define_method klass, "#{method_name}?", -> do aasm(aasm_name).current_state == state end const_name = namespace? ? "STATE_#{namespace.upcase}_#{name.upcase}" : "STATE_#{name.upcase}" unless klass.const_defined?(const_name) klass.const_set(const_name, name) end end end
Also aliased as: state_without_scope
state_with_scope(*args)
click to toggle source
make sure to create a (named) scope for each state
# File lib/aasm/persistence/base.rb, line 60 def state_with_scope(*args) names = state_without_scope(*args) names.each do |name| create_scopes(name) end end
Also aliased as: state
states()
click to toggle source
# File lib/aasm/base.rb, line 173 def states @state_machine.states end
states_for_select()
click to toggle source
# File lib/aasm/base.rb, line 186 def states_for_select states.map { |state| state.for_select } end
Private Instance Methods
apply_ruby2_keyword(klass, sym)
click to toggle source
# File lib/aasm/base.rb, line 231 def apply_ruby2_keyword(klass, sym) if RUBY_VERSION >= '2.7.1' if klass.instance_method(sym).parameters.find { |type, _| type.to_s.start_with?('rest') } # If there is a place where you are receiving in *args, do ruby2_keywords. klass.module_eval do ruby2_keywords sym end end end end
configure(key, default_value)
click to toggle source
# File lib/aasm/base.rb, line 205 def configure(key, default_value) if @options.key?(key) @state_machine.config.send("#{key}=", @options[key]) elsif @state_machine.config.send(key).nil? @state_machine.config.send("#{key}=", default_value) end end
create_scope(name)
click to toggle source
# File lib/aasm/persistence/base.rb, line 75 def create_scope(name) @klass.aasm_create_scope(@name, name) if create_scope?(name) end
create_scope?(name)
click to toggle source
# File lib/aasm/persistence/base.rb, line 71 def create_scope?(name) @state_machine.config.create_scopes && !@klass.respond_to?(name) && @klass.respond_to?(:aasm_create_scope) end
create_scopes(name)
click to toggle source
# File lib/aasm/persistence/base.rb, line 79 def create_scopes(name) if namespace? # Create default scopes even when namespace? for backward compatibility namepaced_name = "#{namespace}_#{name}" create_scope(namepaced_name) end create_scope(name) end
default_column()
click to toggle source
# File lib/aasm/base.rb, line 201 def default_column @name.to_sym == :default ? :aasm_state : @name.to_sym end
interpret_state_args(args)
click to toggle source
# File lib/aasm/base.rb, line 254 def interpret_state_args(args) if args.last.is_a?(Hash) && args.size == 2 [[args.first], args.last] elsif args.size > 0 [args, {}] else raise "count not parse states: #{args}" end end
namespace()
click to toggle source
# File lib/aasm/base.rb, line 246 def namespace if @state_machine.config.namespace == true @name else @state_machine.config.namespace end end
namespace?()
click to toggle source
# File lib/aasm/base.rb, line 242 def namespace? !!@state_machine.config.namespace end
safely_define_method(klass, method_name, method_definition)
click to toggle source
# File lib/aasm/base.rb, line 213 def safely_define_method(klass, method_name, method_definition) # Warn if method exists and it did not originate from an enum if klass.method_defined?(method_name) && ! ( @state_machine.config.enum && klass.respond_to?(:defined_enums) && klass.defined_enums.values.any?{ |methods| methods.keys{| enum | enum + '?' == method_name } }) unless AASM::Configuration.hide_warnings @state_machine.config.logger.warn "#{klass.name}: overriding method '#{method_name}'!" end end klass.send(:define_method, method_name, method_definition).tap do |sym| apply_ruby2_keyword(klass, sym) end end
setup_no_direct_assignment(aasm_name)
click to toggle source
Calls superclass method
# File lib/aasm/base.rb, line 290 def setup_no_direct_assignment(aasm_name) return unless @state_machine.config.no_direct_assignment @klass.send(:define_method, "#{@state_machine.config.column}=") do |state_name| if self.class.aasm(:"#{aasm_name}").state_machine.config.no_direct_assignment raise AASM::NoDirectAssignmentError.new('direct assignment of AASM column has been disabled (see AASM configuration for this class)') else super(state_name) end end end
setup_timestamps(aasm_name)
click to toggle source
# File lib/aasm/base.rb, line 279 def setup_timestamps(aasm_name) return unless @state_machine.config.timestamps after_all_transitions do if self.class.aasm(:"#{aasm_name}").state_machine.config.timestamps ts_setter = "#{aasm(aasm_name).to_state}_at=" respond_to?(ts_setter) && send(ts_setter, ::Time.now) end end end
skip_instance_level_validation(event, name, aasm_name, klass)
click to toggle source
# File lib/aasm/base.rb, line 264 def skip_instance_level_validation(event, name, aasm_name, klass) # Overrides the skip_validation config for an instance (If skip validation is set to false in original config) and # restores it back to the original value after the event is fired. safely_define_method klass, "#{name}_without_validation!", ->(*args, &block) do original_config = AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save begin AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = true unless original_config aasm(aasm_name).current_event = :"#{name}!" aasm_fire_event(aasm_name, event, {:persist => true}, *args, &block) ensure AASM::StateMachineStore.fetch(self.class, true).machine(aasm_name).config.skip_validation_on_save = original_config end end end