| 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.46'; | 
| 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.46'; | 
| 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 |  |  |  |  |  |  | <database> | 
| 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 |  |  |  |  |  |  | </database> | 
| 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 DATABASE | 
| 672 |  |  |  |  |  |  |  | 
| 673 |  |  |  |  |  |  | See L<Nile::Database> | 
| 674 |  |  |  |  |  |  |  | 
| 675 |  |  |  |  |  |  | The database 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; | 
| 684 | 1 |  |  | 1 |  | 70810 | use CGI::Carp qw(fatalsToBrowser warningsToBrowser set_message); | 
|  | 1 |  |  |  |  | 8829 |  | 
|  | 1 |  |  |  |  | 8 |  | 
| 685 | 1 |  |  | 1 |  | 5984 | use Devel::StackTrace; | 
|  | 1 |  |  |  |  | 22793 |  | 
|  | 1 |  |  |  |  | 31 |  | 
| 686 | 1 |  |  | 1 |  | 12107 | use Devel::StackTrace::AsHTML; | 
|  | 1 |  |  |  |  | 22531 |  | 
|  | 1 |  |  |  |  | 42 |  | 
| 687 | 1 |  |  | 1 |  | 815 | use PadWalker; | 
|  | 1 |  |  |  |  | 6475 |  | 
|  | 1 |  |  |  |  | 92 |  | 
| 688 | 1 |  |  | 1 |  | 1074 | use Devel::StackTrace::WithLexicals; | 
|  | 0 |  |  |  |  |  |  | 
|  | 0 |  |  |  |  |  |  | 
| 689 |  |  |  |  |  |  |  | 
| 690 |  |  |  |  |  |  | sub handle_errors { | 
| 691 |  |  |  |  |  |  | my $msg = shift; | 
| 692 |  |  |  |  |  |  | #my $trace = Devel::StackTrace->new(indent => 1, message => $msg, ignore_package => [qw(Carp CGI::Carp)]); | 
| 693 |  |  |  |  |  |  | my $trace = Devel::StackTrace::WithLexicals->new(indent => 1, message => $msg, ignore_package => [qw(Carp CGI::Carp)]); | 
| 694 |  |  |  |  |  |  | #$trace->frames(reverse $trace->frames); | 
| 695 |  |  |  |  |  |  | print $trace->as_html; | 
| 696 |  |  |  |  |  |  | } | 
| 697 |  |  |  |  |  |  | set_message(\&handle_errors); | 
| 698 |  |  |  |  |  |  | } | 
| 699 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 700 |  |  |  |  |  |  | use Moose; | 
| 701 |  |  |  |  |  |  | use namespace::autoclean; | 
| 702 |  |  |  |  |  |  | use MooseX::MethodAttributes; | 
| 703 |  |  |  |  |  |  | #use MooseX::ClassAttribute; | 
| 704 |  |  |  |  |  |  |  | 
| 705 |  |  |  |  |  |  | use utf8; | 
| 706 |  |  |  |  |  |  | use File::Spec; | 
| 707 |  |  |  |  |  |  | use File::Basename; | 
| 708 |  |  |  |  |  |  | use Cwd; | 
| 709 |  |  |  |  |  |  | use URI; | 
| 710 |  |  |  |  |  |  | use Encode (); | 
| 711 |  |  |  |  |  |  | use URI::Escape; | 
| 712 |  |  |  |  |  |  | use Crypt::RC4; | 
| 713 |  |  |  |  |  |  | #use Crypt::CBC; | 
| 714 |  |  |  |  |  |  | use Capture::Tiny (); | 
| 715 |  |  |  |  |  |  | use Time::Local; | 
| 716 |  |  |  |  |  |  | use File::Slurp; | 
| 717 |  |  |  |  |  |  | use Time::HiRes qw(gettimeofday tv_interval); | 
| 718 |  |  |  |  |  |  | use MIME::Base64 3.11 qw(encode_base64 decode_base64 decode_base64url encode_base64url); | 
| 719 |  |  |  |  |  |  |  | 
| 720 |  |  |  |  |  |  | use Data::Dumper; | 
| 721 |  |  |  |  |  |  | $Data::Dumper::Deparse = 1; #stringify coderefs | 
| 722 |  |  |  |  |  |  | #use LWP::UserAgent; | 
| 723 |  |  |  |  |  |  |  | 
| 724 |  |  |  |  |  |  | #no warnings qw(void once uninitialized numeric); | 
| 725 |  |  |  |  |  |  |  | 
| 726 |  |  |  |  |  |  | use Nile::App; | 
| 727 |  |  |  |  |  |  | use Nile::Say; | 
| 728 |  |  |  |  |  |  | use Nile::Plugin; | 
| 729 |  |  |  |  |  |  | use Nile::Plugin::Object; | 
| 730 |  |  |  |  |  |  | use Nile::Module; | 
| 731 |  |  |  |  |  |  | use Nile::View; | 
| 732 |  |  |  |  |  |  | use Nile::XML; | 
| 733 |  |  |  |  |  |  | use Nile::Var; | 
| 734 |  |  |  |  |  |  | use Nile::File; | 
| 735 |  |  |  |  |  |  | use Nile::Lang; | 
| 736 |  |  |  |  |  |  | use Nile::Config; | 
| 737 |  |  |  |  |  |  | use Nile::Router; | 
| 738 |  |  |  |  |  |  | use Nile::Dispatcher; | 
| 739 |  |  |  |  |  |  | use Nile::Database; | 
| 740 |  |  |  |  |  |  | use Nile::Setting; | 
| 741 |  |  |  |  |  |  | use Nile::Timer; | 
| 742 |  |  |  |  |  |  | use Nile::HTTP::Request; | 
| 743 |  |  |  |  |  |  | use Nile::HTTP::Response; | 
| 744 |  |  |  |  |  |  |  | 
| 745 |  |  |  |  |  |  | #use base 'Import::Base'; | 
| 746 |  |  |  |  |  |  | use Import::Into; | 
| 747 |  |  |  |  |  |  | use Module::Load; | 
| 748 |  |  |  |  |  |  | use Module::Runtime qw(use_module); | 
| 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 |  |  |  |  |  |  | use base 'Exporter'; | 
| 762 |  |  |  |  |  |  | our @EXPORT = qw(); | 
| 763 |  |  |  |  |  |  | our @EXPORT_OK = qw(); | 
| 764 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 765 |  |  |  |  |  |  | sub import { | 
| 766 |  |  |  |  |  |  |  | 
| 767 |  |  |  |  |  |  | my ($class, @args) = @_; | 
| 768 |  |  |  |  |  |  |  | 
| 769 |  |  |  |  |  |  | my ($package, $script) = caller; | 
| 770 |  |  |  |  |  |  |  | 
| 771 |  |  |  |  |  |  | # import list of modules to the calling package | 
| 772 |  |  |  |  |  |  | my @modules = @EXPORT_MODULES; | 
| 773 |  |  |  |  |  |  | while (@modules) { | 
| 774 |  |  |  |  |  |  | my $module = shift @modules; | 
| 775 |  |  |  |  |  |  | my $imports = ref $modules[0] eq 'ARRAY' ? shift @modules : []; | 
| 776 |  |  |  |  |  |  | use_module($module)->import::into($package, @{$imports}); | 
| 777 |  |  |  |  |  |  | } | 
| 778 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 779 |  |  |  |  |  |  | $class->detect_app_path($script); | 
| 780 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 781 |  |  |  |  |  |  | my $caller = $class.'::'; | 
| 782 |  |  |  |  |  |  | { | 
| 783 |  |  |  |  |  |  | no strict 'refs'; | 
| 784 |  |  |  |  |  |  | @{$caller.'EXPORT'} = @EXPORT; | 
| 785 |  |  |  |  |  |  | foreach my $sub (@EXPORT) { | 
| 786 |  |  |  |  |  |  | next if (*{"$caller$sub"}{CODE}); | 
| 787 |  |  |  |  |  |  | *{"$caller$sub"} = \*{$sub}; | 
| 788 |  |  |  |  |  |  | } | 
| 789 |  |  |  |  |  |  | } | 
| 790 |  |  |  |  |  |  |  | 
| 791 |  |  |  |  |  |  | $class->export_to_level(1, $class, @args); | 
| 792 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 793 |  |  |  |  |  |  | } | 
| 794 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 795 |  |  |  |  |  |  | sub detect_app_path { | 
| 796 |  |  |  |  |  |  |  | 
| 797 |  |  |  |  |  |  | my ($self, $script) = @_; | 
| 798 |  |  |  |  |  |  |  | 
| 799 |  |  |  |  |  |  | $script ||= (caller)[1]; | 
| 800 |  |  |  |  |  |  |  | 
| 801 |  |  |  |  |  |  | my ($vol, $dirs, $name) =   File::Spec->splitpath(File::Spec->rel2abs($script)); | 
| 802 |  |  |  |  |  |  |  | 
| 803 |  |  |  |  |  |  | if (-d (my $fulldir = File::Spec->catdir($dirs, $name))) { | 
| 804 |  |  |  |  |  |  | $dirs = $fulldir; | 
| 805 |  |  |  |  |  |  | $name = ""; | 
| 806 |  |  |  |  |  |  | } | 
| 807 |  |  |  |  |  |  |  | 
| 808 |  |  |  |  |  |  | my $path = $vol? File::Spec->catpath($vol, $dirs) : File::Spec->catdir($dirs); | 
| 809 |  |  |  |  |  |  |  | 
| 810 |  |  |  |  |  |  | $ENV{NILE_APP_DIR} = $path; | 
| 811 |  |  |  |  |  |  |  | 
| 812 |  |  |  |  |  |  | return ($path); | 
| 813 |  |  |  |  |  |  | } | 
| 814 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 815 |  |  |  |  |  |  | sub BUILD { # our sub new {...} | 
| 816 |  |  |  |  |  |  | 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 |  |  |  |  |  |  | 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 |  |  |  |  |  |  | $arg = $self->init(); | 
| 891 |  |  |  |  |  |  |  | 
| 892 |  |  |  |  |  |  | my ($package, $script) = caller; | 
| 893 |  |  |  |  |  |  |  | 
| 894 |  |  |  |  |  |  | $arg->{path} ||= $self->detect_app_path($script); | 
| 895 |  |  |  |  |  |  |  | 
| 896 |  |  |  |  |  |  | my $file = $self->file; | 
| 897 |  |  |  |  |  |  |  | 
| 898 |  |  |  |  |  |  | # setup the path for the app folders | 
| 899 |  |  |  |  |  |  | foreach (qw(api cache cmd config cron data file lib log route temp web)) { | 
| 900 |  |  |  |  |  |  | $self->var->set($_."_dir" => $file->catdir($arg->{path}, $_)); | 
| 901 |  |  |  |  |  |  | } | 
| 902 |  |  |  |  |  |  |  | 
| 903 |  |  |  |  |  |  | $self->var->set( | 
| 904 |  |  |  |  |  |  | 'path'                  =>  $arg->{path}, | 
| 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 |  |  |  |  |  |  | push @INC, $self->var->get("lib_dir"); | 
| 913 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 914 |  |  |  |  |  |  | # detect and load request and response handler classes | 
| 915 |  |  |  |  |  |  | $arg->{mode} ||= "cgi"; | 
| 916 |  |  |  |  |  |  | $arg->{mode} = lc($arg->{mode}); | 
| 917 |  |  |  |  |  |  | $self->mode($arg->{mode}); | 
| 918 |  |  |  |  |  |  |  | 
| 919 |  |  |  |  |  |  | #$self->log->debug("mode: $arg{mode}"); | 
| 920 |  |  |  |  |  |  |  | 
| 921 |  |  |  |  |  |  | # force PSGI if PLACK_ENV is set | 
| 922 |  |  |  |  |  |  | if ($ENV{'PLACK_ENV'}) { | 
| 923 |  |  |  |  |  |  | $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 |  |  |  |  |  |  | if ($self->mode() ne "psgi") { | 
| 930 |  |  |  |  |  |  | if (exists $ENV{GATEWAY_INTERFACE} ) { | 
| 931 |  |  |  |  |  |  | # CGI | 
| 932 |  |  |  |  |  |  | $self->mode("cgi"); | 
| 933 |  |  |  |  |  |  | } | 
| 934 |  |  |  |  |  |  | else { | 
| 935 |  |  |  |  |  |  | # FCGI or command line | 
| 936 |  |  |  |  |  |  | $self->mode("fcgi"); | 
| 937 |  |  |  |  |  |  | } | 
| 938 |  |  |  |  |  |  | } | 
| 939 |  |  |  |  |  |  |  | 
| 940 |  |  |  |  |  |  | #$self->log->debug("mode to run: $arg{mode}"); | 
| 941 |  |  |  |  |  |  |  | 
| 942 |  |  |  |  |  |  | if ($self->mode() eq "psgi") { | 
| 943 |  |  |  |  |  |  | load Nile::HTTP::Request::PSGI; | 
| 944 |  |  |  |  |  |  | load Nile::Handler::PSGI; | 
| 945 |  |  |  |  |  |  | } | 
| 946 |  |  |  |  |  |  | elsif ($self->mode() eq "fcgi") { | 
| 947 |  |  |  |  |  |  | load Nile::HTTP::Request; | 
| 948 |  |  |  |  |  |  | load Nile::Handler::CGI; | 
| 949 |  |  |  |  |  |  | load Nile::Handler::FCGI; | 
| 950 |  |  |  |  |  |  | } | 
| 951 |  |  |  |  |  |  | else { | 
| 952 |  |  |  |  |  |  | load Nile::HTTP::Request; | 
| 953 |  |  |  |  |  |  | load Nile::Handler::CGI; | 
| 954 |  |  |  |  |  |  | } | 
| 955 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 956 |  |  |  |  |  |  | # load config files from init | 
| 957 |  |  |  |  |  |  | foreach (@{$arg->{config}}) { | 
| 958 |  |  |  |  |  |  | #$self->config->xml->keep_order(1); | 
| 959 |  |  |  |  |  |  | $self->config->load($_); | 
| 960 |  |  |  |  |  |  | } | 
| 961 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 962 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 963 |  |  |  |  |  |  | # load extra config files from config files settings | 
| 964 |  |  |  |  |  |  | foreach my $config ($self->config->get("app/config")) { | 
| 965 |  |  |  |  |  |  | $config = $self->filter->trim($config); | 
| 966 |  |  |  |  |  |  | $self->config->load($config) if ($config); | 
| 967 |  |  |  |  |  |  | } | 
| 968 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 969 |  |  |  |  |  |  | # load route files | 
| 970 |  |  |  |  |  |  | foreach my $route($self->config->get("app/route")) { | 
| 971 |  |  |  |  |  |  | $route = $self->filter->trim($route); | 
| 972 |  |  |  |  |  |  | $self->router->load($route) if ($route); | 
| 973 |  |  |  |  |  |  | } | 
| 974 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 975 |  |  |  |  |  |  | foreach my $config (qw(charset action_name lang theme default_route log_file)) { | 
| 976 |  |  |  |  |  |  | if ($self->config->get("app/$config")) { | 
| 977 |  |  |  |  |  |  | $self->var->set($config, $self->config->get("app/$config")); | 
| 978 |  |  |  |  |  |  | } | 
| 979 |  |  |  |  |  |  | } | 
| 980 |  |  |  |  |  |  | #------------------------------------------------------ | 
| 981 |  |  |  |  |  |  | my $class = "Nile::Handler::" . uc($self->mode()); | 
| 982 |  |  |  |  |  |  | my $handler = $self->object($class); | 
| 983 |  |  |  |  |  |  | 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 |  |  |  |  |  |  | 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 |  |  |  |  |  |  | my $self = shift; | 
| 1014 |  |  |  |  |  |  | $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 |  |  |  |  |  |  | my $self = shift; | 
| 1038 |  |  |  |  |  |  | $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 |  |  |  |  |  |  | my $self = shift; | 
| 1063 |  |  |  |  |  |  | $self->add_action_route("command", @_); | 
| 1064 |  |  |  |  |  |  | } | 
| 1065 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 1066 |  |  |  |  |  |  | sub add_action_route { | 
| 1067 |  |  |  |  |  |  | my $self = shift; | 
| 1068 |  |  |  |  |  |  | my $type = shift; | 
| 1069 |  |  |  |  |  |  | my ($method, $route, $action) = $self->action_args(@_); | 
| 1070 |  |  |  |  |  |  | $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 |  |  |  |  |  |  | my $self = shift; | 
| 1085 |  |  |  |  |  |  |  | 
| 1086 |  |  |  |  |  |  | #my @methods = qw(get post put patch delete options head); | 
| 1087 |  |  |  |  |  |  |  | 
| 1088 |  |  |  |  |  |  | my ($method, $route, $action); | 
| 1089 |  |  |  |  |  |  |  | 
| 1090 |  |  |  |  |  |  | if (@_ == 1) { | 
| 1091 |  |  |  |  |  |  | #$app->action(sub {}); | 
| 1092 |  |  |  |  |  |  | ($action) = @_; | 
| 1093 |  |  |  |  |  |  | } | 
| 1094 |  |  |  |  |  |  | elsif (@_ == 2) { | 
| 1095 |  |  |  |  |  |  | #$app->action("/home", sub {}); | 
| 1096 |  |  |  |  |  |  | ($route, $action) = @_; | 
| 1097 |  |  |  |  |  |  | } | 
| 1098 |  |  |  |  |  |  | elsif (@_ == 3) { | 
| 1099 |  |  |  |  |  |  | #$app->action("get", "/home", sub {}); | 
| 1100 |  |  |  |  |  |  | ($method, $route, $action) = @_; | 
| 1101 |  |  |  |  |  |  | } | 
| 1102 |  |  |  |  |  |  | else { | 
| 1103 |  |  |  |  |  |  | $self->abort("Action error. Empty action and route. Syntax \$app->action(\$method, \$route, \$coderef) "); | 
| 1104 |  |  |  |  |  |  | } | 
| 1105 |  |  |  |  |  |  |  | 
| 1106 |  |  |  |  |  |  | $method ||= ""; | 
| 1107 |  |  |  |  |  |  | $route ||= "/"; | 
| 1108 |  |  |  |  |  |  |  | 
| 1109 |  |  |  |  |  |  | if (ref($action) ne "CODE") { | 
| 1110 |  |  |  |  |  |  | $self->abort("Action error, must be a valid code reference. Syntax \$app->action(\$method, \$route, \$coderef) "); | 
| 1111 |  |  |  |  |  |  | } | 
| 1112 |  |  |  |  |  |  |  | 
| 1113 |  |  |  |  |  |  | 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 |  |  |  |  |  |  | my ($self, $class, @args) = @_; | 
| 1223 |  |  |  |  |  |  | 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 |  |  |  |  |  |  | if (@args && @args % 2) { | 
| 1231 |  |  |  |  |  |  | # Moose needs args as hash, so convert odd size arrays to even for hashing | 
| 1232 |  |  |  |  |  |  | $object = $class->new(@args, undef); | 
| 1233 |  |  |  |  |  |  | } | 
| 1234 |  |  |  |  |  |  | else { | 
| 1235 |  |  |  |  |  |  | $object = $class->new(@args); | 
| 1236 |  |  |  |  |  |  | } | 
| 1237 |  |  |  |  |  |  |  | 
| 1238 |  |  |  |  |  |  | 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 |  |  |  |  |  |  | $self->add_object_context($object, $meta); | 
| 1246 |  |  |  |  |  |  |  | 
| 1247 |  |  |  |  |  |  | # if class has defined "main" method, then call it | 
| 1248 |  |  |  |  |  |  | if ($object->can("main")) { | 
| 1249 |  |  |  |  |  |  | $object->main(@args); | 
| 1250 |  |  |  |  |  |  | } | 
| 1251 |  |  |  |  |  |  |  | 
| 1252 |  |  |  |  |  |  | #no strict 'refs'; | 
| 1253 |  |  |  |  |  |  | #*{"$object"."::app"} = \&app; | 
| 1254 |  |  |  |  |  |  | #${"${package}::$apps"} = 1; | 
| 1255 |  |  |  |  |  |  |  | 
| 1256 |  |  |  |  |  |  | return $object; | 
| 1257 |  |  |  |  |  |  | } | 
| 1258 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 1259 |  |  |  |  |  |  | sub add_object_context { | 
| 1260 |  |  |  |  |  |  | my ($self, $object, $meta) = @_; | 
| 1261 |  |  |  |  |  |  | $meta ||= $object->meta; | 
| 1262 |  |  |  |  |  |  | # add method "me" or one of its alt | 
| 1263 |  |  |  |  |  |  | #foreach (qw(app APP _app)) { | 
| 1264 |  |  |  |  |  |  | foreach (qw(app)) { | 
| 1265 |  |  |  |  |  |  | unless ($object->can($_)) { | 
| 1266 |  |  |  |  |  |  | $meta->add_attribute($_ => (is => 'rw', default => sub{$self})); | 
| 1267 |  |  |  |  |  |  | $object->$_($self); | 
| 1268 |  |  |  |  |  |  | last; | 
| 1269 |  |  |  |  |  |  | } | 
| 1270 |  |  |  |  |  |  | } | 
| 1271 |  |  |  |  |  |  | } | 
| 1272 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 1273 |  |  |  |  |  |  | =head2 dump() | 
| 1274 |  |  |  |  |  |  |  | 
| 1275 |  |  |  |  |  |  | $app->dump({...}); | 
| 1276 |  |  |  |  |  |  |  | 
| 1277 |  |  |  |  |  |  | Print object to the STDOUT. Same as C<say Dumper (@_);>. | 
| 1278 |  |  |  |  |  |  |  | 
| 1279 |  |  |  |  |  |  | =cut | 
| 1280 |  |  |  |  |  |  |  | 
| 1281 |  |  |  |  |  |  | sub dump { | 
| 1282 |  |  |  |  |  |  | my $self = shift; | 
| 1283 |  |  |  |  |  |  | say Dumper (@_); | 
| 1284 |  |  |  |  |  |  | return; | 
| 1285 |  |  |  |  |  |  | } | 
| 1286 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 1287 |  |  |  |  |  |  | =head2 is_loaded() | 
| 1288 |  |  |  |  |  |  |  | 
| 1289 |  |  |  |  |  |  | if ($app->is_loaded("Nile::SomeModule")) { | 
| 1290 |  |  |  |  |  |  | #... | 
| 1291 |  |  |  |  |  |  | } | 
| 1292 |  |  |  |  |  |  |  | 
| 1293 |  |  |  |  |  |  | if ($app->is_loaded("Nile/SomeModule.pm")) { | 
| 1294 |  |  |  |  |  |  | #... | 
| 1295 |  |  |  |  |  |  | } | 
| 1296 |  |  |  |  |  |  |  | 
| 1297 |  |  |  |  |  |  | Returns true if module is loaded, false otherwise. | 
| 1298 |  |  |  |  |  |  |  | 
| 1299 |  |  |  |  |  |  | =cut | 
| 1300 |  |  |  |  |  |  |  | 
| 1301 |  |  |  |  |  |  | sub is_loaded { | 
| 1302 |  |  |  |  |  |  | my ($self, $module) = @_; | 
| 1303 |  |  |  |  |  |  | (my $file = $module) =~ s/::/\//g; | 
| 1304 |  |  |  |  |  |  | $file .= '.pm' unless ($file =~ /\.pm$/); | 
| 1305 |  |  |  |  |  |  | #note: do() does unconditional loading -- no lookup in the %INC hash is made. | 
| 1306 |  |  |  |  |  |  | exists $INC{$file}; | 
| 1307 |  |  |  |  |  |  | #return eval { $module->can( 'can' ) }; | 
| 1308 |  |  |  |  |  |  | #return UNIVERSAL::can($module,'can'); | 
| 1309 |  |  |  |  |  |  | } | 
| 1310 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 1311 |  |  |  |  |  |  | =head2 cli_mode() | 
| 1312 |  |  |  |  |  |  |  | 
| 1313 |  |  |  |  |  |  | if ($app->cli_mode) { | 
| 1314 |  |  |  |  |  |  | say "Running from the command line"; | 
| 1315 |  |  |  |  |  |  | } | 
| 1316 |  |  |  |  |  |  | else { | 
| 1317 |  |  |  |  |  |  | say "Running from web server"; | 
| 1318 |  |  |  |  |  |  | } | 
| 1319 |  |  |  |  |  |  |  | 
| 1320 |  |  |  |  |  |  | Returns true if running from the command line interface, false if called from web server. | 
| 1321 |  |  |  |  |  |  |  | 
| 1322 |  |  |  |  |  |  | =cut | 
| 1323 |  |  |  |  |  |  |  | 
| 1324 |  |  |  |  |  |  | sub cli_mode { | 
| 1325 |  |  |  |  |  |  | my ($self) = @_; | 
| 1326 |  |  |  |  |  |  |  | 
| 1327 |  |  |  |  |  |  | if  (exists $ENV{REQUEST_METHOD} || defined $ENV{GATEWAY_INTERFACE} ||  exists $ENV{HTTP_HOST}){ | 
| 1328 |  |  |  |  |  |  | return 0; | 
| 1329 |  |  |  |  |  |  | } | 
| 1330 |  |  |  |  |  |  |  | 
| 1331 |  |  |  |  |  |  | # PSGI | 
| 1332 |  |  |  |  |  |  | if  (exists $self->env->{REQUEST_METHOD} || defined $self->env->{GATEWAY_INTERFACE} ||  exists $self->env->{HTTP_HOST}){ | 
| 1333 |  |  |  |  |  |  | return 0; | 
| 1334 |  |  |  |  |  |  | } | 
| 1335 |  |  |  |  |  |  |  | 
| 1336 |  |  |  |  |  |  | # CLI | 
| 1337 |  |  |  |  |  |  | return 1; | 
| 1338 |  |  |  |  |  |  |  | 
| 1339 |  |  |  |  |  |  | #if (-t STDIN) { } | 
| 1340 |  |  |  |  |  |  | #use IO::Interactive qw(is_interactive interactive busy);if ( is_interactive() ) {print "Running interactively\n";} | 
| 1341 |  |  |  |  |  |  | } | 
| 1342 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 1343 |  |  |  |  |  |  | =head2 error() | 
| 1344 |  |  |  |  |  |  |  | 
| 1345 |  |  |  |  |  |  | $app->error("error message"); | 
| 1346 |  |  |  |  |  |  |  | 
| 1347 |  |  |  |  |  |  | Fatal errors with custom error message. This is the same as C<croak> in L<CGI::Carp|CGI::Carp/croak>. | 
| 1348 |  |  |  |  |  |  |  | 
| 1349 |  |  |  |  |  |  | =cut | 
| 1350 |  |  |  |  |  |  |  | 
| 1351 |  |  |  |  |  |  | sub error { | 
| 1352 |  |  |  |  |  |  | my $self = shift; | 
| 1353 |  |  |  |  |  |  | goto &CGI::Carp::croak; | 
| 1354 |  |  |  |  |  |  | } | 
| 1355 |  |  |  |  |  |  |  | 
| 1356 |  |  |  |  |  |  | =head2 errors() | 
| 1357 |  |  |  |  |  |  |  | 
| 1358 |  |  |  |  |  |  | $app->errors("error message"); | 
| 1359 |  |  |  |  |  |  |  | 
| 1360 |  |  |  |  |  |  | Fatal errors with custom error message and full starcktrace. This is the same as C<confess> in L<CGI::Carp|CGI::Carp/confess>. | 
| 1361 |  |  |  |  |  |  |  | 
| 1362 |  |  |  |  |  |  | =cut | 
| 1363 |  |  |  |  |  |  |  | 
| 1364 |  |  |  |  |  |  | sub errors { | 
| 1365 |  |  |  |  |  |  | my $self = shift; | 
| 1366 |  |  |  |  |  |  | goto &CGI::Carp::confess; | 
| 1367 |  |  |  |  |  |  | } | 
| 1368 |  |  |  |  |  |  |  | 
| 1369 |  |  |  |  |  |  | =head2 warn() | 
| 1370 |  |  |  |  |  |  |  | 
| 1371 |  |  |  |  |  |  | $app->warn("warning  message"); | 
| 1372 |  |  |  |  |  |  |  | 
| 1373 |  |  |  |  |  |  | Display warning message. This is the same as C<carp> in L<CGI::Carp|CGI::Carp/carp>. | 
| 1374 |  |  |  |  |  |  |  | 
| 1375 |  |  |  |  |  |  | To view warnings in the browser, switch to the view source mode since warnings appear as | 
| 1376 |  |  |  |  |  |  | a comment at the top of the page. | 
| 1377 |  |  |  |  |  |  |  | 
| 1378 |  |  |  |  |  |  | =cut | 
| 1379 |  |  |  |  |  |  |  | 
| 1380 |  |  |  |  |  |  | sub warn { | 
| 1381 |  |  |  |  |  |  | my $self = shift; | 
| 1382 |  |  |  |  |  |  | # warnings appear commented at the top of the page, use view source | 
| 1383 |  |  |  |  |  |  | warningsToBrowser(1) unless ($self->cli_mode); | 
| 1384 |  |  |  |  |  |  | goto &CGI::Carp::carp; | 
| 1385 |  |  |  |  |  |  | } | 
| 1386 |  |  |  |  |  |  |  | 
| 1387 |  |  |  |  |  |  | =head2 warns() | 
| 1388 |  |  |  |  |  |  |  | 
| 1389 |  |  |  |  |  |  | $app->warns("warning  message"); | 
| 1390 |  |  |  |  |  |  |  | 
| 1391 |  |  |  |  |  |  | Display warning message and full starcktrace. This is the same as C<cluck> in L<CGI::Carp|CGI::Carp/cluck>. | 
| 1392 |  |  |  |  |  |  |  | 
| 1393 |  |  |  |  |  |  | To view warnings in the browser, switch to the view source mode since warnings appear as | 
| 1394 |  |  |  |  |  |  | a comment at the top of the page. | 
| 1395 |  |  |  |  |  |  |  | 
| 1396 |  |  |  |  |  |  | =cut | 
| 1397 |  |  |  |  |  |  |  | 
| 1398 |  |  |  |  |  |  | sub warns { | 
| 1399 |  |  |  |  |  |  | my $self = shift; | 
| 1400 |  |  |  |  |  |  | # warnings appear commented at the top of the page, use view source | 
| 1401 |  |  |  |  |  |  | warningsToBrowser(1) unless ($self->cli_mode); | 
| 1402 |  |  |  |  |  |  | goto &CGI::Carp::cluck; | 
| 1403 |  |  |  |  |  |  | } | 
| 1404 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 1405 |  |  |  |  |  |  | =head2 abort() | 
| 1406 |  |  |  |  |  |  |  | 
| 1407 |  |  |  |  |  |  | $app->abort("error message"); | 
| 1408 |  |  |  |  |  |  |  | 
| 1409 |  |  |  |  |  |  | $app->abort("error title", "error message"); | 
| 1410 |  |  |  |  |  |  |  | 
| 1411 |  |  |  |  |  |  | Stop and quit the application and display message to the user. See L<Nile::Abort> module. | 
| 1412 |  |  |  |  |  |  |  | 
| 1413 |  |  |  |  |  |  | =cut | 
| 1414 |  |  |  |  |  |  |  | 
| 1415 |  |  |  |  |  |  | sub abort { | 
| 1416 |  |  |  |  |  |  | my ($self) = shift; | 
| 1417 |  |  |  |  |  |  | load Nile::Abort; | 
| 1418 |  |  |  |  |  |  | Nile::Abort->abort(@_); | 
| 1419 |  |  |  |  |  |  | } | 
| 1420 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 1421 |  |  |  |  |  |  | #__PACKAGE__->meta->make_immutable;#(inline_constructor => 0) | 
| 1422 |  |  |  |  |  |  | #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | 
| 1423 |  |  |  |  |  |  |  | 
| 1424 |  |  |  |  |  |  | =head1 Sub Modules | 
| 1425 |  |  |  |  |  |  |  | 
| 1426 |  |  |  |  |  |  | App L<Nile::App>. | 
| 1427 |  |  |  |  |  |  |  | 
| 1428 |  |  |  |  |  |  | Views   L<Nile::View>. | 
| 1429 |  |  |  |  |  |  |  | 
| 1430 |  |  |  |  |  |  | Shared Vars L<Nile::Var>. | 
| 1431 |  |  |  |  |  |  |  | 
| 1432 |  |  |  |  |  |  | Langauge    L<Nile::Lang>. | 
| 1433 |  |  |  |  |  |  |  | 
| 1434 |  |  |  |  |  |  | Request L<Nile::HTTP::Request>. | 
| 1435 |  |  |  |  |  |  |  | 
| 1436 |  |  |  |  |  |  | PSGI Request    L<Nile::HTTP::Request::PSGI>. | 
| 1437 |  |  |  |  |  |  |  | 
| 1438 |  |  |  |  |  |  | PSGI Request Base   L<Nile::HTTP::PSGI>. | 
| 1439 |  |  |  |  |  |  |  | 
| 1440 |  |  |  |  |  |  | Response    L<Nile::HTTP::Response>. | 
| 1441 |  |  |  |  |  |  |  | 
| 1442 |  |  |  |  |  |  | PSGI Handler L<Nile::Handler::PSGI>. | 
| 1443 |  |  |  |  |  |  |  | 
| 1444 |  |  |  |  |  |  | FCGI Handler L<Nile::Handler::FCGI>. | 
| 1445 |  |  |  |  |  |  |  | 
| 1446 |  |  |  |  |  |  | CGI Handler L<Nile::Handler::CGI>. | 
| 1447 |  |  |  |  |  |  |  | 
| 1448 |  |  |  |  |  |  | Dispatcher L<Nile::Dispatcher>. | 
| 1449 |  |  |  |  |  |  |  | 
| 1450 |  |  |  |  |  |  | Router L<Nile::Router>. | 
| 1451 |  |  |  |  |  |  |  | 
| 1452 |  |  |  |  |  |  | File Utils L<Nile::File>. | 
| 1453 |  |  |  |  |  |  |  | 
| 1454 |  |  |  |  |  |  | Database L<Nile::Database>. | 
| 1455 |  |  |  |  |  |  |  | 
| 1456 |  |  |  |  |  |  | XML L<Nile::XML>. | 
| 1457 |  |  |  |  |  |  |  | 
| 1458 |  |  |  |  |  |  | Settings    L<Nile::Setting>. | 
| 1459 |  |  |  |  |  |  |  | 
| 1460 |  |  |  |  |  |  | Serializer L<Nile::Serializer>. | 
| 1461 |  |  |  |  |  |  |  | 
| 1462 |  |  |  |  |  |  | Deserializer L<Nile::Deserializer>. | 
| 1463 |  |  |  |  |  |  |  | 
| 1464 |  |  |  |  |  |  | Serialization Base L<Nile::Serialization>. | 
| 1465 |  |  |  |  |  |  |  | 
| 1466 |  |  |  |  |  |  | Filter  L<Nile::Filter>. | 
| 1467 |  |  |  |  |  |  |  | 
| 1468 |  |  |  |  |  |  | MIME L<Nile::MIME>. | 
| 1469 |  |  |  |  |  |  |  | 
| 1470 |  |  |  |  |  |  | Timer   L<Nile::Timer>. | 
| 1471 |  |  |  |  |  |  |  | 
| 1472 |  |  |  |  |  |  | Plugin  L<Nile::Plugin>. | 
| 1473 |  |  |  |  |  |  |  | 
| 1474 |  |  |  |  |  |  | Session L<Nile::Plugin::Session>. | 
| 1475 |  |  |  |  |  |  |  | 
| 1476 |  |  |  |  |  |  | Cache L<Nile::Plugin::Cache>. | 
| 1477 |  |  |  |  |  |  |  | 
| 1478 |  |  |  |  |  |  | Cache Redis L<Nile::Plugin::Cache::Redis>. | 
| 1479 |  |  |  |  |  |  |  | 
| 1480 |  |  |  |  |  |  | Email L<Nile::Plugin::Email>. | 
| 1481 |  |  |  |  |  |  |  | 
| 1482 |  |  |  |  |  |  | Paginatation L<Nile::Plugin::Paginate>. | 
| 1483 |  |  |  |  |  |  |  | 
| 1484 |  |  |  |  |  |  | Module L<Nile::Module>. | 
| 1485 |  |  |  |  |  |  |  | 
| 1486 |  |  |  |  |  |  | Hook L<Nile::Hook>. | 
| 1487 |  |  |  |  |  |  |  | 
| 1488 |  |  |  |  |  |  | Base L<Nile::Base>. | 
| 1489 |  |  |  |  |  |  |  | 
| 1490 |  |  |  |  |  |  | Abort L<Nile::Abort>. | 
| 1491 |  |  |  |  |  |  |  | 
| 1492 |  |  |  |  |  |  | =head1 Bugs | 
| 1493 |  |  |  |  |  |  |  | 
| 1494 |  |  |  |  |  |  | This project is available on github at L<https://github.com/mewsoft/Nile>. | 
| 1495 |  |  |  |  |  |  |  | 
| 1496 |  |  |  |  |  |  | =head1 HOMEPAGE | 
| 1497 |  |  |  |  |  |  |  | 
| 1498 |  |  |  |  |  |  | Please visit the project's homepage at L<https://metacpan.org/release/Nile>. | 
| 1499 |  |  |  |  |  |  |  | 
| 1500 |  |  |  |  |  |  | =head1 SOURCE | 
| 1501 |  |  |  |  |  |  |  | 
| 1502 |  |  |  |  |  |  | Source repository is at L<https://github.com/mewsoft/Nile>. | 
| 1503 |  |  |  |  |  |  |  | 
| 1504 |  |  |  |  |  |  | =head1 AUTHOR | 
| 1505 |  |  |  |  |  |  |  | 
| 1506 |  |  |  |  |  |  | Ahmed Amin Elsheshtawy,  اØÙ
د اÙ
ÙÙ Ø§ÙØ´Ø´ØªØ§ÙÙ <mewsoft@cpan.org> | 
| 1507 |  |  |  |  |  |  | Website: http://www.mewsoft.com | 
| 1508 |  |  |  |  |  |  |  | 
| 1509 |  |  |  |  |  |  | =head1 COPYRIGHT AND LICENSE | 
| 1510 |  |  |  |  |  |  |  | 
| 1511 |  |  |  |  |  |  | Copyright (C) 2014-2015 by Dr. Ahmed Amin Elsheshtawy mewsoft@cpan.org, support@mewsoft.com, | 
| 1512 |  |  |  |  |  |  | L<https://github.com/mewsoft/Nile>, L<http://www.mewsoft.com> | 
| 1513 |  |  |  |  |  |  |  | 
| 1514 |  |  |  |  |  |  | This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself. | 
| 1515 |  |  |  |  |  |  |  | 
| 1516 |  |  |  |  |  |  | =cut | 
| 1517 |  |  |  |  |  |  |  | 
| 1518 |  |  |  |  |  |  |  | 
| 1519 |  |  |  |  |  |  | 1; |