mirror of
https://github.com/aasm/aasm
synced 2023-03-27 23:22:41 -04:00
Support for object proxies for RubyMotion !
This commit is contained in:
parent
79f9178286
commit
3214e46f74
14 changed files with 105 additions and 142 deletions
97
README.md
97
README.md
|
@ -886,103 +886,6 @@ Now supports [CodeDataQuery](https://github.com/infinitered/cdq.git) !
|
|||
However I'm still in the process of submitting my compatibility updates to their repository.
|
||||
In the meantime you can use [my fork](https://github.com/Infotaku/cdq.git), there may still be some minor issues but I intend to extensively use it myself, so fixes should come fast.
|
||||
|
||||
AASM still has some issues with object proxies, so if you are using some libraries that put proxies in front of your objects, use
|
||||
`AASM::StateMachine.copy_over(self, :ProxyClass)` after having defined your states.
|
||||
ex :
|
||||
```ruby
|
||||
class WillBeProxiedByCDQ < CDQManagedObject; end
|
||||
o = WillBeProxiedByCDQ.new
|
||||
o.class
|
||||
=> WillBeProxiedByCDQ_WillBeProxiedByCDQ_
|
||||
|
||||
include BW::KVO # From BubbleWrap
|
||||
class WillBeProxiedByKVO
|
||||
attr_accessor :any
|
||||
end
|
||||
o = WillBeProxiedByKVO.new
|
||||
observe(o, [:any]) do; end
|
||||
o.class
|
||||
=> NSKVONotifying_WillBeProxiedByKVO
|
||||
```
|
||||
Any of these will break AASM if you include it to their class :
|
||||
```ruby
|
||||
class WillBeProxiedByCDQ < CDQManagedObject
|
||||
include AASM
|
||||
|
||||
aasm do
|
||||
state :state1, initial: true
|
||||
state :state2
|
||||
|
||||
event :change_state do
|
||||
transitions from: :state1, to: :state2
|
||||
end
|
||||
end
|
||||
end
|
||||
o = WillBeProxiedByCDQ.new
|
||||
=> Error
|
||||
|
||||
include BW::KVO # From BubbleWrap
|
||||
class WillBeProxiedByKVO
|
||||
include AASM
|
||||
|
||||
attr_accessor :any
|
||||
|
||||
aasm do
|
||||
state :state1, initial: true
|
||||
state :state2
|
||||
|
||||
event :change_state do
|
||||
transitions from: :state1, to: :state2
|
||||
end
|
||||
end
|
||||
end
|
||||
o = WillBeProxiedByKVO.new
|
||||
observe(o, [:any]) do; end
|
||||
o.state1?
|
||||
=> Error
|
||||
```
|
||||
Fix it by doing :
|
||||
```ruby
|
||||
class WillBeProxiedByCDQ < CDQManagedObject
|
||||
include AASM
|
||||
|
||||
aasm do
|
||||
state :state1, initial: true
|
||||
state :state2
|
||||
|
||||
event :change_state do
|
||||
transitions from: :state1, to: :state2
|
||||
end
|
||||
end
|
||||
AASM::StateMachine.copy_over(self, :WillBeProxiedByCDQ_WillBeProxiedByCDQ_)
|
||||
end
|
||||
o = WillBeProxiedByCDQ.new
|
||||
o.state1?
|
||||
=> true
|
||||
|
||||
include BW::KVO # From BubbleWrap
|
||||
class WillBeProxiedByKVO
|
||||
include AASM
|
||||
|
||||
attr_accessor :any
|
||||
|
||||
aasm do
|
||||
state :state1, initial: true
|
||||
state :state2
|
||||
|
||||
event :change_state do
|
||||
transitions from: :state1, to: :state2
|
||||
end
|
||||
end
|
||||
AASM::StateMachine.copy_over(self, :NSKVONotifying_WillBeProxiedByKVO)
|
||||
end
|
||||
o = WillBeProxiedByKVO.new
|
||||
observe(o, [:any]) do; end
|
||||
o.state1?
|
||||
=> true
|
||||
```
|
||||
I'm working on a fix to remove all of that, but it does the trick for now.
|
||||
|
||||
Warnings:
|
||||
- Due to RubyMotion Proc's lack of 'source_location' method, it may be harder
|
||||
to find out the origin of a "cannot transition from" error. I would recommend using
|
||||
|
|
|
@ -10,6 +10,7 @@ require 'aasm/core/transition'
|
|||
require 'aasm/core/event'
|
||||
require 'aasm/core/state'
|
||||
require 'aasm/localizer'
|
||||
require 'aasm/state_machine_store'
|
||||
require 'aasm/state_machine'
|
||||
require 'aasm/persistence'
|
||||
require 'aasm/persistence/base'
|
||||
|
|
|
@ -8,7 +8,7 @@ module AASM
|
|||
|
||||
# do not overwrite existing state machines, which could have been created by
|
||||
# inheritance, see class method inherited
|
||||
AASM::StateMachine[base] ||= {}
|
||||
AASM::StateMachineStore.register(base)
|
||||
|
||||
AASM::Persistence.load_persistence(base)
|
||||
super
|
||||
|
@ -17,7 +17,8 @@ module AASM
|
|||
module ClassMethods
|
||||
# make sure inheritance (aka subclassing) works with AASM
|
||||
def inherited(base)
|
||||
AASM::StateMachine.copy_over(self, base)
|
||||
AASM::StateMachineStore.register(base, self)
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
|
@ -33,7 +34,7 @@ module AASM
|
|||
options = args[0] || {}
|
||||
end
|
||||
|
||||
AASM::StateMachine[self][state_machine_name] ||= AASM::StateMachine.new(state_machine_name)
|
||||
AASM::StateMachineStore.fetch(self, true).register(state_machine_name, AASM::StateMachine.new(state_machine_name))
|
||||
|
||||
# use a default despite the DSL configuration default.
|
||||
# this is because configuration hasn't been setup for the AASM class but we are accessing a DSL option already for the class.
|
||||
|
@ -52,7 +53,7 @@ module AASM
|
|||
@aasm[state_machine_name] = aasm_klass.new(
|
||||
self,
|
||||
state_machine_name,
|
||||
AASM::StateMachine[self][state_machine_name],
|
||||
AASM::StateMachineStore.fetch(self, true).machine(state_machine_name),
|
||||
options
|
||||
)
|
||||
end
|
||||
|
@ -63,7 +64,7 @@ module AASM
|
|||
|
||||
# this is the entry point for all instance-level access to AASM
|
||||
def aasm(name=:default)
|
||||
unless AASM::StateMachine[self.class][name.to_sym]
|
||||
unless AASM::StateMachineStore.fetch(self.class, true).machine(name)
|
||||
raise AASM::UnknownStateMachineError.new("There is no state machine with the name '#{name}' defined in #{self.class.name}!")
|
||||
end
|
||||
@aasm ||= {}
|
||||
|
@ -180,7 +181,7 @@ private
|
|||
self.aasm_event_failed(event_name, old_state.name)
|
||||
end
|
||||
|
||||
if AASM::StateMachine[self.class][state_machine_name].config.whiny_transitions
|
||||
if AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.whiny_transitions
|
||||
raise AASM::InvalidTransition.new(self, event_name, state_machine_name, failures)
|
||||
else
|
||||
false
|
||||
|
|
|
@ -91,11 +91,11 @@ module AASM
|
|||
end
|
||||
|
||||
def aasm_enum(name=:default)
|
||||
case AASM::StateMachine[self.class][name].config.enum
|
||||
case AASM::StateMachineStore.fetch(self.class, true).machine(name).config.enum
|
||||
when false then nil
|
||||
when true then aasm_guess_enum_method(name)
|
||||
when nil then aasm_guess_enum_method(name) if aasm_column_looks_like_enum(name)
|
||||
else AASM::StateMachine[self.class][name].config.enum
|
||||
else AASM::StateMachineStore.fetch(self.class, true).machine(name).config.enum
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -111,7 +111,7 @@ module AASM
|
|||
end
|
||||
|
||||
def aasm_skipping_validations(state_machine_name)
|
||||
AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.skip_validation_on_save
|
||||
end
|
||||
|
||||
def aasm_write_attribute(state, name=:default)
|
||||
|
@ -140,7 +140,7 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
||||
# checking via respond_to? does not work in Rails <= 3
|
||||
# if respond_to?(self.class.aasm(state_machine_name).attribute_name) && send(self.class.aasm(state_machine_name).attribute_name).blank? # Rails 4
|
||||
if aasm_column_is_blank?(state_machine_name)
|
||||
|
@ -187,18 +187,18 @@ module AASM
|
|||
end
|
||||
|
||||
def requires_new?(state_machine_name)
|
||||
AASM::StateMachine[self.class][state_machine_name].config.requires_new_transaction
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.requires_new_transaction
|
||||
end
|
||||
|
||||
def requires_lock?(state_machine_name)
|
||||
AASM::StateMachine[self.class][state_machine_name].config.requires_lock
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.requires_lock
|
||||
end
|
||||
|
||||
def aasm_validate_states
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
||||
unless aasm_skipping_validations(state_machine_name)
|
||||
if aasm_invalid_state?(state_machine_name)
|
||||
self.errors.add(AASM::StateMachine[self.class][state_machine_name].config.column , "is invalid")
|
||||
self.errors.add(AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.column , "is invalid")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,7 +69,7 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
||||
send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -14,7 +14,7 @@ module AASM
|
|||
#
|
||||
base.class_eval %Q(
|
||||
def method_missing(method_name, *arguments, &block)
|
||||
if (AASM::StateMachine[self.class].keys.map { |state_machine_name| self.class.aasm(state_machine_name).attribute_name.to_s + "=" }).include? method_name.to_s
|
||||
if (AASM::StateMachineStore.fetch(self.class, true).machine_names.map { |state_machine_name| self.class.aasm(state_machine_name).attribute_name.to_s + "=" }).include? method_name.to_s
|
||||
attribute_name = method_name.to_s.gsub("=", '')
|
||||
write_attribute(attribute_name.to_sym, *arguments)
|
||||
else
|
||||
|
@ -82,7 +82,7 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
||||
aasm(state_machine_name).enter_initial_state if send(self.class.aasm(state_machine_name).attribute_name).blank?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -85,11 +85,11 @@ module AASM
|
|||
|
||||
private
|
||||
def aasm_enum(name=:default)
|
||||
case AASM::StateMachine[self.class][name].config.enum
|
||||
case AASM::StateMachineStore.fetch(self.class, true).machine(name).config.enum
|
||||
when false then nil
|
||||
when true then aasm_guess_enum_method(name)
|
||||
when nil then aasm_guess_enum_method(name) if aasm_column_looks_like_enum(name)
|
||||
else AASM::StateMachine[self.class][name].config.enum
|
||||
else AASM::StateMachineStore.fetch(self.class, true).machine(name).config.enum
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -102,7 +102,7 @@ module AASM
|
|||
end
|
||||
|
||||
def aasm_skipping_validations(state_machine_name)
|
||||
AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.skip_validation_on_save
|
||||
end
|
||||
|
||||
def aasm_write_attribute(state, name=:default)
|
||||
|
@ -133,17 +133,17 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
||||
send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
|
||||
end
|
||||
end
|
||||
|
||||
def aasm_validate_states
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
||||
send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
|
||||
unless AASM::StateMachine[self.class][state_machine_name].config.skip_validation_on_save
|
||||
unless AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.skip_validation_on_save
|
||||
if aasm(state_machine_name).current_state && !aasm(state_machine_name).states.include?(aasm(state_machine_name).current_state)
|
||||
self.errors.add(AASM::StateMachine[self.class][state_machine_name].config.column , "is invalid")
|
||||
self.errors.add(AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.column , "is invalid")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -93,7 +93,7 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
||||
send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s) if send(self.class.aasm(state_machine_name).attribute_name).blank?
|
||||
end
|
||||
end
|
||||
|
|
|
@ -69,7 +69,7 @@ module AASM
|
|||
# foo.aasm_state # => nil
|
||||
#
|
||||
def aasm_ensure_initial_state
|
||||
AASM::StateMachine[self.class].keys.each do |state_machine_name|
|
||||
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
||||
aasm(state_machine_name).enter_initial_state if
|
||||
(new? || values.key?(self.class.aasm(state_machine_name).attribute_name)) &&
|
||||
send(self.class.aasm(state_machine_name).attribute_name).to_s.strip.empty?
|
||||
|
|
|
@ -1,21 +1,6 @@
|
|||
module AASM
|
||||
class StateMachine
|
||||
|
||||
# the following two methods provide the storage of all state machines
|
||||
def self.[](klass)
|
||||
(@machines ||= {})[klass.to_s]
|
||||
end
|
||||
|
||||
def self.[]=(klass, machine)
|
||||
(@machines ||= {})[klass.to_s] = machine
|
||||
end
|
||||
|
||||
def self.copy_over(klass, alias_klass)
|
||||
AASM::StateMachine[alias_klass] = {}
|
||||
AASM::StateMachine[klass].keys.each do |state_machine_name|
|
||||
AASM::StateMachine[alias_klass][state_machine_name] = AASM::StateMachine[klass][state_machine_name].clone
|
||||
end
|
||||
end
|
||||
# the following four methods provide the storage of all state machines
|
||||
|
||||
attr_accessor :states, :events, :initial_state, :config, :name, :global_callbacks
|
||||
|
||||
|
|
73
lib/aasm/state_machine_store.rb
Normal file
73
lib/aasm/state_machine_store.rb
Normal file
|
@ -0,0 +1,73 @@
|
|||
module AASM
|
||||
class StateMachineStore
|
||||
class << self
|
||||
def stores
|
||||
@stores ||= {}
|
||||
end
|
||||
|
||||
# do not overwrite existing state machines, which could have been created by
|
||||
# inheritance, see AASM::ClassMethods method inherited
|
||||
def register(klass, overwrite = false, state_machine = nil)
|
||||
raise "Cannot register #{klass}" unless klass.is_a?(Class)
|
||||
|
||||
case name = template = overwrite
|
||||
when FalseClass then stores[klass.to_s] ||= new
|
||||
when TrueClass then stores[klass.to_s] = new
|
||||
when Class then stores[klass.to_s] = stores[template.to_s].clone
|
||||
when Symbol then stores[klass.to_s].register(name, state_machine)
|
||||
when String then stores[klass.to_s].register(name, state_machine)
|
||||
else raise "Don't know what to do with #{overwrite}"
|
||||
end
|
||||
end
|
||||
alias_method :[]=, :register
|
||||
|
||||
def fetch(klass, fallback = nil)
|
||||
stores[klass.to_s] || fallback && begin
|
||||
match = klass.ancestors.find do |ancestor|
|
||||
ancestor.include? AASM and stores[ancestor.to_s]
|
||||
end
|
||||
|
||||
stores[match.to_s]
|
||||
end
|
||||
end
|
||||
alias_method :[], :fetch
|
||||
|
||||
def unregister(klass)
|
||||
stores.delete(klass.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
def initialize
|
||||
@machines = {}
|
||||
end
|
||||
|
||||
def clone
|
||||
StateMachineStore.new.tap do |store|
|
||||
@machines.each_pair do |name, machine|
|
||||
store.register(name, machine.clone)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def machine(name)
|
||||
@machines[name.to_s]
|
||||
end
|
||||
alias_method :[], :machine
|
||||
|
||||
def machine_names
|
||||
@machines.keys
|
||||
end
|
||||
alias_method :keys, :machine_names
|
||||
|
||||
def register(name, machine, force = false)
|
||||
raise "Cannot use #{name.inspect} for machine name" unless name.is_a?(Symbol) or name.is_a?(String)
|
||||
raise "Cannot use #{machine.inspect} as a machine" unless machine.is_a?(AASM::StateMachine)
|
||||
|
||||
if force
|
||||
@machines[name.to_s] = machine
|
||||
else
|
||||
@machines[name.to_s] ||= machine
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -7,7 +7,7 @@
|
|||
# end
|
||||
|
||||
# def machines
|
||||
# AASM::StateMachine.instance_variable_get("@machines")
|
||||
# AASM::StateMachineStore.instance_variable_get("@stores")
|
||||
# end
|
||||
|
||||
# it "should be created without memory leak" do
|
||||
|
|
|
@ -452,7 +452,7 @@ describe 'transitions with persistence' do
|
|||
|
||||
it "should only rollback changes in the main transaction not the nested one" do
|
||||
# change configuration to not require new transaction
|
||||
AASM::StateMachine[MultipleTransactor][:left].config.requires_new_transaction = false
|
||||
AASM::StateMachineStore[MultipleTransactor][:left].config.requires_new_transaction = false
|
||||
|
||||
expect(transactor).to be_sleeping
|
||||
expect(worker.status).to eq('sleeping')
|
||||
|
|
|
@ -486,7 +486,7 @@ describe 'transitions with persistence' do
|
|||
|
||||
it "should only rollback changes in the main transaction not the nested one" do
|
||||
# change configuration to not require new transaction
|
||||
AASM::StateMachine[Transactor][:default].config.requires_new_transaction = false
|
||||
AASM::StateMachineStore[Transactor][:default].config.requires_new_transaction = false
|
||||
|
||||
expect(transactor).to be_sleeping
|
||||
expect(worker.status).to eq('sleeping')
|
||||
|
|
Loading…
Add table
Reference in a new issue