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,54 @@
|
|
1
|
+
module AASM
|
2
|
+
class Localizer
|
3
|
+
def human_event_name(klass, event)
|
4
|
+
checklist = ancestors_list(klass).inject([]) do |list, ancestor|
|
5
|
+
list << :"#{i18n_scope(klass)}.events.#{i18n_klass(ancestor)}.#{event}"
|
6
|
+
list
|
7
|
+
end
|
8
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => event.to_s.humanize)
|
9
|
+
end
|
10
|
+
|
11
|
+
def human_state_name(klass, state)
|
12
|
+
checklist = ancestors_list(klass).inject([]) do |list, ancestor|
|
13
|
+
list << item_for(klass, state, ancestor)
|
14
|
+
list << item_for(klass, state, ancestor, :old_style => true)
|
15
|
+
list
|
16
|
+
end
|
17
|
+
translate_queue(checklist) || I18n.translate(checklist.shift, :default => state.to_s.humanize)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def item_for(klass, state, ancestor, options={})
|
23
|
+
separator = options[:old_style] ? '.' : '/'
|
24
|
+
:"#{i18n_scope(klass)}.attributes.#{i18n_klass(ancestor)}.#{klass.aasm(state.state_machine.name).attribute_name}#{separator}#{state}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def translate_queue(checklist)
|
28
|
+
(0...(checklist.size-1)).each do |i|
|
29
|
+
begin
|
30
|
+
return I18n.translate(checklist.shift, :raise => true)
|
31
|
+
rescue I18n::MissingTranslationData
|
32
|
+
# that's okay
|
33
|
+
end
|
34
|
+
end
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
|
38
|
+
# added for rails 2.x compatibility
|
39
|
+
def i18n_scope(klass)
|
40
|
+
klass.respond_to?(:i18n_scope) ? klass.i18n_scope : :activerecord
|
41
|
+
end
|
42
|
+
|
43
|
+
# added for rails < 3.0.3 compatibility
|
44
|
+
def i18n_klass(klass)
|
45
|
+
klass.model_name.respond_to?(:i18n_key) ? klass.model_name.i18n_key : klass.name.underscore
|
46
|
+
end
|
47
|
+
|
48
|
+
def ancestors_list(klass)
|
49
|
+
klass.ancestors.select do |ancestor|
|
50
|
+
ancestor.respond_to?(:model_name) unless ancestor.name == 'ActiveRecord::Base'
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end # AASM
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Minitest::Assertions
|
2
|
+
def assert_event_allowed(object, event, options = {})
|
3
|
+
state_machine_name = options.fetch(:on, :default)
|
4
|
+
assert object.aasm(state_machine_name).may_fire_event?(event),
|
5
|
+
"Expected that the event :#{event} would be allowed (on :#{state_machine_name})"
|
6
|
+
end
|
7
|
+
|
8
|
+
def refute_event_allowed(object, event, options = {})
|
9
|
+
state_machine_name = options.fetch(:on, :default)
|
10
|
+
refute object.aasm(state_machine_name).may_fire_event?(event),
|
11
|
+
"Expected that the event :#{event} would not be allowed (on :#{state_machine_name})"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Minitest::Assertions
|
2
|
+
def assert_transition_to_allowed(object, to_state, options = {})
|
3
|
+
state_machine_name = options.fetch(:on, :default)
|
4
|
+
assert object.aasm(state_machine_name).states(permitted: true).include?(to_state),
|
5
|
+
"Expected that the state :#{to_state} would be reachable (on :#{state_machine_name})"
|
6
|
+
end
|
7
|
+
|
8
|
+
def refute_transition_to_allowed(object, to_state, options = {})
|
9
|
+
state_machine_name = options.fetch(:on, :default)
|
10
|
+
refute object.aasm(state_machine_name).states(permitted: true).include?(to_state),
|
11
|
+
"Expected that the state :#{to_state} would be reachable (on :#{state_machine_name})"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Minitest::Assertions
|
2
|
+
def assert_have_state(object, state, options = {})
|
3
|
+
state_machine_name = options.fetch(:on, :default)
|
4
|
+
assert object.aasm(state_machine_name).current_state == state,
|
5
|
+
"Expected that :#{object.aasm(state_machine_name).current_state} would be :#{state} (on :#{state_machine_name})"
|
6
|
+
end
|
7
|
+
|
8
|
+
def refute_have_state(object, state, options = {})
|
9
|
+
state_machine_name = options.fetch(:on, :default)
|
10
|
+
refute object.aasm(state_machine_name).current_state == state,
|
11
|
+
"Expected that :#{object.aasm(state_machine_name).current_state} would be :#{state} (on :#{state_machine_name})"
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Minitest::Assertions
|
2
|
+
def assert_transitions_from(object, from_state, *args)
|
3
|
+
options = args.first
|
4
|
+
options[:on] ||= :default
|
5
|
+
assert _transitions_from?(object, from_state, args, options),
|
6
|
+
"Expected transition state to :#{options[:to]} from :#{from_state} on event :#{options[:on_event]}, (on :#{options[:on]})"
|
7
|
+
end
|
8
|
+
|
9
|
+
def refute_transitions_from(object, from_state, *args)
|
10
|
+
options = args.first
|
11
|
+
options[:on] ||= :default
|
12
|
+
refute _transitions_from?(object, from_state, args, options),
|
13
|
+
"Expected transition state to :#{options[:to]} from :#{from_state} on event :#{options[:on_event]}, (on :#{options[:on]})"
|
14
|
+
end
|
15
|
+
|
16
|
+
def _transitions_from?(object, from_state, args, options)
|
17
|
+
state_machine_name = options[:on]
|
18
|
+
object.aasm(state_machine_name).current_state = from_state.to_sym
|
19
|
+
object.send(options[:on_event], *args) && options[:to].to_sym == object.aasm(state_machine_name).current_state
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'aasm/minitest'
|
2
|
+
|
3
|
+
module Minitest::Expectations
|
4
|
+
AASM.infect_an_assertion :assert_transitions_from, :must_transition_from, :do_not_flip
|
5
|
+
AASM.infect_an_assertion :refute_transitions_from, :wont_transition_from, :do_not_flip
|
6
|
+
|
7
|
+
AASM.infect_an_assertion :assert_transition_to_allowed, :must_allow_transition_to, :do_not_flip
|
8
|
+
AASM.infect_an_assertion :refute_transition_to_allowed, :wont_allow_transition_to, :do_not_flip
|
9
|
+
|
10
|
+
AASM.infect_an_assertion :assert_have_state, :must_have_state, :do_not_flip
|
11
|
+
AASM.infect_an_assertion :refute_have_state, :wont_have_state, :do_not_flip
|
12
|
+
|
13
|
+
AASM.infect_an_assertion :assert_event_allowed, :must_allow_event, :do_not_flip
|
14
|
+
AASM.infect_an_assertion :refute_event_allowed, :wont_allow_event, :do_not_flip
|
15
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module AASM
|
2
|
+
module Persistence
|
3
|
+
class << self
|
4
|
+
|
5
|
+
def load_persistence(base)
|
6
|
+
# Use a fancier auto-loading thingy, perhaps. When there are more persistence engines.
|
7
|
+
hierarchy = base.ancestors.map {|klass| klass.to_s}
|
8
|
+
|
9
|
+
if hierarchy.include?("ActiveRecord::Base")
|
10
|
+
require_persistence :active_record
|
11
|
+
include_persistence base, :active_record
|
12
|
+
elsif hierarchy.include?("Mongoid::Document")
|
13
|
+
require_persistence :mongoid
|
14
|
+
include_persistence base, :mongoid
|
15
|
+
elsif hierarchy.include?("NoBrainer::Document")
|
16
|
+
require_persistence :no_brainer
|
17
|
+
include_persistence base, :no_brainer
|
18
|
+
elsif hierarchy.include?("Sequel::Model")
|
19
|
+
require_persistence :sequel
|
20
|
+
include_persistence base, :sequel
|
21
|
+
elsif hierarchy.include?("Dynamoid::Document")
|
22
|
+
require_persistence :dynamoid
|
23
|
+
include_persistence base, :dynamoid
|
24
|
+
elsif hierarchy.include?("Redis::Objects")
|
25
|
+
require_persistence :redis
|
26
|
+
include_persistence base, :redis
|
27
|
+
elsif hierarchy.include?("CDQManagedObject")
|
28
|
+
include_persistence base, :core_data_query
|
29
|
+
else
|
30
|
+
include_persistence base, :plain
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def require_persistence(type)
|
37
|
+
require File.join(File.dirname(__FILE__), 'persistence', "#{type}_persistence")
|
38
|
+
end
|
39
|
+
|
40
|
+
def include_persistence(base, type)
|
41
|
+
base.send(:include, constantize("#{capitalize(type)}Persistence"))
|
42
|
+
end
|
43
|
+
|
44
|
+
def capitalize(string_or_symbol)
|
45
|
+
string_or_symbol.to_s.split('_').map {|segment| segment[0].upcase + segment[1..-1]}.join('')
|
46
|
+
end
|
47
|
+
|
48
|
+
def constantize(string)
|
49
|
+
AASM::Persistence.const_get(string)
|
50
|
+
end
|
51
|
+
|
52
|
+
end # class << self
|
53
|
+
end
|
54
|
+
end # AASM
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'aasm/persistence/orm'
|
2
|
+
module AASM
|
3
|
+
module Persistence
|
4
|
+
module ActiveRecordPersistence
|
5
|
+
# This method:
|
6
|
+
#
|
7
|
+
# * extends the model with ClassMethods
|
8
|
+
# * includes InstanceMethods
|
9
|
+
#
|
10
|
+
# Adds
|
11
|
+
#
|
12
|
+
# after_initialize :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 < ActiveRecord::Base
|
17
|
+
# def aasm_write_state(state)
|
18
|
+
# "bar"
|
19
|
+
# end
|
20
|
+
# include AASM
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# class Foo < ActiveRecord::Base
|
24
|
+
# include AASM
|
25
|
+
# def aasm_write_state(state)
|
26
|
+
# "bar"
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
def self.included(base)
|
31
|
+
base.send(:include, AASM::Persistence::Base)
|
32
|
+
base.send(:include, AASM::Persistence::ORM)
|
33
|
+
base.send(:include, AASM::Persistence::ActiveRecordPersistence::InstanceMethods)
|
34
|
+
base.extend AASM::Persistence::ActiveRecordPersistence::ClassMethods
|
35
|
+
|
36
|
+
base.after_initialize :aasm_ensure_initial_state
|
37
|
+
|
38
|
+
# ensure state is in the list of states
|
39
|
+
base.validate :aasm_validate_states
|
40
|
+
end
|
41
|
+
|
42
|
+
module ClassMethods
|
43
|
+
def aasm_create_scope(state_machine_name, scope_name)
|
44
|
+
if ActiveRecord::VERSION::MAJOR >= 3
|
45
|
+
conditions = { aasm(state_machine_name).attribute_name => scope_name.to_s }
|
46
|
+
class_eval do
|
47
|
+
scope scope_name, lambda { where(table_name => conditions) }
|
48
|
+
end
|
49
|
+
else
|
50
|
+
conditions = {
|
51
|
+
table_name => { aasm(state_machine_name).attribute_name => scope_name.to_s }
|
52
|
+
}
|
53
|
+
class_eval do
|
54
|
+
named_scope scope_name, :conditions => conditions
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
module InstanceMethods
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def aasm_raise_invalid_record
|
65
|
+
raise ActiveRecord::RecordInvalid.new(self)
|
66
|
+
end
|
67
|
+
|
68
|
+
def aasm_save
|
69
|
+
self.save
|
70
|
+
end
|
71
|
+
|
72
|
+
def aasm_update_column(attribute_name, value)
|
73
|
+
self.class.unscoped.where(self.class.primary_key => self.id).update_all(attribute_name => value) == 1
|
74
|
+
end
|
75
|
+
|
76
|
+
def aasm_read_attribute(name)
|
77
|
+
read_attribute(name)
|
78
|
+
end
|
79
|
+
|
80
|
+
def aasm_write_attribute(name, value)
|
81
|
+
write_attribute(name, value)
|
82
|
+
end
|
83
|
+
|
84
|
+
def aasm_transaction(requires_new, requires_lock)
|
85
|
+
self.class.transaction(:requires_new => requires_new) do
|
86
|
+
lock!(requires_lock) if requires_lock
|
87
|
+
yield
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def aasm_enum(name=:default)
|
92
|
+
case AASM::StateMachineStore.fetch(self.class, true).machine(name).config.enum
|
93
|
+
when false then nil
|
94
|
+
when true then aasm_guess_enum_method(name)
|
95
|
+
when nil then aasm_guess_enum_method(name) if aasm_column_looks_like_enum(name)
|
96
|
+
else AASM::StateMachineStore.fetch(self.class, true).machine(name).config.enum
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def aasm_column_looks_like_enum(name=:default)
|
101
|
+
column_name = self.class.aasm(name).attribute_name.to_s
|
102
|
+
column = self.class.columns_hash[column_name]
|
103
|
+
raise NoMethodError.new("undefined method '#{column_name}' for #{self.class}") if column.nil?
|
104
|
+
column.type == :integer
|
105
|
+
end
|
106
|
+
|
107
|
+
def aasm_guess_enum_method(name=:default)
|
108
|
+
self.class.aasm(name).attribute_name.to_s.pluralize.to_sym
|
109
|
+
end
|
110
|
+
|
111
|
+
def aasm_raw_attribute_value(state, name=:default)
|
112
|
+
if aasm_enum(name)
|
113
|
+
self.class.send(aasm_enum(name))[state]
|
114
|
+
else
|
115
|
+
super
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# Ensures that if the aasm_state column is nil and the record is new
|
120
|
+
# then the initial state gets populated after initialization
|
121
|
+
#
|
122
|
+
# foo = Foo.new
|
123
|
+
# foo.aasm_state # => "open" (where :open is the initial state)
|
124
|
+
#
|
125
|
+
#
|
126
|
+
# foo = Foo.find(:first)
|
127
|
+
# foo.aasm_state # => 1
|
128
|
+
# foo.aasm_state = nil
|
129
|
+
# foo.valid?
|
130
|
+
# foo.aasm_state # => nil
|
131
|
+
#
|
132
|
+
def aasm_ensure_initial_state
|
133
|
+
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
134
|
+
# checking via respond_to? does not work in Rails <= 3
|
135
|
+
# if respond_to?(self.class.aasm(state_machine_name).attribute_name) && send(self.class.aasm(state_machine_name).attribute_name).blank? # Rails 4
|
136
|
+
if aasm_column_is_blank?(state_machine_name)
|
137
|
+
aasm(state_machine_name).enter_initial_state
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def aasm_column_is_blank?(state_machine_name)
|
143
|
+
attribute_name = self.class.aasm(state_machine_name).attribute_name
|
144
|
+
attribute_names.include?(attribute_name.to_s) &&
|
145
|
+
(send(attribute_name).respond_to?(:empty?) ? !!send(attribute_name).empty? : !send(attribute_name))
|
146
|
+
end
|
147
|
+
|
148
|
+
def aasm_validate_states
|
149
|
+
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
150
|
+
unless aasm_skipping_validations(state_machine_name)
|
151
|
+
if aasm_invalid_state?(state_machine_name)
|
152
|
+
self.errors.add(AASM::StateMachineStore.fetch(self.class, true).machine(state_machine_name).config.column , "is invalid")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def aasm_invalid_state?(state_machine_name)
|
159
|
+
aasm(state_machine_name).current_state && !aasm(state_machine_name).states.include?(aasm(state_machine_name).current_state)
|
160
|
+
end
|
161
|
+
end # InstanceMethods
|
162
|
+
|
163
|
+
end
|
164
|
+
end # Persistence
|
165
|
+
end # AASM
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module AASM
|
2
|
+
module Persistence
|
3
|
+
module Base
|
4
|
+
|
5
|
+
def self.included(base) #:nodoc:
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
# Returns the value of the aasm.attribute_name - called from <tt>aasm.current_state</tt>
|
10
|
+
#
|
11
|
+
# If it's a new record, and the aasm state column is blank it returns the initial state
|
12
|
+
# (example provided here for ActiveRecord, but it's true for Mongoid as well):
|
13
|
+
#
|
14
|
+
# class Foo < ActiveRecord::Base
|
15
|
+
# include AASM
|
16
|
+
# aasm :column => :status do
|
17
|
+
# state :opened
|
18
|
+
# state :closed
|
19
|
+
# end
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# foo = Foo.new
|
23
|
+
# foo.current_state # => :opened
|
24
|
+
# foo.close
|
25
|
+
# foo.current_state # => :closed
|
26
|
+
#
|
27
|
+
# foo = Foo.find(1)
|
28
|
+
# foo.current_state # => :opened
|
29
|
+
# foo.aasm_state = nil
|
30
|
+
# foo.current_state # => nil
|
31
|
+
#
|
32
|
+
# NOTE: intended to be called from an event
|
33
|
+
#
|
34
|
+
# This allows for nil aasm states - be sure to add validation to your model
|
35
|
+
def aasm_read_state(name=:default)
|
36
|
+
state = send(self.class.aasm(name).attribute_name)
|
37
|
+
if !state || state.empty?
|
38
|
+
aasm_new_record? ? aasm(name).determine_state_name(self.class.aasm(name).initial_state) : nil
|
39
|
+
else
|
40
|
+
state.to_sym
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def aasm_new_record?
|
45
|
+
new_record?
|
46
|
+
end
|
47
|
+
|
48
|
+
module ClassMethods
|
49
|
+
def aasm_column(attribute_name=nil)
|
50
|
+
warn "[DEPRECATION] aasm_column is deprecated. Use aasm.attribute_name instead"
|
51
|
+
aasm.attribute_name(attribute_name)
|
52
|
+
end
|
53
|
+
end # ClassMethods
|
54
|
+
|
55
|
+
end # Base
|
56
|
+
end # Persistence
|
57
|
+
|
58
|
+
class Base
|
59
|
+
# make sure to create a (named) scope for each state
|
60
|
+
def state_with_scope(*args)
|
61
|
+
names = state_without_scope(*args)
|
62
|
+
names.each { |name| create_scope(name) if create_scope?(name) }
|
63
|
+
end
|
64
|
+
alias_method :state_without_scope, :state
|
65
|
+
alias_method :state, :state_with_scope
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def create_scope?(name)
|
70
|
+
@state_machine.config.create_scopes && !@klass.respond_to?(name) && @klass.respond_to?(:aasm_create_scope)
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_scope(name)
|
74
|
+
@klass.aasm_create_scope(@name, name)
|
75
|
+
end
|
76
|
+
end # Base
|
77
|
+
|
78
|
+
end # AASM
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module AASM
|
2
|
+
module Persistence
|
3
|
+
module CoreDataQueryPersistence
|
4
|
+
# This method:
|
5
|
+
#
|
6
|
+
# * extends the model with ClassMethods
|
7
|
+
# * includes InstanceMethods
|
8
|
+
#
|
9
|
+
# Adds
|
10
|
+
#
|
11
|
+
# after_initialize :aasm_ensure_initial_state
|
12
|
+
#
|
13
|
+
|
14
|
+
def self.included(base)
|
15
|
+
base.send(:include, AASM::Persistence::Base)
|
16
|
+
base.send(:include, AASM::Persistence::CoreDataQueryPersistence::InstanceMethods)
|
17
|
+
base.extend AASM::Persistence::CoreDataQueryPersistence::ClassMethods
|
18
|
+
|
19
|
+
base.after_initialize :aasm_ensure_initial_state
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def aasm_create_scope(state_machine_name, scope_name)
|
24
|
+
scope(scope_name.to_sym, lambda { where(aasm(state_machine_name).attribute_name.to_sym).eq(scope_name.to_s) })
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module InstanceMethods
|
29
|
+
|
30
|
+
# Writes <tt>state</tt> to the state column and persists it to the database
|
31
|
+
# using update_attribute (which bypasses validation)
|
32
|
+
#
|
33
|
+
# foo = Foo.find(1)
|
34
|
+
# foo.aasm.current_state # => :opened
|
35
|
+
# foo.close!
|
36
|
+
# foo.aasm.current_state # => :closed
|
37
|
+
# Foo.find(1).aasm.current_state # => :closed
|
38
|
+
#
|
39
|
+
# NOTE: intended to be called from an event
|
40
|
+
def aasm_write_state(state, name=:default)
|
41
|
+
raise "Cowardly refusing to save the current CoreDataQuery context"
|
42
|
+
aasm_write_state_without_persistence(state, name)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Writes <tt>state</tt> to the state column, but does not persist it to the database
|
46
|
+
#
|
47
|
+
# foo = Foo.find(1)
|
48
|
+
# foo.aasm.current_state # => :opened
|
49
|
+
# foo.close
|
50
|
+
# foo.aasm.current_state # => :closed
|
51
|
+
# Foo.find(1).aasm.current_state # => :opened
|
52
|
+
# foo.save
|
53
|
+
# foo.aasm.current_state # => :closed
|
54
|
+
# Foo.find(1).aasm.current_state # => :closed
|
55
|
+
#
|
56
|
+
# NOTE: intended to be called from an event
|
57
|
+
def aasm_write_state_without_persistence(state, name=:default)
|
58
|
+
write_attribute(self.class.aasm(name).attribute_name, state.to_s)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# Ensures that if the aasm_state column is nil and the record is new
|
64
|
+
# that the initial state gets populated before validation on create
|
65
|
+
#
|
66
|
+
# foo = Foo.new
|
67
|
+
# foo.aasm_state # => nil
|
68
|
+
# foo.valid?
|
69
|
+
# foo.aasm_state # => "open" (where :open is the initial state)
|
70
|
+
#
|
71
|
+
#
|
72
|
+
# foo = Foo.find(:first)
|
73
|
+
# foo.aasm_state # => 1
|
74
|
+
# foo.aasm_state = nil
|
75
|
+
# foo.valid?
|
76
|
+
# foo.aasm_state # => nil
|
77
|
+
#
|
78
|
+
def aasm_ensure_initial_state
|
79
|
+
AASM::StateMachineStore.fetch(self.class, true).machine_names.each do |state_machine_name|
|
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)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end # InstanceMethods
|
85
|
+
|
86
|
+
# module NamedScopeMethods
|
87
|
+
# def aasm_state_with_named_scope name, options = {}
|
88
|
+
# aasm_state_without_named_scope name, options
|
89
|
+
# self.named_scope name, :conditions => { "#{table_name}.#{self.aasm.attribute_name}" => name.to_s} unless self.respond_to?(name)
|
90
|
+
# end
|
91
|
+
# end
|
92
|
+
end
|
93
|
+
end # Persistence
|
94
|
+
end # AASM
|