state_machine_updated_for_ruby_3_2 2.0.0
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 +10 -0
- data/.travis.yml +72 -0
- data/.yardopts +5 -0
- data/Appraisals +491 -0
- data/CHANGELOG.md +506 -0
- data/Dockerfile +13 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/README.md +1244 -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/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/class/state_machine.rb +5 -0
- data/lib/state_machine/core_ext.rb +2 -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/merb.rb +1 -0
- data/lib/state_machine/initializers/rails.rb +25 -0
- data/lib/state_machine/initializers.rb +4 -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_model.rb +585 -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/active_record.rb +548 -0
- data/lib/state_machine/integrations/base.rb +100 -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/data_mapper.rb +511 -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/mongo_mapper.rb +389 -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/mongoid.rb +461 -0
- data/lib/state_machine/integrations/sequel/versions.rb +95 -0
- data/lib/state_machine/integrations/sequel.rb +486 -0
- data/lib/state_machine/integrations.rb +121 -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/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/handlers.rb +12 -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/state_machine/yard/templates.rb +3 -0
- data/lib/state_machine/yard.rb +8 -0
- data/lib/state_machine.rb +8 -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 +23 -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 +272 -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 +3407 -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 +450 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
require 'state_machine/node_collection'
|
|
2
|
+
|
|
3
|
+
module StateMachine
|
|
4
|
+
# Represents a collection of events in a state machine
|
|
5
|
+
class EventCollection < NodeCollection
|
|
6
|
+
def initialize(machine) #:nodoc:
|
|
7
|
+
super(machine, :index => [:name, :qualified_name])
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Gets the list of events that can be fired on the given object.
|
|
11
|
+
#
|
|
12
|
+
# Valid requirement options:
|
|
13
|
+
# * <tt>:from</tt> - One or more states being transitioned from. If none
|
|
14
|
+
# are specified, then this will be the object's current state.
|
|
15
|
+
# * <tt>:to</tt> - One or more states being transitioned to. If none are
|
|
16
|
+
# specified, then this will match any to state.
|
|
17
|
+
# * <tt>:on</tt> - One or more events that fire the transition. If none
|
|
18
|
+
# are specified, then this will match any event.
|
|
19
|
+
# * <tt>:guard</tt> - Whether to guard transitions with the if/unless
|
|
20
|
+
# conditionals defined for each one. Default is true.
|
|
21
|
+
#
|
|
22
|
+
# == Examples
|
|
23
|
+
#
|
|
24
|
+
# class Vehicle
|
|
25
|
+
# state_machine :initial => :parked do
|
|
26
|
+
# event :park do
|
|
27
|
+
# transition :idling => :parked
|
|
28
|
+
# end
|
|
29
|
+
#
|
|
30
|
+
# event :ignite do
|
|
31
|
+
# transition :parked => :idling
|
|
32
|
+
# end
|
|
33
|
+
# end
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# events = Vehicle.state_machine(:state).events
|
|
37
|
+
#
|
|
38
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
|
|
39
|
+
# events.valid_for(vehicle) # => [#<StateMachine::Event name=:ignite transitions=[:parked => :idling]>]
|
|
40
|
+
#
|
|
41
|
+
# vehicle.state = 'idling'
|
|
42
|
+
# events.valid_for(vehicle) # => [#<StateMachine::Event name=:park transitions=[:idling => :parked]>]
|
|
43
|
+
def valid_for(object, requirements = {})
|
|
44
|
+
match(requirements).select {|event| event.can_fire?(object, requirements)}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Gets the list of transitions that can be run on the given object.
|
|
48
|
+
#
|
|
49
|
+
# Valid requirement options:
|
|
50
|
+
# * <tt>:from</tt> - One or more states being transitioned from. If none
|
|
51
|
+
# are specified, then this will be the object's current state.
|
|
52
|
+
# * <tt>:to</tt> - One or more states being transitioned to. If none are
|
|
53
|
+
# specified, then this will match any to state.
|
|
54
|
+
# * <tt>:on</tt> - One or more events that fire the transition. If none
|
|
55
|
+
# are specified, then this will match any event.
|
|
56
|
+
# * <tt>:guard</tt> - Whether to guard transitions with the if/unless
|
|
57
|
+
# conditionals defined for each one. Default is true.
|
|
58
|
+
#
|
|
59
|
+
# == Examples
|
|
60
|
+
#
|
|
61
|
+
# class Vehicle
|
|
62
|
+
# state_machine :initial => :parked do
|
|
63
|
+
# event :park do
|
|
64
|
+
# transition :idling => :parked
|
|
65
|
+
# end
|
|
66
|
+
#
|
|
67
|
+
# event :ignite do
|
|
68
|
+
# transition :parked => :idling
|
|
69
|
+
# end
|
|
70
|
+
# end
|
|
71
|
+
# end
|
|
72
|
+
#
|
|
73
|
+
# events = Vehicle.state_machine.events
|
|
74
|
+
#
|
|
75
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
|
|
76
|
+
# events.transitions_for(vehicle) # => [#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
|
|
77
|
+
#
|
|
78
|
+
# vehicle.state = 'idling'
|
|
79
|
+
# events.transitions_for(vehicle) # => [#<StateMachine::Transition attribute=:state event=:park from="idling" from_name=:idling to="parked" to_name=:parked>]
|
|
80
|
+
#
|
|
81
|
+
# # Search for explicit transitions regardless of the current state
|
|
82
|
+
# events.transitions_for(vehicle, :from => :parked) # => [#<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>]
|
|
83
|
+
def transitions_for(object, requirements = {})
|
|
84
|
+
match(requirements).map {|event| event.transition_for(object, requirements)}.compact
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Gets the transition that should be performed for the event stored in the
|
|
88
|
+
# given object's event attribute. This also takes an additional parameter
|
|
89
|
+
# for automatically invalidating the object if the event or transition are
|
|
90
|
+
# invalid. By default, this is turned off.
|
|
91
|
+
#
|
|
92
|
+
# *Note* that if a transition has already been generated for the event, then
|
|
93
|
+
# that transition will be used.
|
|
94
|
+
#
|
|
95
|
+
# == Examples
|
|
96
|
+
#
|
|
97
|
+
# class Vehicle < ActiveRecord::Base
|
|
98
|
+
# state_machine :initial => :parked do
|
|
99
|
+
# event :ignite do
|
|
100
|
+
# transition :parked => :idling
|
|
101
|
+
# end
|
|
102
|
+
# end
|
|
103
|
+
# end
|
|
104
|
+
#
|
|
105
|
+
# vehicle = Vehicle.new # => #<Vehicle id: nil, state: "parked">
|
|
106
|
+
# events = Vehicle.state_machine.events
|
|
107
|
+
#
|
|
108
|
+
# vehicle.state_event = nil
|
|
109
|
+
# events.attribute_transition_for(vehicle) # => nil # Event isn't defined
|
|
110
|
+
#
|
|
111
|
+
# vehicle.state_event = 'invalid'
|
|
112
|
+
# events.attribute_transition_for(vehicle) # => false # Event is invalid
|
|
113
|
+
#
|
|
114
|
+
# vehicle.state_event = 'ignite'
|
|
115
|
+
# events.attribute_transition_for(vehicle) # => #<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
|
|
116
|
+
def attribute_transition_for(object, invalidate = false)
|
|
117
|
+
return unless machine.action
|
|
118
|
+
|
|
119
|
+
result = machine.read(object, :event_transition) || if event_name = machine.read(object, :event)
|
|
120
|
+
if event = self[event_name.to_sym, :name]
|
|
121
|
+
event.transition_for(object) || begin
|
|
122
|
+
# No valid transition: invalidate
|
|
123
|
+
machine.invalidate(object, :event, :invalid_event, [[:state, machine.states.match!(object).human_name(object.class)]]) if invalidate
|
|
124
|
+
false
|
|
125
|
+
end
|
|
126
|
+
else
|
|
127
|
+
# Event is unknown: invalidate
|
|
128
|
+
machine.invalidate(object, :event, :invalid) if invalidate
|
|
129
|
+
false
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
result
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
private
|
|
137
|
+
def match(requirements) #:nodoc:
|
|
138
|
+
requirements && requirements[:on] ? [fetch(requirements.delete(:on))] : self
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
require 'state_machine/machine_collection'
|
|
2
|
+
|
|
3
|
+
module StateMachine
|
|
4
|
+
module ClassMethods
|
|
5
|
+
def self.extended(base) #:nodoc:
|
|
6
|
+
base.class_eval do
|
|
7
|
+
@state_machines = MachineCollection.new
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Gets the current list of state machines defined for this class. This
|
|
12
|
+
# class-level attribute acts like an inheritable attribute. The attribute
|
|
13
|
+
# is available to each subclass, each having a copy of its superclass's
|
|
14
|
+
# attribute.
|
|
15
|
+
#
|
|
16
|
+
# The hash of state machines maps <tt>:attribute</tt> => +machine+, e.g.
|
|
17
|
+
#
|
|
18
|
+
# Vehicle.state_machines # => {:state => #<StateMachine::Machine:0xb6f6e4a4 ...>}
|
|
19
|
+
def state_machines
|
|
20
|
+
@state_machines ||= superclass.state_machines.dup
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
module InstanceMethods
|
|
25
|
+
# Runs one or more events in parallel. All events will run through the
|
|
26
|
+
# following steps:
|
|
27
|
+
# * Before callbacks
|
|
28
|
+
# * Persist state
|
|
29
|
+
# * Invoke action
|
|
30
|
+
# * After callbacks
|
|
31
|
+
#
|
|
32
|
+
# For example, if two events (for state machines A and B) are run in
|
|
33
|
+
# parallel, the order in which steps are run is:
|
|
34
|
+
# * A - Before transition callbacks
|
|
35
|
+
# * B - Before transition callbacks
|
|
36
|
+
# * A - Persist new state
|
|
37
|
+
# * B - Persist new state
|
|
38
|
+
# * A - Invoke action
|
|
39
|
+
# * B - Invoke action (only if different than A's action)
|
|
40
|
+
# * A - After transition callbacks
|
|
41
|
+
# * B - After transition callbacks
|
|
42
|
+
#
|
|
43
|
+
# *Note* that multiple events on the same state machine / attribute cannot
|
|
44
|
+
# be run in parallel. If this is attempted, an ArgumentError will be
|
|
45
|
+
# raised.
|
|
46
|
+
#
|
|
47
|
+
# == Halting callbacks
|
|
48
|
+
#
|
|
49
|
+
# When running multiple events in parallel, special consideration should
|
|
50
|
+
# be taken with regard to how halting within callbacks affects the flow.
|
|
51
|
+
#
|
|
52
|
+
# For *before* callbacks, any <tt>:halt</tt> error that's thrown will
|
|
53
|
+
# immediately cancel the perform for all transitions. As a result, it's
|
|
54
|
+
# possible for one event's transition to affect the continuation of
|
|
55
|
+
# another.
|
|
56
|
+
#
|
|
57
|
+
# On the other hand, any <tt>:halt</tt> error that's thrown within an
|
|
58
|
+
# *after* callback with only affect that event's transition. Other
|
|
59
|
+
# transitions will continue to run their own callbacks.
|
|
60
|
+
#
|
|
61
|
+
# == Example
|
|
62
|
+
#
|
|
63
|
+
# class Vehicle
|
|
64
|
+
# state_machine :initial => :parked do
|
|
65
|
+
# event :ignite do
|
|
66
|
+
# transition :parked => :idling
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# event :park do
|
|
70
|
+
# transition :idling => :parked
|
|
71
|
+
# end
|
|
72
|
+
# end
|
|
73
|
+
#
|
|
74
|
+
# state_machine :alarm_state, :namespace => 'alarm', :initial => :on do
|
|
75
|
+
# event :enable do
|
|
76
|
+
# transition all => :active
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# event :disable do
|
|
80
|
+
# transition all => :off
|
|
81
|
+
# end
|
|
82
|
+
# end
|
|
83
|
+
# end
|
|
84
|
+
#
|
|
85
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7c02850 @state="parked", @alarm_state="active">
|
|
86
|
+
# vehicle.state # => "parked"
|
|
87
|
+
# vehicle.alarm_state # => "active"
|
|
88
|
+
#
|
|
89
|
+
# vehicle.fire_events(:ignite, :disable_alarm) # => true
|
|
90
|
+
# vehicle.state # => "idling"
|
|
91
|
+
# vehicle.alarm_state # => "off"
|
|
92
|
+
#
|
|
93
|
+
# # If any event fails, the entire event chain fails
|
|
94
|
+
# vehicle.fire_events(:ignite, :enable_alarm) # => false
|
|
95
|
+
# vehicle.state # => "idling"
|
|
96
|
+
# vehicle.alarm_state # => "off"
|
|
97
|
+
#
|
|
98
|
+
# # Exception raised on invalid event
|
|
99
|
+
# vehicle.fire_events(:park, :invalid) # => StateMachine::InvalidEvent: :invalid is an unknown event
|
|
100
|
+
# vehicle.state # => "idling"
|
|
101
|
+
# vehicle.alarm_state # => "off"
|
|
102
|
+
def fire_events(*events)
|
|
103
|
+
self.class.state_machines.fire_events(self, *events)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Run one or more events in parallel. If any event fails to run, then
|
|
107
|
+
# a StateMachine::InvalidTransition exception will be raised.
|
|
108
|
+
#
|
|
109
|
+
# See StateMachine::InstanceMethods#fire_events for more information.
|
|
110
|
+
#
|
|
111
|
+
# == Example
|
|
112
|
+
#
|
|
113
|
+
# class Vehicle
|
|
114
|
+
# state_machine :initial => :parked do
|
|
115
|
+
# event :ignite do
|
|
116
|
+
# transition :parked => :idling
|
|
117
|
+
# end
|
|
118
|
+
#
|
|
119
|
+
# event :park do
|
|
120
|
+
# transition :idling => :parked
|
|
121
|
+
# end
|
|
122
|
+
# end
|
|
123
|
+
#
|
|
124
|
+
# state_machine :alarm_state, :namespace => 'alarm', :initial => :active do
|
|
125
|
+
# event :enable do
|
|
126
|
+
# transition all => :active
|
|
127
|
+
# end
|
|
128
|
+
#
|
|
129
|
+
# event :disable do
|
|
130
|
+
# transition all => :off
|
|
131
|
+
# end
|
|
132
|
+
# end
|
|
133
|
+
# end
|
|
134
|
+
#
|
|
135
|
+
# vehicle = Vehicle.new # => #<Vehicle:0xb7c02850 @state="parked", @alarm_state="active">
|
|
136
|
+
# vehicle.fire_events(:ignite, :disable_alarm) # => true
|
|
137
|
+
#
|
|
138
|
+
# vehicle.fire_events!(:ignite, :disable_alarm) # => StateMachine::InvalidTranstion: Cannot run events in parallel: ignite, disable_alarm
|
|
139
|
+
def fire_events!(*events)
|
|
140
|
+
run_action = [true, false].include?(events.last) ? events.pop : true
|
|
141
|
+
fire_events(*(events + [run_action])) || raise(StateMachine::InvalidParallelTransition.new(self, events))
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
protected
|
|
145
|
+
def initialize_state_machines(options = {}, &block) #:nodoc:
|
|
146
|
+
self.class.state_machines.initialize_states(self, options, &block)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
begin
|
|
2
|
+
require 'rubygems'
|
|
3
|
+
gem 'ruby-graphviz', '>=0.9.17'
|
|
4
|
+
require 'graphviz'
|
|
5
|
+
rescue LoadError => ex
|
|
6
|
+
$stderr.puts "Cannot draw the machine (#{ex.message}). `gem install ruby-graphviz` >= v0.9.17 and try again."
|
|
7
|
+
raise
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
require 'state_machine/assertions'
|
|
11
|
+
|
|
12
|
+
module StateMachine
|
|
13
|
+
# Provides a set of higher-order features on top of the raw GraphViz graphs
|
|
14
|
+
class Graph < GraphViz
|
|
15
|
+
include Assertions
|
|
16
|
+
|
|
17
|
+
# The name of the font to draw state names in
|
|
18
|
+
attr_reader :font
|
|
19
|
+
|
|
20
|
+
# The graph's full filename
|
|
21
|
+
attr_reader :file_path
|
|
22
|
+
|
|
23
|
+
# The image format to generate the graph in
|
|
24
|
+
attr_reader :file_format
|
|
25
|
+
|
|
26
|
+
# Creates a new graph with the given name.
|
|
27
|
+
#
|
|
28
|
+
# Configuration options:
|
|
29
|
+
# * <tt>:path</tt> - The path to write the graph file to. Default is the
|
|
30
|
+
# current directory (".").
|
|
31
|
+
# * <tt>:format</tt> - The image format to generate the graph in.
|
|
32
|
+
# Default is "png'.
|
|
33
|
+
# * <tt>:font</tt> - The name of the font to draw state names in.
|
|
34
|
+
# Default is "Arial".
|
|
35
|
+
# * <tt>:orientation</tt> - The direction of the graph ("portrait" or
|
|
36
|
+
# "landscape"). Default is "portrait".
|
|
37
|
+
def initialize(name, options = {})
|
|
38
|
+
options = {:path => '.', :format => 'png', :font => 'Arial', :orientation => 'portrait'}.merge(options)
|
|
39
|
+
assert_valid_keys(options, :path, :format, :font, :orientation)
|
|
40
|
+
|
|
41
|
+
@font = options[:font]
|
|
42
|
+
@file_path = File.join(options[:path], "#{name}.#{options[:format]}")
|
|
43
|
+
@file_format = options[:format]
|
|
44
|
+
|
|
45
|
+
super('G', :rankdir => options[:orientation] == 'landscape' ? 'LR' : 'TB')
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Generates the actual image file based on the nodes / edges added to the
|
|
49
|
+
# graph. The path to the file is based on the configuration options for
|
|
50
|
+
# this graph.
|
|
51
|
+
def output
|
|
52
|
+
super(@file_format => @file_path)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Adds a new node to the graph. The font for the node will be automatically
|
|
56
|
+
# set based on the graph configuration. The generated node will be returned.
|
|
57
|
+
#
|
|
58
|
+
# For example,
|
|
59
|
+
#
|
|
60
|
+
# graph = StateMachine::Graph.new('test')
|
|
61
|
+
# graph.add_nodes('parked', :label => 'Parked', :width => '1', :height => '1', :shape => 'ellipse')
|
|
62
|
+
def add_nodes(*args)
|
|
63
|
+
node = v0_api? ? add_node(*args) : super
|
|
64
|
+
node.fontname = @font
|
|
65
|
+
node
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Adds a new edge to the graph. The font for the edge will be automatically
|
|
69
|
+
# set based on the graph configuration. The generated edge will be returned.
|
|
70
|
+
#
|
|
71
|
+
# For example,
|
|
72
|
+
#
|
|
73
|
+
# graph = StateMachine::Graph.new('test')
|
|
74
|
+
# graph.add_edges('parked', 'idling', :label => 'ignite')
|
|
75
|
+
def add_edges(*args)
|
|
76
|
+
edge = v0_api? ? add_edge(*args) : super
|
|
77
|
+
edge.fontname = @font
|
|
78
|
+
edge
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
private
|
|
82
|
+
# Determines whether the old v0 api is in use
|
|
83
|
+
def v0_api?
|
|
84
|
+
version[0] == '0' || version[0] == '1' && version[1] == '0' && version[2] <= '2'
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# The ruby-graphviz version data
|
|
88
|
+
def version
|
|
89
|
+
Constants::RGV_VERSION.split('.')
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module StateMachine
|
|
2
|
+
# Represents a type of module that defines instance / class methods for a
|
|
3
|
+
# state machine
|
|
4
|
+
class HelperModule < Module #:nodoc:
|
|
5
|
+
def initialize(machine, kind)
|
|
6
|
+
@machine = machine
|
|
7
|
+
@kind = kind
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Provides a human-readable description of the module
|
|
11
|
+
def to_s
|
|
12
|
+
owner_class = @machine.owner_class
|
|
13
|
+
owner_class_name = owner_class.name && !owner_class.name.empty? ? owner_class.name : owner_class.to_s
|
|
14
|
+
"#{owner_class_name} #{@machine.name.inspect} #{@kind} helpers"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Merb::Plugins.add_rakefiles("#{File.dirname(__FILE__)}/../../tasks/state_machine") if defined?(Merb::Plugins)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
if defined?(Rails)
|
|
2
|
+
# Track all of the applicable locales to load
|
|
3
|
+
locale_paths = []
|
|
4
|
+
StateMachine::Integrations.all.each do |integration|
|
|
5
|
+
locale_paths << integration.locale_path if integration.available? && integration.locale_path
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
if defined?(Rails::Engine)
|
|
9
|
+
# Rails 3.x
|
|
10
|
+
class StateMachine::RailsEngine < Rails::Engine
|
|
11
|
+
rake_tasks do
|
|
12
|
+
load 'tasks/state_machine.rb'
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
if Rails::VERSION::MAJOR == 3 && Rails::VERSION::MINOR == 0
|
|
17
|
+
StateMachine::RailsEngine.paths.config.locales = locale_paths
|
|
18
|
+
else
|
|
19
|
+
StateMachine::RailsEngine.paths['config/locales'] = locale_paths
|
|
20
|
+
end
|
|
21
|
+
elsif defined?(I18n)
|
|
22
|
+
# Rails 2.x
|
|
23
|
+
I18n.load_path.unshift(*locale_paths)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{:en => {
|
|
2
|
+
:activemodel => {
|
|
3
|
+
:errors => {
|
|
4
|
+
:messages => {
|
|
5
|
+
:invalid => StateMachine::Machine.default_messages[:invalid],
|
|
6
|
+
:invalid_event => StateMachine::Machine.default_messages[:invalid_event] % ['%{state}'],
|
|
7
|
+
:invalid_transition => StateMachine::Machine.default_messages[:invalid_transition] % ['%{event}']
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
}}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module StateMachine
|
|
2
|
+
module Integrations #:nodoc:
|
|
3
|
+
module ActiveModel
|
|
4
|
+
# Adds support for invoking callbacks on ActiveModel observers with more
|
|
5
|
+
# than one argument (e.g. the record *and* the state transition). By
|
|
6
|
+
# default, ActiveModel only supports passing the record into the
|
|
7
|
+
# callbacks.
|
|
8
|
+
#
|
|
9
|
+
# For example:
|
|
10
|
+
#
|
|
11
|
+
# class VehicleObserver < ActiveModel::Observer
|
|
12
|
+
# # The default behavior: only pass in the record
|
|
13
|
+
# def after_save(vehicle)
|
|
14
|
+
# end
|
|
15
|
+
#
|
|
16
|
+
# # Custom behavior: allow the transition to be passed in as well
|
|
17
|
+
# def after_transition(vehicle, transition)
|
|
18
|
+
# Audit.log(vehicle, transition)
|
|
19
|
+
# end
|
|
20
|
+
# end
|
|
21
|
+
module Observer
|
|
22
|
+
def update_with_transition(observer_update)
|
|
23
|
+
method = observer_update.method
|
|
24
|
+
send(method, *observer_update.args) if respond_to?(method)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
ActiveModel::Observer.class_eval do
|
|
32
|
+
include StateMachine::Integrations::ActiveModel::Observer
|
|
33
|
+
end if defined?(ActiveModel::Observer)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module StateMachine
|
|
2
|
+
module Integrations #:nodoc:
|
|
3
|
+
module ActiveModel
|
|
4
|
+
# Represents the encapsulation of all of the details to be included in an
|
|
5
|
+
# update to state machine observers. This allows multiple arguments to
|
|
6
|
+
# get passed to an observer method (instead of just a single +object+)
|
|
7
|
+
# while still respecting the way in which ActiveModel checks for the
|
|
8
|
+
# object's list of observers.
|
|
9
|
+
class ObserverUpdate
|
|
10
|
+
# The method to invoke on the observer
|
|
11
|
+
attr_reader :method
|
|
12
|
+
|
|
13
|
+
# The object being transitioned
|
|
14
|
+
attr_reader :object
|
|
15
|
+
|
|
16
|
+
# The transition being run
|
|
17
|
+
attr_reader :transition
|
|
18
|
+
|
|
19
|
+
def initialize(method, object, transition) #:nodoc:
|
|
20
|
+
@method, @object, @transition = method, object, transition
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# The arguments to pass into the method
|
|
24
|
+
def args
|
|
25
|
+
[object, transition]
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The class of the object being transitioned. Normally the object
|
|
29
|
+
# getting passed into observer methods is the actual instance of the
|
|
30
|
+
# ActiveModel class. ActiveModel uses that instance's class to check
|
|
31
|
+
# for enabled / disabled observers.
|
|
32
|
+
#
|
|
33
|
+
# Since state_machine is passing an ObserverUpdate instance into observer
|
|
34
|
+
# methods, +class+ needs to be overridden so that ActiveModel can still
|
|
35
|
+
# get access to the enabled / disabled observers.
|
|
36
|
+
def class
|
|
37
|
+
object.class
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module StateMachine
|
|
2
|
+
module Integrations #:nodoc:
|
|
3
|
+
module ActiveModel
|
|
4
|
+
version '2.x' do
|
|
5
|
+
def self.active?
|
|
6
|
+
!defined?(::ActiveModel::VERSION) || ::ActiveModel::VERSION::MAJOR == 2
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def define_validation_hook
|
|
10
|
+
define_helper :instance, <<-end_eval, __FILE__, __LINE__ + 1
|
|
11
|
+
def valid?(*)
|
|
12
|
+
self.class.state_machines.transitions(self, #{action.inspect}, :after => false).perform { super }
|
|
13
|
+
end
|
|
14
|
+
end_eval
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
version '3.0.x' do
|
|
19
|
+
def self.active?
|
|
20
|
+
defined?(::ActiveModel::VERSION) && ::ActiveModel::VERSION::MAJOR == 3 && ::ActiveModel::VERSION::MINOR == 0
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def define_validation_hook
|
|
24
|
+
# +around+ callbacks don't have direct access to results until AS 3.1
|
|
25
|
+
owner_class.set_callback(:validation, :after, 'value', :prepend => true)
|
|
26
|
+
super
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|