telvue_state_machine 1.2.1
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/.gitignore +8 -0
- data/.travis.yml +72 -0
- data/.yardopts +5 -0
- data/Appraisals +491 -0
- data/CHANGELOG.md +502 -0
- data/Gemfile +3 -0
- data/LICENSE +20 -0
- data/README.md +1263 -0
- data/Rakefile +41 -0
- data/examples/AutoShop_state.png +0 -0
- data/examples/Car_state.png +0 -0
- data/examples/Gemfile +5 -0
- data/examples/Gemfile.lock +14 -0
- data/examples/TrafficLight_state.png +0 -0
- data/examples/Vehicle_state.png +0 -0
- data/examples/auto_shop.rb +13 -0
- data/examples/car.rb +21 -0
- data/examples/doc/AutoShop.html +2856 -0
- data/examples/doc/AutoShop_state.png +0 -0
- data/examples/doc/Car.html +919 -0
- data/examples/doc/Car_state.png +0 -0
- data/examples/doc/TrafficLight.html +2230 -0
- data/examples/doc/TrafficLight_state.png +0 -0
- data/examples/doc/Vehicle.html +7921 -0
- data/examples/doc/Vehicle_state.png +0 -0
- data/examples/doc/_index.html +136 -0
- data/examples/doc/class_list.html +47 -0
- data/examples/doc/css/common.css +1 -0
- data/examples/doc/css/full_list.css +55 -0
- data/examples/doc/css/style.css +322 -0
- data/examples/doc/file_list.html +46 -0
- data/examples/doc/frames.html +13 -0
- data/examples/doc/index.html +136 -0
- data/examples/doc/js/app.js +205 -0
- data/examples/doc/js/full_list.js +173 -0
- data/examples/doc/js/jquery.js +16 -0
- data/examples/doc/method_list.html +734 -0
- data/examples/doc/top-level-namespace.html +105 -0
- data/examples/merb-rest/controller.rb +51 -0
- data/examples/merb-rest/model.rb +28 -0
- data/examples/merb-rest/view_edit.html.erb +24 -0
- data/examples/merb-rest/view_index.html.erb +23 -0
- data/examples/merb-rest/view_new.html.erb +13 -0
- data/examples/merb-rest/view_show.html.erb +17 -0
- data/examples/rails-rest/controller.rb +43 -0
- data/examples/rails-rest/migration.rb +7 -0
- data/examples/rails-rest/model.rb +23 -0
- data/examples/rails-rest/view__form.html.erb +34 -0
- data/examples/rails-rest/view_edit.html.erb +6 -0
- data/examples/rails-rest/view_index.html.erb +25 -0
- data/examples/rails-rest/view_new.html.erb +5 -0
- data/examples/rails-rest/view_show.html.erb +19 -0
- data/examples/traffic_light.rb +9 -0
- data/examples/vehicle.rb +33 -0
- data/gemfiles/active_model_3.0.0.gemfile +7 -0
- data/gemfiles/active_model_3.0.0.gemfile.lock +35 -0
- data/gemfiles/active_model_3.0.5.gemfile +7 -0
- data/gemfiles/active_model_3.0.5.gemfile.lock +35 -0
- data/gemfiles/active_model_3.1.1.gemfile +7 -0
- data/gemfiles/active_model_3.1.1.gemfile.lock +36 -0
- data/gemfiles/active_model_3.2.1.gemfile +7 -0
- data/gemfiles/active_model_3.2.12.gemfile +7 -0
- data/gemfiles/active_model_3.2.12.gemfile.lock +36 -0
- data/gemfiles/active_model_3.2.13.rc1.gemfile +7 -0
- data/gemfiles/active_model_3.2.13.rc1.gemfile.lock +36 -0
- data/gemfiles/active_model_4.0.0.gemfile +9 -0
- data/gemfiles/active_model_4.0.0.gemfile.lock +78 -0
- data/gemfiles/active_record_2.0.0.gemfile +9 -0
- data/gemfiles/active_record_2.0.0.gemfile.lock +39 -0
- data/gemfiles/active_record_2.0.5.gemfile +9 -0
- data/gemfiles/active_record_2.0.5.gemfile.lock +39 -0
- data/gemfiles/active_record_2.1.0.gemfile +9 -0
- data/gemfiles/active_record_2.1.0.gemfile.lock +39 -0
- data/gemfiles/active_record_2.1.2.gemfile +9 -0
- data/gemfiles/active_record_2.1.2.gemfile.lock +39 -0
- data/gemfiles/active_record_2.2.3.gemfile +9 -0
- data/gemfiles/active_record_2.2.3.gemfile.lock +39 -0
- data/gemfiles/active_record_2.3.12.gemfile +9 -0
- data/gemfiles/active_record_2.3.12.gemfile.lock +39 -0
- data/gemfiles/active_record_2.3.5.gemfile +9 -0
- data/gemfiles/active_record_2.3.5.gemfile.lock +39 -0
- data/gemfiles/active_record_3.0.0.gemfile +9 -0
- data/gemfiles/active_record_3.0.0.gemfile.lock +51 -0
- data/gemfiles/active_record_3.0.5.gemfile +9 -0
- data/gemfiles/active_record_3.0.5.gemfile.lock +50 -0
- data/gemfiles/active_record_3.1.1.gemfile +9 -0
- data/gemfiles/active_record_3.1.1.gemfile.lock +51 -0
- data/gemfiles/active_record_3.2.12.gemfile +9 -0
- data/gemfiles/active_record_3.2.12.gemfile.lock +51 -0
- data/gemfiles/active_record_3.2.13.rc1.gemfile +9 -0
- data/gemfiles/active_record_3.2.13.rc1.gemfile.lock +51 -0
- data/gemfiles/active_record_4.0.0.gemfile +11 -0
- data/gemfiles/active_record_4.0.0.gemfile.lock +83 -0
- data/gemfiles/data_mapper_0.10.2.gemfile +13 -0
- data/gemfiles/data_mapper_0.10.2.gemfile.lock +56 -0
- data/gemfiles/data_mapper_0.9.11.gemfile +13 -0
- data/gemfiles/data_mapper_0.9.11.gemfile.lock +71 -0
- data/gemfiles/data_mapper_0.9.4.gemfile +12 -0
- data/gemfiles/data_mapper_0.9.4.gemfile.lock +70 -0
- data/gemfiles/data_mapper_0.9.7.gemfile +13 -0
- data/gemfiles/data_mapper_0.9.7.gemfile.lock +67 -0
- data/gemfiles/data_mapper_1.0.0.gemfile +12 -0
- data/gemfiles/data_mapper_1.0.0.gemfile.lock +63 -0
- data/gemfiles/data_mapper_1.0.1.gemfile +12 -0
- data/gemfiles/data_mapper_1.0.1.gemfile.lock +63 -0
- data/gemfiles/data_mapper_1.0.2.gemfile +12 -0
- data/gemfiles/data_mapper_1.0.2.gemfile.lock +63 -0
- data/gemfiles/data_mapper_1.1.0.gemfile +12 -0
- data/gemfiles/data_mapper_1.1.0.gemfile.lock +61 -0
- data/gemfiles/data_mapper_1.2.0.gemfile +12 -0
- data/gemfiles/data_mapper_1.2.0.gemfile.lock +61 -0
- data/gemfiles/default.gemfile +7 -0
- data/gemfiles/default.gemfile.lock +27 -0
- data/gemfiles/graphviz_0.9.17.gemfile +7 -0
- data/gemfiles/graphviz_0.9.17.gemfile.lock +29 -0
- data/gemfiles/graphviz_0.9.21.gemfile +7 -0
- data/gemfiles/graphviz_0.9.21.gemfile.lock +29 -0
- data/gemfiles/graphviz_1.0.0.gemfile +7 -0
- data/gemfiles/graphviz_1.0.0.gemfile.lock +29 -0
- data/gemfiles/graphviz_1.0.3.gemfile +7 -0
- data/gemfiles/graphviz_1.0.3.gemfile.lock +29 -0
- data/gemfiles/graphviz_1.0.8.gemfile +7 -0
- data/gemfiles/graphviz_1.0.8.gemfile.lock +29 -0
- data/gemfiles/mongo_mapper_0.10.0.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.10.0.gemfile.lock +47 -0
- data/gemfiles/mongo_mapper_0.11.2.gemfile +9 -0
- data/gemfiles/mongo_mapper_0.11.2.gemfile.lock +48 -0
- data/gemfiles/mongo_mapper_0.12.0.gemfile +9 -0
- data/gemfiles/mongo_mapper_0.12.0.gemfile.lock +48 -0
- data/gemfiles/mongo_mapper_0.5.5.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.5.5.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.5.8.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.5.8.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.6.0.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.6.0.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.6.10.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.6.10.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.7.0.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.7.0.gemfile.lock +36 -0
- data/gemfiles/mongo_mapper_0.7.5.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.7.5.gemfile.lock +39 -0
- data/gemfiles/mongo_mapper_0.8.0.gemfile +10 -0
- data/gemfiles/mongo_mapper_0.8.0.gemfile.lock +43 -0
- data/gemfiles/mongo_mapper_0.8.3.gemfile +10 -0
- data/gemfiles/mongo_mapper_0.8.3.gemfile.lock +43 -0
- data/gemfiles/mongo_mapper_0.8.4.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.8.4.gemfile.lock +42 -0
- data/gemfiles/mongo_mapper_0.8.6.gemfile +8 -0
- data/gemfiles/mongo_mapper_0.8.6.gemfile.lock +42 -0
- data/gemfiles/mongo_mapper_0.9.0.gemfile +7 -0
- data/gemfiles/mongo_mapper_0.9.0.gemfile.lock +45 -0
- data/gemfiles/mongoid_2.0.0.gemfile +9 -0
- data/gemfiles/mongoid_2.0.0.gemfile.lock +49 -0
- data/gemfiles/mongoid_2.1.4.gemfile +9 -0
- data/gemfiles/mongoid_2.1.4.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.2.4.gemfile +9 -0
- data/gemfiles/mongoid_2.2.4.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.3.3.gemfile +9 -0
- data/gemfiles/mongoid_2.3.3.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.4.0.gemfile +9 -0
- data/gemfiles/mongoid_2.4.0.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.4.10.gemfile +9 -0
- data/gemfiles/mongoid_2.4.10.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.5.2.gemfile +9 -0
- data/gemfiles/mongoid_2.5.2.gemfile.lock +47 -0
- data/gemfiles/mongoid_2.6.0.gemfile +9 -0
- data/gemfiles/mongoid_2.6.0.gemfile.lock +47 -0
- data/gemfiles/mongoid_3.0.0.gemfile +8 -0
- data/gemfiles/mongoid_3.0.0.gemfile.lock +45 -0
- data/gemfiles/mongoid_3.0.22.gemfile +8 -0
- data/gemfiles/mongoid_3.0.22.gemfile.lock +45 -0
- data/gemfiles/mongoid_3.1.0.gemfile +8 -0
- data/gemfiles/mongoid_3.1.0.gemfile.lock +45 -0
- data/gemfiles/sequel_2.11.0.gemfile +9 -0
- data/gemfiles/sequel_2.11.0.gemfile.lock +33 -0
- data/gemfiles/sequel_2.12.0.gemfile +9 -0
- data/gemfiles/sequel_2.12.0.gemfile.lock +33 -0
- data/gemfiles/sequel_2.8.0.gemfile +9 -0
- data/gemfiles/sequel_2.8.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.0.0.gemfile +9 -0
- data/gemfiles/sequel_3.0.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.10.0.gemfile +9 -0
- data/gemfiles/sequel_3.10.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.13.0.gemfile +9 -0
- data/gemfiles/sequel_3.13.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.14.0.gemfile +9 -0
- data/gemfiles/sequel_3.14.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.23.0.gemfile +9 -0
- data/gemfiles/sequel_3.23.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.24.0.gemfile +9 -0
- data/gemfiles/sequel_3.24.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.29.0.gemfile +9 -0
- data/gemfiles/sequel_3.29.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.34.0.gemfile +9 -0
- data/gemfiles/sequel_3.34.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.35.0.gemfile +9 -0
- data/gemfiles/sequel_3.35.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.4.0.gemfile +9 -0
- data/gemfiles/sequel_3.4.0.gemfile.lock +33 -0
- data/gemfiles/sequel_3.44.0.gemfile +9 -0
- data/gemfiles/sequel_3.44.0.gemfile.lock +33 -0
- data/init.rb +1 -0
- data/lib/state_machine.rb +8 -0
- data/lib/state_machine/assertions.rb +36 -0
- data/lib/state_machine/branch.rb +225 -0
- data/lib/state_machine/callback.rb +236 -0
- data/lib/state_machine/core.rb +12 -0
- data/lib/state_machine/core_ext.rb +2 -0
- data/lib/state_machine/core_ext/class/state_machine.rb +5 -0
- data/lib/state_machine/error.rb +13 -0
- data/lib/state_machine/eval_helpers.rb +87 -0
- data/lib/state_machine/event.rb +257 -0
- data/lib/state_machine/event_collection.rb +141 -0
- data/lib/state_machine/extensions.rb +149 -0
- data/lib/state_machine/graph.rb +92 -0
- data/lib/state_machine/helper_module.rb +17 -0
- data/lib/state_machine/initializers.rb +4 -0
- data/lib/state_machine/initializers/merb.rb +1 -0
- data/lib/state_machine/initializers/rails.rb +25 -0
- data/lib/state_machine/integrations.rb +121 -0
- data/lib/state_machine/integrations/active_model.rb +585 -0
- data/lib/state_machine/integrations/active_model/locale.rb +11 -0
- data/lib/state_machine/integrations/active_model/observer.rb +33 -0
- data/lib/state_machine/integrations/active_model/observer_update.rb +42 -0
- data/lib/state_machine/integrations/active_model/versions.rb +31 -0
- data/lib/state_machine/integrations/active_record.rb +552 -0
- data/lib/state_machine/integrations/active_record/locale.rb +20 -0
- data/lib/state_machine/integrations/active_record/versions.rb +123 -0
- data/lib/state_machine/integrations/base.rb +100 -0
- data/lib/state_machine/integrations/data_mapper.rb +511 -0
- data/lib/state_machine/integrations/data_mapper/observer.rb +210 -0
- data/lib/state_machine/integrations/data_mapper/versions.rb +85 -0
- data/lib/state_machine/integrations/mongo_mapper.rb +389 -0
- data/lib/state_machine/integrations/mongo_mapper/locale.rb +4 -0
- data/lib/state_machine/integrations/mongo_mapper/versions.rb +89 -0
- data/lib/state_machine/integrations/mongoid.rb +465 -0
- data/lib/state_machine/integrations/mongoid/locale.rb +4 -0
- data/lib/state_machine/integrations/mongoid/versions.rb +81 -0
- data/lib/state_machine/integrations/sequel.rb +486 -0
- data/lib/state_machine/integrations/sequel/versions.rb +95 -0
- data/lib/state_machine/machine.rb +2292 -0
- data/lib/state_machine/machine_collection.rb +86 -0
- data/lib/state_machine/macro_methods.rb +522 -0
- data/lib/state_machine/matcher.rb +123 -0
- data/lib/state_machine/matcher_helpers.rb +54 -0
- data/lib/state_machine/node_collection.rb +222 -0
- data/lib/state_machine/path.rb +120 -0
- data/lib/state_machine/path_collection.rb +90 -0
- data/lib/state_machine/state.rb +297 -0
- data/lib/state_machine/state_collection.rb +112 -0
- data/lib/state_machine/state_context.rb +138 -0
- data/lib/state_machine/transition.rb +470 -0
- data/lib/state_machine/transition_collection.rb +245 -0
- data/lib/state_machine/version.rb +3 -0
- data/lib/state_machine/yard.rb +8 -0
- data/lib/state_machine/yard/handlers.rb +12 -0
- data/lib/state_machine/yard/handlers/base.rb +32 -0
- data/lib/state_machine/yard/handlers/event.rb +25 -0
- data/lib/state_machine/yard/handlers/machine.rb +344 -0
- data/lib/state_machine/yard/handlers/state.rb +25 -0
- data/lib/state_machine/yard/handlers/transition.rb +47 -0
- data/lib/state_machine/yard/templates.rb +3 -0
- data/lib/state_machine/yard/templates/default/class/html/setup.rb +30 -0
- data/lib/state_machine/yard/templates/default/class/html/state_machines.erb +12 -0
- data/lib/tasks/state_machine.rake +1 -0
- data/lib/tasks/state_machine.rb +30 -0
- data/lib/yard-state_machine.rb +2 -0
- data/state_machine.gemspec +22 -0
- data/test/files/en.yml +17 -0
- data/test/files/switch.rb +15 -0
- data/test/functional/state_machine_test.rb +1066 -0
- data/test/test_helper.rb +7 -0
- data/test/unit/assertions_test.rb +40 -0
- data/test/unit/branch_test.rb +969 -0
- data/test/unit/callback_test.rb +704 -0
- data/test/unit/error_test.rb +43 -0
- data/test/unit/eval_helpers_test.rb +270 -0
- data/test/unit/event_collection_test.rb +398 -0
- data/test/unit/event_test.rb +1196 -0
- data/test/unit/graph_test.rb +98 -0
- data/test/unit/helper_module_test.rb +17 -0
- data/test/unit/integrations/active_model_test.rb +1245 -0
- data/test/unit/integrations/active_record_test.rb +2551 -0
- data/test/unit/integrations/base_test.rb +104 -0
- data/test/unit/integrations/data_mapper_test.rb +2194 -0
- data/test/unit/integrations/mongo_mapper_test.rb +2026 -0
- data/test/unit/integrations/mongoid_test.rb +2309 -0
- data/test/unit/integrations/sequel_test.rb +1896 -0
- data/test/unit/integrations_test.rb +83 -0
- data/test/unit/invalid_event_test.rb +20 -0
- data/test/unit/invalid_parallel_transition_test.rb +18 -0
- data/test/unit/invalid_transition_test.rb +115 -0
- data/test/unit/machine_collection_test.rb +603 -0
- data/test/unit/machine_test.rb +3431 -0
- data/test/unit/matcher_helpers_test.rb +37 -0
- data/test/unit/matcher_test.rb +155 -0
- data/test/unit/node_collection_test.rb +362 -0
- data/test/unit/path_collection_test.rb +266 -0
- data/test/unit/path_test.rb +485 -0
- data/test/unit/state_collection_test.rb +352 -0
- data/test/unit/state_context_test.rb +441 -0
- data/test/unit/state_machine_test.rb +31 -0
- data/test/unit/state_test.rb +1101 -0
- data/test/unit/transition_collection_test.rb +2168 -0
- data/test/unit/transition_test.rb +1558 -0
- metadata +435 -0
@@ -0,0 +1,245 @@
|
|
1
|
+
module StateMachine
|
2
|
+
# Represents a collection of transitions in a state machine
|
3
|
+
class TransitionCollection < Array
|
4
|
+
include Assertions
|
5
|
+
|
6
|
+
# Whether to skip running the action for each transition's machine
|
7
|
+
attr_reader :skip_actions
|
8
|
+
|
9
|
+
# Whether to skip running the after callbacks
|
10
|
+
attr_reader :skip_after
|
11
|
+
|
12
|
+
# Whether transitions should wrapped around a transaction block
|
13
|
+
attr_reader :use_transaction
|
14
|
+
|
15
|
+
# Creates a new collection of transitions that can be run in parallel. Each
|
16
|
+
# transition *must* be for a different attribute.
|
17
|
+
#
|
18
|
+
# Configuration options:
|
19
|
+
# * <tt>:actions</tt> - Whether to run the action configured for each transition
|
20
|
+
# * <tt>:after</tt> - Whether to run after callbacks
|
21
|
+
# * <tt>:transaction</tt> - Whether to wrap transitions within a transaction
|
22
|
+
def initialize(transitions = [], options = {})
|
23
|
+
super(transitions)
|
24
|
+
|
25
|
+
# Determine the validity of the transitions as a whole
|
26
|
+
@valid = all?
|
27
|
+
reject! {|transition| !transition}
|
28
|
+
|
29
|
+
attributes = map {|transition| transition.attribute}.uniq
|
30
|
+
raise ArgumentError, 'Cannot perform multiple transitions in parallel for the same state machine attribute' if attributes.length != length
|
31
|
+
|
32
|
+
assert_valid_keys(options, :actions, :after, :transaction)
|
33
|
+
options = {:actions => true, :after => true, :transaction => true}.merge(options)
|
34
|
+
@skip_actions = !options[:actions]
|
35
|
+
@skip_after = !options[:after]
|
36
|
+
@use_transaction = options[:transaction]
|
37
|
+
end
|
38
|
+
|
39
|
+
# Runs each of the collection's transitions in parallel.
|
40
|
+
#
|
41
|
+
# All transitions will run through the following steps:
|
42
|
+
# 1. Before callbacks
|
43
|
+
# 2. Persist state
|
44
|
+
# 3. Invoke action
|
45
|
+
# 4. After callbacks (if configured)
|
46
|
+
# 5. Rollback (if action is unsuccessful)
|
47
|
+
#
|
48
|
+
# If a block is passed to this method, that block will be called instead
|
49
|
+
# of invoking each transition's action.
|
50
|
+
def perform(&block)
|
51
|
+
reset
|
52
|
+
|
53
|
+
if valid?
|
54
|
+
if use_event_attributes? && !block_given?
|
55
|
+
each do |transition|
|
56
|
+
transition.transient = true
|
57
|
+
transition.machine.write(object, :event_transition, transition)
|
58
|
+
end
|
59
|
+
|
60
|
+
run_actions
|
61
|
+
else
|
62
|
+
within_transaction do
|
63
|
+
catch(:halt) { run_callbacks(&block) }
|
64
|
+
rollback unless success?
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
if actions.length == 1 && results.include?(actions.first)
|
70
|
+
results[actions.first]
|
71
|
+
else
|
72
|
+
success?
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
protected
|
77
|
+
attr_reader :results #:nodoc:
|
78
|
+
|
79
|
+
private
|
80
|
+
# Is this a valid set of transitions? If the collection was creating with
|
81
|
+
# any +false+ values for transitions, then the the collection will be
|
82
|
+
# marked as invalid.
|
83
|
+
def valid?
|
84
|
+
@valid
|
85
|
+
end
|
86
|
+
|
87
|
+
# Did each transition perform successfully? This will only be true if the
|
88
|
+
# following requirements are met:
|
89
|
+
# * No +before+ callbacks halt
|
90
|
+
# * All actions run successfully (always true if skipping actions)
|
91
|
+
def success?
|
92
|
+
@success
|
93
|
+
end
|
94
|
+
|
95
|
+
# Gets the object being transitioned
|
96
|
+
def object
|
97
|
+
first.object
|
98
|
+
end
|
99
|
+
|
100
|
+
# Gets the list of actions to run. If configured to skip actions, then
|
101
|
+
# this will return an empty collection.
|
102
|
+
def actions
|
103
|
+
empty? ? [nil] : map {|transition| transition.action}.uniq
|
104
|
+
end
|
105
|
+
|
106
|
+
# Determines whether an event attribute be used to trigger the transitions
|
107
|
+
# in this collection or whether the transitions be run directly *outside*
|
108
|
+
# of the action.
|
109
|
+
def use_event_attributes?
|
110
|
+
!skip_actions && !skip_after && actions.all? && actions.length == 1 && first.machine.action_hook?
|
111
|
+
end
|
112
|
+
|
113
|
+
# Resets any information tracked from previous attempts to perform the
|
114
|
+
# collection
|
115
|
+
def reset
|
116
|
+
@results = {}
|
117
|
+
@success = false
|
118
|
+
end
|
119
|
+
|
120
|
+
# Runs each transition's callbacks recursively. Once all before callbacks
|
121
|
+
# have been executed, the transitions will then be persisted and the
|
122
|
+
# configured actions will be run.
|
123
|
+
#
|
124
|
+
# If any transition fails to run its callbacks, :halt will be thrown.
|
125
|
+
def run_callbacks(index = 0, &block)
|
126
|
+
if transition = self[index]
|
127
|
+
throw :halt unless transition.run_callbacks(:after => !skip_after) do
|
128
|
+
run_callbacks(index + 1, &block)
|
129
|
+
{:result => results[transition.action], :success => success?}
|
130
|
+
end
|
131
|
+
else
|
132
|
+
persist
|
133
|
+
run_actions(&block)
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Transitions the current value of the object's states to those specified by
|
138
|
+
# each transition
|
139
|
+
def persist
|
140
|
+
each {|transition| transition.persist}
|
141
|
+
end
|
142
|
+
|
143
|
+
# Runs the actions for each transition. If a block is given method, then it
|
144
|
+
# will be called instead of invoking each transition's action.
|
145
|
+
#
|
146
|
+
# The results of the actions will be used to determine #success?.
|
147
|
+
def run_actions
|
148
|
+
catch_exceptions do
|
149
|
+
@success = if block_given?
|
150
|
+
result = yield
|
151
|
+
actions.each {|action| results[action] = result}
|
152
|
+
!!result
|
153
|
+
else
|
154
|
+
actions.compact.each {|action| !skip_actions && results[action] = object.send(action)}
|
155
|
+
results.values.all?
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
# Rolls back changes made to the object's states via each transition
|
161
|
+
def rollback
|
162
|
+
each {|transition| transition.rollback}
|
163
|
+
end
|
164
|
+
|
165
|
+
# Wraps the given block with a rescue handler so that any exceptions that
|
166
|
+
# occur will automatically result in the transition rolling back any changes
|
167
|
+
# that were made to the object involved.
|
168
|
+
def catch_exceptions
|
169
|
+
begin
|
170
|
+
yield
|
171
|
+
rescue Exception
|
172
|
+
rollback
|
173
|
+
raise
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Runs a block within a transaction for the object being transitioned. If
|
178
|
+
# transactions are disabled, then this is a no-op.
|
179
|
+
def within_transaction
|
180
|
+
if use_transaction && !empty?
|
181
|
+
first.within_transaction do
|
182
|
+
yield
|
183
|
+
success?
|
184
|
+
end
|
185
|
+
else
|
186
|
+
yield
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# Represents a collection of transitions that were generated from attribute-
|
192
|
+
# based events
|
193
|
+
class AttributeTransitionCollection < TransitionCollection
|
194
|
+
def initialize(transitions = [], options = {}) #:nodoc:
|
195
|
+
super(transitions, {:transaction => false, :actions => false}.merge(options))
|
196
|
+
end
|
197
|
+
|
198
|
+
private
|
199
|
+
# Hooks into running transition callbacks so that event / event transition
|
200
|
+
# attributes can be properly updated
|
201
|
+
def run_callbacks(index = 0)
|
202
|
+
if index == 0
|
203
|
+
# Clears any traces of the event attribute to prevent it from being
|
204
|
+
# evaluated multiple times if actions are nested
|
205
|
+
each do |transition|
|
206
|
+
transition.machine.write(object, :event, nil)
|
207
|
+
transition.machine.write(object, :event_transition, nil)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Rollback only if exceptions occur during before callbacks
|
211
|
+
begin
|
212
|
+
super
|
213
|
+
rescue Exception
|
214
|
+
rollback unless @before_run
|
215
|
+
raise
|
216
|
+
end
|
217
|
+
|
218
|
+
# Persists transitions on the object if partial transition was successful.
|
219
|
+
# This allows us to reference them later to complete the transition with
|
220
|
+
# after callbacks.
|
221
|
+
each {|transition| transition.machine.write(object, :event_transition, transition)} if skip_after && success?
|
222
|
+
else
|
223
|
+
super
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
# Tracks that before callbacks have now completed
|
228
|
+
def persist
|
229
|
+
@before_run = true
|
230
|
+
super
|
231
|
+
end
|
232
|
+
|
233
|
+
# Resets callback tracking
|
234
|
+
def reset
|
235
|
+
super
|
236
|
+
@before_run = false
|
237
|
+
end
|
238
|
+
|
239
|
+
# Resets the event attribute so it can be re-evaluated if attempted again
|
240
|
+
def rollback
|
241
|
+
super
|
242
|
+
each {|transition| transition.machine.write(object, :event, transition.event) unless transition.transient?}
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module StateMachine
|
2
|
+
module YARD
|
3
|
+
# YARD custom handlers for integrating the state_machine DSL with the
|
4
|
+
# YARD documentation system
|
5
|
+
module Handlers
|
6
|
+
end
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
Dir["#{File.dirname(__FILE__)}/handlers/*.rb"].sort.each do |path|
|
11
|
+
require "state_machine/yard/handlers/#{File.basename(path)}"
|
12
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module StateMachine
|
2
|
+
module YARD
|
3
|
+
module Handlers
|
4
|
+
# Handles and processes nodes
|
5
|
+
class Base < ::YARD::Handlers::Ruby::Base
|
6
|
+
private
|
7
|
+
# Extracts the value from the node as either a string or symbol
|
8
|
+
def extract_node_name(ast)
|
9
|
+
case ast.type
|
10
|
+
when :symbol_literal
|
11
|
+
ast.jump(:ident).source.to_sym
|
12
|
+
when :string_literal
|
13
|
+
ast.jump(:tstring_content).source
|
14
|
+
else
|
15
|
+
nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Extracts the values from the node as either strings or symbols.
|
20
|
+
# If the node isn't an array, it'll be converted to an array.
|
21
|
+
def extract_node_names(ast, convert_to_array = true)
|
22
|
+
if [nil, :array].include?(ast.type)
|
23
|
+
ast.children.map {|child| extract_node_name(child)}
|
24
|
+
else
|
25
|
+
node_name = extract_node_name(ast)
|
26
|
+
convert_to_array ? [node_name] : node_name
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module StateMachine
|
2
|
+
module YARD
|
3
|
+
module Handlers
|
4
|
+
# Handles and processes #event
|
5
|
+
class Event < Base
|
6
|
+
handles method_call(:event)
|
7
|
+
|
8
|
+
def process
|
9
|
+
if owner.is_a?(StateMachine::Machine)
|
10
|
+
handler = self
|
11
|
+
statement = self.statement
|
12
|
+
names = extract_node_names(statement.parameters(false))
|
13
|
+
|
14
|
+
names.each do |name|
|
15
|
+
owner.event(name) do
|
16
|
+
# Parse the block
|
17
|
+
handler.parse_block(statement.last.last, :owner => self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,344 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
|
3
|
+
module StateMachine
|
4
|
+
module YARD
|
5
|
+
module Handlers
|
6
|
+
# Handles and processes #state_machine
|
7
|
+
class Machine < Base
|
8
|
+
handles method_call(:state_machine)
|
9
|
+
namespace_only
|
10
|
+
|
11
|
+
# The generated state machine
|
12
|
+
attr_reader :machine
|
13
|
+
|
14
|
+
def process
|
15
|
+
# Cross-file storage for state machines
|
16
|
+
globals.state_machines ||= Hash.new {|h, k| h[k] = {}}
|
17
|
+
namespace['state_machines'] ||= {}
|
18
|
+
|
19
|
+
# Create new machine
|
20
|
+
klass = inherited_machine ? Class.new(inherited_machine.owner_class) : Class.new { extend StateMachine::MacroMethods }
|
21
|
+
@machine = klass.state_machine(name, options) {}
|
22
|
+
|
23
|
+
# Track the state machine
|
24
|
+
globals.state_machines[namespace.name][name] = machine
|
25
|
+
namespace['state_machines'][name] = {:name => name, :description => statement.docstring}
|
26
|
+
|
27
|
+
# Parse the block
|
28
|
+
parse_block(statement.last.last, :owner => machine)
|
29
|
+
|
30
|
+
# Draw the machine for reference in the template
|
31
|
+
file = Tempfile.new(['state_machine', '.png'])
|
32
|
+
begin
|
33
|
+
if machine.draw(:name => File.basename(file.path, '.png'), :path => File.dirname(file.path), :orientation => 'landscape')
|
34
|
+
namespace['state_machines'][name][:image] = file.read
|
35
|
+
end
|
36
|
+
ensure
|
37
|
+
# Clean up tempfile
|
38
|
+
file.close
|
39
|
+
file.unlink
|
40
|
+
end
|
41
|
+
|
42
|
+
# Define auto-generated methods
|
43
|
+
define_macro_methods
|
44
|
+
define_state_methods
|
45
|
+
define_event_methods
|
46
|
+
end
|
47
|
+
|
48
|
+
protected
|
49
|
+
# Extracts the machine name's
|
50
|
+
def name
|
51
|
+
@name ||= begin
|
52
|
+
ast = statement.parameters.first
|
53
|
+
if ast && [:symbol_literal, :string_literal].include?(ast.type)
|
54
|
+
extract_node_name(ast)
|
55
|
+
else
|
56
|
+
:state
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Extracts the machine options. Note that this will only extract a
|
62
|
+
# subset of the options supported.
|
63
|
+
def options
|
64
|
+
@options ||= begin
|
65
|
+
options = {}
|
66
|
+
ast = statement.parameters(false).last
|
67
|
+
|
68
|
+
if !inherited_machine && ast && ![:symbol_literal, :string_literal].include?(ast.type)
|
69
|
+
ast.children.each do |assoc|
|
70
|
+
# Only extract important options
|
71
|
+
key = extract_node_name(assoc[0])
|
72
|
+
next unless [:initial, :attribute, :namespace, :action].include?(key)
|
73
|
+
|
74
|
+
value = extract_node_name(assoc[1])
|
75
|
+
options[key] = value
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
options
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Gets the machine that was inherited from a superclass. This also
|
84
|
+
# ensures each ancestor has been loaded prior to looking up their definitions.
|
85
|
+
def inherited_machine
|
86
|
+
@inherited_machine ||= begin
|
87
|
+
namespace.inheritance_tree.each do |ancestor|
|
88
|
+
begin
|
89
|
+
ensure_loaded!(ancestor)
|
90
|
+
rescue ::YARD::Handlers::NamespaceMissingError
|
91
|
+
# Ignore: just means that we can't access an ancestor
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Find the first ancestor that has the machine
|
96
|
+
loaded_superclasses.detect do |superclass|
|
97
|
+
if superclass != namespace
|
98
|
+
machine = globals.state_machines[superclass.name][name]
|
99
|
+
break machine if machine
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Gets members of this class's superclasses have already been loaded
|
106
|
+
# by YARD
|
107
|
+
def loaded_superclasses
|
108
|
+
namespace.inheritance_tree.select {|ancestor| ancestor.is_a?(::YARD::CodeObjects::ClassObject)}
|
109
|
+
end
|
110
|
+
|
111
|
+
# Gets a list of all attributes for the current class, including those
|
112
|
+
# that are inherited
|
113
|
+
def instance_attributes
|
114
|
+
attributes = {}
|
115
|
+
loaded_superclasses.each {|superclass| attributes.merge!(superclass.instance_attributes)}
|
116
|
+
attributes
|
117
|
+
end
|
118
|
+
|
119
|
+
# Gets the type of ORM integration being used based on the list of
|
120
|
+
# ancestors (including mixins)
|
121
|
+
def integration
|
122
|
+
@integration ||= Integrations.match_ancestors(namespace.inheritance_tree(true).map {|ancestor| ancestor.path})
|
123
|
+
end
|
124
|
+
|
125
|
+
# Gets the class type being used to define states. Default is "Symbol".
|
126
|
+
def state_type
|
127
|
+
@state_type ||= machine.states.any? ? machine.states.map {|state| state.name}.compact.first.class.to_s : 'Symbol'
|
128
|
+
end
|
129
|
+
|
130
|
+
# Gets the class type being used to define events. Default is "Symbol".
|
131
|
+
def event_type
|
132
|
+
@event_type ||= machine.events.any? ? machine.events.first.name.class.to_s : 'Symbol'
|
133
|
+
end
|
134
|
+
|
135
|
+
# Defines auto-generated macro methods for the given machine
|
136
|
+
def define_macro_methods
|
137
|
+
return if inherited_machine
|
138
|
+
|
139
|
+
# Human state name lookup
|
140
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "human_#{machine.attribute(:name)}", :class))
|
141
|
+
m.docstring = [
|
142
|
+
"Gets the humanized name for the given state.",
|
143
|
+
"@param [#{state_type}] state The state to look up",
|
144
|
+
"@return [String] The human state name"
|
145
|
+
]
|
146
|
+
m.parameters = ["state"]
|
147
|
+
|
148
|
+
# Human event name lookup
|
149
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "human_#{machine.attribute(:event_name)}", :class))
|
150
|
+
m.docstring = [
|
151
|
+
"Gets the humanized name for the given event.",
|
152
|
+
"@param [#{event_type}] event The event to look up",
|
153
|
+
"@return [String] The human event name"
|
154
|
+
]
|
155
|
+
m.parameters = ["event"]
|
156
|
+
|
157
|
+
# Only register attributes when the accessor isn't explicitly defined
|
158
|
+
# by the class / superclass *and* isn't defined by inference from the
|
159
|
+
# ORM being used
|
160
|
+
unless integration || instance_attributes.include?(machine.attribute.to_sym)
|
161
|
+
attribute = machine.attribute
|
162
|
+
namespace.attributes[:instance][attribute] = {}
|
163
|
+
|
164
|
+
# Machine attribute getter
|
165
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, attribute))
|
166
|
+
namespace.attributes[:instance][attribute][:read] = m
|
167
|
+
m.docstring = [
|
168
|
+
"Gets the current attribute value for the machine",
|
169
|
+
"@return The attribute value"
|
170
|
+
]
|
171
|
+
|
172
|
+
# Machine attribute setter
|
173
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{attribute}="))
|
174
|
+
namespace.attributes[:instance][attribute][:write] = m
|
175
|
+
m.docstring = [
|
176
|
+
"Sets the current value for the machine",
|
177
|
+
"@param new_#{attribute} The new value to set"
|
178
|
+
]
|
179
|
+
m.parameters = ["new_#{attribute}"]
|
180
|
+
end
|
181
|
+
|
182
|
+
if integration && integration.defaults[:action] && !options.include?(:action) || options[:action]
|
183
|
+
attribute = "#{machine.name}_event"
|
184
|
+
namespace.attributes[:instance][attribute] = {}
|
185
|
+
|
186
|
+
# Machine event attribute getter
|
187
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, attribute))
|
188
|
+
namespace.attributes[:instance][attribute][:read] = m
|
189
|
+
m.docstring = [
|
190
|
+
"Gets the current event attribute value for the machine",
|
191
|
+
"@return The event attribute value"
|
192
|
+
]
|
193
|
+
|
194
|
+
# Machine event attribute setter
|
195
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{attribute}="))
|
196
|
+
namespace.attributes[:instance][attribute][:write] = m
|
197
|
+
m.docstring = [
|
198
|
+
"Sets the current value for the machine",
|
199
|
+
"@param new_#{attribute} The new value to set"
|
200
|
+
]
|
201
|
+
m.parameters = ["new_#{attribute}"]
|
202
|
+
end
|
203
|
+
|
204
|
+
# Presence query
|
205
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{machine.name}?"))
|
206
|
+
m.docstring = [
|
207
|
+
"Checks the given state name against the current state.",
|
208
|
+
"@param [#{state_type}] state_name The name of the state to check",
|
209
|
+
"@return [Boolean] True if they are the same state, otherwise false",
|
210
|
+
"@raise [IndexError] If the state name is invalid"
|
211
|
+
]
|
212
|
+
m.parameters = ["state_name"]
|
213
|
+
|
214
|
+
# Internal state name
|
215
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:name)))
|
216
|
+
m.docstring = [
|
217
|
+
"Gets the internal name of the state for the current value.",
|
218
|
+
"@return [#{state_type}] The internal name of the state"
|
219
|
+
]
|
220
|
+
|
221
|
+
# Human state name
|
222
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "human_#{machine.attribute(:name)}"))
|
223
|
+
m.docstring = [
|
224
|
+
"Gets the human-readable name of the state for the current value.",
|
225
|
+
"@return [String] The human-readable state name"
|
226
|
+
]
|
227
|
+
|
228
|
+
# Available events
|
229
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:events)))
|
230
|
+
m.docstring = [
|
231
|
+
"Gets the list of events that can be fired on the current #{machine.name} (uses the *unqualified* event names)",
|
232
|
+
"@param [Hash] requirements The transition requirements to test against",
|
233
|
+
"@option requirements [#{state_type}] :from (the current state) One or more initial states",
|
234
|
+
"@option requirements [#{state_type}] :to One or more target states",
|
235
|
+
"@option requirements [#{event_type}] :on One or more events that fire the transition",
|
236
|
+
"@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
|
237
|
+
"@return [Array<#{event_type}>] The list of event names"
|
238
|
+
]
|
239
|
+
m.parameters = [["requirements", "{}"]]
|
240
|
+
|
241
|
+
# Available transitions
|
242
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:transitions)))
|
243
|
+
m.docstring = [
|
244
|
+
"Gets the list of transitions that can be made for the current #{machine.name}",
|
245
|
+
"@param [Hash] requirements The transition requirements to test against",
|
246
|
+
"@option requirements [#{state_type}] :from (the current state) One or more initial states",
|
247
|
+
"@option requirements [#{state_type}] :to One or more target states",
|
248
|
+
"@option requirements [#{event_type}] :on One or more events that fire the transition",
|
249
|
+
"@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
|
250
|
+
"@return [Array<StateMachine::Transition>] The available transitions"
|
251
|
+
]
|
252
|
+
m.parameters = [["requirements", "{}"]]
|
253
|
+
|
254
|
+
# Available transition paths
|
255
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, machine.attribute(:paths)))
|
256
|
+
m.docstring = [
|
257
|
+
"Gets the list of sequences of transitions that can be run for the current #{machine.name}",
|
258
|
+
"@param [Hash] requirements The transition requirements to test against",
|
259
|
+
"@option requirements [#{state_type}] :from (the current state) The initial state",
|
260
|
+
"@option requirements [#{state_type}] :to The target state",
|
261
|
+
"@option requirements [Boolean] :deep Whether to enable deep searches for the target state",
|
262
|
+
"@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
|
263
|
+
"@return [StateMachine::PathCollection] The collection of paths"
|
264
|
+
]
|
265
|
+
m.parameters = [["requirements", "{}"]]
|
266
|
+
|
267
|
+
# Generic event fire
|
268
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "fire_#{machine.attribute(:event)}"))
|
269
|
+
m.docstring = [
|
270
|
+
"Fires an arbitrary #{machine.name} event with the given argument list",
|
271
|
+
"@param [#{event_type}] event The name of the event to fire",
|
272
|
+
"@param args Optional arguments to include in the transition",
|
273
|
+
"@return [Boolean] +true+ if the event succeeds, otherwise +false+"
|
274
|
+
]
|
275
|
+
m.parameters = ["event", "*args"]
|
276
|
+
end
|
277
|
+
|
278
|
+
# Defines auto-generated event methods for the given machine
|
279
|
+
def define_event_methods
|
280
|
+
machine.events.each do |event|
|
281
|
+
next if inherited_machine && inherited_machine.events[event.name]
|
282
|
+
|
283
|
+
# Event query
|
284
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "can_#{event.qualified_name}?"))
|
285
|
+
m.docstring = [
|
286
|
+
"Checks whether #{event.name.inspect} can be fired.",
|
287
|
+
"@param [Hash] requirements The transition requirements to test against",
|
288
|
+
"@option requirements [#{state_type}] :from (the current state) One or more initial states",
|
289
|
+
"@option requirements [#{state_type}] :to One or more target states",
|
290
|
+
"@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
|
291
|
+
"@return [Boolean] +true+ if #{event.name.inspect} can be fired, otherwise +false+"
|
292
|
+
]
|
293
|
+
m.parameters = [["requirements", "{}"]]
|
294
|
+
|
295
|
+
# Event transition
|
296
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{event.qualified_name}_transition"))
|
297
|
+
m.docstring = [
|
298
|
+
"Gets the next transition that would be performed if #{event.name.inspect} were to be fired.",
|
299
|
+
"@param [Hash] requirements The transition requirements to test against",
|
300
|
+
"@option requirements [#{state_type}] :from (the current state) One or more initial states",
|
301
|
+
"@option requirements [#{state_type}] :to One or more target states",
|
302
|
+
"@option requirements [Boolean] :guard Whether to guard transitions with conditionals",
|
303
|
+
"@return [StateMachine::Transition] The transition that would be performed or +nil+"
|
304
|
+
]
|
305
|
+
m.parameters = [["requirements", "{}"]]
|
306
|
+
|
307
|
+
# Fire event
|
308
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, event.qualified_name))
|
309
|
+
m.docstring = [
|
310
|
+
"Fires the #{event.name.inspect} event.",
|
311
|
+
"@param [Array] args Optional arguments to include in transition callbacks",
|
312
|
+
"@return [Boolean] +true+ if the transition succeeds, otherwise +false+"
|
313
|
+
]
|
314
|
+
m.parameters = ["*args"]
|
315
|
+
|
316
|
+
# Fire event (raises exception)
|
317
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{event.qualified_name}!"))
|
318
|
+
m.docstring = [
|
319
|
+
"Fires the #{event.name.inspect} event, raising an exception if it fails.",
|
320
|
+
"@param [Array] args Optional arguments to include in transition callbacks",
|
321
|
+
"@return [Boolean] +true+ if the transition succeeds",
|
322
|
+
"@raise [StateMachine::InvalidTransition] If the transition fails"
|
323
|
+
]
|
324
|
+
m.parameters = ["*args"]
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
# Defines auto-generated state methods for the given machine
|
329
|
+
def define_state_methods
|
330
|
+
machine.states.each do |state|
|
331
|
+
next if inherited_machine && inherited_machine.states[state.name] || !state.name
|
332
|
+
|
333
|
+
# State query
|
334
|
+
register(m = ::YARD::CodeObjects::MethodObject.new(namespace, "#{state.qualified_name}?"))
|
335
|
+
m.docstring = [
|
336
|
+
"Checks whether #{state.name.inspect} is the current state.",
|
337
|
+
"@return [Boolean] +true+ if this is the current state, otherwise +false+"
|
338
|
+
]
|
339
|
+
end
|
340
|
+
end
|
341
|
+
end
|
342
|
+
end
|
343
|
+
end
|
344
|
+
end
|