File Coverage

blib/lib/Nile.pm
Criterion Covered Total %
statement 159 286 55.5
branch 3 56 5.3
condition 1 33 3.0
subroutine 48 69 69.5
pod 4 20 20.0
total 215 464 46.3


line stmt bran cond sub pod time code
1             # Copyright Infomation
2             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
3             # Author : Dr. Ahmed Amin Elsheshtawy, Ph.D.
4             # Website: https://github.com/mewsoft/Nile, http://www.mewsoft.com
5             # Email : mewsoft@cpan.org, support@mewsoft.com
6             # Copyrights (c) 2014-2015 Mewsoft Corp. All rights reserved.
7             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8             package Nile;
9              
10             our $VERSION = '0.55';
11             our $AUTHORITY = 'cpan:MEWSOFT';
12              
13             =pod
14              
15             =encoding utf8
16              
17             =head1 NAME
18              
19             Nile - Android Like Visual Web App Framework Separating Code From Design Multi Lingual And Multi Theme.
20              
21             =head1 SYNOPSIS
22            
23             #!/usr/bin/perl
24              
25             use Nile;
26              
27             my $app = Nile->new();
28            
29             # initialize the application with the shared and safe sessions settings
30             $app->init({
31             # base application path, auto detected if not set
32             path => dirname(File::Spec->rel2abs(__FILE__)),
33            
34             # load config files, default extension is xml
35             config => [ qw(config) ],
36              
37             # force run mode if not auto detected by default. modes: "psgi", "fcgi" (direct), "cgi" (direct)
38             #mode => "fcgi", # psgi, cgi, fcgi
39             });
40            
41             # inline actions, return content. url: /forum/home
42             $app->action("get", "/forum/home", sub {
43             my ($self) = @_;
44             # $self is set to the application context object same as $self->app in plugins
45             my $content = "Host: " . ($self->request->virtual_host || "") ."<br>\n";
46             $content .= "Request method: " . ($self->request->request_method || "") . "<br>\n";
47             $content .= "App Mode: " . $self->mode . "<br>\n";
48             $content .= "Time: ". time . "<br>\n";
49             $content .= "Hello world from inline action /forum/home" ."<br>\n";
50             $content .= "أحمد الششتاوى" ."<br>\n";
51             $self->response->encoded(0); # encode content
52             return $content;
53             });
54              
55             # inline actions, capture print statements, ignore the return value. url: /accounts/login
56             $app->capture("get", "/accounts/login", sub {
57             my ($self) = @_;
58             # $self is set to the application context object same as $self->app in plugins
59             say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
60             say "Request method: " . ($self->request->request_method || "") . "<br>\n";
61             say "App Mode: " . $self->mode . "<br>\n";
62             say "Time: ". time . "<br>\n";
63             say "Hello world from inline action with capture /accounts/login", "<br>\n";
64             say $self->encode("أحمد الششتاوى ") ."<br>\n";
65             $self->response->encoded(1); # content already encoded
66             });
67              
68             # inline actions, capture print statements and the return value. url: /blog/new
69             $app->command("get", "/blog/new", sub {
70             my ($self) = @_;
71             # $self is set to the application context object same as $self->app in plugins
72             say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
73             say "Request method: " . ($self->request->request_method || "") . "<br>\n";
74             say "App Mode: " . $self->mode . "<br>\n";
75             say "Time: ". time . "<br>\n";
76             say "Hello world from inline action with capture /blog/new and return value.", "<br>\n";
77             say $self->encode("أحمد الششتاوى ") ."<br>\n";
78             $self->response->encoded(1); # content already encoded
79             return " This value is returned from the command.";
80             });
81              
82             # run the application and return the PSGI response or print to the output
83             # the run process will also run plugins with matched routes files loaded
84             $app->run();
85              
86             =head1 DESCRIPTION
87              
88             Nile - Android Like Visual Web App Framework Separating Code From Design Multi Lingual And Multi Theme.
89              
90             B<Beta> version, API may change. The project's homepage L<https://github.com/mewsoft/Nile>.
91              
92             The main idea in this framework is to separate all the html design and layout from programming.
93             The framework uses html templates for the design with special xml tags for inserting the dynamic output into the templates.
94             All the application text is separated in langauge files in xml format supporting multi lingual applications with easy translating and modifying all the text.
95             The framework supports PSGI and also direct CGI and direct FCGI without any modifications to your applications.
96              
97             =head1 EXAMPLE APPLICATION
98              
99             Download and uncompress the module file. You will find an example application folder named B<app>.
100              
101             =head1 URLs
102              
103             This framework support SEO friendly url's, routing specific urls and short urls to actions.
104              
105             The url routing system works in the following formats:
106              
107             http://domain.com/module/controller/action # mapped from route file or to Module/Controller/action
108             http://domain.com/module/action # mapped from route file or to Module/Module/action or Module/Module/index
109             http://domain.com/module # mapped from route file or to Module/Module/module or Module/Module/index
110             http://domain.com/index.cgi?action=module/controller/action
111             http://domain.com/?action=module/controller/action
112             http://domain.com/blog/2014/11/28 # route mapped from route file and args passed as request params
113              
114             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):
115            
116             # direct cgi call, you can use action=home, route=home, or cmd=home
117             http://domain.com/index.cgi?action=home
118              
119             # using .htaccess to redirect to index.cgi
120             http://domain.com/?action=home
121              
122             # SEO url using with .htaccess. route is auto detected from url.
123             http://domain.com/home
124              
125             =head1 APPLICATION DIRECTORY STRUCTURE
126              
127             Applications built with this framework must have basic folder structure. Applications may have any additional directories.
128              
129             The following is the basic application folder tree that must be created manually before runing:
130              
131             ├───api
132             ├───cache
133             ├───cmd
134             ├───config
135             ├───cron
136             ├───data
137             ├───file
138             ├───lang
139             │ ├───ar
140             │ └───en-US
141             ├───lib
142             │ └───Nile
143             │ ├───Module
144             │ │ └───Home
145             │ └───Plugin
146             ├───log
147             ├───route
148             ├───temp
149             ├───theme
150             │ └───default
151             │ ├───css
152             │ ├───icon
153             │ ├───image
154             │ ├───js
155             │ ├───view
156             │ └───widget
157             └───web
158              
159             =head1 CREATING YOUR FIRST MODULE 'HOME'
160              
161             To create your first module called Home for your site home page, create a folder called B<Home> in your application path
162             C</path/lib/Nile/Module/Home>, then create the module Controller file say B<Home.pm> and put the following code:
163              
164             package Nile::Module::Home::Home;
165              
166             our $VERSION = '0.55';
167              
168             use Nile::Module; # automatically extends Nile::Module
169             use DateTime qw();
170             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
171             # plugin action, return content. url is routed direct or from routes files. url: /home
172             sub home : GET Action {
173            
174             my ($self, $app) = @_;
175            
176             # $app is set to the application context object, same as $self->app inside any method
177             #my $app = $self->app;
178            
179             my $view = $app->view("home");
180            
181             $view->var(
182             fname => 'Ahmed',
183             lname => 'Elsheshtawy',
184             email => 'sales@mewsoft.com',
185             website => 'http://www.mewsoft.com',
186             singleline => 'Single line variable <b>Good</b>',
187             multiline => 'Multi line variable <b>Nice</b>',
188             );
189            
190             #my $var = $view->block();
191             #say "block: " . $app->dump($view->block("first/second/third/fourth/fifth"));
192             #$view->block("first/second/third/fourth/fifth", "Block Modified ");
193             #say "block: " . $app->dump($view->block("first/second/third/fourth/fifth"));
194              
195             $view->block("first", "1st Block New Content ");
196             $view->block("six", "6th Block New Content ");
197              
198             #say "dump: " . $app->dump($view->block->{first}->{second}->{third}->{fourth}->{fifth});
199            
200             # module settings from config files
201             my $setting = $self->setting();
202            
203             # plugin session must be enabled in config.xml
204             if (!$app->session->{first_visit}) {
205             $app->session->{first_visit} = time;
206             }
207             my $dt = DateTime->from_epoch(epoch => $app->session->{first_visit});
208             $view->set("first_visit", $dt->strftime("%a, %d %b %Y %H:%M:%S"));
209            
210             # save visitors count to the cache
211             $app->cache->set("visitor_count", $app->cache->get("visitor_count") + 1, "1 year");
212             $view->set("visitor_count", $app->cache->get("visitor_count"));
213              
214             return $view->out();
215             }
216             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
217             # run action and capture print statements, no returns. url: /home/news
218             sub news: GET Capture {
219              
220             my ($self, $app) = @_;
221              
222             say qq{Hello world. This content is captured from print statements.
223             The action must be marked by 'Capture' attribute. No returns.};
224              
225             }
226             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
227             # run action and capture print statements and the return value. url: /home/info
228             sub info: GET Command {
229              
230             my ($self, $app) = @_;
231              
232             say qq{This content is captured from print statements.
233             The action marked by 'Command' attribute. };
234            
235             return qq{This content is the return value on the action.};
236             }
237             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
238             # regular method, can be invoked by views:
239             # <vars type="module" method="Home::Home->welcome" message="Welcome back!" />
240             sub welcome {
241             my ($self, %args) = @_;
242             my $app = $self->app();
243             return "Nice to see you, " . $args{message};
244             }
245             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
246             1;
247              
248             =head1 YOUR FIRST VIEW 'home'
249              
250             Create an html file name it as B<home.html>, put it in the default theme folder B</path/theme/default/views>
251             and put in this file the following code:
252              
253             <vars type="widget" name="header" charset_name="UTF-8" lang_name="en" />
254              
255             {first_name} <vars name="fname" /><br>
256             {last_name} <vars name="lname" /><br>
257             {email} <vars type="var" name='email' /><br>
258             {website} <vars type="var" name="website" /><br>
259             <br>
260              
261             global variables:<br>
262             language: <vars name='lang' /><br>
263             theme: <vars name="theme" /><br>
264             base url: <vars name="base_url" /><br>
265             image url: <vars name="image_url" /><br>
266             css url: <vars name="css_url" /><br>
267             new url: <a href="<vars name="base_url" />comments" >comments</a><br>
268             image: <img src="<vars name="image_url" />logo.png" /><br>
269             <br>
270             first visit: <vars name="first_visit" /><br>
271             <br>
272              
273             {date_now} <vars type="plugin" method="Date->date" format="%a, %d %b %Y %H:%M:%S" /><br>
274             {time_now} <vars type="plugin" method="Date->time" format="%A %d, %B %Y %T %p" /><br>
275             {date_time} <vars type="plugin" method="Date::now" capture="1" format="%B %d, %Y %r" /><br>
276              
277             <br>
278             <vars type="module" method="Home::Home->welcome" message="Welcome back!" /><br>
279             <br>
280              
281             Our Version: <vars type="perl"><![CDATA[print $self->app->VERSION; return;]]></vars><br>
282             <br>
283              
284             <pre>
285             <vars type="perl">system ('dir *.cgi');</vars>
286             </pre>
287             <br>
288              
289             <vars type="var" name="singleline" width="400px" height="300px" content="ahmed<b>class/subclass">
290             cdata start here is may have html tags and 'single' and "double" qoutes
291             </vars>
292             <br>
293              
294             <vars type="var" name="multiline" width="400px" height="300px"><![CDATA[
295             cdata start here is may have html tags <b>hello</b> and 'single' and "double" qoutes
296             another cdata line
297             ]]></vars>
298             <br>
299              
300             <vars type="perl"><![CDATA[
301             say "";
302             say "<br>active language: " . $self->app->var->get("lang");
303             say "<br>active theme: " . $self->app->var->get("theme");
304             say "<br>app path: " . $self->app->var->get("path");
305             say "<br>";
306             ]]></vars>
307             <br><br>
308              
309             html content 1-5 top
310             <!--block:first-->
311             <table border="1" style="color:red;">
312             <tr class="lines">
313             <td align="left" valign="<--valign-->">
314             <b>bold</b><a href="http://www.mewsoft.com">mewsoft</a>
315             <!--hello--> <--again--><!--world-->
316             some html content here 1 top
317             <!--block:second-->
318             some html content here 2 top
319             <!--block:third-->
320             some html content here 3 top
321             <!--block:fourth-->
322             some html content here 4 top
323             <!--block:fifth-->
324             some html content here 5a
325             some html content here 5b
326             <!--endblock-->
327             <!--endblock-->
328             some html content here 3a
329             some html content here 3b
330             <!--endblock-->
331             some html content here 2 bottom
332             </tr>
333             <!--endblock-->
334             some html content here 1 bottom
335             </table>
336             <!--endblock-->
337             html content 1-5 bottom
338              
339             <br><br>
340              
341             html content 6-8 top
342             <!--block:six-->
343             some html content here 6 top
344             <!--block:seven-->
345             some html content here 7 top
346             <!--block:eight-->
347             some html content here 8a
348             some html content here 8b
349             <!--endblock-->
350             some html content here 7 bottom
351             <!--endblock-->
352             some html content here 6 bottom
353             <!--endblock-->
354             html content 6-8 bottom
355              
356             <br><br>
357              
358             <vars type="widget" name="footer" title="cairo" lang="ar" />
359              
360             =head1 YOUR FIRST WIDGETS 'header' AND 'footer'
361              
362             The framework supports widgets, widgets are small views that can be repeated in many views for easy layout and design.
363             For example, you could make the site header template as a widget called B<header> and the site footer template as a
364             widget called B<footer> and just put the required xml special tag for these widgets in all the B<Views> you want.
365             Widgets files are html files located in the theme B<'widget'> folder
366              
367             Example widget B<header.html>
368              
369             <!doctype html>
370             <html lang="{lang_code}">
371             <head>
372             <meta http-equiv="content-type" content="text/html; charset=[:charset_name:]" />
373             <title>{page_title}</title>
374             <meta name="Keywords" content="{meta_keywords}" />
375             <meta name="Description" content="{meta_description}" />
376             </head>
377             <body>
378              
379             Example widget B<footer.html>
380              
381             </body>
382             </html>
383              
384             then all you need to include the widget in the view is to insert these tags:
385              
386             <vars type="widget" name="header" charset_name="UTF-8" />
387             <vars type="widget" name="footer" />
388              
389             You can pass args to the widget like B<charset_name> to the widget above and will be replaced with their values.
390              
391              
392             =head1 LANGUAGES
393              
394             All application text is located in text files in xml format. Each language supported should be put under a folder named
395             with the iso name of the langauge under the folder path/lang.
396              
397             Example langauge file B<'general.xml'>:
398              
399             <?xml version="1.0" encoding="UTF-8" ?>
400              
401             <lang_code>en</lang_code>
402             <site_name>Site Name</site_name>
403             <home>Home</home>
404             <register>Register</register>
405             <contact>Contact</contact>
406             <about>About</about>
407             <copyright>Copyright</copyright>
408             <privacy>Privacy</privacy>
409              
410             <page_title>Create New Account</page_title>
411             <first_name>First name:</first_name>
412             <middle_name>Middle name:</middle_name>
413             <last_name>Last name:</last_name>
414             <full_name>Full name:</full_name>
415             <email>Email:</email>
416             <job>Job title:</job>
417             <website>Website:</website>
418             <agree>Agree:</agree>
419             <company>Company</company>
420              
421             <date_now>Date: </date_now>
422             <time_now>Time: </time_now>
423             <date_time>Now: </date_time>
424              
425             =head1 Routing
426              
427             The framework supports url routing, route specific short name actions like 'register' to specific plugins like Accounts/Register/create.
428              
429             Below is B<route.xml> file example should be created under the path/route folder.
430              
431             <?xml version="1.0" encoding="UTF-8" ?>
432              
433             <home route="/home" action="/Home/Home/home" method="get" />
434             <register route="/register" action="/Accounts/Register/register" method="get" defaults="year=1900|month=1|day=23" />
435             <post route="/blog/post/{cid:\d+}/{id:\d+}" action="/Blog/Article/Post" method="post" />
436             <browse route="/blog/{id:\d+}" action="/Blog/Article/Browse" method="get" />
437             <view route="/blog/view/{id:\d+}" action="/Blog/Article/View" method="get" />
438             <edit route="/blog/edit/{id:\d+}" action="/Blog/Article/Edit" method="get" />
439              
440             =head1 CONFIG
441              
442             The framework supports loading and working with config files in xml formate located in the folder 'config'.
443              
444             Example config file path/config/config.xml:
445              
446             <?xml version="1.0" encoding="UTF-8" ?>
447              
448             <app>
449             <config></config>
450             <route>route.xml</route>
451             <log_file>log.pm</log_file>
452             <action_name>action,route,cmd</action_name>
453             <default_route>/Home/Home/home</default_route>
454             <charset>utf-8</charset>
455             <theme>default</theme>
456             <lang>en-US</lang>
457             <lang_param_key>lang</lang_param_key>
458             <lang_cookie_key>lang</lang_cookie_key>
459             <lang_session_key>lang</lang_session_key>
460             <lang_file>general</lang_file>
461             </app>
462              
463             <admin>
464             <user>admin_user</user>
465             <password>admin_pass</password>
466             </admin>
467              
468             <dbi>
469             <driver>mysql</driver>
470             <host>localhost</host>
471             <dsn></dsn>
472             <port>3306</port>
473             <name>auctions</name>
474             <user>auctions</user>
475             <pass>auctions</pass>
476             <attr>
477             </attr>
478             <encoding>utf8</encoding>
479             </dbi>
480              
481             <module>
482             <home>
483             <header>home</header>
484             <footer>footer</footer>
485             </home>
486             </module>
487              
488             <plugin>
489             <email>
490             <transport>Sendmail</transport>
491             <sendmail>/usr/sbin/sendmail</sendmail>
492             </email>
493              
494             <session>
495             <autoload>1</autoload>
496             <key>nile_session_key</key>
497             <expire>1 year</expire>
498             <cache>
499             <driver>File</driver>
500             <root_dir></root_dir>
501             <namespace>session</namespace>
502             </cache>
503             <cookie>
504             <path>/</path>
505             <secure></secure>
506             <domain></domain>
507             <httponly></httponly>
508             </cookie>
509             </session>
510              
511             <cache>
512             <autoload>0</autoload>
513             </cache>
514             </plugin>
515              
516             =head1 APPLICATION INSTANCE SHARED DATA
517              
518             The framework is fully Object-oriented to allow multiple separate instances. Inside any module or plugin
519             you will be able to access the application instance by calling the method C<< $self->app >> which is automatically
520             injected into all modules with the application instance.
521              
522             The plugins and modules files will have the following features.
523              
524             Moose enabled
525             Strict and Warnings enabled.
526             a Moose attribute called C<app> injected holds the application singleton instance to access all the data and methods.
527              
528             Inside your modules and plugins, you will be able to access the application instance by:
529            
530             my $app = $self->app;
531              
532             Then you can access the application methods and objects like:
533            
534             $app->request->param("username");
535             # same as
536             $self->app->request->param("username");
537              
538             $app->response->code(200);
539              
540             $app->var->set("name", "value");
541              
542             =head1 URL REWRITE .htaccess for CGI and FCGI
543              
544             To hide the script name B<index.cgi> from the url and allow nice SEO url routing, you need to turn on url rewrite on
545             your web server and have .htaccess file in the application folder with the index.cgi.
546              
547             Below is a sample .htaccess which redirects all requests to index.cgi file and hides index.cgi from the url,
548             so instead of calling the application as:
549              
550             http://domain.com/index.cgi?action=register
551              
552             using the .htaccess you will be able to call it as:
553              
554             http://domain.com/register
555              
556             without any changes in the code.
557              
558             For direct FCGI, just replace .cgi with .fcgi in the .htaccess and rename index.cgi to index.fcgi.
559              
560             # Don't show directory listings for URLs which map to a directory.
561             Options -Indexes -MultiViews
562              
563             # Follow symbolic links in this directory.
564             Options +FollowSymLinks
565              
566             #Note that AllowOverride Options and AllowOverride FileInfo must both be in effect for these directives to have any effect,
567             #i.e. AllowOverride All in httpd.conf
568             Options +ExecCGI
569             AddHandler cgi-script cgi pl
570              
571             # Set the default handler.
572             DirectoryIndex index.cgi index.html index.shtml
573              
574             # save this file as UTF-8 and enable the next line for utf contents
575             #AddDefaultCharset UTF-8
576              
577             # REQUIRED: requires mod_rewrite to be enabled in Apache.
578             # Please check that, if you get an "Internal Server Error".
579             RewriteEngine On
580             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
581             # force use www with http and https so http://domain.com redirect to http://www.domain.com
582             #add www with https support - you have to put this in .htaccess file in the site root folder
583             # skip local host
584             RewriteCond %{HTTP_HOST} !^localhost
585             # skip IP addresses
586             RewriteCond %{HTTP_HOST} ^([a-z.]+)$ [NC]
587             RewriteCond %{HTTP_HOST} !^www\.
588             RewriteCond %{HTTPS}s ^on(s)|''
589             RewriteRule ^ http%1://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
590             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
591             RewriteCond %{REQUEST_FILENAME} !-f
592             RewriteCond %{REQUEST_FILENAME} !-d
593             RewriteCond %{REQUEST_URI} !=/favicon.ico
594             RewriteRule ^(.*)$ index.cgi [L,QSA]
595              
596             =head1 REQUEST
597              
598             The http request is available as a shared object extending the L<CGI::Simple> module. This means that all methods supported
599             by L<CGI::Simple> is available with the additions of these few methods:
600              
601             is_ajax
602             is_post
603             is_get
604             is_head
605             is_put
606             is_delete
607             is_patch
608              
609             You access the request object by $self->app->request.
610              
611             =head1 ERRORS, WARNINGS, ABORTING
612            
613             To abort the application at anytime with optional message and stacktrace, call the method:
614            
615             $self->app->abort("application error, can not find file required");
616              
617             For fatal errors with custom error message
618            
619             $self->app->error("error message");
620              
621             For fatal errors with custom error message and full starcktrace
622            
623             $self->app->errors("error message");
624              
625             For displaying warning message
626              
627             $self->app->warning("warning message");
628              
629             =head1 LOGS
630              
631             The framework supports a log object which is a L<Log::Tiny> object which supports unlimited log categories, so simply
632             you can do this:
633              
634             $app->log->info("application run start");
635             $app->log->DEBUG("application run start");
636             $app->log->ERROR("application run start");
637             $app->log->INFO("application run start");
638             $app->log->ANYTHING("application run start");
639              
640             =head1 FILE
641              
642             The file object provides tools for reading files, folders, and most of the functions in the modules L<File::Spec> and L<File::Basename>.
643              
644             to get file content as single string or array of strings:
645            
646             $content = $app->file->get($file);
647             @lines = $app->file->get($file);
648              
649             supports options same as L<File::Slurp>.
650              
651             To get list of files in a specific folder:
652            
653             #files($dir, $match, $relative)
654             @files = $app->file->files("c:/apache/htdocs/nile/", "*.pm, *.cgi");
655            
656             #files_tree($dir, $match, $relative, $depth)
657             @files = $app->file->files_tree("c:/apache/htdocs/nile/", "*.pm, *.cgi");
658              
659             #folders($dir, $match, $relative)
660             @folders = $app->file->folders("c:/apache/htdocs/nile/", "", 1);
661              
662             #folders_tree($dir, $match, $relative, $depth)
663             @folders = $app->file->folders_tree("c:/apache/htdocs/nile/", "", 1);
664              
665             =head1 XML
666              
667             Loads xml files into hash tree using L<XML::TreePP>
668            
669             $xml = $app->xml->load("configs.xml");
670              
671             =head1 DBI
672              
673             See L<Nile::DBI>
674              
675             The DBI class provides methods for connecting to the sql database and easy methods for sql operations.
676              
677             =head1 METHODS
678              
679             =cut
680             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
681             # the first thing to do, catch and show errors nicely
682             BEGIN {
683 1     1   4 $|=1;
684 1     1   15498 use CGI::Carp qw(fatalsToBrowser warningsToBrowser set_message);
  1         3521  
  1         4  
685 1     1   2053 use Devel::StackTrace;
  1         5008  
  1         45  
686 1     1   669 use Devel::StackTrace::AsHTML;
  1         10821  
  1         48  
687 1     1   437 use PadWalker;
  1         22125  
  1         105  
688 1     1   802 use Devel::StackTrace::WithLexicals;
  1         2516  
  1         102  
689              
690             sub handle_errors {
691 0     0   0 my $msg = shift;
692             #my $trace = Devel::StackTrace->new(indent => 1, message => $msg, ignore_package => [qw(Carp CGI::Carp)]);
693 0         0 my $trace = Devel::StackTrace::WithLexicals->new(indent => 1, message => $msg, ignore_package => [qw(Carp CGI::Carp)]);
694             #$trace->frames(reverse $trace->frames);
695 0         0 print $trace->as_html;
696             }
697 1         8 set_message(\&handle_errors);
698             }
699             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
700 1     1   862 use Moose;
  1         345422  
  1         8  
701 1     1   6110 use namespace::autoclean;
  1         1139  
  1         4  
702 1     1   543 use MooseX::MethodAttributes;
  1         102244  
  1         6  
703             #use MooseX::ClassAttribute;
704              
705 1     1   75663 use utf8;
  1         9  
  1         6  
706 1     1   32 use File::Spec;
  1         1  
  1         15  
707 1     1   4 use File::Basename;
  1         1  
  1         75  
708 1     1   5 use Cwd;
  1         1  
  1         42  
709 1     1   1772 use URI;
  1         3860  
  1         23  
710 1     1   522 use Encode ();
  1         9609  
  1         23  
711 1     1   5 use URI::Escape;
  1         1  
  1         41  
712 1     1   541 use Crypt::RC4;
  1         471  
  1         53  
713             #use Crypt::CBC;
714 1     1   698 use Capture::Tiny ();
  1         27943  
  1         40  
715 1     1   645 use Time::Local;
  1         1981  
  1         73  
716 1     1   619 use File::Slurp;
  1         12986  
  1         66  
717 1     1   7 use Time::HiRes qw(gettimeofday tv_interval);
  1         2  
  1         6  
718 1     1   549 use MIME::Base64 3.11 qw(encode_base64 decode_base64 decode_base64url encode_base64url);
  1         536  
  1         57  
719              
720 1     1   6 use Data::Dumper;
  1         2  
  1         48  
721             $Data::Dumper::Deparse = 1; #stringify coderefs
722             #use LWP::UserAgent;
723              
724             #no warnings qw(void once uninitialized numeric);
725              
726 1     1   454 use Nile::App;
  1         2  
  1         13  
727 1     1   8 use Nile::Say;
  1         1  
  1         7  
728 1     1   5 use Nile::Plugin;
  1         1  
  1         9  
729 1     1   7 use Nile::Plugin::Object;
  1         2  
  1         15  
730 1     1   19 use Nile::Module;
  1         1  
  1         10  
731 1     1   5 use Nile::View;
  1         2  
  1         11  
732 1     1   19 use Nile::XML;
  1         3  
  1         15  
733 1     1   32 use Nile::Var;
  1         3  
  1         12  
734 1     1   31 use Nile::File;
  1         2  
  1         9  
735 1     1   19 use Nile::Lang;
  1         1  
  1         10  
736 1     1   17 use Nile::Config;
  1         2  
  1         9  
737 1     1   19 use Nile::Router;
  1         1  
  1         8  
738 1     1   17 use Nile::Dispatcher;
  1         1  
  1         8  
739 1     1   14 use Nile::DBI;
  1         3  
  1         5  
740 1     1   660 use Nile::Setting;
  1         4  
  1         15  
741 1     1   43 use Nile::Timer;
  1         2  
  1         6  
742 1     1   28 use Nile::HTTP::Request;
  1         2  
  1         9  
743 1     1   20 use Nile::HTTP::Response;
  1         1  
  1         8  
744              
745             #use base 'Import::Base';
746 1     1   17 use Import::Into;
  1         1  
  1         8  
747 1     1   21 use Module::Load;
  1         1  
  1         9  
748 1     1   54 use Module::Runtime qw(use_module);
  1         2  
  1         7  
749             our @EXPORT_MODULES = (
750             #strict => [],
751             #warnings => [],
752             Moose => [],
753             utf8 => [],
754             #'File::Spec' => [],
755             #'File::Basename' => [],
756             Cwd => [],
757             'Nile::Say' => [],
758             'MooseX::MethodAttributes' => [],
759             );
760              
761 1     1   62 use base 'Exporter';
  1         1  
  1         248  
762             our @EXPORT = qw();
763             our @EXPORT_OK = qw();
764             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
765             sub import {
766              
767 1     1   9 my ($class, @args) = @_;
768              
769 1         3 my ($package, $script) = caller;
770            
771             # import list of modules to the calling package
772 1         5 my @modules = @EXPORT_MODULES;
773 1         4 while (@modules) {
774 5         5629 my $module = shift @modules;
775 5 50       14 my $imports = ref $modules[0] eq 'ARRAY' ? shift @modules : [];
776 5         14 use_module($module)->import::into($package, @{$imports});
  5         127  
777             }
778             #------------------------------------------------------
779 1         6429 $class->detect_app_path($script);
780             #------------------------------------------------------
781 1         3 my $caller = $class.'::';
782             {
783 1     1   6 no strict 'refs';
  1         1  
  1         2208  
  1         1  
784 1         2 @{$caller.'EXPORT'} = @EXPORT;
  1         4  
785 1         3 foreach my $sub (@EXPORT) {
786 0 0       0 next if (*{"$caller$sub"}{CODE});
  0         0  
787 0         0 *{"$caller$sub"} = \*{$sub};
  0         0  
  0         0  
788             }
789             }
790              
791 1         103 $class->export_to_level(1, $class, @args);
792             #------------------------------------------------------
793             }
794             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
795             sub detect_app_path {
796              
797 1     1 0 2 my ($self, $script) = @_;
798              
799 1   33     3 $script ||= (caller)[1];
800              
801 1         64 my ($vol, $dirs, $name) = File::Spec->splitpath(File::Spec->rel2abs($script));
802              
803 1 50       54 if (-d (my $fulldir = File::Spec->catdir($dirs, $name))) {
804 0         0 $dirs = $fulldir;
805 0         0 $name = "";
806             }
807              
808 1 50       8 my $path = $vol? File::Spec->catpath($vol, $dirs) : File::Spec->catdir($dirs);
809            
810 1         7 $ENV{NILE_APP_DIR} = $path;
811              
812 1         3 return ($path);
813             }
814             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
815             sub BUILD { # our sub new {...}
816 0     0 0   my ($self, $arg) = @_;
817             }
818             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
819             =head2 init()
820            
821             use Nile;
822              
823             my $app = Nile->new();
824              
825             $app->init({
826             # base application path, auto detected if not set
827             path => dirname(File::Spec->rel2abs(__FILE__)),
828            
829             # load config files, default extension is xml
830             config => [ qw(config) ],
831              
832             # force run mode if not auto detected by default. modes: "psgi", "fcgi" (direct), "cgi" (direct)
833             #mode => "fcgi", # psgi, cgi, fcgi
834             });
835              
836             Initialize the application with the shared and safe sessions settings.
837              
838             =cut
839              
840             has 'init' => (
841             is => 'rw',
842             isa => 'HashRef',
843             default => sub { +{} }
844             );
845             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
846             =head2 var()
847            
848             See L<Nile::Var>.
849              
850             =cut
851              
852             has 'var' => (
853             is => 'rw',
854             lazy => 1,
855             default => sub {
856             shift->object ("Nile::Var", @_);
857             }
858             );
859             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
860             =head2 config()
861            
862             See L<Nile::Config>.
863              
864             =cut
865              
866             has 'config' => (
867             is => 'rw',
868             isa => 'Nile::Config',
869             lazy => 1,
870             default => sub {
871             shift->object("Nile::Config", @_);
872             }
873             );
874             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
875             =head2 run()
876            
877             $app->run();
878              
879             Run the application and dispatch the command.
880              
881             =cut
882              
883             sub run {
884              
885 0     0 0   my ($self, $arg) = @_;
886            
887             #$self->log->info("application run start in mode: ". uc($self->mode));
888             #say "run_time: " . $self->run_time->total;
889             #------------------------------------------------------
890 0           $arg = $self->init();
891              
892 0           my ($package, $script) = caller;
893            
894 0   0       $arg->{path} ||= $self->detect_app_path($script);
895              
896 0           my $file = $self->file;
897              
898             # setup the path for the app folders
899 0           foreach (qw(api cache cmd config cron data file lib log route temp web)) {
900 0           $self->var->set($_."_dir" => $file->catdir($arg->{path}, $_));
901             }
902              
903             $self->var->set(
904 0   0       'path' => $arg->{path},
      0        
      0        
905             'base_dir' => $arg->{path},
906             'langs_dir' => $file->catdir($arg->{path}, "lang"),
907             'themes_dir' => $file->catdir($arg->{path}, "theme"),
908             'log_file' => $arg->{log_file} || "log.pm",
909             'action_name' => $arg->{action_name} || "action,route,cmd",
910             'default_route' => $arg->{default_route} || "/Home/Home/index",
911             );
912            
913 0           push @INC, $self->var->get("lib_dir");
914             #------------------------------------------------------
915             # detect and load request and response handler classes
916 0   0       $arg->{mode} ||= "cgi";
917 0           $arg->{mode} = lc($arg->{mode});
918 0           $self->mode($arg->{mode});
919            
920             #$self->log->debug("mode: $arg{mode}");
921              
922             # force PSGI if PLACK_ENV is set
923 0 0         if ($ENV{'PLACK_ENV'}) {
924 0           $self->mode("psgi");
925             }
926             #$self->log->debug("mode after PLACK_ENV: $arg{mode}");
927            
928             # FCGI sets $ENV{GATEWAY_INTERFACE }=> 'CGI/1.1' inside the accept request loop but nothing is set before the accept loop
929             # command line invocations will not set this variable also
930 0 0         if ($self->mode() ne "psgi") {
931 0 0         if (exists $ENV{GATEWAY_INTERFACE} ) {
932             # CGI
933 0           $self->mode("cgi");
934             }
935             else {
936             # FCGI or command line
937 0           $self->mode("fcgi");
938             }
939             }
940            
941             #$self->log->debug("mode to run: $arg{mode}");
942              
943 0 0         if ($self->mode() eq "psgi") {
    0          
944 0           load Nile::HTTP::Request::PSGI;
945 0           load Nile::Handler::PSGI;
946             }
947             elsif ($self->mode() eq "fcgi") {
948 0           load Nile::HTTP::Request;
949 0           load Nile::Handler::CGI;
950 0           load Nile::Handler::FCGI;
951             }
952             else {
953 0           load Nile::HTTP::Request;
954 0           load Nile::Handler::CGI;
955             }
956             #------------------------------------------------------
957             # load config files from init
958 0           foreach (@{$arg->{config}}) {
  0            
959             #$self->config->xml->keep_order(1);
960 0           $self->config->load($_);
961             }
962             #------------------------------------------------------
963             #------------------------------------------------------
964             # load extra config files from config files settings
965 0           foreach my $config ($self->config->get("app/config")) {
966 0           $config = $self->filter->trim($config);
967 0 0         $self->config->load($config) if ($config);
968             }
969             #------------------------------------------------------
970             # load route files
971 0           foreach my $route($self->config->get("app/route")) {
972 0           $route = $self->filter->trim($route);
973 0 0         $self->router->load($route) if ($route);
974             }
975             #------------------------------------------------------
976 0           foreach my $config (qw(charset action_name lang theme default_route log_file)) {
977 0 0         if ($self->config->get("app/$config")) {
978 0           $self->var->set($config, $self->config->get("app/$config"));
979             }
980             }
981             #------------------------------------------------------
982 0           my $class = "Nile::Handler::" . uc($self->mode());
983 0           my $handler = $self->object($class);
984 0           my $psgi = $handler->run();
985              
986             #say "run_time: " . $self->run_time->total;
987             #$self->log->info("application run end");
988            
989             # return the PSGI app
990 0           return $psgi;
991             }
992             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
993             =head2 prefix()
994            
995             # from here, any route handler is defined to /forum/*:
996             $app->prefix("/forum");
997            
998             # will match '/forum/login'
999             $app->action("get", "/login", sub {return "Forum login"});
1000              
1001             Defines a prefix for each route handler from now on.
1002              
1003             =cut
1004              
1005             has 'prefix' => (
1006             is => 'rw',
1007             default => "",
1008             );
1009             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1010             =head2 action()
1011            
1012             # inline actions, return content. url: /forum/home
1013             $app->action("get", "/forum/home", sub {
1014             my ($self) = @_;
1015             # $self is set to the application context object same as $self->app in plugins
1016             my $content = "Host: " . ($self->request->virtual_host || "") ."<br>\n";
1017             $content .= "Request method: " . ($self->request->request_method || "") . "<br>\n";
1018             $content .= "App Mode: " . $self->mode . "<br>\n";
1019             $content .= "Time: ". time . "<br>\n";
1020             $content .= "Hello world from inline action /forum/home" ."<br>\n";
1021             $content .= "أحمد الششتاوى" ."<br>\n";
1022             $self->response->encoded(0); # encode content
1023             return $content;
1024             });
1025              
1026             Add inline action, return content to the dispatcher.
1027              
1028             =cut
1029              
1030             sub action {
1031 0     0 0   my $self = shift;
1032 0           $self->add_action_route(undef, @_);
1033             }
1034             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1035             =head2 capture()
1036            
1037             # inline actions, capture print statements, no returns. url: /accounts/login
1038             $app->capture("get", "/accounts/login", sub {
1039             my ($self) = @_;
1040             # $self is set to the application context object same as $self->app in plugins
1041             say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
1042             say "Request method: " . ($self->request->request_method || "") . "<br>\n";
1043             say "App Mode: " . $self->mode . "<br>\n";
1044             say "Time: ". time . "<br>\n";
1045             say "Hello world from inline action with capture /accounts/login", "<br>\n";
1046             say $self->encode("أحمد الششتاوى ") ."<br>\n";
1047             $self->response->encoded(1); # content already encoded
1048             });
1049              
1050             Add inline action, capture print statements, no returns to the dispatcher.
1051              
1052             =cut
1053              
1054             sub capture {
1055 0     0 0   my $self = shift;
1056 0           $self->add_action_route("capture", @_);
1057             }
1058             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1059             =head2 command()
1060            
1061             # inline actions, capture print statements and return value. url: /blog/new
1062             $app->command("get", "/blog/new", sub {
1063             my ($self) = @_;
1064             # $self is set to the application context object same as $self->app in plugins
1065             say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
1066             say "Request method: " . ($self->request->request_method || "") . "<br>\n";
1067             say "App Mode: " . $self->mode . "<br>\n";
1068             say "Time: ". time . "<br>\n";
1069             say "Hello world from inline action with capture /blog/new and return value.", "<br>\n";
1070             say $self->encode("أحمد الششتاوى ") ."<br>\n";
1071             $self->response->encoded(1); # content already encoded
1072             return " This value is returned from the command.";
1073             });
1074              
1075             Add inline action, capture print statements and returns to the dispatcher.
1076              
1077             =cut
1078              
1079             sub command {
1080 0     0 0   my $self = shift;
1081 0           $self->add_action_route("command", @_);
1082             }
1083             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1084             sub add_action_route {
1085 0     0 0   my $self = shift;
1086 0           my $type = shift;
1087 0           my ($method, $route, $action) = $self->action_args(@_);
1088 0 0         if ($self->prefix) {
1089 0           $route = $self->prefix.$route;
1090             }
1091             $self->router->add_route(
1092 0           name => "",
1093             path => $route,
1094             target => $action,
1095             method => $method,
1096             defaults => {
1097             #id => 1
1098             },
1099             attributes => $type,
1100             );
1101             }
1102             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1103             sub action_args {
1104            
1105 0     0 0   my $self = shift;
1106              
1107             #my @methods = qw(get post put patch delete options head);
1108              
1109 0           my ($method, $route, $action);
1110              
1111 0 0         if (@_ == 1) {
    0          
    0          
1112             #$app->action(sub {});
1113 0           ($action) = @_;
1114             }
1115             elsif (@_ == 2) {
1116             #$app->action("/home", sub {});
1117 0           ($route, $action) = @_;
1118             }
1119             elsif (@_ == 3) {
1120             #$app->action("get", "/home", sub {});
1121 0           ($method, $route, $action) = @_;
1122             }
1123             else {
1124 0           $self->abort("Action error. Empty action and route. Syntax \$app->action(\$method, \$route, \$coderef) ");
1125             }
1126              
1127 0   0       $method ||= "";
1128 0   0       $route ||= "/";
1129            
1130 0 0         if (ref($action) ne "CODE") {
1131 0           $self->abort("Action error, must be a valid code reference. Syntax \$app->action(\$method, \$route, \$coderef) ");
1132             }
1133            
1134 0           return ($method, $route, $action);
1135             }
1136             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1137             =head2 router()
1138            
1139             See L<Nile::Router>.
1140              
1141             =cut
1142              
1143             has 'router' => (
1144             is => 'rw',
1145             isa => 'Nile::Router',
1146             lazy => 1,
1147             default => sub {
1148             shift->object("Nile::Router", @_);
1149             }
1150             );
1151             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1152             =head2 filter()
1153            
1154             See L<Nile::Filter>.
1155              
1156             =cut
1157              
1158             has 'filter' => (
1159             is => 'rw',
1160             isa => 'Nile::Filter',
1161             lazy => 1,
1162             default => sub {
1163             load Nile::Filter;
1164             shift->object("Nile::Filter", @_);
1165             }
1166             );
1167             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1168             =head2 file()
1169              
1170             See L<Nile::File>.
1171              
1172             =cut
1173              
1174             has 'file' => (
1175             is => 'rw',
1176             default => sub {
1177             shift->object("Nile::File", @_);
1178             }
1179             );
1180             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1181             =head2 xml()
1182            
1183             See L<Nile::XML>.
1184              
1185             =cut
1186              
1187             has 'xml' => (
1188             is => 'rw',
1189             lazy => 1,
1190             default => sub {
1191             my $self = shift;
1192             $self->object("Nile::XML", @_);
1193             }
1194             );
1195             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1196             =head2 mode()
1197            
1198             my $mode = $app->mode;
1199              
1200             Returns the current application mode PSGI, FCGI or CGI.
1201              
1202             =cut
1203              
1204             has 'mode' => (
1205             is => 'rw',
1206             isa => 'Str',
1207             default => "cgi",
1208             );
1209             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1210             =head2 lang()
1211            
1212             See L<Nile::Lang>.
1213              
1214             =cut
1215              
1216             has 'lang' => (
1217             is => 'rw',
1218             isa => 'Nile::Lang',
1219             lazy => 1,
1220             default => sub {
1221             shift->object("Nile::Lang", @_);
1222             }
1223             );
1224             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1225             =head2 object()
1226            
1227             $obj = $app->object("Nile::MyClass", @args);
1228             $obj = $app->object("Nile::Plugin::MyClass", @args);
1229             $obj = $app->object("Nile::Module::MyClass", @args);
1230              
1231             #...
1232              
1233             $me = $obj->app;
1234            
1235             Creates and returns an object. This automatically adds the method L<me> to the object
1236             and sets it to the current context so your object or class can access the current instance.
1237              
1238             =cut
1239              
1240             sub object {
1241              
1242 0     0 0   my ($self, $class, @args) = @_;
1243 0           my ($object);
1244            
1245             #if (@args == 1 && ref($args[0]) eq "HASH") {
1246             # # Moose single arguments must be hash ref
1247             # $object = $class->new(@args);
1248             #}
1249              
1250 0 0 0       if (@args && @args % 2) {
1251             # Moose needs args as hash, so convert odd size arrays to even for hashing
1252 0           $object = $class->new(@args, undef);
1253             }
1254             else {
1255 0           $object = $class->new(@args);
1256             }
1257              
1258             #$meta->add_method( 'hello' => sub { return "Hello inside hello method. @_" } );
1259             #$meta->add_class_attribute( $_, %options ) for @{$attrs}; #MooseX::ClassAttribute
1260             #$meta->add_class_attribute( 'cash', ());
1261              
1262             # add attribute "app" to the object
1263 0           $self->add_object_context($object);
1264            
1265             # if class has defined "main" method, then call it
1266 0 0         if ($object->can("main")) {
1267 0           my %ret = $object->main(@args);
1268 0 0         if ($ret{rebless}) {
1269 0           $object = $ret{rebless};
1270             }
1271             }
1272            
1273 0           return $object;
1274             }
1275             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1276             sub add_object_context {
1277 0     0 0   my ($self, $object) = @_;
1278 0           my $meta = $object->meta;
1279             # add method "app" or one of its alt
1280 0 0         if (!$object->can("app")) {
1281 0     0     $meta->add_attribute(app => (is => 'rw', default => sub{$self}));
  0            
1282             }
1283 0           $object->app($self);
1284             }
1285             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1286             =head2 dump()
1287            
1288             $app->dump({...});
1289              
1290             Print object to the STDOUT. Same as C<say Dumper (@_);>.
1291              
1292             =cut
1293              
1294             sub dump {
1295 0     0 1   my $self = shift;
1296 0           say Dumper (@_);
1297 0           return;
1298             }
1299             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1300             =head2 is_loaded()
1301            
1302             if ($app->is_loaded("Nile::SomeModule")) {
1303             #...
1304             }
1305            
1306             if ($app->is_loaded("Nile/SomeModule.pm")) {
1307             #...
1308             }
1309              
1310             Returns true if module is loaded, false otherwise.
1311              
1312             =cut
1313              
1314             sub is_loaded {
1315 0     0 0   my ($self, $module) = @_;
1316 0           (my $file = $module) =~ s/::/\//g;
1317 0 0         $file .= '.pm' unless ($file =~ /\.pm$/);
1318             #note: do() does unconditional loading -- no lookup in the %INC hash is made.
1319 0           exists $INC{$file};
1320             #return eval { $module->can( 'can' ) };
1321             #return UNIVERSAL::can($module,'can');
1322             }
1323             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1324             has 'loaded_modules' => (
1325             is => 'rw',
1326             isa => 'HashRef',
1327             default => sub { +{} }
1328             );
1329             =head2 load_once()
1330            
1331             $app->load_once("Module::SomeModule");
1332              
1333             Load modules if not already loaded.
1334              
1335             =cut
1336              
1337             sub load_once {
1338 0     0 0   my ($self, $module, @arg) = @_;
1339 0 0         if (!exists $self->loaded_modules->{$module}) {
1340 0           load $module;
1341 0           $self->loaded_modules->{$module} = 1;
1342             }
1343             }
1344             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1345             =head2 load_class()
1346            
1347             $app->load_class("Module::SomeModule");
1348              
1349             Load modules if not already loaded.
1350              
1351             =cut
1352              
1353             sub load_class {
1354 0     0 0   my ($self, $module, @arg) = @_;
1355              
1356 0 0         if (!$self->is_loaded($module)) {
1357 0           load $module;
1358             }
1359             }
1360             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1361             =head2 cli_mode()
1362            
1363             if ($app->cli_mode) {
1364             say "Running from the command line";
1365             }
1366             else {
1367             say "Running from web server";
1368             }
1369              
1370             Returns true if running from the command line interface, false if called from web server.
1371              
1372             =cut
1373              
1374             sub cli_mode {
1375 0     0 0   my ($self) = @_;
1376            
1377 0 0 0       if (exists $ENV{REQUEST_METHOD} || defined $ENV{GATEWAY_INTERFACE} || exists $ENV{HTTP_HOST}){
      0        
1378 0           return 0;
1379             }
1380            
1381             # PSGI
1382 0 0 0       if (exists $self->env->{REQUEST_METHOD} || defined $self->env->{GATEWAY_INTERFACE} || exists $self->env->{HTTP_HOST}){
      0        
1383 0           return 0;
1384             }
1385            
1386             # CLI
1387 0           return 1;
1388              
1389             #if (-t STDIN) { }
1390             #use IO::Interactive qw(is_interactive interactive busy);if ( is_interactive() ) {print "Running interactively\n";}
1391             }
1392             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1393             =head2 error()
1394            
1395             $app->error("error message");
1396              
1397             Fatal errors with custom error message. This is the same as C<croak> in L<CGI::Carp|CGI::Carp/croak>.
1398              
1399             =cut
1400              
1401             sub error {
1402 0     0 0   my $self = shift;
1403 0           goto &CGI::Carp::croak;
1404             }
1405              
1406             =head2 errors()
1407            
1408             $app->errors("error message");
1409              
1410             Fatal errors with custom error message and full starcktrace. This is the same as C<confess> in L<CGI::Carp|CGI::Carp/confess>.
1411              
1412             =cut
1413              
1414             sub errors {
1415 0     0 1   my $self = shift;
1416 0           goto &CGI::Carp::confess;
1417             }
1418              
1419             =head2 warn()
1420            
1421             $app->warn("warning message");
1422              
1423             Display warning message. This is the same as C<carp> in L<CGI::Carp|CGI::Carp/carp>.
1424              
1425             To view warnings in the browser, switch to the view source mode since warnings appear as
1426             a comment at the top of the page.
1427              
1428             =cut
1429              
1430             sub warn {
1431 0     0 1   my $self = shift;
1432             # warnings appear commented at the top of the page, use view source
1433 0 0         warningsToBrowser(1) unless ($self->cli_mode);
1434 0           goto &CGI::Carp::carp;
1435             }
1436              
1437             =head2 warns()
1438            
1439             $app->warns("warning message");
1440              
1441             Display warning message and full starcktrace. This is the same as C<cluck> in L<CGI::Carp|CGI::Carp/cluck>.
1442              
1443             To view warnings in the browser, switch to the view source mode since warnings appear as
1444             a comment at the top of the page.
1445              
1446             =cut
1447              
1448             sub warns {
1449 0     0 1   my $self = shift;
1450             # warnings appear commented at the top of the page, use view source
1451 0 0         warningsToBrowser(1) unless ($self->cli_mode);
1452 0           goto &CGI::Carp::cluck;
1453             }
1454             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1455             =head2 abort()
1456            
1457             $app->abort("error message");
1458              
1459             $app->abort("error title", "error message");
1460              
1461             Stop and quit the application and display message to the user. See L<Nile::Abort> module.
1462              
1463             =cut
1464              
1465             sub abort {
1466 0     0 0   my ($self) = shift;
1467 0           load Nile::Abort;
1468 0           Nile::Abort->abort(@_);
1469             }
1470             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1471             #__PACKAGE__->meta->make_immutable;#(inline_constructor => 0)
1472             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1473              
1474             =head1 Sub Modules
1475              
1476             App L<Nile::App>.
1477              
1478             Views L<Nile::View>.
1479              
1480             Shared Vars L<Nile::Var>.
1481              
1482             Langauge L<Nile::Lang>.
1483              
1484             Request L<Nile::HTTP::Request>.
1485              
1486             PSGI Request L<Nile::HTTP::Request::PSGI>.
1487              
1488             PSGI Request Base L<Nile::HTTP::PSGI>.
1489              
1490             Response L<Nile::HTTP::Response>.
1491              
1492             PSGI Handler L<Nile::Handler::PSGI>.
1493              
1494             FCGI Handler L<Nile::Handler::FCGI>.
1495              
1496             CGI Handler L<Nile::Handler::CGI>.
1497              
1498             Dispatcher L<Nile::Dispatcher>.
1499              
1500             Router L<Nile::Router>.
1501              
1502             File Utils L<Nile::File>.
1503              
1504             DBI L<Nile::DBI>.
1505              
1506             DBI Table L<Nile::DBI::Table>.
1507              
1508             XML L<Nile::XML>.
1509              
1510             Settings L<Nile::Setting>.
1511              
1512             Serializer L<Nile::Serializer>.
1513              
1514             Deserializer L<Nile::Deserializer>.
1515              
1516             Serialization Base L<Nile::Serialization>.
1517              
1518             Filter L<Nile::Filter>.
1519              
1520             MIME L<Nile::MIME>.
1521              
1522             Timer L<Nile::Timer>.
1523              
1524             Plugin L<Nile::Plugin>.
1525              
1526             Session L<Nile::Plugin::Session>.
1527              
1528             Cache L<Nile::Plugin::Cache>.
1529              
1530             Cache Redis L<Nile::Plugin::Cache::Redis>.
1531              
1532             Email L<Nile::Plugin::Email>.
1533              
1534             Paginatation L<Nile::Plugin::Paginate>.
1535              
1536             MongoDB L<Nile::Plugin::MongoDB>.
1537              
1538             Redis L<Nile::Plugin::Redis>.
1539              
1540             Memcached L<Nile::Plugin::Memcached>.
1541              
1542             Module L<Nile::Module>.
1543              
1544             Hook L<Nile::Hook>.
1545              
1546             Base L<Nile::Base>.
1547              
1548             Abort L<Nile::Abort>.
1549              
1550             =head1 Bugs
1551              
1552             This project is available on github at L<https://github.com/mewsoft/Nile>.
1553              
1554             =head1 HOMEPAGE
1555              
1556             Please visit the project's homepage at L<https://metacpan.org/release/Nile>.
1557              
1558             =head1 SOURCE
1559              
1560             Source repository is at L<https://github.com/mewsoft/Nile>.
1561              
1562             =head1 AUTHOR
1563              
1564             Ahmed Amin Elsheshtawy, احمد امين الششتاوى <mewsoft@cpan.org>
1565             Website: http://www.mewsoft.com
1566              
1567             =head1 COPYRIGHT AND LICENSE
1568              
1569             Copyright (C) 2014-2015 by Dr. Ahmed Amin Elsheshtawy mewsoft@cpan.org, support@mewsoft.com,
1570             L<https://github.com/mewsoft/Nile>, L<http://www.mewsoft.com>
1571              
1572             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
1573              
1574             =cut
1575              
1576              
1577             1;