Create a web-app with Cgi Appplication


Published on

How to create a simple Web application with CGI::Application Template::Toolkit and DBIx::Class

Published in: Technology
1 Comment
1 Like
No Downloads
Total Views
On Slideshare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide
  • First off, who is this talk for? A few years ago I had a conversation with a co-worker who was a competent programmer, with a degree in computer science. In all of his classes, he had never heard of MVC, and found my group’s method of programming to be confusing. He knew Perl, among other languages, so I decided to write this talk for perl programmers who don’t know MVC. Programmers who need to write web applications in their day to day work. Not necessarily heavy perl programmers, and they don’t necessarily have full access to the (machines/apache configs) their scripts are running on, therefore Catalyst is out of the question. As I said my coworker a few years back was an experienced programmer, he just didn’t know exactly what MVC meant, nor does he use Perl a lot. He understood Data how structures work, as well as databases and the complications that arise when using a database. Just not the in’s and out’s of a web app in perl My co-worker also knew about CPAN and knew how to leverage CPAN modules. He just didn’t know about these modules. Also this talk is for me nine years ago. I was a new programmer and didn’t know to separate the code into the MVC components.
  • Each of the modules can be used and tested alone, you are not required to commit to all three at once. You could do a rolling implementation where you pick one, say Template toolkit, and move to it one page at a time. This way you can test each little piece, and you could switch over the course of a year, each time you do a bug fix on a specific page, you can start to use TT on that page. Also, by themselves, each module is easy to understand. This doesn’t mean that we are tied to these three modules to accomplish these goals, I gave a version of this talk at YAPC08 where instead of DBIx::Class I used the outdated Class::DBI . If you are really tied to a specific ORM like Dave Rolsky’s FEY that you will hear about later today, you can use that instead. The point of this talk is to show one way to implement MVC
  • Catalyst is a great framework, but Catalyst is too big and not easily available on the systems that my employer uses, but it is easy for us to download and install these modules into a local directory without any elevated access, or access to the apache config files.
  • The model is the Database. If you have special database needs, or wanted to write your own SQL, that code would go into your Model section. If you were using a completely different database system besides SQL, that would go here. Its like saying: I don’t care how, just get the user with this Primary Key The View is for all the viewing of the data. If you are going to write HTML, that would go into the View. Outside of the Web Application World, the view is the User interface, and all that would go with the UI belongs here. The controller is the glue that takes information from the View, decides what to do with it, processes it, does inserts/deletes/reads/updates as required with the database, and then tells the VIEW what to do. This is where all the business logic belongs, doing things like data validation, and applying business rules
  • Who has seen code like this? While this isn’t “bad” code, it could be a lot better. Notice on the bottom we have HTML printing out in the while loop. While this code isn’t so bad, you can see where this is going. This is only one slide, and an html error would be very difficult to find once given enough “code rot”
  • Here I have pointed out that in addition to the Perl you have SQL, HTTP headers, and HTML. MVC here would move each of those into their own modules.
  • Here is another example of some code that isn’t bad, but can get messy quickly. Here we are explicitly looking to see if a user has filled out a form,with a “first name” and branching on that. Also there is sql mixed into the perl, but the html is abstracted away, which is good,
  • However submission_form() might have sql embedded in it, and if there is an HTML error it is still hard to find. MVC here would again break the HTML out into its own file, as well as the SQL. CGI::Application also handles the problem of “what function do I call” by having explicit run modes, using an html input field of ‘rm’
  • Do we have any questions regarding MVC
  • I am going to create a simple CGI::Application, there are six files that I will need to create my webapp. The .cgi file will contain config info, and will kick off the Controller. The Controller is The View files will be TemplateToolkit html files The Model is
  • Here is the most basic helloworld.cgi you can get. We use the lib, create the object, and then run the object.
  • This is a little more advanced in that this cgi is passing a few configuration variables to the application Module. Here I am basically setting up a config hash to pass to template toolkit that will prepend and postpend the two given files. I am also passing in a famous string.
  • By treating the .cgi file as a config file, you make it very easy to setup test scripts/environments. Just put all the production specific information in this file and replace it with the test info including a change to @inc; My office has taken to adding a production flag to the .cgi scripts so that things like overridding of email addresses for testing doesn’t need to be taken out before migrating the code to production.
  • This here is the setup for your basic Controller module. Here we are calling run modes with two modes, mode 1 and mode 2, and they call the functions start() and sec_page() respectively. The next line, we are deciding what runmode to default to, if no runmode is given.
  • Runmodes are key to understanding how CGI::App work. Cgi::app looks for a query parameter named ‘rm’ and calls the corresponding function in the hash here. You can set a hidden variable in your html that will determine the next runmode.
  • This is start() the default runmode that we created in the previous slide. As you can see, we initialize some variables, and then call tt->process() to get the HTML, and we return that. At this point the CGI::App module will take the HTML and send it to the browser along with the HTML headers.
  • If there is anything you need to do before executing any runmode, there is a prerun that will allow you do do things like creating a database handle, or doing some standard logging.
  • Now we have moved onto MODEL. This is the Database table that we are using. We have four fields, the first is an autonumber, and a primary key.
  • Here is the ClassDBI file to interface with the database.
  • Here is the DBIx::Class file to interface with the database. DBIx::Class also allows One to many relationships to be represented but that is beyond the scope of building a simple web app.
  • This is how you use DBIx::Class inside the Controller. You call a retrieve, and then pass the documents to Template toolkit.
  • Here is the basic insert syntax for DBIx::Class
  • There are some things that you still need to do that might be offered for free from a heavier MVC like catalyst. Because this is a lite framework you still need to explicitly open your database, and manually do inserts/updates/deletes. When you trade off a large framework for a smaller light framework you need to make compromises.
  • TT is the View component in our MVC framework. I chose TT for my example because that is what my office uses. Yes you can use HTML::Toolkit or just about any other type of templating system. I chose TT for my presentation here. These files are much simpler for folks like Web designers to manipulate and give back to you. Again this is the strength of MVC: your web person doesn’t need to understand how the file interacts with perl, they just need to make the html.
  • This will print an HTML page with just the scalar var_name printed out. Pretty simple
  • Here we have a for loop iterating over a list of users.
  • Here we are using the hash notation. TT is smart because it figures out if it needs to dereference something for you, so you don’t have to. If your code required an arrow to dereference the variable, your html gurus don’t need to worry about any of that with TT.
  • Here we have an array of users
  • There are a lot more modules available on CPAN for CGI::Application here are a few
  • Create a web-app with Cgi Appplication

    1. 1. How to create a simple Web application with CGI::Application Template::Toolkit and DBIx::Class Leonard Miller February 7, 2009
    2. 2. Who is this talk for? <ul><li>Need to write web applications </li></ul><ul><li>Don’t want/cannot have a heavy framework </li></ul><ul><li>Experienced Programmers. </li></ul><ul><li>Know about CPAN, how to learn from CPAN’s documentation. </li></ul><ul><li>Newer programmers that could use a new/better way to organize their code. </li></ul>
    3. 3. Why these three Modules? <ul><li>Separate out the code to MVC Pieces </li></ul><ul><li>Each can be used/tested alone </li></ul><ul><li>The modules themselves are easy to use and understand </li></ul>
    4. 4. Why not Catalyst? <ul><li>mod_perl/fastcgi: You don’t always have the access on the machine to get Catalyst to work. </li></ul><ul><li>CGI::Application is a ‘lite’ framework, and as such is much smaller. </li></ul><ul><li>Not as big and scary. </li></ul><ul><li>Trivial to install in a local ~/lib dir </li></ul>
    5. 5. What is MVC <ul><li>MVC stands for Model-View-Controller </li></ul>
    6. 6. What is MVC <ul><li>MVC breaks the work into three parts </li></ul><ul><li>Model - Short for database model. DBIx::Class does all the database work: inserts/queries. </li></ul><ul><li>View - Template::Toolkit does all the view/html work. </li></ul><ul><li>Controller - CGI::Application holds all the logic to glue the Model and the View together. </li></ul>
    7. 7. What is MVC <ul><li>Who has seen code like this: </li></ul><ul><li>use DBI; </li></ul><ul><li>my $sql = &quot;select * from users&quot;; </li></ul><ul><li>my $dbh = DBI->connect( $ds, $un, $pw ); </li></ul><ul><li>my $sth = $dbh->prepare($sql); </li></ul><ul><li>$sth->execute(); </li></ul><ul><li>print &quot;Content-type: text/html &quot;; </li></ul><ul><li>while (my $h = $sth->fetchrow_hashref()) </li></ul><ul><li>{ </li></ul><ul><li>print ”Name is:&quot;.$h->{'first_name'}.&quot;<br> &quot;; </li></ul><ul><li>} </li></ul>
    8. 8. What is MVC <ul><li>Who has seen code like this: </li></ul><ul><li>use DBI; </li></ul><ul><li>my $sql = &quot;select * from users&quot; ; </li></ul><ul><li>my $dbh = DBI->connect( $ds, $un, $pw ); </li></ul><ul><li>my $sth = $dbh->prepare($sql); </li></ul><ul><li>$sth->execute(); </li></ul><ul><li>print &quot;Content-type: text/html &quot; ; </li></ul><ul><li>while (my $h = $sth->fetchrow_hashref()) </li></ul><ul><li>{ </li></ul><ul><li>print ”Name is:&quot;.$h->{'first_name'}.&quot;<br> &quot; ; </li></ul><ul><li>} </li></ul>
    9. 9. What is MVC <ul><li>Who has seen code like this: </li></ul><ul><li>my $q = new CGI; </li></ul><ul><li>if ($q-> param('first_name' eq ''){ </li></ul><ul><li>print input_form(); </li></ul><ul><li>} </li></ul><ul><li>else{ </li></ul><ul><li>my $sql = &quot;insert into users ...&quot;; </li></ul><ul><li>my $sth = $dbh->prepare($sql); </li></ul><ul><li>$sth->execute(); </li></ul><ul><li>print submission_form(); </li></ul><ul><li>} </li></ul>
    10. 10. What is MVC <ul><li>Who has seen code like this: </li></ul><ul><li>my $q = new CGI; </li></ul><ul><li>if ($q-> param('first_name' eq ''){ </li></ul><ul><li>print input_form(); </li></ul><ul><li>} </li></ul><ul><li>else{ </li></ul><ul><li>my $sql = &quot;insert into users ...&quot; ; </li></ul><ul><li>my $sth = $dbh->prepare($sql); </li></ul><ul><li>$sth->execute(); </li></ul><ul><li>print submission_form(); </li></ul><ul><li>} </li></ul>
    11. 11. What is MVC <ul><li>Any questions regarding what MVC is? </li></ul>
    12. 12. CGI::Application <ul><li>A sample program: </li></ul><ul><li>Helloworld.cgi <- config info </li></ul><ul><li> <- controller </li></ul><ul><li>Html files: <- View </li></ul><ul><ul><li>header.html </li></ul></ul><ul><ul><li>body.html </li></ul></ul><ul><ul><li>footer.html </li></ul></ul><ul><li>DB/ <- Model </li></ul><ul><ul><li>DB/Main/ </li></ul></ul><ul><ul><li>DB/Main/ </li></ul></ul><ul><ul><li>DB/Main/ </li></ul></ul>
    13. 13. CGI::Application <ul><li>helloworld.cgi: </li></ul><ul><li>use HelloWorldCgiApp; </li></ul><ul><li>my $helloworld = HelloWorldCgiApp->new(); </li></ul><ul><li>$helloworld->run(); </li></ul>
    14. 14. CGI::Application <ul><li>helloworld.cgi (with config info): </li></ul><ul><li>use HelloWorldCgiApp; </li></ul><ul><li>my $helloworld = HelloWorldCgiApp->new </li></ul><ul><li>( </li></ul><ul><li>PARAMS => </li></ul><ul><li>{ </li></ul><ul><li>tt_config => { </li></ul><ul><li>INCLUDE_PATH => &quot;.&quot;, </li></ul><ul><li>PRE_PROCESS => 'header.html', </li></ul><ul><li>POST_PROCESS => 'footer.html', </li></ul><ul><li>}, </li></ul><ul><li>hw_string => &quot;Hello world!&quot;, </li></ul><ul><li>}, </li></ul><ul><li>); </li></ul><ul><li>$helloworld->run(); </li></ul>
    15. 15. CGI::Application <ul><li>helloworld.cgi (with config info): </li></ul><ul><li>use lib “~/testdir”; </li></ul><ul><li>use HelloWorldCgiApp; </li></ul><ul><li>my $helloworld = HelloWorldCgiApp->new </li></ul><ul><li>( </li></ul><ul><li>PARAMS => </li></ul><ul><li>{ </li></ul><ul><li>tt_config => { </li></ul><ul><li>INCLUDE_PATH => &quot;.&quot;, </li></ul><ul><li>PRE_PROCESS => ‘ test/ header.html', </li></ul><ul><li>POST_PROCESS => ‘ test/ footer.html', </li></ul><ul><li>}, </li></ul><ul><li>hw_string => &quot;Hello test world!&quot;, </li></ul><ul><li>}, </li></ul><ul><li>); </li></ul><ul><li>$helloworld->run(); </li></ul>
    16. 16. CGI::Application <ul><li>HelloWorldCgiApp (continued): </li></ul><ul><li>package HelloWorldCgiApp; </li></ul><ul><li>use base 'CGI::Application'; </li></ul><ul><li>use Template; </li></ul><ul><li>sub setup { </li></ul><ul><li>my $self = shift; </li></ul><ul><li>$self->run_modes( 'mode1' => 'start’, </li></ul><ul><li>'mode2' => 'sec_page' </li></ul><ul><li>); </li></ul><ul><li>$self->start_mode('mode1'); </li></ul><ul><li>} </li></ul>
    17. 17. CGI::Application <ul><li>HelloWorldCgiApp (continued): </li></ul><ul><li>package HelloWorldCgiApp; </li></ul><ul><li>use base 'CGI::Application'; </li></ul><ul><li>use Template; </li></ul><ul><li>sub setup { </li></ul><ul><li>my $self = shift; </li></ul><ul><li>$self->run_modes( 'mode1' => 'start’, </li></ul><ul><li>'mode2' => 'sec_page' </li></ul><ul><li>); </li></ul><ul><li>$self->start_mode('mode1'); </li></ul><ul><li>} </li></ul>$q -> param (‘ rm ’);
    18. 18. CGI::Application <ul><li>HelloWorldCgiApp (continued): </li></ul><ul><li>sub start { </li></ul><ul><li>my $self = shift; </li></ul><ul><li>my $tt_config = $self->param(‘tt_config’) </li></ul><ul><li>my $tt = Template->new( $tt_config ); </li></ul><ul><li>$tt->process('body.html', </li></ul><ul><li>{ </li></ul><ul><li>hwstr => 'hi world!!!', </li></ul><ul><li>}, </li></ul><ul><li>$html); </li></ul><ul><li>return $html; </li></ul><ul><li>} </li></ul>
    19. 19. CGI::Application <ul><li>HelloWorldCgiApp (continued): </li></ul><ul><li>sub cgiapp_prerun </li></ul><ul><li>{ </li></ul><ul><li>my ($self, $runmode) = @_; </li></ul><ul><li>my $q = $self->query; </li></ul><ul><li>#things you need to run every time </li></ul><ul><li>#input validation etc. </li></ul><ul><li>#logging </li></ul><ul><li>} </li></ul>
    20. 20. DBIx::Class <ul><li>The mysql table: </li></ul><ul><li>CREATE TABLE users ( </li></ul><ul><li>user_id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, </li></ul><ul><li>first_name varchar(75) NOT NULL, </li></ul><ul><li>last_name varchar(75) NOT NULL, </li></ul><ul><li>country_code CHAR(2) NULL </li></ul><ul><li>); </li></ul><ul><li>+---------+------------+-----------+--------------+ </li></ul><ul><li>| user_id | first_name | last_name | country_code | </li></ul><ul><li>+---------+------------+-----------+--------------+ </li></ul><ul><li>| 1 | joe | user | US | </li></ul><ul><li>| 2 | Steve | Jobs | US | </li></ul><ul><li>| 3 | Bill | Gates | US | </li></ul><ul><li>| 4 | Larry | Wall | US | </li></ul><ul><li>+---------+------------+-----------+--------------+ </li></ul>
    21. 21. DBIx::Class <ul><li>DB/ </li></ul><ul><li>package DB::Main; </li></ul><ul><li>use base qw/DBIx::Class::Schema/; </li></ul><ul><li>__PACKAGE__->load_classes(); </li></ul><ul><li>1; </li></ul>
    22. 22. DBIx::Class <ul><li>DB/Main/ </li></ul><ul><li>package DB::Main::User; </li></ul><ul><li>use base qw/DBIx::Class/; </li></ul><ul><li>__PACKAGE__-> load_components(qw/PK::Auto Core/); </li></ul><ul><li>__PACKAGE__->table('users'); </li></ul><ul><li>__PACKAGE__->add_columns(qw/ user_id first_name last_name country_code /); </li></ul><ul><li>__PACKAGE__-> set_primary_key('user_id'); </li></ul>
    23. 23. DBIx::Class <ul><li>Using the DBIx::Class Module </li></ul><ul><li> </li></ul><ul><li>use DB::Main; </li></ul><ul><li>my $schema = DB::Main-> connect('dbi:mysql:db','user', 'password'); </li></ul><ul><li>my @users = $schema->resultset('User')->all; </li></ul><ul><li>my $users_rs = $schema->resultset('User'); </li></ul><ul><li>my $user = $users_rs ->next; </li></ul><ul><li>$tt->process('body.html', </li></ul><ul><li>{ </li></ul><ul><li>users => [ @users ], </li></ul><ul><li>user => $ user, </li></ul><ul><li>}, </li></ul><ul><li>$html); </li></ul>
    24. 24. DBIx::Class <ul><li>Using the Module – inserts: </li></ul><ul><li>my $new_user = $schema-> resultset('User')->new({ </li></ul><ul><li>last_name => $last_name, </li></ul><ul><li>first_name => $first_name, </li></ul><ul><li>}); </li></ul><ul><li>$new_user->insert; </li></ul>
    25. 25. DBIx::Class <ul><li>Things that you still need to do: </li></ul><ul><li>Verify your data -- this is good practice under any circumstances </li></ul><ul><li>Open your database connection </li></ul><ul><li>Error handling -- did the DB connection open? Did the sql succeed? Unlike some larger frameworks, CGI::Application will not do the error checking for you. </li></ul><ul><li>It’s a lite framework, so you still need to do some work </li></ul>
    26. 26. Template::Toolkit <ul><li>Why use Template::Toolkit? </li></ul><ul><ul><li>Common ‘look and feel’ templates. </li></ul></ul><ul><ul><ul><li>Easy to change for those people who are better at design than we are. </li></ul></ul></ul><ul><ul><li>What type of data is It good for? </li></ul></ul><ul><ul><ul><li>arrays </li></ul></ul></ul><ul><ul><ul><li>hashes </li></ul></ul></ul><ul><ul><ul><li>scalars </li></ul></ul></ul>
    27. 27. Template::Toolkit <ul><li>Any html file is a TT file! </li></ul><ul><li>Simplest usage for a scalar: </li></ul><ul><li><html> </li></ul><ul><li><body> </li></ul><ul><li>[% var_name %] </li></ul><ul><li></body> </li></ul><ul><li></html> </li></ul>
    28. 28. Template::Toolkit <ul><li>Usage for an Array where users is an array: </li></ul><ul><li><body> </li></ul><ul><li>[% FOREACH item IN users %] </li></ul><ul><li>[% item %]<br /> </li></ul><ul><li>[% END %] </li></ul><ul><li></body> </li></ul>
    29. 29. Template::Toolkit <ul><li>Usage where item is a hash: </li></ul><ul><li><body> </li></ul><ul><li>[% item.user_id %] [% item.first_name %] </li></ul><ul><li>[% item.last_name %] [% %] <br /> </li></ul><ul><li></body> </li></ul>
    30. 30. Template::Toolkit <ul><li>Usage for an Array where users is an array of hashes (like an array of rows from a database): </li></ul><ul><li><body> </li></ul><ul><li>[% FOREACH item IN users %] </li></ul><ul><li>[% item.user_id %] [% item.first_name %] </li></ul><ul><li>[% item.last_name %] [% %]<br /> </li></ul><ul><li>[% END %] </li></ul><ul><li></body> </li></ul>
    31. 31. Template::Toolkit <ul><li>Usage for an Array where users is an array of hashes (like an array of rows from a database): </li></ul><ul><li><body> </li></ul><ul><li><form> </li></ul><ul><li><input type=“hidden” name= “rm” value=“page_2” </li></ul><ul><li>[% FOREACH item IN users %] </li></ul><ul><li>... </li></ul><ul><li>[% END %] </li></ul><ul><li><input type=“submit”> </li></ul><ul><li></form> </li></ul><ul><li></body> </li></ul>
    32. 32. There’s more available <ul><li>CGI::Application::Plugin::AbstractCallback </li></ul><ul><li>CGI::Application::Plugin::ActionDispatch </li></ul><ul><li>CGI::Application::Plugin::ActionDispatch::Attributes </li></ul><ul><li>CGI::Application::Plugin::AnyCGI </li></ul><ul><li>CGI::Application::Plugin::AnyTemplate </li></ul><ul><li>CGI::Application::Plugin::AnyTemplate::Base </li></ul><ul><li>CGI::Application::Plugin::AnyTemplate::ComponentHandler </li></ul><ul><li>CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplate </li></ul><ul><li>CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplateExpr </li></ul><ul><li>CGI::Application::Plugin::AnyTemplate::Driver::HTMLTemplatePluggable </li></ul><ul><li>CGI::Application::Plugin::AnyTemplate::Driver::Petal </li></ul><ul><li>CGI::Application::Plugin::AnyTemplate::Driver::TemplateToolkit </li></ul><ul><li>CGI::Application::Plugin::Apache </li></ul><ul><li>CGI::Application::Plugin::Apache2::Request </li></ul><ul><li>CGI::Application::Plugin::Apache::Request </li></ul><ul><li>CGI::Application::Plugin::Authentication </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::Authen::Simple </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::CDBI </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::DBI </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::DBIC </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::Dummy </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::Filter::crypt </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::Filter::lc </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::Filter::md5 </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::Filter::sha1 </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::Filter::strip </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::Filter::uc </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::Generic </li></ul><ul><li>CGI::Application::Plugin::Authentication::Driver::HTPasswd </li></ul><ul><li>CGI::Application::Plugin::Authentication::Store </li></ul><ul><li>CGI::Application::Plugin::Authentication::Store::Cookie </li></ul><ul><li>CGI::Application::Plugin::Authentication::Store::Session </li></ul><ul><li>CGI::Application::Plugin::Authorization </li></ul><ul><li>CGI::Application::Plugin::Authorization::Driver </li></ul><ul><li>CGI::Application::Plugin::Authorization::Driver::DBI </li></ul><ul><li>CGI::Application::Plugin::Authorization::Driver::Dummy </li></ul><ul><li>CGI::Application::Plugin::Authorization::Driver::Generic </li></ul><ul><li>CGI::Application::Plugin::Authorization::Driver::HTGroup </li></ul><ul><li>CGI::Application::Plugin::Authorization::Driver::SimpleGroup </li></ul><ul><li>CGI::Application::Plugin::AutoRunmode </li></ul><ul><li>CGI::Application::Plugin::AutoRunmode::FileDelegate </li></ul><ul><li>CGI::Application::Plugin::BREAD </li></ul><ul><li>CGI::Application::Plugin::BrowserDetect </li></ul><ul><li>CGI::Application::Plugin::CAPTCHA </li></ul><ul><li>CGI::Application::Plugin::CHI </li></ul><ul><li>CGI::Application::Plugin::Cache::Adaptive </li></ul><ul><li>CGI::Application::Plugin::CaptureIO </li></ul><ul><li>CGI::Application::Plugin::CompressGzip </li></ul><ul><li>CGI::Application::Plugin::Config::Any </li></ul><ul><li>CGI::Application::Plugin::Config::Context </li></ul><ul><li>CGI::Application::Plugin::Config::General </li></ul><ul><li>CGI::Application::Plugin::Config::IniFiles </li></ul><ul><li>CGI::Application::Plugin::Config::Simple </li></ul><ul><li>CGI::Application::Plugin::Config::YAML </li></ul><ul><li>CGI::Application::Plugin::ConfigAuto </li></ul><ul><li>CGI::Application::Plugin::DBH </li></ul><ul><li>CGI::Application::Plugin::DBIProfile </li></ul><ul><li>CGI::Application::Plugin::DBIProfile::Data </li></ul><ul><li>CGI::Application::Plugin::DBIProfile::Graph::GDGraphInline </li></ul><ul><li>CGI::Application::Plugin::DBIProfile::Graph::HTML </li></ul><ul><li>CGI::Application::Plugin::DBIProfile::Graph::HTML::Horizontal </li></ul><ul><li>CGI::Application::Plugin::DBIProfile::Graph::HTMLBarGraph </li></ul><ul><li>CGI::Application::Plugin::DBIProfile::Graph::SVGTT </li></ul><ul><li>CGI::Application::Plugin::DebugMessage </li></ul><ul><li>CGI::Application::Plugin::DebugScreen </li></ul><ul><li>CGI::Application::Plugin::DevPopup </li></ul><ul><li>CGI::Application::Plugin::DevPopup::HTTPHeaders </li></ul><ul><li>CGI::Application::Plugin::DevPopup::Log </li></ul><ul><li>CGI::Application::Plugin::DevPopup::Timing </li></ul><ul><li>CGI::Application::Plugin::Email </li></ul><ul><li>CGI::Application::Plugin::Eparam </li></ul><ul><li>CGI::Application::Plugin::ErrorPage </li></ul><ul><li>CGI::Application::Plugin::Feedback </li></ul><ul><li>CGI::Application::Plugin::FillInForm </li></ul><ul><li>CGI::Application::Plugin::Flash </li></ul><ul><li>CGI::Application::Plugin::FormState </li></ul><ul><li>CGI::Application::Plugin::FormValidator::Simple </li></ul><ul><li>CGI::Application::Plugin::Forward </li></ul><ul><li>CGI::Application::Plugin::HTCompiled </li></ul><ul><li>CGI::Application::Plugin::HTDot </li></ul><ul><li>CGI::Application::Plugin::HTMLPrototype </li></ul><ul><li>CGI::Application::Plugin::HelpMan </li></ul><ul><li>CGI::Application::Plugin::HtmlTidy </li></ul><ul><li>CGI::Application::Plugin::I18N </li></ul><ul><li>CGI::Application::Plugin::JSON </li></ul><ul><li>CGI::Application::Plugin::LinkIntegrity </li></ul><ul><li>CGI::Application::Plugin::LogDispatch </li></ul><ul><li>CGI::Application::Plugin::Mason </li></ul><ul><li>CGI::Application::Plugin::Menu </li></ul><ul><li>CGI::Application::Plugin::MessageStack </li></ul><ul><li>CGI::Application::Plugin::MetadataDB </li></ul><ul><li>CGI::Application::Plugin::Output::XSV </li></ul><ul><li>CGI::Application::Plugin::PageBuilder </li></ul><ul><li>CGI::Application::Plugin::ParsePath </li></ul><ul><li>CGI::Application::Plugin::Phrasebook </li></ul><ul><li>CGI::Application::Plugin::ProtectCSRF </li></ul><ul><li>CGI::Application::Plugin::RateLimit </li></ul><ul><li>CGI::Application::Plugin::Redirect </li></ul><ul><li>CGI::Application::Plugin::RequireSSL </li></ul><ul><li>CGI::Application::Plugin::Routes </li></ul><ul><li>CGI::Application::Plugin::RunmodeDeclare </li></ul><ul><li>CGI::Application::Plugin::Session </li></ul><ul><li>CGI::Application::Plugin::Stash </li></ul><ul><li>CGI::Application::Plugin::Stream </li></ul><ul><li>CGI::Application::Plugin::TT </li></ul><ul><li>CGI::Application::Plugin::TT::LastModified </li></ul><ul><li>CGI::Application::Plugin::TemplateRunner </li></ul><ul><li>CGI::Application::Plugin::Thumbnail </li></ul><ul><li>CGI::Application::Plugin::TmplInnerOuter </li></ul><ul><li>CGI::Application::Plugin::ValidateRM </li></ul><ul><li>CGI::Application::Plugin::View::HTML::Template </li></ul><ul><li>CGI::Application::Plugin::ViewCode </li></ul><ul><li>CGI::Application::Plugin::YAML </li></ul>
    33. 33. <ul><li>Questions? </li></ul>
    34. 34. <ul><li>Thank you </li></ul>Leonard Miller February 7th Frozen Perl 2009
    1. A particular slide catching your eye?

      Clipping is a handy way to collect important slides you want to go back to later.