NAME

CGI::Application::Muto - A wrapper for CGI::App with some cool features

SYNOPSIS

  # In "App.pm"...
  package App;
  use base 'CGI::Application::Muto';

  sub main_index : Default{ ... }

  sub user_profile : Regex('profile/user/(\d+)'){ ... }

  sub terms : Path('page/terms'){ ... }

  1;


  ### In "app.cgi"...
  use App;
  my $webapp = App->new(
                    PARAMS => {
                          #optional config attributes
                    }
                );

  $webapp->run();

INTRODUCTION

CGI::Application::Muto is my attempt to create a wrapper around CGI::Application to provide some additional functionality. Muto comes from the latin for to change, alter/exchange.

I recommend you read the CGI::Application documentation as I will only explain here the new functions this module brings into the table.

DESCRIPTION

You should use CGI::Application::Muto so that your main application module it's implemented as a sub-class, in the following way:

  package App;
  use base 'CGI::Application::Muto';

Application States

In CGI::App you have to register your run modes on the setup() function. The run modes are but a map that allows CGI::App to know which method to call upon a specific CGI request.

Muto provides a way to sweeten that process by approaching in a way similar to Catalyst. You will be able to use attributes within your function declarations that will allow Muto to correctly map the request to the method.

This functionality has been basically taken from CGI::Application::Plugin::ActionDispatch by Jason Yates.

Muto doesn't rely on the run_modes() function allthough you can still use it. Instead as with Catalyst you should use specific function attributes that will tell Muto the state you wish the function to represent.

When a request is received by Muto it will try to match the PATH_INFO against all your function attributes, smartly calling the correct method.

You will have four different ways to be able to do so:

Regex

Will use regular expressions to match against the PATH_INFO. In the case that you use capturing parentheses, the captured that will be available for you via the action_args() method.

  sub user_profile : Regex('user/(\d+)'){ }

All the Regex methods will take priority, in case that a Path or Regex method match, the Regex method will be used

Path

This attribute works like a shortcut of Regex. For example:

  #Path: shirt/small
  sub view_shirt : Path('shirt/'){
      my $self = shift;

      my ($shirt_size) = $self->action_args();

      ...
  }

Which is basically the same thing if we have done:

  sub view_shirt : Regex('^/shirt/(\w+)'){
      my $self = shift;

      my ($shirt_size) = $self->action_args();

      ...
  }

For those that care, the Path('products/') will be converted to the regular expression "^/products\/?(\/.*)$"; then split('/') is run on the captured value and stored in action_args().

Runmode

This attribute will take the method name and match it to the PATH_INFO.

  sub foo : Runmode{ ... } #will match /foo

Default

If no match is found then Muto will attempt to run the method with this attribute, which can be considered an equivalent functionality to start_mode().

  sub main_index : Default{ ... }

Path arguments with action_args()

Path and Regex can capture arguments provided in the PATH_INFO. You can access the captured arguments by invoking the action_args() method on Muto, which will return an array of the captured arguments.

  my @captured_args = $app->action_args();

Controllers

As CGI::App doesn't provide and easy and straight-forward way to organize your different application states or methods over different files, Muto attempts to fix this.

Muto uses and approach very similar to Catalyst, in which you are able to add your Controllers which will be auto-discovered by Muto and made accesible for your application.

Muto will look for any modules found under the Muto::App::Controller namespace, for example:

  # In "Muto/App/Controller/Books.pm"

  package Muto::App::Controller::Books;

  sub App::book_browse : Path('book/browse'){ ... }

  sub App::book_read : Regex('book/read/(\d+)'){

      my $self = shift;

      my ($book_id) = $self->action_args();

      ...

  }

  1;

Your methods should be declared on the namespace of your main application. In this example that is App.

Now your controller will be immediately available and accesed by your application.

NOTE: Since all methods are imported into your application namespace, you have to be careful with name clash of your method names.

Protected Run-Modes

Muto gives you the availability of protecting certain run modes. Since I wanted this to be as open as possible, Muto doesn't do any authentication, but instead calls a method that should return true or false if the user has been authenticated.

This gives you complete control over how you choose to authenticate or open access to your protected run modes.

You should declare your protected run modes on cgiapp_init(), to do so:

  sub cgiapp_init {

      my $self = shift;

      $self->protect_rm(
                'Regex' => '^protected(.*)',
                'auth_check' => \&_check_user_permissions,
                'login_page' => 'http://mysite.com/login',
            );

  }

The protect_rm() method receives three different arguments:

(Regex|Path)

You have to send either a Regex or Path with the expression as it's value. This works the same as with the method attributes, except that Path matches exactly the expression against the PATH_INFO.

auth_check

This should be a reference to a sub-routine with the authentication check. This function should return 1 if the user is authenticated, and 0 if it's not. If the user is authenticated then it will gain access to the application state, otherwise it will be redirected to the login page.

login_page

The URL to which your users should be redirected in case they haven't been authenticated.

You can add as many protected run-modes as you desire, each with it's own authentication if you choose to do so. Simply call protect_rm() for each protected run-mode or area you want to add.

  sub cgiapp_init {

      my $self = shift;

      #protects everything under /protected
      $self->protect_rm(
                'Regex' => '^protected(.*)',
                'auth_check' => \&_check_user_permissions,
                'login_page' => 'http://mysite.com/login',
            );

      #protects only /ultra/secret/text
      $self->protect_rm(
                'Path' => 'ultra/secret/text',
                'auth_check' => \&_check_higher_permissions,
                'login_page' => 'http://mysite.com/ultra/login',
            );

  }

Since Muto realies completely on your auth_check method to give or deny access, it's really important that you verify that your method returns the correct value.

You can use the different CGI::Application::Plugins available for this to achieve this purposes.

Advanced Configuration

When you create a new Muto object you can send it some arguments to tweak the way Muto behaves.

  use My::App;

  my $webapp = App->new(
                    PARAMS => {
                        'controller_path' => 'My::App::Controllers',
                        'path_env' => 'REQUEST_URI',
                        'path_prefix' => '/myapp',
                    }
                );

  $webapp->run();

The three configuration settings you can modify are:

controller_path

Muto will search for default the Muto::App::Controller namespace for new controllers to import. With this setting you can add a new namespace to search for. This will be additional to the default one.

path_env

For default Muto will use $ENV{PATH_INFO} as the PATH_INFO value to render all the appropiate application states:

  http://mysite.com/index.cgi/path/here

I find it that you may want to do something nicer using mod_rewrite, so with this setting you can actually tell Muto which %ENV variable to use, for example, if you use 'REQUEST_URI' then you can do:

  http://mysite.com/path/here

You may need to do this in conjunction of mod_rewrite with a .htaccess that looks something like this:

    RewriteEngine On

    RewriteCond %{SCRIPT_FILENAME} !-f
    RewriteCond %{SCRIPT_FILENAME} !-d

    RewriteRule (.*) /index.cgi [NC,QSA]
path_prefix

Muto will remove the value of this setting before attempting to do any matching on your methods.

BUGS

This distribution is currently in beta state, this means that it can have some bugs that I'm not yet aware of.

Please report any bugs or feature requests through the web interface at https://rt.cpan.org.

AUTHOR

Uriel Lizama <uriel at baboonsoftware.com>

ACKNOWLEDGMENTS

The method attributes feature of Muto is heavily based on the module CGI::Application::Dispatch by Jason Yates.

Many thanks to Matt S. Trout who took time from his hectic life to advice me on the directions I took.

Thanks to Marco A. Manzo (amnesiac) who was always avilable when I bugged him with suggestions.

COPYRIGHT

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