NAME

MojoX::Automata - Call Mojo dispatchers in a finite automata manner

SYNOPSIS

# Real app flow

              no                                             yes
static file -------> detect user language ---> routes match ---+
     |                                              |          |
     |                                           no |          v
 yes |                   404 ---<-------------------+       get user
     |                    |        403 <------+                |
     |                    +---->----+         |   not ok       v
     |                              |         +-----<----- check access
     |                 not ok       v                          | ok
     +-----<---- 500 <---------- render view <+   ok           v
     |            ^        ok       |         +---------- routes dispatch
     +-----<------^------<----------+                          |
     v            |                                not ok      |
    end           +---------------<---------------<------------+


package MyApp;

use strict;
use warnings;

use base 'Mojolicious';

use MojoX::Automata;

__PACKAGE__->attr(automata => (default => sub { MojoX::Automata->new }));

__PACKAGE__->attr(
    config => (
        default => sub {
            {   languages      => [qw/ de en /],
                language_names => {
                    de => 'Deutsch',
                    en => 'English',
                },
            };
        }
    )
);

# This method will run for each request
sub dispatch {
    my ($self, $c) = @_;

    # Run automata
    $self->automata->run($c);
}

# This method will run once at server start
sub startup {
    my $self = shift;

    # Use our own context class
    $self->ctx_class('MyApp::Context');

    # Routes
    my $r = $self->routes;

    # Default route
    $r->route('/')->to(controller => 'root', action => 'default', id => 1)
      ->name('root');

    my $automata = $self->automata;

    $automata->namespace('MyApp::Automata');

    # Registering states

    # Detect if static file was requested
    $automata->register('S' => 'Static', static => $self->static);

    # Get page rendering time
    $automata->register('t_on'  => 'TimerOn');
    $automata->register('t_off' => 'TimerOff');

    # Detect language from the url (http://example.com/en/stuff) etc.
    $automata->register(
        'l'       => 'DetectLang',
        languages => $self->config->{languages}
    );

    # Mojo Routes (just the part that matches the route without calling
    # appropriate controller
    $automata->register('m' => 'Match', routes => $self->routes);

    # Mojo Routes controller call part
    $automata->register('d' => 'Dispatch', routes => $self->routes);

    # Checking access
    $automata->register('a' => 'CheckAccess');

    # Rendering view (no need to call $self->render in every controller)
    $automata->register('v' => 'RenderView');

    # Serving 404 error
    $automata->register('n' => 'NotFound');

    # Serving 403 error
    $automata->register('f' => 'Forbidden');

    # Serving 500 error
    $automata->register('e' => 'InternalError');

    # Get user from cookie etc
    $automata->register('u' => 'User');

    # End state
    $automata->register('E' => 'End');

    # Setting Start and End states
    $automata->start('S')->end('E');


    # Setting transitions between states

    # If it was a static file request end the automata, otherwise go to the
    # state of language detection
    $automata->add_path('S', 0 => 'l', 1 => 'E');

    # After language detection without any transitions switch to timer
    $automata->add_path('l' => 't_on');

    # After time got the routes matching state
    $automata->add_path('t_on' => 'm');

    # If route is find get user, otherwise switch to 404 state
    $automata->add_path('m', 0 => 'n', 1 => 'u');

    # After getting the user check his/her access to specific route (that is why
    # we didn't call controller just after the match
    $automata->add_path('u' => 'a');

    # If user has access call dispatcher, otherwise serve 403
    $automata->add_path('a', 0 => 'f', 1 => 'd');

    # Dispatcher (calling controller) can return different answers, go to
    # the right state after that
    $automata->add_path(
        'd',
        302 => 'E',
        500 => 'e',
        200 => 't_off',
        404 => 'n',
        403 => 'f'
    );

    # Turn off the timer
    $automata->add_path('t_off' => 'v');

    # Switch from 404 and 500 to the view
    $automata->add_path('n' => 'v');
    $automata->add_path('f' => 'v');

    # Render the view, if there were any rendering errors, switch to 500
    $automata->add_path('v', 0 => 'e', 1 => 'E');

    # End automata
    $automata->add_path('e' => 'E');
}

1;

# You can find all these states in the examples/ dir of the distribution

DESCRIPTION

MojoX::Automata is a Mojo finite automata dispatching mechanizm.

ATTRIBUTES

namespace

# Set namespace for the state classes
$self->namespace(__PACKAGE__);

start

Set Start state.

end

Set End state.

METHODS

MojoX::Automata inherits all methods from Mojo::Base and implements the following the ones.

register

Register a new state(s).

$automata->register('routes' => 'Routes');

add_path

Add path between states.

# Swith from state B<a> to B<a> without any return value checks.
$automata->add_path('r' => 'a');

# Swith from state B<s> to B<e> if state returns B<1>.
$automata->add_path('s', 1 => 'e');

run

# Run automata. Parameters provided will be passed on to every state.
$automata->run($c);

EXAMPLE

You can find example app ready to go in examples/ directory.

COPYRIGHT & LICENSE

Copyright 2008 Viacheslav Tikhanovskii, all rights reserved.

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.