File Coverage

blib/lib/Nile.pm
Criterion Covered Total %
statement 159 287 55.4
branch 3 54 5.5
condition 1 36 2.7
subroutine 48 69 69.5
pod 4 20 20.0
total 215 466 46.1


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.54';
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.54';
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   3 $|=1;
684 1     1   17896 use CGI::Carp qw(fatalsToBrowser warningsToBrowser set_message);
  1         3370  
  1         5  
685 1     1   554 use Devel::StackTrace;
  1         4063  
  1         23  
686 1     1   478 use Devel::StackTrace::AsHTML;
  1         7003  
  1         28  
687 1     1   427 use PadWalker;
  1         577  
  1         37  
688 1     1   471 use Devel::StackTrace::WithLexicals;
  1         1687  
  1         64  
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         4 set_message(\&handle_errors);
698             }
699             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
700 1     1   515 use Moose;
  1         325729  
  1         7  
701 1     1   5715 use namespace::autoclean;
  1         1054  
  1         5  
702 1     1   620 use MooseX::MethodAttributes;
  1         97297  
  1         7  
703             #use MooseX::ClassAttribute;
704              
705 1     1   80100 use utf8;
  1         9  
  1         5  
706 1     1   30 use File::Spec;
  1         1  
  1         19  
707 1     1   4 use File::Basename;
  1         1  
  1         71  
708 1     1   5 use Cwd;
  1         1  
  1         63  
709 1     1   513 use URI;
  1         3444  
  1         23  
710 1     1   516 use Encode ();
  1         7048  
  1         21  
711 1     1   5 use URI::Escape;
  1         1  
  1         39  
712 1     1   422 use Crypt::RC4;
  1         457  
  1         45  
713             #use Crypt::CBC;
714 1     1   487 use Capture::Tiny ();
  1         19695  
  1         30  
715 1     1   488 use Time::Local;
  1         1214  
  1         49  
716 1     1   1531 use File::Slurp;
  1         9229  
  1         60  
717 1     1   7 use Time::HiRes qw(gettimeofday tv_interval);
  1         1  
  1         5  
718 1     1   573 use MIME::Base64 3.11 qw(encode_base64 decode_base64 decode_base64url encode_base64url);
  1         638  
  1         54  
719              
720 1     1   5 use Data::Dumper;
  1         2  
  1         44  
721             $Data::Dumper::Deparse = 1; #stringify coderefs
722             #use LWP::UserAgent;
723              
724             #no warnings qw(void once uninitialized numeric);
725              
726 1     1   519 use Nile::App;
  1         3  
  1         13  
727 1     1   7 use Nile::Say;
  1         2  
  1         6  
728 1     1   8 use Nile::Plugin;
  1         2  
  1         11  
729 1     1   6 use Nile::Plugin::Object;
  1         2  
  1         14  
730 1     1   20 use Nile::Module;
  1         2  
  1         4  
731 1     1   7 use Nile::View;
  1         1  
  1         13  
732 1     1   20 use Nile::XML;
  1         1  
  1         10  
733 1     1   15 use Nile::Var;
  1         1  
  1         8  
734 1     1   16 use Nile::File;
  1         1  
  1         8  
735 1     1   16 use Nile::Lang;
  1         1  
  1         8  
736 1     1   24 use Nile::Config;
  1         1  
  1         9  
737 1     1   21 use Nile::Router;
  1         4  
  1         14  
738 1     1   35 use Nile::Dispatcher;
  1         2  
  1         13  
739 1     1   34 use Nile::DBI;
  1         2  
  1         13  
740 1     1   554 use Nile::Setting;
  1         2  
  1         17  
741 1     1   39 use Nile::Timer;
  1         2  
  1         9  
742 1     1   25 use Nile::HTTP::Request;
  1         1  
  1         12  
743 1     1   22 use Nile::HTTP::Response;
  1         1  
  1         6  
744              
745             #use base 'Import::Base';
746 1     1   17 use Import::Into;
  1         1  
  1         6  
747 1     1   14 use Module::Load;
  1         2  
  1         7  
748 1     1   49 use Module::Runtime qw(use_module);
  1         1  
  1         8  
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         218  
762             our @EXPORT = qw();
763             our @EXPORT_OK = qw();
764             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
765             sub import {
766              
767 1     1   10 my ($class, @args) = @_;
768              
769 1         3 my ($package, $script) = caller;
770            
771             # import list of modules to the calling package
772 1         4 my @modules = @EXPORT_MODULES;
773 1         4 while (@modules) {
774 5         5604 my $module = shift @modules;
775 5 50       17 my $imports = ref $modules[0] eq 'ARRAY' ? shift @modules : [];
776 5         14 use_module($module)->import::into($package, @{$imports});
  5         118  
777             }
778             #------------------------------------------------------
779 1         6392 $class->detect_app_path($script);
780             #------------------------------------------------------
781 1         4 my $caller = $class.'::';
782             {
783 1     1   5 no strict 'refs';
  1         1  
  1         2286  
  1         2  
784 1         4 @{$caller.'EXPORT'} = @EXPORT;
  1         6  
785 1         4 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         111 $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     4 $script ||= (caller)[1];
800              
801 1         61 my ($vol, $dirs, $name) = File::Spec->splitpath(File::Spec->rel2abs($script));
802              
803 1 50       44 if (-d (my $fulldir = File::Spec->catdir($dirs, $name))) {
804 0         0 $dirs = $fulldir;
805 0         0 $name = "";
806             }
807              
808 1 50       13 my $path = $vol? File::Spec->catpath($vol, $dirs) : File::Spec->catdir($dirs);
809            
810 1         12 $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             'langs_dir' => $file->catdir($arg->{path}, "lang"),
906             'themes_dir' => $file->catdir($arg->{path}, "theme"),
907             'log_file' => $arg->{log_file} || "log.pm",
908             'action_name' => $arg->{action_name} || "action,route,cmd",
909             'default_route' => $arg->{default_route} || "/Home/Home/index",
910             );
911            
912 0           push @INC, $self->var->get("lib_dir");
913             #------------------------------------------------------
914             # detect and load request and response handler classes
915 0   0       $arg->{mode} ||= "cgi";
916 0           $arg->{mode} = lc($arg->{mode});
917 0           $self->mode($arg->{mode});
918            
919             #$self->log->debug("mode: $arg{mode}");
920              
921             # force PSGI if PLACK_ENV is set
922 0 0         if ($ENV{'PLACK_ENV'}) {
923 0           $self->mode("psgi");
924             }
925             #$self->log->debug("mode after PLACK_ENV: $arg{mode}");
926            
927             # FCGI sets $ENV{GATEWAY_INTERFACE }=> 'CGI/1.1' inside the accept request loop but nothing is set before the accept loop
928             # command line invocations will not set this variable also
929 0 0         if ($self->mode() ne "psgi") {
930 0 0         if (exists $ENV{GATEWAY_INTERFACE} ) {
931             # CGI
932 0           $self->mode("cgi");
933             }
934             else {
935             # FCGI or command line
936 0           $self->mode("fcgi");
937             }
938             }
939            
940             #$self->log->debug("mode to run: $arg{mode}");
941              
942 0 0         if ($self->mode() eq "psgi") {
    0          
943 0           load Nile::HTTP::Request::PSGI;
944 0           load Nile::Handler::PSGI;
945             }
946             elsif ($self->mode() eq "fcgi") {
947 0           load Nile::HTTP::Request;
948 0           load Nile::Handler::CGI;
949 0           load Nile::Handler::FCGI;
950             }
951             else {
952 0           load Nile::HTTP::Request;
953 0           load Nile::Handler::CGI;
954             }
955             #------------------------------------------------------
956             # load config files from init
957 0           foreach (@{$arg->{config}}) {
  0            
958             #$self->config->xml->keep_order(1);
959 0           $self->config->load($_);
960             }
961             #------------------------------------------------------
962             #------------------------------------------------------
963             # load extra config files from config files settings
964 0           foreach my $config ($self->config->get("app/config")) {
965 0           $config = $self->filter->trim($config);
966 0 0         $self->config->load($config) if ($config);
967             }
968             #------------------------------------------------------
969             # load route files
970 0           foreach my $route($self->config->get("app/route")) {
971 0           $route = $self->filter->trim($route);
972 0 0         $self->router->load($route) if ($route);
973             }
974             #------------------------------------------------------
975 0           foreach my $config (qw(charset action_name lang theme default_route log_file)) {
976 0 0         if ($self->config->get("app/$config")) {
977 0           $self->var->set($config, $self->config->get("app/$config"));
978             }
979             }
980             #------------------------------------------------------
981 0           my $class = "Nile::Handler::" . uc($self->mode());
982 0           my $handler = $self->object($class);
983 0           my $psgi = $handler->run();
984              
985             #say "run_time: " . $self->run_time->total;
986             #$self->log->info("application run end");
987            
988             # return the PSGI app
989 0           return $psgi;
990             }
991             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
992             =head2 action()
993            
994             # inline actions, return content. url: /forum/home
995             $app->action("get", "/forum/home", sub {
996             my ($self) = @_;
997             # $self is set to the application context object same as $self->app in plugins
998             my $content = "Host: " . ($self->request->virtual_host || "") ."<br>\n";
999             $content .= "Request method: " . ($self->request->request_method || "") . "<br>\n";
1000             $content .= "App Mode: " . $self->mode . "<br>\n";
1001             $content .= "Time: ". time . "<br>\n";
1002             $content .= "Hello world from inline action /forum/home" ."<br>\n";
1003             $content .= "أحمد الششتاوى" ."<br>\n";
1004             $self->response->encoded(0); # encode content
1005             return $content;
1006             });
1007              
1008             Add inline action, return content to the dispatcher.
1009              
1010             =cut
1011              
1012             sub action {
1013 0     0 0   my $self = shift;
1014 0           $self->add_action_route(undef, @_);
1015             }
1016             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1017             =head2 capture()
1018            
1019             # inline actions, capture print statements, no returns. url: /accounts/login
1020             $app->capture("get", "/accounts/login", sub {
1021             my ($self) = @_;
1022             # $self is set to the application context object same as $self->app in plugins
1023             say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
1024             say "Request method: " . ($self->request->request_method || "") . "<br>\n";
1025             say "App Mode: " . $self->mode . "<br>\n";
1026             say "Time: ". time . "<br>\n";
1027             say "Hello world from inline action with capture /accounts/login", "<br>\n";
1028             say $self->encode("أحمد الششتاوى ") ."<br>\n";
1029             $self->response->encoded(1); # content already encoded
1030             });
1031              
1032             Add inline action, capture print statements, no returns to the dispatcher.
1033              
1034             =cut
1035              
1036             sub capture {
1037 0     0 0   my $self = shift;
1038 0           $self->add_action_route("capture", @_);
1039             }
1040             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1041             =head2 command()
1042            
1043             # inline actions, capture print statements and return value. url: /blog/new
1044             $app->command("get", "/blog/new", sub {
1045             my ($self) = @_;
1046             # $self is set to the application context object same as $self->app in plugins
1047             say "Host: " . ($self->request->virtual_host || "") . "<br>\n";
1048             say "Request method: " . ($self->request->request_method || "") . "<br>\n";
1049             say "App Mode: " . $self->mode . "<br>\n";
1050             say "Time: ". time . "<br>\n";
1051             say "Hello world from inline action with capture /blog/new and return value.", "<br>\n";
1052             say $self->encode("أحمد الششتاوى ") ."<br>\n";
1053             $self->response->encoded(1); # content already encoded
1054             return " This value is returned from the command.";
1055             });
1056              
1057             Add inline action, capture print statements and returns to the dispatcher.
1058              
1059             =cut
1060              
1061             sub command {
1062 0     0 0   my $self = shift;
1063 0           $self->add_action_route("command", @_);
1064             }
1065             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1066             sub add_action_route {
1067 0     0 0   my $self = shift;
1068 0           my $type = shift;
1069 0           my ($method, $route, $action) = $self->action_args(@_);
1070 0           $self->router->add_route(
1071             name => "",
1072             path => $route,
1073             target => $action,
1074             method => $method,
1075             defaults => {
1076             #id => 1
1077             },
1078             attributes => $type,
1079             );
1080             }
1081             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1082             sub action_args {
1083            
1084 0     0 0   my $self = shift;
1085              
1086             #my @methods = qw(get post put patch delete options head);
1087              
1088 0           my ($method, $route, $action);
1089              
1090 0 0         if (@_ == 1) {
    0          
    0          
1091             #$app->action(sub {});
1092 0           ($action) = @_;
1093             }
1094             elsif (@_ == 2) {
1095             #$app->action("/home", sub {});
1096 0           ($route, $action) = @_;
1097             }
1098             elsif (@_ == 3) {
1099             #$app->action("get", "/home", sub {});
1100 0           ($method, $route, $action) = @_;
1101             }
1102             else {
1103 0           $self->abort("Action error. Empty action and route. Syntax \$app->action(\$method, \$route, \$coderef) ");
1104             }
1105              
1106 0   0       $method ||= "";
1107 0   0       $route ||= "/";
1108            
1109 0 0         if (ref($action) ne "CODE") {
1110 0           $self->abort("Action error, must be a valid code reference. Syntax \$app->action(\$method, \$route, \$coderef) ");
1111             }
1112            
1113 0           return ($method, $route, $action);
1114             }
1115             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1116             =head2 router()
1117            
1118             See L<Nile::Router>.
1119              
1120             =cut
1121              
1122             has 'router' => (
1123             is => 'rw',
1124             isa => 'Nile::Router',
1125             lazy => 1,
1126             default => sub {
1127             shift->object("Nile::Router", @_);
1128             }
1129             );
1130             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1131             =head2 filter()
1132            
1133             See L<Nile::Filter>.
1134              
1135             =cut
1136              
1137             has 'filter' => (
1138             is => 'rw',
1139             isa => 'Nile::Filter',
1140             lazy => 1,
1141             default => sub {
1142             load Nile::Filter;
1143             shift->object("Nile::Filter", @_);
1144             }
1145             );
1146             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1147             =head2 file()
1148              
1149             See L<Nile::File>.
1150              
1151             =cut
1152              
1153             has 'file' => (
1154             is => 'rw',
1155             isa => 'Nile::File',
1156             default => sub {
1157             shift->object("Nile::File", @_);
1158             }
1159             );
1160             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1161             =head2 xml()
1162            
1163             See L<Nile::XML>.
1164              
1165             =cut
1166              
1167             has 'xml' => (
1168             is => 'rw',
1169             lazy => 1,
1170             default => sub {
1171             my $self = shift;
1172             $self->object("Nile::XML", @_);
1173             }
1174             );
1175             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1176             =head2 mode()
1177            
1178             my $mode = $app->mode;
1179              
1180             Returns the current application mode PSGI, FCGI or CGI.
1181              
1182             =cut
1183              
1184             has 'mode' => (
1185             is => 'rw',
1186             isa => 'Str',
1187             default => "cgi",
1188             );
1189             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1190             =head2 lang()
1191            
1192             See L<Nile::Lang>.
1193              
1194             =cut
1195              
1196             has 'lang' => (
1197             is => 'rw',
1198             isa => 'Nile::Lang',
1199             lazy => 1,
1200             default => sub {
1201             shift->object("Nile::Lang", @_);
1202             }
1203             );
1204             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1205             =head2 object()
1206            
1207             $obj = $app->object("Nile::MyClass", @args);
1208             $obj = $app->object("Nile::Plugin::MyClass", @args);
1209             $obj = $app->object("Nile::Module::MyClass", @args);
1210              
1211             #...
1212              
1213             $me = $obj->app;
1214            
1215             Creates and returns an object. This automatically adds the method L<me> to the object
1216             and sets it to the current context so your object or class can access the current instance.
1217              
1218             =cut
1219              
1220             sub object {
1221              
1222 0     0 0   my ($self, $class, @args) = @_;
1223 0           my ($object, $app);
1224            
1225             #if (@args == 1 && ref($args[0]) eq "HASH") {
1226             # # Moose single arguments must be hash ref
1227             # $object = $class->new(@args);
1228             #}
1229              
1230 0 0 0       if (@args && @args % 2) {
1231             # Moose needs args as hash, so convert odd size arrays to even for hashing
1232 0           $object = $class->new(@args, undef);
1233             }
1234             else {
1235 0           $object = $class->new(@args);
1236             }
1237              
1238 0           my $meta = $object->meta;
1239              
1240             #$meta->add_method( 'hello' => sub { return "Hello inside hello method. @_" } );
1241             #$meta->add_class_attribute( $_, %options ) for @{$attrs}; #MooseX::ClassAttribute
1242             #$meta->add_class_attribute( 'cash', ());
1243              
1244             # add method "me" or one of its alt
1245 0           $self->add_object_context($object, $meta);
1246              
1247             # if class has defined "main" method, then call it
1248 0 0         if ($object->can("main")) {
1249 0           my %ret = $object->main(@args);
1250 0 0         if ($ret{rebless}) {
1251 0           $object = $ret{rebless};
1252             }
1253             }
1254            
1255             #no strict 'refs';
1256             #*{"$object"."::app"} = \&app;
1257             #${"${package}::$apps"} = 1;
1258            
1259 0           return $object;
1260             }
1261             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1262             sub add_object_context {
1263 0     0 0   my ($self, $object, $meta) = @_;
1264 0   0       $meta ||= $object->meta;
1265             # add method "me" or one of its alt
1266             #foreach (qw(app APP _app)) {
1267 0           foreach (qw(app)) {
1268 0 0         unless ($object->can($_)) {
1269 0     0     $meta->add_attribute($_ => (is => 'rw', default => sub{$self}));
  0            
1270 0           $object->$_($self);
1271 0           last;
1272             }
1273             }
1274             }
1275             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1276             =head2 dump()
1277            
1278             $app->dump({...});
1279              
1280             Print object to the STDOUT. Same as C<say Dumper (@_);>.
1281              
1282             =cut
1283              
1284             sub dump {
1285 0     0 1   my $self = shift;
1286 0           say Dumper (@_);
1287 0           return;
1288             }
1289             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1290             =head2 is_loaded()
1291            
1292             if ($app->is_loaded("Nile::SomeModule")) {
1293             #...
1294             }
1295            
1296             if ($app->is_loaded("Nile/SomeModule.pm")) {
1297             #...
1298             }
1299              
1300             Returns true if module is loaded, false otherwise.
1301              
1302             =cut
1303              
1304             sub is_loaded {
1305 0     0 0   my ($self, $module) = @_;
1306 0           (my $file = $module) =~ s/::/\//g;
1307 0 0         $file .= '.pm' unless ($file =~ /\.pm$/);
1308             #note: do() does unconditional loading -- no lookup in the %INC hash is made.
1309 0           exists $INC{$file};
1310             #return eval { $module->can( 'can' ) };
1311             #return UNIVERSAL::can($module,'can');
1312             }
1313             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1314             has 'loaded_modules' => (
1315             is => 'rw',
1316             isa => 'HashRef',
1317             default => sub { +{} }
1318             );
1319             =head2 load_once()
1320            
1321             $app->load_once("Module::SomeModule");
1322              
1323             Load modules if not already loaded.
1324              
1325             =cut
1326              
1327             sub load_once {
1328 0     0 0   my ($self, $module, @arg) = @_;
1329 0 0         if (!exists $self->loaded_modules->{$module}) {
1330 0           load $module;
1331 0           $self->loaded_modules->{$module} = 1;
1332             }
1333             }
1334             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1335             =head2 load_class()
1336            
1337             $app->load_class("Module::SomeModule");
1338              
1339             Load modules if not already loaded.
1340              
1341             =cut
1342              
1343             sub load_class {
1344 0     0 0   my ($self, $module, @arg) = @_;
1345              
1346 0 0         if (!$self->is_loaded($module)) {
1347 0           load $module;
1348             }
1349             }
1350             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1351             =head2 cli_mode()
1352            
1353             if ($app->cli_mode) {
1354             say "Running from the command line";
1355             }
1356             else {
1357             say "Running from web server";
1358             }
1359              
1360             Returns true if running from the command line interface, false if called from web server.
1361              
1362             =cut
1363              
1364             sub cli_mode {
1365 0     0 0   my ($self) = @_;
1366            
1367 0 0 0       if (exists $ENV{REQUEST_METHOD} || defined $ENV{GATEWAY_INTERFACE} || exists $ENV{HTTP_HOST}){
      0        
1368 0           return 0;
1369             }
1370            
1371             # PSGI
1372 0 0 0       if (exists $self->env->{REQUEST_METHOD} || defined $self->env->{GATEWAY_INTERFACE} || exists $self->env->{HTTP_HOST}){
      0        
1373 0           return 0;
1374             }
1375            
1376             # CLI
1377 0           return 1;
1378              
1379             #if (-t STDIN) { }
1380             #use IO::Interactive qw(is_interactive interactive busy);if ( is_interactive() ) {print "Running interactively\n";}
1381             }
1382             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1383             =head2 error()
1384            
1385             $app->error("error message");
1386              
1387             Fatal errors with custom error message. This is the same as C<croak> in L<CGI::Carp|CGI::Carp/croak>.
1388              
1389             =cut
1390              
1391             sub error {
1392 0     0 0   my $self = shift;
1393 0           goto &CGI::Carp::croak;
1394             }
1395              
1396             =head2 errors()
1397            
1398             $app->errors("error message");
1399              
1400             Fatal errors with custom error message and full starcktrace. This is the same as C<confess> in L<CGI::Carp|CGI::Carp/confess>.
1401              
1402             =cut
1403              
1404             sub errors {
1405 0     0 1   my $self = shift;
1406 0           goto &CGI::Carp::confess;
1407             }
1408              
1409             =head2 warn()
1410            
1411             $app->warn("warning message");
1412              
1413             Display warning message. This is the same as C<carp> in L<CGI::Carp|CGI::Carp/carp>.
1414              
1415             To view warnings in the browser, switch to the view source mode since warnings appear as
1416             a comment at the top of the page.
1417              
1418             =cut
1419              
1420             sub warn {
1421 0     0 1   my $self = shift;
1422             # warnings appear commented at the top of the page, use view source
1423 0 0         warningsToBrowser(1) unless ($self->cli_mode);
1424 0           goto &CGI::Carp::carp;
1425             }
1426              
1427             =head2 warns()
1428            
1429             $app->warns("warning message");
1430              
1431             Display warning message and full starcktrace. This is the same as C<cluck> in L<CGI::Carp|CGI::Carp/cluck>.
1432              
1433             To view warnings in the browser, switch to the view source mode since warnings appear as
1434             a comment at the top of the page.
1435              
1436             =cut
1437              
1438             sub warns {
1439 0     0 1   my $self = shift;
1440             # warnings appear commented at the top of the page, use view source
1441 0 0         warningsToBrowser(1) unless ($self->cli_mode);
1442 0           goto &CGI::Carp::cluck;
1443             }
1444             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1445             =head2 abort()
1446            
1447             $app->abort("error message");
1448              
1449             $app->abort("error title", "error message");
1450              
1451             Stop and quit the application and display message to the user. See L<Nile::Abort> module.
1452              
1453             =cut
1454              
1455             sub abort {
1456 0     0 0   my ($self) = shift;
1457 0           load Nile::Abort;
1458 0           Nile::Abort->abort(@_);
1459             }
1460             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1461             #__PACKAGE__->meta->make_immutable;#(inline_constructor => 0)
1462             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1463              
1464             =head1 Sub Modules
1465              
1466             App L<Nile::App>.
1467              
1468             Views L<Nile::View>.
1469              
1470             Shared Vars L<Nile::Var>.
1471              
1472             Langauge L<Nile::Lang>.
1473              
1474             Request L<Nile::HTTP::Request>.
1475              
1476             PSGI Request L<Nile::HTTP::Request::PSGI>.
1477              
1478             PSGI Request Base L<Nile::HTTP::PSGI>.
1479              
1480             Response L<Nile::HTTP::Response>.
1481              
1482             PSGI Handler L<Nile::Handler::PSGI>.
1483              
1484             FCGI Handler L<Nile::Handler::FCGI>.
1485              
1486             CGI Handler L<Nile::Handler::CGI>.
1487              
1488             Dispatcher L<Nile::Dispatcher>.
1489              
1490             Router L<Nile::Router>.
1491              
1492             File Utils L<Nile::File>.
1493              
1494             DBI L<Nile::DBI>.
1495              
1496             DBI Table L<Nile::DBI::Table>.
1497              
1498             XML L<Nile::XML>.
1499              
1500             Settings L<Nile::Setting>.
1501              
1502             Serializer L<Nile::Serializer>.
1503              
1504             Deserializer L<Nile::Deserializer>.
1505              
1506             Serialization Base L<Nile::Serialization>.
1507              
1508             Filter L<Nile::Filter>.
1509              
1510             MIME L<Nile::MIME>.
1511              
1512             Timer L<Nile::Timer>.
1513              
1514             Plugin L<Nile::Plugin>.
1515              
1516             Session L<Nile::Plugin::Session>.
1517              
1518             Cache L<Nile::Plugin::Cache>.
1519              
1520             Cache Redis L<Nile::Plugin::Cache::Redis>.
1521              
1522             Email L<Nile::Plugin::Email>.
1523              
1524             Paginatation L<Nile::Plugin::Paginate>.
1525              
1526             MongoDB L<Nile::Plugin::MongoDB>.
1527              
1528             Redis L<Nile::Plugin::Redis>.
1529              
1530             Memcached L<Nile::Plugin::Memcached>.
1531              
1532             Module L<Nile::Module>.
1533              
1534             Hook L<Nile::Hook>.
1535              
1536             Base L<Nile::Base>.
1537              
1538             Abort L<Nile::Abort>.
1539              
1540             =head1 Bugs
1541              
1542             This project is available on github at L<https://github.com/mewsoft/Nile>.
1543              
1544             =head1 HOMEPAGE
1545              
1546             Please visit the project's homepage at L<https://metacpan.org/release/Nile>.
1547              
1548             =head1 SOURCE
1549              
1550             Source repository is at L<https://github.com/mewsoft/Nile>.
1551              
1552             =head1 AUTHOR
1553              
1554             Ahmed Amin Elsheshtawy, احمد امين الششتاوى <mewsoft@cpan.org>
1555             Website: http://www.mewsoft.com
1556              
1557             =head1 COPYRIGHT AND LICENSE
1558              
1559             Copyright (C) 2014-2015 by Dr. Ahmed Amin Elsheshtawy mewsoft@cpan.org, support@mewsoft.com,
1560             L<https://github.com/mewsoft/Nile>, L<http://www.mewsoft.com>
1561              
1562             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
1563              
1564             =cut
1565              
1566              
1567             1;