| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package FSA::Rules; | 
| 2 |  |  |  |  |  |  |  | 
| 3 | 5 |  |  | 5 |  | 80154 | use strict; | 
|  | 5 |  |  |  |  | 10 |  | 
|  | 5 |  |  |  |  | 184 |  | 
| 4 | 5 |  |  | 5 |  | 91 | use 5.006_002; | 
|  | 5 |  |  |  |  | 13 |  | 
|  | 5 |  |  |  |  | 156 |  | 
| 5 | 5 |  |  | 5 |  | 25 | use Scalar::Util 1.01 (); | 
|  | 5 |  |  |  |  | 118 |  | 
|  | 5 |  |  |  |  | 14082 |  | 
| 6 |  |  |  |  |  |  | $FSA::Rules::VERSION = '0.35'; | 
| 7 |  |  |  |  |  |  |  | 
| 8 |  |  |  |  |  |  | =head1 Name | 
| 9 |  |  |  |  |  |  |  | 
| 10 |  |  |  |  |  |  | FSA::Rules - Build simple rules-based state machines in Perl | 
| 11 |  |  |  |  |  |  |  | 
| 12 |  |  |  |  |  |  | =head1 Synopsis | 
| 13 |  |  |  |  |  |  |  | 
| 14 |  |  |  |  |  |  | my $fsa = FSA::Rules->new( | 
| 15 |  |  |  |  |  |  | ping => { | 
| 16 |  |  |  |  |  |  | do => sub { | 
| 17 |  |  |  |  |  |  | print "ping!\n"; | 
| 18 |  |  |  |  |  |  | my $state = shift; | 
| 19 |  |  |  |  |  |  | $state->result('pong'); | 
| 20 |  |  |  |  |  |  | $state->machine->{count}++; | 
| 21 |  |  |  |  |  |  | }, | 
| 22 |  |  |  |  |  |  | rules => [ | 
| 23 |  |  |  |  |  |  | game_over => sub { shift->machine->{count} >= 20 }, | 
| 24 |  |  |  |  |  |  | pong      => sub { shift->result eq 'pong' }, | 
| 25 |  |  |  |  |  |  | ], | 
| 26 |  |  |  |  |  |  | }, | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | pong => { | 
| 29 |  |  |  |  |  |  | do => sub { print "pong!\n" }, | 
| 30 |  |  |  |  |  |  | rules => [ ping => 1, ], # always goes back to ping | 
| 31 |  |  |  |  |  |  | }, | 
| 32 |  |  |  |  |  |  | game_over => { do => sub { print "Game Over\n" } } | 
| 33 |  |  |  |  |  |  | ); | 
| 34 |  |  |  |  |  |  |  | 
| 35 |  |  |  |  |  |  | $fsa->start; | 
| 36 |  |  |  |  |  |  | $fsa->switch until $fsa->at('game_over'); | 
| 37 |  |  |  |  |  |  |  | 
| 38 |  |  |  |  |  |  | =head1 Description | 
| 39 |  |  |  |  |  |  |  | 
| 40 |  |  |  |  |  |  | This class implements a simple state machine pattern, allowing you to quickly | 
| 41 |  |  |  |  |  |  | build rules-based state machines in Perl. As a simple implementation of a | 
| 42 |  |  |  |  |  |  | powerful concept, it differs slightly from an ideal DFA model in that it does | 
| 43 |  |  |  |  |  |  | not enforce a single possible switch from one state to another. Rather, it | 
| 44 |  |  |  |  |  |  | short circuits the evaluation of the rules for such switches, so that the | 
| 45 |  |  |  |  |  |  | first rule to return a true value will trigger its switch and no other switch | 
| 46 |  |  |  |  |  |  | rules will be checked. (But see the C attribute and parameter to | 
| 47 |  |  |  |  |  |  | C.) It differs from an NFA model in that it offers no back-tracking. | 
| 48 |  |  |  |  |  |  | But in truth, you can use it to build a state machine that adheres to either | 
| 49 |  |  |  |  |  |  | model--hence the more generic FSA moniker. | 
| 50 |  |  |  |  |  |  |  | 
| 51 |  |  |  |  |  |  | FSA::Rules uses named states so that it's easy to tell what state you're in | 
| 52 |  |  |  |  |  |  | and what state you want to go to. Each state may optionally define actions | 
| 53 |  |  |  |  |  |  | that are triggered upon entering the state, after entering the state, and upon | 
| 54 |  |  |  |  |  |  | exiting the state. They may also define rules for switching to other states, | 
| 55 |  |  |  |  |  |  | and these rules may specify the execution of switch-specific actions. All | 
| 56 |  |  |  |  |  |  | actions are defined in terms of anonymous subroutines that should expect an | 
| 57 |  |  |  |  |  |  | FSA::State object itself to be passed as the sole argument. | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | FSA::Rules objects and the FSA::State objects that make them up are all | 
| 60 |  |  |  |  |  |  | implemented as empty hash references. This design allows the action | 
| 61 |  |  |  |  |  |  | subroutines to use the FSA::State object passed as the sole argument, as well | 
| 62 |  |  |  |  |  |  | as the FSA::Rules object available via its C method, to stash data | 
| 63 |  |  |  |  |  |  | for other states to access, without the possibility of interfering with the | 
| 64 |  |  |  |  |  |  | state or the state machine itself. | 
| 65 |  |  |  |  |  |  |  | 
| 66 |  |  |  |  |  |  | =head2 Serialization | 
| 67 |  |  |  |  |  |  |  | 
| 68 |  |  |  |  |  |  | As of version 0.24, FSA::Rules supports serialization by L 2.05 and | 
| 69 |  |  |  |  |  |  | later. In other words, FSA::Rules can function as a persistent state machine. | 
| 70 |  |  |  |  |  |  |  | 
| 71 |  |  |  |  |  |  | However, FSA::Rules stores data outside of FSA::Rules objects, in private data | 
| 72 |  |  |  |  |  |  | structures inside the FSA::Rules module itself. Therefore, unless you want to | 
| 73 |  |  |  |  |  |  | clone your FSA::Rules object, you must let it fall out of scope after you | 
| 74 |  |  |  |  |  |  | serialize it, so that its data will be cleared from memory. Otherwise, if you | 
| 75 |  |  |  |  |  |  | freeze and thaw an FSA::Rules object in a single process without Cing | 
| 76 |  |  |  |  |  |  | the original, there will be I copies of the object stored by FSA::Rules. | 
| 77 |  |  |  |  |  |  |  | 
| 78 |  |  |  |  |  |  | So how does it work? Because the rules are defined as code references, you | 
| 79 |  |  |  |  |  |  | must use Storable 2.05 or later and set its C<$Deparse> and C<$Eval> variables | 
| 80 |  |  |  |  |  |  | to true values: | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | use Storable 2.05 qw(freeze thaw); | 
| 83 |  |  |  |  |  |  |  | 
| 84 |  |  |  |  |  |  | local $Storable::Deparse = 1; | 
| 85 |  |  |  |  |  |  | local $Storable::Eval    = 1; | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | my $frozen = freeze($fsa); | 
| 88 |  |  |  |  |  |  | $fsa = thaw($frozen); | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  | The only caveat is that, while Storable can serialize code references, it | 
| 91 |  |  |  |  |  |  | doesn't properly reference closure variables. So if your rules code references | 
| 92 |  |  |  |  |  |  | are closures, you'll have to serialize the data that they refer to yourself. | 
| 93 |  |  |  |  |  |  |  | 
| 94 |  |  |  |  |  |  | =cut | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  | ############################################################################## | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | =head1 Class Interface | 
| 99 |  |  |  |  |  |  |  | 
| 100 |  |  |  |  |  |  | =head2 Constructor | 
| 101 |  |  |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | =head3 new | 
| 103 |  |  |  |  |  |  |  | 
| 104 |  |  |  |  |  |  | my $fsa = FSA::Rules->new( | 
| 105 |  |  |  |  |  |  | foo_state => { ... }, | 
| 106 |  |  |  |  |  |  | bar_state => { ... }, | 
| 107 |  |  |  |  |  |  | ); | 
| 108 |  |  |  |  |  |  |  | 
| 109 |  |  |  |  |  |  | $fsa = FSA::Rules->new( | 
| 110 |  |  |  |  |  |  | \%params, | 
| 111 |  |  |  |  |  |  | foo_state => { ... }, | 
| 112 |  |  |  |  |  |  | bar_state => { ... }, | 
| 113 |  |  |  |  |  |  | ); | 
| 114 |  |  |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | Constructs and returns a new FSA::Rules object. An optional first argument | 
| 116 |  |  |  |  |  |  | is a hash reference that may contain one or more of these keys: | 
| 117 |  |  |  |  |  |  |  | 
| 118 |  |  |  |  |  |  | =over | 
| 119 |  |  |  |  |  |  |  | 
| 120 |  |  |  |  |  |  | =item start | 
| 121 |  |  |  |  |  |  |  | 
| 122 |  |  |  |  |  |  | Causes the C method to be called on the machine before returning it. | 
| 123 |  |  |  |  |  |  |  | 
| 124 |  |  |  |  |  |  | =item done | 
| 125 |  |  |  |  |  |  |  | 
| 126 |  |  |  |  |  |  | A value to which to set the C attribute. | 
| 127 |  |  |  |  |  |  |  | 
| 128 |  |  |  |  |  |  | =item strict | 
| 129 |  |  |  |  |  |  |  | 
| 130 |  |  |  |  |  |  | A value to which to set the C attribute. | 
| 131 |  |  |  |  |  |  |  | 
| 132 |  |  |  |  |  |  | =item state_class | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | The name of the class to use for state objects. Defaults to "FSA::State". Use | 
| 135 |  |  |  |  |  |  | this parameter if you want to use a subclass of FSA::State. | 
| 136 |  |  |  |  |  |  |  | 
| 137 |  |  |  |  |  |  | =item state_params | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | A hash reference of parameters to pass as a list to the C | 
| 140 |  |  |  |  |  |  | constructor. | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | =back | 
| 143 |  |  |  |  |  |  |  | 
| 144 |  |  |  |  |  |  | All other parameters define the state table, where each key is the name of a | 
| 145 |  |  |  |  |  |  | state and the following hash reference defines the state, its actions, and its | 
| 146 |  |  |  |  |  |  | switch rules. These state specifications will be converted to FSA::State | 
| 147 |  |  |  |  |  |  | objects available via the C method. The first state parameter is | 
| 148 |  |  |  |  |  |  | considered to be the start state; call the C method to automatically | 
| 149 |  |  |  |  |  |  | enter that state. | 
| 150 |  |  |  |  |  |  |  | 
| 151 |  |  |  |  |  |  | The supported keys in the state definition hash references are: | 
| 152 |  |  |  |  |  |  |  | 
| 153 |  |  |  |  |  |  | =over | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | =item label | 
| 156 |  |  |  |  |  |  |  | 
| 157 |  |  |  |  |  |  | label => 'Do we have a username?', | 
| 158 |  |  |  |  |  |  | label => 'Create a new user', | 
| 159 |  |  |  |  |  |  |  | 
| 160 |  |  |  |  |  |  | A label for the state. It might be the question that is being asked within the | 
| 161 |  |  |  |  |  |  | state (think decision tree), the answer to which determines which rule will | 
| 162 |  |  |  |  |  |  | trigger the switch to the next state. Or it might merely describe what's | 
| 163 |  |  |  |  |  |  | happening in the state. | 
| 164 |  |  |  |  |  |  |  | 
| 165 |  |  |  |  |  |  | =item on_enter | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | on_enter => sub { ... } | 
| 168 |  |  |  |  |  |  | on_enter => [ sub {... }, sub { ... } ] | 
| 169 |  |  |  |  |  |  |  | 
| 170 |  |  |  |  |  |  | Optional. A code reference or array reference of code references. These will | 
| 171 |  |  |  |  |  |  | be executed when entering the state, after any switch actions defined by the | 
| 172 |  |  |  |  |  |  | C of the previous state. The FSA::State for which the C | 
| 173 |  |  |  |  |  |  | actions are defined will be passed to each code reference as the sole | 
| 174 |  |  |  |  |  |  | argument. | 
| 175 |  |  |  |  |  |  |  | 
| 176 |  |  |  |  |  |  | =item do | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | do => sub { ... } | 
| 179 |  |  |  |  |  |  | do => [ sub {... }, sub { ... } ] | 
| 180 |  |  |  |  |  |  |  | 
| 181 |  |  |  |  |  |  | Optional. A code reference or array reference of code references. These are | 
| 182 |  |  |  |  |  |  | the actions to be taken while in the state, and will execute after any | 
| 183 |  |  |  |  |  |  | C actions. The FSA::State object for which the C actions are | 
| 184 |  |  |  |  |  |  | defined will be passed to each code reference as the sole argument. | 
| 185 |  |  |  |  |  |  |  | 
| 186 |  |  |  |  |  |  | =item on_exit | 
| 187 |  |  |  |  |  |  |  | 
| 188 |  |  |  |  |  |  | on_exit => sub { ... } | 
| 189 |  |  |  |  |  |  | on_exit => [ sub {... }, sub { ... } ] | 
| 190 |  |  |  |  |  |  |  | 
| 191 |  |  |  |  |  |  | Optional. A code reference or array reference of code references. These will | 
| 192 |  |  |  |  |  |  | be executed when exiting the state, before any switch actions (defined by | 
| 193 |  |  |  |  |  |  | C). The FSA::State object for which the C actions are defined | 
| 194 |  |  |  |  |  |  | will be passed to each code reference as the sole argument. | 
| 195 |  |  |  |  |  |  |  | 
| 196 |  |  |  |  |  |  | =item rules | 
| 197 |  |  |  |  |  |  |  | 
| 198 |  |  |  |  |  |  | Optional. The rules for switching from the state to other states. This is an | 
| 199 |  |  |  |  |  |  | array reference but shaped like a hash. The keys are the names of the states | 
| 200 |  |  |  |  |  |  | to consider moving to, while the values are the rules for switching to that | 
| 201 |  |  |  |  |  |  | state. The rules will be executed in the order specified in the array | 
| 202 |  |  |  |  |  |  | reference, and I unless the C attribute has | 
| 203 |  |  |  |  |  |  | been set to a true value. So for the sake of efficiency it's worthwhile to | 
| 204 |  |  |  |  |  |  | specify the switch rules most likely to evaluate to true before those more | 
| 205 |  |  |  |  |  |  | likely to evaluate to false. | 
| 206 |  |  |  |  |  |  |  | 
| 207 |  |  |  |  |  |  | Rules themselves are best specified as hash references with the following | 
| 208 |  |  |  |  |  |  | keys: | 
| 209 |  |  |  |  |  |  |  | 
| 210 |  |  |  |  |  |  | =over | 
| 211 |  |  |  |  |  |  |  | 
| 212 |  |  |  |  |  |  | =item rule | 
| 213 |  |  |  |  |  |  |  | 
| 214 |  |  |  |  |  |  | A code reference or value that will be evaluated to determine whether to | 
| 215 |  |  |  |  |  |  | switch to the specified state. The value must be true or the code reference | 
| 216 |  |  |  |  |  |  | must return a true value to trigger the switch to the new state, and false not | 
| 217 |  |  |  |  |  |  | to switch to the new state. When executed, it will be passed the FSA::State | 
| 218 |  |  |  |  |  |  | object for the state for which the rules were defined, along with any other | 
| 219 |  |  |  |  |  |  | arguments passed to C or C--the methods that execute | 
| 220 |  |  |  |  |  |  | the rule code references. These arguments may be inputs that are specifically | 
| 221 |  |  |  |  |  |  | tested to determine whether to switch states. To be polite, rules should not | 
| 222 |  |  |  |  |  |  | transform the passed values if they're returning false, as other rules may | 
| 223 |  |  |  |  |  |  | need to evaluate them (unless you're building some sort of chaining rules--but | 
| 224 |  |  |  |  |  |  | those aren't really rules, are they?). | 
| 225 |  |  |  |  |  |  |  | 
| 226 |  |  |  |  |  |  | =item message | 
| 227 |  |  |  |  |  |  |  | 
| 228 |  |  |  |  |  |  | An optional message that will be added to the current state when the rule | 
| 229 |  |  |  |  |  |  | specified by the C parameter evaluates to true. The message will also be | 
| 230 |  |  |  |  |  |  | used to label switches in the output of the C method. | 
| 231 |  |  |  |  |  |  |  | 
| 232 |  |  |  |  |  |  | =item action | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | A code reference or an array reference of code references to be executed | 
| 235 |  |  |  |  |  |  | during the switch, after the C actions have been executed in the | 
| 236 |  |  |  |  |  |  | current state, but before the C actions execute in the new state. | 
| 237 |  |  |  |  |  |  | Two arguments will be passed to these code references: the FSA::State object | 
| 238 |  |  |  |  |  |  | for the state for which they were defined, and the FSA::State object for the | 
| 239 |  |  |  |  |  |  | new state (which will not yet be the current state). | 
| 240 |  |  |  |  |  |  |  | 
| 241 |  |  |  |  |  |  | =back | 
| 242 |  |  |  |  |  |  |  | 
| 243 |  |  |  |  |  |  | A couple of examples: | 
| 244 |  |  |  |  |  |  |  | 
| 245 |  |  |  |  |  |  | rules => [ | 
| 246 |  |  |  |  |  |  | foo => { | 
| 247 |  |  |  |  |  |  | rule => 1 | 
| 248 |  |  |  |  |  |  | }, | 
| 249 |  |  |  |  |  |  | bar => { | 
| 250 |  |  |  |  |  |  | rule    => \&goto_bar, | 
| 251 |  |  |  |  |  |  | message => 'Have we got a bar?', | 
| 252 |  |  |  |  |  |  | }, | 
| 253 |  |  |  |  |  |  | yow => { | 
| 254 |  |  |  |  |  |  | rule    => \&goto_yow, | 
| 255 |  |  |  |  |  |  | message => 'Yow!', | 
| 256 |  |  |  |  |  |  | action  => [ \&action_one, \&action_two], | 
| 257 |  |  |  |  |  |  | } | 
| 258 |  |  |  |  |  |  | ] | 
| 259 |  |  |  |  |  |  |  | 
| 260 |  |  |  |  |  |  | A rule may also simply be a code reference or value that will be evaluated | 
| 261 |  |  |  |  |  |  | when FSA::Rules is determining whether to switch to the new state. You might want | 
| 262 |  |  |  |  |  |  | just specify a value or code reference if you don't need a message label or | 
| 263 |  |  |  |  |  |  | switch actions to be executed. For example, this C specification: | 
| 264 |  |  |  |  |  |  |  | 
| 265 |  |  |  |  |  |  | rules => [ | 
| 266 |  |  |  |  |  |  | foo => 1 | 
| 267 |  |  |  |  |  |  | ] | 
| 268 |  |  |  |  |  |  |  | 
| 269 |  |  |  |  |  |  | Is equivalent to this C specification: | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | rules => [ | 
| 272 |  |  |  |  |  |  | foo => { rule => 1 } | 
| 273 |  |  |  |  |  |  | ] | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  | And finally, you can specify a rule as an array reference. In this case, the | 
| 276 |  |  |  |  |  |  | first item in the array will be evaluated to determine whether to switch to | 
| 277 |  |  |  |  |  |  | the new state, and any other items must be code references that will be | 
| 278 |  |  |  |  |  |  | executed during the switch. For example, this C specification: | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | rules => [ | 
| 281 |  |  |  |  |  |  | yow => [ \&check_yow, \&action_one, \&action_two ] | 
| 282 |  |  |  |  |  |  | ] | 
| 283 |  |  |  |  |  |  |  | 
| 284 |  |  |  |  |  |  | Is equivalent to this C specification: | 
| 285 |  |  |  |  |  |  |  | 
| 286 |  |  |  |  |  |  | rules => [ | 
| 287 |  |  |  |  |  |  | yow => { | 
| 288 |  |  |  |  |  |  | rule   =>  \&check_yow, | 
| 289 |  |  |  |  |  |  | action => [ \&action_one, \&action_two ], | 
| 290 |  |  |  |  |  |  | } | 
| 291 |  |  |  |  |  |  | ] | 
| 292 |  |  |  |  |  |  |  | 
| 293 |  |  |  |  |  |  | =back | 
| 294 |  |  |  |  |  |  |  | 
| 295 |  |  |  |  |  |  | =cut | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | my (%machines, %states); | 
| 298 |  |  |  |  |  |  |  | 
| 299 |  |  |  |  |  |  | sub new { | 
| 300 | 35 |  |  | 35 | 1 | 4687 | my $class = shift; | 
| 301 | 35 |  |  |  |  | 95 | my $self = bless {}, $class; | 
| 302 | 35 | 100 |  |  |  | 98 | my $params = ref $_[0] ? shift : {}; | 
| 303 | 35 |  |  |  |  | 223 | my $fsa = $machines{$self} = { | 
| 304 |  |  |  |  |  |  | done   => undef, | 
| 305 |  |  |  |  |  |  | notes  => {}, | 
| 306 |  |  |  |  |  |  | stack  => [], | 
| 307 |  |  |  |  |  |  | table  => {}, | 
| 308 |  |  |  |  |  |  | self   => $self, | 
| 309 |  |  |  |  |  |  | }; | 
| 310 |  |  |  |  |  |  |  | 
| 311 |  |  |  |  |  |  | # Weaken the circular reference. | 
| 312 | 35 |  |  |  |  | 129 | Scalar::Util::weaken $fsa->{self}; | 
| 313 |  |  |  |  |  |  |  | 
| 314 | 35 |  | 100 |  |  | 167 | $params->{state_class}  ||= 'FSA::State'; | 
| 315 | 35 |  | 100 |  |  | 130 | $params->{state_params} ||= {}; | 
| 316 | 35 |  |  |  |  | 90 | while (@_) { | 
| 317 | 60 |  |  |  |  | 85 | my $state = shift; | 
| 318 | 60 |  |  |  |  | 54 | my $def   = shift; | 
| 319 | 60 | 100 |  |  |  | 141 | $self->_croak(qq{The state "$state" already exists}) | 
| 320 |  |  |  |  |  |  | if exists $fsa->{table}{$state}; | 
| 321 |  |  |  |  |  |  |  | 
| 322 |  |  |  |  |  |  | # Setup enter, exit, and do actions. | 
| 323 | 59 |  |  |  |  | 99 | for (qw(on_enter do on_exit)) { | 
| 324 | 177 | 100 |  |  |  | 319 | if (my $ref = ref $def->{$_}) { | 
| 325 | 59 | 100 |  |  |  | 177 | $def->{$_} = [$def->{$_}] if $ref eq 'CODE'; | 
| 326 |  |  |  |  |  |  | } else { | 
| 327 | 118 |  |  |  |  | 256 | $def->{$_} = []; | 
| 328 |  |  |  |  |  |  | } | 
| 329 |  |  |  |  |  |  | } | 
| 330 |  |  |  |  |  |  |  | 
| 331 |  |  |  |  |  |  | # Create the state object and cache the state data. | 
| 332 | 59 |  |  |  |  | 93 | my $obj = $params->{state_class}->new(%{$params->{state_params}}); | 
|  | 59 |  |  |  |  | 188 |  | 
| 333 | 59 |  |  |  |  | 96 | $def->{name} = $state; | 
| 334 | 59 |  |  |  |  | 73 | $def->{machine} = $self; | 
| 335 | 59 |  |  |  |  | 87 | $fsa->{table}{$state} = $obj; | 
| 336 | 59 |  |  |  |  | 66 | push @{$fsa->{ord}}, $obj; | 
|  | 59 |  |  |  |  | 101 |  | 
| 337 | 59 |  |  |  |  | 156 | $states{$obj} = $def; | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | # Weaken the circular reference. | 
| 340 | 59 |  |  |  |  | 206 | Scalar::Util::weaken $def->{machine}; | 
| 341 |  |  |  |  |  |  | } | 
| 342 |  |  |  |  |  |  |  | 
| 343 |  |  |  |  |  |  | # Setup rules. We process the table a second time to catch invalid | 
| 344 |  |  |  |  |  |  | # references. | 
| 345 | 34 |  |  |  |  | 46 | while (my ($key, $obj) = each %{$fsa->{table}}) { | 
|  | 91 |  |  |  |  | 272 |  | 
| 346 | 58 |  |  |  |  | 92 | my $def = $states{$obj}; | 
| 347 | 58 | 100 |  |  |  | 109 | if (my $rule_spec = $def->{rules}) { | 
| 348 | 32 |  |  |  |  | 31 | my @rules; | 
| 349 | 32 |  |  |  |  | 74 | while (@$rule_spec) { | 
| 350 | 44 |  |  |  |  | 53 | my $state = shift @$rule_spec; | 
| 351 | 44 | 100 |  |  |  | 130 | $self->_croak( | 
| 352 |  |  |  |  |  |  | qq{Unknown state "$state" referenced by state "$key"} | 
| 353 |  |  |  |  |  |  | ) unless $fsa->{table}{$state}; | 
| 354 |  |  |  |  |  |  |  | 
| 355 | 43 |  |  |  |  | 49 | my $rules = shift @$rule_spec; | 
| 356 | 43 | 100 |  |  |  | 83 | my $exec = ref $rules eq 'ARRAY' ? $rules : [$rules]; | 
| 357 | 43 |  |  |  |  | 39 | my $rule = shift @$exec; | 
| 358 | 43 |  |  |  |  | 39 | my $message; | 
| 359 | 43 | 100 |  |  |  | 80 | if (ref $rule eq 'HASH') { | 
| 360 | 5 | 50 |  |  |  | 10 | $self->_croak( | 
| 361 |  |  |  |  |  |  | qq{In rule "$state", state "$key": you must supply a rule.} | 
| 362 |  |  |  |  |  |  | ) unless exists $rule->{rule}; | 
| 363 | 5 | 100 |  |  |  | 15 | $exec = ref $rule->{action} eq 'ARRAY' | 
|  |  | 100 |  |  |  |  |  | 
| 364 |  |  |  |  |  |  | ? $rule->{action} | 
| 365 |  |  |  |  |  |  | : [$rule->{action}] | 
| 366 |  |  |  |  |  |  | if exists $rule->{action}; | 
| 367 | 5 | 100 |  |  |  | 13 | $message = $rule->{message} if exists $rule->{message}; | 
| 368 | 5 |  |  |  |  | 7 | $rule    = $rule->{rule}; | 
| 369 |  |  |  |  |  |  | } | 
| 370 |  |  |  |  |  |  | # Used to convert a raw value to a code reference here, but as | 
| 371 |  |  |  |  |  |  | # it ended up as a closure and these don't serialize very | 
| 372 |  |  |  |  |  |  | # well, I pulled it out. Now try_switch has to check to see if | 
| 373 |  |  |  |  |  |  | # a rule is a literal value each time it's called. This | 
| 374 |  |  |  |  |  |  | # actually makes it faster for literal values, but a little | 
| 375 |  |  |  |  |  |  | # slower for code references. | 
| 376 |  |  |  |  |  |  |  | 
| 377 | 43 |  |  |  |  | 139 | push @rules, { | 
| 378 |  |  |  |  |  |  | state   => $fsa->{table}{$state}, | 
| 379 |  |  |  |  |  |  | rule    => $rule, | 
| 380 |  |  |  |  |  |  | exec    => $exec, | 
| 381 |  |  |  |  |  |  | message => $message, | 
| 382 |  |  |  |  |  |  | }; | 
| 383 |  |  |  |  |  |  |  | 
| 384 |  |  |  |  |  |  | # Weaken the circular reference. | 
| 385 | 43 |  |  |  |  | 140 | Scalar::Util::weaken $rules[-1]->{state}; | 
| 386 |  |  |  |  |  |  | } | 
| 387 | 31 |  |  |  |  | 85 | $def->{rules} = \@rules; | 
| 388 |  |  |  |  |  |  | } else { | 
| 389 | 26 |  |  |  |  | 63 | $def->{rules} = []; | 
| 390 |  |  |  |  |  |  | } | 
| 391 |  |  |  |  |  |  | } | 
| 392 |  |  |  |  |  |  |  | 
| 393 |  |  |  |  |  |  | # Handle any parameters. | 
| 394 | 33 | 100 |  |  |  | 97 | $self->start if $params->{start}; | 
| 395 | 33 | 100 |  |  |  | 71 | $self->done($params->{done}) if exists $params->{done}; | 
| 396 | 33 | 100 |  |  |  | 71 | $self->strict($params->{strict}) if exists $params->{strict}; | 
| 397 | 33 |  |  |  |  | 154 | return $self; | 
| 398 |  |  |  |  |  |  | } | 
| 399 |  |  |  |  |  |  |  | 
| 400 |  |  |  |  |  |  | ############################################################################## | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | =head1 Instance Interface | 
| 403 |  |  |  |  |  |  |  | 
| 404 |  |  |  |  |  |  | =head2 Instance Methods | 
| 405 |  |  |  |  |  |  |  | 
| 406 |  |  |  |  |  |  | =head3 start | 
| 407 |  |  |  |  |  |  |  | 
| 408 |  |  |  |  |  |  | my $state = $fsa->start; | 
| 409 |  |  |  |  |  |  |  | 
| 410 |  |  |  |  |  |  | Starts the state machine by setting the state to the first state defined in | 
| 411 |  |  |  |  |  |  | the call to C. If the machine is already in a state, an exception will | 
| 412 |  |  |  |  |  |  | be thrown. Returns the start state FSA::State object. | 
| 413 |  |  |  |  |  |  |  | 
| 414 |  |  |  |  |  |  | =cut | 
| 415 |  |  |  |  |  |  |  | 
| 416 |  |  |  |  |  |  | sub start { | 
| 417 | 21 |  |  | 21 | 1 | 40 | my $self = shift; | 
| 418 | 21 |  |  |  |  | 40 | my $fsa = $machines{$self}; | 
| 419 | 21 | 100 |  |  |  | 55 | $self->_croak( | 
| 420 |  |  |  |  |  |  | 'Cannot start machine because it is already running' | 
| 421 |  |  |  |  |  |  | ) if $fsa->{current}; | 
| 422 | 20 | 50 |  |  |  | 60 | my $state = $fsa->{ord}[0] or return $self; | 
| 423 | 20 |  |  |  |  | 34 | $self->curr_state($state); | 
| 424 | 20 |  |  |  |  | 50 | return $state; | 
| 425 |  |  |  |  |  |  | } | 
| 426 |  |  |  |  |  |  |  | 
| 427 |  |  |  |  |  |  | ############################################################################## | 
| 428 |  |  |  |  |  |  |  | 
| 429 |  |  |  |  |  |  | =head3 at | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | $fsa->switch until $fsa->at('game_over'); | 
| 432 |  |  |  |  |  |  |  | 
| 433 |  |  |  |  |  |  | Requires a state name. Returns false if the current machine state does not | 
| 434 |  |  |  |  |  |  | match the name. Otherwise, it returns the state. | 
| 435 |  |  |  |  |  |  |  | 
| 436 |  |  |  |  |  |  | =cut | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | sub at { | 
| 439 | 47 |  |  | 47 | 1 | 773 | my ($self, $name) = @_; | 
| 440 | 47 | 100 |  |  |  | 74 | $self->_croak("You must supply a state name") unless defined $name; | 
| 441 | 46 |  |  |  |  | 60 | my $fsa = $machines{$self}; | 
| 442 | 46 | 100 |  |  |  | 88 | $self->_croak(qq{No such state "$name"}) | 
| 443 |  |  |  |  |  |  | unless exists $fsa->{table}{$name}; | 
| 444 | 45 | 50 |  |  |  | 55 | my $state = $self->curr_state or return; | 
| 445 | 45 | 100 |  |  |  | 57 | return unless $state->name eq $name; | 
| 446 | 3 |  |  |  |  | 6 | return $state; | 
| 447 |  |  |  |  |  |  | } | 
| 448 |  |  |  |  |  |  |  | 
| 449 |  |  |  |  |  |  | ############################################################################## | 
| 450 |  |  |  |  |  |  |  | 
| 451 |  |  |  |  |  |  | =head3 curr_state | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | my $curr_state = $fsa->curr_state; | 
| 454 |  |  |  |  |  |  | $fsa->curr_state($curr_state); | 
| 455 |  |  |  |  |  |  |  | 
| 456 |  |  |  |  |  |  | Get or set the current FSA::State object. Pass a state name or object to set | 
| 457 |  |  |  |  |  |  | the state. Setting a new state will cause the C actions of the | 
| 458 |  |  |  |  |  |  | current state to be executed, if there is a current state, and then execute | 
| 459 |  |  |  |  |  |  | the C and C actions of the new state. Returns the new FSA::State | 
| 460 |  |  |  |  |  |  | object when setting the current state. | 
| 461 |  |  |  |  |  |  |  | 
| 462 |  |  |  |  |  |  | =cut | 
| 463 |  |  |  |  |  |  |  | 
| 464 |  |  |  |  |  |  | sub curr_state { | 
| 465 | 273 |  |  | 273 | 1 | 296 | my $self = shift; | 
| 466 | 273 |  |  |  |  | 795 | my $fsa = $machines{$self}; | 
| 467 | 273 |  |  |  |  | 307 | my $curr = $fsa->{current}; | 
| 468 | 273 | 100 |  |  |  | 778 | return $curr unless @_; | 
| 469 |  |  |  |  |  |  |  | 
| 470 | 142 |  |  |  |  | 120 | my $state = shift; | 
| 471 | 142 | 100 |  |  |  | 268 | unless (ref $state) { | 
| 472 | 15 |  |  |  |  | 14 | my $name = $state; | 
| 473 | 15 | 100 |  |  |  | 42 | $state = $fsa->{table}{$name} | 
| 474 |  |  |  |  |  |  | or $self->_croak(qq{No such state "$name"}); | 
| 475 |  |  |  |  |  |  | } | 
| 476 |  |  |  |  |  |  |  | 
| 477 |  |  |  |  |  |  | # Exit the current state. | 
| 478 | 141 | 100 |  |  |  | 270 | $curr->exit if $curr; | 
| 479 |  |  |  |  |  |  | # Run any switch actions. | 
| 480 | 141 | 100 |  |  |  | 322 | if (my $exec = delete $fsa->{exec}) { | 
| 481 | 107 |  |  |  |  | 264 | $_->($curr, $state) for @$exec; | 
| 482 |  |  |  |  |  |  | } | 
| 483 |  |  |  |  |  |  |  | 
| 484 |  |  |  |  |  |  | # Push the new state onto the stack and cache the index. | 
| 485 | 141 |  |  |  |  | 141 | push @{$fsa->{stack}} | 
|  | 141 |  |  |  |  | 262 |  | 
| 486 |  |  |  |  |  |  | => [$state->name => { result => undef, message => undef}]; | 
| 487 | 141 |  |  |  |  | 142 | push @{$states{$state}->{index}}, $#{$fsa->{stack}}; | 
|  | 141 |  |  |  |  | 232 |  | 
|  | 141 |  |  |  |  | 208 |  | 
| 488 |  |  |  |  |  |  |  | 
| 489 |  |  |  |  |  |  | # Set the new state. | 
| 490 | 141 |  |  |  |  | 163 | $fsa->{current} = $state; | 
| 491 | 141 |  |  |  |  | 188 | $state->enter; | 
| 492 | 141 |  |  |  |  | 180 | $state->do; | 
| 493 | 140 |  |  |  |  | 212 | return $state; | 
| 494 |  |  |  |  |  |  | } | 
| 495 |  |  |  |  |  |  |  | 
| 496 |  |  |  |  |  |  | ############################################################################## | 
| 497 |  |  |  |  |  |  |  | 
| 498 |  |  |  |  |  |  | =head3 state | 
| 499 |  |  |  |  |  |  |  | 
| 500 |  |  |  |  |  |  | Deprecated alias for C. This method will issue a warning and | 
| 501 |  |  |  |  |  |  | will be removed in a future version of FSA::Rules. Use C, | 
| 502 |  |  |  |  |  |  | instead. | 
| 503 |  |  |  |  |  |  |  | 
| 504 |  |  |  |  |  |  | =cut | 
| 505 |  |  |  |  |  |  |  | 
| 506 |  |  |  |  |  |  | sub state { | 
| 507 | 0 |  |  | 0 | 1 | 0 | require Carp; | 
| 508 | 0 |  |  |  |  | 0 | Carp::carp( | 
| 509 |  |  |  |  |  |  | 'The state() method has been deprecated. Use curr_state() instead' | 
| 510 |  |  |  |  |  |  | ); | 
| 511 | 0 |  |  |  |  | 0 | shift->curr_state(@_); | 
| 512 |  |  |  |  |  |  | } | 
| 513 |  |  |  |  |  |  |  | 
| 514 |  |  |  |  |  |  | ############################################################################## | 
| 515 |  |  |  |  |  |  |  | 
| 516 |  |  |  |  |  |  | =head3 prev_state | 
| 517 |  |  |  |  |  |  |  | 
| 518 |  |  |  |  |  |  | my $prev_state = $fsa->prev_state; | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | Returns the FSA::State object representing the previous state. This is useful | 
| 521 |  |  |  |  |  |  | in states where you need to know what state you came from, and can be very | 
| 522 |  |  |  |  |  |  | useful in "fail" states. | 
| 523 |  |  |  |  |  |  |  | 
| 524 |  |  |  |  |  |  | =cut | 
| 525 |  |  |  |  |  |  |  | 
| 526 |  |  |  |  |  |  | sub prev_state { | 
| 527 | 5 |  |  | 5 | 1 | 7 | my $self = shift; | 
| 528 | 5 |  |  |  |  | 12 | my $stacktrace = $self->raw_stacktrace; | 
| 529 | 5 | 50 |  |  |  | 18 | return unless @$stacktrace > 1; | 
| 530 | 5 |  |  |  |  | 34 | return $machines{$self}->{table}{$stacktrace->[-2][0]}; | 
| 531 |  |  |  |  |  |  | } | 
| 532 |  |  |  |  |  |  |  | 
| 533 |  |  |  |  |  |  | ############################################################################## | 
| 534 |  |  |  |  |  |  |  | 
| 535 |  |  |  |  |  |  | =head3 states | 
| 536 |  |  |  |  |  |  |  | 
| 537 |  |  |  |  |  |  | my @states = $fsa->states; | 
| 538 |  |  |  |  |  |  | my $states = $fsa->states; | 
| 539 |  |  |  |  |  |  | my $state  = $fsa->states($state_name); | 
| 540 |  |  |  |  |  |  | @states    = $fsa->states(@state_names); | 
| 541 |  |  |  |  |  |  | $states    = $fsa->states(@state_names); | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  | Called with no arguments, this method returns a list or array reference of all | 
| 544 |  |  |  |  |  |  | of the FSA::State objects that represent the states defined in the state | 
| 545 |  |  |  |  |  |  | machine. When called with a single state name, it returns the FSA::State object | 
| 546 |  |  |  |  |  |  | object for that state. When called with more than one state name arguments, | 
| 547 |  |  |  |  |  |  | it returns a list or array reference of those states. | 
| 548 |  |  |  |  |  |  |  | 
| 549 |  |  |  |  |  |  | If called with any state names that did not exist in the original definition of | 
| 550 |  |  |  |  |  |  | the state machine, this method will C. | 
| 551 |  |  |  |  |  |  |  | 
| 552 |  |  |  |  |  |  | =cut | 
| 553 |  |  |  |  |  |  |  | 
| 554 |  |  |  |  |  |  | sub states { | 
| 555 | 16 |  |  | 16 | 1 | 335 | my $self = shift; | 
| 556 | 16 |  |  |  |  | 33 | my $fsa = $machines{$self}; | 
| 557 | 16 | 50 |  |  |  | 45 | return wantarray ? @{$fsa->{ord}} : $fsa->{ord} unless @_; | 
|  | 4 | 100 |  |  |  | 18 |  | 
| 558 |  |  |  |  |  |  |  | 
| 559 | 12 | 100 |  |  |  | 25 | if (my @errors = grep { not exists $fsa->{table}{$_} } @_) { | 
|  | 13 |  |  |  |  | 66 |  | 
| 560 | 1 |  |  |  |  | 5 | $self->_croak("No such state(s) '@errors'"); | 
| 561 |  |  |  |  |  |  | } | 
| 562 |  |  |  |  |  |  |  | 
| 563 | 11 | 100 |  |  |  | 63 | return $fsa->{table}{+shift} unless @_ > 1; | 
| 564 | 1 | 50 |  |  |  | 5 | return wantarray ? @{$fsa->{table}}{@_} : [ @{$fsa->{table}}{@_} ]; | 
|  | 1 |  |  |  |  | 6 |  | 
|  | 0 |  |  |  |  | 0 |  | 
| 565 |  |  |  |  |  |  |  | 
| 566 |  |  |  |  |  |  | } | 
| 567 |  |  |  |  |  |  |  | 
| 568 |  |  |  |  |  |  | ############################################################################## | 
| 569 |  |  |  |  |  |  |  | 
| 570 |  |  |  |  |  |  | =head3 try_switch | 
| 571 |  |  |  |  |  |  |  | 
| 572 |  |  |  |  |  |  | my $state = $fsa->try_switch; | 
| 573 |  |  |  |  |  |  | $state = $fsa->try_switch(@inputs); | 
| 574 |  |  |  |  |  |  |  | 
| 575 |  |  |  |  |  |  | Checks the switch rules of the current state and switches to the first new | 
| 576 |  |  |  |  |  |  | state for which a rule returns a true value. The evaluation of switch rules | 
| 577 |  |  |  |  |  |  | short-circuits to switch to the first state for which a rule evaluates to a | 
| 578 |  |  |  |  |  |  | true value unless the C attribute is set to a true value. | 
| 579 |  |  |  |  |  |  |  | 
| 580 |  |  |  |  |  |  | If  is set to a true value, I rules will be evaluated, and if | 
| 581 |  |  |  |  |  |  | more than one returns a true statement, an exception will be thrown. This | 
| 582 |  |  |  |  |  |  | approach guarantees that every attempt to switch from one state to another | 
| 583 |  |  |  |  |  |  | will have one and only one possible destination state to which to switch, thus | 
| 584 |  |  |  |  |  |  | satisfying the DFA pattern. | 
| 585 |  |  |  |  |  |  |  | 
| 586 |  |  |  |  |  |  | All arguments passed to C will be passed to the switch rule code | 
| 587 |  |  |  |  |  |  | references as inputs. If a switch rule evaluates to true and there are | 
| 588 |  |  |  |  |  |  | additional switch actions for that rule, these actions will be executed after | 
| 589 |  |  |  |  |  |  | the C actions of the current state (if there is one) but before the | 
| 590 |  |  |  |  |  |  | C actions of the new state. They will be passed the current state | 
| 591 |  |  |  |  |  |  | object and the new state object as arguments. | 
| 592 |  |  |  |  |  |  |  | 
| 593 |  |  |  |  |  |  | Returns the FSA::State object representing the state to which it switched and | 
| 594 |  |  |  |  |  |  | C if it cannot switch to another state. | 
| 595 |  |  |  |  |  |  |  | 
| 596 |  |  |  |  |  |  | =cut | 
| 597 |  |  |  |  |  |  |  | 
| 598 |  |  |  |  |  |  | sub try_switch { | 
| 599 | 109 |  |  | 109 | 1 | 98 | my $self = shift; | 
| 600 | 109 |  |  |  |  | 145 | my $fsa = $machines{$self}; | 
| 601 | 109 |  |  |  |  | 132 | my $state = $fsa->{current}; | 
| 602 |  |  |  |  |  |  | # XXX Factor this out to the state class to evaluate the rules? | 
| 603 | 109 |  |  |  |  | 179 | my @rules = $state->_rules; | 
| 604 | 109 |  |  |  |  | 96 | my $next; | 
| 605 | 109 |  |  |  |  | 224 | while (my $rule = shift @rules) { | 
| 606 | 134 |  |  |  |  | 177 | my $code = $rule->{rule}; | 
| 607 | 134 | 100 |  |  |  | 323 | next unless ref $code eq 'CODE' ? $code->($state, @_) : $code; | 
|  |  | 100 |  |  |  |  |  | 
| 608 |  |  |  |  |  |  |  | 
| 609 |  |  |  |  |  |  | # Make sure that no other rules evaluate to true in strict mode. | 
| 610 | 108 | 100 | 100 |  |  | 307 | if (@rules && $self->strict) { | 
| 611 | 4 | 100 |  |  |  | 9 | if ( my @new = grep { | 
|  | 4 |  |  |  |  | 6 |  | 
| 612 |  |  |  |  |  |  | my $c = $_->{rule}; | 
| 613 | 4 | 100 |  |  |  | 23 | ref $c eq 'CODE' ? $c->( $state, @_ ) : $c | 
| 614 |  |  |  |  |  |  | } @rules ) { | 
| 615 | 2 |  |  |  |  | 5 | $self->_croak( | 
| 616 |  |  |  |  |  |  | 'Attempt to switch from state "', $state->name, '"', | 
| 617 |  |  |  |  |  |  | ' improperly found multiple destination states: "', | 
| 618 | 1 |  |  |  |  | 4 | join('", "', map { $_->{state}->name } $rule, @new), '"' | 
| 619 |  |  |  |  |  |  | ); | 
| 620 |  |  |  |  |  |  | } | 
| 621 |  |  |  |  |  |  | } | 
| 622 |  |  |  |  |  |  |  | 
| 623 |  |  |  |  |  |  | # We're good to go. | 
| 624 | 107 |  |  |  |  | 175 | $fsa->{exec} = $rule->{exec}; | 
| 625 | 107 | 100 |  |  |  | 217 | $state->message($rule->{message}) if defined $rule->{message}; | 
| 626 | 107 |  |  |  |  | 173 | $next = $self->curr_state($rule->{state}); | 
| 627 | 106 |  |  |  |  | 125 | last; | 
| 628 |  |  |  |  |  |  | } | 
| 629 | 107 |  |  |  |  | 169 | return $next; | 
| 630 |  |  |  |  |  |  | } | 
| 631 |  |  |  |  |  |  |  | 
| 632 |  |  |  |  |  |  | ############################################################################## | 
| 633 |  |  |  |  |  |  |  | 
| 634 |  |  |  |  |  |  | =head3 switch | 
| 635 |  |  |  |  |  |  |  | 
| 636 |  |  |  |  |  |  | my $state = eval { $fsa->switch(@inputs) }; | 
| 637 |  |  |  |  |  |  | print "No can do" if $@; | 
| 638 |  |  |  |  |  |  |  | 
| 639 |  |  |  |  |  |  | The fatal form of C. This method attempts to switch states and | 
| 640 |  |  |  |  |  |  | returns the FSA::State object on success and throws an exception on failure. | 
| 641 |  |  |  |  |  |  |  | 
| 642 |  |  |  |  |  |  | =cut | 
| 643 |  |  |  |  |  |  |  | 
| 644 |  |  |  |  |  |  | sub switch { | 
| 645 | 102 |  |  | 102 | 1 | 252 | my $self = shift; | 
| 646 | 102 |  |  |  |  | 155 | my $ret = $self->try_switch(@_); | 
| 647 | 101 | 100 |  |  |  | 338 | return $ret if defined $ret; | 
| 648 | 1 |  |  |  |  | 5 | $self->_croak( | 
| 649 |  |  |  |  |  |  | 'Cannot determine transition from state "', | 
| 650 |  |  |  |  |  |  | $machines{$self}->{current}->name, '"' | 
| 651 |  |  |  |  |  |  | ); | 
| 652 |  |  |  |  |  |  | } | 
| 653 |  |  |  |  |  |  |  | 
| 654 |  |  |  |  |  |  | ############################################################################## | 
| 655 |  |  |  |  |  |  |  | 
| 656 |  |  |  |  |  |  | =head3 done | 
| 657 |  |  |  |  |  |  |  | 
| 658 |  |  |  |  |  |  | my $done = $fsa->done; | 
| 659 |  |  |  |  |  |  | $fsa->done($done); | 
| 660 |  |  |  |  |  |  | $fsa->done( sub {...} ); | 
| 661 |  |  |  |  |  |  |  | 
| 662 |  |  |  |  |  |  | Get or set a value to indicate whether the engine is done running. Or set it | 
| 663 |  |  |  |  |  |  | to a code reference to have that code reference called each time C is | 
| 664 |  |  |  |  |  |  | called without arguments and have I return value returned. A code | 
| 665 |  |  |  |  |  |  | reference should expect the FSA::Rules object passed in as its only argument. | 
| 666 |  |  |  |  |  |  | Note that this varies from the pattern for state actions, which should expect | 
| 667 |  |  |  |  |  |  | the relevant FSA::State object to be passed as the argument. Call the | 
| 668 |  |  |  |  |  |  | C method on the FSA::Rules object if you want the current state | 
| 669 |  |  |  |  |  |  | in your C code reference. | 
| 670 |  |  |  |  |  |  |  | 
| 671 |  |  |  |  |  |  | This method can be useful for checking to see if your state engine is done | 
| 672 |  |  |  |  |  |  | running, and calling C when it isn't. States can set it to a true | 
| 673 |  |  |  |  |  |  | value when they consider processing complete, or you can use a code reference | 
| 674 |  |  |  |  |  |  | that determines whether the machine is done. Something like this: | 
| 675 |  |  |  |  |  |  |  | 
| 676 |  |  |  |  |  |  | my $fsa = FSA::Rules->new( | 
| 677 |  |  |  |  |  |  | foo => { | 
| 678 |  |  |  |  |  |  | do    => { $_[0]->machine->done(1) if ++$_[0]->{count} >= 5 }, | 
| 679 |  |  |  |  |  |  | rules => [ foo => 1 ], | 
| 680 |  |  |  |  |  |  | } | 
| 681 |  |  |  |  |  |  | ); | 
| 682 |  |  |  |  |  |  |  | 
| 683 |  |  |  |  |  |  | Or this: | 
| 684 |  |  |  |  |  |  |  | 
| 685 |  |  |  |  |  |  | my $fsa = FSA::Rules->new( | 
| 686 |  |  |  |  |  |  | foo => { | 
| 687 |  |  |  |  |  |  | do    => { ++shift->machine->{count} }, | 
| 688 |  |  |  |  |  |  | rules => [ foo => 1 ], | 
| 689 |  |  |  |  |  |  | } | 
| 690 |  |  |  |  |  |  | ); | 
| 691 |  |  |  |  |  |  | $fsa->done( sub { shift->{count} >= 5 }); | 
| 692 |  |  |  |  |  |  |  | 
| 693 |  |  |  |  |  |  | Then you can just run the state engine, checking C to find out when | 
| 694 |  |  |  |  |  |  | it's, uh, done. | 
| 695 |  |  |  |  |  |  |  | 
| 696 |  |  |  |  |  |  | $fsa->start; | 
| 697 |  |  |  |  |  |  | $fsa->switch until $fsa->done; | 
| 698 |  |  |  |  |  |  |  | 
| 699 |  |  |  |  |  |  | Although you could just use the C method if you wanted to do that. | 
| 700 |  |  |  |  |  |  |  | 
| 701 |  |  |  |  |  |  | Note that C will be reset to C by a call to C when it's | 
| 702 |  |  |  |  |  |  | not a code reference. If it I a code reference, you need to be sure to | 
| 703 |  |  |  |  |  |  | write it in such a way that it knows that things have been reset (by examining | 
| 704 |  |  |  |  |  |  | states, for example, all of which will have been removed by C). | 
| 705 |  |  |  |  |  |  |  | 
| 706 |  |  |  |  |  |  | =cut | 
| 707 |  |  |  |  |  |  |  | 
| 708 |  |  |  |  |  |  | sub done { | 
| 709 | 76 |  |  | 76 | 1 | 5790 | my $self = shift; | 
| 710 | 76 |  |  |  |  | 107 | my $fsa = $machines{$self}; | 
| 711 | 76 | 100 |  |  |  | 144 | if (@_) { | 
| 712 | 23 |  |  |  |  | 34 | $fsa->{done} = shift; | 
| 713 | 23 |  |  |  |  | 61 | return $self; | 
| 714 |  |  |  |  |  |  | } | 
| 715 | 53 |  |  |  |  | 58 | my $code = $fsa->{done}; | 
| 716 | 53 | 100 |  |  |  | 187 | return $code unless ref $code eq 'CODE'; | 
| 717 | 6 |  |  |  |  | 14 | return $code->($self); | 
| 718 |  |  |  |  |  |  | } | 
| 719 |  |  |  |  |  |  |  | 
| 720 |  |  |  |  |  |  | ############################################################################## | 
| 721 |  |  |  |  |  |  |  | 
| 722 |  |  |  |  |  |  | =head3 strict | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  | my $strict = $fsa->strict; | 
| 725 |  |  |  |  |  |  | $fsa->strict(1); | 
| 726 |  |  |  |  |  |  |  | 
| 727 |  |  |  |  |  |  | Get or set the C attribute of the state machine. When set to true, the | 
| 728 |  |  |  |  |  |  | strict attribute disallows the short-circuiting of rules and allows a transfer | 
| 729 |  |  |  |  |  |  | if only one rule returns a true value. If more than one rule evaluates to | 
| 730 |  |  |  |  |  |  | true, an exception will be thrown. | 
| 731 |  |  |  |  |  |  |  | 
| 732 |  |  |  |  |  |  | =cut | 
| 733 |  |  |  |  |  |  |  | 
| 734 |  |  |  |  |  |  | sub strict { | 
| 735 | 24 |  |  | 24 | 1 | 28 | my $self = shift; | 
| 736 | 24 | 100 |  |  |  | 126 | return $machines{$self}->{strict} unless @_; | 
| 737 | 5 |  |  |  |  | 15 | $machines{$self}->{strict} = shift; | 
| 738 | 5 |  |  |  |  | 7 | return $self; | 
| 739 |  |  |  |  |  |  | } | 
| 740 |  |  |  |  |  |  |  | 
| 741 |  |  |  |  |  |  | ############################################################################## | 
| 742 |  |  |  |  |  |  |  | 
| 743 |  |  |  |  |  |  | =head3 run | 
| 744 |  |  |  |  |  |  |  | 
| 745 |  |  |  |  |  |  | $fsa->run; | 
| 746 |  |  |  |  |  |  |  | 
| 747 |  |  |  |  |  |  | This method starts the FSA::Rules engine (if it hasn't already been set to a | 
| 748 |  |  |  |  |  |  | state) by calling C, and then calls the C method repeatedly | 
| 749 |  |  |  |  |  |  | until C returns a true value. In other words, it's a convenient | 
| 750 |  |  |  |  |  |  | shortcut for: | 
| 751 |  |  |  |  |  |  |  | 
| 752 |  |  |  |  |  |  | $fsa->start unless $self->curr_state; | 
| 753 |  |  |  |  |  |  | $fsa->switch until $self->done; | 
| 754 |  |  |  |  |  |  |  | 
| 755 |  |  |  |  |  |  | But be careful when calling this method. If you have no failed switches | 
| 756 |  |  |  |  |  |  | between states and the states never set the C attribute to a true value, | 
| 757 |  |  |  |  |  |  | then this method will never die or return, but run forever. So plan carefully! | 
| 758 |  |  |  |  |  |  |  | 
| 759 |  |  |  |  |  |  | Returns the FSA::Rules object. | 
| 760 |  |  |  |  |  |  |  | 
| 761 |  |  |  |  |  |  | =cut | 
| 762 |  |  |  |  |  |  |  | 
| 763 |  |  |  |  |  |  | sub run { | 
| 764 | 6 |  |  | 6 | 1 | 11 | my $self = shift; | 
| 765 | 6 | 100 |  |  |  | 17 | $self->start unless $self->curr_state; | 
| 766 | 6 |  |  |  |  | 17 | $self->switch until $self->done; | 
| 767 | 6 |  |  |  |  | 24 | return $self; | 
| 768 |  |  |  |  |  |  | } | 
| 769 |  |  |  |  |  |  |  | 
| 770 |  |  |  |  |  |  | ############################################################################## | 
| 771 |  |  |  |  |  |  |  | 
| 772 |  |  |  |  |  |  | =head3 reset | 
| 773 |  |  |  |  |  |  |  | 
| 774 |  |  |  |  |  |  | $fsa->reset; | 
| 775 |  |  |  |  |  |  |  | 
| 776 |  |  |  |  |  |  | The C method clears the stack and notes, sets the current state to | 
| 777 |  |  |  |  |  |  | C, and sets C to C (unless C is a code reference). | 
| 778 |  |  |  |  |  |  | Also clears any temporary data stored directly in the machine hash reference | 
| 779 |  |  |  |  |  |  | and the state hash references. Use this method when you want to reuse your | 
| 780 |  |  |  |  |  |  | state machine. Returns the DFA::Rules object. | 
| 781 |  |  |  |  |  |  |  | 
| 782 |  |  |  |  |  |  | my $fsa = FSA::Rules->new(@state_machine); | 
| 783 |  |  |  |  |  |  | $fsa->done(sub {$done}); | 
| 784 |  |  |  |  |  |  | $fsa->run; | 
| 785 |  |  |  |  |  |  | # do a bunch of stuff | 
| 786 |  |  |  |  |  |  | $fsa->{miscellaneous} = 42; | 
| 787 |  |  |  |  |  |  | $fsa->reset->run; | 
| 788 |  |  |  |  |  |  | # $fsa->{miscellaneous} does not exist | 
| 789 |  |  |  |  |  |  |  | 
| 790 |  |  |  |  |  |  | =cut | 
| 791 |  |  |  |  |  |  |  | 
| 792 |  |  |  |  |  |  | sub reset { | 
| 793 | 3 |  |  | 3 | 1 | 7 | my $self = shift; | 
| 794 | 3 |  |  |  |  | 93 | my $fsa = $machines{$self}; | 
| 795 | 3 |  |  |  |  | 8 | $fsa->{current} = undef; | 
| 796 | 3 |  |  |  |  | 8 | $fsa->{notes} = {}; | 
| 797 | 3 | 50 |  |  |  | 18 | $fsa->{done} = undef unless ref $fsa->{done} eq 'CODE'; | 
| 798 | 3 |  |  |  |  | 4 | @{$fsa->{stack}} = (); | 
|  | 3 |  |  |  |  | 18 |  | 
| 799 | 3 |  |  |  |  | 11 | for my $state ($self->states) { | 
| 800 | 6 |  |  |  |  | 8 | @{$states{$state}->{index}} = (); | 
|  | 6 |  |  |  |  | 15 |  | 
| 801 | 6 |  |  |  |  | 21 | delete $state->{$_} for keys %$state; | 
| 802 |  |  |  |  |  |  | } | 
| 803 | 3 |  |  |  |  | 11 | delete $self->{$_} for keys %$self; | 
| 804 | 3 |  |  |  |  | 13 | return $self; | 
| 805 |  |  |  |  |  |  | } | 
| 806 |  |  |  |  |  |  |  | 
| 807 |  |  |  |  |  |  | ############################################################################## | 
| 808 |  |  |  |  |  |  |  | 
| 809 |  |  |  |  |  |  | =head3 notes | 
| 810 |  |  |  |  |  |  |  | 
| 811 |  |  |  |  |  |  | $fsa->notes($key => $value); | 
| 812 |  |  |  |  |  |  | my $val = $fsa->notes($key); | 
| 813 |  |  |  |  |  |  | my $notes = $fsa->notes; | 
| 814 |  |  |  |  |  |  |  | 
| 815 |  |  |  |  |  |  | The C method provides a place to store arbitrary data in the state | 
| 816 |  |  |  |  |  |  | machine, just in case you're not comfortable using the FSA::Rules object | 
| 817 |  |  |  |  |  |  | itself, which is an empty hash. Any data stored here persists for the lifetime | 
| 818 |  |  |  |  |  |  | of the state machine or until C is called. | 
| 819 |  |  |  |  |  |  |  | 
| 820 |  |  |  |  |  |  | Conceptually, C contains a hash of key-value pairs. | 
| 821 |  |  |  |  |  |  |  | 
| 822 |  |  |  |  |  |  | C<< $fsa->notes($key => $value) >> stores a new entry in this hash. | 
| 823 |  |  |  |  |  |  | C<< $fsa->notes->($key) >> returns a previously stored value. | 
| 824 |  |  |  |  |  |  | C<< $fsa->notes >>, called without arguments, returns a reference to the | 
| 825 |  |  |  |  |  |  | entire hash of key-value pairs. | 
| 826 |  |  |  |  |  |  |  | 
| 827 |  |  |  |  |  |  | Returns the FSA::Rules object when setting a note value. | 
| 828 |  |  |  |  |  |  |  | 
| 829 |  |  |  |  |  |  | =cut | 
| 830 |  |  |  |  |  |  |  | 
| 831 |  |  |  |  |  |  | sub notes { | 
| 832 | 25 |  |  | 25 | 1 | 31 | my $self = shift; | 
| 833 | 25 |  |  |  |  | 42 | my $fsa = $machines{$self}; | 
| 834 | 25 | 100 |  |  |  | 74 | return $fsa->{notes} unless @_; | 
| 835 | 22 |  |  |  |  | 26 | my $key = shift; | 
| 836 | 22 | 100 |  |  |  | 86 | return $fsa->{notes}{$key} unless @_; | 
| 837 | 10 |  |  |  |  | 24 | $fsa->{notes}{$key} = shift; | 
| 838 | 10 |  |  |  |  | 29 | return $self; | 
| 839 |  |  |  |  |  |  | } | 
| 840 |  |  |  |  |  |  |  | 
| 841 |  |  |  |  |  |  | ############################################################################## | 
| 842 |  |  |  |  |  |  |  | 
| 843 |  |  |  |  |  |  | =head3 last_message | 
| 844 |  |  |  |  |  |  |  | 
| 845 |  |  |  |  |  |  | my $message = $fsa->last_message; | 
| 846 |  |  |  |  |  |  | $message = $fsa->last_message($state_name); | 
| 847 |  |  |  |  |  |  |  | 
| 848 |  |  |  |  |  |  | Returns the last message of the current state. Pass in the name of a state to | 
| 849 |  |  |  |  |  |  | get the last message for that state, instead. | 
| 850 |  |  |  |  |  |  |  | 
| 851 |  |  |  |  |  |  | =cut | 
| 852 |  |  |  |  |  |  |  | 
| 853 |  |  |  |  |  |  | sub last_message { | 
| 854 | 2 |  |  | 2 | 1 | 5 | my $self = shift; | 
| 855 | 2 | 100 |  |  |  | 10 | return $self->curr_state->message unless @_; | 
| 856 | 1 |  |  |  |  | 4 | return $self->states(@_)->message; | 
| 857 |  |  |  |  |  |  | } | 
| 858 |  |  |  |  |  |  |  | 
| 859 |  |  |  |  |  |  | ############################################################################## | 
| 860 |  |  |  |  |  |  |  | 
| 861 |  |  |  |  |  |  | =head3 last_result | 
| 862 |  |  |  |  |  |  |  | 
| 863 |  |  |  |  |  |  | my $result = $fsa->last_result; | 
| 864 |  |  |  |  |  |  | $result = $fsa->last_result($state_name); | 
| 865 |  |  |  |  |  |  |  | 
| 866 |  |  |  |  |  |  | Returns the last result of the current state. Pass in the name of a state to | 
| 867 |  |  |  |  |  |  | get the last result for that state, instead. | 
| 868 |  |  |  |  |  |  |  | 
| 869 |  |  |  |  |  |  | =cut | 
| 870 |  |  |  |  |  |  |  | 
| 871 |  |  |  |  |  |  | sub last_result { | 
| 872 | 2 |  |  | 2 | 1 | 4 | my $self = shift; | 
| 873 | 2 | 100 |  |  |  | 6 | return $self->curr_state->result unless @_; | 
| 874 | 1 |  |  |  |  | 3 | return $self->states(@_)->result; | 
| 875 |  |  |  |  |  |  | } | 
| 876 |  |  |  |  |  |  |  | 
| 877 |  |  |  |  |  |  | ############################################################################## | 
| 878 |  |  |  |  |  |  |  | 
| 879 |  |  |  |  |  |  | =head3 stack | 
| 880 |  |  |  |  |  |  |  | 
| 881 |  |  |  |  |  |  | my $stack = $fsa->stack; | 
| 882 |  |  |  |  |  |  |  | 
| 883 |  |  |  |  |  |  | Returns an array reference of all states the machine has been in since it was | 
| 884 |  |  |  |  |  |  | created or since C was last called, beginning with the first state | 
| 885 |  |  |  |  |  |  | and ending with the current state. No state name will be added to the stack | 
| 886 |  |  |  |  |  |  | until the machine has entered that state. This method is useful for debugging. | 
| 887 |  |  |  |  |  |  |  | 
| 888 |  |  |  |  |  |  | =cut | 
| 889 |  |  |  |  |  |  |  | 
| 890 |  |  |  |  |  |  | sub stack { | 
| 891 | 3 |  |  | 3 | 1 | 4 | my $self = shift; | 
| 892 | 3 |  |  |  |  | 4 | return [map { $_->[0] } @{$machines{$self}->{stack}}]; | 
|  | 9 |  |  |  |  | 20 |  | 
|  | 3 |  |  |  |  | 10 |  | 
| 893 |  |  |  |  |  |  | } | 
| 894 |  |  |  |  |  |  |  | 
| 895 |  |  |  |  |  |  | ############################################################################## | 
| 896 |  |  |  |  |  |  |  | 
| 897 |  |  |  |  |  |  | =head3 raw_stacktrace | 
| 898 |  |  |  |  |  |  |  | 
| 899 |  |  |  |  |  |  | my $stacktrace = $fsa->raw_stacktrace; | 
| 900 |  |  |  |  |  |  |  | 
| 901 |  |  |  |  |  |  | Similar to C, this method returns an array reference of the states | 
| 902 |  |  |  |  |  |  | that the machine has been in. Each state is an array reference with two | 
| 903 |  |  |  |  |  |  | elements. The first element is the name of the state and the second element is | 
| 904 |  |  |  |  |  |  | a hash reference with two keys, "result" and "message". These are set to the | 
| 905 |  |  |  |  |  |  | values (if used) set by the C and C methods on the | 
| 906 |  |  |  |  |  |  | corresponding FSA::State objects. | 
| 907 |  |  |  |  |  |  |  | 
| 908 |  |  |  |  |  |  | A sample state: | 
| 909 |  |  |  |  |  |  |  | 
| 910 |  |  |  |  |  |  | [ | 
| 911 |  |  |  |  |  |  | some_state, | 
| 912 |  |  |  |  |  |  | { | 
| 913 |  |  |  |  |  |  | result  => 7, | 
| 914 |  |  |  |  |  |  | message => 'A human readable message' | 
| 915 |  |  |  |  |  |  | } | 
| 916 |  |  |  |  |  |  | ] | 
| 917 |  |  |  |  |  |  |  | 
| 918 |  |  |  |  |  |  | =cut | 
| 919 |  |  |  |  |  |  |  | 
| 920 | 18 |  |  | 18 | 1 | 637 | sub raw_stacktrace { $machines{shift()}->{stack} } | 
| 921 |  |  |  |  |  |  |  | 
| 922 |  |  |  |  |  |  | ############################################################################## | 
| 923 |  |  |  |  |  |  |  | 
| 924 |  |  |  |  |  |  | =head3 stacktrace | 
| 925 |  |  |  |  |  |  |  | 
| 926 |  |  |  |  |  |  | my $trace = $fsa->stacktrace; | 
| 927 |  |  |  |  |  |  |  | 
| 928 |  |  |  |  |  |  | Similar to C, except that the Cs and Cs are | 
| 929 |  |  |  |  |  |  | output in a human readable format with nicely formatted data (using | 
| 930 |  |  |  |  |  |  | Data::Dumper). Functionally there is no difference from C | 
| 931 |  |  |  |  |  |  | unless your states are storing references in their Cs or Cs | 
| 932 |  |  |  |  |  |  |  | 
| 933 |  |  |  |  |  |  | For example, if your state machine ran for only three states, the output may | 
| 934 |  |  |  |  |  |  | resemble the following: | 
| 935 |  |  |  |  |  |  |  | 
| 936 |  |  |  |  |  |  | State: foo | 
| 937 |  |  |  |  |  |  | { | 
| 938 |  |  |  |  |  |  | message => 'some message', | 
| 939 |  |  |  |  |  |  | result => 'a' | 
| 940 |  |  |  |  |  |  | } | 
| 941 |  |  |  |  |  |  |  | 
| 942 |  |  |  |  |  |  | State: bar | 
| 943 |  |  |  |  |  |  | { | 
| 944 |  |  |  |  |  |  | message => 'another message', | 
| 945 |  |  |  |  |  |  | result => [0, 1, 2] | 
| 946 |  |  |  |  |  |  | } | 
| 947 |  |  |  |  |  |  |  | 
| 948 |  |  |  |  |  |  | State: bar | 
| 949 |  |  |  |  |  |  | { | 
| 950 |  |  |  |  |  |  | message => 'and yet another message', | 
| 951 |  |  |  |  |  |  | result => 2 | 
| 952 |  |  |  |  |  |  | } | 
| 953 |  |  |  |  |  |  |  | 
| 954 |  |  |  |  |  |  | =cut | 
| 955 |  |  |  |  |  |  |  | 
| 956 |  |  |  |  |  |  | sub stacktrace { | 
| 957 | 1 |  |  | 1 | 1 | 3 | my $states     = shift->raw_stacktrace; | 
| 958 | 1 |  |  |  |  | 2 | my $stacktrace = ''; | 
| 959 | 1 |  |  |  |  | 715 | require Data::Dumper; | 
| 960 | 1 |  |  |  |  | 4600 | local $Data::Dumper::Terse     = 1; | 
| 961 | 1 |  |  |  |  | 2 | local $Data::Dumper::Indent    = 1; | 
| 962 | 1 |  |  |  |  | 1 | local $Data::Dumper::Quotekeys = 0; | 
| 963 | 1 |  |  |  |  | 1 | local $Data::Dumper::Sortkeys  = 1; | 
| 964 | 1 |  |  |  |  | 3 | local  $Data::Dumper::Useperl  = $] < 5.008; | 
| 965 | 1 |  |  |  |  | 3 | foreach my $state (@$states) { | 
| 966 | 4 |  |  |  |  | 9 | $stacktrace .= "State: $state->[0]\n"; | 
| 967 | 4 |  |  |  |  | 9 | $stacktrace .= Data::Dumper::Dumper($state->[1]); | 
| 968 | 4 |  |  |  |  | 160 | $stacktrace .= "\n"; | 
| 969 |  |  |  |  |  |  | } | 
| 970 | 1 |  |  |  |  | 4 | return $stacktrace; | 
| 971 |  |  |  |  |  |  | } | 
| 972 |  |  |  |  |  |  |  | 
| 973 |  |  |  |  |  |  | ############################################################################## | 
| 974 |  |  |  |  |  |  |  | 
| 975 |  |  |  |  |  |  | =head3 graph | 
| 976 |  |  |  |  |  |  |  | 
| 977 |  |  |  |  |  |  | my $graph_viz = $fsa->graph(@graph_viz_args); | 
| 978 |  |  |  |  |  |  | $graph_viz = $fsa->graph(\%params, @graph_viz_args); | 
| 979 |  |  |  |  |  |  |  | 
| 980 |  |  |  |  |  |  | Constructs and returns a L object useful for generating graphical | 
| 981 |  |  |  |  |  |  | representations of the complete rules engine. The parameters to C are | 
| 982 |  |  |  |  |  |  | all those supported by the GraphViz constructor; consult the L | 
| 983 |  |  |  |  |  |  | documentation for details. | 
| 984 |  |  |  |  |  |  |  | 
| 985 |  |  |  |  |  |  | Each node in the graph represents a single state. The label for each node in | 
| 986 |  |  |  |  |  |  | the graph will be either the state label or if there is no label, the state | 
| 987 |  |  |  |  |  |  | name. | 
| 988 |  |  |  |  |  |  |  | 
| 989 |  |  |  |  |  |  | Each edge in the graph represents a rule that defines the relationship between | 
| 990 |  |  |  |  |  |  | two states. If a rule is specified as a hash reference, the C key | 
| 991 |  |  |  |  |  |  | will be used as the edge label; otherwise the label will be blank. | 
| 992 |  |  |  |  |  |  |  | 
| 993 |  |  |  |  |  |  | An optional hash reference of parameters may be passed as the first argument | 
| 994 |  |  |  |  |  |  | to C. The supported parameters are: | 
| 995 |  |  |  |  |  |  |  | 
| 996 |  |  |  |  |  |  | =over | 
| 997 |  |  |  |  |  |  |  | 
| 998 |  |  |  |  |  |  | =item with_state_name | 
| 999 |  |  |  |  |  |  |  | 
| 1000 |  |  |  |  |  |  | This parameter, if set to true, prepends the name of the state and two | 
| 1001 |  |  |  |  |  |  | newlines to the label for each node. If a state has no label, then the state | 
| 1002 |  |  |  |  |  |  | name is simply used, regardless. Defaults to false. | 
| 1003 |  |  |  |  |  |  |  | 
| 1004 |  |  |  |  |  |  | =item wrap_nodes | 
| 1005 |  |  |  |  |  |  |  | 
| 1006 |  |  |  |  |  |  | =item wrap_node_labels | 
| 1007 |  |  |  |  |  |  |  | 
| 1008 |  |  |  |  |  |  | This parameter, if set to true, will wrap the node label text. This can be | 
| 1009 |  |  |  |  |  |  | useful if the label is long. The line length is determined by the | 
| 1010 |  |  |  |  |  |  | C parameter. Defaults to false. | 
| 1011 |  |  |  |  |  |  |  | 
| 1012 |  |  |  |  |  |  | =item wrap_edge_labels | 
| 1013 |  |  |  |  |  |  |  | 
| 1014 |  |  |  |  |  |  | =item wrap_labels | 
| 1015 |  |  |  |  |  |  |  | 
| 1016 |  |  |  |  |  |  | This parameter, if set to true, will wrap the edge text. This can be useful if | 
| 1017 |  |  |  |  |  |  | the rule message is long. The line length is determined by the C | 
| 1018 |  |  |  |  |  |  | parameter. Defaults to false C is deprecated and will be removed | 
| 1019 |  |  |  |  |  |  | in a future version. | 
| 1020 |  |  |  |  |  |  |  | 
| 1021 |  |  |  |  |  |  | =item text_wrap | 
| 1022 |  |  |  |  |  |  |  | 
| 1023 |  |  |  |  |  |  | =item wrap_length | 
| 1024 |  |  |  |  |  |  |  | 
| 1025 |  |  |  |  |  |  | The line length to use for wrapping text when C or C | 
| 1026 |  |  |  |  |  |  | is set to true. C is deprecated and will be removed in a future | 
| 1027 |  |  |  |  |  |  | version. Defaults to 25. | 
| 1028 |  |  |  |  |  |  |  | 
| 1029 |  |  |  |  |  |  | =item node_params | 
| 1030 |  |  |  |  |  |  |  | 
| 1031 |  |  |  |  |  |  | A hash reference of parameters to be passed to the GraphViz C | 
| 1032 |  |  |  |  |  |  | method when setting up a state as a node. Only the C | 
| 1033 |  |  |  |  |  |  | ignored. See the C documentation for the list of | 
| 1034 |  |  |  |  |  |  | supported parameters. | 
| 1035 |  |  |  |  |  |  |  | 
| 1036 |  |  |  |  |  |  | =item edge_params | 
| 1037 |  |  |  |  |  |  |  | 
| 1038 |  |  |  |  |  |  | A hash reference of parameters to be passed to the GraphViz C | 
| 1039 |  |  |  |  |  |  | method when setting up a state as a node. See the | 
| 1040 |  |  |  |  |  |  | C documentation for the list of supported | 
| 1041 |  |  |  |  |  |  | parameters. | 
| 1042 |  |  |  |  |  |  |  | 
| 1043 |  |  |  |  |  |  | =back | 
| 1044 |  |  |  |  |  |  |  | 
| 1045 |  |  |  |  |  |  | B If either C or C is not available on your | 
| 1046 |  |  |  |  |  |  | system, C will simply will warn and return. | 
| 1047 |  |  |  |  |  |  |  | 
| 1048 |  |  |  |  |  |  | =cut | 
| 1049 |  |  |  |  |  |  |  | 
| 1050 |  |  |  |  |  |  | sub graph { | 
| 1051 | 0 |  |  | 0 | 1 | 0 | my $self = shift; | 
| 1052 | 0 | 0 |  |  |  | 0 | my $params = ref $_[0] ? shift : {}; | 
| 1053 |  |  |  |  |  |  |  | 
| 1054 | 0 |  |  |  |  | 0 | eval "use GraphViz 2.00; use Text::Wrap"; | 
| 1055 | 0 | 0 |  |  |  | 0 | if ($@) { | 
| 1056 | 0 |  |  |  |  | 0 | warn "Cannot create graph object: $@"; | 
| 1057 | 0 |  |  |  |  | 0 | return; | 
| 1058 |  |  |  |  |  |  | } | 
| 1059 |  |  |  |  |  |  |  | 
| 1060 |  |  |  |  |  |  | # Handle backwards compatibility. | 
| 1061 | 0 | 0 |  |  |  | 0 | $params->{wrap_node_labels} = $params->{wrap_nodes} | 
| 1062 |  |  |  |  |  |  | unless exists $params->{wrap_node_labels}; | 
| 1063 | 0 | 0 |  |  |  | 0 | $params->{wrap_edge_labels} = $params->{wrap_labels} | 
| 1064 |  |  |  |  |  |  | unless exists $params->{wrap_edge_labels}; | 
| 1065 | 0 | 0 |  |  |  | 0 | $params->{wrap_length} = $params->{text_wrap} | 
| 1066 |  |  |  |  |  |  | unless exists $params->{wrap_length}; | 
| 1067 |  |  |  |  |  |  |  | 
| 1068 |  |  |  |  |  |  | # Set up defaults. | 
| 1069 | 0 |  | 0 |  |  | 0 | local $Text::Wrap::columns = $params->{wrap_length} || 25; | 
| 1070 | 0 | 0 |  |  |  | 0 | my @node_params = %{ $params->{node_params} || {} }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1071 | 0 | 0 |  |  |  | 0 | my @edge_params = %{ $params->{edge_params} || {} }; | 
|  | 0 |  |  |  |  | 0 |  | 
| 1072 |  |  |  |  |  |  |  | 
| 1073 |  |  |  |  |  |  | # Iterate over the states. | 
| 1074 | 0 |  |  |  |  | 0 | my $machine = $machines{$self}; | 
| 1075 | 0 |  |  |  |  | 0 | my $graph = GraphViz->new(@_); | 
| 1076 | 0 |  |  |  |  | 0 | for my $state (@{ $machine->{ord} }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 1077 | 0 |  |  |  |  | 0 | my $def = $states{$state}; | 
| 1078 | 0 |  |  |  |  | 0 | my $name  = $def->{name}; | 
| 1079 |  |  |  |  |  |  |  | 
| 1080 | 0 | 0 |  |  |  | 0 | my $label = !$def->{label} ? $name | 
|  |  | 0 |  |  |  |  |  | 
| 1081 |  |  |  |  |  |  | : $params->{with_state_name}  ? "$name\n\n$def->{label}" | 
| 1082 |  |  |  |  |  |  | :                               $def->{label}; | 
| 1083 |  |  |  |  |  |  |  | 
| 1084 | 0 | 0 |  |  |  | 0 | $graph->add_node( | 
| 1085 |  |  |  |  |  |  | $name, | 
| 1086 |  |  |  |  |  |  | @node_params, | 
| 1087 |  |  |  |  |  |  | label => $params->{wrap_node_labels} ? wrap('', '', $label) : $label, | 
| 1088 |  |  |  |  |  |  | ); | 
| 1089 | 0 | 0 |  |  |  | 0 | next unless exists $def->{rules}; | 
| 1090 | 0 |  |  |  |  | 0 | for my $condition (@{ $def->{rules} }) { | 
|  | 0 |  |  |  |  | 0 |  | 
| 1091 | 0 |  |  |  |  | 0 | my $rule = $condition->{state}->name; | 
| 1092 | 0 |  |  |  |  | 0 | my @edge = ($name => $rule); | 
| 1093 | 0 | 0 |  |  |  | 0 | if ($condition->{message}) { | 
| 1094 | 0 | 0 |  |  |  | 0 | push @edge, label => $params->{wrap_edge_labels} | 
| 1095 |  |  |  |  |  |  | ? wrap('', '', $condition->{message}) | 
| 1096 |  |  |  |  |  |  | : $condition->{message}; | 
| 1097 |  |  |  |  |  |  | } | 
| 1098 | 0 |  |  |  |  | 0 | $graph->add_edge( @edge, @edge_params ); | 
| 1099 |  |  |  |  |  |  | } | 
| 1100 |  |  |  |  |  |  | } | 
| 1101 | 0 |  |  |  |  | 0 | return $graph; | 
| 1102 |  |  |  |  |  |  | } | 
| 1103 |  |  |  |  |  |  |  | 
| 1104 |  |  |  |  |  |  | ############################################################################## | 
| 1105 |  |  |  |  |  |  |  | 
| 1106 |  |  |  |  |  |  | =head3 DESTROY | 
| 1107 |  |  |  |  |  |  |  | 
| 1108 |  |  |  |  |  |  | This method cleans up an FSA::Rules object's internal data when it is released | 
| 1109 |  |  |  |  |  |  | from memory. In general, you don't have to worry about the C method | 
| 1110 |  |  |  |  |  |  | unless you're subclassing FSA::Rules. In that case, if you implement your own | 
| 1111 |  |  |  |  |  |  | C method, just be sure to call C to prevent | 
| 1112 |  |  |  |  |  |  | memory leaks. | 
| 1113 |  |  |  |  |  |  |  | 
| 1114 |  |  |  |  |  |  | =cut | 
| 1115 |  |  |  |  |  |  |  | 
| 1116 |  |  |  |  |  |  | # This method deletes the record from %machines, which has a reference to each | 
| 1117 |  |  |  |  |  |  | # state, so those are deleted too. Each state refers back to the FSA::Rules | 
| 1118 |  |  |  |  |  |  | # object itself, so as each of them is destroyed, it's removed from %states | 
| 1119 |  |  |  |  |  |  | # and the FSA::Rules object gets all of its references defined in this file | 
| 1120 |  |  |  |  |  |  | # freed, too. No circular references, so no problem. | 
| 1121 |  |  |  |  |  |  |  | 
| 1122 | 36 |  |  | 36 |  | 2322 | sub DESTROY { delete $machines{+shift}; } | 
| 1123 |  |  |  |  |  |  |  | 
| 1124 |  |  |  |  |  |  | ############################################################################## | 
| 1125 |  |  |  |  |  |  |  | 
| 1126 |  |  |  |  |  |  | # Private error handler. | 
| 1127 |  |  |  |  |  |  | sub _croak { | 
| 1128 | 9 |  |  | 9 |  | 14 | shift; | 
| 1129 | 9 |  |  |  |  | 65 | require Carp; | 
| 1130 | 9 |  |  |  |  | 1237 | Carp::croak(@_); | 
| 1131 |  |  |  |  |  |  | } | 
| 1132 |  |  |  |  |  |  |  | 
| 1133 |  |  |  |  |  |  | ############################################################################## | 
| 1134 |  |  |  |  |  |  |  | 
| 1135 |  |  |  |  |  |  | =begin comment | 
| 1136 |  |  |  |  |  |  |  | 
| 1137 |  |  |  |  |  |  | Let's just keep the STORABLE methods hidden. They should just magically work. | 
| 1138 |  |  |  |  |  |  |  | 
| 1139 |  |  |  |  |  |  | =head3 STORABLE_freeze | 
| 1140 |  |  |  |  |  |  |  | 
| 1141 |  |  |  |  |  |  | =cut | 
| 1142 |  |  |  |  |  |  |  | 
| 1143 |  |  |  |  |  |  | sub STORABLE_freeze { | 
| 1144 | 1 |  |  | 1 | 1 | 48 | my ($self, $clone) = @_; | 
| 1145 | 1 | 50 |  |  |  | 4 | return if $clone; | 
| 1146 | 1 |  |  |  |  | 2 | my $fsa = $machines{$self}; | 
| 1147 | 1 |  |  |  |  | 3 | return ( $self, [ { %$self }, $fsa, @states{ @{ $fsa->{ord} } } ] ); | 
|  | 1 |  |  |  |  | 90 |  | 
| 1148 |  |  |  |  |  |  | } | 
| 1149 |  |  |  |  |  |  |  | 
| 1150 |  |  |  |  |  |  | ############################################################################## | 
| 1151 |  |  |  |  |  |  |  | 
| 1152 |  |  |  |  |  |  | =head3 STORABLE_thaw | 
| 1153 |  |  |  |  |  |  |  | 
| 1154 |  |  |  |  |  |  | =end comment | 
| 1155 |  |  |  |  |  |  |  | 
| 1156 |  |  |  |  |  |  | =cut | 
| 1157 |  |  |  |  |  |  |  | 
| 1158 |  |  |  |  |  |  | sub STORABLE_thaw { | 
| 1159 | 1 |  |  | 1 | 1 | 2770 | my ($self, $clone, $junk, $data) = @_; | 
| 1160 | 1 | 50 |  |  |  | 4 | return if $clone; | 
| 1161 | 1 |  |  |  |  | 2 | %{ $self }                  = %{ shift @$data }; | 
|  | 1 |  |  |  |  | 2 |  | 
|  | 1 |  |  |  |  | 3 |  | 
| 1162 | 1 |  |  |  |  | 2 | my $fsa                     = shift @$data; | 
| 1163 | 1 |  |  |  |  | 2 | $machines{ $self }          = $fsa; | 
| 1164 | 1 |  |  |  |  | 1 | @states{ @{ $fsa->{ord} } } = @$data; | 
|  | 1 |  |  |  |  | 4 |  | 
| 1165 | 1 |  |  |  |  | 7 | return $self; | 
| 1166 |  |  |  |  |  |  | } | 
| 1167 |  |  |  |  |  |  |  | 
| 1168 |  |  |  |  |  |  | ############################################################################## | 
| 1169 |  |  |  |  |  |  |  | 
| 1170 |  |  |  |  |  |  | package FSA::State; | 
| 1171 |  |  |  |  |  |  | $FSA::State::VERSION = '0.35'; | 
| 1172 |  |  |  |  |  |  |  | 
| 1173 |  |  |  |  |  |  | =head1 FSA::State Interface | 
| 1174 |  |  |  |  |  |  |  | 
| 1175 |  |  |  |  |  |  | FSA::State objects represent individual states in a state machine. They are | 
| 1176 |  |  |  |  |  |  | passed as the first argument to state actions, where their methods can be | 
| 1177 |  |  |  |  |  |  | called to handle various parts of the processing, set up messages and results, | 
| 1178 |  |  |  |  |  |  | or access the state machine object itself. | 
| 1179 |  |  |  |  |  |  |  | 
| 1180 |  |  |  |  |  |  | Like FSA::Rules objects, FSA::State objects are empty hashes, so you can feel | 
| 1181 |  |  |  |  |  |  | free to stash data in them. But note that each state object is independent of | 
| 1182 |  |  |  |  |  |  | all others, so if you want to stash data for other states to access, you'll | 
| 1183 |  |  |  |  |  |  | likely have to stash it in the state machine object (in its hash | 
| 1184 |  |  |  |  |  |  | implementation or via the C method), or retrieve other states from | 
| 1185 |  |  |  |  |  |  | the state machine using its C method and then access their hash data | 
| 1186 |  |  |  |  |  |  | directly. | 
| 1187 |  |  |  |  |  |  |  | 
| 1188 |  |  |  |  |  |  | =head2 Constructor | 
| 1189 |  |  |  |  |  |  |  | 
| 1190 |  |  |  |  |  |  | =head3 new | 
| 1191 |  |  |  |  |  |  |  | 
| 1192 |  |  |  |  |  |  | my $state = FSA::State->new; | 
| 1193 |  |  |  |  |  |  |  | 
| 1194 |  |  |  |  |  |  | Constructs and returns a new FSA::State object. Not intended to be called | 
| 1195 |  |  |  |  |  |  | directly, but by FSA::Rules. | 
| 1196 |  |  |  |  |  |  |  | 
| 1197 |  |  |  |  |  |  | =cut | 
| 1198 |  |  |  |  |  |  |  | 
| 1199 |  |  |  |  |  |  | sub new { | 
| 1200 | 59 |  |  | 59 |  | 62 | my $class = shift; | 
| 1201 | 59 |  |  |  |  | 137 | return bless {@_} => $class; | 
| 1202 |  |  |  |  |  |  | } | 
| 1203 |  |  |  |  |  |  |  | 
| 1204 |  |  |  |  |  |  | ############################################################################## | 
| 1205 |  |  |  |  |  |  |  | 
| 1206 |  |  |  |  |  |  | =head2 Instance Methods | 
| 1207 |  |  |  |  |  |  |  | 
| 1208 |  |  |  |  |  |  | =head3 name | 
| 1209 |  |  |  |  |  |  |  | 
| 1210 |  |  |  |  |  |  | my $name = $state->name; | 
| 1211 |  |  |  |  |  |  |  | 
| 1212 |  |  |  |  |  |  | Returns the name of the state. | 
| 1213 |  |  |  |  |  |  |  | 
| 1214 |  |  |  |  |  |  | =cut | 
| 1215 |  |  |  |  |  |  |  | 
| 1216 | 244 |  |  | 244 |  | 1957 | sub name { $states{shift()}->{name} } | 
| 1217 |  |  |  |  |  |  |  | 
| 1218 |  |  |  |  |  |  | ############################################################################## | 
| 1219 |  |  |  |  |  |  |  | 
| 1220 |  |  |  |  |  |  | =head3 label | 
| 1221 |  |  |  |  |  |  |  | 
| 1222 |  |  |  |  |  |  | my $label = $state->label; | 
| 1223 |  |  |  |  |  |  |  | 
| 1224 |  |  |  |  |  |  | Returns the label of the state. | 
| 1225 |  |  |  |  |  |  |  | 
| 1226 |  |  |  |  |  |  | =cut | 
| 1227 |  |  |  |  |  |  |  | 
| 1228 | 2 |  |  | 2 |  | 12 | sub label { $states{shift()}->{label} } | 
| 1229 |  |  |  |  |  |  |  | 
| 1230 |  |  |  |  |  |  | ############################################################################## | 
| 1231 |  |  |  |  |  |  |  | 
| 1232 |  |  |  |  |  |  | =head3 machine | 
| 1233 |  |  |  |  |  |  |  | 
| 1234 |  |  |  |  |  |  | my $machine = $state->machine; | 
| 1235 |  |  |  |  |  |  |  | 
| 1236 |  |  |  |  |  |  | Returns the FSA::Rules object for which the state was defined. | 
| 1237 |  |  |  |  |  |  |  | 
| 1238 |  |  |  |  |  |  | =cut | 
| 1239 |  |  |  |  |  |  |  | 
| 1240 | 216 |  |  | 216 |  | 938 | sub machine { $states{shift()}->{machine} } | 
| 1241 |  |  |  |  |  |  |  | 
| 1242 |  |  |  |  |  |  | ############################################################################## | 
| 1243 |  |  |  |  |  |  |  | 
| 1244 |  |  |  |  |  |  | =head3 result | 
| 1245 |  |  |  |  |  |  |  | 
| 1246 |  |  |  |  |  |  | my $fsa = FSA::Rules->new( | 
| 1247 |  |  |  |  |  |  | # ... | 
| 1248 |  |  |  |  |  |  | some_state => { | 
| 1249 |  |  |  |  |  |  | do => sub { | 
| 1250 |  |  |  |  |  |  | my $state = shift; | 
| 1251 |  |  |  |  |  |  | # Do stuff... | 
| 1252 |  |  |  |  |  |  | $state->result(1); # We're done! | 
| 1253 |  |  |  |  |  |  | }, | 
| 1254 |  |  |  |  |  |  | rules => [ | 
| 1255 |  |  |  |  |  |  | bad  => sub { ! shift->result }, | 
| 1256 |  |  |  |  |  |  | good => sub {   shift->result }, | 
| 1257 |  |  |  |  |  |  | ] | 
| 1258 |  |  |  |  |  |  | }, | 
| 1259 |  |  |  |  |  |  | # ... | 
| 1260 |  |  |  |  |  |  | ); | 
| 1261 |  |  |  |  |  |  |  | 
| 1262 |  |  |  |  |  |  | This is a useful method to store results on a per-state basis. Anything can be | 
| 1263 |  |  |  |  |  |  | stored in the result slot. Each time the state is entered, it gets a new | 
| 1264 |  |  |  |  |  |  | result slot. Call C without arguments in a scalar context to get the | 
| 1265 |  |  |  |  |  |  | current result; call it without arguments in an array context to get all of | 
| 1266 |  |  |  |  |  |  | the results for the state for each time it has been entered into, from first | 
| 1267 |  |  |  |  |  |  | to last. The contents of each result slot can also be viewed in a | 
| 1268 |  |  |  |  |  |  | C or C. | 
| 1269 |  |  |  |  |  |  |  | 
| 1270 |  |  |  |  |  |  | =cut | 
| 1271 |  |  |  |  |  |  |  | 
| 1272 |  |  |  |  |  |  | sub result { | 
| 1273 | 9 |  |  | 9 |  | 19 | my $self = shift; | 
| 1274 | 9 | 100 |  |  |  | 23 | return $self->_state_slot('result') unless @_; | 
| 1275 |  |  |  |  |  |  | # XXX Yow! | 
| 1276 | 4 |  |  |  |  | 6 | $machines{$self->machine}->{stack}[$states{$self}->{index}[-1]][1]{result} | 
| 1277 |  |  |  |  |  |  | = shift; | 
| 1278 | 4 |  |  |  |  | 6 | return $self; | 
| 1279 |  |  |  |  |  |  | } | 
| 1280 |  |  |  |  |  |  |  | 
| 1281 |  |  |  |  |  |  | ############################################################################## | 
| 1282 |  |  |  |  |  |  |  | 
| 1283 |  |  |  |  |  |  | =head3 message | 
| 1284 |  |  |  |  |  |  |  | 
| 1285 |  |  |  |  |  |  | my $fsa = FSA::Rules->new( | 
| 1286 |  |  |  |  |  |  | # ... | 
| 1287 |  |  |  |  |  |  | some_state => { | 
| 1288 |  |  |  |  |  |  | do => sub { | 
| 1289 |  |  |  |  |  |  | my $state = shift; | 
| 1290 |  |  |  |  |  |  | # Do stuff... | 
| 1291 |  |  |  |  |  |  | $state->message('hello ', $ENV{USER}); | 
| 1292 |  |  |  |  |  |  | }, | 
| 1293 |  |  |  |  |  |  | rules => [ | 
| 1294 |  |  |  |  |  |  | bad  => sub { ! shift->message }, | 
| 1295 |  |  |  |  |  |  | good => sub {   shift->message }, | 
| 1296 |  |  |  |  |  |  | ] | 
| 1297 |  |  |  |  |  |  | }, | 
| 1298 |  |  |  |  |  |  | # ... | 
| 1299 |  |  |  |  |  |  | ); | 
| 1300 |  |  |  |  |  |  |  | 
| 1301 |  |  |  |  |  |  | This is a useful method to store messages on a per-state basis. Anything can | 
| 1302 |  |  |  |  |  |  | be stored in the message slot. Each time the state is entered, it gets a new | 
| 1303 |  |  |  |  |  |  | message slot. Call C without arguments in a scalar context to get | 
| 1304 |  |  |  |  |  |  | the current message; call it without arguments in an array context to get all | 
| 1305 |  |  |  |  |  |  | of the messages for the state for each time it has been entered into, from | 
| 1306 |  |  |  |  |  |  | first to last. The contents of each message slot can also be viewed in a | 
| 1307 |  |  |  |  |  |  | C or C. | 
| 1308 |  |  |  |  |  |  |  | 
| 1309 |  |  |  |  |  |  | =cut | 
| 1310 |  |  |  |  |  |  |  | 
| 1311 |  |  |  |  |  |  | sub message { | 
| 1312 | 10 |  |  | 10 |  | 19 | my $self = shift; | 
| 1313 | 10 | 100 |  |  |  | 29 | return $self->_state_slot('message') unless @_; | 
| 1314 |  |  |  |  |  |  | # XXX Yow! | 
| 1315 | 4 |  |  |  |  | 11 | $machines{$self->machine}->{stack}[$states{$self}->{index}[-1]][1]{message} | 
| 1316 |  |  |  |  |  |  | = join '', @_; | 
| 1317 | 4 |  |  |  |  | 6 | return $self; | 
| 1318 |  |  |  |  |  |  | } | 
| 1319 |  |  |  |  |  |  |  | 
| 1320 |  |  |  |  |  |  | ############################################################################## | 
| 1321 |  |  |  |  |  |  |  | 
| 1322 |  |  |  |  |  |  | =head3 prev_state | 
| 1323 |  |  |  |  |  |  |  | 
| 1324 |  |  |  |  |  |  | my $prev = $state->prev_state; | 
| 1325 |  |  |  |  |  |  |  | 
| 1326 |  |  |  |  |  |  | A shortcut for C<< $state->machine->prev_state >>. | 
| 1327 |  |  |  |  |  |  |  | 
| 1328 |  |  |  |  |  |  | =head3 done | 
| 1329 |  |  |  |  |  |  |  | 
| 1330 |  |  |  |  |  |  | my $done = $state->done; | 
| 1331 |  |  |  |  |  |  | $state->done($done); | 
| 1332 |  |  |  |  |  |  |  | 
| 1333 |  |  |  |  |  |  | A shortcut for C<< $state->machine->done >>. Note that, unlike C and | 
| 1334 |  |  |  |  |  |  | C, the C attribute is stored machine-wide, rather than | 
| 1335 |  |  |  |  |  |  | state-wide. You'll generally call it on the state object when you want to tell | 
| 1336 |  |  |  |  |  |  | the machine that processing is complete. | 
| 1337 |  |  |  |  |  |  |  | 
| 1338 |  |  |  |  |  |  | =head3 notes | 
| 1339 |  |  |  |  |  |  |  | 
| 1340 |  |  |  |  |  |  | my $notes = $state->notes; | 
| 1341 |  |  |  |  |  |  | $state->notes($notes); | 
| 1342 |  |  |  |  |  |  |  | 
| 1343 |  |  |  |  |  |  | A shortcut for C<< $state->machine->notes >>. Note that, unlike C and | 
| 1344 |  |  |  |  |  |  | C, notes are stored machine-wide, rather than state-wide. It is | 
| 1345 |  |  |  |  |  |  | therefore probably the most convenient way to stash data for other states to | 
| 1346 |  |  |  |  |  |  | access. | 
| 1347 |  |  |  |  |  |  |  | 
| 1348 |  |  |  |  |  |  | =cut | 
| 1349 |  |  |  |  |  |  |  | 
| 1350 | 4 |  |  | 4 |  | 18 | sub prev_state { shift->machine->prev_state(@_) } | 
| 1351 | 11 |  |  | 11 |  | 43 | sub notes      { shift->machine->notes(@_) } | 
| 1352 | 13 |  |  | 13 |  | 22 | sub done       { shift->machine->done(@_) } | 
| 1353 |  |  |  |  |  |  |  | 
| 1354 |  |  |  |  |  |  | ############################################################################## | 
| 1355 |  |  |  |  |  |  |  | 
| 1356 |  |  |  |  |  |  | =head3 enter | 
| 1357 |  |  |  |  |  |  |  | 
| 1358 |  |  |  |  |  |  | Executes all of the C actions. Called by FSA::Rules's | 
| 1359 |  |  |  |  |  |  | C method, and not intended to be called directly. | 
| 1360 |  |  |  |  |  |  |  | 
| 1361 |  |  |  |  |  |  | =cut | 
| 1362 |  |  |  |  |  |  |  | 
| 1363 |  |  |  |  |  |  | sub enter { | 
| 1364 | 141 |  |  | 141 |  | 136 | my $self = shift; | 
| 1365 | 141 |  |  |  |  | 164 | my $state = $states{$self}; | 
| 1366 | 141 |  |  |  |  | 120 | $_->($self) for @{$state->{on_enter}}; | 
|  | 141 |  |  |  |  | 270 |  | 
| 1367 | 141 |  |  |  |  | 164 | return $self; | 
| 1368 |  |  |  |  |  |  | } | 
| 1369 |  |  |  |  |  |  |  | 
| 1370 |  |  |  |  |  |  | ############################################################################## | 
| 1371 |  |  |  |  |  |  |  | 
| 1372 |  |  |  |  |  |  | =head3 do | 
| 1373 |  |  |  |  |  |  |  | 
| 1374 |  |  |  |  |  |  | Executes all of the C actions. Called by FSA::Rules's C | 
| 1375 |  |  |  |  |  |  | method, and not intended to be called directly. | 
| 1376 |  |  |  |  |  |  |  | 
| 1377 |  |  |  |  |  |  | =cut | 
| 1378 |  |  |  |  |  |  |  | 
| 1379 |  |  |  |  |  |  | sub do { | 
| 1380 | 141 |  |  | 141 |  | 125 | my $self = shift; | 
| 1381 | 141 |  |  |  |  | 155 | my $state = $states{$self}; | 
| 1382 | 141 |  |  |  |  | 109 | $_->($self) for @{$state->{do}}; | 
|  | 141 |  |  |  |  | 281 |  | 
| 1383 | 140 |  |  |  |  | 878 | return $self; | 
| 1384 |  |  |  |  |  |  | } | 
| 1385 |  |  |  |  |  |  |  | 
| 1386 |  |  |  |  |  |  | ############################################################################## | 
| 1387 |  |  |  |  |  |  |  | 
| 1388 |  |  |  |  |  |  | =head3 exit | 
| 1389 |  |  |  |  |  |  |  | 
| 1390 |  |  |  |  |  |  | Executes all of the C actions. Called by FSA::Rules's C | 
| 1391 |  |  |  |  |  |  | method, and not intended to be called directly. | 
| 1392 |  |  |  |  |  |  |  | 
| 1393 |  |  |  |  |  |  | =cut | 
| 1394 |  |  |  |  |  |  |  | 
| 1395 |  |  |  |  |  |  | sub exit { | 
| 1396 | 109 |  |  | 109 |  | 103 | my $self = shift; | 
| 1397 | 109 |  |  |  |  | 139 | my $state = $states{$self}; | 
| 1398 | 109 |  |  |  |  | 90 | $_->($self) for @{$state->{on_exit}}; | 
|  | 109 |  |  |  |  | 205 |  | 
| 1399 | 109 |  |  |  |  | 152 | return $self; | 
| 1400 |  |  |  |  |  |  | } | 
| 1401 |  |  |  |  |  |  |  | 
| 1402 |  |  |  |  |  |  | ############################################################################## | 
| 1403 |  |  |  |  |  |  |  | 
| 1404 |  |  |  |  |  |  | =head3 DESTROY | 
| 1405 |  |  |  |  |  |  |  | 
| 1406 |  |  |  |  |  |  | This method cleans up an FSA::State object's internal data when it is released | 
| 1407 |  |  |  |  |  |  | from memory. In general, you don't have to worry about the C method | 
| 1408 |  |  |  |  |  |  | unless you're subclassing FSA::State. In that case, if you implement your own | 
| 1409 |  |  |  |  |  |  | C method, just be sure to call C to prevent | 
| 1410 |  |  |  |  |  |  | memory leaks. | 
| 1411 |  |  |  |  |  |  |  | 
| 1412 |  |  |  |  |  |  | =cut | 
| 1413 |  |  |  |  |  |  |  | 
| 1414 | 61 |  |  | 61 |  | 1071 | sub DESTROY { delete $states{+shift}; } | 
| 1415 |  |  |  |  |  |  |  | 
| 1416 |  |  |  |  |  |  | ############################################################################## | 
| 1417 |  |  |  |  |  |  |  | 
| 1418 |  |  |  |  |  |  | # Used by message() and result() to get messages and results from the stack. | 
| 1419 |  |  |  |  |  |  |  | 
| 1420 |  |  |  |  |  |  | sub _state_slot { | 
| 1421 | 11 |  |  | 11 |  | 13 | my ($self, $slot) = @_; | 
| 1422 | 11 |  |  |  |  | 22 | my $trace = $self->machine->raw_stacktrace; | 
| 1423 | 11 |  |  |  |  | 19 | my $state = $states{$self}; | 
| 1424 |  |  |  |  |  |  | return wantarray | 
| 1425 | 11 | 100 |  |  |  | 53 | ? map { $_->[1]{$slot} } @{$trace}[@{$state->{index}} ] | 
|  | 5 |  |  |  |  | 29 |  | 
|  | 3 |  |  |  |  | 13 |  | 
|  | 3 |  |  |  |  | 6 |  | 
| 1426 |  |  |  |  |  |  | : $trace->[$state->{index}[-1]][1]{$slot}; | 
| 1427 |  |  |  |  |  |  | } | 
| 1428 |  |  |  |  |  |  |  | 
| 1429 |  |  |  |  |  |  | ############################################################################## | 
| 1430 |  |  |  |  |  |  | # Called by FSA::Rules->try_switch to get a list of the rules. I wonder if | 
| 1431 |  |  |  |  |  |  | # rules should become objects one day? | 
| 1432 |  |  |  |  |  |  |  | 
| 1433 |  |  |  |  |  |  | sub _rules { | 
| 1434 | 109 |  |  | 109 |  | 108 | my $self = shift; | 
| 1435 | 109 |  |  |  |  | 152 | my $state = $states{$self}; | 
| 1436 | 109 |  |  |  |  | 84 | return @{$state->{rules}} | 
|  | 109 |  |  |  |  | 245 |  | 
| 1437 |  |  |  |  |  |  | } | 
| 1438 |  |  |  |  |  |  |  | 
| 1439 |  |  |  |  |  |  | 1; | 
| 1440 |  |  |  |  |  |  | __END__ |