The vast majority of Perl scripts that I write are for managing information via HTML forms. The script will generate the form, and then process the results when the user hits submit. In order to keep track of what form the user is submitting data for, I’ve generally used a hidden form variable named “state”. Each form has a unique state, so the script knows what data to expect and how to respond.
This all works great for simple scripts, but once you get past 3 or 4 different states, the waters get muddy, and the code gets ugly. 20 years ago before I learned this trick I would write endless rows of if-then statements to match the forms state to the required actions. Then I would write most of the code to respond to those states in the midst of the if-then statements.
if ( $state =~ m/record_/ig ) {
$data->_fill_lev1( { 'object' => 'record' } ); if ( $state eq 'record_preview' and $userPerm >= 2 ) { ... } elsif ( $state eq 'record_archive' and $userPerm >= 4 ) { ...
On top of keeping track of states, I then had to deal with permissions separately. The code would eventually grow to several hundred or thousands of lines of code. I would call this block of code my state tree because it vaguely resembled a tree with branches.
I didn’t know it then, but this is all ridiculous, and there is definitely a better way!
StateTreeObjects
I was inspired to create “StateTreeObjects” after working with a lot of Kotlin code for a news-reading app. Everything in Kotlin is object-based, and I honestly didn’t set out to make such a simple and easy system, I just wanted to pack all of the State-Tree’s information into one object per state. I wanted this Perl code to be easy to understand and simple to use like Kotlin data classes.
package StateTreeObject; use warnings; sub new { my ($class, %args) = @_; # assign the hash of supplied values to the object itself. my $self = \%args; bless ($self, $class); return $self; } sub getAction { my ($self) = @_; return $self->{'ActionText'}; } sub getPermissionRequired { my ($self) = @_; return $self->{'PermissionRequired'}; } sub getSubroutineToCall { my ($self) = @_; return $self->{'SubroutineToCall'} } 1;
The new subroutine takes in a hash of keys and values, which are then saved into $self as a reference to %args. The reference is then blessed as an object. ( What does bless do? )
The getters (getAction, getPermissionRequired, and getSubroutineToCall) simply return their respective values in the $self object.
Create a New StateTreeObject:
To make the code as simple as possible, define all of the StateTreeObjects at once, storing them in a hash, with the key being the state that they are responsible for:
sub defineStateTreeA { my %stateTree = ( '' => StateTreeObject->new( 'ActionText' => '', 'PermissionRequired' => 1, 'SubroutineToCall'=> \&stateNoAction ), 'StateNoAction' => StateTreeObject->new( 'ActionText' => 'NoAction', 'PermissionRequired' => 0, 'SubroutineToCall' => \&stateNoAction ), 'StatePrevListPage' => StateTreeObject->new( 'ActionText' => 'Previous Page when listing items', 'PermissionRequired' => 0, 'SubroutineToCall' => \&statePrevListPage ), 'StateNextListPage' => StateTreeObject->new( 'ActionText' => 'Next Page when listing items', 'PermissionRequired' => 0, 'SubroutineToCall' => \&stateNextListPage ),
You can clearly see the states on the left in bold and all of their respective information on the right in italics. It’s simple to change the ‘ActionText’ that could be used for debugging or shown to the user.
The permission required is clearly noted and easily modified. This would allow multiple different user types. I’ve used it to great effect with 10 different permission levels. The most restricted users would be 0, and the least restricted would be the highest value.
The real simplicity here is defining the subroutine to call. This is just a reference to a subroutine.
Here’s the best part, the Dispatch portion:
sub executeState{ my ($state, $userPermissions) = @_; %stateTree = %{ &defineStateTreeA() }; if ( exists $stateTree{$state}){ my $requiredPermissions = $stateTree{$state}->getPermissionRequired(); if ($userPermissions > $requiredPermissions){ my $stateAction = $stateTree{$state}->getSubroutineToCall(); &$stateAction; } else { print("Permission Denied"); } } else { print("System Error: \"$state\" is Unknown State, or State does not exist. "); } }
The subroutine &executeState above will take care of the whole dispatch process for you, just supply the state from form data, and the current user’s permission level.
If the state exists as defined in &defineStateTree, then the user’s permission is checked. If they have a sufficient permission level, then the subroutine assigned to the state tree is called.
I’ve used this system to very rapidly build a web interface in Perl.
Note: All code is a work in progress. This is the code I’m working on, and it’s posted as I run into errors. This work is not suitable for reuse, and no guarantees are offered to its suitability for any function. If you do find something of interest, feel free to reuse it in whole or part.