File Coverage

blib/lib/Nile/App.pm
Criterion Covered Total %
statement 102 279 36.5
branch 0 64 0.0
condition 0 26 0.0
subroutine 34 71 47.8
pod 5 36 13.8
total 141 476 29.6


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::App;
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::App - App base class for the Nile framework.
20              
21             =head1 SYNOPSIS
22              
23             =head1 DESCRIPTION
24              
25             Nile::App - App base class for the Nile framework.
26              
27             =cut
28              
29 1     1   489 use Module::Load;
  1         839  
  1         5  
30 1     1   35 use Data::Dumper;
  1         2  
  1         41  
31             $Data::Dumper::Deparse = 1; #stringify coderefs
32 1     1   437 use HTTP::AcceptLanguage;
  1         1808  
  1         25  
33 1     1   5 use utf8;
  1         1  
  1         9  
34 1     1   18 use File::Spec;
  1         1  
  1         42  
35 1     1   5 use File::Basename;
  1         1  
  1         55  
36 1     1   3 use Cwd;
  1         1  
  1         37  
37 1     1   3 use URI;
  1         2  
  1         13  
38 1     1   4 use Encode ();
  1         0  
  1         18  
39 1     1   5 use URI::Escape;
  1         2  
  1         73  
40 1     1   5 use Crypt::RC4;
  1         1  
  1         34  
41             #use Crypt::CBC;
42 1     1   4 use Capture::Tiny ();
  1         20  
  1         15  
43 1     1   3 use Time::Local;
  1         2  
  1         40  
44 1     1   6 use File::Slurp;
  1         1  
  1         66  
45 1     1   5 use Time::HiRes qw(gettimeofday tv_interval);
  1         1  
  1         4  
46 1     1   104 use MIME::Base64 3.11 qw(encode_base64 decode_base64 decode_base64url encode_base64url);
  1         18  
  1         41  
47 1     1   3400 use DateTime ();
  1         117263  
  1         50  
48              
49 1     1   520 use Nile::Plugin;
  1         39  
  1         4  
50 1     1   451 use Nile::Plugin::Object;
  1         3  
  1         37  
51 1     1   462 use Nile::Module;
  1         3  
  1         4  
52 1     1   560 use Nile::View;
  1         2  
  1         48  
53 1     1   712 use Nile::XML;
  1         2  
  1         43  
54 1     1   418 use Nile::Var;
  1         2  
  1         36  
55 1     1   512 use Nile::File;
  1         3  
  1         53  
56 1     1   408 use Nile::Lang;
  1         5  
  1         60  
57 1     1   572 use Nile::Config;
  1         4  
  1         45  
58 1     1   500 use Nile::Router;
  1         4  
  1         51  
59 1     1   593 use Nile::Dispatcher;
  1         3  
  1         36  
60 1     1   448 use Nile::DBI;
  1         4  
  1         27  
61 1     1   572 use Nile::Timer;
  1         3  
  1         13  
62 1     1   551 use Nile::HTTP::Request;
  1         3  
  1         7  
63 1     1   554 use Nile::HTTP::Response;
  1         3  
  1         17  
64              
65 1     1   38 use Nile::Base;
  1         2  
  1         7  
66             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
67             # Application 'Nile' object instance
68             has 'app' => (
69             is => 'rw',
70             default => undef
71             );
72             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
73             sub BUILD {
74 0     0 0   my ($self, $arg) = @_;
75 0           $self->app($arg->{app});
76             # start the app page load timer
77 0           $self->run_time->start();
78             }
79             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
80             =head2 object()
81            
82             $obj = $app->object("Nile::MyClass", @args);
83             $obj = $app->object("Nile::Plugin::MyClass", @args);
84             $obj = $app->object("Nile::Module::MyClass", @args);
85              
86             #...
87              
88             $app = $obj->app;
89             $request = $app->request;
90             $response = $app->response;
91            
92             Creates and returns an object. This automatically adds the method L<app> to the object
93             and sets it to the current context so your object or class can access the current instance.
94              
95             =cut
96              
97             sub object {
98              
99 0     0 0   my ($self, $class, @args) = @_;
100 0           my ($object, $me);
101            
102             #if (@args == 1 && ref($args[0]) eq "HASH") {
103             # # Moose single arguments must be hash ref
104             # $object = $class->new(@args);
105             #}
106              
107 0 0 0       if (@args && @args % 2) {
108             # Moose needs args as hash, so convert odd size arrays to even for hashing
109 0           $object = $class->new(@args, undef);
110             }
111             else {
112 0           $object = $class->new(@args);
113             }
114              
115 0           my $meta = $object->meta;
116              
117             #$meta->add_method( 'hello' => sub { return "Hello inside hello method. @_" } );
118             #$meta->add_class_attribute( $_, %options ) for @{$attrs}; #MooseX::ClassAttribute
119             #$meta->add_class_attribute( 'cash', ());
120              
121             # add method "me" or one of its alt
122 0           $self->add_object_context($object, $meta);
123            
124             # if class has defined "main" method, then call it
125 0 0         if ($object->can("main")) {
126 0           my %ret = $object->main(@args);
127 0 0         if ($ret{rebless}) {
128 0           $object = $ret{rebless};
129             }
130             }
131            
132             #no strict 'refs';
133             #*{"$object"."::me"} = \&me;
134             #${"${package}::$var_name"} = 1;
135            
136 0           return $object;
137             }
138             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
139             sub add_object_context {
140 0     0 0   my ($self, $object, $meta) = @_;
141 0   0       $meta ||= $object->meta;
142             # add method "app" or one of its alt
143             #foreach (qw(app APP _app)) {
144 0           foreach (qw(app)) {
145 0 0         unless ($object->can($_)) {
146 0     0     $meta->add_attribute($_ => (is => 'rw', default => sub{$self}));
  0            
147 0           $object->$_($self);
148 0           last;
149             }
150             }
151             }
152             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
153             sub result {
154 0     0 0   my ($self, @data) = @_;
155 1     1   3104 use Nile::Result;
  1         1  
  1         9  
156 0           Nile::Result->new(@data);
157             }
158             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
159             sub is_result {
160 0     0 0   my ($self, $result) = @_;
161 0           ref($result) eq "Nile::Result";
162             }
163             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
164              
165             =head2 start()
166            
167             $app->start;
168              
169             Set the application startup variables.
170              
171             =cut
172              
173             sub start {
174              
175 0     0 1   my ($self) = @_;
176             #------------------------------------------------------
177 0           my $app = $self->app;
178 0           my $file = $self->file;
179            
180             # shared vars
181 0           my %arg = $app->var->vars();
182              
183 0           $self->var->set(%arg);
184              
185             #$self->dump({$app->var->vars()});
186             #$self->dump({$self->var->vars()});
187              
188 0   0       $arg{lang} ||= "";
189 0   0       $arg{theme} ||= "default";
190            
191 0           my $path = $self->var->get("path");
192              
193             # detect user language
194 0           $arg{lang} = $self->detect_user_language($arg{lang});
195              
196 0           $self->var->set(
197             'lang' => $arg{lang},
198             'theme' => $arg{theme},
199             'lang_dir' => $file->catdir($path, "lang", $arg{lang}),
200             'theme_dir' => $file->catdir($path, "theme", $arg{theme}),
201             );
202            
203             #$self->dump({$self->var->vars()});
204              
205             # load language files
206 0           foreach ($self->config->get("app/lang_file")) {
207 0           $self->lang->load($_);
208             }
209             #------------------------------------------------------
210             #$self->hook->on_start;
211              
212 0           my $req = $self->request;
213              
214             # global variables, safe to render in views
215 0           $self->var->set(
216             url => $req->url,
217             base_url => $req->base_url,
218             abs_url => $req->abs_url,
219             url_path => $req->url_path,
220             );
221              
222             #$self->uri_mode(1);
223             # app folders url's
224 0           foreach (qw(api cache file temp web)) {
225 0           $self->var->set($_."_url" => $self->uri_for("$_/"));
226             }
227            
228             # themes and current theme url's
229             $self->var->set(
230 0           themes_url => $self->uri_for("theme/"),
231             theme_url => $self->uri_for("theme/$arg{theme}/"),
232             );
233              
234 0           foreach (qw(css icon image js view widget)) {
235 0           $self->var->set($_."_url" => $self->uri_for("theme/$arg{theme}/$_/"));
236             }
237             #------------------------------------------------------
238             # load plugins set to autoload in the config files
239 0           while (my ($name, $plugin) = each %{$self->config->get("plugin")} ) {
  0            
240 0 0         next if (!$plugin->{autoload});
241 0           $name = ucfirst($name);
242 0           my $class = "Nile::Plugin::$name";
243 0 0         if (!$self->is_loaded($class)) {
244 0           load $class;
245 0           $self->plugin->$name;
246             }
247             }
248             #------------------------------------------------------
249             # connect to database
250 0 0         if ($self->config->get("db_connect")) {
251 0           $self->connect;
252             }
253             #------------------------------------------------------
254             #$self->hook->off_start;
255             }
256             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
257             =head2 mode()
258            
259             my $mode = $app->mode;
260              
261             Returns the current application mode PSGI, FCGI or CGI.
262              
263             =cut
264              
265             has 'mode' => (
266             is => 'rw',
267             isa => 'Str',
268             lazy => 1,
269             default => sub {shift->app->mode(@_)},
270             );
271             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
272             =head2 config()
273            
274             See L<Nile::Config>.
275              
276             =cut
277              
278             has 'config' => (
279             is => 'rw',
280             lazy => 1,
281             default => sub {
282             shift->app->config(@_);
283             }
284             );
285             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
286             =head2 router()
287            
288             See L<Nile::Router>.
289              
290             =cut
291              
292             has 'router' => (
293             is => 'rw',
294             isa => 'Nile::Router',
295             lazy => 1,
296             default => sub {
297             shift->app->router(@_);
298             }
299             );
300             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
301             =head2 lang()
302            
303             See L<Nile::Lang>.
304              
305             =cut
306              
307             has 'lang' => (
308             is => 'rw',
309             isa => 'Nile::Lang',
310             lazy => 1,
311             default => sub {
312             #shift->app->lang(@_);
313             shift->object("Nile::Lang", @_);
314             }
315             );
316             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
317             =head2 uri_mode()
318            
319             # uri mode: 0=full, 1=absolute, 2=relative
320             $app->uri_mode(1);
321              
322             Set the uri mode. The values allowed are: 0= full, 1=absolute, 2=relative
323              
324             =cut
325              
326             has 'uri_mode' => (
327             is => 'rw',
328             default => 0, # 0= full, 1=absolute, 2=relative
329             );
330              
331             =head2 uri_for()
332            
333             $url = $app->uri_for("/users", [$mode]);
334              
335             Returns the uri for specific action or route. The mode parameter is optional. The mode values allowed are: 0= full, 1=absolute, 2=relative.
336              
337             =cut
338              
339             sub uri_for {
340 0     0 1   my ($self, $uri, $mode) = @_;
341            
342 0 0         if (!defined $mode) {
343 0           $mode = $self->uri_mode;
344             }
345              
346 0 0         if ($self->uri_mode == 1) {
347 0           return $self->var->get("abs_url") . $uri;
348             }
349             else {
350 0           return $self->var->get("base_url") . $uri;
351             }
352             }
353             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
354             sub forward {
355 0     0 0   my ($self, $uri) = @_;
356            
357             #$me->forward($uri);
358              
359             }
360             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
361             =head2 debug()
362            
363             # 1=enabled, 0=disabled
364             $app->debug(1);
365              
366             Enable or disable debugging flag.
367              
368             =cut
369              
370             has 'debug' => (
371             is => 'rw',
372             isa => 'Bool',
373             default => 0,
374             );
375             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
376             =head2 bm()
377            
378             $app->bm->lap("start task");
379             ....
380             $app->bm->lap("end task");
381            
382             say $app->bm->stop->summary;
383              
384             # NAME TIME CUMULATIVE PERCENTAGE
385             # start task 0.123 0.123 34.462%
386             # end task 0.234 0.357 65.530%
387             # _stop_ 0.000 0.357 0.008%
388            
389             say "Total time: " . $app->bm->total_time;
390              
391             Benchmark specific parts of your code. This is a L<Benchmark::Stopwatch> object.
392              
393             =cut
394              
395             has 'bm' => (
396             is => 'rw',
397             isa => 'Benchmark::Stopwatch',
398             lazy => 1,
399             default => sub{
400             #autoload, load CGI, ':all';
401             load Benchmark::Stopwatch;
402             Benchmark::Stopwatch->new->start;
403             }
404             );
405             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
406             =head2 file()
407            
408             See L<Nile::File>.
409              
410             =cut
411              
412             has 'file' => (
413             is => 'rw',
414             isa => 'Nile::File',
415             lazy => 1,
416             default => sub {
417             my $self = shift;
418             $self->object("Nile::File", @_);
419             }
420             );
421             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
422             =head2 xml()
423            
424             See L<Nile::XML>.
425              
426             =cut
427              
428             has 'xml' => (
429             is => 'rw',
430             lazy => 1,
431             default => sub {
432             my $self = shift;
433             $self->object("Nile::XML", @_);
434             }
435             );
436             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
437             =head2 setting()
438            
439             See L<Nile::Setting>.
440              
441             =cut
442              
443             has 'setting' => (
444             is => 'rw',
445             lazy => 1,
446             default => sub {
447             my $self = shift;
448             load Nile::Setting;
449             $self->object("Nile::Setting", @_);
450             }
451             );
452             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
453             =head2 mime()
454            
455             See L<Nile::MIME>.
456              
457             =cut
458              
459             has 'mime' => (
460             is => 'rw',
461             isa => 'Nile::MIME',
462             lazy => 1,
463             default => sub {
464             load Nile::MIME;
465             shift->object("Nile::MIME", only_complete => 1);
466             }
467             );
468             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
469             =head2 dispatcher()
470            
471             See L<Nile::Dispatcher>.
472              
473             =cut
474              
475             has 'dispatcher' => (
476             is => 'rw',
477             isa => 'Nile::Dispatcher',
478             lazy => 1,
479             default => sub {
480             shift->object("Nile::Dispatcher", @_);
481             }
482             );
483             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
484             =head2 logger()
485            
486             Returns L<Log::Tiny> object.
487              
488             =cut
489              
490             has 'logger' => (
491             is => 'rw',
492             lazy => 1,
493             default => sub {
494             my $self = shift;
495             load Log::Tiny;
496             Log::Tiny->new($self->file->catfile($self->var->get("log_dir"), $self->var->get("log_file") || 'log.pm'));
497             }
498             );
499             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
500             =head2 log()
501              
502             $app->log->info("application run start");
503             $app->log->DEBUG("application run start");
504             $app->log->ERROR("application run start");
505             $app->log->INFO("application run start");
506             $app->log->ANYTHING("application run start");
507              
508             Log object L<Log::Tiny> supports unlimited log categories.
509              
510             =cut
511              
512             sub log {
513 0     0 0   my $self = shift;
514 0 0         $self->start_logger if (!$self->logger);
515 0           $self->logger(@_);
516             }
517              
518             =head2 start_logger()
519            
520             $app->start_logger();
521              
522             Start the log object and open the log file for writing logs.
523              
524             =cut
525              
526             sub start_logger {
527 0     0 1   my $self = shift;
528 0           $self->stop_logger;
529 0   0       $self->logger(Log::Tiny->new($self->file->catfile($self->var->get("log_dir"), $self->var->get("log_file") || 'log.pm')));
530             }
531              
532             =head2 stop_logger()
533            
534             $app->stop_logger();
535              
536             Stops the log object and close the log file.
537              
538             =cut
539              
540             sub stop_logger {
541 0     0 1   my $self = shift;
542             # close log file
543 0           $self->logger(undef);
544             }
545             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
546             =head2 timer()
547            
548             # start the timer
549             $app->timer->start;
550            
551             # do some operations...
552            
553             # get time elapsed since start called
554             say $app->timer->lap;
555              
556             # do some other operations...
557              
558             # get time elapsed since last lap called
559             say $app->timer->lap;
560              
561             # get another timer object, timer automatically starts
562             my $timer = $app->timer->new;
563             say $timer->lap;
564             #...
565             say $timer->lap;
566             #...
567             say $timer->total;
568              
569             Returns L<Nile::Timer> object. See L<Nile::Timer> for more details.
570              
571             =cut
572              
573             has 'timer' => (
574             is => 'rw',
575             #lazy => 1,
576             default => sub{
577             Nile::Timer->new;
578             }
579             );
580             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
581             # page load timer, run time
582              
583             =head2 run_time()
584            
585             # get time elapsed since app started
586             say $app->run_time->lap;
587              
588             # do some other operations...
589              
590             # get time elapsed since last lap called
591             say $app->run_time->lap;
592              
593             Returns L<Nile::Timer> object. Timer automatically starts with the application.
594              
595             =cut
596              
597             has 'run_time' => (
598             is => 'rw',
599             #lazy => 1,
600             default => sub{
601             Nile::Timer->new;
602             }
603             );
604             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
605             =head2 var()
606            
607             See L<Nile::Var>.
608              
609             =cut
610              
611             has 'var' => (
612             is => 'rw',
613             lazy => 1,
614             default => sub {
615             shift->object("Nile::Var", @_);
616             }
617             );
618             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
619             =head2 env()
620            
621             $request_uri = $app->env->{REQUEST_URI};
622              
623             Application env object for CGI and Plack/PSGI.
624              
625             =cut
626              
627             has 'env' => (
628             is => 'rw',
629             isa => 'HashRef',
630             default => sub { \%ENV }
631             );
632             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
633             =head2 browser()
634            
635             $browser = $app->browser;
636             say $browser->version;
637             say $browser->browser_string;
638             say $browser->os_string;
639             if ($browser->mobile) { say "Mobile device"; }
640              
641             Determine Web browser, version, and platform. Returns L<HTTP::BrowserDetect> object.
642              
643             =cut
644              
645             has 'browsers' => (
646             is => 'rw',
647             lazy => 1,
648             default => sub {
649             load HTTP::BrowserDetect;
650             HTTP::BrowserDetect->new(shift->env->{HTTP_USER_AGENT})
651             }
652             );
653             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
654             =head2 request()
655            
656             See L<Nile::Request>.
657              
658             =cut
659              
660             has 'request' => (
661             is => 'rw',
662             lazy => 1,
663             default => sub {},
664             );
665             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
666             =head2 response()
667            
668             See L<Nile::Response>.
669              
670             =cut
671              
672             has 'response' => (
673             is => 'rw',
674             isa => 'Nile::HTTP::Response',
675             lazy => 1,
676             default => sub {
677             shift->object("Nile::HTTP::Response", @_);
678             }
679             );
680             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
681             =head2 plugin()
682            
683             See L<Nile::Plugin>.
684              
685             =cut
686              
687             has 'plugin_object' => (
688             is => 'rw',
689             lazy => 1,
690             default => sub {
691             shift->object("Nile::Plugin::Object", @_);
692             }
693             );
694              
695             has 'plugin_loaded' => (
696             is => 'rw',
697             lazy => 1,
698             isa => 'HashRef',
699             default => sub { +{} }
700             );
701              
702             sub plugin {
703              
704 0     0 0   my ($self, $plugin) = @_;
705              
706 0 0         if (!$plugin) {
707 0           return $self->plugin_object;
708             }
709              
710 0 0         if ($plugin !~ /::/) {
711 0           return $self->plugin_object->$plugin;
712             }
713              
714 0           my $name = "Nile::Plugin::" . ucfirst($plugin);
715              
716            
717 0 0         return $self->plugin_loaded->{$plugin} if ($self->plugin_loaded->{$plugin});
718              
719 0           eval "use $name";
720            
721 0 0         if ($@) {
722 0           $self->abort("Plugin Error: $name. $@");
723             }
724              
725 0           $self->plugin_loaded->{$plugin}= $self->object($name, @_);
726              
727 0           return $self->plugin_loaded->{$plugin};
728             }
729              
730             sub plugins {
731 0     0 0   my ($self, $plugin) = @_;
732 0           say "plugin: $plugin";
733 0 0         if ($plugin !~ /::/) {
734 0           return $self->plugin->$plugin;
735             }
736              
737 0           my $name = "Nile::Plugin::" . ucfirst($plugin);
738              
739            
740 0 0         return $self->plugin_loaded->{$plugin} if ($self->plugin_loaded->{$plugin});
741              
742 0           eval "use $name";
743            
744 0 0         if ($@) {
745 0           $self->abort("Plugins Error: $name. $@");
746             }
747              
748 0           $self->plugin_loaded->{$plugin}= $self->object($name, @_);
749              
750 0           return $self->plugin_loaded->{$plugin};
751              
752             #$self->object("Nile::Plugin::Object", $plugin);
753             }
754             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
755             =head2 helper()
756            
757             # add helper method to the framework
758            
759             $app->helper($method => $coderef);
760            
761             # add method "echo"
762             $app->helper("echo" => sub{shift; say @_;});
763              
764             # access the helper method normal from plugins and modules
765             $app->echo("Helper echo example.");
766              
767             =cut
768              
769             sub helper {
770 0     0 0   my ($self, %arg) = @_;
771 0           while (my($name, $code) = each %arg) {
772 0 0         if (ref($code) ne "CODE") {
773 0           $self->abort("Helper setup error: helper '$name' code should be a code ref. $code");
774             }
775              
776 0 0         if (!$self->can($name)) {
777 0           $self->meta->add_method($name => $code);
778             }
779             else {
780 0           $self->abort("Helper setup error: helper '$name' method already exists. $code");
781             }
782              
783             }
784             }
785             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
786             =head2 attr()
787            
788             # add attr to the framework
789            
790             $app->attr($name => $default);
791            
792             # add attribute "PI"
793             $app->attr("PI" => 4 * atan2(1, 1));
794            
795             # or
796             $app->attr("PI" => sub{4 * atan2(1, 1)});
797              
798             # get the attribute value
799             say $app->PI;
800              
801             # set the the attribute value to new value
802             $app->PI(3.14159265358979);
803              
804             =cut
805              
806             sub attr {
807 0     0 0   my ($self, %arg) = @_;
808 0           while (my($name, $code) = each %arg) {
809 0 0         if (!$self->can($name)) {
810 0           $self->meta->add_attribute($name => (is => 'rw', lazy=>1, default => $code));
811             }
812             else {
813 0           $self->abort("Attr setup error: attr '$name' already exists. $code");
814             }
815             }
816             }
817             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
818             =head2 ua()
819            
820             my $response = $app->ua->get('http://example.com/');
821             say $response->{content} if length $response->{content};
822            
823             $response = $app->ua->get($url, \%options);
824             $response = $app->ua->head($url);
825            
826             $response = $app->ua->post_form($url, $form_data);
827             $response = $app->ua->post_form($url, $form_data, \%options);
828              
829             Simple HTTP client. This is a L<HTTP::Tiny> object.
830              
831             =cut
832              
833             has 'ua' => (
834             is => 'rw',
835             isa => 'HTTP::Tiny',
836             lazy => 1,
837             #trigger => sub {shift->clearer},
838             default => sub {
839             load HTTP::Tiny;
840             HTTP::Tiny->new;
841             }
842             );
843             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
844             =head2 uri()
845            
846             my $uri = $app->uri('http://mewsoft.com/');
847              
848             Returns L<URI> object.
849              
850             =cut
851              
852             has 'uri' => (
853             is => 'rw',
854             isa => 'URI',
855             lazy => 1,
856             default => sub {
857             load URI;
858             URI->new;
859             }
860             );
861             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
862             =head2 charset()
863            
864             $app->charset('utf8');
865             $charset = $app->charset;
866              
867             Set or return the charset for encoding and decoding. Default is C<utf8>.
868              
869             =cut
870              
871             has 'charset' => (
872             is => 'rw',
873             lazy => 1,
874             default => sub {shift->var->get("charset")}
875             );
876             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
877             =head2 freeze()
878            
879             See L<Nile::Serializer>.
880              
881             =cut
882              
883             has 'freeze' => (
884             is => 'rw',
885             isa => 'Nile::Serializer',
886             lazy => 1,
887             default => sub {
888             load Nile::Serializer;
889             Nile::Serializer->new;
890             }
891             );
892 0     0 0   sub serialize {shift->freeze(@_);}
893             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
894             =head2 thaw()
895            
896             See L<Nile::Deserializer>.
897              
898             =cut
899              
900             has 'thaw' => (
901             is => 'rw',
902             isa => 'Nile::Deserializer',
903             lazy => 1,
904             default => sub {
905             load Nile::Deserializer;
906             Nile::Deserializer->new;
907             }
908             );
909 0     0 0   sub deserialize {shift->thaw(@_);}
910             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
911             =head2 module()
912            
913             # load module Nile::Module::Home::Contact and create a new object
914             $contact = $me->module("Home::Contact");
915              
916             # to get another new instance
917             $contact1 = $me->module("Home::MyModule")->new();
918             # or
919             $contact2 = $contact->new();
920              
921             # if you are calling from inside the Home module, you can just use
922             $contact = $me->module("Contact");
923              
924             # of course you can load sub classes
925             $send = $me->module("Home::Contact::Send");
926              
927             # if you are calling from inside the Home module, you can just use
928             $send = $me->module("Contact::Send");
929              
930             # all the above is the same as
931             use Nile::Module::Home::Contact;
932             $contact = Nile::Module::Home::Contact->new();
933             $contact->main() if ($contact->can("main"));
934              
935             Load modules classes.
936              
937             =cut
938              
939             sub module {
940            
941 0     0 0   my ($self, $module) = @_;
942            
943 0           my ($package, $script) = caller;
944 0           my ($class, $method) = $package =~ /^(.*)::(\w+)$/;
945            
946 0           $module = ucfirst($module);
947 0           my $name;
948              
949 0 0         if ($module =~ /::/) {
950             # module("Home::Contact") called from any module
951 0           $name = "Nile::Module::" . $module;
952             }
953             else {
954             # module("Contact") called from Home module
955 0           $name = $class . "::" . $module;
956             }
957              
958 0 0         return $self->{module}->{$name} if ($self->{module}->{$name});
959              
960 0           eval "use $name";
961            
962 0 0         if ($@) {
963 0           $self->abort("Module Load Error: $name . $@");
964             }
965              
966 0           $self->{module}->{$name} = $self->object($name, @_);
967              
968 0           return $self->{module}->{$name};
969             }
970             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
971             =head2 hook()
972            
973             See L<Nile::Hook>.
974              
975             =cut
976              
977             has 'hook' => (
978             is => 'rw',
979             lazy => 1,
980             default => sub {
981             load Nile::Hook;
982             shift->object("Nile::Hook", @_);
983             }
984             );
985             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
986             =head2 filter()
987            
988             See L<Nile::Filter>.
989              
990             =cut
991              
992             has 'filter' => (
993             is => 'rw',
994             isa => 'Nile::Filter',
995             lazy => 1,
996             default => sub {
997             load Nile::Filter;
998             shift->object("Nile::Filter", @_);
999             }
1000             );
1001             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1002             =head2 session()
1003            
1004             See session plugin L<Nile::Plugin::Session>.
1005              
1006             =cut
1007              
1008             has 'session' => (
1009             is => 'rw',
1010             lazy => 1,
1011             isa => 'HashRef',
1012             default => sub { +{} }
1013             );
1014             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1015             =head2 date()
1016              
1017             # get date object with time set to from epoch time
1018             my $dt = $app->date(time());
1019            
1020             # the same
1021             my $dt = $app->date(epoch => time());
1022            
1023             # object with time component
1024             my $dt = $app->date(
1025             year => 2014,
1026             month => 9,
1027             day => 3,
1028             hour => 22,
1029             minute => 12,
1030             second => 24,
1031             nanosecond => 500000000,
1032             time_zone => 'Africa/Cairo',
1033             );
1034            
1035             # get date object with time set to now
1036             my $dt = $app->date;
1037              
1038             # then all methods of DateTime module is available
1039             $dt->set_time_zone('America/Chicago');
1040             $dt->strftime("%a, %d %b %Y %H:%M:%S");
1041             $ymd = $dt->ymd('/');
1042              
1043             Date and time object wrapper around L<DateTime> module.
1044              
1045             =cut
1046              
1047             sub date {
1048 0     0 0   my ($self) = shift;
1049 0 0         if (scalar @_ == 1) {
    0          
1050 0           return DateTime->from_epoch(epoch => shift);
1051             }
1052             elsif (scalar @_ > 1) {
1053 0           my %arg = @_;
1054 0 0         if (exists $arg{epoch}) {
1055 0           return DateTime->from_epoch(epoch => $arg{epoch});
1056             }
1057 0           return DateTime->new(%arg);
1058            
1059             }
1060             else {
1061 0           return DateTime->now;
1062             }
1063             }
1064             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1065             has 'dbh' => (
1066             is => 'rw',
1067             );
1068              
1069             has 'db' => (
1070             is => 'rw',
1071             );
1072              
1073             sub connect {
1074 0     0 0   my $self = shift;
1075 0           $self->db($self->object("Nile::DBI"));
1076 0           $self->dbh($self->db->connect(@_));
1077 0           $self->db;
1078             }
1079             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1080             sub new_request {
1081            
1082 0     0 0   my ($self, $env) = @_;
1083              
1084 0 0 0       if (defined($env) && ref ($env) eq "HASH") {
1085 0           $self->mode("psgi");
1086             #load Nile::HTTP::PSGI;
1087 0           $self->request($self->object("Nile::HTTP::Request::PSGI", $env));
1088             }
1089             else {
1090 0           $self->request($self->object("Nile::HTTP::Request"));
1091             }
1092            
1093 0           $self->request();
1094             }
1095             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1096             =head2 detect_user_language()
1097            
1098             $user_lang = $app->detect_user_language;
1099              
1100             Detects and retuns the user langauge.
1101              
1102             =cut
1103              
1104             sub detect_user_language {
1105 0     0 0   my ($self, $default) = @_;
1106              
1107 0 0         if ($self->request->param($self->config->get("app/lang_param_key"))) {
1108 0           return $self->request->param($self->config->get("app/lang_param_key"));
1109             }
1110            
1111 0 0         if ($self->session->{$self->config->get("app/lang_session_key")}) {
1112 0           return $self->session->{$self->config->get("app/lang_session_key")};
1113             }
1114              
1115 0 0         if ($self->request->cookie($self->config->get("app/lang_cookie_key"))) {
1116 0           return $self->request->cookie($self->config->get("app/lang_cookie_key"));
1117             }
1118              
1119             # detect user browser language settings
1120 0           my @langs = $self->lang_list();
1121 0           my $lang = HTTP::AcceptLanguage->new($ENV{HTTP_ACCEPT_LANGUAGE})->match(@langs);
1122              
1123 0   0       $lang ||= $default ||= $langs[0] ||= "en-US";
      0        
      0        
1124              
1125 0           return $lang;
1126             }
1127             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1128             =head2 view()
1129            
1130             Returns L<Nile::View> object.
1131              
1132             =cut
1133              
1134             sub view {
1135 0     0 0   my ($self) = shift;
1136 0           return $self->object("Nile::View", @_);
1137             }
1138             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1139             =head2 dbi()
1140            
1141             Returns L<Nile::DBI> object.
1142              
1143             =cut
1144              
1145             sub dbi {
1146 0     0 0   my ($self) = shift;
1147 0           return $self->object("Nile::DBI", @_);
1148             }
1149             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1150             =head2 theme_list()
1151            
1152             @themes = $app->theme_list;
1153              
1154             Returns themes names installed.
1155              
1156             =cut
1157              
1158             sub theme_list {
1159 0     0 0   my ($self) = @_;
1160 0           my @folders = ($self->file->folders($self->var->get("themes_dir"), "", 1));
1161 0           return grep (/^[^_]/, @folders);
1162             }
1163             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1164             =head2 lang_list()
1165            
1166             @langs = $app->lang_list;
1167              
1168             Returns languages names installed.
1169              
1170             =cut
1171              
1172             sub lang_list {
1173 0     0 0   my ($self) = @_;
1174 0           my @folders = ($self->file->folders($self->var->get("langs_dir"), "", 1));
1175 0           return grep (/^[^_]/, @folders);
1176             }
1177             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1178             =head2 dump()
1179            
1180             $app->dump({...});
1181              
1182             Print object to the STDOUT. Same as C<say Dumper (@_);>.
1183              
1184             =cut
1185              
1186             sub dump {
1187 0     0 1   return shift->app->dump(@_);
1188             }
1189             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1190             =head2 is_loaded()
1191            
1192             if ($app->is_loaded("Nile::SomeModule")) {
1193             #...
1194             }
1195            
1196             if ($app->is_loaded("Nile/SomeModule.pm")) {
1197             #...
1198             }
1199              
1200             Returns true if module is loaded, false otherwise.
1201              
1202             =cut
1203              
1204             sub is_loaded {
1205 0     0 0   shift->app->is_loaded(@_);
1206             }
1207             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1208             sub load_once {
1209 0     0 0   shift->app->load_once(@_);
1210             }
1211             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1212             =head2 cli_mode()
1213            
1214             if ($app->cli_mode) {
1215             say "Running from the command line";
1216             }
1217             else {
1218             say "Running from web server";
1219             }
1220              
1221             Returns true if running from the command line interface, false if called from web server.
1222              
1223             =cut
1224              
1225             sub cli_mode {
1226 0     0 0   shift->app->cli_mode(@_);
1227             }
1228             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1229             =head2 utf8_safe()
1230            
1231             $str_utf8 = $app->utf8_safe($str);
1232              
1233             Encode data in C<utf8> safely.
1234              
1235             =cut
1236              
1237             sub utf8_safe {
1238 0     0 0   my ($self, $str) = @_;
1239 0 0         if (utf8::is_utf8($str)) {
1240 0           utf8::encode($str);
1241             }
1242 0           $str;
1243             }
1244             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1245             =head2 encode()
1246            
1247             $encoded = $app->encode($data);
1248              
1249             Encode data using the current L</charset>.
1250              
1251             =cut
1252              
1253             sub encode {
1254 0     0 0   my ($self, $data) = @_;
1255 0           return Encode::encode($self->charset, $data);
1256             }
1257             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1258             =head2 decode()
1259            
1260             $data = $app->decode($encoded);
1261              
1262             Decode data using the current L</charset>.
1263              
1264             =cut
1265              
1266             sub decode {
1267 0     0 0   my ($self, $data) = @_;
1268 0           return Encode::decode($self->charset, $data);
1269             }
1270             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1271             =head2 instance_isa()
1272            
1273             $app->instance_isa($object, $class);
1274              
1275             Test for an object of a particular class in a strictly correct manner.
1276              
1277             Returns the object itself or C<undef> if the value provided is not an object of that type.
1278              
1279             =cut
1280              
1281             sub instance_isa ($$) {
1282             #my ($self, $object, $class) = @_;
1283 0 0 0 0 0   (Scalar::Util::blessed($_[1]) and $_[1]->isa($_[2])) ? $_[1] : undef;
1284             }
1285             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1286             sub content_type_text {
1287 0     0 0   my ($self, $content_type) = @_;
1288 0           return $content_type =~ /(\bx(?:ht)?ml\b|text|json|javascript)/;
1289             }
1290             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1291             =head2 abort()
1292            
1293             $app->abort("error message");
1294              
1295             $app->abort("error title", "error message");
1296              
1297             Stop and quit the application and display message to the user. See L<Nile::Abort> module.
1298              
1299             =cut
1300              
1301             sub abort {
1302 0     0 0   shift->app->abort(@_);
1303             }
1304             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1305              
1306             #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1307              
1308             =pod
1309              
1310             =head1 Bugs
1311              
1312             This project is available on github at L<https://github.com/mewsoft/Nile>.
1313              
1314             =head1 HOMEPAGE
1315              
1316             Please visit the project's homepage at L<https://metacpan.org/release/Nile>.
1317              
1318             =head1 SOURCE
1319              
1320             Source repository is at L<https://github.com/mewsoft/Nile>.
1321              
1322             =head1 SEE ALSO
1323              
1324             See L<Nile> for details about the complete framework.
1325              
1326             =head1 AUTHOR
1327              
1328             Ahmed Amin Elsheshtawy, احمد امين الششتاوى <mewsoft@cpan.org>
1329             Website: http://www.mewsoft.com
1330              
1331             =head1 COPYRIGHT AND LICENSE
1332              
1333             Copyright (C) 2014-2015 by Dr. Ahmed Amin Elsheshtawy احمد امين الششتاوى mewsoft@cpan.org, support@mewsoft.com,
1334             L<https://github.com/mewsoft/Nile>, L<http://www.mewsoft.com>
1335              
1336             This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
1337              
1338             =cut
1339              
1340             1;