aasm 4.12.2 → 5.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|