class refactorings

This commit is contained in:
Thorsten Böttger 2011-11-26 18:30:47 +01:00
parent 20f4fed777
commit 14061ca752
12 changed files with 323 additions and 313 deletions

View File

@ -1,9 +1,10 @@
module AASM
end
require 'ostruct'
require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes')
require File.join(File.dirname(__FILE__), 'aasm', 'version')
require File.join(File.dirname(__FILE__), 'aasm', 'base')
require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes', 'state_transition')
require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes', 'event')
require File.join(File.dirname(__FILE__), 'aasm', 'supporting_classes', 'state')
require File.join(File.dirname(__FILE__), 'aasm', 'state_machine')
require File.join(File.dirname(__FILE__), 'aasm', 'persistence')
require File.join(File.dirname(__FILE__), 'aasm', 'aasm')

View File

@ -45,7 +45,7 @@ module AASM
def aasm_state(name, options={})
aasm.state(name, options)
end
# deprecated
def aasm_event(name, options = {}, &block)
aasm.event(name, options, &block)
@ -70,19 +70,14 @@ module AASM
AASM::Localizer.new.human_event_name(self, event)
end
end
# Instance methods
# this method does what? does it deliver the current state?
def aasm_current_state
return @aasm_current_state if @aasm_current_state
if self.respond_to?(:aasm_read_state) || self.private_methods.include?('aasm_read_state')
@aasm_current_state = aasm_read_state
end
return @aasm_current_state if @aasm_current_state
aasm_enter_initial_state
@aasm_current_state ||=
aasm_persistable? ? aasm_read_state : aasm_enter_initial_state
end
# private?
def aasm_enter_initial_state
state_name = aasm_determine_state_name(self.class.aasm_initial_state)
state = aasm_state_object_for_state(state_name)
@ -104,7 +99,7 @@ module AASM
def aasm_permissible_events_for_current_state
aasm_events_for_current_state.select{ |e| self.send(("may_" + e.to_s + "?").to_sym) }
end
def aasm_events_for_state(state)
events = self.class.aasm_events.values.select {|event| event.transitions_from_state?(state) }
events.map {|event| event.name}
@ -116,6 +111,10 @@ module AASM
private
def aasm_persistable?
self.respond_to?(:aasm_read_state) || self.private_methods.include?('aasm_read_state')
end
def aasm_set_current_state_with_persistence(state)
save_success = true
if self.respond_to?(:aasm_write_state) || self.private_methods.include?('aasm_write_state')
@ -154,7 +153,7 @@ private
event = self.class.aasm_events[name]
event.may_fire?(self, *args)
end
def aasm_fire_event(name, persist, *args)
event = self.class.aasm_events[name]
begin

View File

@ -1,127 +0,0 @@
class AASM::SupportingClasses::Event
attr_reader :name, :success, :options
def initialize(name, options = {}, &block)
@name = name
@transitions = []
update(options, &block)
end
# a neutered version of fire - it doesn't actually fire the event, it just
# executes the transition guards to determine if a transition is even
# an option given current conditions.
def may_fire?(obj, to_state=nil)
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
return false if transitions.size == 0
result = false
transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state)
if transition.perform(obj)
result = true
break
end
end
result
end
def fire(obj, to_state=nil, *args)
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
raise AASM::InvalidTransition, "Event '#{name}' cannot transition from '#{obj.aasm_current_state}'" if transitions.size == 0
next_state = nil
transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state)
if transition.perform(obj, *args)
next_state = to_state || Array(transition.to).first
transition.execute(obj, *args)
break
end
end
next_state
end
def transitions_from_state?(state)
@transitions.any? { |t| t.from == state }
end
def transitions_from_state(state)
@transitions.select { |t| t.from == state }
end
def all_transitions
@transitions
end
def call_action(action, record)
action = @options[action]
action.is_a?(Array) ?
action.each {|a| _call_action(a, record)} :
_call_action(action, record)
end
def ==(event)
if event.is_a? Symbol
name == event
else
name == event.name
end
end
def update(options = {}, &block)
if options.key?(:success) then
@success = options[:success]
end
if options.key?(:error) then
@error = options[:error]
end
if block then
instance_eval(&block)
end
@options = options
self
end
def execute_success_callback(obj, success = nil)
callback = success || @success
case(callback)
when String, Symbol
obj.send(callback)
when Proc
callback.call(obj)
when Array
callback.each{|meth|self.execute_success_callback(obj, meth)}
end
end
def execute_error_callback(obj, error, error_callback=nil)
callback = error_callback || @error
raise error unless callback
case(callback)
when String, Symbol
raise NoMethodError unless obj.respond_to?(callback.to_sym)
obj.send(callback, error)
when Proc
callback.call(obj, error)
when Array
callback.each{|meth|self.execute_error_callback(obj, error, meth)}
end
end
private
def _call_action(action, record)
case action
when Symbol, String
record.send(action)
when Proc
action.call(record)
end
end
def transitions(trans_opts)
Array(trans_opts[:from]).each do |s|
@transitions << AASM::SupportingClasses::StateTransition.new(trans_opts.merge({:from => s.to_sym}))
end
end
end

View File

@ -1,36 +1,38 @@
class AASM::Localizer
def human_event_name(klass, event)
defaults = ancestors_list(klass).map do |ancestor|
:"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
end << event.to_s.humanize
module AASM
class Localizer
def human_event_name(klass, event)
defaults = ancestors_list(klass).map do |ancestor|
:"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
end << event.to_s.humanize
I18n.translate(defaults.shift, :default => defaults, :raise => true)
end
I18n.translate(defaults.shift, :default => defaults, :raise => true)
end
def human_state(obj)
klass = obj.class
defaults = ancestors_list(klass).map do |ancestor|
:"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm_column}.#{obj.aasm_current_state}"
end << obj.aasm_current_state.to_s.humanize
def human_state(obj)
klass = obj.class
defaults = ancestors_list(klass).map do |ancestor|
:"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm_column}.#{obj.aasm_current_state}"
end << obj.aasm_current_state.to_s.humanize
I18n.translate(defaults.shift, :default => defaults, :raise => true)
end
I18n.translate(defaults.shift, :default => defaults, :raise => true)
end
private
private
# added for rails 2.x compatibility
def i18n_scope(klass)
klass.respond_to?(:i18n_scope) ? klass.i18n_scope : :activerecord
end
# added for rails 2.x compatibility
def i18n_scope(klass)
klass.respond_to?(:i18n_scope) ? klass.i18n_scope : :activerecord
end
# added for rails < 3.0.3 compatibility
def i18n_klass(klass)
klass.model_name.respond_to?(:i18n_key) ? klass.model_name.i18n_key : klass.name.underscore
end
# added for rails < 3.0.3 compatibility
def i18n_klass(klass)
klass.model_name.respond_to?(:i18n_key) ? klass.model_name.i18n_key : klass.name.underscore
end
def ancestors_list(klass)
klass.ancestors.select do |ancestor|
ancestor.respond_to?(:model_name) unless ancestor == ActiveRecord::Base
def ancestors_list(klass)
klass.ancestors.select do |ancestor|
ancestor.respond_to?(:model_name) unless ancestor == ActiveRecord::Base
end
end
end
end
end # AASM

View File

@ -1,14 +1,15 @@
module AASM::Persistence
module AASM
module Persistence
# Checks to see this class or any of it's superclasses inherit from
# ActiveRecord::Base and if so includes ActiveRecordPersistence
def self.set_persistence(base)
# Use a fancier auto-loading thingy, perhaps. When there are more persistence engines.
hierarchy = base.ancestors.map {|klass| klass.to_s}
# Checks to see this class or any of it's superclasses inherit from
# ActiveRecord::Base and if so includes ActiveRecordPersistence
def self.set_persistence(base)
# Use a fancier auto-loading thingy, perhaps. When there are more persistence engines.
hierarchy = base.ancestors.map {|klass| klass.to_s}
if hierarchy.include?("ActiveRecord::Base")
require File.join(File.dirname(__FILE__), 'persistence', 'active_record_persistence')
base.send(:include, AASM::Persistence::ActiveRecordPersistence)
if hierarchy.include?("ActiveRecord::Base")
require File.join(File.dirname(__FILE__), 'persistence', 'active_record_persistence')
base.send(:include, AASM::Persistence::ActiveRecordPersistence)
end
end
end
end
end # AASM

View File

@ -1,53 +0,0 @@
class AASM::SupportingClasses::State
attr_reader :name, :options
def initialize(name, options={})
@name = name
update(options)
end
def ==(state)
if state.is_a? Symbol
name == state
else
name == state.name
end
end
def call_action(action, record)
action = @options[action]
catch :halt_aasm_chain do
action.is_a?(Array) ?
action.each {|a| _call_action(a, record)} :
_call_action(action, record)
end
end
def display_name
@display_name ||= name.to_s.gsub(/_/, ' ').capitalize
end
def for_select
[display_name, name.to_s]
end
def update(options = {})
if options.key?(:display) then
@display_name = options.delete(:display)
end
@options = options
self
end
private
def _call_action(action, record)
case action
when Symbol, String
record.send(action)
when Proc
action.call(record)
end
end
end

View File

@ -1,31 +1,33 @@
class AASM::StateMachine
def self.[](clazz)
(@machines ||= {})[clazz.to_s]
end
module AASM
class StateMachine
def self.[](clazz)
(@machines ||= {})[clazz.to_s]
end
def self.[]=(clazz, machine)
(@machines ||= {})[clazz.to_s] = machine
end
def self.[]=(clazz, machine)
(@machines ||= {})[clazz.to_s] = machine
end
attr_accessor :states, :events, :initial_state, :config
attr_reader :name
attr_accessor :states, :events, :initial_state, :config
attr_reader :name
def initialize(name)
@name = name
@initial_state = nil
@states = []
@events = {}
@config = OpenStruct.new
end
def initialize(name)
@name = name
@initial_state = nil
@states = []
@events = {}
@config = OpenStruct.new
end
def clone
klone = super
klone.states = states.clone
klone.events = events.clone
klone
end
def clone
klone = super
klone.states = states.clone
klone.events = events.clone
klone
end
def create_state(name, options)
@states << AASM::SupportingClasses::State.new(name, options) unless @states.include?(name)
def create_state(name, options)
@states << AASM::SupportingClasses::State.new(name, options) unless @states.include?(name)
end
end
end
end # AASM

View File

@ -1,46 +0,0 @@
class AASM::SupportingClasses::StateTransition
attr_reader :from, :to, :opts
alias_method :options, :opts
def initialize(opts)
@from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
@opts = opts
end
def perform(obj, *args)
case @guard
when Symbol, String
obj.send(@guard, *args)
when Proc
@guard.call(obj, *args)
else
true
end
end
def execute(obj, *args)
@on_transition.is_a?(Array) ?
@on_transition.each {|ot| _execute(obj, ot, *args)} :
_execute(obj, @on_transition, *args)
end
def ==(obj)
@from == obj.from && @to == obj.to
end
def from?(value)
@from == value
end
private
def _execute(obj, on_transition, *args)
case on_transition
when Proc
on_transition.arity == 0 ? on_transition.call : on_transition.call(obj, *args)
when Symbol, String
obj.send(:method, on_transition.to_sym).arity == 0 ? obj.send(on_transition) : obj.send(on_transition, *args)
end
end
end

View File

@ -1,7 +0,0 @@
module AASM::SupportingClasses
end
require File.join(File.dirname(__FILE__), 'base')
require File.join(File.dirname(__FILE__), 'state_transition')
require File.join(File.dirname(__FILE__), 'event')
require File.join(File.dirname(__FILE__), 'state')

View File

@ -0,0 +1,131 @@
module AASM
module SupportingClasses
class Event
attr_reader :name, :success, :options
def initialize(name, options = {}, &block)
@name = name
@transitions = []
update(options, &block)
end
# a neutered version of fire - it doesn't actually fire the event, it just
# executes the transition guards to determine if a transition is even
# an option given current conditions.
def may_fire?(obj, to_state=nil)
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
return false if transitions.size == 0
result = false
transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state)
if transition.perform(obj)
result = true
break
end
end
result
end
def fire(obj, to_state=nil, *args)
transitions = @transitions.select { |t| t.from == obj.aasm_current_state }
raise AASM::InvalidTransition, "Event '#{name}' cannot transition from '#{obj.aasm_current_state}'" if transitions.size == 0
next_state = nil
transitions.each do |transition|
next if to_state and !Array(transition.to).include?(to_state)
if transition.perform(obj, *args)
next_state = to_state || Array(transition.to).first
transition.execute(obj, *args)
break
end
end
next_state
end
def transitions_from_state?(state)
@transitions.any? { |t| t.from == state }
end
def transitions_from_state(state)
@transitions.select { |t| t.from == state }
end
def all_transitions
@transitions
end
def call_action(action, record)
action = @options[action]
action.is_a?(Array) ?
action.each {|a| _call_action(a, record)} :
_call_action(action, record)
end
def ==(event)
if event.is_a? Symbol
name == event
else
name == event.name
end
end
def update(options = {}, &block)
if options.key?(:success) then
@success = options[:success]
end
if options.key?(:error) then
@error = options[:error]
end
if block then
instance_eval(&block)
end
@options = options
self
end
def execute_success_callback(obj, success = nil)
callback = success || @success
case(callback)
when String, Symbol
obj.send(callback)
when Proc
callback.call(obj)
when Array
callback.each{|meth|self.execute_success_callback(obj, meth)}
end
end
def execute_error_callback(obj, error, error_callback=nil)
callback = error_callback || @error
raise error unless callback
case(callback)
when String, Symbol
raise NoMethodError unless obj.respond_to?(callback.to_sym)
obj.send(callback, error)
when Proc
callback.call(obj, error)
when Array
callback.each{|meth|self.execute_error_callback(obj, error, meth)}
end
end
private
def _call_action(action, record)
case action
when Symbol, String
record.send(action)
when Proc
action.call(record)
end
end
def transitions(trans_opts)
Array(trans_opts[:from]).each do |s|
@transitions << AASM::SupportingClasses::StateTransition.new(trans_opts.merge({:from => s.to_sym}))
end
end
end
end # SupportingClasses
end # AASM

View File

@ -0,0 +1,57 @@
module AASM
module SupportingClasses
class State
attr_reader :name, :options
def initialize(name, options={})
@name = name
update(options)
end
def ==(state)
if state.is_a? Symbol
name == state
else
name == state.name
end
end
def call_action(action, record)
action = @options[action]
catch :halt_aasm_chain do
action.is_a?(Array) ?
action.each {|a| _call_action(a, record)} :
_call_action(action, record)
end
end
def display_name
@display_name ||= name.to_s.gsub(/_/, ' ').capitalize
end
def for_select
[display_name, name.to_s]
end
def update(options = {})
if options.key?(:display) then
@display_name = options.delete(:display)
end
@options = options
self
end
private
def _call_action(action, record)
case action
when Symbol, String
record.send(action)
when Proc
action.call(record)
end
end
end
end # SupportingClasses
end # AASM

View File

@ -0,0 +1,50 @@
module AASM
module SupportingClasses
class StateTransition
attr_reader :from, :to, :opts
alias_method :options, :opts
def initialize(opts)
@from, @to, @guard, @on_transition = opts[:from], opts[:to], opts[:guard], opts[:on_transition]
@opts = opts
end
def perform(obj, *args)
case @guard
when Symbol, String
obj.send(@guard, *args)
when Proc
@guard.call(obj, *args)
else
true
end
end
def execute(obj, *args)
@on_transition.is_a?(Array) ?
@on_transition.each {|ot| _execute(obj, ot, *args)} :
_execute(obj, @on_transition, *args)
end
def ==(obj)
@from == obj.from && @to == obj.to
end
def from?(value)
@from == value
end
private
def _execute(obj, on_transition, *args)
case on_transition
when Proc
on_transition.arity == 0 ? on_transition.call : on_transition.call(obj, *args)
when Symbol, String
obj.send(:method, on_transition.to_sym).arity == 0 ? obj.send(on_transition) : obj.send(on_transition, *args)
end
end
end
end # SupportingClasses
end # AASM