aasm 4.12.2 → 5.5.0
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 +5 -5
- data/LICENSE +1 -1
- data/README.md +393 -116
- data/lib/aasm/aasm.rb +30 -27
- data/lib/aasm/base.rb +64 -11
- data/lib/aasm/configuration.rb +6 -0
- data/lib/aasm/core/event.rb +26 -30
- data/lib/aasm/core/invoker.rb +129 -0
- data/lib/aasm/core/invokers/base_invoker.rb +75 -0
- data/lib/aasm/core/invokers/class_invoker.rb +52 -0
- data/lib/aasm/core/invokers/literal_invoker.rb +49 -0
- data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
- data/lib/aasm/core/state.rb +16 -14
- data/lib/aasm/core/transition.rb +8 -69
- data/lib/aasm/dsl_helper.rb +24 -22
- data/lib/aasm/errors.rb +5 -3
- data/lib/aasm/instance_base.rb +34 -3
- data/lib/aasm/localizer.rb +13 -3
- data/lib/aasm/persistence/active_record_persistence.rb +25 -5
- data/lib/aasm/persistence/base.rb +14 -3
- data/lib/aasm/persistence/core_data_query_persistence.rb +2 -1
- data/lib/aasm/persistence/dynamoid_persistence.rb +1 -1
- data/lib/aasm/persistence/mongoid_persistence.rb +1 -1
- data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
- data/lib/aasm/persistence/orm.rb +26 -14
- data/lib/aasm/persistence/plain_persistence.rb +2 -1
- data/lib/aasm/persistence/redis_persistence.rb +1 -1
- data/lib/aasm/persistence/sequel_persistence.rb +0 -1
- data/lib/aasm/persistence.rb +3 -0
- data/lib/aasm/rspec/allow_event.rb +5 -1
- data/lib/aasm/rspec/allow_transition_to.rb +5 -1
- data/lib/aasm/rspec/transition_from.rb +5 -1
- data/lib/aasm/version.rb +1 -1
- data/lib/aasm.rb +5 -2
- data/lib/generators/aasm/orm_helpers.rb +7 -1
- data/lib/generators/active_record/aasm_generator.rb +3 -1
- data/lib/generators/active_record/templates/migration.rb +1 -1
- data/lib/generators/nobrainer/aasm_generator.rb +28 -0
- data/lib/motion-aasm.rb +1 -0
- metadata +42 -343
- data/.document +0 -6
- data/.gitignore +0 -20
- data/.travis.yml +0 -52
- data/API +0 -34
- data/Appraisals +0 -43
- data/CHANGELOG.md +0 -365
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -24
- data/Gemfile +0 -7
- data/HOWTO +0 -12
- data/PLANNED_CHANGES.md +0 -11
- data/README_FROM_VERSION_3_TO_4.md +0 -240
- data/Rakefile +0 -31
- data/TESTING.md +0 -25
- data/aasm.gemspec +0 -35
- data/callbacks.txt +0 -51
- data/gemfiles/rails_3.2.gemfile +0 -13
- data/gemfiles/rails_4.0.gemfile +0 -15
- data/gemfiles/rails_4.2.gemfile +0 -16
- data/gemfiles/rails_4.2_mongoid_5.gemfile +0 -11
- data/gemfiles/rails_5.0.gemfile +0 -14
- data/spec/database.rb +0 -44
- data/spec/database.yml +0 -3
- data/spec/en.yml +0 -12
- data/spec/en_deprecated_style.yml +0 -10
- data/spec/generators/active_record_generator_spec.rb +0 -47
- data/spec/generators/mongoid_generator_spec.rb +0 -31
- data/spec/models/active_record/basic_active_record_two_state_machines_example.rb +0 -25
- data/spec/models/active_record/complex_active_record_example.rb +0 -37
- data/spec/models/active_record/derivate_new_dsl.rb +0 -7
- data/spec/models/active_record/false_state.rb +0 -35
- data/spec/models/active_record/gate.rb +0 -39
- data/spec/models/active_record/invalid_persistor.rb +0 -29
- data/spec/models/active_record/localizer_test_model.rb +0 -34
- data/spec/models/active_record/no_direct_assignment.rb +0 -21
- data/spec/models/active_record/no_scope.rb +0 -21
- data/spec/models/active_record/persisted_state.rb +0 -12
- data/spec/models/active_record/provided_and_persisted_state.rb +0 -24
- data/spec/models/active_record/reader.rb +0 -7
- data/spec/models/active_record/readme_job.rb +0 -21
- data/spec/models/active_record/silent_persistor.rb +0 -29
- data/spec/models/active_record/simple_new_dsl.rb +0 -17
- data/spec/models/active_record/thief.rb +0 -29
- data/spec/models/active_record/transactor.rb +0 -99
- data/spec/models/active_record/transient.rb +0 -6
- data/spec/models/active_record/validator.rb +0 -118
- data/spec/models/active_record/with_enum.rb +0 -39
- data/spec/models/active_record/with_enum_without_column.rb +0 -38
- data/spec/models/active_record/with_false_enum.rb +0 -31
- data/spec/models/active_record/with_true_enum.rb +0 -39
- data/spec/models/active_record/worker.rb +0 -2
- data/spec/models/active_record/writer.rb +0 -6
- data/spec/models/basic_two_state_machines_example.rb +0 -25
- data/spec/models/callbacks/basic.rb +0 -98
- data/spec/models/callbacks/basic_multiple.rb +0 -75
- data/spec/models/callbacks/guard_within_block.rb +0 -67
- data/spec/models/callbacks/guard_within_block_multiple.rb +0 -66
- data/spec/models/callbacks/multiple_transitions_transition_guard.rb +0 -66
- data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +0 -65
- data/spec/models/callbacks/private_method.rb +0 -44
- data/spec/models/callbacks/private_method_multiple.rb +0 -44
- data/spec/models/callbacks/with_args.rb +0 -62
- data/spec/models/callbacks/with_args_multiple.rb +0 -61
- data/spec/models/callbacks/with_state_arg.rb +0 -30
- data/spec/models/callbacks/with_state_arg_multiple.rb +0 -26
- data/spec/models/complex_example.rb +0 -222
- data/spec/models/conversation.rb +0 -93
- data/spec/models/default_state.rb +0 -12
- data/spec/models/double_definer.rb +0 -21
- data/spec/models/dynamoid/complex_dynamoid_example.rb +0 -37
- data/spec/models/dynamoid/dynamoid_multiple.rb +0 -18
- data/spec/models/dynamoid/dynamoid_simple.rb +0 -18
- data/spec/models/foo.rb +0 -106
- data/spec/models/foo_callback_multiple.rb +0 -45
- data/spec/models/guard_arguments_check.rb +0 -17
- data/spec/models/guard_with_params.rb +0 -24
- data/spec/models/guard_with_params_multiple.rb +0 -18
- data/spec/models/guardian.rb +0 -58
- data/spec/models/guardian_multiple.rb +0 -48
- data/spec/models/guardian_without_from_specified.rb +0 -18
- data/spec/models/initial_state_proc.rb +0 -31
- data/spec/models/mongoid/complex_mongoid_example.rb +0 -37
- data/spec/models/mongoid/invalid_persistor_mongoid.rb +0 -39
- data/spec/models/mongoid/mongoid_relationships.rb +0 -26
- data/spec/models/mongoid/no_scope_mongoid.rb +0 -21
- data/spec/models/mongoid/silent_persistor_mongoid.rb +0 -39
- data/spec/models/mongoid/simple_mongoid.rb +0 -23
- data/spec/models/mongoid/simple_new_dsl_mongoid.rb +0 -25
- data/spec/models/mongoid/validator_mongoid.rb +0 -100
- data/spec/models/multi_transitioner.rb +0 -34
- data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +0 -31
- data/spec/models/namespaced_multiple_example.rb +0 -42
- data/spec/models/no_initial_state.rb +0 -25
- data/spec/models/not_auto_loaded/process.rb +0 -21
- data/spec/models/parametrised_event.rb +0 -42
- data/spec/models/parametrised_event_multiple.rb +0 -29
- data/spec/models/process_with_new_dsl.rb +0 -31
- data/spec/models/provided_state.rb +0 -24
- data/spec/models/redis/complex_redis_example.rb +0 -40
- data/spec/models/redis/redis_multiple.rb +0 -20
- data/spec/models/redis/redis_simple.rb +0 -20
- data/spec/models/sequel/complex_sequel_example.rb +0 -46
- data/spec/models/sequel/invalid_persistor.rb +0 -52
- data/spec/models/sequel/sequel_multiple.rb +0 -25
- data/spec/models/sequel/sequel_simple.rb +0 -26
- data/spec/models/sequel/silent_persistor.rb +0 -50
- data/spec/models/sequel/transactor.rb +0 -112
- data/spec/models/sequel/validator.rb +0 -93
- data/spec/models/sequel/worker.rb +0 -12
- data/spec/models/silencer.rb +0 -27
- data/spec/models/simple_custom_example.rb +0 -53
- data/spec/models/simple_example.rb +0 -15
- data/spec/models/simple_multiple_example.rb +0 -42
- data/spec/models/state_machine_with_failed_event.rb +0 -20
- data/spec/models/states_on_one_line_example.rb +0 -8
- data/spec/models/sub_class.rb +0 -41
- data/spec/models/sub_class_with_more_states.rb +0 -18
- data/spec/models/sub_classing.rb +0 -3
- data/spec/models/super_class.rb +0 -46
- data/spec/models/this_name_better_not_be_in_use.rb +0 -11
- data/spec/models/valid_state_name.rb +0 -23
- data/spec/spec_helper.rb +0 -26
- data/spec/spec_helpers/active_record.rb +0 -7
- data/spec/spec_helpers/dynamoid.rb +0 -33
- data/spec/spec_helpers/mongoid.rb +0 -7
- data/spec/spec_helpers/redis.rb +0 -15
- data/spec/spec_helpers/remove_warnings.rb +0 -1
- data/spec/spec_helpers/sequel.rb +0 -7
- data/spec/unit/api_spec.rb +0 -100
- data/spec/unit/basic_two_state_machines_example_spec.rb +0 -10
- data/spec/unit/callback_multiple_spec.rb +0 -300
- data/spec/unit/callbacks_spec.rb +0 -491
- data/spec/unit/complex_example_spec.rb +0 -84
- data/spec/unit/complex_multiple_example_spec.rb +0 -99
- data/spec/unit/edge_cases_spec.rb +0 -16
- data/spec/unit/event_multiple_spec.rb +0 -73
- data/spec/unit/event_naming_spec.rb +0 -16
- data/spec/unit/event_spec.rb +0 -381
- data/spec/unit/exception_spec.rb +0 -11
- data/spec/unit/guard_arguments_check_spec.rb +0 -9
- data/spec/unit/guard_multiple_spec.rb +0 -60
- data/spec/unit/guard_spec.rb +0 -89
- data/spec/unit/guard_with_params_multiple_spec.rb +0 -10
- data/spec/unit/guard_with_params_spec.rb +0 -14
- data/spec/unit/guard_without_from_specified_spec.rb +0 -10
- data/spec/unit/initial_state_multiple_spec.rb +0 -15
- data/spec/unit/initial_state_spec.rb +0 -12
- data/spec/unit/inspection_multiple_spec.rb +0 -201
- data/spec/unit/inspection_spec.rb +0 -149
- data/spec/unit/localizer_spec.rb +0 -78
- data/spec/unit/memory_leak_spec.rb +0 -38
- data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +0 -14
- data/spec/unit/namespaced_multiple_example_spec.rb +0 -75
- data/spec/unit/new_dsl_spec.rb +0 -12
- data/spec/unit/override_warning_spec.rb +0 -94
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +0 -618
- data/spec/unit/persistence/active_record_persistence_spec.rb +0 -721
- data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +0 -135
- data/spec/unit/persistence/dynamoid_persistence_spec.rb +0 -84
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +0 -204
- data/spec/unit/persistence/mongoid_persistence_spec.rb +0 -169
- data/spec/unit/persistence/redis_persistence_multiple_spec.rb +0 -88
- data/spec/unit/persistence/redis_persistence_spec.rb +0 -53
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +0 -148
- data/spec/unit/persistence/sequel_persistence_spec.rb +0 -368
- data/spec/unit/readme_spec.rb +0 -41
- data/spec/unit/reloading_spec.rb +0 -15
- data/spec/unit/rspec_matcher_spec.rb +0 -79
- data/spec/unit/simple_custom_example_spec.rb +0 -39
- data/spec/unit/simple_example_spec.rb +0 -42
- data/spec/unit/simple_multiple_example_spec.rb +0 -91
- data/spec/unit/state_spec.rb +0 -89
- data/spec/unit/states_on_one_line_example_spec.rb +0 -16
- data/spec/unit/subclassing_multiple_spec.rb +0 -74
- data/spec/unit/subclassing_spec.rb +0 -46
- data/spec/unit/transition_spec.rb +0 -436
- data/test/minitest_helper.rb +0 -57
- data/test/unit/minitest_matcher_test.rb +0 -80
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AASM
|
4
|
+
module Core
|
5
|
+
module Invokers
|
6
|
+
##
|
7
|
+
# Proc invoker which allows to use Procs as
|
8
|
+
# state/event/transition callbacks.
|
9
|
+
class ProcInvoker < BaseInvoker
|
10
|
+
def may_invoke?
|
11
|
+
subject.is_a?(Proc)
|
12
|
+
end
|
13
|
+
|
14
|
+
def log_failure
|
15
|
+
return log_source_location if Method.method_defined?(:source_location)
|
16
|
+
log_proc_info
|
17
|
+
end
|
18
|
+
|
19
|
+
def invoke_subject
|
20
|
+
@result = if support_parameters?
|
21
|
+
exec_proc(parameters_to_arity)
|
22
|
+
else
|
23
|
+
exec_proc(subject.arity)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def support_parameters?
|
30
|
+
subject.respond_to?(:parameters)
|
31
|
+
end
|
32
|
+
|
33
|
+
# rubocop:disable Metrics/AbcSize
|
34
|
+
def exec_proc(parameters_size)
|
35
|
+
return record.instance_exec(&subject) if parameters_size.zero?
|
36
|
+
return record.instance_exec(*args, &subject) if parameters_size < 0
|
37
|
+
record.instance_exec(*args[0..(parameters_size - 1)], &subject)
|
38
|
+
end
|
39
|
+
# rubocop:enable Metrics/AbcSize
|
40
|
+
|
41
|
+
def log_source_location
|
42
|
+
failures << subject.source_location.join('#')
|
43
|
+
end
|
44
|
+
|
45
|
+
def log_proc_info
|
46
|
+
failures << subject
|
47
|
+
end
|
48
|
+
|
49
|
+
def parameters_to_arity
|
50
|
+
subject.parameters.inject(0) do |memo, parameter|
|
51
|
+
memo += 1
|
52
|
+
memo *= -1 if parameter[0] == :rest && memo > 0
|
53
|
+
memo
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/aasm/core/state.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AASM::Core
|
2
4
|
class State
|
3
|
-
attr_reader :name, :state_machine, :options
|
5
|
+
attr_reader :name, :state_machine, :options, :default_display_name
|
4
6
|
|
5
7
|
def initialize(name, klass, state_machine, options={})
|
6
8
|
@name = name
|
7
9
|
@klass = klass
|
8
10
|
@state_machine = state_machine
|
11
|
+
@default_display_name = name.to_s.gsub(/_/, ' ').capitalize
|
9
12
|
update(options)
|
10
13
|
end
|
11
14
|
|
@@ -13,7 +16,13 @@ module AASM::Core
|
|
13
16
|
def initialize_copy(orig)
|
14
17
|
super
|
15
18
|
@options = {}
|
16
|
-
orig.options.each_pair
|
19
|
+
orig.options.each_pair do |name, setting|
|
20
|
+
@options[name] = if setting.is_a?(Hash) || setting.is_a?(Array)
|
21
|
+
setting.dup
|
22
|
+
else
|
23
|
+
setting
|
24
|
+
end
|
25
|
+
end
|
17
26
|
end
|
18
27
|
|
19
28
|
def ==(state)
|
@@ -46,11 +55,11 @@ module AASM::Core
|
|
46
55
|
end
|
47
56
|
|
48
57
|
def display_name
|
49
|
-
@display_name
|
58
|
+
@display_name = begin
|
50
59
|
if Module.const_defined?(:I18n)
|
51
60
|
localized_name
|
52
61
|
else
|
53
|
-
|
62
|
+
@default_display_name
|
54
63
|
end
|
55
64
|
end
|
56
65
|
end
|
@@ -67,22 +76,15 @@ module AASM::Core
|
|
67
76
|
private
|
68
77
|
|
69
78
|
def update(options = {})
|
70
|
-
if options.key?(:display)
|
71
|
-
@
|
79
|
+
if options.key?(:display)
|
80
|
+
@default_display_name = options.delete(:display)
|
72
81
|
end
|
73
82
|
@options = options
|
74
83
|
self
|
75
84
|
end
|
76
85
|
|
77
86
|
def _fire_callbacks(action, record, args)
|
78
|
-
|
79
|
-
when Symbol, String
|
80
|
-
arity = record.__send__(:method, action.to_sym).arity
|
81
|
-
record.__send__(action, *(arity < 0 ? args : args[0...arity]))
|
82
|
-
when Proc
|
83
|
-
arity = action.arity
|
84
|
-
action.call(record, *(arity < 0 ? args : args[0...arity]))
|
85
|
-
end
|
87
|
+
Invoker.new(action, record, args).invoke
|
86
88
|
end
|
87
89
|
|
88
90
|
end
|
data/lib/aasm/core/transition.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module AASM::Core
|
2
4
|
class Transition
|
3
|
-
include DslHelper
|
5
|
+
include AASM::DslHelper
|
4
6
|
|
5
7
|
attr_reader :from, :to, :event, :opts, :failures
|
6
8
|
alias_method :options, :opts
|
@@ -67,77 +69,14 @@ module AASM::Core
|
|
67
69
|
record.aasm(event.state_machine.name).to_state = @to if record.aasm(event.state_machine.name).respond_to?(:to_state=)
|
68
70
|
end
|
69
71
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
result
|
75
|
-
when Proc
|
76
|
-
if code.respond_to?(:parameters)
|
77
|
-
# In Ruby's Proc, the 'arity' method is not a good condidate to know if
|
78
|
-
# we should pass the arguments or not, since it does return 0 even in
|
79
|
-
# presence of optional parameters.
|
80
|
-
result = (code.parameters.size == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code))
|
81
|
-
|
82
|
-
failures << code.source_location.join('#') unless result
|
83
|
-
else
|
84
|
-
# In RubyMotion's Proc, the 'parameter' method does not exists, however its
|
85
|
-
# 'arity' method works just like the one from Method, only returning 0 when
|
86
|
-
# there is no parameters whatsoever, optional or not.
|
87
|
-
result = (code.arity == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code))
|
88
|
-
|
89
|
-
# Sadly, RubyMotion's Proc does not define the method 'source_location' either.
|
90
|
-
failures << code unless result
|
91
|
-
end
|
92
|
-
|
93
|
-
result
|
94
|
-
when Class
|
95
|
-
arity = code.instance_method(:initialize).arity
|
96
|
-
if arity == 0
|
97
|
-
instance = code.new
|
98
|
-
elsif arity == 1
|
99
|
-
instance = code.new(record)
|
100
|
-
else
|
101
|
-
instance = code.new(record, *args)
|
102
|
-
end
|
103
|
-
result = instance.call
|
104
|
-
|
105
|
-
if Method.method_defined?(:source_location)
|
106
|
-
failures << instance.method(:call).source_location.join('#') unless result
|
107
|
-
else
|
108
|
-
# RubyMotion support ('source_location' not defined for Method)
|
109
|
-
failures << instance.method(:call) unless result
|
110
|
-
end
|
111
|
-
|
112
|
-
result
|
113
|
-
when Array
|
114
|
-
if options[:guard]
|
115
|
-
# invoke guard callbacks
|
116
|
-
code.all? {|a| invoke_callbacks_compatible_with_guard(a, record, args)}
|
117
|
-
elsif options[:unless]
|
118
|
-
# invoke unless callbacks
|
119
|
-
code.all? {|a| !invoke_callbacks_compatible_with_guard(a, record, args)}
|
120
|
-
else
|
121
|
-
# invoke after callbacks
|
122
|
-
code.map {|a| invoke_callbacks_compatible_with_guard(a, record, args)}
|
123
|
-
end
|
124
|
-
else
|
125
|
-
true
|
126
|
-
end
|
72
|
+
Invoker.new(code, record, args)
|
73
|
+
.with_options(options)
|
74
|
+
.with_failures(failures)
|
75
|
+
.invoke
|
127
76
|
end
|
128
77
|
|
129
78
|
def _fire_callbacks(code, record, args)
|
130
|
-
|
131
|
-
when Symbol, String
|
132
|
-
arity = record.send(:method, code.to_sym).arity
|
133
|
-
record.send(code, *(arity < 0 ? args : args[0...arity]))
|
134
|
-
when Proc
|
135
|
-
code.arity == 0 ? record.instance_exec(&code) : record.instance_exec(*args, &code)
|
136
|
-
when Array
|
137
|
-
code.map {|a| _fire_callbacks(a, record, args)}
|
138
|
-
else
|
139
|
-
true
|
140
|
-
end
|
79
|
+
Invoker.new(code, record, args).invoke
|
141
80
|
end
|
142
81
|
|
143
82
|
end
|
data/lib/aasm/dsl_helper.rb
CHANGED
@@ -1,30 +1,32 @@
|
|
1
|
-
module
|
1
|
+
module AASM
|
2
|
+
module DslHelper
|
2
3
|
|
3
|
-
|
4
|
-
|
4
|
+
class Proxy
|
5
|
+
attr_accessor :options
|
5
6
|
|
6
|
-
|
7
|
-
|
8
|
-
|
7
|
+
def initialize(options, valid_keys, source)
|
8
|
+
@valid_keys = valid_keys
|
9
|
+
@source = source
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
@options = options
|
12
|
+
end
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
14
|
+
def method_missing(name, *args, &block)
|
15
|
+
if @valid_keys.include?(name)
|
16
|
+
options[name] = Array(options[name])
|
17
|
+
options[name] << block if block
|
18
|
+
options[name] += Array(args)
|
19
|
+
else
|
20
|
+
@source.send name, *args, &block
|
21
|
+
end
|
20
22
|
end
|
21
23
|
end
|
22
|
-
end
|
23
24
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
def add_options_from_dsl(options, valid_keys, &block)
|
26
|
+
proxy = Proxy.new(options, valid_keys, self)
|
27
|
+
proxy.instance_eval(&block)
|
28
|
+
proxy.options
|
29
|
+
end
|
29
30
|
|
30
|
-
end
|
31
|
+
end
|
32
|
+
end
|
data/lib/aasm/errors.rb
CHANGED
@@ -3,18 +3,20 @@ module AASM
|
|
3
3
|
class UnknownStateMachineError < RuntimeError; end
|
4
4
|
|
5
5
|
class InvalidTransition < RuntimeError
|
6
|
-
attr_reader :object, :event_name, :originating_state, :failures
|
6
|
+
attr_reader :object, :event_name, :originating_state, :failures, :state_machine_name
|
7
7
|
|
8
8
|
def initialize(object, event_name, state_machine_name, failures = [])
|
9
9
|
@object, @event_name, @originating_state, @failures = object, event_name, object.aasm(state_machine_name).current_state, failures
|
10
|
-
|
10
|
+
@state_machine_name = state_machine_name
|
11
|
+
super("Event '#{event_name}' cannot transition from '#{originating_state}'.#{reasoning}")
|
11
12
|
end
|
12
13
|
|
13
14
|
def reasoning
|
14
|
-
"Failed callback(s): #{failures}." unless failures.empty?
|
15
|
+
" Failed callback(s): #{failures}." unless failures.empty?
|
15
16
|
end
|
16
17
|
end
|
17
18
|
|
18
19
|
class UndefinedState < RuntimeError; end
|
20
|
+
class UndefinedEvent < UndefinedState; end
|
19
21
|
class NoDirectAssignmentError < RuntimeError; end
|
20
22
|
end
|
data/lib/aasm/instance_base.rb
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
module AASM
|
2
2
|
class InstanceBase
|
3
|
-
|
4
3
|
attr_accessor :from_state, :to_state, :current_event
|
5
4
|
|
6
5
|
def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
|
@@ -28,7 +27,7 @@ module AASM
|
|
28
27
|
end
|
29
28
|
|
30
29
|
def human_state
|
31
|
-
|
30
|
+
state_object_for_name(current_state).display_name
|
32
31
|
end
|
33
32
|
|
34
33
|
def states(options={}, *args)
|
@@ -78,6 +77,17 @@ module AASM
|
|
78
77
|
events
|
79
78
|
end
|
80
79
|
|
80
|
+
def permitted_transitions
|
81
|
+
events(permitted: true).flat_map do |event|
|
82
|
+
available_transitions = event.transitions_from_state(current_state)
|
83
|
+
allowed_transitions = available_transitions.select { |t| t.allowed?(@instance) }
|
84
|
+
|
85
|
+
allowed_transitions.map do |transition|
|
86
|
+
{ event: event.name, state: transition.to }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
81
91
|
def state_object_for_name(name)
|
82
92
|
obj = @instance.class.aasm(@name).states.find {|s| s.name == name}
|
83
93
|
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
|
@@ -91,7 +101,7 @@ module AASM
|
|
91
101
|
when Proc
|
92
102
|
state.call(@instance)
|
93
103
|
else
|
94
|
-
raise NotImplementedError, "Unrecognized state-type given.
|
104
|
+
raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
|
95
105
|
end
|
96
106
|
end
|
97
107
|
|
@@ -103,11 +113,32 @@ module AASM
|
|
103
113
|
end
|
104
114
|
end
|
105
115
|
|
116
|
+
def fire(event_name, *args, &block)
|
117
|
+
event_exists?(event_name)
|
118
|
+
|
119
|
+
@instance.send(event_name, *args, &block)
|
120
|
+
end
|
121
|
+
|
122
|
+
def fire!(event_name, *args, &block)
|
123
|
+
event_exists?(event_name, true)
|
124
|
+
bang_event_name = "#{event_name}!".to_sym
|
125
|
+
@instance.send(bang_event_name, *args, &block)
|
126
|
+
end
|
127
|
+
|
106
128
|
def set_current_state_with_persistence(state)
|
107
129
|
save_success = @instance.aasm_write_state(state, @name)
|
108
130
|
self.current_state = state if save_success
|
109
131
|
save_success
|
110
132
|
end
|
111
133
|
|
134
|
+
private
|
135
|
+
|
136
|
+
def event_exists?(event_name, bang = false)
|
137
|
+
event = @instance.class.aasm(@name).state_machine.events[event_name.to_sym]
|
138
|
+
return true if event
|
139
|
+
|
140
|
+
event_error = bang ? "#{event_name}!" : event_name
|
141
|
+
raise AASM::UndefinedEvent, "Event :#{event_error} doesn't exist" if event.nil?
|
142
|
+
end
|
112
143
|
end
|
113
144
|
end
|
data/lib/aasm/localizer.rb
CHANGED
@@ -5,7 +5,7 @@ module AASM
|
|
5
5
|
list << :"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
|
6
6
|
list
|
7
7
|
end
|
8
|
-
translate_queue(checklist) || I18n.translate(checklist.shift, :default => event
|
8
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => default_display_name(event))
|
9
9
|
end
|
10
10
|
|
11
11
|
def human_state_name(klass, state)
|
@@ -14,7 +14,7 @@ module AASM
|
|
14
14
|
list << item_for(klass, state, ancestor, :old_style => true)
|
15
15
|
list
|
16
16
|
end
|
17
|
-
translate_queue(checklist) || I18n.translate(checklist.shift, :default => state
|
17
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => default_display_name(state))
|
18
18
|
end
|
19
19
|
|
20
20
|
private
|
@@ -46,8 +46,18 @@ module AASM
|
|
46
46
|
end
|
47
47
|
|
48
48
|
def ancestors_list(klass)
|
49
|
+
has_active_record_base = defined?(::ActiveRecord::Base)
|
49
50
|
klass.ancestors.select do |ancestor|
|
50
|
-
|
51
|
+
not_active_record_base = has_active_record_base ? (ancestor != ::ActiveRecord::Base) : true
|
52
|
+
ancestor.respond_to?(:model_name) && not_active_record_base
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_display_name(object) # Can use better arguement name
|
57
|
+
if object.respond_to?(:default_display_name)
|
58
|
+
object.default_display_name
|
59
|
+
else
|
60
|
+
object.to_s.gsub(/_/, ' ').capitalize
|
51
61
|
end
|
52
62
|
end
|
53
63
|
end
|
@@ -41,14 +41,15 @@ module AASM
|
|
41
41
|
|
42
42
|
module ClassMethods
|
43
43
|
def aasm_create_scope(state_machine_name, scope_name)
|
44
|
-
conditions = {
|
45
|
-
table_name => { aasm(state_machine_name).attribute_name => scope_name.to_s }
|
46
|
-
}
|
47
44
|
if ActiveRecord::VERSION::MAJOR >= 3
|
45
|
+
conditions = { aasm(state_machine_name).attribute_name => scope_name.to_s }
|
48
46
|
class_eval do
|
49
|
-
scope scope_name, lambda { where(conditions) }
|
47
|
+
scope scope_name, lambda { where(table_name => conditions) }
|
50
48
|
end
|
51
49
|
else
|
50
|
+
conditions = {
|
51
|
+
table_name => { aasm(state_machine_name).attribute_name => scope_name.to_s }
|
52
|
+
}
|
52
53
|
class_eval do
|
53
54
|
named_scope scope_name, :conditions => conditions
|
54
55
|
end
|
@@ -60,6 +61,24 @@ module AASM
|
|
60
61
|
|
61
62
|
private
|
62
63
|
|
64
|
+
def aasm_execute_after_commit
|
65
|
+
begin
|
66
|
+
require 'after_commit_everywhere'
|
67
|
+
raise LoadError unless Gem::Version.new(::AfterCommitEverywhere::VERSION) >= Gem::Version.new('0.1.5')
|
68
|
+
|
69
|
+
self.extend ::AfterCommitEverywhere
|
70
|
+
after_commit do
|
71
|
+
yield
|
72
|
+
end
|
73
|
+
rescue LoadError
|
74
|
+
warn <<-MSG
|
75
|
+
[DEPRECATION] :after_commit AASM callback is not safe in terms of race conditions and redundant calls.
|
76
|
+
Please add `gem 'after_commit_everywhere', '~> 1.0'` to your Gemfile in order to fix that.
|
77
|
+
MSG
|
78
|
+
yield
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
63
82
|
def aasm_raise_invalid_record
|
64
83
|
raise ActiveRecord::RecordInvalid.new(self)
|
65
84
|
end
|
@@ -140,7 +159,8 @@ module AASM
|
|
140
159
|
|
141
160
|
def aasm_column_is_blank?(state_machine_name)
|
142
161
|
attribute_name = self.class.aasm(state_machine_name).attribute_name
|
143
|
-
attribute_names.include?(attribute_name.to_s) &&
|
162
|
+
attribute_names.include?(attribute_name.to_s) &&
|
163
|
+
(send(attribute_name).respond_to?(:empty?) ? !!send(attribute_name).empty? : !send(attribute_name))
|
144
164
|
end
|
145
165
|
|
146
166
|
def aasm_validate_states
|
@@ -34,7 +34,7 @@ module AASM
|
|
34
34
|
# This allows for nil aasm states - be sure to add validation to your model
|
35
35
|
def aasm_read_state(name=:default)
|
36
36
|
state = send(self.class.aasm(name).attribute_name)
|
37
|
-
if state.
|
37
|
+
if !state || state.empty?
|
38
38
|
aasm_new_record? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : nil
|
39
39
|
else
|
40
40
|
state.to_sym
|
@@ -59,7 +59,9 @@ module AASM
|
|
59
59
|
# make sure to create a (named) scope for each state
|
60
60
|
def state_with_scope(*args)
|
61
61
|
names = state_without_scope(*args)
|
62
|
-
names.each
|
62
|
+
names.each do |name|
|
63
|
+
create_scopes(name)
|
64
|
+
end
|
63
65
|
end
|
64
66
|
alias_method :state_without_scope, :state
|
65
67
|
alias_method :state, :state_with_scope
|
@@ -71,7 +73,16 @@ module AASM
|
|
71
73
|
end
|
72
74
|
|
73
75
|
def create_scope(name)
|
74
|
-
@klass.aasm_create_scope(@name, name)
|
76
|
+
@klass.aasm_create_scope(@name, name) if create_scope?(name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def create_scopes(name)
|
80
|
+
if namespace?
|
81
|
+
# Create default scopes even when namespace? for backward compatibility
|
82
|
+
namepaced_name = "#{namespace}_#{name}"
|
83
|
+
create_scope(namepaced_name)
|
84
|
+
end
|
85
|
+
create_scope(name)
|
75
86
|
end
|
76
87
|
end # Base
|
77
88
|
|
@@ -77,7 +77,8 @@ module AASM
|
|
77
77
|
#
|
78
78
|
def aasm_ensure_initial_state
|
79
79
|
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
80
|
-
send(
|
80
|
+
next if !send(self.class.aasm(state_machine_name).attribute_name) || send(self.class.aasm(state_machine_name).attribute_name).empty?
|
81
|
+
send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s)
|
81
82
|
end
|
82
83
|
end
|
83
84
|
end # InstanceMethods
|
@@ -83,7 +83,7 @@ module AASM
|
|
83
83
|
#
|
84
84
|
def aasm_ensure_initial_state
|
85
85
|
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
86
|
-
aasm(state_machine_name).enter_initial_state if send(self.class.aasm(state_machine_name).attribute_name).
|
86
|
+
aasm(state_machine_name).enter_initial_state if !send(self.class.aasm(state_machine_name).attribute_name) || send(self.class.aasm(state_machine_name).attribute_name).empty?
|
87
87
|
end
|
88
88
|
end
|
89
89
|
end # InstanceMethods
|
@@ -106,7 +106,7 @@ module AASM
|
|
106
106
|
# mongoid has_many relationship does not load child object attributes when
|
107
107
|
# only ids are loaded, for example parent.child_ids will not load child object attributes.
|
108
108
|
# This feature is introduced in mongoid > 4.
|
109
|
-
if attribute_names.include?(attribute_name) && attributes[attribute_name].
|
109
|
+
if attribute_names.include?(attribute_name) && !attributes[attribute_name] || attributes[attribute_name].empty?
|
110
110
|
# attribute_missing? is defined in mongoid > 4
|
111
111
|
return if Mongoid::VERSION.to_f >= 4 && attribute_missing?(attribute_name)
|
112
112
|
send("#{self.class.aasm(state_machine_name).attribute_name}=", aasm(state_machine_name).enter_initial_state.to_s)
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'aasm/persistence/orm'
|
2
|
+
module AASM
|
3
|
+
module Persistence
|
4
|
+
module NoBrainerPersistence
|
5
|
+
# This method:
|
6
|
+
#
|
7
|
+
# * extends the model with ClassMethods
|
8
|
+
# * includes InstanceMethods
|
9
|
+
#
|
10
|
+
# Adds
|
11
|
+
#
|
12
|
+
# before_validation :aasm_ensure_initial_state
|
13
|
+
#
|
14
|
+
# As a result, it doesn't matter when you define your methods - the following 2 are equivalent
|
15
|
+
#
|
16
|
+
# class Foo
|
17
|
+
# include NoBrainer::Document
|
18
|
+
# def aasm_write_state(state)
|
19
|
+
# "bar"
|
20
|
+
# end
|
21
|
+
# include AASM
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# class Foo
|
25
|
+
# include NoBrainer::Document
|
26
|
+
# include AASM
|
27
|
+
# def aasm_write_state(state)
|
28
|
+
# "bar"
|
29
|
+
# end
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
def self.included(base)
|
33
|
+
base.send(:include, AASM::Persistence::Base)
|
34
|
+
base.send(:include, AASM::Persistence::ORM)
|
35
|
+
base.send(:include, AASM::Persistence::NoBrainerPersistence::InstanceMethods)
|
36
|
+
base.extend AASM::Persistence::NoBrainerPersistence::ClassMethods
|
37
|
+
|
38
|
+
base.after_initialize :aasm_ensure_initial_state
|
39
|
+
end
|
40
|
+
|
41
|
+
module ClassMethods
|
42
|
+
def aasm_create_scope(state_machine_name, scope_name)
|
43
|
+
scope_options = lambda {
|
44
|
+
where(aasm(state_machine_name).attribute_name.to_sym => scope_name.to_s)
|
45
|
+
}
|
46
|
+
send(:scope, scope_name, scope_options)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
module InstanceMethods
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def aasm_save
|
55
|
+
self.save
|
56
|
+
end
|
57
|
+
|
58
|
+
def aasm_raise_invalid_record
|
59
|
+
raise NoBrainer::Error::DocumentInvalid.new(self)
|
60
|
+
end
|
61
|
+
|
62
|
+
def aasm_supports_transactions?
|
63
|
+
false
|
64
|
+
end
|
65
|
+
|
66
|
+
def aasm_update_column(attribute_name, value)
|
67
|
+
write_attribute(attribute_name, value)
|
68
|
+
save(validate: false)
|
69
|
+
|
70
|
+
true
|
71
|
+
end
|
72
|
+
|
73
|
+
def aasm_read_attribute(name)
|
74
|
+
read_attribute(name)
|
75
|
+
end
|
76
|
+
|
77
|
+
def aasm_write_attribute(name, value)
|
78
|
+
write_attribute(name, value)
|
79
|
+
end
|
80
|
+
|
81
|
+
# Ensures that if the aasm_state column is nil and the record is new
|
82
|
+
# that the initial state gets populated before validation on create
|
83
|
+
#
|
84
|
+
# foo = Foo.new
|
85
|
+
# foo.aasm_state # => nil
|
86
|
+
# foo.valid?
|
87
|
+
# foo.aasm_state # => "open" (where :open is the initial state)
|
88
|
+
#
|
89
|
+
#
|
90
|
+
# foo = Foo.find(:first)
|
91
|
+
# foo.aasm_state # => 1
|
92
|
+
# foo.aasm_state = nil
|
93
|
+
# foo.valid?
|
94
|
+
# foo.aasm_state # => nil
|
95
|
+
#
|
96
|
+
def aasm_ensure_initial_state
|
97
|
+
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |name|
|
98
|
+
aasm_column = self.class.aasm(name).attribute_name
|
99
|
+
aasm(name).enter_initial_state if !read_attribute(aasm_column) || read_attribute(aasm_column).empty?
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end # InstanceMethods
|
103
|
+
end
|
104
|
+
end # Persistence
|
105
|
+
end # AASM
|