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