CGI::Application::Muto - A wrapper for CGI::App with some cool features
# 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();
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.
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';
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:
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
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().
"^/products\/?(\/.*)$";
This attribute will take the method name and match it to the PATH_INFO.
sub foo : Runmode{ ... } #will match /foo
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 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();
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.
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.
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:
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.
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.
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.
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:
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.
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]
Muto will remove the value of this setting before attempting to do any matching on your methods.
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.
Uriel Lizama <uriel at baboonsoftware.com>
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.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
To install CGI::Application::Muto, copy and paste the appropriate command in to your terminal.
cpanm
cpanm CGI::Application::Muto
CPAN shell
perl -MCPAN -e shell install CGI::Application::Muto
For more information on module installation, please visit the detailed CPAN module installation guide.