The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.

NAME

Leyland::Manual::Applications - Leyland application structure and creation

VERSION

version 0.001007

APPLICATION STRUCTURE

Leyland application structure is a fusion of the Catalyst and Dancer application structures. The following is the basic structure of a Leyland application. Note that other than files and directories marked "(required)", everything else you see is purely a suggestion or an example and is not integral to a Leyland application, which can perfectly live without it.

        |- /
        |----- app.psgi (required)
        |----- lib/ (required)
        |-------- MyApp.pm (required)
        |-------- MyApp/ (required)
        |----------- Context.pm
        |----------- Controller/ (required)
        |-------------- Root.pm (required)
        |-------------- SomeOtherController.pm
        |-------------- SomeOtherController/
        |----------------- Admin.pm
        |----------- Model/
        |----------- View/
        |----- public/
        |-------- css/
        |-------- js/
        |-------- images/
        |----- views/ (required)
        |-------- index.html (required)
        |-------- layouts/ (required)
        |----------- main.html (required)
        |----- i18n/
        |-------- es.json
        |-------- he_and_it.coll.json

The app.psgi file is a standard PSGI application that configures and initializes the Leyland application; possibly adds Plack middlewares; and lets Leyland handle incoming requests. A minimal app.psgi file will look something like this:

        #!/usr/bin/perl -w

        use lib './lib';
        use strict;
        use warnings;
        use MyApp;

        my $config = { app => 'MyApp' };

        my $a = MyApp->new(config => $config);

        my $app = sub {
                $a->handle(shift);
        };

The lib/MyApp.pm file is a simple class the extends Leyland. A minimal example would be:

        package MyApp;

        use Moose;
        use namespace::autoclean;

        extends 'Leyland';

        sub setup {
                my $self = shift;
                
                # perform some initializations
        }

        __PACKAGE__->meta->make_immutable;

The setup() method is called immediately after the class is loaded and a new instance of it is created. It is useful for performing some necessary initializations and whatever you need to do just once to get your app working. You might initiate a database connection here, clear some files left over by your application in previous runs (if it generates any), whatever you find appropriate.

Next in line we find lib/MyApp/Context.pm. Most applications will not have this file. This file (which can actually be named differently and located differently) is supposed to extend Leyland::Context when you want to provide it with methods and attributes specific to your application. I will not describe it further here, take a look at Leyland::Manual::Extending for more information.

Next we have the controllers. Those are located under lib/MyApp/Controller/. The controllers are those that handle an incoming request and return a response. A Leyland application must have a root controller, called Root.pm. A minimal controller will look something like this:

        package MyApp::Controller::Root;

        use Moose;
        use Leyland::Parser;
        use namespace::autoclean;

        with 'Leyland::Controller';

        prefix { '' }

        get '^/$' {
                $c->template('index.html');
        }
        
        __PACKAGE__->meta->make_immutable;

More information about controllers in Leyland::Manual::Controllers.

The lib/MyApp/Model/ directory is really just a suggestion. Leyland, as opposed to Catalyst, does not concern itself with models. Do what you like, how you like. If you want to create model classes in this directory, go ahead. If you want to do that somewhere/somehow else, knock yourself out. Leyland will not attempt to load model classes for you. More information about models in Leyland::Manual::Models.

The lib/MyApp/View/ directory is where you can put your own customized view classes. You don't have to do so, and in my opinion you're not likely to do so, but this option is available for you. As Leyland-provided view classes are meant to be "plug-and-play" (i.e. you don't have to create view classes to use them like in Catalyst for example), Leyland gives you the ability to create your own classes, or extend those provided by Leyland if you wish. Any class in the lib/MyApp/View/ directory will be automatically loaded by Leyland when your app is started. Read Leyland::Manual::Views for more information.

Next up is the public/ directory, which includes static files of your application, such as images and CSS files. Note that this is not the place for views/templates. Since Leyland is designed for web applications, pretty much every Leyland application will have a public directory, though it doesn't have to be called "public", nor does it need to contain the directories in the above example ("css", "js" and "images"). Static files are not handled by Leyland directly. Instead, Leyland either relies on Plack::Middleware::Static to serve those files, or on a front-line web server such as Apache, nginx, lighttpd or cherokee. More information about static files in Leyland::Manual::StaticFiles.

The views/ directory is where your views and templates reside. These will mostly be HTML templates, but can actually be anything, even JSON or XML or whatever. Views and templates are handled by Leyland's view classes, and are rendered and returned to the clients by your controllers. A view is written in the language of the template engine of your choice, such as Tenjin or Template::Toolkit. Currently, however, Tenjin is the only view class available for Leyland applications.

The views/layouts/ subdirectory contains layouts for your views. Views from the views/ directory, when rendered, can (but doesn't have to) be rendered into these layouts. For example, the index.html view can be:

        <h1>Hello World</h1>

While a layout view called layouts/main.html can be:

        <html>
                <head><title>Hi there</title></head>
                <body>[== $_content =]</body>
        </html>

When a controller renders index.html for output, index.html will be wrapped by layouts/main.html where [== $_content =] resides (this is Tenjin specific, other template engines will be different), with the final output:

        <html>
                <head><title>Hi there</title></head>
                <body><h1>Hello World</h1></body>
        </html>

See Leyland::Manual::Views for more information about views.

Finally, we have the i18n/ directory. Once again, this is optional and purely a suggestion. If used, this directory will hold JSON localization files for localizing your application with Locale::Wolowitz. See Leyland::Manual::Localization for more information.

CREATING LEYLAND APPLICATIONS

To start a new Leyland application, one needs to create the above application structure. You can do it by hand, or you can use the provided script, leyland, which will create this structure for you automatically:

        # from the command line
        leyland app --author "Some Guy" --email "some_guy@email.com" MyApp

Replace MyApp with the name of your application. That would be the package name (i.e. with "::", like "My::New::App"), not the distribution name (i.e. with "-", like "My-New-App"). You don't have to provide the --author and --email options, these are just used in the POD documentation of your classes, but keep in mind that if you don't, some useless default values will be used.

CONFIGURING LEYLAND APPLICATIONS

While most other application frameworks like Catalyst and Dancer take configuration from configuration files, for Leyland I have currently decided to create a configuration directly inside the app.psgi file. In the future, configuration files may be supported.

If you take a look at the example of the app.psgi file above, you will notice the $config variable. This is the configuration hash-ref of your application. At its minimum, it should contain the name of your application (under the 'app' key), otherwise 'Leyland' will be used.

Other configuration options are:

  • "views": takes an array reference with the names of all view classes to initialize. These are classes of the Leyland::View::* family. If you don't provide this option, "['Tenjin']" will be used, and thus Leyland::View::Tenjin will be available for your app. View classes you define here will be added to the automatically loaded from lib/MyApp/View/, if you have any there (you probably don't).

  • "locales": takes a path (can be relative) to a directory containing your application's Locale::Wolowitz localization files. If you're not gonna localize, you don't need this.

  • "logger": takes a hash-ref with options for the logger class to use for logging. Read Leyland::Manual::Logging for more information. If not provided, your application will log to STDERR.

  • "environments": takes a hash-ref with configuration options which are only relevant to the Plack environment (the PLACK_ENV environment variable) on which the application is running. When your app is initialized, if configuration options are available for the selected environment, they will also be used, and will even take precedence over the basic configuration options. For example, you can define a "logger" option in the top level of the $config variable that logs to the screen, and in "environments", under "deployment" perhaps, define another "logger" option that logs both to the screen and also the file. When your app is running on the "deployment" environment, the latter "logger" option will take effect.

Anything else you provide in the $config variable (including under the working environment) will also be available for usage by your application. A rather complete example for the $config variable will be:

        my $config = {
                app => 'MyApp',
                views => ['Tenjin'],
                locales => './i18n',
                default_lang => 'en',
                environments => {
                        development => {
                                database_host => 'mongodb://localhost:27017',
                                logger => {
                                        class => 'LogHandler',
                                        opts => {
                                                outputs => [
                                                        file => {
                                                                filename => "myapp.$ENV{PLACK_ENV}.log",
                                                                minlevel => 0,
                                                                maxlevel => 8,
                                                                utf8 => 1,
                                                        },
                                                        screen => {
                                                                log_to   => "STDERR",
                                                                minlevel => 0,
                                                                maxlevel => 8,
                                                        },
                                                ]
                                        }
                                },
                        },
                        deployment => {
                                database_host => 'mongodb://localhost:27017,mongodb://localhost:27018',
                                logger => {
                                        class => 'LogHandler',
                                        opts => {
                                                outputs => [
                                                        file => {
                                                                filename => "myapp.$ENV{PLACK_ENV}.log",
                                                                minlevel => 0,
                                                                maxlevel => 8,
                                                                utf8 => 1,
                                                        },
                                                ]
                                        }
                                },
                        },
                }
        };

As you can see, every environment has a different "logger" configuration. Plus, every environment has its own "database_host" value, which is not Leyland-specific, but supposedly used by MyApp. The same goes for the "default_lang" option, which is maybe used by the application developer to define the default language in which to respond to requests that do not define a language to use.

RUNNING LEYLAND APPLICATIONS

Since Leyland is Plack based, running Leyland applications is very easy. To start testing your application, change into the application's root directory and simply run plackup. This will start your application with Plack's standalone web server, under the "development" environment, listening on port 5000 (so point your browser to http://localhost:5000/). Read Leyland::Manual::Deployment and plackup for more information about running and deploying plack applications.

WHAT'S NEXT?

Read Leyland::Manual::Controllers now to learn how to create Leyland controllers or return to the table of contents.

AUTHOR

Ido Perlmuter, <ido at ido50.net>

BUGS

Please report any bugs or feature requests to bug-Leyland at rt.cpan.org, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Leyland. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.

SUPPORT

You can find documentation for this module with the perldoc command.

        perldoc Leyland::Manual::Applications

You can also look for information at:

LICENSE AND COPYRIGHT

Copyright 2010-2011 Ido Perlmuter.

This program is free software; you can redistribute it and/or modify it under the terms of either: the GNU General Public License as published by the Free Software Foundation; or the Artistic License.

See http://dev.perl.org/licenses/ for more information.