The London Perl and Raku Workshop takes place on 26th Oct 2024. If your company depends on Perl, please consider sponsoring and/or attending.

NAME

Nile - Android Like Visual Web App Framework Separating Code From Design Multi Lingual And Multi Theme.

SYNOPSIS

#!/usr/bin/perl

use Nile;

my $app = Nile->new();

# initialize the application with the shared and safe sessions settings
$app->init({
    # base application path, auto detected if not set
    path        =>  dirname(File::Spec->rel2abs(__FILE__)),
    
    # load config files, default extension is xml
    config      => [ qw(config) ],

    # force run mode if not auto detected by default. modes: "psgi", "fcgi" (direct), "cgi" (direct)
    #mode   =>  "fcgi", # psgi, cgi, fcgi
});

# inline actions, return content. url: /forum/home
$app->action("get", "/forum/home", sub {
    my ($self) = @_;
    # $self is set to the application context object same as $self->app in plugins
    my $content = "Host: " . ($self->request->virtual_host || "") ."<br>\n";
    $content .= "Request method: " . ($self->request->request_method || "") . "<br>\n";
    $content .= "App Mode: " . $self->mode . "<br>\n";
    $content .= "Time: ". time . "<br>\n";
    $content .= "Hello world from inline action /forum/home" ."<br>\n";
    $content .= "أحمد الششتاوى" ."<br>\n";
    $self->response->encoded(0); # encode content
    return $content;
});

# inline actions, capture print statements, ignore the return value. url: /accounts/login
$app->capture("get", "/accounts/login", sub {
    my ($self) = @_;
    # $self is set to the application context object same as $self->app in plugins
    say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
    say "Request method: " . ($self->request->request_method || "") . "<br>\n";
    say "App Mode: " . $self->mode . "<br>\n";
    say "Time: ". time . "<br>\n";
    say "Hello world from inline action with capture /accounts/login", "<br>\n";
    say $self->encode("أحمد الششتاوى ") ."<br>\n";
    $self->response->encoded(1); # content already encoded
});

# inline actions, capture print statements and the return value. url: /blog/new
$app->command("get", "/blog/new", sub {
    my ($self) = @_;
    # $self is set to the application context object same as $self->app in plugins
    say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
    say "Request method: " . ($self->request->request_method || "") . "<br>\n";
    say "App Mode: " . $self->mode . "<br>\n";
    say "Time: ". time . "<br>\n";
    say "Hello world from inline action with capture /blog/new and return value.", "<br>\n";
    say $self->encode("أحمد الششتاوى ") ."<br>\n";
    $self->response->encoded(1); # content already encoded
    return " This value is returned from the command.";
});

# run the application and return the PSGI response or print to the output
# the run process will also run plugins with matched routes files loaded
$app->run();

DESCRIPTION

Nile - Android Like Visual Web App Framework Separating Code From Design Multi Lingual And Multi Theme.

Beta version, API may change. The project's homepage https://github.com/mewsoft/Nile.

The main idea in this framework is to separate all the html design and layout from programming. The framework uses html templates for the design with special xml tags for inserting the dynamic output into the templates. All the application text is separated in langauge files in xml format supporting multi lingual applications with easy translating and modifying all the text. The framework supports PSGI and also direct CGI and direct FCGI without any modifications to your applications.

EXAMPLE APPLICATION

Download and uncompress the module file. You will find an example application folder named app.

URLs

This framework support SEO friendly url's, routing specific urls and short urls to actions.

The url routing system works in the following formats:

http://domain.com/module/controller/action  # mapped from route file or to Module/Controller/action
http://domain.com/module/action         # mapped from route file or to Module/Module/action or Module/Module/index
http://domain.com/module            # mapped from route file or to Module/Module/module or Module/Module/index
http://domain.com/index.cgi?action=module/controller/action
http://domain.com/?action=module/controller/action
http://domain.com/blog/2014/11/28   # route mapped from route file and args passed as request params

The following urls formats are all the same and all are mapped to the route /Home/Home/index or /Home/Home/home (/Module/Controller/Action):

# direct cgi call, you can use action=home, route=home, or cmd=home
http://domain.com/index.cgi?action=home

# using .htaccess to redirect to index.cgi
http://domain.com/?action=home

# SEO url using with .htaccess. route is auto detected from url.
http://domain.com/home

APPLICATION DIRECTORY STRUCTURE

Applications built with this framework must have basic folder structure. Applications may have any additional directories.

The following is the basic application folder tree that must be created manually before runing:

├───api
├───cache
├───cmd
├───config
├───cron
├───data
├───file
├───lang
│   ├───ar
│   └───en-US
├───lib
│   └───Nile
│       ├───Module
│       │   └───Home
│       └───Plugin
├───log
├───route
├───temp
├───theme
│   └───default
│       ├───css
│       ├───icon
│       ├───image
│       ├───js
│       ├───view
│       └───widget
└───web

CREATING YOUR FIRST MODULE 'HOME'

To create your first module called Home for your site home page, create a folder called Home in your application path /path/lib/Nile/Module/Home, then create the module Controller file say Home.pm and put the following code:

package Nile::Module::Home::Home;

our $VERSION = '0.55';

use Nile::Module; # automatically extends Nile::Module
use DateTime qw();
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# plugin action, return content. url is routed direct or from routes files. url: /home
sub home : GET Action {
    
    my ($self, $app) = @_;
    
    # $app is set to the application context object, same as $self->app inside any method
    #my $app = $self->app;
    
    my $view = $app->view("home");
    
    $view->var(
        fname           =>  'Ahmed',
        lname           =>  'Elsheshtawy',
        email           =>  'sales@mewsoft.com',
        website     =>  'http://www.mewsoft.com',
        singleline      =>  'Single line variable <b>Good</b>',
        multiline       =>  'Multi line variable <b>Nice</b>',
    );
    
    #my $var = $view->block();
    #say "block: " . $app->dump($view->block("first/second/third/fourth/fifth"));
    #$view->block("first/second/third/fourth/fifth", "Block Modified ");
    #say "block: " . $app->dump($view->block("first/second/third/fourth/fifth"));

    $view->block("first", "1st Block New Content ");
    $view->block("six", "6th Block New Content ");

    #say "dump: " . $app->dump($view->block->{first}->{second}->{third}->{fourth}->{fifth});
    
    # module settings from config files
    my $setting = $self->setting();
    
    # plugin session must be enabled in config.xml
    if (!$app->session->{first_visit}) {
        $app->session->{first_visit} = time;
    }
    my $dt = DateTime->from_epoch(epoch => $app->session->{first_visit});
    $view->set("first_visit", $dt->strftime("%a, %d %b %Y %H:%M:%S"));
    
    # save visitors count to the cache
    $app->cache->set("visitor_count", $app->cache->get("visitor_count") + 1, "1 year");
    $view->set("visitor_count", $app->cache->get("visitor_count"));

    return $view->out();
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# run action and capture print statements, no returns. url: /home/news
sub news: GET Capture {

    my ($self, $app) = @_;

    say qq{Hello world. This content is captured from print statements.
        The action must be marked by 'Capture' attribute. No returns.};

}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# run action and capture print statements and the return value. url: /home/info
sub info: GET Command {

    my ($self, $app) = @_;

    say qq{This content is captured from print statements.
        The action marked by 'Command' attribute. };
    
    return qq{This content is the return value on the action.};
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# regular method, can be invoked by views:
# <vars type="module" method="Home::Home->welcome" message="Welcome back!" />
sub welcome {
    my ($self, %args) = @_;
    my $app = $self->app();
    return "Nice to see you, " . $args{message};
}
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1;

YOUR FIRST VIEW 'home'

Create an html file name it as home.html, put it in the default theme folder /path/theme/default/views and put in this file the following code:

<vars type="widget" name="header" charset_name="UTF-8" lang_name="en" />

{first_name} <vars name="fname" /><br>
{last_name} <vars name="lname" /><br>
{email} <vars type="var" name='email' /><br>
{website} <vars type="var" name="website" /><br>
<br>

global variables:<br>
language: <vars name='lang' /><br>
theme: <vars name="theme" /><br>
base url: <vars name="base_url" /><br>
image url: <vars name="image_url" /><br>
css url: <vars name="css_url" /><br>
new url: <a href="<vars name="base_url" />comments" >comments</a><br>
image: <img src="<vars name="image_url" />logo.png" /><br>
<br>
first visit: <vars name="first_visit" /><br>
<br>

{date_now} <vars type="plugin" method="Date->date" format="%a, %d %b %Y %H:%M:%S" /><br>
{time_now} <vars type="plugin" method="Date->time" format="%A %d, %B %Y  %T %p" /><br>
{date_time} <vars type="plugin" method="Date::now" capture="1" format="%B %d, %Y  %r" /><br>

<br>
<vars type="module" method="Home::Home->welcome" message="Welcome back!" /><br>
<br>

Our Version: <vars type="perl"><![CDATA[print $self->app->VERSION; return;]]></vars><br>
<br>

<pre>
<vars type="perl">system ('dir *.cgi');</vars>
</pre>
<br>

<vars type="var" name="singleline" width="400px" height="300px" content="ahmed<b>class/subclass">
cdata start here is may have html tags and 'single' and "double" qoutes
</vars>
<br>

<vars type="var" name="multiline" width="400px" height="300px"><![CDATA[ 
    cdata start here is may have html tags <b>hello</b> and 'single' and "double" qoutes
    another cdata line
]]></vars>
<br>

<vars type="perl"><![CDATA[ 
    say "";
    say "<br>active language: " . $self->app->var->get("lang");
    say "<br>active theme: " . $self->app->var->get("theme");
    say "<br>app path: " . $self->app->var->get("path");
    say "<br>";
]]></vars>
<br><br>

html content 1-5 top
<!--block:first-->
    <table border="1" style="color:red;">
    <tr class="lines">
        <td align="left" valign="<--valign-->">
            <b>bold</b><a href="http://www.mewsoft.com">mewsoft</a>
            <!--hello--> <--again--><!--world-->
            some html content here 1 top
            <!--block:second-->
                some html content here 2 top
                <!--block:third-->
                    some html content here 3 top
                    <!--block:fourth-->
                    some html content here 4 top
                        <!--block:fifth-->
                            some html content here 5a
                            some html content here 5b
                        <!--endblock-->
                    <!--endblock-->
                    some html content here 3a
                some html content here 3b
            <!--endblock-->
        some html content here 2 bottom
        </tr>
    <!--endblock-->
    some html content here 1 bottom
</table>
<!--endblock-->
html content 1-5 bottom

<br><br>

html content 6-8 top
<!--block:six-->
    some html content here 6 top
    <!--block:seven-->
        some html content here 7 top
        <!--block:eight-->
            some html content here 8a
            some html content here 8b
        <!--endblock-->
        some html content here 7 bottom
    <!--endblock-->
    some html content here 6 bottom
<!--endblock-->
html content 6-8 bottom

<br><br>

<vars type="widget" name="footer" title="cairo" lang="ar" />

YOUR FIRST WIDGETS 'header' AND 'footer'

The framework supports widgets, widgets are small views that can be repeated in many views for easy layout and design. For example, you could make the site header template as a widget called header and the site footer template as a widget called footer and just put the required xml special tag for these widgets in all the Views you want. Widgets files are html files located in the theme 'widget' folder

Example widget header.html

<!doctype html>
<html lang="{lang_code}">
 <head>
  <meta http-equiv="content-type" content="text/html; charset=[:charset_name:]" />
  <title>{page_title}</title>
  <meta name="Keywords" content="{meta_keywords}" />
  <meta name="Description" content="{meta_description}" />
 </head>
 <body>

Example widget footer.html

</body>
</html>

then all you need to include the widget in the view is to insert these tags:

<vars type="widget" name="header" charset_name="UTF-8" />
<vars type="widget" name="footer" />

You can pass args to the widget like charset_name to the widget above and will be replaced with their values.

LANGUAGES

All application text is located in text files in xml format. Each language supported should be put under a folder named with the iso name of the langauge under the folder path/lang.

Example langauge file 'general.xml':

<?xml version="1.0" encoding="UTF-8" ?>

<lang_code>en</lang_code>
<site_name>Site Name</site_name>
<home>Home</home>
<register>Register</register>
<contact>Contact</contact>
<about>About</about>
<copyright>Copyright</copyright>
<privacy>Privacy</privacy>

<page_title>Create New Account</page_title>
<first_name>First name:</first_name>
<middle_name>Middle name:</middle_name>
<last_name>Last name:</last_name>
<full_name>Full name:</full_name>
<email>Email:</email>
<job>Job title:</job>
<website>Website:</website>
<agree>Agree:</agree>
<company>Company</company>

<date_now>Date: </date_now>
<time_now>Time: </time_now>
<date_time>Now: </date_time>

Routing

The framework supports url routing, route specific short name actions like 'register' to specific plugins like Accounts/Register/create.

Below is route.xml file example should be created under the path/route folder.

<?xml version="1.0" encoding="UTF-8" ?>

<home route="/home" action="/Home/Home/home" method="get" />
<register route="/register" action="/Accounts/Register/register" method="get" defaults="year=1900|month=1|day=23" />
<post route="/blog/post/{cid:\d+}/{id:\d+}" action="/Blog/Article/Post" method="post" />
<browse route="/blog/{id:\d+}" action="/Blog/Article/Browse" method="get" />
<view route="/blog/view/{id:\d+}" action="/Blog/Article/View" method="get" />
<edit route="/blog/edit/{id:\d+}" action="/Blog/Article/Edit" method="get" />

CONFIG

The framework supports loading and working with config files in xml formate located in the folder 'config'.

Example config file path/config/config.xml:

<?xml version="1.0" encoding="UTF-8" ?>

<app>
    <config></config>
    <route>route.xml</route>
    <log_file>log.pm</log_file>
    <action_name>action,route,cmd</action_name>
    <default_route>/Home/Home/home</default_route>
    <charset>utf-8</charset>
    <theme>default</theme>
    <lang>en-US</lang>
    <lang_param_key>lang</lang_param_key>
    <lang_cookie_key>lang</lang_cookie_key>
    <lang_session_key>lang</lang_session_key>
    <lang_file>general</lang_file>
</app>

<admin>
    <user>admin_user</user>
    <password>admin_pass</password>
</admin>

<dbi>
    <driver>mysql</driver>
    <host>localhost</host>
    <dsn></dsn>
    <port>3306</port>
    <name>auctions</name>
    <user>auctions</user>
    <pass>auctions</pass>
    <attr>
    </attr>
    <encoding>utf8</encoding>
</dbi>

<module>
    <home>
        <header>home</header>
        <footer>footer</footer>
    </home>
</module>

<plugin>
    <email>
        <transport>Sendmail</transport>
        <sendmail>/usr/sbin/sendmail</sendmail>
    </email>

    <session>
        <autoload>1</autoload>
        <key>nile_session_key</key>
        <expire>1 year</expire>
        <cache>
            <driver>File</driver>
            <root_dir></root_dir>
            <namespace>session</namespace>
        </cache>
        <cookie>
            <path>/</path>
            <secure></secure>
            <domain></domain>
            <httponly></httponly>
        </cookie>
    </session>

    <cache>
        <autoload>0</autoload>
    </cache>
</plugin>

APPLICATION INSTANCE SHARED DATA

The framework is fully Object-oriented to allow multiple separate instances. Inside any module or plugin you will be able to access the application instance by calling the method $self->app which is automatically injected into all modules with the application instance.

The plugins and modules files will have the following features.

Moose enabled Strict and Warnings enabled. a Moose attribute called app injected holds the application singleton instance to access all the data and methods.

Inside your modules and plugins, you will be able to access the application instance by:

my $app = $self->app;

Then you can access the application methods and objects like:

$app->request->param("username");
# same as
$self->app->request->param("username");

$app->response->code(200);

$app->var->set("name", "value");

URL REWRITE .htaccess for CGI and FCGI

To hide the script name index.cgi from the url and allow nice SEO url routing, you need to turn on url rewrite on your web server and have .htaccess file in the application folder with the index.cgi.

Below is a sample .htaccess which redirects all requests to index.cgi file and hides index.cgi from the url, so instead of calling the application as:

http://domain.com/index.cgi?action=register

using the .htaccess you will be able to call it as:

http://domain.com/register

without any changes in the code.

For direct FCGI, just replace .cgi with .fcgi in the .htaccess and rename index.cgi to index.fcgi.

# Don't show directory listings for URLs which map to a directory.
Options -Indexes -MultiViews

# Follow symbolic links in this directory.
Options +FollowSymLinks

#Note that AllowOverride Options and AllowOverride FileInfo must both be in effect for these directives to have any effect, 
#i.e. AllowOverride All in httpd.conf
Options +ExecCGI
AddHandler cgi-script cgi pl 

# Set the default handler.
DirectoryIndex index.cgi index.html index.shtml

# save this file as UTF-8 and enable the next line for utf contents
#AddDefaultCharset UTF-8

# REQUIRED: requires mod_rewrite to be enabled in Apache.
# Please check that, if you get an "Internal Server Error".
RewriteEngine On
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# force use www with http and https so http://domain.com redirect to http://www.domain.com
#add www with https support - you have to put this in .htaccess file in the site root folder
# skip local host
RewriteCond %{HTTP_HOST} !^localhost
# skip IP addresses
RewriteCond %{HTTP_HOST} ^([a-z.]+)$ [NC]
RewriteCond %{HTTP_HOST} !^www\. 
RewriteCond %{HTTPS}s ^on(s)|''
RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} !=/favicon.ico
RewriteRule ^(.*)$ index.cgi [L,QSA]

REQUEST

The http request is available as a shared object extending the CGI::Simple module. This means that all methods supported by CGI::Simple is available with the additions of these few methods:

is_ajax
is_post
is_get
is_head
is_put
is_delete
is_patch

You access the request object by $self->app->request.

ERRORS, WARNINGS, ABORTING

To abort the application at anytime with optional message and stacktrace, call the method:

$self->app->abort("application error, can not find file required");

For fatal errors with custom error message

$self->app->error("error message");

For fatal errors with custom error message and full starcktrace

$self->app->errors("error message");

For displaying warning message

$self->app->warning("warning message");

LOGS

The framework supports a log object which is a Log::Tiny object which supports unlimited log categories, so simply you can do this:

$app->log->info("application run start");
$app->log->DEBUG("application run start");
$app->log->ERROR("application run start");
$app->log->INFO("application run start");
$app->log->ANYTHING("application run start");

FILE

The file object provides tools for reading files, folders, and most of the functions in the modules File::Spec and File::Basename.

to get file content as single string or array of strings:

$content = $app->file->get($file);
@lines = $app->file->get($file);

supports options same as File::Slurp.

To get list of files in a specific folder:

#files($dir, $match, $relative)
@files = $app->file->files("c:/apache/htdocs/nile/", "*.pm, *.cgi");

#files_tree($dir, $match, $relative, $depth)
@files = $app->file->files_tree("c:/apache/htdocs/nile/", "*.pm, *.cgi");

#folders($dir, $match, $relative)
@folders = $app->file->folders("c:/apache/htdocs/nile/", "", 1);

#folders_tree($dir, $match, $relative, $depth)
@folders = $app->file->folders_tree("c:/apache/htdocs/nile/", "", 1);

XML

Loads xml files into hash tree using XML::TreePP

$xml = $app->xml->load("configs.xml");

DBI

See Nile::DBI

The DBI class provides methods for connecting to the sql database and easy methods for sql operations.

METHODS

init()

use Nile;

my $app = Nile->new();

$app->init({
    # base application path, auto detected if not set
    path        =>  dirname(File::Spec->rel2abs(__FILE__)),
    
    # load config files, default extension is xml
    config      => [ qw(config) ],

    # force run mode if not auto detected by default. modes: "psgi", "fcgi" (direct), "cgi" (direct)
    #mode   =>  "fcgi", # psgi, cgi, fcgi
});

Initialize the application with the shared and safe sessions settings.

var()

See Nile::Var.

config()

See Nile::Config.

run()

$app->run();

Run the application and dispatch the command.

prefix()

# from here, any route handler is defined to /forum/*:
$app->prefix("/forum");

# will match '/forum/login'
$app->action("get", "/login", sub {return "Forum login"});

Defines a prefix for each route handler from now on.

action()

# inline actions, return content. url: /forum/home
$app->action("get", "/forum/home", sub {
    my ($self) = @_;
    # $self is set to the application context object same as $self->app in plugins
    my $content = "Host: " . ($self->request->virtual_host || "") ."<br>\n";
    $content .= "Request method: " . ($self->request->request_method || "") . "<br>\n";
    $content .= "App Mode: " . $self->mode . "<br>\n";
    $content .= "Time: ". time . "<br>\n";
    $content .= "Hello world from inline action /forum/home" ."<br>\n";
    $content .= "أحمد الششتاوى" ."<br>\n";
    $self->response->encoded(0); # encode content
    return $content;
});

Add inline action, return content to the dispatcher.

capture()

# inline actions, capture print statements, no returns. url: /accounts/login
$app->capture("get", "/accounts/login", sub {
    my ($self) = @_;
    # $self is set to the application context object same as $self->app in plugins
    say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
    say "Request method: " . ($self->request->request_method || "") . "<br>\n";
    say "App Mode: " . $self->mode . "<br>\n";
    say "Time: ". time . "<br>\n";
    say "Hello world from inline action with capture /accounts/login", "<br>\n";
    say $self->encode("أحمد الششتاوى ") ."<br>\n";
    $self->response->encoded(1); # content already encoded
});

Add inline action, capture print statements, no returns to the dispatcher.

command()

# inline actions, capture print statements and return value. url: /blog/new
$app->command("get", "/blog/new", sub {
    my ($self) = @_;
    # $self is set to the application context object same as $self->app in plugins
    say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
    say "Request method: " . ($self->request->request_method || "") . "<br>\n";
    say "App Mode: " . $self->mode . "<br>\n";
    say "Time: ". time . "<br>\n";
    say "Hello world from inline action with capture /blog/new and return value.", "<br>\n";
    say $self->encode("أحمد الششتاوى ") ."<br>\n";
    $self->response->encoded(1); # content already encoded
    return " This value is returned from the command.";
});

Add inline action, capture print statements and returns to the dispatcher.

router()

See Nile::Router.

filter()

See Nile::Filter.

file()

See Nile::File.

xml()

See Nile::XML.

mode()

my $mode = $app->mode;

Returns the current application mode PSGI, FCGI or CGI.

lang()

See Nile::Lang.

object()

$obj = $app->object("Nile::MyClass", @args);
$obj = $app->object("Nile::Plugin::MyClass", @args);
$obj = $app->object("Nile::Module::MyClass", @args);

#...

$me = $obj->app;

Creates and returns an object. This automatically adds the method me to the object and sets it to the current context so your object or class can access the current instance.

dump()

$app->dump({...});

Print object to the STDOUT. Same as say Dumper (@_);.

is_loaded()

if ($app->is_loaded("Nile::SomeModule")) {
    #...
}

if ($app->is_loaded("Nile/SomeModule.pm")) {
    #...
}

Returns true if module is loaded, false otherwise.

load_once()

$app->load_once("Module::SomeModule");

Load modules if not already loaded.

load_class()

$app->load_class("Module::SomeModule");

Load modules if not already loaded.

cli_mode()

if ($app->cli_mode) {
    say "Running from the command line";
}
else {
    say "Running from web server";
}

Returns true if running from the command line interface, false if called from web server.

error()

$app->error("error message");

Fatal errors with custom error message. This is the same as croak in CGI::Carp.

errors()

$app->errors("error message");

Fatal errors with custom error message and full starcktrace. This is the same as confess in CGI::Carp.

warn()

$app->warn("warning  message");

Display warning message. This is the same as carp in CGI::Carp.

To view warnings in the browser, switch to the view source mode since warnings appear as a comment at the top of the page.

warns()

$app->warns("warning  message");

Display warning message and full starcktrace. This is the same as cluck in CGI::Carp.

To view warnings in the browser, switch to the view source mode since warnings appear as a comment at the top of the page.

abort()

$app->abort("error message");

$app->abort("error title", "error message");

Stop and quit the application and display message to the user. See Nile::Abort module.

Sub Modules

App Nile::App.

Views Nile::View.

Shared Vars Nile::Var.

Langauge Nile::Lang.

Request Nile::HTTP::Request.

PSGI Request Nile::HTTP::Request::PSGI.

PSGI Request Base Nile::HTTP::PSGI.

Response Nile::HTTP::Response.

PSGI Handler Nile::Handler::PSGI.

FCGI Handler Nile::Handler::FCGI.

CGI Handler Nile::Handler::CGI.

Dispatcher Nile::Dispatcher.

Router Nile::Router.

File Utils Nile::File.

DBI Nile::DBI.

DBI Table Nile::DBI::Table.

XML Nile::XML.

Settings Nile::Setting.

Serializer Nile::Serializer.

Deserializer Nile::Deserializer.

Serialization Base Nile::Serialization.

Filter Nile::Filter.

MIME Nile::MIME.

Timer Nile::Timer.

Plugin Nile::Plugin.

Session Nile::Plugin::Session.

Cache Nile::Plugin::Cache.

Cache Redis Nile::Plugin::Cache::Redis.

Email Nile::Plugin::Email.

Paginatation Nile::Plugin::Paginate.

MongoDB Nile::Plugin::MongoDB.

Redis Nile::Plugin::Redis.

Memcached Nile::Plugin::Memcached.

Module Nile::Module.

Hook Nile::Hook.

Base Nile::Base.

Abort Nile::Abort.

Bugs

This project is available on github at https://github.com/mewsoft/Nile.

HOMEPAGE

Please visit the project's homepage at https://metacpan.org/release/Nile.

SOURCE

Source repository is at https://github.com/mewsoft/Nile.

AUTHOR

Ahmed Amin Elsheshtawy, احمد امين الششتاوى <mewsoft@cpan.org> Website: http://www.mewsoft.com

COPYRIGHT AND LICENSE

Copyright (C) 2014-2015 by Dr. Ahmed Amin Elsheshtawy mewsoft@cpan.org, support@mewsoft.com, https://github.com/mewsoft/Nile, http://www.mewsoft.com

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