aasm 5.0.8
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 +7 -0
- data/.document +6 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +27 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.gitignore +20 -0
- data/.travis.yml +100 -0
- data/API +34 -0
- data/Appraisals +71 -0
- data/CHANGELOG.md +431 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/CONTRIBUTING.md +24 -0
- data/Dockerfile +44 -0
- data/Gemfile +6 -0
- data/Gemfile.lock_old +151 -0
- data/HOWTO +12 -0
- data/LICENSE +20 -0
- data/PLANNED_CHANGES.md +11 -0
- data/README.md +1439 -0
- data/README_FROM_VERSION_3_TO_4.md +240 -0
- data/Rakefile +31 -0
- data/TESTING.md +25 -0
- data/aasm.gemspec +37 -0
- data/callbacks.txt +51 -0
- data/docker-compose.yml +40 -0
- data/gemfiles/norails.gemfile +10 -0
- data/gemfiles/rails_3.2.gemfile +14 -0
- data/gemfiles/rails_4.2.gemfile +16 -0
- data/gemfiles/rails_4.2_mongoid_5.gemfile +11 -0
- data/gemfiles/rails_4.2_nobrainer.gemfile +9 -0
- data/gemfiles/rails_5.0.gemfile +13 -0
- data/gemfiles/rails_5.0_nobrainer.gemfile +9 -0
- data/gemfiles/rails_5.1.gemfile +13 -0
- data/gemfiles/rails_5.2.gemfile +13 -0
- data/lib/aasm.rb +23 -0
- data/lib/aasm/aasm.rb +208 -0
- data/lib/aasm/base.rb +271 -0
- data/lib/aasm/configuration.rb +45 -0
- data/lib/aasm/core/event.rb +172 -0
- 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 +47 -0
- data/lib/aasm/core/invokers/proc_invoker.rb +59 -0
- data/lib/aasm/core/state.rb +90 -0
- data/lib/aasm/core/transition.rb +83 -0
- data/lib/aasm/dsl_helper.rb +30 -0
- data/lib/aasm/errors.rb +21 -0
- data/lib/aasm/instance_base.rb +133 -0
- data/lib/aasm/localizer.rb +54 -0
- data/lib/aasm/minitest.rb +5 -0
- data/lib/aasm/minitest/allow_event.rb +13 -0
- data/lib/aasm/minitest/allow_transition_to.rb +13 -0
- data/lib/aasm/minitest/have_state.rb +13 -0
- data/lib/aasm/minitest/transition_from.rb +21 -0
- data/lib/aasm/minitest_spec.rb +15 -0
- data/lib/aasm/persistence.rb +54 -0
- data/lib/aasm/persistence/active_record_persistence.rb +165 -0
- data/lib/aasm/persistence/base.rb +78 -0
- data/lib/aasm/persistence/core_data_query_persistence.rb +94 -0
- data/lib/aasm/persistence/dynamoid_persistence.rb +92 -0
- data/lib/aasm/persistence/mongoid_persistence.rb +126 -0
- data/lib/aasm/persistence/no_brainer_persistence.rb +105 -0
- data/lib/aasm/persistence/orm.rb +150 -0
- data/lib/aasm/persistence/plain_persistence.rb +26 -0
- data/lib/aasm/persistence/redis_persistence.rb +112 -0
- data/lib/aasm/persistence/sequel_persistence.rb +83 -0
- data/lib/aasm/rspec.rb +5 -0
- data/lib/aasm/rspec/allow_event.rb +26 -0
- data/lib/aasm/rspec/allow_transition_to.rb +26 -0
- data/lib/aasm/rspec/have_state.rb +22 -0
- data/lib/aasm/rspec/transition_from.rb +36 -0
- data/lib/aasm/state_machine.rb +53 -0
- data/lib/aasm/state_machine_store.rb +76 -0
- data/lib/aasm/version.rb +3 -0
- data/lib/generators/aasm/aasm_generator.rb +16 -0
- data/lib/generators/aasm/orm_helpers.rb +41 -0
- data/lib/generators/active_record/aasm_generator.rb +40 -0
- data/lib/generators/active_record/templates/migration.rb +8 -0
- data/lib/generators/active_record/templates/migration_existing.rb +5 -0
- data/lib/generators/mongoid/aasm_generator.rb +28 -0
- data/lib/generators/nobrainer/aasm_generator.rb +28 -0
- data/lib/motion-aasm.rb +37 -0
- data/spec/database.rb +59 -0
- data/spec/database.yml +3 -0
- data/spec/en.yml +12 -0
- data/spec/en_deprecated_style.yml +10 -0
- data/spec/generators/active_record_generator_spec.rb +53 -0
- data/spec/generators/mongoid_generator_spec.rb +31 -0
- data/spec/generators/no_brainer_generator_spec.rb +29 -0
- 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 +37 -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/instance_level_skip_validation_example.rb +19 -0
- data/spec/models/active_record/invalid_persistor.rb +29 -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/person.rb +23 -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/silent_persistor.rb +29 -0
- data/spec/models/active_record/simple_new_dsl.rb +32 -0
- data/spec/models/active_record/thief.rb +29 -0
- data/spec/models/active_record/transactor.rb +124 -0
- data/spec/models/active_record/transient.rb +6 -0
- data/spec/models/active_record/validator.rb +118 -0
- data/spec/models/active_record/with_enum.rb +39 -0
- data/spec/models/active_record/with_enum_without_column.rb +38 -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/work.rb +3 -0
- data/spec/models/active_record/worker.rb +2 -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 +98 -0
- data/spec/models/callbacks/basic_multiple.rb +75 -0
- data/spec/models/callbacks/guard_within_block.rb +67 -0
- data/spec/models/callbacks/guard_within_block_multiple.rb +66 -0
- data/spec/models/callbacks/multiple_transitions_transition_guard.rb +66 -0
- data/spec/models/callbacks/multiple_transitions_transition_guard_multiple.rb +65 -0
- data/spec/models/callbacks/private_method.rb +44 -0
- data/spec/models/callbacks/private_method_multiple.rb +44 -0
- data/spec/models/callbacks/with_args.rb +62 -0
- data/spec/models/callbacks/with_args_multiple.rb +61 -0
- data/spec/models/callbacks/with_state_arg.rb +34 -0
- data/spec/models/callbacks/with_state_arg_multiple.rb +29 -0
- data/spec/models/complex_example.rb +222 -0
- data/spec/models/conversation.rb +93 -0
- data/spec/models/default_state.rb +12 -0
- data/spec/models/double_definer.rb +21 -0
- data/spec/models/dynamoid/complex_dynamoid_example.rb +37 -0
- data/spec/models/dynamoid/dynamoid_multiple.rb +18 -0
- data/spec/models/dynamoid/dynamoid_simple.rb +18 -0
- data/spec/models/foo.rb +106 -0
- data/spec/models/foo_callback_multiple.rb +45 -0
- data/spec/models/guard_arguments_check.rb +17 -0
- data/spec/models/guard_with_params.rb +24 -0
- data/spec/models/guard_with_params_multiple.rb +18 -0
- data/spec/models/guardian.rb +58 -0
- data/spec/models/guardian_multiple.rb +48 -0
- data/spec/models/guardian_without_from_specified.rb +18 -0
- data/spec/models/initial_state_proc.rb +31 -0
- data/spec/models/mongoid/complex_mongoid_example.rb +37 -0
- data/spec/models/mongoid/invalid_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/mongoid_relationships.rb +26 -0
- data/spec/models/mongoid/no_scope_mongoid.rb +21 -0
- data/spec/models/mongoid/silent_persistor_mongoid.rb +39 -0
- data/spec/models/mongoid/simple_mongoid.rb +23 -0
- data/spec/models/mongoid/simple_new_dsl_mongoid.rb +25 -0
- data/spec/models/mongoid/validator_mongoid.rb +100 -0
- data/spec/models/multi_transitioner.rb +34 -0
- data/spec/models/multiple_transitions_that_differ_only_by_guard.rb +31 -0
- data/spec/models/namespaced_multiple_example.rb +42 -0
- data/spec/models/no_initial_state.rb +25 -0
- data/spec/models/nobrainer/complex_no_brainer_example.rb +36 -0
- data/spec/models/nobrainer/invalid_persistor_no_brainer.rb +39 -0
- data/spec/models/nobrainer/no_scope_no_brainer.rb +21 -0
- data/spec/models/nobrainer/nobrainer_relationships.rb +25 -0
- data/spec/models/nobrainer/silent_persistor_no_brainer.rb +39 -0
- data/spec/models/nobrainer/simple_new_dsl_nobrainer.rb +25 -0
- data/spec/models/nobrainer/simple_no_brainer.rb +23 -0
- data/spec/models/nobrainer/validator_no_brainer.rb +98 -0
- data/spec/models/not_auto_loaded/process.rb +21 -0
- data/spec/models/parametrised_event.rb +42 -0
- data/spec/models/parametrised_event_multiple.rb +29 -0
- data/spec/models/process_with_new_dsl.rb +31 -0
- data/spec/models/provided_state.rb +24 -0
- data/spec/models/redis/complex_redis_example.rb +40 -0
- data/spec/models/redis/redis_multiple.rb +20 -0
- data/spec/models/redis/redis_simple.rb +20 -0
- data/spec/models/sequel/complex_sequel_example.rb +46 -0
- data/spec/models/sequel/invalid_persistor.rb +52 -0
- data/spec/models/sequel/sequel_multiple.rb +25 -0
- data/spec/models/sequel/sequel_simple.rb +26 -0
- data/spec/models/sequel/silent_persistor.rb +50 -0
- data/spec/models/sequel/transactor.rb +112 -0
- data/spec/models/sequel/validator.rb +93 -0
- data/spec/models/sequel/worker.rb +12 -0
- data/spec/models/silencer.rb +27 -0
- data/spec/models/simple_custom_example.rb +53 -0
- data/spec/models/simple_example.rb +23 -0
- data/spec/models/simple_example_with_guard_args.rb +17 -0
- data/spec/models/simple_multiple_example.rb +42 -0
- data/spec/models/state_machine_with_failed_event.rb +20 -0
- data/spec/models/states_on_one_line_example.rb +8 -0
- data/spec/models/sub_class.rb +41 -0
- data/spec/models/sub_class_with_more_states.rb +18 -0
- data/spec/models/sub_classing.rb +3 -0
- data/spec/models/super_class.rb +46 -0
- data/spec/models/this_name_better_not_be_in_use.rb +11 -0
- data/spec/models/valid_state_name.rb +23 -0
- data/spec/spec_helper.rb +36 -0
- data/spec/spec_helpers/active_record.rb +8 -0
- data/spec/spec_helpers/dynamoid.rb +35 -0
- data/spec/spec_helpers/mongoid.rb +26 -0
- data/spec/spec_helpers/nobrainer.rb +15 -0
- data/spec/spec_helpers/redis.rb +18 -0
- data/spec/spec_helpers/remove_warnings.rb +1 -0
- data/spec/spec_helpers/sequel.rb +7 -0
- data/spec/unit/abstract_class_spec.rb +27 -0
- data/spec/unit/api_spec.rb +100 -0
- data/spec/unit/basic_two_state_machines_example_spec.rb +10 -0
- data/spec/unit/callback_multiple_spec.rb +304 -0
- data/spec/unit/callbacks_spec.rb +521 -0
- data/spec/unit/complex_example_spec.rb +93 -0
- data/spec/unit/complex_multiple_example_spec.rb +115 -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 +16 -0
- data/spec/unit/event_spec.rb +394 -0
- data/spec/unit/exception_spec.rb +11 -0
- data/spec/unit/guard_arguments_check_spec.rb +9 -0
- data/spec/unit/guard_multiple_spec.rb +60 -0
- data/spec/unit/guard_spec.rb +89 -0
- data/spec/unit/guard_with_params_multiple_spec.rb +10 -0
- data/spec/unit/guard_with_params_spec.rb +14 -0
- data/spec/unit/guard_without_from_specified_spec.rb +10 -0
- data/spec/unit/initial_state_multiple_spec.rb +15 -0
- data/spec/unit/initial_state_spec.rb +12 -0
- data/spec/unit/inspection_multiple_spec.rb +201 -0
- data/spec/unit/inspection_spec.rb +149 -0
- data/spec/unit/invoker_spec.rb +189 -0
- data/spec/unit/invokers/base_invoker_spec.rb +72 -0
- data/spec/unit/invokers/class_invoker_spec.rb +95 -0
- data/spec/unit/invokers/literal_invoker_spec.rb +86 -0
- data/spec/unit/invokers/proc_invoker_spec.rb +86 -0
- data/spec/unit/localizer_spec.rb +78 -0
- data/spec/unit/memory_leak_spec.rb +38 -0
- data/spec/unit/multiple_transitions_that_differ_only_by_guard_spec.rb +14 -0
- data/spec/unit/namespaced_multiple_example_spec.rb +75 -0
- data/spec/unit/new_dsl_spec.rb +12 -0
- data/spec/unit/override_warning_spec.rb +94 -0
- data/spec/unit/persistence/active_record_persistence_multiple_spec.rb +618 -0
- data/spec/unit/persistence/active_record_persistence_spec.rb +773 -0
- data/spec/unit/persistence/dynamoid_persistence_multiple_spec.rb +135 -0
- data/spec/unit/persistence/dynamoid_persistence_spec.rb +84 -0
- data/spec/unit/persistence/mongoid_persistence_multiple_spec.rb +200 -0
- data/spec/unit/persistence/mongoid_persistence_spec.rb +165 -0
- data/spec/unit/persistence/no_brainer_persistence_multiple_spec.rb +198 -0
- data/spec/unit/persistence/no_brainer_persistence_spec.rb +158 -0
- data/spec/unit/persistence/redis_persistence_multiple_spec.rb +88 -0
- data/spec/unit/persistence/redis_persistence_spec.rb +53 -0
- data/spec/unit/persistence/sequel_persistence_multiple_spec.rb +148 -0
- data/spec/unit/persistence/sequel_persistence_spec.rb +368 -0
- data/spec/unit/readme_spec.rb +41 -0
- data/spec/unit/reloading_spec.rb +15 -0
- data/spec/unit/rspec_matcher_spec.rb +88 -0
- data/spec/unit/simple_custom_example_spec.rb +39 -0
- data/spec/unit/simple_example_spec.rb +57 -0
- data/spec/unit/simple_multiple_example_spec.rb +91 -0
- data/spec/unit/state_spec.rb +89 -0
- data/spec/unit/states_on_one_line_example_spec.rb +16 -0
- data/spec/unit/subclassing_multiple_spec.rb +74 -0
- data/spec/unit/subclassing_spec.rb +46 -0
- data/spec/unit/transition_spec.rb +436 -0
- data/test/minitest_helper.rb +57 -0
- data/test/unit/minitest_matcher_test.rb +80 -0
- metadata +609 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AASM
|
4
|
+
module Core
|
5
|
+
module Invokers
|
6
|
+
##
|
7
|
+
# Literal invoker which allows to use strings or symbols to call
|
8
|
+
# record methods as state/event/transition callbacks.
|
9
|
+
class LiteralInvoker < BaseInvoker
|
10
|
+
def may_invoke?
|
11
|
+
subject.is_a?(String) || subject.is_a?(Symbol)
|
12
|
+
end
|
13
|
+
|
14
|
+
def log_failure
|
15
|
+
failures << subject
|
16
|
+
end
|
17
|
+
|
18
|
+
def invoke_subject
|
19
|
+
@result = exec_subject
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def subject_arity
|
25
|
+
@arity ||= record.__send__(:method, subject.to_sym).arity
|
26
|
+
end
|
27
|
+
|
28
|
+
# rubocop:disable Metrics/AbcSize
|
29
|
+
def exec_subject
|
30
|
+
raise(*record_error) unless record.respond_to?(subject, true)
|
31
|
+
return record.__send__(subject) if subject_arity.zero?
|
32
|
+
return record.__send__(subject, *args) if subject_arity < 0
|
33
|
+
record.__send__(subject, *args[0..(subject_arity - 1)])
|
34
|
+
end
|
35
|
+
# rubocop:enable Metrics/AbcSize
|
36
|
+
|
37
|
+
def record_error
|
38
|
+
[
|
39
|
+
NoMethodError,
|
40
|
+
'NoMethodError: undefined method ' \
|
41
|
+
"`#{subject}' for #{record.inspect}:#{record.class}"
|
42
|
+
]
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -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
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AASM::Core
|
4
|
+
class State
|
5
|
+
attr_reader :name, :state_machine, :options
|
6
|
+
|
7
|
+
def initialize(name, klass, state_machine, options={})
|
8
|
+
@name = name
|
9
|
+
@klass = klass
|
10
|
+
@state_machine = state_machine
|
11
|
+
update(options)
|
12
|
+
end
|
13
|
+
|
14
|
+
# called internally by Ruby 1.9 after clone()
|
15
|
+
def initialize_copy(orig)
|
16
|
+
super
|
17
|
+
@options = {}
|
18
|
+
orig.options.each_pair do |name, setting|
|
19
|
+
@options[name] = if setting.is_a?(Hash) || setting.is_a?(Array)
|
20
|
+
setting.dup
|
21
|
+
else
|
22
|
+
setting
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def ==(state)
|
28
|
+
if state.is_a? Symbol
|
29
|
+
name == state
|
30
|
+
else
|
31
|
+
name == state.name
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def <=>(state)
|
36
|
+
if state.is_a? Symbol
|
37
|
+
name <=> state
|
38
|
+
else
|
39
|
+
name <=> state.name
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
name.to_s
|
45
|
+
end
|
46
|
+
|
47
|
+
def fire_callbacks(action, record, *args)
|
48
|
+
action = @options[action]
|
49
|
+
catch :halt_aasm_chain do
|
50
|
+
action.is_a?(Array) ?
|
51
|
+
action.each {|a| _fire_callbacks(a, record, args)} :
|
52
|
+
_fire_callbacks(action, record, args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def display_name
|
57
|
+
@display_name ||= begin
|
58
|
+
if Module.const_defined?(:I18n)
|
59
|
+
localized_name
|
60
|
+
else
|
61
|
+
name.to_s.gsub(/_/, ' ').capitalize
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def localized_name
|
67
|
+
AASM::Localizer.new.human_state_name(@klass, self)
|
68
|
+
end
|
69
|
+
alias human_name localized_name
|
70
|
+
|
71
|
+
def for_select
|
72
|
+
[display_name, name.to_s]
|
73
|
+
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def update(options = {})
|
78
|
+
if options.key?(:display) then
|
79
|
+
@display_name = options.delete(:display)
|
80
|
+
end
|
81
|
+
@options = options
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def _fire_callbacks(action, record, args)
|
86
|
+
Invoker.new(action, record, args).invoke
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end # AASM
|
@@ -0,0 +1,83 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module AASM::Core
|
4
|
+
class Transition
|
5
|
+
include DslHelper
|
6
|
+
|
7
|
+
attr_reader :from, :to, :event, :opts, :failures
|
8
|
+
alias_method :options, :opts
|
9
|
+
|
10
|
+
def initialize(event, opts, &block)
|
11
|
+
add_options_from_dsl(opts, [:on_transition, :guard, :after, :success], &block) if block
|
12
|
+
|
13
|
+
@event = event
|
14
|
+
@from = opts[:from]
|
15
|
+
@to = opts[:to]
|
16
|
+
@guards = Array(opts[:guards]) + Array(opts[:guard]) + Array(opts[:if])
|
17
|
+
@unless = Array(opts[:unless]) #TODO: This could use a better name
|
18
|
+
@failures = []
|
19
|
+
|
20
|
+
if opts[:on_transition]
|
21
|
+
warn '[DEPRECATION] :on_transition is deprecated, use :after instead'
|
22
|
+
opts[:after] = Array(opts[:after]) + Array(opts[:on_transition])
|
23
|
+
end
|
24
|
+
@after = Array(opts[:after])
|
25
|
+
@after = @after[0] if @after.size == 1
|
26
|
+
|
27
|
+
@success = Array(opts[:success])
|
28
|
+
@success = @success[0] if @success.size == 1
|
29
|
+
|
30
|
+
@opts = opts
|
31
|
+
end
|
32
|
+
|
33
|
+
# called internally by Ruby 1.9 after clone()
|
34
|
+
def initialize_copy(orig)
|
35
|
+
super
|
36
|
+
@guards = @guards.dup
|
37
|
+
@unless = @unless.dup
|
38
|
+
@opts = {}
|
39
|
+
orig.opts.each_pair { |name, setting| @opts[name] = setting.is_a?(Hash) || setting.is_a?(Array) ? setting.dup : setting }
|
40
|
+
end
|
41
|
+
|
42
|
+
def allowed?(obj, *args)
|
43
|
+
invoke_callbacks_compatible_with_guard(@guards, obj, args, :guard => true) &&
|
44
|
+
invoke_callbacks_compatible_with_guard(@unless, obj, args, :unless => true)
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute(obj, *args)
|
48
|
+
invoke_callbacks_compatible_with_guard(event.state_machine.global_callbacks[:after_all_transitions], obj, args)
|
49
|
+
invoke_callbacks_compatible_with_guard(@after, obj, args)
|
50
|
+
end
|
51
|
+
|
52
|
+
def ==(obj)
|
53
|
+
@from == obj.from && @to == obj.to
|
54
|
+
end
|
55
|
+
|
56
|
+
def from?(value)
|
57
|
+
@from == value
|
58
|
+
end
|
59
|
+
|
60
|
+
def invoke_success_callbacks(obj, *args)
|
61
|
+
_fire_callbacks(@success, obj, args)
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def invoke_callbacks_compatible_with_guard(code, record, args, options={})
|
67
|
+
if record.respond_to?(:aasm)
|
68
|
+
record.aasm(event.state_machine.name).from_state = @from if record.aasm(event.state_machine.name).respond_to?(:from_state=)
|
69
|
+
record.aasm(event.state_machine.name).to_state = @to if record.aasm(event.state_machine.name).respond_to?(:to_state=)
|
70
|
+
end
|
71
|
+
|
72
|
+
Invoker.new(code, record, args)
|
73
|
+
.with_options(options)
|
74
|
+
.with_failures(failures)
|
75
|
+
.invoke
|
76
|
+
end
|
77
|
+
|
78
|
+
def _fire_callbacks(code, record, args)
|
79
|
+
Invoker.new(code, record, args).invoke
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end # AASM
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module DslHelper
|
2
|
+
|
3
|
+
class Proxy
|
4
|
+
attr_accessor :options
|
5
|
+
|
6
|
+
def initialize(options, valid_keys, source)
|
7
|
+
@valid_keys = valid_keys
|
8
|
+
@source = source
|
9
|
+
|
10
|
+
@options = options
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(name, *args, &block)
|
14
|
+
if @valid_keys.include?(name)
|
15
|
+
options[name] = Array(options[name])
|
16
|
+
options[name] << block if block
|
17
|
+
options[name] += Array(args)
|
18
|
+
else
|
19
|
+
@source.send name, *args, &block
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def add_options_from_dsl(options, valid_keys, &block)
|
25
|
+
proxy = Proxy.new(options, valid_keys, self)
|
26
|
+
proxy.instance_eval(&block)
|
27
|
+
proxy.options
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/lib/aasm/errors.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module AASM
|
2
|
+
|
3
|
+
class UnknownStateMachineError < RuntimeError; end
|
4
|
+
|
5
|
+
class InvalidTransition < RuntimeError
|
6
|
+
attr_reader :object, :event_name, :originating_state, :failures, :state_machine_name
|
7
|
+
|
8
|
+
def initialize(object, event_name, state_machine_name, failures = [])
|
9
|
+
@object, @event_name, @originating_state, @failures = object, event_name, object.aasm(state_machine_name).current_state, failures
|
10
|
+
@state_machine_name = state_machine_name
|
11
|
+
super("Event '#{event_name}' cannot transition from '#{originating_state}'.#{reasoning}")
|
12
|
+
end
|
13
|
+
|
14
|
+
def reasoning
|
15
|
+
" Failed callback(s): #{failures}." unless failures.empty?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class UndefinedState < RuntimeError; end
|
20
|
+
class NoDirectAssignmentError < RuntimeError; end
|
21
|
+
end
|
@@ -0,0 +1,133 @@
|
|
1
|
+
module AASM
|
2
|
+
class InstanceBase
|
3
|
+
|
4
|
+
attr_accessor :from_state, :to_state, :current_event
|
5
|
+
|
6
|
+
def initialize(instance, name=:default) # instance of the class including AASM, name of the state machine
|
7
|
+
@instance = instance
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_state
|
12
|
+
@instance.aasm_read_state(@name)
|
13
|
+
end
|
14
|
+
|
15
|
+
def current_state=(state)
|
16
|
+
@instance.aasm_write_state_without_persistence(state, @name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def enter_initial_state
|
20
|
+
state_name = determine_state_name(@instance.class.aasm(@name).initial_state)
|
21
|
+
state_object = state_object_for_name(state_name)
|
22
|
+
|
23
|
+
state_object.fire_callbacks(:before_enter, @instance)
|
24
|
+
self.current_state = state_name
|
25
|
+
state_object.fire_callbacks(:after_enter, @instance)
|
26
|
+
|
27
|
+
state_name
|
28
|
+
end
|
29
|
+
|
30
|
+
def human_state
|
31
|
+
AASM::Localizer.new.human_state_name(@instance.class, state_object_for_name(current_state))
|
32
|
+
end
|
33
|
+
|
34
|
+
def states(options={}, *args)
|
35
|
+
if options.has_key?(:permitted)
|
36
|
+
selected_events = events({:permitted => options[:permitted]}, *args)
|
37
|
+
# An array of arrays. Each inner array represents the transitions that
|
38
|
+
# transition from the current state for an event
|
39
|
+
event_transitions = selected_events.map {|e| e.transitions_from_state(current_state) }
|
40
|
+
|
41
|
+
# An array of :to transition states
|
42
|
+
to_state_names = event_transitions.map do |transitions|
|
43
|
+
return nil if transitions.empty?
|
44
|
+
|
45
|
+
# Return the :to state of the first transition that is allowed (or not) or nil
|
46
|
+
if options[:permitted]
|
47
|
+
transition = transitions.find { |t| t.allowed?(@instance, *args) }
|
48
|
+
else
|
49
|
+
transition = transitions.find { |t| !t.allowed?(@instance, *args) }
|
50
|
+
end
|
51
|
+
transition ? transition.to : nil
|
52
|
+
end.flatten.compact.uniq
|
53
|
+
|
54
|
+
# Select states that are in to_state_names
|
55
|
+
@instance.class.aasm(@name).states.select {|s| to_state_names.include?(s.name)}
|
56
|
+
else
|
57
|
+
@instance.class.aasm(@name).states
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def events(options={}, *args)
|
62
|
+
state = options[:state] || current_state
|
63
|
+
events = @instance.class.aasm(@name).events.select {|e| e.transitions_from_state?(state) }
|
64
|
+
|
65
|
+
options[:reject] = Array(options[:reject])
|
66
|
+
events.reject! { |e| options[:reject].include?(e.name) }
|
67
|
+
|
68
|
+
if options.has_key?(:permitted)
|
69
|
+
# filters the results of events_for_current_state so that only those that
|
70
|
+
# are really currently possible (given transition guards) are shown.
|
71
|
+
if options[:permitted]
|
72
|
+
events.select! { |e| @instance.send("may_#{e.name}?", *args) }
|
73
|
+
else
|
74
|
+
events.select! { |e| !@instance.send("may_#{e.name}?", *args) }
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
events
|
79
|
+
end
|
80
|
+
|
81
|
+
def permitted_transitions
|
82
|
+
events(permitted: true).flat_map do |event|
|
83
|
+
available_transitions = event.transitions_from_state(current_state)
|
84
|
+
allowed_transitions = available_transitions.select { |t| t.allowed?(@instance) }
|
85
|
+
|
86
|
+
allowed_transitions.map do |transition|
|
87
|
+
{ event: event.name, state: transition.to }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def state_object_for_name(name)
|
93
|
+
obj = @instance.class.aasm(@name).states.find {|s| s.name == name}
|
94
|
+
raise AASM::UndefinedState, "State :#{name} doesn't exist" if obj.nil?
|
95
|
+
obj
|
96
|
+
end
|
97
|
+
|
98
|
+
def determine_state_name(state)
|
99
|
+
case state
|
100
|
+
when Symbol, String
|
101
|
+
state
|
102
|
+
when Proc
|
103
|
+
state.call(@instance)
|
104
|
+
else
|
105
|
+
raise NotImplementedError, "Unrecognized state-type given. Expected Symbol, String, or Proc."
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def may_fire_event?(name, *args)
|
110
|
+
if event = @instance.class.aasm(@name).state_machine.events[name]
|
111
|
+
!!event.may_fire?(@instance, *args)
|
112
|
+
else
|
113
|
+
false # unknown event
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def fire(event_name, *args, &block)
|
118
|
+
@instance.send(event_name, *args, &block)
|
119
|
+
end
|
120
|
+
|
121
|
+
def fire!(event_name, *args, &block)
|
122
|
+
event_name = event_name.to_s.+("!").to_sym
|
123
|
+
@instance.send(event_name, *args, &block)
|
124
|
+
end
|
125
|
+
|
126
|
+
def set_current_state_with_persistence(state)
|
127
|
+
save_success = @instance.aasm_write_state(state, @name)
|
128
|
+
self.current_state = state if save_success
|
129
|
+
save_success
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
end
|