aasm 4.0.8 → 4.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +7 -2
- data/CHANGELOG.md +43 -2
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +6 -3
- data/LICENSE +1 -1
- data/PLANNED_CHANGES.md +8 -4
- data/README.md +171 -14
- data/aasm.gemspec +1 -5
- data/gemfiles/rails_3.2.gemfile +3 -2
- data/gemfiles/rails_4.0.gemfile +2 -6
- data/gemfiles/rails_4.0_mongo_mapper.gemfile +14 -0
- data/gemfiles/rails_4.1.gemfile +2 -6
- data/gemfiles/rails_4.1_mongo_mapper.gemfile +14 -0
- data/gemfiles/rails_4.2.gemfile +1 -5
- data/gemfiles/rails_4.2_mongo_mapper.gemfile +14 -0
- data/gemfiles/rails_4.2_mongoid_5.gemfile +12 -0
- data/lib/aasm/aasm.rb +69 -36
- data/lib/aasm/base.rb +51 -21
- data/lib/aasm/core/event.rb +7 -6
- data/lib/aasm/core/state.rb +11 -8
- data/lib/aasm/core/transition.rb +7 -5
- data/lib/aasm/errors.rb +15 -1
- data/lib/aasm/instance_base.rb +17 -13
- data/lib/aasm/localizer.rb +1 -1
- data/lib/aasm/persistence/active_record_persistence.rb +63 -73
- data/lib/aasm/persistence/base.rb +55 -20
- data/lib/aasm/persistence/mongo_mapper_persistence.rb +157 -0
- data/lib/aasm/persistence/mongoid_persistence.rb +16 -41
- data/lib/aasm/persistence/plain_persistence.rb +8 -7
- data/lib/aasm/persistence/sequel_persistence.rb +12 -9
- data/lib/aasm/persistence.rb +2 -0
- data/lib/aasm/rspec/allow_event.rb +22 -0
- data/lib/aasm/rspec/allow_transition_to.rb +22 -0
- data/lib/aasm/rspec/have_state.rb +22 -0
- data/lib/aasm/rspec/transition_from.rb +32 -0
- data/lib/aasm/rspec.rb +5 -0
- data/lib/aasm/state_machine.rb +20 -6
- data/lib/aasm/version.rb +1 -1
- data/spec/database.rb +27 -1
- data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +25 -0
- data/spec/models/active_record/complex_active_record_example.rb +33 -0
- data/spec/models/active_record/derivate_new_dsl.rb +7 -0
- data/spec/models/active_record/false_state.rb +35 -0
- data/spec/models/active_record/gate.rb +39 -0
- data/spec/models/active_record/localizer_test_model.rb +34 -0
- data/spec/models/active_record/no_direct_assignment.rb +21 -0
- data/spec/models/active_record/no_scope.rb +21 -0
- data/spec/models/active_record/persisted_state.rb +12 -0
- data/spec/models/active_record/provided_and_persisted_state.rb +24 -0
- data/spec/models/active_record/reader.rb +7 -0
- data/spec/models/active_record/readme_job.rb +21 -0
- data/spec/models/active_record/simple_new_dsl.rb +17 -0
- data/spec/models/active_record/thief.rb +29 -0
- data/spec/models/active_record/transient.rb +6 -0
- data/spec/models/active_record/with_enum.rb +39 -0
- data/spec/models/active_record/with_false_enum.rb +31 -0
- data/spec/models/active_record/with_true_enum.rb +39 -0
- data/spec/models/active_record/writer.rb +6 -0
- data/spec/models/basic_two_state_machines_example.rb +25 -0
- data/spec/models/callbacks/basic.rb +3 -0
- data/spec/models/callbacks/basic_multiple.rb +75 -0
- data/spec/models/callbacks/guard_within_block_multiple.rb +66 -0
- data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +65 -0
- data/spec/models/callbacks/private_method_multiple.rb +44 -0
- data/spec/models/callbacks/with_args.rb +9 -9
- data/spec/models/callbacks/with_args_multiple.rb +61 -0
- data/spec/models/callbacks/{with_state_args.rb → with_state_arg.rb} +1 -1
- data/spec/models/callbacks/with_state_arg_multiple.rb +26 -0
- data/spec/models/complex_example.rb +222 -0
- data/spec/models/conversation.rb +47 -1
- data/spec/models/default_state.rb +12 -0
- data/spec/models/foo.rb +57 -0
- data/spec/models/foo_callback_multiple.rb +45 -0
- data/spec/models/guardian.rb +10 -0
- data/spec/models/guardian_multiple.rb +48 -0
- data/spec/models/initial_state_proc.rb +31 -0
- data/spec/models/invalid_persistor.rb +15 -0
- data/spec/models/mongo_mapper/complex_mongo_mapper_example.rb +37 -0
- data/spec/models/mongo_mapper/no_scope_mongo_mapper.rb +21 -0
- data/spec/models/mongo_mapper/simple_mongo_mapper.rb +23 -0
- data/spec/models/mongo_mapper/simple_new_dsl_mongo_mapper.rb +25 -0
- data/spec/models/mongoid/complex_mongoid_example.rb +37 -0
- data/spec/models/mongoid/no_scope_mongoid.rb +11 -0
- data/spec/models/mongoid/simple_mongoid.rb +12 -0
- data/spec/models/mongoid/simple_new_dsl_mongoid.rb +14 -1
- data/spec/models/no_initial_state.rb +25 -0
- data/spec/models/parametrised_event.rb +1 -1
- data/spec/models/parametrised_event_multiple.rb +29 -0
- data/spec/models/provided_state.rb +24 -0
- data/spec/models/sequel/complex_sequel_example.rb +45 -0
- data/spec/models/sequel/sequel_multiple.rb +25 -0
- data/spec/models/sequel/sequel_simple.rb +25 -0
- data/spec/models/silencer.rb +5 -0
- data/spec/models/simple_example.rb +15 -0
- data/spec/models/simple_multiple_example.rb +30 -0
- data/spec/models/state_machine_with_failed_event.rb +12 -0
- data/spec/models/sub_class.rb +7 -0
- data/spec/models/sub_class_with_more_states.rb +18 -0
- data/spec/models/super_class.rb +46 -0
- data/spec/models/transactor.rb +27 -0
- data/spec/models/valid_state_name.rb +23 -0
- data/spec/models/validator.rb +47 -0
- data/spec/spec_helper.rb +4 -4
- data/spec/unit/api_spec.rb +6 -1
- data/spec/unit/basic_two_state_machines_example_spec.rb +10 -0
- data/spec/unit/callback_multiple_spec.rb +295 -0
- data/spec/unit/callbacks_spec.rb +3 -2
- data/spec/unit/complex_example_spec.rb +2 -2
- data/spec/unit/complex_multiple_example_spec.rb +99 -0
- data/spec/unit/edge_cases_spec.rb +16 -0
- data/spec/unit/event_multiple_spec.rb +73 -0
- data/spec/unit/event_naming_spec.rb +2 -15
- data/spec/unit/event_spec.rb +11 -6
- data/spec/unit/guard_multiple_spec.rb +60 -0
- data/spec/unit/guard_spec.rb +12 -0
- data/spec/unit/initial_state_multiple_spec.rb +15 -0
- data/spec/unit/initial_state_spec.rb +3 -18
- data/spec/unit/inspection_multiple_spec.rb +201 -0
- data/spec/unit/inspection_spec.rb +12 -7
- data/spec/unit/localizer_spec.rb +0 -38
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +573 -0
- data/spec/unit/persistence/active_record_persistence_spec.rb +70 -13
- data/spec/unit/persistence/mongo_mapper_persistence_multiple_spec.rb +146 -0
- data/spec/unit/persistence/mongo_mapper_persistence_spec.rb +93 -0
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +127 -0
- data/spec/unit/persistence/mongoid_persistence_spec.rb +79 -0
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +153 -0
- data/spec/unit/persistence/sequel_persistence_spec.rb +20 -23
- data/spec/unit/readme_spec.rb +42 -0
- data/spec/unit/reloading_spec.rb +1 -1
- data/spec/unit/rspec_matcher_spec.rb +79 -0
- data/spec/unit/simple_example_spec.rb +26 -42
- data/spec/unit/simple_multiple_example_spec.rb +63 -0
- data/spec/unit/state_spec.rb +3 -1
- data/spec/unit/subclassing_multiple_spec.rb +39 -0
- data/spec/unit/subclassing_spec.rb +10 -10
- data/spec/unit/transition_spec.rb +39 -25
- metadata +152 -23
- data/spec/models/active_record/api.rb +0 -75
- data/spec/models/argument.rb +0 -11
- data/spec/models/auth_machine.rb +0 -88
- data/spec/models/bar.rb +0 -15
- data/spec/models/father.rb +0 -21
- data/spec/models/persistence.rb +0 -164
- data/spec/models/son.rb +0 -3
- data/spec/unit/persistence/mongoid_persistance_spec.rb +0 -152
data/lib/aasm/aasm.rb
CHANGED
|
@@ -8,36 +8,63 @@ module AASM
|
|
|
8
8
|
|
|
9
9
|
# do not overwrite existing state machines, which could have been created by
|
|
10
10
|
# inheritance, see class method inherited
|
|
11
|
-
AASM::StateMachine[base] ||=
|
|
11
|
+
AASM::StateMachine[base] ||= {}
|
|
12
12
|
|
|
13
13
|
AASM::Persistence.load_persistence(base)
|
|
14
14
|
super
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
module ClassMethods
|
|
18
|
-
|
|
19
18
|
# make sure inheritance (aka subclassing) works with AASM
|
|
20
19
|
def inherited(base)
|
|
21
|
-
AASM::StateMachine[base] =
|
|
20
|
+
AASM::StateMachine[base] = {}
|
|
21
|
+
AASM::StateMachine[self].keys.each do |state_machine_name|
|
|
22
|
+
AASM::StateMachine[base][state_machine_name] = AASM::StateMachine[self][state_machine_name].clone
|
|
23
|
+
end
|
|
22
24
|
super
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
# this is the entry point for all state and event definitions
|
|
26
|
-
def aasm(
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
28
|
+
def aasm(*args, &block)
|
|
29
|
+
if args[0].is_a?(Symbol) || args[0].is_a?(String)
|
|
30
|
+
# using custom name
|
|
31
|
+
state_machine_name = args[0].to_sym
|
|
32
|
+
options = args[1] || {}
|
|
33
|
+
else
|
|
34
|
+
# using the default state_machine_name
|
|
35
|
+
state_machine_name = :default
|
|
36
|
+
options = args[0] || {}
|
|
37
|
+
end
|
|
31
38
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
aasm
|
|
39
|
+
AASM::StateMachine[self][state_machine_name] ||= AASM::StateMachine.new(state_machine_name)
|
|
40
|
+
|
|
41
|
+
@aasm ||= {}
|
|
42
|
+
if @aasm[state_machine_name]
|
|
43
|
+
# make sure to use provided options
|
|
44
|
+
options.each do |key, value|
|
|
45
|
+
@aasm[state_machine_name].state_machine.config.send("#{key}=", value)
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
# create a new base
|
|
49
|
+
@aasm[state_machine_name] = AASM::Base.new(
|
|
50
|
+
self,
|
|
51
|
+
state_machine_name,
|
|
52
|
+
AASM::StateMachine[self][state_machine_name],
|
|
53
|
+
options
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
@aasm[state_machine_name].instance_eval(&block) if block # new DSL
|
|
57
|
+
@aasm[state_machine_name]
|
|
36
58
|
end
|
|
37
59
|
end # ClassMethods
|
|
38
60
|
|
|
39
|
-
|
|
40
|
-
|
|
61
|
+
# this is the entry point for all instance-level access to AASM
|
|
62
|
+
def aasm(name=:default)
|
|
63
|
+
unless AASM::StateMachine[self.class][name.to_sym]
|
|
64
|
+
raise AASM::UnknownStateMachineError.new("There is no state machine with the name '#{name}' defined in #{self.class.name}!")
|
|
65
|
+
end
|
|
66
|
+
@aasm ||= {}
|
|
67
|
+
@aasm[name.to_sym] ||= AASM::InstanceBase.new(self, name.to_sym)
|
|
41
68
|
end
|
|
42
69
|
|
|
43
70
|
private
|
|
@@ -56,66 +83,72 @@ private
|
|
|
56
83
|
return args
|
|
57
84
|
end
|
|
58
85
|
|
|
59
|
-
def aasm_fire_event(event_name, options, *args, &block)
|
|
60
|
-
event = self.class.aasm.state_machine.events[event_name]
|
|
86
|
+
def aasm_fire_event(state_machine_name, event_name, options, *args, &block)
|
|
87
|
+
event = self.class.aasm(state_machine_name).state_machine.events[event_name]
|
|
61
88
|
begin
|
|
62
|
-
old_state = aasm.state_object_for_name(aasm.current_state)
|
|
89
|
+
old_state = aasm(state_machine_name).state_object_for_name(aasm(state_machine_name).current_state)
|
|
63
90
|
|
|
64
91
|
# new event before callback
|
|
65
92
|
event.fire_callbacks(
|
|
66
93
|
:before,
|
|
67
94
|
self,
|
|
68
|
-
*process_args(event, aasm.current_state, *args)
|
|
95
|
+
*process_args(event, aasm(state_machine_name).current_state, *args)
|
|
69
96
|
)
|
|
70
97
|
|
|
71
98
|
if may_fire_to = event.may_fire?(self, *args)
|
|
72
|
-
old_state.fire_callbacks(:before_exit, self
|
|
73
|
-
|
|
99
|
+
old_state.fire_callbacks(:before_exit, self,
|
|
100
|
+
*process_args(event, aasm(state_machine_name).current_state, *args))
|
|
101
|
+
old_state.fire_callbacks(:exit, self,
|
|
102
|
+
*process_args(event, aasm(state_machine_name).current_state, *args))
|
|
74
103
|
|
|
75
104
|
if new_state_name = event.fire(self, {:may_fire => may_fire_to}, *args)
|
|
76
|
-
aasm_fired(event, old_state, new_state_name, options, *args, &block)
|
|
105
|
+
aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args, &block)
|
|
77
106
|
else
|
|
78
|
-
aasm_failed(event_name, old_state)
|
|
107
|
+
aasm_failed(state_machine_name, event_name, old_state)
|
|
79
108
|
end
|
|
80
109
|
else
|
|
81
|
-
aasm_failed(event_name, old_state)
|
|
110
|
+
aasm_failed(state_machine_name, event_name, old_state)
|
|
82
111
|
end
|
|
83
112
|
rescue StandardError => e
|
|
84
|
-
event.fire_callbacks(:error, self, e, *process_args(event, aasm.current_state, *args)) || raise(e)
|
|
113
|
+
event.fire_callbacks(:error, self, e, *process_args(event, aasm(state_machine_name).current_state, *args)) || raise(e)
|
|
85
114
|
end
|
|
86
115
|
end
|
|
87
116
|
|
|
88
|
-
def aasm_fired(event, old_state, new_state_name, options, *args)
|
|
117
|
+
def aasm_fired(state_machine_name, event, old_state, new_state_name, options, *args)
|
|
89
118
|
persist = options[:persist]
|
|
90
119
|
|
|
91
|
-
new_state = aasm.state_object_for_name(new_state_name)
|
|
120
|
+
new_state = aasm(state_machine_name).state_object_for_name(new_state_name)
|
|
92
121
|
|
|
93
|
-
new_state.fire_callbacks(:before_enter, self
|
|
122
|
+
new_state.fire_callbacks(:before_enter, self,
|
|
123
|
+
*process_args(event, aasm(state_machine_name).current_state, *args))
|
|
94
124
|
|
|
95
|
-
new_state.fire_callbacks(:enter, self
|
|
125
|
+
new_state.fire_callbacks(:enter, self,
|
|
126
|
+
*process_args(event, aasm(state_machine_name).current_state, *args)) # TODO: remove for AASM 4?
|
|
96
127
|
|
|
97
128
|
persist_successful = true
|
|
98
129
|
if persist
|
|
99
|
-
persist_successful = aasm.set_current_state_with_persistence(new_state_name)
|
|
130
|
+
persist_successful = aasm(state_machine_name).set_current_state_with_persistence(new_state_name)
|
|
100
131
|
if persist_successful
|
|
101
132
|
yield if block_given?
|
|
102
133
|
event.fire_callbacks(:success, self)
|
|
103
134
|
end
|
|
104
135
|
else
|
|
105
|
-
aasm.current_state = new_state_name
|
|
136
|
+
aasm(state_machine_name).current_state = new_state_name
|
|
106
137
|
yield if block_given?
|
|
107
138
|
end
|
|
108
139
|
|
|
109
140
|
if persist_successful
|
|
110
|
-
old_state.fire_callbacks(:after_exit, self
|
|
111
|
-
|
|
141
|
+
old_state.fire_callbacks(:after_exit, self,
|
|
142
|
+
*process_args(event, aasm(state_machine_name).current_state, *args))
|
|
143
|
+
new_state.fire_callbacks(:after_enter, self,
|
|
144
|
+
*process_args(event, aasm(state_machine_name).current_state, *args))
|
|
112
145
|
event.fire_callbacks(
|
|
113
146
|
:after,
|
|
114
147
|
self,
|
|
115
148
|
*process_args(event, old_state.name, *args)
|
|
116
149
|
)
|
|
117
150
|
|
|
118
|
-
self.aasm_event_fired(event.name, old_state.name, aasm.current_state) if self.respond_to?(:aasm_event_fired)
|
|
151
|
+
self.aasm_event_fired(event.name, old_state.name, aasm(state_machine_name).current_state) if self.respond_to?(:aasm_event_fired)
|
|
119
152
|
else
|
|
120
153
|
self.aasm_event_failed(event.name, old_state.name) if self.respond_to?(:aasm_event_failed)
|
|
121
154
|
end
|
|
@@ -123,13 +156,13 @@ private
|
|
|
123
156
|
persist_successful
|
|
124
157
|
end
|
|
125
158
|
|
|
126
|
-
def aasm_failed(event_name, old_state)
|
|
159
|
+
def aasm_failed(state_machine_name, event_name, old_state)
|
|
127
160
|
if self.respond_to?(:aasm_event_failed)
|
|
128
161
|
self.aasm_event_failed(event_name, old_state.name)
|
|
129
162
|
end
|
|
130
163
|
|
|
131
|
-
if AASM::StateMachine[self.class].config.whiny_transitions
|
|
132
|
-
raise AASM::InvalidTransition,
|
|
164
|
+
if AASM::StateMachine[self.class][state_machine_name].config.whiny_transitions
|
|
165
|
+
raise AASM::InvalidTransition.new(self, event_name, state_machine_name)
|
|
133
166
|
else
|
|
134
167
|
false
|
|
135
168
|
end
|
data/lib/aasm/base.rb
CHANGED
|
@@ -3,10 +3,12 @@ module AASM
|
|
|
3
3
|
|
|
4
4
|
attr_reader :state_machine
|
|
5
5
|
|
|
6
|
-
def initialize(klass, options={}, &block)
|
|
6
|
+
def initialize(klass, name, state_machine, options={}, &block)
|
|
7
7
|
@klass = klass
|
|
8
|
-
@
|
|
9
|
-
@state_machine
|
|
8
|
+
@name = name
|
|
9
|
+
# @state_machine = @klass.aasm(@name).state_machine
|
|
10
|
+
@state_machine = state_machine
|
|
11
|
+
@state_machine.config.column ||= (options[:column] || default_column).to_sym
|
|
10
12
|
# @state_machine.config.column = options[:column].to_sym if options[:column] # master
|
|
11
13
|
@options = options
|
|
12
14
|
|
|
@@ -27,11 +29,19 @@ module AASM
|
|
|
27
29
|
|
|
28
30
|
configure :enum, nil
|
|
29
31
|
|
|
30
|
-
if
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
# make sure to raise an error if no_direct_assignment is enabled
|
|
33
|
+
# and attribute is directly assigned though
|
|
34
|
+
@klass.class_eval %Q(
|
|
35
|
+
def #{@state_machine.config.column}=(state_name)
|
|
36
|
+
if self.class.aasm(:#{@name}).state_machine.config.no_direct_assignment
|
|
37
|
+
raise AASM::NoDirectAssignmentError.new(
|
|
38
|
+
'direct assignment of AASM column has been disabled (see AASM configuration for this class)'
|
|
39
|
+
)
|
|
40
|
+
else
|
|
41
|
+
super
|
|
42
|
+
end
|
|
33
43
|
end
|
|
34
|
-
|
|
44
|
+
)
|
|
35
45
|
end
|
|
36
46
|
|
|
37
47
|
# This method is both a getter and a setter
|
|
@@ -56,10 +66,16 @@ module AASM
|
|
|
56
66
|
def state(name, options={})
|
|
57
67
|
@state_machine.add_state(name, @klass, options)
|
|
58
68
|
|
|
59
|
-
@klass.
|
|
60
|
-
|
|
69
|
+
if @klass.instance_methods.include?("#{name}?")
|
|
70
|
+
warn "#{@klass.name}: The aasm state name #{name} is already used!"
|
|
61
71
|
end
|
|
62
72
|
|
|
73
|
+
@klass.class_eval <<-EORUBY, __FILE__, __LINE__ + 1
|
|
74
|
+
def #{name}?
|
|
75
|
+
aasm(:#{@name}).current_state == :#{name}
|
|
76
|
+
end
|
|
77
|
+
EORUBY
|
|
78
|
+
|
|
63
79
|
unless @klass.const_defined?("STATE_#{name.upcase}")
|
|
64
80
|
@klass.const_set("STATE_#{name.upcase}", name)
|
|
65
81
|
end
|
|
@@ -67,24 +83,34 @@ module AASM
|
|
|
67
83
|
|
|
68
84
|
# define an event
|
|
69
85
|
def event(name, options={}, &block)
|
|
70
|
-
@state_machine.
|
|
86
|
+
@state_machine.add_event(name, options, &block)
|
|
87
|
+
|
|
88
|
+
if @klass.instance_methods.include?("may_#{name}?".to_sym)
|
|
89
|
+
warn "#{@klass.name}: The aasm event name #{name} is already used!"
|
|
90
|
+
end
|
|
71
91
|
|
|
72
92
|
# an addition over standard aasm so that, before firing an event, you can ask
|
|
73
93
|
# may_event? and get back a boolean that tells you whether the guard method
|
|
74
94
|
# on the transition will let this happen.
|
|
75
|
-
@klass.
|
|
76
|
-
|
|
77
|
-
|
|
95
|
+
@klass.class_eval <<-EORUBY, __FILE__, __LINE__ + 1
|
|
96
|
+
def may_#{name}?(*args)
|
|
97
|
+
aasm(:#{@name}).may_fire_event?(:#{name}, *args)
|
|
98
|
+
end
|
|
78
99
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
100
|
+
def #{name}!(*args, &block)
|
|
101
|
+
aasm(:#{@name}).current_event = :#{name}!
|
|
102
|
+
aasm_fire_event(:#{@name}, :#{name}, {:persist => true}, *args, &block)
|
|
103
|
+
end
|
|
83
104
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
105
|
+
def #{name}(*args, &block)
|
|
106
|
+
aasm(:#{@name}).current_event = :#{name}
|
|
107
|
+
aasm_fire_event(:#{@name}, :#{name}, {:persist => false}, *args, &block)
|
|
108
|
+
end
|
|
109
|
+
EORUBY
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def after_all_transitions(*callbacks, &block)
|
|
113
|
+
@state_machine.add_global_callbacks(:after_all_transitions, *callbacks, &block)
|
|
88
114
|
end
|
|
89
115
|
|
|
90
116
|
def states
|
|
@@ -114,6 +140,10 @@ module AASM
|
|
|
114
140
|
|
|
115
141
|
private
|
|
116
142
|
|
|
143
|
+
def default_column
|
|
144
|
+
@name.to_sym == :default ? :aasm_state : @name.to_sym
|
|
145
|
+
end
|
|
146
|
+
|
|
117
147
|
def configure(key, default_value)
|
|
118
148
|
if @options.key?(key)
|
|
119
149
|
@state_machine.config.send("#{key}=", @options[key])
|
data/lib/aasm/core/event.rb
CHANGED
|
@@ -2,17 +2,18 @@ module AASM::Core
|
|
|
2
2
|
class Event
|
|
3
3
|
include DslHelper
|
|
4
4
|
|
|
5
|
-
attr_reader :name, :options
|
|
5
|
+
attr_reader :name, :state_machine, :options
|
|
6
6
|
|
|
7
|
-
def initialize(name, options = {}, &block)
|
|
7
|
+
def initialize(name, state_machine, options = {}, &block)
|
|
8
8
|
@name = name
|
|
9
|
+
@state_machine = state_machine
|
|
9
10
|
@transitions = []
|
|
10
11
|
@guards = Array(options[:guard] || options[:guards] || options[:if])
|
|
11
12
|
@unless = Array(options[:unless]) #TODO: This could use a better name
|
|
12
13
|
|
|
13
14
|
# from aasm4
|
|
14
15
|
@options = options # QUESTION: .dup ?
|
|
15
|
-
add_options_from_dsl(@options, [:after, :before, :error, :success], &block) if block
|
|
16
|
+
add_options_from_dsl(@options, [:after, :before, :error, :success, :after_commit], &block) if block
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
# a neutered version of fire - it doesn't actually fire the event, it just
|
|
@@ -61,11 +62,11 @@ module AASM::Core
|
|
|
61
62
|
if definitions # define new transitions
|
|
62
63
|
# Create a separate transition for each from-state to the given state
|
|
63
64
|
Array(definitions[:from]).each do |s|
|
|
64
|
-
@transitions << AASM::Core::Transition.new(attach_event_guards(definitions.merge(:from => s.to_sym)), &block)
|
|
65
|
+
@transitions << AASM::Core::Transition.new(self, attach_event_guards(definitions.merge(:from => s.to_sym)), &block)
|
|
65
66
|
end
|
|
66
67
|
# Create a transition if :to is specified without :from (transitions from ANY state)
|
|
67
68
|
if @transitions.empty? && definitions[:to]
|
|
68
|
-
@transitions << AASM::Core::Transition.new(attach_event_guards(definitions), &block)
|
|
69
|
+
@transitions << AASM::Core::Transition.new(self, attach_event_guards(definitions), &block)
|
|
69
70
|
end
|
|
70
71
|
end
|
|
71
72
|
@transitions
|
|
@@ -89,7 +90,7 @@ module AASM::Core
|
|
|
89
90
|
def _fire(obj, options={}, to_state=nil, *args)
|
|
90
91
|
result = options[:test_only] ? false : nil
|
|
91
92
|
if @transitions.map(&:from).any?
|
|
92
|
-
transitions = @transitions.select { |t| t.from == obj.aasm.current_state }
|
|
93
|
+
transitions = @transitions.select { |t| t.from == obj.aasm(state_machine.name).current_state }
|
|
93
94
|
return result if transitions.size == 0
|
|
94
95
|
else
|
|
95
96
|
transitions = @transitions
|
data/lib/aasm/core/state.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
module AASM::Core
|
|
2
2
|
class State
|
|
3
|
-
attr_reader :name, :options
|
|
3
|
+
attr_reader :name, :state_machine, :options
|
|
4
4
|
|
|
5
|
-
def initialize(name, klass, options={})
|
|
5
|
+
def initialize(name, klass, state_machine, options={})
|
|
6
6
|
@name = name
|
|
7
7
|
@klass = klass
|
|
8
|
+
@state_machine = state_machine
|
|
8
9
|
update(options)
|
|
9
10
|
end
|
|
10
11
|
|
|
@@ -28,12 +29,12 @@ module AASM::Core
|
|
|
28
29
|
name.to_s
|
|
29
30
|
end
|
|
30
31
|
|
|
31
|
-
def fire_callbacks(action, record)
|
|
32
|
+
def fire_callbacks(action, record, *args)
|
|
32
33
|
action = @options[action]
|
|
33
34
|
catch :halt_aasm_chain do
|
|
34
35
|
action.is_a?(Array) ?
|
|
35
|
-
action.each {|a| _fire_callbacks(a, record)} :
|
|
36
|
-
_fire_callbacks(action, record)
|
|
36
|
+
action.each {|a| _fire_callbacks(a, record, args)} :
|
|
37
|
+
_fire_callbacks(action, record, args)
|
|
37
38
|
end
|
|
38
39
|
end
|
|
39
40
|
|
|
@@ -66,12 +67,14 @@ module AASM::Core
|
|
|
66
67
|
self
|
|
67
68
|
end
|
|
68
69
|
|
|
69
|
-
def _fire_callbacks(action, record)
|
|
70
|
+
def _fire_callbacks(action, record, args)
|
|
70
71
|
case action
|
|
71
72
|
when Symbol, String
|
|
72
|
-
record.send(action)
|
|
73
|
+
arity = record.send(:method, action.to_sym).arity
|
|
74
|
+
record.send(action, *(arity < 0 ? args : args[0...arity]))
|
|
73
75
|
when Proc
|
|
74
|
-
action.
|
|
76
|
+
arity = action.arity
|
|
77
|
+
action.call(record, *(arity < 0 ? args : args[0...arity]))
|
|
75
78
|
end
|
|
76
79
|
end
|
|
77
80
|
|
data/lib/aasm/core/transition.rb
CHANGED
|
@@ -2,12 +2,13 @@ module AASM::Core
|
|
|
2
2
|
class Transition
|
|
3
3
|
include DslHelper
|
|
4
4
|
|
|
5
|
-
attr_reader :from, :to, :opts
|
|
5
|
+
attr_reader :from, :to, :event, :opts
|
|
6
6
|
alias_method :options, :opts
|
|
7
7
|
|
|
8
|
-
def initialize(opts, &block)
|
|
8
|
+
def initialize(event, opts, &block)
|
|
9
9
|
add_options_from_dsl(opts, [:on_transition, :guard, :after], &block) if block
|
|
10
10
|
|
|
11
|
+
@event = event
|
|
11
12
|
@from = opts[:from]
|
|
12
13
|
@to = opts[:to]
|
|
13
14
|
@guards = Array(opts[:guards]) + Array(opts[:guard]) + Array(opts[:if])
|
|
@@ -29,6 +30,7 @@ module AASM::Core
|
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def execute(obj, *args)
|
|
33
|
+
invoke_callbacks_compatible_with_guard(event.state_machine.global_callbacks[:after_all_transitions], obj, args)
|
|
32
34
|
invoke_callbacks_compatible_with_guard(@after, obj, args)
|
|
33
35
|
end
|
|
34
36
|
|
|
@@ -44,8 +46,8 @@ module AASM::Core
|
|
|
44
46
|
|
|
45
47
|
def invoke_callbacks_compatible_with_guard(code, record, args, options={})
|
|
46
48
|
if record.respond_to?(:aasm)
|
|
47
|
-
record.aasm.from_state = @from if record.aasm.respond_to?(:from_state=)
|
|
48
|
-
record.aasm.to_state = @to if record.aasm.respond_to?(:to_state=)
|
|
49
|
+
record.aasm(event.state_machine.name).from_state = @from if record.aasm(event.state_machine.name).respond_to?(:from_state=)
|
|
50
|
+
record.aasm(event.state_machine.name).to_state = @to if record.aasm(event.state_machine.name).respond_to?(:to_state=)
|
|
49
51
|
end
|
|
50
52
|
|
|
51
53
|
case code
|
|
@@ -53,7 +55,7 @@ module AASM::Core
|
|
|
53
55
|
arity = record.send(:method, code.to_sym).arity
|
|
54
56
|
arity == 0 ? record.send(code) : record.send(code, *args)
|
|
55
57
|
when Proc
|
|
56
|
-
code.
|
|
58
|
+
code.parameters.size == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code)
|
|
57
59
|
when Array
|
|
58
60
|
if options[:guard]
|
|
59
61
|
# invoke guard callbacks
|
data/lib/aasm/errors.rb
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
module AASM
|
|
2
|
-
|
|
2
|
+
|
|
3
|
+
class UnknownStateMachineError < RuntimeError; end
|
|
4
|
+
|
|
5
|
+
class InvalidTransition < RuntimeError
|
|
6
|
+
attr_reader :object, :event_name, :state_machine_name
|
|
7
|
+
|
|
8
|
+
def initialize(object, event_name, state_machine_name)
|
|
9
|
+
@object, @event_name, @state_machine_name = object, event_name, state_machine_name
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def message
|
|
13
|
+
"Event '#{event_name}' cannot transition from '#{object.aasm(state_machine_name).current_state}'"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
3
17
|
class UndefinedState < RuntimeError; end
|
|
4
18
|
class NoDirectAssignmentError < RuntimeError; end
|
|
5
19
|
end
|
data/lib/aasm/instance_base.rb
CHANGED
|
@@ -3,21 +3,22 @@ module AASM
|
|
|
3
3
|
|
|
4
4
|
attr_accessor :from_state, :to_state, :current_event
|
|
5
5
|
|
|
6
|
-
def initialize(instance)
|
|
6
|
+
def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
|
|
7
7
|
@instance = instance
|
|
8
|
+
@name = name
|
|
8
9
|
end
|
|
9
10
|
|
|
10
11
|
def current_state
|
|
11
|
-
@instance.aasm_read_state
|
|
12
|
+
@instance.aasm_read_state(@name)
|
|
12
13
|
end
|
|
13
14
|
|
|
14
15
|
def current_state=(state)
|
|
15
|
-
@instance.aasm_write_state_without_persistence(state)
|
|
16
|
-
@current_state = state
|
|
16
|
+
@instance.aasm_write_state_without_persistence(state, @name)
|
|
17
|
+
# @current_state = state
|
|
17
18
|
end
|
|
18
19
|
|
|
19
20
|
def enter_initial_state
|
|
20
|
-
state_name = determine_state_name(@instance.class.aasm.initial_state)
|
|
21
|
+
state_name = determine_state_name(@instance.class.aasm(@name).initial_state)
|
|
21
22
|
state_object = state_object_for_name(state_name)
|
|
22
23
|
|
|
23
24
|
state_object.fire_callbacks(:before_enter, @instance)
|
|
@@ -29,24 +30,27 @@ module AASM
|
|
|
29
30
|
end
|
|
30
31
|
|
|
31
32
|
def human_state
|
|
32
|
-
AASM::Localizer.new.human_state_name(@instance.class, current_state)
|
|
33
|
+
AASM::Localizer.new.human_state_name(@instance.class, state_object_for_name(current_state))
|
|
33
34
|
end
|
|
34
35
|
|
|
35
36
|
def states(options={})
|
|
36
37
|
if options[:permitted]
|
|
37
38
|
# ugliness level 1000
|
|
38
39
|
permitted_event_names = events(:permitted => true).map(&:name)
|
|
39
|
-
transitions = @instance.class.aasm.state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) }
|
|
40
|
+
transitions = @instance.class.aasm(@name).state_machine.events.values_at(*permitted_event_names).compact.map {|e| e.transitions_from_state(current_state) }
|
|
40
41
|
tos = transitions.map {|t| t[0] ? t[0].to : nil}.flatten.compact.map(&:to_sym).uniq
|
|
41
|
-
@instance.class.aasm.states.select {|s| tos.include?(s.name.to_sym)}
|
|
42
|
+
@instance.class.aasm(@name).states.select {|s| tos.include?(s.name.to_sym)}
|
|
42
43
|
else
|
|
43
|
-
@instance.class.aasm.states
|
|
44
|
+
@instance.class.aasm(@name).states
|
|
44
45
|
end
|
|
45
46
|
end
|
|
46
47
|
|
|
47
48
|
def events(options={})
|
|
48
49
|
state = options[:state] || current_state
|
|
49
|
-
events = @instance.class.aasm.events.select {|e| e.transitions_from_state?(state) }
|
|
50
|
+
events = @instance.class.aasm(@name).events.select {|e| e.transitions_from_state?(state) }
|
|
51
|
+
|
|
52
|
+
options[:reject] = Array(options[:reject])
|
|
53
|
+
events.reject! { |e| options[:reject].include?(e.name) }
|
|
50
54
|
|
|
51
55
|
if options[:permitted]
|
|
52
56
|
# filters the results of events_for_current_state so that only those that
|
|
@@ -58,7 +62,7 @@ module AASM
|
|
|
58
62
|
end
|
|
59
63
|
|
|
60
64
|
def state_object_for_name(name)
|
|
61
|
-
obj = @instance.class.aasm.states.find {|s| s == name}
|
|
65
|
+
obj = @instance.class.aasm(@name).states.find {|s| s.name == name}
|
|
62
66
|
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
|
|
63
67
|
obj
|
|
64
68
|
end
|
|
@@ -75,7 +79,7 @@ module AASM
|
|
|
75
79
|
end
|
|
76
80
|
|
|
77
81
|
def may_fire_event?(name, *args)
|
|
78
|
-
if event = @instance.class.aasm.state_machine.events[name]
|
|
82
|
+
if event = @instance.class.aasm(@name).state_machine.events[name]
|
|
79
83
|
!!event.may_fire?(@instance, *args)
|
|
80
84
|
else
|
|
81
85
|
false # unknown event
|
|
@@ -83,7 +87,7 @@ module AASM
|
|
|
83
87
|
end
|
|
84
88
|
|
|
85
89
|
def set_current_state_with_persistence(state)
|
|
86
|
-
save_success = @instance.aasm_write_state(state)
|
|
90
|
+
save_success = @instance.aasm_write_state(state, @name)
|
|
87
91
|
self.current_state = state if save_success
|
|
88
92
|
save_success
|
|
89
93
|
end
|
data/lib/aasm/localizer.rb
CHANGED
|
@@ -21,7 +21,7 @@ module AASM
|
|
|
21
21
|
|
|
22
22
|
def item_for(klass, state, ancestor, options={})
|
|
23
23
|
separator = options[:old_style] ? '.' : '/'
|
|
24
|
-
:"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm.attribute_name}#{separator}#{state}"
|
|
24
|
+
:"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm(state.state_machine.name).attribute_name}#{separator}#{state}"
|
|
25
25
|
end
|
|
26
26
|
|
|
27
27
|
def translate_queue(checklist)
|