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
state_without_scope(*args)
Alias for: 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