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,26 @@
|
|
1
|
+
module AASM
|
2
|
+
module Persistence
|
3
|
+
module PlainPersistence
|
4
|
+
|
5
|
+
# may be overwritten by persistence mixins
|
6
|
+
def aasm_read_state(name=:default)
|
7
|
+
# all the following lines behave like @current_state ||= aasm(name).enter_initial_state
|
8
|
+
current = aasm(name).instance_variable_defined?("@current_state_#{name}") &&
|
9
|
+
aasm(name).instance_variable_get("@current_state_#{name}")
|
10
|
+
return current if current
|
11
|
+
aasm(name).instance_variable_set("@current_state_#{name}", aasm(name).enter_initial_state)
|
12
|
+
end
|
13
|
+
|
14
|
+
# may be overwritten by persistence mixins
|
15
|
+
def aasm_write_state(new_state, name=:default)
|
16
|
+
true
|
17
|
+
end
|
18
|
+
|
19
|
+
# may be overwritten by persistence mixins
|
20
|
+
def aasm_write_state_without_persistence(new_state, name=:default)
|
21
|
+
aasm(name).instance_variable_set("@current_state_#{name}", new_state)
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module AASM
|
2
|
+
module Persistence
|
3
|
+
module RedisPersistence
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:include, AASM::Persistence::Base)
|
7
|
+
base.send(:include, AASM::Persistence::RedisPersistence::InstanceMethods)
|
8
|
+
end
|
9
|
+
|
10
|
+
module InstanceMethods
|
11
|
+
# Initialize with default values
|
12
|
+
#
|
13
|
+
# Redis::Objects removes the key from Redis when set to `nil`
|
14
|
+
def initialize(*args)
|
15
|
+
super
|
16
|
+
aasm_ensure_initial_state
|
17
|
+
end
|
18
|
+
# Returns the value of the aasm.attribute_name - called from <tt>aasm.current_state</tt>
|
19
|
+
#
|
20
|
+
# If it's a new record, and the aasm state column is blank it returns the initial state
|
21
|
+
#
|
22
|
+
# class Foo
|
23
|
+
# include Redis::Objects
|
24
|
+
# include AASM
|
25
|
+
# aasm :column => :status do
|
26
|
+
# state :opened
|
27
|
+
# state :closed
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# foo = Foo.new
|
32
|
+
# foo.current_state # => :opened
|
33
|
+
# foo.close
|
34
|
+
# foo.current_state # => :closed
|
35
|
+
#
|
36
|
+
# foo = Foo[1]
|
37
|
+
# foo.current_state # => :opened
|
38
|
+
# foo.aasm_state = nil
|
39
|
+
# foo.current_state # => nil
|
40
|
+
#
|
41
|
+
# NOTE: intended to be called from an event
|
42
|
+
#
|
43
|
+
# This allows for nil aasm states - be sure to add validation to your model
|
44
|
+
def aasm_read_state(name=:default)
|
45
|
+
state = send(self.class.aasm(name).attribute_name)
|
46
|
+
|
47
|
+
if state.value.nil?
|
48
|
+
nil
|
49
|
+
else
|
50
|
+
state.value.to_sym
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Ensures that if the aasm_state column is nil and the record is new
|
55
|
+
# that the initial state gets populated before validation on create
|
56
|
+
#
|
57
|
+
# foo = Foo.new
|
58
|
+
# foo.aasm_state # => nil
|
59
|
+
# foo.valid?
|
60
|
+
# foo.aasm_state # => "open" (where :open is the initial state)
|
61
|
+
#
|
62
|
+
#
|
63
|
+
# foo = Foo.find(:first)
|
64
|
+
# foo.aasm_state # => 1
|
65
|
+
# foo.aasm_state = nil
|
66
|
+
# foo.valid?
|
67
|
+
# foo.aasm_state # => nil
|
68
|
+
#
|
69
|
+
def aasm_ensure_initial_state
|
70
|
+
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |name|
|
71
|
+
aasm_column = self.class.aasm(name).attribute_name
|
72
|
+
aasm(name).enter_initial_state if !send(aasm_column).value || send(aasm_column).value.empty?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# Writes <tt>state</tt> to the state column and persists it to the database
|
77
|
+
#
|
78
|
+
# foo = Foo[1]
|
79
|
+
# foo.aasm.current_state # => :opened
|
80
|
+
# foo.close!
|
81
|
+
# foo.aasm.current_state # => :closed
|
82
|
+
# Foo[1].aasm.current_state # => :closed
|
83
|
+
#
|
84
|
+
# NOTE: intended to be called from an event
|
85
|
+
def aasm_write_state(state, name=:default)
|
86
|
+
aasm_column = self.class.aasm(name).attribute_name
|
87
|
+
send("#{aasm_column}").value = state
|
88
|
+
end
|
89
|
+
|
90
|
+
# Writes <tt>state</tt> to the state column, but does not persist it to the database
|
91
|
+
# (but actually it still does)
|
92
|
+
#
|
93
|
+
# With Redis::Objects it's not possible to skip persisting - it's not an ORM,
|
94
|
+
# it does not operate like an AR model and does not know how to postpone changes.
|
95
|
+
#
|
96
|
+
# foo = Foo[1]
|
97
|
+
# foo.aasm.current_state # => :opened
|
98
|
+
# foo.close
|
99
|
+
# foo.aasm.current_state # => :closed
|
100
|
+
# Foo[1].aasm.current_state # => :opened
|
101
|
+
# foo.save
|
102
|
+
# foo.aasm.current_state # => :closed
|
103
|
+
# Foo[1].aasm.current_state # => :closed
|
104
|
+
#
|
105
|
+
# NOTE: intended to be called from an event
|
106
|
+
def aasm_write_state_without_persistence(state, name=:default)
|
107
|
+
aasm_write_state(state, name)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'aasm/persistence/orm'
|
2
|
+
module AASM
|
3
|
+
module Persistence
|
4
|
+
module SequelPersistence
|
5
|
+
def self.included(base)
|
6
|
+
base.send(:include, AASM::Persistence::Base)
|
7
|
+
base.send(:include, AASM::Persistence::ORM)
|
8
|
+
base.send(:include, AASM::Persistence::SequelPersistence::InstanceMethods)
|
9
|
+
end
|
10
|
+
|
11
|
+
module InstanceMethods
|
12
|
+
def before_validation
|
13
|
+
aasm_ensure_initial_state
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
def before_create
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def aasm_raise_invalid_record
|
22
|
+
raise Sequel::ValidationFailed.new(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def aasm_new_record?
|
26
|
+
new?
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns nil if fails silently
|
30
|
+
# http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-save
|
31
|
+
def aasm_save
|
32
|
+
!save(raise_on_failure: false).nil?
|
33
|
+
end
|
34
|
+
|
35
|
+
def aasm_read_attribute(name)
|
36
|
+
send(name)
|
37
|
+
end
|
38
|
+
|
39
|
+
def aasm_write_attribute(name, value)
|
40
|
+
send("#{name}=", value)
|
41
|
+
end
|
42
|
+
|
43
|
+
def aasm_transaction(requires_new, requires_lock)
|
44
|
+
self.class.db.transaction(savepoint: requires_new) do
|
45
|
+
if requires_lock
|
46
|
+
# http://sequel.jeremyevans.net/rdoc/classes/Sequel/Model/InstanceMethods.html#method-i-lock-21
|
47
|
+
requires_lock.is_a?(String) ? lock!(requires_lock) : lock!
|
48
|
+
end
|
49
|
+
yield
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def aasm_update_column(attribute_name, value)
|
54
|
+
this.update(attribute_name => value)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Ensures that if the aasm_state column is nil and the record is new
|
58
|
+
# that the initial state gets populated before validation on create
|
59
|
+
#
|
60
|
+
# foo = Foo.new
|
61
|
+
# foo.aasm_state # => nil
|
62
|
+
# foo.valid?
|
63
|
+
# foo.aasm_state # => "open" (where :open is the initial state)
|
64
|
+
#
|
65
|
+
#
|
66
|
+
# foo = Foo.find(:first)
|
67
|
+
# foo.aasm_state # => 1
|
68
|
+
# foo.aasm_state = nil
|
69
|
+
# foo.valid?
|
70
|
+
# foo.aasm_state # => nil
|
71
|
+
#
|
72
|
+
def aasm_ensure_initial_state
|
73
|
+
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
74
|
+
aasm(state_machine_name).enter_initial_state if
|
75
|
+
(new? || values.key?(self.class.aasm(state_machine_name).attribute_name)) &&
|
76
|
+
send(self.class.aasm(state_machine_name).attribute_name).to_s.strip.empty?
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
data/lib/aasm/rspec.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
RSpec::Matchers.define :allow_event do |event|
|
2
|
+
match do |obj|
|
3
|
+
@state_machine_name ||= :default
|
4
|
+
obj.aasm(@state_machine_name).may_fire_event?(event, *@args)
|
5
|
+
end
|
6
|
+
|
7
|
+
chain :on do |state_machine_name|
|
8
|
+
@state_machine_name = state_machine_name
|
9
|
+
end
|
10
|
+
|
11
|
+
chain :with do |*args|
|
12
|
+
@args = args
|
13
|
+
end
|
14
|
+
|
15
|
+
description do
|
16
|
+
"allow event #{expected} (on :#{@state_machine_name})"
|
17
|
+
end
|
18
|
+
|
19
|
+
failure_message do |obj|
|
20
|
+
"expected that the event :#{expected} would be allowed (on :#{@state_machine_name})"
|
21
|
+
end
|
22
|
+
|
23
|
+
failure_message_when_negated do |obj|
|
24
|
+
"expected that the event :#{expected} would not be allowed (on :#{@state_machine_name})"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
RSpec::Matchers.define :allow_transition_to do |state|
|
2
|
+
match do |obj|
|
3
|
+
@state_machine_name ||= :default
|
4
|
+
obj.aasm(@state_machine_name).states({:permitted => true}, *@args).include?(state)
|
5
|
+
end
|
6
|
+
|
7
|
+
chain :on do |state_machine_name|
|
8
|
+
@state_machine_name = state_machine_name
|
9
|
+
end
|
10
|
+
|
11
|
+
chain :with do |*args|
|
12
|
+
@args = args
|
13
|
+
end
|
14
|
+
|
15
|
+
description do
|
16
|
+
"allow transition to #{expected} (on :#{@state_machine_name})"
|
17
|
+
end
|
18
|
+
|
19
|
+
failure_message do |obj|
|
20
|
+
"expected that the state :#{expected} would be reachable (on :#{@state_machine_name})"
|
21
|
+
end
|
22
|
+
|
23
|
+
failure_message_when_negated do |obj|
|
24
|
+
"expected that the state :#{expected} would not be reachable (on :#{@state_machine_name})"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
RSpec::Matchers.define :have_state do |state|
|
2
|
+
match do |obj|
|
3
|
+
@state_machine_name ||= :default
|
4
|
+
obj.aasm(@state_machine_name).current_state == state.to_sym
|
5
|
+
end
|
6
|
+
|
7
|
+
chain :on do |state_machine_name|
|
8
|
+
@state_machine_name = state_machine_name
|
9
|
+
end
|
10
|
+
|
11
|
+
description do
|
12
|
+
"have state #{expected} (on :#{@state_machine_name})"
|
13
|
+
end
|
14
|
+
|
15
|
+
failure_message do |obj|
|
16
|
+
"expected that :#{obj.aasm(@state_machine_name).current_state} would be :#{expected} (on :#{@state_machine_name})"
|
17
|
+
end
|
18
|
+
|
19
|
+
failure_message_when_negated do |obj|
|
20
|
+
"expected that :#{obj.aasm(@state_machine_name).current_state} would not be :#{expected} (on :#{@state_machine_name})"
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
RSpec::Matchers.define :transition_from do |from_state|
|
2
|
+
match do |obj|
|
3
|
+
@state_machine_name ||= :default
|
4
|
+
obj.aasm(@state_machine_name).current_state = from_state.to_sym
|
5
|
+
begin
|
6
|
+
obj.send(@event, *@args) && obj.aasm(@state_machine_name).current_state == @to_state.to_sym
|
7
|
+
rescue AASM::InvalidTransition
|
8
|
+
false
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
chain :on do |state_machine_name|
|
13
|
+
@state_machine_name = state_machine_name
|
14
|
+
end
|
15
|
+
|
16
|
+
chain :to do |state|
|
17
|
+
@to_state = state
|
18
|
+
end
|
19
|
+
|
20
|
+
chain :on_event do |event, *args|
|
21
|
+
@event = event
|
22
|
+
@args = args
|
23
|
+
end
|
24
|
+
|
25
|
+
description do
|
26
|
+
"transition state to :#{@to_state} from :#{expected} on event :#{@event}, with params: #{@args} (on :#{@state_machine_name})"
|
27
|
+
end
|
28
|
+
|
29
|
+
failure_message do |obj|
|
30
|
+
"expected that :#{obj.aasm(@state_machine_name).current_state} would be :#{@to_state} (on :#{@state_machine_name})"
|
31
|
+
end
|
32
|
+
|
33
|
+
failure_message_when_negated do |obj|
|
34
|
+
"expected that :#{obj.aasm(@state_machine_name).current_state} would not be :#{@to_state} (on :#{@state_machine_name})"
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module AASM
|
2
|
+
class StateMachine
|
3
|
+
# the following four methods provide the storage of all state machines
|
4
|
+
|
5
|
+
attr_accessor :states, :events, :initial_state, :config, :name, :global_callbacks
|
6
|
+
|
7
|
+
def initialize(name)
|
8
|
+
@initial_state = nil
|
9
|
+
@states = []
|
10
|
+
@events = {}
|
11
|
+
@global_callbacks = {}
|
12
|
+
@config = AASM::Configuration.new
|
13
|
+
@name = name
|
14
|
+
end
|
15
|
+
|
16
|
+
# called internally by Ruby 1.9 after clone()
|
17
|
+
def initialize_copy(orig)
|
18
|
+
super
|
19
|
+
@states = orig.states.collect { |state| state.clone }
|
20
|
+
@events = {}
|
21
|
+
orig.events.each_pair { |name, event| @events[name] = event.clone }
|
22
|
+
@global_callbacks = @global_callbacks.dup
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_state(state_name, klass, options)
|
26
|
+
set_initial_state(state_name, options)
|
27
|
+
|
28
|
+
# allow reloading, extending or redefining a state
|
29
|
+
@states.delete(state_name) if @states.include?(state_name)
|
30
|
+
|
31
|
+
@states << AASM::Core::State.new(state_name, klass, self, options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def add_event(name, options, &block)
|
35
|
+
@events[name] = AASM::Core::Event.new(name, self, options, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
def add_global_callbacks(name, *callbacks, &block)
|
39
|
+
@global_callbacks[name] ||= []
|
40
|
+
callbacks.each do |callback|
|
41
|
+
@global_callbacks[name] << callback unless @global_callbacks[name].include? callback
|
42
|
+
end
|
43
|
+
@global_callbacks[name] << block if block
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def set_initial_state(name, options)
|
49
|
+
@initial_state = name if options[:initial] || !initial_state
|
50
|
+
end
|
51
|
+
|
52
|
+
end # StateMachine
|
53
|
+
end # AASM
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'concurrent'
|
2
|
+
module AASM
|
3
|
+
class StateMachineStore
|
4
|
+
@stores = Concurrent::Map.new
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def stores
|
8
|
+
@stores
|
9
|
+
end
|
10
|
+
|
11
|
+
# do not overwrite existing state machines, which could have been created by
|
12
|
+
# inheritance, see AASM::ClassMethods method inherited
|
13
|
+
def register(klass, overwrite = false, state_machine = nil)
|
14
|
+
raise "Cannot register #{klass}" unless klass.is_a?(Class)
|
15
|
+
|
16
|
+
case name = template = overwrite
|
17
|
+
when FalseClass then stores[klass.to_s] ||= new
|
18
|
+
when TrueClass then stores[klass.to_s] = new
|
19
|
+
when Class then stores[klass.to_s] = stores[template.to_s].clone
|
20
|
+
when Symbol then stores[klass.to_s].register(name, state_machine)
|
21
|
+
when String then stores[klass.to_s].register(name, state_machine)
|
22
|
+
else raise "Don't know what to do with #{overwrite}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
alias_method :[]=, :register
|
26
|
+
|
27
|
+
def fetch(klass, fallback = nil)
|
28
|
+
stores[klass.to_s] || fallback && begin
|
29
|
+
match = klass.ancestors.find do |ancestor|
|
30
|
+
ancestor.include? AASM and stores[ancestor.to_s]
|
31
|
+
end
|
32
|
+
|
33
|
+
stores[match.to_s]
|
34
|
+
end
|
35
|
+
end
|
36
|
+
alias_method :[], :fetch
|
37
|
+
|
38
|
+
def unregister(klass)
|
39
|
+
stores.delete(klass.to_s)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize
|
44
|
+
@machines = Concurrent::Map.new
|
45
|
+
end
|
46
|
+
|
47
|
+
def clone
|
48
|
+
StateMachineStore.new.tap do |store|
|
49
|
+
@machines.each_pair do |name, machine|
|
50
|
+
store.register(name, machine.clone)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def machine(name)
|
56
|
+
@machines[name.to_s]
|
57
|
+
end
|
58
|
+
alias_method :[], :machine
|
59
|
+
|
60
|
+
def machine_names
|
61
|
+
@machines.keys
|
62
|
+
end
|
63
|
+
alias_method :keys, :machine_names
|
64
|
+
|
65
|
+
def register(name, machine, force = false)
|
66
|
+
raise "Cannot use #{name.inspect} for machine name" unless name.is_a?(Symbol) or name.is_a?(String)
|
67
|
+
raise "Cannot use #{machine.inspect} as a machine" unless machine.is_a?(AASM::StateMachine)
|
68
|
+
|
69
|
+
if force
|
70
|
+
@machines[name.to_s] = machine
|
71
|
+
else
|
72
|
+
@machines[name.to_s] ||= machine
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|