Skip to content

Commit

Permalink
using default name (all tests green)
Browse files Browse the repository at this point in the history
  • Loading branch information
alto committed May 5, 2015
1 parent a8e10dc commit 5c0e086
Show file tree
Hide file tree
Showing 13 changed files with 193 additions and 154 deletions.
81 changes: 46 additions & 35 deletions lib/aasm/aasm.rb
Expand Up @@ -8,36 +8,47 @@ def self.included(base) #:nodoc:

# do not overwrite existing state machines, which could have been created by
# inheritance, see class method inherited
AASM::StateMachine[base] ||= AASM::StateMachine.new
AASM::StateMachine[base] ||= {}

AASM::Persistence.load_persistence(base)
super
end

module ClassMethods

# make sure inheritance (aka subclassing) works with AASM
def inherited(base)
AASM::StateMachine[base] = AASM::StateMachine[self].clone
AASM::StateMachine[base] = {}
AASM::StateMachine[self].keys.each do |state_machine_name|
AASM::StateMachine[base][state_machine_name] = AASM::StateMachine[self][state_machine_name].clone
end
super
end

# this is the entry point for all state and event definitions
def aasm(options={}, &block)
@aasm ||= AASM::Base.new(self, options)
@aasm.instance_eval(&block) if block # new DSL
@aasm
end
def aasm(*args, &block)
if args[0].is_a?(Symbol) || args[0].is_a?(String)
# using custom name
name = args[0].to_sym
options = args[1] || {}
else
# using the default name
name = :default
options = args[0] || {}
end

AASM::StateMachine[self][name] ||= AASM::StateMachine.new

# deprecated, remove in version 4.1
def aasm_human_event_name(event) # event_name?
warn '[DEPRECATION] AASM: aasm_human_event_name is deprecated, use aasm.human_event_name instead'
aasm.human_event_name(event)
@aasm ||= {}
@aasm[name] ||= AASM::Base.new(self, name, AASM::StateMachine[self][name], options)
@aasm[name].instance_eval(&block) if block # new DSL
@aasm[name]
end
end # ClassMethods

def aasm
@aasm ||= AASM::InstanceBase.new(self)
# this is the entry point for all instance-level access to AASM
def aasm(name=:default)
@aasm ||= {}
@aasm[name.to_sym] ||= AASM::InstanceBase.new(self, name.to_sym)
end

private
Expand All @@ -56,10 +67,10 @@ def process_args(event, from_state, *args)
return args
end

def aasm_fire_event(event_name, options, *args, &block)
event = self.class.aasm.state_machine.events[event_name]
def aasm_fire_event(state_machine_name, event_name, options, *args, &block)
event = self.class.aasm(state_machine_name).state_machine.events[event_name]
begin
old_state = aasm.state_object_for_name(aasm.current_state)
old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)

# new event before callback
event.fire_callbacks(
Expand All @@ -70,72 +81,72 @@ def aasm_fire_event(event_name, options, *args, &block)

if may_fire_to = event.may_fire?(self, *args)
old_state.fire_callbacks(:before_exit, self,
*process_args(event, aasm.current_state, *args))
*process_args(event, aasm(state_machine_name).current_state, *args))
old_state.fire_callbacks(:exit, self,
*process_args(event, aasm.current_state, *args)) # TODO: remove for AASM 4?
*process_args(event, aasm(state_machine_name).current_state, *args))

if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
aasm_fired(event, old_state, new_state_name, options, *args, &block)
aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args, &block)
else
aasm_failed(event_name, old_state)
aasm_failed(state_machine_name, event_name, old_state)
end
else
aasm_failed(event_name, old_state)
aasm_failed(state_machine_name, event_name, old_state)
end
rescue StandardError => e
event.fire_callbacks(:error, self, e, *process_args(event, aasm.current_state, *args)) || raise(e)
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) || raise(e)
end
end

def aasm_fired(event, old_state, new_state_name, options, *args)
def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
persist = options[:persist]

new_state = aasm.state_object_for_name(new_state_name)
new_state = aasm(state_machine_name).state_object_for_name(new_state_name)

new_state.fire_callbacks(:before_enter, self,
*process_args(event, aasm.current_state, *args))
*process_args(event, aasm(state_machine_name).current_state, *args))

new_state.fire_callbacks(:enter, self,
*process_args(event, aasm.current_state, *args)) # TODO: remove for AASM 4?
*process_args(event, aasm(state_machine_name).current_state, *args)) # TODO: remove for AASM 4?

persist_successful = true
if persist
persist_successful = aasm.set_current_state_with_persistence(new_state_name)
persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
if persist_successful
yield if block_given?
event.fire_callbacks(:success, self)
end
else
aasm.current_state = new_state_name
aasm(state_machine_name).current_state = new_state_name
yield if block_given?
end

if persist_successful
old_state.fire_callbacks(:after_exit, self,
*process_args(event, aasm.current_state, *args))
*process_args(event, aasm(state_machine_name).current_state, *args))
new_state.fire_callbacks(:after_enter, self,
*process_args(event, aasm.current_state, *args))
*process_args(event, aasm(state_machine_name).current_state, *args))
event.fire_callbacks(
:after,
self,
*process_args(event, old_state.name, *args)
)

self.aasm_event_fired(event.name, old_state.name, aasm.current_state) if self.respond_to?(:aasm_event_fired)
self.aasm_event_fired(event.name, old_state.name, aasm(state_machine_name).current_state) if self.respond_to?(:aasm_event_fired)
else
self.aasm_event_failed(event.name, old_state.name) if self.respond_to?(:aasm_event_failed)
end

persist_successful
end

def aasm_failed(event_name, old_state)
def aasm_failed(state_machine_name, event_name, old_state)
if self.respond_to?(:aasm_event_failed)
self.aasm_event_failed(event_name, old_state.name)
end

if AASM::StateMachine[self.class].config.whiny_transitions
raise AASM::InvalidTransition.new(self, event_name)
if AASM::StateMachine[self.class][state_machine_name].config.whiny_transitions
raise AASM::InvalidTransition.new(self, event_name)
else
false
end
Expand Down
44 changes: 29 additions & 15 deletions lib/aasm/base.rb
Expand Up @@ -3,9 +3,11 @@ class Base

attr_reader :state_machine

def initialize(klass, options={}, &block)
def initialize(klass, name, state_machine, options={}, &block)
@klass = klass
@state_machine = AASM::StateMachine[@klass]
@name = name
# @state_machine = @klass.aasm(@name).state_machine
@state_machine = state_machine
@state_machine.config.column ||= (options[:column] || :aasm_state).to_sym # aasm4
# @state_machine.config.column = options[:column].to_sym if options[:column] # master
@options = options
Expand Down Expand Up @@ -56,10 +58,16 @@ def initial_state(new_initial_state=nil)
def state(name, options={})
@state_machine.add_state(name, @klass, options)

@klass.send(:define_method, "#{name}?") do
aasm.current_state == name
if @klass.instance_methods.include?("#{name}?")
warn "The state name #{name} is already used!"
end

@klass.class_eval <<-EORUBY, __FILE__, __LINE__ + 1
def #{name}?
aasm(:#{@name}).current_state == :#{name}
end
EORUBY

unless @klass.const_defined?("STATE_#{name.upcase}")
@klass.const_set("STATE_#{name.upcase}", name)
end
Expand All @@ -69,22 +77,28 @@ def state(name, options={})
def event(name, options={}, &block)
@state_machine.events[name] = AASM::Core::Event.new(name, options, &block)

if @klass.instance_methods.include?("may_#{name}?")
warn "The event name #{name} is already used!"
end

# 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.
@klass.send(:define_method, "may_#{name}?") do |*args|
aasm.may_fire_event?(name, *args)
end
@klass.class_eval <<-EORUBY, __FILE__, __LINE__ + 1
def may_#{name}?(*args)
aasm(:#{@name}).may_fire_event?(:#{name}, *args)
end
@klass.send(:define_method, "#{name}!") do |*args, &block|
aasm.current_event = "#{name}!".to_sym
aasm_fire_event(name, {:persist => true}, *args, &block)
end
def #{name}!(*args, &block)
aasm(:#{@name}).current_event = :#{name}!
aasm_fire_event(:#{@name}, :#{name}, {:persist => true}, *args, &block)
end
@klass.send(:define_method, "#{name}") do |*args, &block|
aasm.current_event = name.to_sym
aasm_fire_event(name, {:persist => false}, *args, &block)
end
def #{name}(*args, &block)
aasm(:#{@name}).current_event = :#{name}
aasm_fire_event(:#{@name}, :#{name}, {:persist => false}, *args, &block)
end
EORUBY
end

def states
Expand Down
25 changes: 13 additions & 12 deletions lib/aasm/instance_base.rb
Expand Up @@ -3,21 +3,22 @@ class InstanceBase

attr_accessor :from_state, :to_state, :current_event

def initialize(instance)
def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
@instance = instance
@name = name
end

def current_state
@instance.aasm_read_state
@instance.aasm_read_state(@name)
end

def current_state=(state)
@instance.aasm_write_state_without_persistence(state)
@current_state = state
@instance.aasm_write_state_without_persistence(state, @name)
# @current_state = state
end

def enter_initial_state
state_name = determine_state_name(@instance.class.aasm.initial_state)
state_name = determine_state_name(@instance.class.aasm(@name).initial_state)
state_object = state_object_for_name(state_name)

state_object.fire_callbacks(:before_enter, @instance)
Expand All @@ -36,17 +37,17 @@ def states(options={})
if options[:permitted]
# ugliness level 1000
permitted_event_names = events(:permitted => true).map(&:name)
transitions = @instance.class.aasm.state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) }
transitions = @instance.class.aasm(@name).state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) }
tos = transitions.map {|t| t[0] ? t[0].to : nil}.flatten.compact.map(&:to_sym).uniq
@instance.class.aasm.states.select {|s| tos.include?(s.name.to_sym)}
@instance.class.aasm(@name).states.select {|s| tos.include?(s.name.to_sym)}
else
@instance.class.aasm.states
@instance.class.aasm(@name).states
end
end

def events(options={})
state = options[:state] || current_state
events = @instance.class.aasm.events.select {|e| e.transitions_from_state?(state) }
events = @instance.class.aasm(@name).events.select {|e| e.transitions_from_state?(state) }

if options[:permitted]
# filters the results of events_for_current_state so that only those that
Expand All @@ -58,7 +59,7 @@ def events(options={})
end

def state_object_for_name(name)
obj = @instance.class.aasm.states.find {|s| s == name}
obj = @instance.class.aasm(@name).states.find {|s| s == name}
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
obj
end
Expand All @@ -75,15 +76,15 @@ def determine_state_name(state)
end

def may_fire_event?(name, *args)
if event = @instance.class.aasm.state_machine.events[name]
if event = @instance.class.aasm(@name).state_machine.events[name]
!!event.may_fire?(@instance, *args)
else
false # unknown event
end
end

def set_current_state_with_persistence(state)
save_success = @instance.aasm_write_state(state)
save_success = @instance.aasm_write_state(state, @name)
self.current_state = state if save_success
save_success
end
Expand Down

0 comments on commit 5c0e086

Please sign in to comment.