CGI::Ap...uctured(3)

πŸ“¦
version 0.007 Β· 2 versions
, in fedora 43

🌸 NAME 🌸

CGI::Application::Structured β€” bestie, it's a medium-weight, MVC, DB web micro-framework. That's the whole vibe. πŸ’…

This page is CGI::Ap...uctured(3), from the User Contributed Perl Documentation, and yes we're gonna get through all of it together. 🫢


✨ SYNOPSIS ✨

So picture this: a simple, medium-weight, MVC, DB web micro-framework, built right on top of CGI::Application. It takes tested, well-known plugins plus helper scripts and smooshes them into a rapid development environment. It's giving "I have a group project due and I refuse to suffer" energy. πŸ”₯

The bundled plugins mix these methods straight into your controller runmodes:

$c->forward(runmode)

$c->redirect(url)

$c->tt_param(name=>value)

$c->tt_process()

$c->schema()->resultset("Things")->find($id)

$c->resultset("Things)->search({color=>"red"})

$c->log->info('This also works')

my $value = $c->session->param('key')

my $conf_val = $c->cfg('field');

my $select = $c->superform->select(
                    name    => 'select',
                    default => 2,
                    values  => [ 0, 1, 2, 3 ],
                    labels  => {
                            0 => 'Zero',
                            1 => 'One',
                            2 => 'Two',
                            3 => 'Three'
                    }
            );

sub method: Runmode {my $c = shift; do_something();}

$c->fill_form( \$template )

my  $results = $ ->check_rm(
          'form_display','_form_profile')
          || return $c->check_rm_error_page;

Look at all those methods just existing in your runmodes. Iconic. ✨


πŸ’– DESCRIPTION πŸ’–

Okay so the author literally took to heart a recent comment by Mark Stosberg on the CGIApp mailing list, and it went a little something like this:

"Titanium is just one vision of what can be built on top of CGI::Application. Someone else could easily combine their own combination of CGI::Application and different favorite plugins, and publish that with a different name."

And honestly? That comment ate. πŸ“

CGI::Application::Structured, like Titanium, is an opinionated framework, based on CGI::Application. Both frameworks include a number of vetted CGI-App plugins, each of which are well documented and tested. C::A::Structured, however, takes the view that developer time and consistent project structures can often be more cost-effective than focusing on the highest performance on low cost hosting solutions. That being said β€” and this is important bestie β€” C::A::Structured can be deployed on CGI, FastCGI or mod_perl based on your needs. 🌈

C::A::Structured focuses on:

  • adequate performance under CGI, but with more focus speed of development.
  • a well-defined project structure with directories for model classes, controllers and view templates.
  • a powerful templating DSL via Template Toolkit integration.
  • a integrated Object Relational Mapper, DBIx::Class
  • no runmode configuration required.
  • integrated form building to simplify creation of complex HTML form elements.
  • clean url-to-controller mapping by default.

Not me listing all seven of those and being obsessed. πŸŽ€

C::A::Structured comes in two packages:

  • CGI::Application::Strutured β€” encapsulates the runtime environment.
  • CGI::Application::Structured::Tools β€” includes project creation and developemt scripts for developers.

CGI::Application::Structured::Tools are used to generate a micro-architecture and helper scripts that work within that structure to speed development and maintain project structure across development teams and projects. The helper scripts eliminate the tedium of error-prone manual creation of controllers, templates and database mappings and provide a recognizeable structural conventions that reduced stress on the developer when working across multiple apps, or working with other developers code on the same project. πŸ’œ

The first script that is used is 'cas-starter.pl'. This script is used to generate the initial project skeleton. The skeleton app contains a base controller class rather than runnable module as would be found in Titanium. Also generated is a default 'Home' controller subclass and a URL dispatcher that is customized to default to the Home controllers generated 'index' runmode.

cas-starter.pl also generates additional helper scripts in your projects 'scripts' subdirectory:

  • create_controller.pl
  • create_dbic_schema.pl

'create_controller.pl' is used by the developer to generate additional controller subclasses of your base module with a default 'index' runmode and a default TT template for that runmode.

'create_dbic_schema.pl' generates a DBIx::Class::Schema subclass and a set of resultset subclasses for your database. These Object Relational Maps (ORM) will greatly simplify and speed database assess and manipulation in the development process. πŸ”₯

Finally β€” and this is the part that's lowkey main character energy β€” CGI::Application::Structured aims to be as compatible as possible with Catalyst. Many plugins used in CGI::Application::Structured are also available for Catalyst, or are even defaults in Catalyst. If your projects needs grow in scope or scale to require Catalyst, porting much of your code will be very easy. πŸ‘‘


ngl that was a lot, but we're not even close to done. Part 2 is coming, bestie, don't go anywhere πŸ’–

πŸŽ€ CGI::Application::Structured Tutorial πŸŽ€

okay bestie, so in this tutorial we're gonna build a whole lil database-driven web app using CGI::Application::Structured πŸ’– β€” mostly to show off how the starter and helper scripts work, plus the absolute bare minimum config you need. no gatekeeping here.

CGI::Application::Structured kinda assumes you've already got a database you wanna put on the web 🌸. If you do, use it for this tutorial! If you don't, no stress β€” just hop down to the "Create The Example Database" section at the bottom of this page before you start. 🫢


🧚 Installation

You're gonna need two things, bestie. Install CGI::Application::Structured (that's the runtime requirements) and also CGI::Application::Structured::Tools (that's your whole dev environment, iconic).

~/dev$ sudo cpan
cpan> install CGI::Application::Structured
      ... ok
cpan> install CGI::Application::Structured::Tools
      ... ok
cpan> exit

✨ Creating A Project

~/dev$ cas-starter.pl --module=MyApp1 \
                           --author=gordon \
                           --email="vanamburg@cpan.org" \
                           --verbose
Created MyApp1
Created MyApp1/lib
Created MyApp1/lib/MyApp1.pm                      # YOUR *CONTROLLER BASE CLASS* !
Created MyApp1/t
Created MyApp1/t/pod-coverage.t
Created MyApp1/t/pod.t
Created MyApp1/t/01-load.t
Created MyApp1/t/test-app.t
Created MyApp1/t/perl-critic.t
Created MyApp1/t/boilerplate.t
Created MyApp1/t/00-signature.t
Created MyApp1/t/www
Created MyApp1/t/www/PUT.STATIC.CONTENT.HERE
Created MyApp1/templates/MyApp1/C/Home
Created MyApp1/templates/MyApp1/C/Home/index.tmpl # DEFAULT HOME PAGE TEMPLATE
Created MyApp1/Makefile.PL
Created MyApp1/Changes
Created MyApp1/README
Created MyApp1/MANIFEST.SKIP
Created MyApp1/t/perlcriticrc
Created MyApp1/lib/MyApp1/C                       # YOUR CONTROLLERS GO HERE
Created MyApp1/lib/MyApp1/C/Home.pm               # YOUR *DEFAULT CONTROLLER SUBCLASS*
Created MyApp1/lib/MyApp1/Dispatch.pm             # YOUR CUSTOM DISPATCHER
Created MyApp1/config
Created MyApp1/config/config-dev.pl               # YOU CONFIG -- MUST BE EDITED BY YOU!
Created MyApp1/script
Created MyApp1/script/create_dbic_schema.pl       # IMPORTANT HELPER SCRIPT
Created MyApp1/script/create_controller.pl        # ANOTHER IMPORTANT HELPER SCRIPT.
Created MyApp1/server.pl                          # SERVER USES YOUR CUSTOM DISPATCH.PM
Created MyApp1/MANIFEST
Created starter directories and files

🌸 Configure Your Database

So here's the thing β€” CGI::Application::Structured is database centric in some sense 😌 and it genuinely expects you to have a database. Before you run your app via server.pl, you gotta configure your database access. no skipping this step, bestie.

The example config gets generated at MyApp1/config/config.pl. Here's what's inside:

use strict;
my %CFG;

$CFG{db_dsn} = "dbi:mysql:myapp_dev";
$CFG{db_user} = "root";
$CFG{db_pw} = "root";
$CFG{tt2_dir} = "templates";
return \%CFG;
okay so using the root account like this is literally shown here as a worst-practice πŸ’€ do NOT do this. You should customize the file and supply the correct database dsn, user and passwords for your database. periodt.

If you don't have a database and wanna use an example, go read "Create Example Database" below before you keep going. 🌈

The configuration file gets found automatically when you're running with the built in server πŸŽ€ β€” but when you deploy your application you may want, or need, to update the config file location in lib/MyApp1/Dispatch.pm so it points at your production config file.

For the advanced config tea, see: CGI::Application::Plugin::ConfigAuto


πŸ’… Generate A DBIx::Class Schema For Your Database

From your project root directory, run the helper script to generate DBIx::Class::Schema and Resultset packages ✨. This uses the config you supplied in config_dev.pl to produce a DB.pm in your app's lib/MAINMODULE directory.

~/dev/My-App1$ perl script/create_dbic_schema.pl
Dumping manual schema for DB to directory /home/gordon/dev/MyApp1/lib/MyApp1/DB ...
Schema dump completed.

Given the example database shown below, your resulting DBIx::Class related files and folders would look like this:

~/dev/MyApp1$ find lib/MyApp1/ | grep DB
lib/MyApp1/DB
lib/MyApp1/DB/Result
lib/MyApp1/DB/Result/Orders.pm
lib/MyApp1/DB/Result/Customer.pm
lib/MyApp1/DB.pm

For more info see: CGI::Application::Plugin::DBIC::Schema, DBIx::Class


πŸ‘‘ Run Your App

Now that your database is configured and the schema generated, you can finally run your app πŸŽ‰.

Run the server:

~/dev/MyApp1$ perl server.pl
access your default runmode at /cgi-bin/index.cgi
CGI::Application::Server: You can connect to your server at http://localhost:8060/

Open your browser and test it at

http://localhost:8060/cgi-bin/index.cgi

For more info on the nature of the development server see: CGI::Application::Server


πŸ’€ Try Plugin::DebugScreen;

CAS comes with CGI::Application::Plugin::DebugScreen. Plugin::DebugScreen gives you a very useful stack trace with multi-line source quotations for each level of the stack πŸ”₯. cas-starter.pl has put debug.sh in your project root directory β€” it sets up the environment for Plugin::DebugPage and runs the built in server.

Edit lib/MyApp1/C/Home.pm to generate an error so we can demonstrate the DebugScreen:

sub index: StartRunmode {
    my ($c) = @_;

    # add this line for demo of debug screen
    die "testing";

    $c->tt_params({
        message => 'Hello world!',
        title   => 'C::Home'
                  });
    return $c->tt_process();

}

Run the server in debug mode:

~/dev/MyApp1$ bash debug.sh
access your default runmode at /cgi-bin/index.cgi
CGI::Application::Server: You can connect to your server at http://localhost:8060/

Open your browser and test/demo the Plugin::DebugScreen:

http://localhost:8060/cgi-bin/index.cgi

Then remove the line you added to Home.pm 🧚 (yes, actually remove it).

For more info on Plugin::DebugScreen see: CGI::Application::Plugin::DebugScreen


πŸ¦‹ Create A New Controller

This is where the create_controller.pl helper script comes in very handy πŸ’–. create_controller.pl creates a new controller with a default runmode called 'index', plus an index template to go with it.

As an example, let's generate a new module to interact with the Orders table of the example database:

~/dev/MyApp1$ perl script/create_controller.pl --name=Orders
will try to create lib/MyApp1/C
Created lib/MyApp1/C/Orders.pm
will try to create template directory templates/MyApp1/C/Orders
Created templates/MyApp1/C/Orders
Created templates/MyApp1/C/Orders/index.tmpl

You can restart server.pl and view the default output at:

http://localhost:8060/cgi-bin/orders

Add a new runmode to lib/MyApp1/C/Orders.pm to show the orders we have from the example database:

sub list: Runmode{
    my $c = shift;

    my @orders = $c->resultset("MyApp1::DB::Result::Orders")->all;

    $c->tt_params(orders =>\@orders);
    return $c->tt_process();

}

Then add a template for this runmode at templates/MyApp1/C/Orders/list.tmpl with the following content:

<h1>Order List</h1>
<table>
  <tr><th>Cust No</th><th>Order No</th></tr>
  [% FOREACH order = orders %]
     <tr>
       <td>[% order.customer_id %]</td>
       <td>[% order.id %]</td>
     </tr>
  [% END %]
</table>

Restart server.pl and visit the page to see the list of orders at:

http://localhost:8060/cgi-bin/orders/list

For advanced info on creating controllers, runmodes and templates see: CGI::Application::Plugin::AutoRunmode, CGI::Application::Plugin::TT, CGI::Application and Template::Toolkit.


πŸ“ Creating The Example Database (if you don't already have one)

The CGI::Application::Structured distrubution contains an example sql file that you can use for this example app 🌸. Use the download link at CGI::Application::Structured on CPAN, grab the archive and extract the file from the 'examples' directory of the distribution.

The script will create the 'myapp1_dev' database, create 2 tables and load a few Notice that the create table statements end with 'engine=InnoDB'. This is important since our DBIC generator script will create perl modules to represent database table relationships, based on the metadata in the database. While the InnoDB engine will work, the default engine for mysql will not store the relationship metadata and you would then need to hand-craft the relationships at the botton of the generated DB::Result classes. 😭

Example:

~/dev/MyApp1$ mysql -u root -p < example_tables.mysql.ddl

The contents of the example sql file are as follows:

CREATE DATABASE myapp1_dev;
USE myapp1_dev;

CREATE TABLE customer(
   id integer not null auto_increment PRIMARY KEY,
   last_name varchar(25) null,
   first_name varchar(25) not null
)engine=InnoDB;

CREATE TABLE orders(
  id integer not null auto_increment PRIMARY KEY,
  customer_id integer not null,
  order_status varchar(10) default "OPEN" not null,
  order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP not null,
  CONSTRAINT orders_customer_id_fk FOREIGN KEY (customer_id) REFERENCES customer(id)
)engine=InnoDB;

INSERT INTO customer (last_name, first_name) VALUES("Doe","John");
INSERT INTO orders (customer_id) VALUES(  1 );
INSERT INTO orders (customer_id) VALUES(  1 );
INSERT INTO orders (customer_id) VALUES(  1 );

If you did not use 'engine=InnoDB' or your database doesn't support relationships, you can paste the following at the bottom of your "MyApp/DB/Result/Orders.pm" to tell DBIx::Class how the example tables relate:

# Created by DBIx::Class::Schema::Loader v0.04006 @ 2009-09-15 16:05:33
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:znOKfDkdRzpL0KHWpfpJ+Q

__PACKAGE__->belongs_to(
  "customer",
  "MyApp1::DB::Result::Customer",
  { id => "customer" },
);

See the documentation for DBIx::Class::Manual for more info on configuring and using relationships in your model 🫢.

πŸ“š Further Reading πŸ“š

Okay bestie, you made it to the rec list, and this is the part where I hand you the aux cord. Here's where to go when you wanna level up ✨

  • Peep CGI::Application::Structured::Tools for the tea on the developer tools package πŸ› οΈ
  • Peep CGI::Application::Plugin::DBIC::Schema if you wanna know how to access DBIx::Class from your CGI::Application::Structured modules πŸ‘‘
  • Peep CGI::Application::Plugin::SuperForm for form building support, which is built right into CGI::Application::Structured πŸŽ€
  • Peep DBIx::Class::Manual::Intro for the lowdown on the powerful ORM that ships with CGI::Application::Structured πŸ”₯
  • Peep Template::Toolkit and CGI::Application::Plugin::TT for the advanced templating lore πŸ¦‹
  • Peep CGI::Application for SO many good ideas and examples that will absolutely work with your CGI::Application::Structured app 🌸

πŸ’€ BUGS πŸ’€

Good news, bestie: there are no known bugs for this distribution. We're thriving fr fr 🫢

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

I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. Iconic teamwork energy, no cap ✨


πŸ«‚ SUPPORT πŸ«‚

I recommend joining the cgi-application mailing list. Come hang out, it's giving community 🌈


πŸ‘‘ AUTHOR πŸ‘‘

Gordon Van Amburg
CPAN ID: VANAMBURG
vanamburg at cpan.org

Not me making sure the legend gets their credit 😭 πŸ’–


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

The full text of the license can be found in the LICENSE file included with this module.


And that's a wrap, bestie!! πŸŽ€ This page comes to you courtesy of perl v5.42.0, dated 2025-07-25 β€” the exact version and date of this doc, so if you're reading this in the future, hi, hope the Unix still slaps πŸ’œ Go build something cute with your structured app, you've got this, periodt πŸ’…βœ¨

πŸ“¦ where this page comes from