branston 0.6.1 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +1 -1
- data/lib/branston/Gemfile +25 -0
- data/lib/branston/Gemfile.lock +76 -0
- data/lib/branston/app/controllers/application_controller.rb +1 -1
- data/lib/branston/app/controllers/outcomes_controller.rb +2 -0
- data/lib/branston/app/controllers/stories_controller.rb +82 -86
- data/lib/branston/app/controllers/users_controller.rb +69 -11
- data/lib/branston/app/helpers/iterations_helper.rb +13 -13
- data/lib/branston/app/models/iteration.rb +3 -1
- data/lib/branston/app/models/release.rb +0 -1
- data/lib/branston/app/models/story.rb +30 -28
- data/lib/branston/app/models/user.rb +46 -1
- data/lib/branston/app/views/layouts/_header.html.erb +8 -3
- data/lib/branston/app/views/layouts/user_roles.html.erb +5 -5
- data/lib/branston/app/views/sessions/new.html.erb +8 -14
- data/lib/branston/app/views/users/_admin_controls.html.erb +14 -0
- data/lib/branston/app/views/users/_form.html.erb +27 -0
- data/lib/branston/app/views/users/edit.html.erb +9 -0
- data/lib/branston/app/views/users/index.html.erb +14 -0
- data/lib/branston/app/views/users/new.html.erb +3 -22
- data/lib/branston/config/boot.rb +20 -0
- data/lib/branston/config/environment.rb +2 -7
- data/lib/branston/config/environments/test.rb +0 -8
- data/lib/branston/config/preinitializer.rb +21 -0
- data/lib/branston/config/routes.rb +15 -10
- data/lib/branston/db/development.sqlite3 +0 -0
- data/lib/branston/db/development_structure.sql +21 -8
- data/lib/branston/db/migrate/20100723161424_add_state_to_user.rb +12 -0
- data/lib/branston/db/migrate/20100726150322_add_activation_fields_to_user.rb +12 -0
- data/lib/branston/db/migrate/20100729125551_set_default_user_state_to_pending.rb +10 -0
- data/lib/branston/db/migrate/20100812133837_add_is_admin_property_to_user.rb +10 -0
- data/lib/branston/db/migrate/20100812140532_set_default_user_state_to_active.rb +10 -0
- data/lib/branston/db/migrate/20100812143455_add_default_admin_user.rb +17 -0
- data/lib/branston/db/migrate/20110408162438_remove_is_admin_property_and_add_role_instead.rb +12 -0
- data/lib/branston/db/pristine.sqlite3 +0 -0
- data/lib/branston/db/schema.rb +6 -8
- data/lib/branston/db/test.sqlite3 +0 -0
- data/lib/branston/log/development.log +1181 -433
- data/lib/branston/log/test.log +145306 -52026
- data/lib/branston/test/blueprints.rb +22 -28
- data/lib/branston/test/functional/iterations_controller_test.rb +149 -113
- data/lib/branston/test/functional/outcomes_controller_test.rb +94 -60
- data/lib/branston/test/functional/preconditions_controller_test.rb +101 -67
- data/lib/branston/test/functional/releases_controller_test.rb +85 -49
- data/lib/branston/test/functional/scenarios_controller_test.rb +104 -70
- data/lib/branston/test/functional/stories_controller_test.rb +41 -12
- data/lib/branston/test/functional/users_controller_test.rb +364 -43
- data/lib/branston/test/unit/iteration_test.rb +37 -6
- data/lib/branston/test/unit/outcome_test.rb +2 -2
- data/lib/branston/test/unit/participation_test.rb +2 -2
- data/lib/branston/test/unit/precondition_test.rb +3 -3
- data/lib/branston/test/unit/release_test.rb +4 -0
- data/lib/branston/test/unit/scenario_test.rb +4 -4
- data/lib/branston/test/unit/story_test.rb +62 -40
- data/lib/branston/test/unit/user_test.rb +195 -5
- metadata +136 -156
- data/lib/branston/app/controllers/user_roles_controller.rb +0 -105
- data/lib/branston/app/helpers/user_roles_helper.rb +0 -2
- data/lib/branston/app/models/user_role.rb +0 -21
- data/lib/branston/app/views/layouts/outcomes.html.erb +0 -17
- data/lib/branston/app/views/layouts/preconditions.html.erb +0 -17
- data/lib/branston/app/views/layouts/releases.html.erb +0 -17
- data/lib/branston/app/views/user_roles/edit.html.erb +0 -16
- data/lib/branston/app/views/user_roles/index.html.erb +0 -20
- data/lib/branston/app/views/user_roles/new.html.erb +0 -15
- data/lib/branston/app/views/user_roles/show.html.erb +0 -8
- data/lib/branston/coverage/app-controllers-application_controller_rb.html +0 -231
- data/lib/branston/coverage/app-controllers-iterations_controller_rb.html +0 -801
- data/lib/branston/coverage/app-controllers-outcomes_controller_rb.html +0 -759
- data/lib/branston/coverage/app-controllers-preconditions_controller_rb.html +0 -783
- data/lib/branston/coverage/app-controllers-releases_controller_rb.html +0 -705
- data/lib/branston/coverage/app-controllers-scenarios_controller_rb.html +0 -777
- data/lib/branston/coverage/app-controllers-sessions_controller_rb.html +0 -411
- data/lib/branston/coverage/app-controllers-stories_controller_rb.html +0 -1071
- data/lib/branston/coverage/app-controllers-user_roles_controller_rb.html +0 -693
- data/lib/branston/coverage/app-controllers-users_controller_rb.html +0 -315
- data/lib/branston/coverage/app-helpers-application_helper_rb.html +0 -327
- data/lib/branston/coverage/app-helpers-iterations_helper_rb.html +0 -363
- data/lib/branston/coverage/app-helpers-outcomes_helper_rb.html +0 -75
- data/lib/branston/coverage/app-helpers-preconditions_helper_rb.html +0 -75
- data/lib/branston/coverage/app-helpers-releases_helper_rb.html +0 -75
- data/lib/branston/coverage/app-helpers-sessions_helper_rb.html +0 -75
- data/lib/branston/coverage/app-helpers-stories_helper_rb.html +0 -75
- data/lib/branston/coverage/app-helpers-user_roles_helper_rb.html +0 -75
- data/lib/branston/coverage/app-models-iteration_rb.html +0 -321
- data/lib/branston/coverage/app-models-outcome_rb.html +0 -243
- data/lib/branston/coverage/app-models-participation_rb.html +0 -189
- data/lib/branston/coverage/app-models-precondition_rb.html +0 -243
- data/lib/branston/coverage/app-models-release_rb.html +0 -195
- data/lib/branston/coverage/app-models-scenario_rb.html +0 -231
- data/lib/branston/coverage/app-models-story_rb.html +0 -621
- data/lib/branston/coverage/app-models-user_rb.html +0 -513
- data/lib/branston/coverage/app-models-user_role_rb.html +0 -189
- data/lib/branston/coverage/index.html +0 -570
- data/lib/branston/coverage/jquery-1.3.2.min.js +0 -19
- data/lib/branston/coverage/jquery.tablesorter.min.js +0 -15
- data/lib/branston/coverage/lib-client_rb.html +0 -537
- data/lib/branston/coverage/lib-faker_extras_rb.html +0 -207
- data/lib/branston/coverage/lib-story_generator_rb.html +0 -873
- data/lib/branston/coverage/print.css +0 -12
- data/lib/branston/coverage/rcov.js +0 -42
- data/lib/branston/coverage/screen.css +0 -270
- data/lib/branston/db/migrate/20091127131037_create_user_roles.rb +0 -13
- data/lib/branston/db/migrate/20091127172950_add_story_id_to_user_role.rb +0 -10
- data/lib/branston/test/functional/user_roles_controller_test.rb +0 -71
- data/lib/branston/test/unit/helpers/user_roles_helper_test.rb +0 -4
- data/lib/branston/test/unit/user_role_test.rb +0 -9
- data/lib/branston/tmp/performance/BrowsingTest#test_homepage_process_time_flat.txt +0 -8
- data/lib/branston/tmp/performance/BrowsingTest#test_homepage_process_time_graph.html +0 -6718
- data/lib/branston/tmp/performance/BrowsingTest#test_homepage_process_time_tree.txt +0 -9942
- data/lib/branston/vendor/plugins/state_machine/CHANGELOG.rdoc +0 -298
- data/lib/branston/vendor/plugins/state_machine/LICENSE +0 -20
- data/lib/branston/vendor/plugins/state_machine/README.rdoc +0 -466
- data/lib/branston/vendor/plugins/state_machine/Rakefile +0 -98
- data/lib/branston/vendor/plugins/state_machine/examples/AutoShop_state.png +0 -0
- data/lib/branston/vendor/plugins/state_machine/examples/Car_state.png +0 -0
- data/lib/branston/vendor/plugins/state_machine/examples/TrafficLight_state.png +0 -0
- data/lib/branston/vendor/plugins/state_machine/examples/Vehicle_state.png +0 -0
- data/lib/branston/vendor/plugins/state_machine/examples/auto_shop.rb +0 -11
- data/lib/branston/vendor/plugins/state_machine/examples/car.rb +0 -19
- data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/controller.rb +0 -51
- data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/model.rb +0 -28
- data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/view_edit.html.erb +0 -24
- data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/view_index.html.erb +0 -23
- data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/view_new.html.erb +0 -13
- data/lib/branston/vendor/plugins/state_machine/examples/merb-rest/view_show.html.erb +0 -17
- data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/controller.rb +0 -43
- data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/migration.rb +0 -11
- data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/model.rb +0 -23
- data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/view_edit.html.erb +0 -25
- data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/view_index.html.erb +0 -23
- data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/view_new.html.erb +0 -14
- data/lib/branston/vendor/plugins/state_machine/examples/rails-rest/view_show.html.erb +0 -17
- data/lib/branston/vendor/plugins/state_machine/examples/traffic_light.rb +0 -7
- data/lib/branston/vendor/plugins/state_machine/examples/vehicle.rb +0 -31
- data/lib/branston/vendor/plugins/state_machine/init.rb +0 -1
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine.rb +0 -388
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/assertions.rb +0 -36
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/callback.rb +0 -189
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/condition_proxy.rb +0 -94
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/eval_helpers.rb +0 -67
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/event.rb +0 -252
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/event_collection.rb +0 -122
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/extensions.rb +0 -149
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/guard.rb +0 -230
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations.rb +0 -68
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/active_record.rb +0 -492
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/active_record/locale.rb +0 -11
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/active_record/observer.rb +0 -41
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/data_mapper.rb +0 -351
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/data_mapper/observer.rb +0 -139
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/integrations/sequel.rb +0 -322
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/machine.rb +0 -1467
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/machine_collection.rb +0 -155
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/matcher.rb +0 -123
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/matcher_helpers.rb +0 -54
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/node_collection.rb +0 -152
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/state.rb +0 -249
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/state_collection.rb +0 -112
- data/lib/branston/vendor/plugins/state_machine/lib/state_machine/transition.rb +0 -394
- data/lib/branston/vendor/plugins/state_machine/state_machine.gemspec +0 -30
- data/lib/branston/vendor/plugins/state_machine/tasks/state_machine.rake +0 -1
- data/lib/branston/vendor/plugins/state_machine/tasks/state_machine.rb +0 -30
- data/lib/branston/vendor/plugins/state_machine/test/classes/switch.rb +0 -11
- data/lib/branston/vendor/plugins/state_machine/test/functional/state_machine_test.rb +0 -941
- data/lib/branston/vendor/plugins/state_machine/test/test_helper.rb +0 -4
- data/lib/branston/vendor/plugins/state_machine/test/unit/assertions_test.rb +0 -40
- data/lib/branston/vendor/plugins/state_machine/test/unit/callback_test.rb +0 -455
- data/lib/branston/vendor/plugins/state_machine/test/unit/condition_proxy_test.rb +0 -328
- data/lib/branston/vendor/plugins/state_machine/test/unit/eval_helpers_test.rb +0 -120
- data/lib/branston/vendor/plugins/state_machine/test/unit/event_collection_test.rb +0 -326
- data/lib/branston/vendor/plugins/state_machine/test/unit/event_test.rb +0 -743
- data/lib/branston/vendor/plugins/state_machine/test/unit/guard_test.rb +0 -908
- data/lib/branston/vendor/plugins/state_machine/test/unit/integrations/active_record_test.rb +0 -1367
- data/lib/branston/vendor/plugins/state_machine/test/unit/integrations/data_mapper_test.rb +0 -962
- data/lib/branston/vendor/plugins/state_machine/test/unit/integrations/sequel_test.rb +0 -859
- data/lib/branston/vendor/plugins/state_machine/test/unit/integrations_test.rb +0 -42
- data/lib/branston/vendor/plugins/state_machine/test/unit/invalid_event_test.rb +0 -7
- data/lib/branston/vendor/plugins/state_machine/test/unit/invalid_transition_test.rb +0 -7
- data/lib/branston/vendor/plugins/state_machine/test/unit/machine_collection_test.rb +0 -938
- data/lib/branston/vendor/plugins/state_machine/test/unit/machine_test.rb +0 -2004
- data/lib/branston/vendor/plugins/state_machine/test/unit/matcher_helpers_test.rb +0 -37
- data/lib/branston/vendor/plugins/state_machine/test/unit/matcher_test.rb +0 -155
- data/lib/branston/vendor/plugins/state_machine/test/unit/node_collection_test.rb +0 -207
- data/lib/branston/vendor/plugins/state_machine/test/unit/state_collection_test.rb +0 -280
- data/lib/branston/vendor/plugins/state_machine/test/unit/state_machine_test.rb +0 -31
- data/lib/branston/vendor/plugins/state_machine/test/unit/state_test.rb +0 -795
- data/lib/branston/vendor/plugins/state_machine/test/unit/transition_test.rb +0 -1212
@@ -1,249 +0,0 @@
|
|
1
|
-
require 'state_machine/assertions'
|
2
|
-
require 'state_machine/condition_proxy'
|
3
|
-
|
4
|
-
module StateMachine
|
5
|
-
# A state defines a value that an attribute can be in after being transitioned
|
6
|
-
# 0 or more times. States can represent a value of any type in Ruby, though
|
7
|
-
# the most common (and default) type is String.
|
8
|
-
#
|
9
|
-
# In addition to defining the machine's value, a state can also define a
|
10
|
-
# behavioral context for an object when that object is in the state. See
|
11
|
-
# StateMachine::Machine#state for more information about how state-driven
|
12
|
-
# behavior can be utilized.
|
13
|
-
class State
|
14
|
-
include Assertions
|
15
|
-
|
16
|
-
# The state machine for which this state is defined
|
17
|
-
attr_accessor :machine
|
18
|
-
|
19
|
-
# The unique identifier for the state used in event and callback definitions
|
20
|
-
attr_reader :name
|
21
|
-
|
22
|
-
# The fully-qualified identifier for the state, scoped by the machine's
|
23
|
-
# namespace
|
24
|
-
attr_reader :qualified_name
|
25
|
-
|
26
|
-
# The value that is written to a machine's attribute when an object
|
27
|
-
# transitions into this state
|
28
|
-
attr_writer :value
|
29
|
-
|
30
|
-
# Whether this state's value should be cached after being evaluated
|
31
|
-
attr_accessor :cache
|
32
|
-
|
33
|
-
# Whether or not this state is the initial state to use for new objects
|
34
|
-
attr_accessor :initial
|
35
|
-
alias_method :initial?, :initial
|
36
|
-
|
37
|
-
# A custom lambda block for determining whether a given value matches this
|
38
|
-
# state
|
39
|
-
attr_accessor :matcher
|
40
|
-
|
41
|
-
# Tracks all of the methods that have been defined for the machine's owner
|
42
|
-
# class when objects are in this state.
|
43
|
-
#
|
44
|
-
# Maps :method_name => UnboundMethod
|
45
|
-
attr_reader :methods
|
46
|
-
|
47
|
-
# Creates a new state within the context of the given machine.
|
48
|
-
#
|
49
|
-
# Configuration options:
|
50
|
-
# * <tt>:initial</tt> - Whether this state is the beginning state for the
|
51
|
-
# machine. Default is false.
|
52
|
-
# * <tt>:value</tt> - The value to store when an object transitions to this
|
53
|
-
# state. Default is the name (stringified).
|
54
|
-
# * <tt>:cache</tt> - If a dynamic value (via a lambda block) is being used,
|
55
|
-
# then setting this to true will cache the evaluated result
|
56
|
-
# * <tt>:if</tt> - Determines whether a value matches this state
|
57
|
-
# (e.g. :value => lambda {Time.now}, :if => lambda {|state| !state.nil?}).
|
58
|
-
# By default, the configured value is matched.
|
59
|
-
def initialize(machine, name, options = {}) #:nodoc:
|
60
|
-
assert_valid_keys(options, :initial, :value, :cache, :if)
|
61
|
-
|
62
|
-
@machine = machine
|
63
|
-
@name = name
|
64
|
-
@qualified_name = name && machine.namespace ? :"#{machine.namespace}_#{name}" : name
|
65
|
-
@value = options.include?(:value) ? options[:value] : name && name.to_s
|
66
|
-
@cache = options[:cache]
|
67
|
-
@matcher = options[:if]
|
68
|
-
@methods = {}
|
69
|
-
@initial = options[:initial] == true
|
70
|
-
|
71
|
-
add_predicate
|
72
|
-
end
|
73
|
-
|
74
|
-
# Creates a copy of this state in addition to the list of associated
|
75
|
-
# methods to prevent conflicts across different states.
|
76
|
-
def initialize_copy(orig) #:nodoc:
|
77
|
-
super
|
78
|
-
@methods = methods.dup
|
79
|
-
end
|
80
|
-
|
81
|
-
# Determines whether there are any states that can be transitioned to from
|
82
|
-
# this state. If there are none, then this state is considered *final*.
|
83
|
-
# Any objects in a final state will remain so forever given the current
|
84
|
-
# machine's definition.
|
85
|
-
def final?
|
86
|
-
!machine.events.any? do |event|
|
87
|
-
event.guards.any? do |guard|
|
88
|
-
guard.state_requirements.any? do |requirement|
|
89
|
-
requirement[:from].matches?(name) && !requirement[:to].matches?(name, :from => name)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
# Generates a human-readable description of this state's name / value:
|
96
|
-
#
|
97
|
-
# For example,
|
98
|
-
#
|
99
|
-
# State.new(machine, :parked).description # => "parked"
|
100
|
-
# State.new(machine, :parked, :value => :parked).description # => "parked"
|
101
|
-
# State.new(machine, :parked, :value => nil).description # => "parked (nil)"
|
102
|
-
# State.new(machine, :parked, :value => 1).description # => "parked (1)"
|
103
|
-
# State.new(machine, :parked, :value => lambda {Time.now}).description # => "parked (*)
|
104
|
-
def description
|
105
|
-
description = name ? name.to_s : name.inspect
|
106
|
-
description << " (#{@value.is_a?(Proc) ? '*' : @value.inspect})" unless name.to_s == @value.to_s
|
107
|
-
description
|
108
|
-
end
|
109
|
-
|
110
|
-
# The value that represents this state. This will optionally evaluate the
|
111
|
-
# original block if it's a lambda block. Otherwise, the static value is
|
112
|
-
# returned.
|
113
|
-
#
|
114
|
-
# For example,
|
115
|
-
#
|
116
|
-
# State.new(machine, :parked, :value => 1).value # => 1
|
117
|
-
# State.new(machine, :parked, :value => lambda {Time.now}).value # => Tue Jan 01 00:00:00 UTC 2008
|
118
|
-
# State.new(machine, :parked, :value => lambda {Time.now}).value(false) # => <Proc:0xb6ea7ca0@...>
|
119
|
-
def value(eval = true)
|
120
|
-
if @value.is_a?(Proc) && eval
|
121
|
-
if cache_value?
|
122
|
-
@value = @value.call
|
123
|
-
machine.states.update(self)
|
124
|
-
@value
|
125
|
-
else
|
126
|
-
@value.call
|
127
|
-
end
|
128
|
-
else
|
129
|
-
@value
|
130
|
-
end
|
131
|
-
end
|
132
|
-
|
133
|
-
# Determines whether this state matches the given value. If no matcher is
|
134
|
-
# configured, then this will check whether the values are equivalent.
|
135
|
-
# Otherwise, the matcher will determine the result.
|
136
|
-
#
|
137
|
-
# For example,
|
138
|
-
#
|
139
|
-
# # Without a matcher
|
140
|
-
# state = State.new(machine, :parked, :value => 1)
|
141
|
-
# state.matches?(1) # => true
|
142
|
-
# state.matches?(2) # => false
|
143
|
-
#
|
144
|
-
# # With a matcher
|
145
|
-
# state = State.new(machine, :parked, :value => lambda {Time.now}, :if => lambda {|value| !value.nil?})
|
146
|
-
# state.matches?(nil) # => false
|
147
|
-
# state.matches?(Time.now) # => true
|
148
|
-
def matches?(other_value)
|
149
|
-
matcher ? matcher.call(other_value) : other_value == value
|
150
|
-
end
|
151
|
-
|
152
|
-
# Defines a context for the state which will be enabled on instances of
|
153
|
-
# the owner class when the machine is in this state.
|
154
|
-
#
|
155
|
-
# This can be called multiple times. Each time a new context is created,
|
156
|
-
# a new module will be included in the owner class.
|
157
|
-
def context(&block)
|
158
|
-
owner_class = machine.owner_class
|
159
|
-
machine_name = machine.name
|
160
|
-
name = self.name
|
161
|
-
|
162
|
-
# Evaluate the method definitions
|
163
|
-
context = ConditionProxy.new(owner_class, lambda {|object| object.class.state_machine(machine_name).states.matches?(object, name)})
|
164
|
-
context.class_eval(&block)
|
165
|
-
context.instance_methods.each do |method|
|
166
|
-
methods[method.to_sym] = context.instance_method(method)
|
167
|
-
|
168
|
-
# Calls the method defined by the current state of the machine
|
169
|
-
context.class_eval <<-end_eval, __FILE__, __LINE__
|
170
|
-
def #{method}(*args, &block)
|
171
|
-
self.class.state_machine(#{machine_name.inspect}).states.match!(self).call(self, #{method.inspect}, *args, &block)
|
172
|
-
end
|
173
|
-
end_eval
|
174
|
-
end
|
175
|
-
|
176
|
-
# Include the context so that it can be bound to the owner class (the
|
177
|
-
# context is considered an ancestor, so it's allowed to be bound)
|
178
|
-
owner_class.class_eval { include context }
|
179
|
-
|
180
|
-
context
|
181
|
-
end
|
182
|
-
|
183
|
-
# Calls a method defined in this state's context on the given object. All
|
184
|
-
# arguments and any block will be passed into the method defined.
|
185
|
-
#
|
186
|
-
# If the method has never been defined for this state, then a NoMethodError
|
187
|
-
# will be raised.
|
188
|
-
def call(object, method, *args, &block)
|
189
|
-
if context_method = methods[method.to_sym]
|
190
|
-
# Method is defined by the state: proxy it through
|
191
|
-
context_method.bind(object).call(*args, &block)
|
192
|
-
else
|
193
|
-
# Raise exception as if the method never existed on the original object
|
194
|
-
raise NoMethodError, "undefined method '#{method}' for #{object} with #{name || 'nil'} #{machine.name}"
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
# Draws a representation of this state on the given machine. This will
|
199
|
-
# create a new node on the graph with the following properties:
|
200
|
-
# * +label+ - The human-friendly description of the state.
|
201
|
-
# * +width+ - The width of the node. Always 1.
|
202
|
-
# * +height+ - The height of the node. Always 1.
|
203
|
-
# * +shape+ - The actual shape of the node. If the state is a final
|
204
|
-
# state, then "doublecircle", otherwise "ellipse".
|
205
|
-
#
|
206
|
-
# The actual node generated on the graph will be returned.
|
207
|
-
def draw(graph)
|
208
|
-
node = graph.add_node(name ? name.to_s : 'nil',
|
209
|
-
:label => description,
|
210
|
-
:width => '1',
|
211
|
-
:height => '1',
|
212
|
-
:shape => final? ? 'doublecircle' : 'ellipse'
|
213
|
-
)
|
214
|
-
|
215
|
-
# Add open arrow for initial state
|
216
|
-
graph.add_edge(graph.add_node('starting_state', :shape => 'point'), node) if initial?
|
217
|
-
|
218
|
-
node
|
219
|
-
end
|
220
|
-
|
221
|
-
# Generates a nicely formatted description of this state's contents.
|
222
|
-
#
|
223
|
-
# For example,
|
224
|
-
#
|
225
|
-
# state = StateMachine::State.new(machine, :parked, :value => 1, :initial => true)
|
226
|
-
# state # => #<StateMachine::State name=:parked value=1 initial=true context=[]>
|
227
|
-
def inspect
|
228
|
-
attributes = [[:name, name], [:value, @value], [:initial, initial?], [:context, methods.keys]]
|
229
|
-
"#<#{self.class} #{attributes.map {|attr, value| "#{attr}=#{value.inspect}"} * ' '}>"
|
230
|
-
end
|
231
|
-
|
232
|
-
private
|
233
|
-
# Should the value be cached after it's evaluated for the first time?
|
234
|
-
def cache_value?
|
235
|
-
@cache
|
236
|
-
end
|
237
|
-
|
238
|
-
# Adds a predicate method to the owner class so long as a name has
|
239
|
-
# actually been configured for the state
|
240
|
-
def add_predicate
|
241
|
-
return unless name
|
242
|
-
|
243
|
-
# Checks whether the current value matches this state
|
244
|
-
machine.define_instance_method("#{qualified_name}?") do |machine, object|
|
245
|
-
machine.states.matches?(object, name)
|
246
|
-
end
|
247
|
-
end
|
248
|
-
end
|
249
|
-
end
|
@@ -1,112 +0,0 @@
|
|
1
|
-
require 'state_machine/node_collection'
|
2
|
-
|
3
|
-
module StateMachine
|
4
|
-
# Represents a collection of states in a state machine
|
5
|
-
class StateCollection < NodeCollection
|
6
|
-
def initialize(machine) #:nodoc:
|
7
|
-
super(machine, :index => [:name, :value])
|
8
|
-
end
|
9
|
-
|
10
|
-
# Determines whether the given object is in a specific state. If the
|
11
|
-
# object's current value doesn't match the state, then this will return
|
12
|
-
# false, otherwise true. If the given state is unknown, then an IndexError
|
13
|
-
# will be raised.
|
14
|
-
#
|
15
|
-
# == Examples
|
16
|
-
#
|
17
|
-
# class Vehicle
|
18
|
-
# state_machine :initial => :parked do
|
19
|
-
# other_states :idling
|
20
|
-
# end
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# states = Vehicle.state_machine.states
|
24
|
-
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
|
25
|
-
#
|
26
|
-
# states.matches?(vehicle, :parked) # => true
|
27
|
-
# states.matches?(vehicle, :idling) # => false
|
28
|
-
# states.matches?(vehicle, :invalid) # => IndexError: :invalid is an invalid key for :name index
|
29
|
-
def matches?(object, name)
|
30
|
-
fetch(name).matches?(machine.read(object, :state))
|
31
|
-
end
|
32
|
-
|
33
|
-
# Determines the current state of the given object as configured by this
|
34
|
-
# state machine. This will attempt to find a known state that matches
|
35
|
-
# the value of the attribute on the object.
|
36
|
-
#
|
37
|
-
# == Examples
|
38
|
-
#
|
39
|
-
# class Vehicle
|
40
|
-
# state_machine :initial => :parked do
|
41
|
-
# other_states :idling
|
42
|
-
# end
|
43
|
-
# end
|
44
|
-
#
|
45
|
-
# states = Vehicle.state_machine.states
|
46
|
-
#
|
47
|
-
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
|
48
|
-
# states.match(vehicle) # => #<StateMachine::State name=:parked value="parked" initial=true>
|
49
|
-
#
|
50
|
-
# vehicle.state = 'idling'
|
51
|
-
# states.match(vehicle) # => #<StateMachine::State name=:idling value="idling" initial=true>
|
52
|
-
#
|
53
|
-
# vehicle.state = 'invalid'
|
54
|
-
# states.match(vehicle) # => nil
|
55
|
-
def match(object)
|
56
|
-
value = machine.read(object, :state)
|
57
|
-
self[value, :value] || detect {|state| state.matches?(value)}
|
58
|
-
end
|
59
|
-
|
60
|
-
# Determines the current state of the given object as configured by this
|
61
|
-
# state machine. If no state is found, then an ArgumentError will be
|
62
|
-
# raised.
|
63
|
-
#
|
64
|
-
# == Examples
|
65
|
-
#
|
66
|
-
# class Vehicle
|
67
|
-
# state_machine :initial => :parked do
|
68
|
-
# other_states :idling
|
69
|
-
# end
|
70
|
-
# end
|
71
|
-
#
|
72
|
-
# states = Vehicle.state_machine.states
|
73
|
-
#
|
74
|
-
# vehicle = Vehicle.new # => #<Vehicle:0xb7c464b0 @state="parked">
|
75
|
-
# states.match!(vehicle) # => #<StateMachine::State name=:parked value="parked" initial=true>
|
76
|
-
#
|
77
|
-
# vehicle.state = 'invalid'
|
78
|
-
# states.match!(vehicle) # => ArgumentError: "invalid" is not a known state value
|
79
|
-
def match!(object)
|
80
|
-
match(object) || raise(ArgumentError, "#{machine.read(object, :state).inspect} is not a known #{machine.name} value")
|
81
|
-
end
|
82
|
-
|
83
|
-
# Gets the order in which states should be displayed based on where they
|
84
|
-
# were first referenced. This will order states in the following priority:
|
85
|
-
#
|
86
|
-
# 1. Initial state
|
87
|
-
# 2. Event transitions (:from, :except_from, :to, :except_to options)
|
88
|
-
# 3. States with behaviors
|
89
|
-
# 4. States referenced via +state+ or +other_states+
|
90
|
-
# 5. States referenced in callbacks
|
91
|
-
#
|
92
|
-
# This order will determine how the GraphViz visualizations are rendered.
|
93
|
-
def by_priority
|
94
|
-
order = select {|state| state.initial}.map {|state| state.name}
|
95
|
-
|
96
|
-
machine.events.each {|event| order += event.known_states}
|
97
|
-
order += select {|state| state.methods.any?}.map {|state| state.name}
|
98
|
-
order += keys(:name) - machine.callbacks.values.flatten.map {|callback| callback.known_states}.flatten
|
99
|
-
order += keys(:name)
|
100
|
-
|
101
|
-
order.uniq!
|
102
|
-
order.map! {|name| self[name]}
|
103
|
-
order
|
104
|
-
end
|
105
|
-
|
106
|
-
private
|
107
|
-
# Gets the value for the given attribute on the node
|
108
|
-
def value(node, attribute)
|
109
|
-
attribute == :value ? node.value(false) : super
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|
@@ -1,394 +0,0 @@
|
|
1
|
-
module StateMachine
|
2
|
-
# An invalid transition was attempted
|
3
|
-
class InvalidTransition < StandardError
|
4
|
-
end
|
5
|
-
|
6
|
-
# A transition represents a state change for a specific attribute.
|
7
|
-
#
|
8
|
-
# Transitions consist of:
|
9
|
-
# * An event
|
10
|
-
# * A starting state
|
11
|
-
# * An ending state
|
12
|
-
class Transition
|
13
|
-
class << self
|
14
|
-
# Runs one or more transitions in parallel. All transitions will run
|
15
|
-
# through the following steps:
|
16
|
-
# 1. Before callbacks
|
17
|
-
# 2. Persist state
|
18
|
-
# 3. Invoke action
|
19
|
-
# 4. After callbacks (if configured)
|
20
|
-
# 5. Rollback (if action is unsuccessful)
|
21
|
-
#
|
22
|
-
# Configuration options:
|
23
|
-
# * <tt>:action</tt> - Whether to run the action configured for each transition
|
24
|
-
# * <tt>:after</tt> - Whether to run after callbacks
|
25
|
-
#
|
26
|
-
# If a block is passed to this method, that block will be called instead
|
27
|
-
# of invoking each transition's action.
|
28
|
-
def perform(transitions, options = {})
|
29
|
-
# Validate that the transitions are for separate machines / attributes
|
30
|
-
attributes = transitions.map {|transition| transition.attribute}.uniq
|
31
|
-
raise ArgumentError, 'Cannot perform multiple transitions in parallel for the same state machine attribute' if attributes.length != transitions.length
|
32
|
-
|
33
|
-
success = false
|
34
|
-
|
35
|
-
# Run before callbacks. If any callback halts, then the entire chain
|
36
|
-
# is halted for every transition.
|
37
|
-
if transitions.all? {|transition| transition.before}
|
38
|
-
# Persist the new state for each attribute
|
39
|
-
transitions.each {|transition| transition.persist}
|
40
|
-
|
41
|
-
# Run the actions associated with each machine
|
42
|
-
begin
|
43
|
-
results = {}
|
44
|
-
success =
|
45
|
-
if block_given?
|
46
|
-
# Block was given: use the result for each transition
|
47
|
-
result = yield
|
48
|
-
transitions.each {|transition| results[transition.action] = result}
|
49
|
-
!!result
|
50
|
-
elsif options[:action] == false
|
51
|
-
# Skip the action
|
52
|
-
true
|
53
|
-
else
|
54
|
-
# Run each transition's action (only once)
|
55
|
-
object = transitions.first.object
|
56
|
-
transitions.all? do |transition|
|
57
|
-
action = transition.action
|
58
|
-
action && !results.include?(action) ? results[action] = object.send(action) : true
|
59
|
-
end
|
60
|
-
end
|
61
|
-
rescue Exception
|
62
|
-
# Action failed: rollback
|
63
|
-
transitions.each {|transition| transition.rollback}
|
64
|
-
raise
|
65
|
-
end
|
66
|
-
|
67
|
-
# Run after callbacks even when the actions failed. The :after option
|
68
|
-
# is ignored if the transitions were unsuccessful.
|
69
|
-
transitions.each {|transition| transition.after(results[transition.action], success)} unless options[:after] == false && success
|
70
|
-
|
71
|
-
# Rollback the transitions if the transaction was unsuccessful
|
72
|
-
transitions.each {|transition| transition.rollback} unless success
|
73
|
-
end
|
74
|
-
|
75
|
-
success
|
76
|
-
end
|
77
|
-
|
78
|
-
# Runs one or more transitions within a transaction. See StateMachine::Transition.perform
|
79
|
-
# for more information.
|
80
|
-
def perform_within_transaction(transitions, options = {})
|
81
|
-
success = false
|
82
|
-
transitions.first.within_transaction do
|
83
|
-
success = perform(transitions, options)
|
84
|
-
end
|
85
|
-
|
86
|
-
success
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
# The object being transitioned
|
91
|
-
attr_reader :object
|
92
|
-
|
93
|
-
# The state machine for which this transition is defined
|
94
|
-
attr_reader :machine
|
95
|
-
|
96
|
-
# The event that triggered the transition
|
97
|
-
attr_reader :event
|
98
|
-
|
99
|
-
# The fully-qualified name of the event that triggered the transition
|
100
|
-
attr_reader :qualified_event
|
101
|
-
|
102
|
-
# The original state value *before* the transition
|
103
|
-
attr_reader :from
|
104
|
-
|
105
|
-
# The original state name *before* the transition
|
106
|
-
attr_reader :from_name
|
107
|
-
|
108
|
-
# The original fully-qualified state name *before* transition
|
109
|
-
attr_reader :qualified_from_name
|
110
|
-
|
111
|
-
# The new state value *after* the transition
|
112
|
-
attr_reader :to
|
113
|
-
|
114
|
-
# The new state name *after* the transition
|
115
|
-
attr_reader :to_name
|
116
|
-
|
117
|
-
# The new fully-qualified state name *after* the transition
|
118
|
-
attr_reader :qualified_to_name
|
119
|
-
|
120
|
-
# The arguments passed in to the event that triggered the transition
|
121
|
-
# (does not include the +run_action+ boolean argument if specified)
|
122
|
-
attr_accessor :args
|
123
|
-
|
124
|
-
# The result of invoking the action associated with the machine
|
125
|
-
attr_reader :result
|
126
|
-
|
127
|
-
# Creates a new, specific transition
|
128
|
-
def initialize(object, machine, event, from_name, to_name, read_state = true) #:nodoc:
|
129
|
-
@object = object
|
130
|
-
@machine = machine
|
131
|
-
@args = []
|
132
|
-
|
133
|
-
# Event information
|
134
|
-
event = machine.events.fetch(event)
|
135
|
-
@event = event.name
|
136
|
-
@qualified_event = event.qualified_name
|
137
|
-
|
138
|
-
# From state information
|
139
|
-
from_state = machine.states.fetch(from_name)
|
140
|
-
@from = read_state ? machine.read(object, :state) : from_state.value
|
141
|
-
@from_name = from_state.name
|
142
|
-
@qualified_from_name = from_state.qualified_name
|
143
|
-
|
144
|
-
# To state information
|
145
|
-
to_state = machine.states.fetch(to_name)
|
146
|
-
@to = to_state.value
|
147
|
-
@to_name = to_state.name
|
148
|
-
@qualified_to_name = to_state.qualified_name
|
149
|
-
end
|
150
|
-
|
151
|
-
# The attribute which this transition's machine is defined for
|
152
|
-
def attribute
|
153
|
-
machine.attribute
|
154
|
-
end
|
155
|
-
|
156
|
-
# The action that will be run when this transition is performed
|
157
|
-
def action
|
158
|
-
machine.action
|
159
|
-
end
|
160
|
-
|
161
|
-
# Does this transition represent a loopback (i.e. the from and to state
|
162
|
-
# are the same)
|
163
|
-
#
|
164
|
-
# == Example
|
165
|
-
#
|
166
|
-
# machine = StateMachine.new(Vehicle)
|
167
|
-
# StateMachine::Transition.new(Vehicle.new, machine, :park, :parked, :parked).loopback? # => true
|
168
|
-
# StateMachine::Transition.new(Vehicle.new, machine, :park, :idling, :parked).loopback? # => false
|
169
|
-
def loopback?
|
170
|
-
from_name == to_name
|
171
|
-
end
|
172
|
-
|
173
|
-
# A hash of all the core attributes defined for this transition with their
|
174
|
-
# names as keys and values of the attributes as values.
|
175
|
-
#
|
176
|
-
# == Example
|
177
|
-
#
|
178
|
-
# machine = StateMachine.new(Vehicle)
|
179
|
-
# transition = StateMachine::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
|
180
|
-
# transition.attributes # => {:object => #<Vehicle:0xb7d60ea4>, :attribute => :state, :event => :ignite, :from => 'parked', :to => 'idling'}
|
181
|
-
def attributes
|
182
|
-
@attributes ||= {:object => object, :attribute => attribute, :event => event, :from => from, :to => to}
|
183
|
-
end
|
184
|
-
|
185
|
-
# Runs the actual transition and any before/after callbacks associated
|
186
|
-
# with the transition. The action associated with the transition/machine
|
187
|
-
# can be skipped by passing in +false+.
|
188
|
-
#
|
189
|
-
# == Examples
|
190
|
-
#
|
191
|
-
# class Vehicle
|
192
|
-
# state_machine :action => :save do
|
193
|
-
# ...
|
194
|
-
# end
|
195
|
-
# end
|
196
|
-
#
|
197
|
-
# vehicle = Vehicle.new
|
198
|
-
# transition = StateMachine::Transition.new(vehicle, machine, :ignite, :parked, :idling)
|
199
|
-
# transition.perform # => Runs the +save+ action after setting the state attribute
|
200
|
-
# transition.perform(false) # => Only sets the state attribute
|
201
|
-
def perform(*args)
|
202
|
-
run_action = [true, false].include?(args.last) ? args.pop : true
|
203
|
-
self.args = args
|
204
|
-
|
205
|
-
# Run the transition
|
206
|
-
self.class.perform_within_transaction([self], :action => run_action)
|
207
|
-
end
|
208
|
-
|
209
|
-
# Runs a block within a transaction for the object being transitioned.
|
210
|
-
# By default, transactions are a no-op unless otherwise defined by the
|
211
|
-
# machine's integration.
|
212
|
-
def within_transaction
|
213
|
-
machine.within_transaction(object) do
|
214
|
-
yield
|
215
|
-
end
|
216
|
-
end
|
217
|
-
|
218
|
-
# Runs the machine's +before+ callbacks for this transition. Only
|
219
|
-
# callbacks that are configured to match the event, from state, and to
|
220
|
-
# state will be invoked.
|
221
|
-
#
|
222
|
-
# Once the callbacks are run, they cannot be run again until this transition
|
223
|
-
# is reset.
|
224
|
-
#
|
225
|
-
# == Example
|
226
|
-
#
|
227
|
-
# class Vehicle
|
228
|
-
# state_machine do
|
229
|
-
# before_transition :on => :ignite, :do => lambda {|vehicle| ...}
|
230
|
-
# end
|
231
|
-
# end
|
232
|
-
#
|
233
|
-
# vehicle = Vehicle.new
|
234
|
-
# transition = StateMachine::Transition.new(vehicle, machine, :ignite, :parked, :idling)
|
235
|
-
# transition.before
|
236
|
-
def before
|
237
|
-
result = false
|
238
|
-
|
239
|
-
catch(:halt) do
|
240
|
-
unless @before_run
|
241
|
-
callback(:before)
|
242
|
-
@before_run = true
|
243
|
-
end
|
244
|
-
|
245
|
-
result = true
|
246
|
-
end
|
247
|
-
|
248
|
-
result
|
249
|
-
end
|
250
|
-
|
251
|
-
# Transitions the current value of the state to that specified by the
|
252
|
-
# transition. Once the state is persisted, it cannot be persisted again
|
253
|
-
# until this transition is reset.
|
254
|
-
#
|
255
|
-
# == Example
|
256
|
-
#
|
257
|
-
# class Vehicle
|
258
|
-
# state_machine do
|
259
|
-
# event :ignite do
|
260
|
-
# transition :parked => :idling
|
261
|
-
# end
|
262
|
-
# end
|
263
|
-
# end
|
264
|
-
#
|
265
|
-
# vehicle = Vehicle.new
|
266
|
-
# transition = StateMachine::Transition.new(vehicle, Vehicle.state_machine, :ignite, :parked, :idling)
|
267
|
-
# transition.persist
|
268
|
-
#
|
269
|
-
# vehicle.state # => 'idling'
|
270
|
-
def persist
|
271
|
-
unless @persisted
|
272
|
-
machine.write(object, :state, to)
|
273
|
-
@persisted = true
|
274
|
-
end
|
275
|
-
end
|
276
|
-
|
277
|
-
# Runs the machine's +after+ callbacks for this transition. Only
|
278
|
-
# callbacks that are configured to match the event, from state, and to
|
279
|
-
# state will be invoked.
|
280
|
-
#
|
281
|
-
# The result can be used to indicate whether the associated machine action
|
282
|
-
# was executed successfully.
|
283
|
-
#
|
284
|
-
# Once the callbacks are run, they cannot be run again until this transition
|
285
|
-
# is reset.
|
286
|
-
#
|
287
|
-
# == Halting
|
288
|
-
#
|
289
|
-
# If any callback throws a <tt>:halt</tt> exception, it will be caught
|
290
|
-
# and the callback chain will be automatically stopped. However, this
|
291
|
-
# exception will not bubble up to the caller since +after+ callbacks
|
292
|
-
# should never halt the execution of a +perform+.
|
293
|
-
#
|
294
|
-
# == Example
|
295
|
-
#
|
296
|
-
# class Vehicle
|
297
|
-
# state_machine do
|
298
|
-
# after_transition :on => :ignite, :do => lambda {|vehicle| ...}
|
299
|
-
#
|
300
|
-
# event :ignite do
|
301
|
-
# transition :parked => :idling
|
302
|
-
# end
|
303
|
-
# end
|
304
|
-
# end
|
305
|
-
#
|
306
|
-
# vehicle = Vehicle.new
|
307
|
-
# transition = StateMachine::Transition.new(vehicle, Vehicle.state_machine, :ignite, :parked, :idling)
|
308
|
-
# transition.after(true)
|
309
|
-
def after(result = nil, success = true)
|
310
|
-
@result = result
|
311
|
-
|
312
|
-
catch(:halt) do
|
313
|
-
unless @after_run
|
314
|
-
callback(:after, :success => success)
|
315
|
-
@after_run = true
|
316
|
-
end
|
317
|
-
end
|
318
|
-
|
319
|
-
true
|
320
|
-
end
|
321
|
-
|
322
|
-
# Rolls back changes made to the object's state via this transition. This
|
323
|
-
# will revert the state back to the +from+ value.
|
324
|
-
#
|
325
|
-
# == Example
|
326
|
-
#
|
327
|
-
# class Vehicle
|
328
|
-
# state_machine :initial => :parked do
|
329
|
-
# event :ignite do
|
330
|
-
# transition :parked => :idling
|
331
|
-
# end
|
332
|
-
# end
|
333
|
-
# end
|
334
|
-
#
|
335
|
-
# vehicle = Vehicle.new # => #<Vehicle:0xb7b7f568 @state="parked">
|
336
|
-
# transition = StateMachine::Transition.new(vehicle, Vehicle.state_machine, :ignite, :parked, :idling)
|
337
|
-
#
|
338
|
-
# # Persist the new state
|
339
|
-
# vehicle.state # => "parked"
|
340
|
-
# transition.persist
|
341
|
-
# vehicle.state # => "idling"
|
342
|
-
#
|
343
|
-
# # Roll back to the original state
|
344
|
-
# transition.rollback
|
345
|
-
# vehicle.state # => "parked"
|
346
|
-
def rollback
|
347
|
-
reset
|
348
|
-
machine.write(object, :state, from)
|
349
|
-
end
|
350
|
-
|
351
|
-
# Resets any tracking of which callbacks have already been run and whether
|
352
|
-
# the state has already been persisted
|
353
|
-
def reset
|
354
|
-
@before_run = @persisted = @after_run = false
|
355
|
-
end
|
356
|
-
|
357
|
-
# Generates a nicely formatted description of this transitions's contents.
|
358
|
-
#
|
359
|
-
# For example,
|
360
|
-
#
|
361
|
-
# transition = StateMachine::Transition.new(object, machine, :ignite, :parked, :idling)
|
362
|
-
# transition # => #<StateMachine::Transition attribute=:state event=:ignite from="parked" from_name=:parked to="idling" to_name=:idling>
|
363
|
-
def inspect
|
364
|
-
"#<#{self.class} #{%w(attribute event from from_name to to_name).map {|attr| "#{attr}=#{send(attr).inspect}"} * ' '}>"
|
365
|
-
end
|
366
|
-
|
367
|
-
protected
|
368
|
-
# Gets a hash of the context defining this unique transition (including
|
369
|
-
# event, from state, and to state).
|
370
|
-
#
|
371
|
-
# == Example
|
372
|
-
#
|
373
|
-
# machine = StateMachine.new(Vehicle)
|
374
|
-
# transition = StateMachine::Transition.new(Vehicle.new, machine, :ignite, :parked, :idling)
|
375
|
-
# transition.context # => {:on => :ignite, :from => :parked, :to => :idling}
|
376
|
-
def context
|
377
|
-
@context ||= {:on => event, :from => from_name, :to => to_name}
|
378
|
-
end
|
379
|
-
|
380
|
-
# Runs the callbacks of the given type for this transition. This will
|
381
|
-
# only invoke callbacks that exactly match the event, from state, and
|
382
|
-
# to state that describe this transition.
|
383
|
-
#
|
384
|
-
# Additional callback parameters can be specified. By default, this
|
385
|
-
# transition is also passed into callbacks.
|
386
|
-
def callback(type, context = {})
|
387
|
-
context = self.context.merge(context)
|
388
|
-
|
389
|
-
machine.callbacks[type].each do |callback|
|
390
|
-
callback.call(object, context, self)
|
391
|
-
end
|
392
|
-
end
|
393
|
-
end
|
394
|
-
end
|