line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Squatting; |
2
|
|
|
|
|
|
|
|
3
|
3
|
|
|
3
|
|
111786
|
use strict; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
130
|
|
4
|
3
|
|
|
3
|
|
16
|
no strict 'refs'; |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
109
|
|
5
|
|
|
|
|
|
|
#use warnings; |
6
|
|
|
|
|
|
|
#no warnings 'redefine'; |
7
|
3
|
|
|
3
|
|
19
|
use base 'Class::C3::Componentised'; |
|
3
|
|
|
|
|
11
|
|
|
3
|
|
|
|
|
3471
|
|
8
|
|
|
|
|
|
|
|
9
|
3
|
|
|
3
|
|
19437
|
use List::Util qw(first); |
|
3
|
|
|
|
|
7
|
|
|
3
|
|
|
|
|
504
|
|
10
|
3
|
|
|
3
|
|
2848
|
use URI::Escape; |
|
3
|
|
|
|
|
4799
|
|
|
3
|
|
|
|
|
231
|
|
11
|
3
|
|
|
3
|
|
22
|
use Carp; |
|
3
|
|
|
|
|
8
|
|
|
3
|
|
|
|
|
278
|
|
12
|
3
|
|
|
3
|
|
3227
|
use Data::Dump 'pp'; |
|
3
|
|
|
|
|
33350
|
|
|
3
|
|
|
|
|
4870
|
|
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
our $VERSION = '0.83'; |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
require Squatting::Controller; |
17
|
|
|
|
|
|
|
require Squatting::View; |
18
|
|
|
|
|
|
|
|
19
|
|
|
|
|
|
|
# XXX - deprecated | use App ':controllers' |
20
|
|
|
|
|
|
|
# XXX - deprecated | use App ':views' |
21
|
|
|
|
|
|
|
# use App @PLUGINS |
22
|
|
|
|
|
|
|
# |
23
|
|
|
|
|
|
|
# No longer have to : use base 'Squatting'; |
24
|
|
|
|
|
|
|
# Simply saying : use Squatting; |
25
|
|
|
|
|
|
|
# will muck with the calling packages @ISA. |
26
|
|
|
|
|
|
|
sub import { |
27
|
3
|
|
|
3
|
|
29
|
my $m = shift; |
28
|
3
|
|
|
|
|
15
|
my $p = (caller)[0]; |
29
|
|
|
|
|
|
|
|
30
|
3
|
50
|
|
|
|
18
|
if ($m ne 'Squatting') { |
31
|
0
|
|
|
|
|
0
|
return $m->load_components(grep /::/, @_); |
32
|
|
|
|
|
|
|
} |
33
|
|
|
|
|
|
|
|
34
|
3
|
|
|
|
|
17
|
push @{$p.'::ISA'}, 'Squatting'; |
|
3
|
|
|
|
|
53
|
|
35
|
|
|
|
|
|
|
|
36
|
|
|
|
|
|
|
# $url = R('Controller', @args, { cgi => vars }) # Generate URLs with the routing function |
37
|
3
|
|
|
|
|
159
|
*{$p."::Controllers::R"} = *{$p."::Views::R"} = *{$p."::R"} = sub { |
|
3
|
|
|
|
|
28
|
|
|
3
|
|
|
|
|
19
|
|
38
|
0
|
|
|
0
|
|
0
|
my ($controller, @args) = @_; |
39
|
0
|
|
|
|
|
0
|
my $input; |
40
|
0
|
0
|
0
|
|
|
0
|
if (@args && ref($args[-1]) eq 'HASH') { |
41
|
0
|
|
|
|
|
0
|
$input = pop(@args); |
42
|
|
|
|
|
|
|
} |
43
|
0
|
|
|
|
|
0
|
my $c = ${$p."::Controllers::C"}{$controller}; |
|
0
|
|
|
|
|
0
|
|
44
|
0
|
0
|
|
|
|
0
|
croak "$controller controller not found in '\%$p\::Controllers::C" unless $c; |
45
|
0
|
|
|
|
|
0
|
my $arity = @args; |
46
|
0
|
|
|
0
|
|
0
|
my $path = first { my @m = /\(.*?\)/g; $arity == @m } @{$c->urls}; |
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
47
|
0
|
0
|
|
|
|
0
|
croak "couldn't find a matching URL path" unless $path; |
48
|
0
|
|
|
|
|
0
|
while ($path =~ /\(.*?\)/) { |
49
|
0
|
|
|
|
|
0
|
$path =~ s{\(.*?\)}{uri_escape(+shift(@args), "^A-Za-z0-9\-_.!~*’()/")}e; |
|
0
|
|
|
|
|
0
|
|
50
|
|
|
|
|
|
|
} |
51
|
0
|
0
|
|
|
|
0
|
if ($input) { |
52
|
0
|
|
|
|
|
0
|
$path .= "?". join('&' => |
53
|
|
|
|
|
|
|
map { |
54
|
0
|
|
|
|
|
0
|
my $k = $_; |
55
|
0
|
|
|
|
|
0
|
ref($input->{$_}) eq 'ARRAY' |
56
|
0
|
0
|
|
|
|
0
|
? map { "$k=".uri_escape($_) } @{$input->{$_}} |
|
0
|
|
|
|
|
0
|
|
57
|
|
|
|
|
|
|
: "$_=".uri_escape($input->{$_}) |
58
|
|
|
|
|
|
|
} keys %$input); |
59
|
|
|
|
|
|
|
} |
60
|
0
|
|
|
|
|
0
|
$path; |
61
|
3
|
|
|
|
|
26
|
}; |
62
|
|
|
|
|
|
|
|
63
|
|
|
|
|
|
|
# ($controller, \@regex_captures) = D($path) # Return controller and captures for a path |
64
|
3
|
|
|
|
|
19
|
*{$p."::D"} = sub { |
65
|
0
|
|
|
0
|
|
0
|
my $url = uri_unescape($_[0]); |
66
|
0
|
|
|
|
|
0
|
my $C = \@{$p.'::Controllers::C'}; |
|
0
|
|
|
|
|
0
|
|
67
|
0
|
|
|
|
|
0
|
my ($c, @regex_captures); |
68
|
0
|
|
|
|
|
0
|
for $c (@$C) { |
69
|
0
|
|
|
|
|
0
|
for (@{$c->urls}) { |
|
0
|
|
|
|
|
0
|
|
70
|
0
|
0
|
|
|
|
0
|
if (@regex_captures = ($url =~ qr{^$_$})) { |
71
|
0
|
0
|
|
|
|
0
|
pop @regex_captures if ($#+ == 0); |
72
|
0
|
|
|
|
|
0
|
return ($c, \@regex_captures); |
73
|
|
|
|
|
|
|
} |
74
|
|
|
|
|
|
|
} |
75
|
|
|
|
|
|
|
} |
76
|
0
|
|
|
|
|
0
|
($Squatting::Controller::r404, []); |
77
|
3
|
|
|
|
|
19
|
}; |
78
|
|
|
|
|
|
|
|
79
|
3
|
|
|
|
|
16
|
*{$p."::Controllers::C"} = sub { |
80
|
1
|
|
|
1
|
|
26
|
Squatting::Controller->new(@_, app => $p) |
81
|
3
|
|
|
|
|
13
|
}; |
82
|
3
|
|
|
|
|
2535
|
*{$p."::Views::V"} = sub { |
83
|
1
|
|
|
1
|
|
30
|
Squatting::View->new(@_) |
84
|
3
|
|
|
|
|
16
|
}; |
85
|
|
|
|
|
|
|
|
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
# Squatting plugins may be anywhere in Squatting::*::* but by convention |
89
|
|
|
|
|
|
|
# (and for fun) you should use poetic diction in your package names. |
90
|
|
|
|
|
|
|
# |
91
|
|
|
|
|
|
|
# Squatting::On::Continuity |
92
|
|
|
|
|
|
|
# Squatting::On::Catalyst |
93
|
|
|
|
|
|
|
# Squatting::On::CGI |
94
|
|
|
|
|
|
|
# Squatting::On::Jifty |
95
|
|
|
|
|
|
|
# |
96
|
|
|
|
|
|
|
# (ALL YOUR FRAMEWORK ARE BELONG TO US) |
97
|
|
|
|
|
|
|
# |
98
|
|
|
|
|
|
|
# Squatting::With::Impunity (What could we do w/ this name?) |
99
|
|
|
|
|
|
|
# Squatting::With::Log4Perl (which is how we could add logging support) |
100
|
|
|
|
|
|
|
# |
101
|
|
|
|
|
|
|
# (etc) |
102
|
0
|
|
|
0
|
0
|
|
sub component_base_class { __PACKAGE__ } |
103
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
# 1 |
105
|
|
|
|
|
|
|
# App->mount($AnotherApp, $prefix) # Map another app on to a URL $prefix. |
106
|
|
|
|
|
|
|
sub mount { |
107
|
0
|
|
|
0
|
1
|
|
my ($app, $other, $prefix) = @_; |
108
|
0
|
|
|
|
|
|
push @{$app."::O"}, $other; |
|
0
|
|
|
|
|
|
|
109
|
0
|
|
|
|
|
|
push @{$app."::Controllers::C"}, map { |
|
0
|
|
|
|
|
|
|
110
|
0
|
|
|
|
|
|
my $urls = $_->urls; |
111
|
0
|
|
|
|
|
|
$_->urls = [ map { $prefix.$_ } @$urls ]; |
|
0
|
|
|
|
|
|
|
112
|
0
|
|
|
|
|
|
$_; |
113
|
0
|
|
|
|
|
|
} @{$other."::Controllers::C"} |
114
|
|
|
|
|
|
|
} |
115
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
# 2 |
117
|
|
|
|
|
|
|
# App->relocate($prefix) # Map main app to a URL $prefix |
118
|
|
|
|
|
|
|
sub relocate { |
119
|
0
|
|
|
0
|
1
|
|
my ($app, $prefix) = @_; |
120
|
0
|
|
|
|
|
|
for (@{$app."::Controllers::C"}) { |
|
0
|
|
|
|
|
|
|
121
|
0
|
|
|
|
|
|
my $urls = $_->urls; |
122
|
0
|
|
|
|
|
|
$_->urls = [ map { $prefix.$_ } @$urls ]; |
|
0
|
|
|
|
|
|
|
123
|
|
|
|
|
|
|
} |
124
|
0
|
|
|
|
|
|
${$app."::CONFIG"}{relocated} = $prefix; |
|
0
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
} |
126
|
|
|
|
|
|
|
|
127
|
|
|
|
|
|
|
# 3 |
128
|
|
|
|
|
|
|
# App->init # Initialize $app |
129
|
|
|
|
|
|
|
sub init { |
130
|
0
|
|
|
0
|
1
|
|
$_->init for (@{$_[0]."::O"}); |
|
0
|
|
|
|
|
|
|
131
|
0
|
|
|
|
|
|
%{$_[0]."::Controllers::C"} = map { $_->name => $_ } @{$_[0]."::Controllers::C"}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
132
|
0
|
|
|
|
|
|
%{$_[0]."::Views::V"} = map { $_->name => $_ } @{$_[0]."::Views::V"}; |
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
# App->service($controller, @args) # Handle an HTTP request |
136
|
|
|
|
|
|
|
sub service { |
137
|
0
|
|
|
0
|
1
|
|
my ($app, $c, @args) = grep { defined } @_; |
|
0
|
|
|
|
|
|
|
138
|
0
|
|
|
|
|
|
my $method = lc $c->env->{REQUEST_METHOD}; |
139
|
0
|
|
|
|
|
|
my $content; |
140
|
|
|
|
|
|
|
|
141
|
0
|
|
|
|
|
|
eval { $content = $c->$method(@args) }; |
|
0
|
|
|
|
|
|
|
142
|
0
|
0
|
|
|
|
|
die $@ if (ref($@) =~ /^HTTP::Exception/); # Pass HTTP::Exceptions on up |
143
|
0
|
0
|
|
|
|
|
warn "EXCEPTION: $@" if ($@); |
144
|
|
|
|
|
|
|
|
145
|
0
|
|
|
|
|
|
my $cookies = $c->cookies; |
146
|
0
|
|
|
|
|
|
$c->headers->{'Set-Cookie'} = join("; ", |
147
|
0
|
|
|
|
|
|
map { CGI::Cookie->new( -name => $_, %{$cookies->{$_}} ) } |
|
0
|
|
|
|
|
|
|
148
|
0
|
0
|
|
|
|
|
grep { ref $cookies->{$_} eq 'HASH' } |
149
|
|
|
|
|
|
|
keys %$cookies) if (%$cookies); |
150
|
|
|
|
|
|
|
|
151
|
0
|
|
|
|
|
|
$content; |
152
|
|
|
|
|
|
|
} |
153
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
1; |
155
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
=head1 NAME |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
Squatting - A Camping-inspired Web Microframework for Perl |
159
|
|
|
|
|
|
|
|
160
|
|
|
|
|
|
|
=head1 SYNOPSIS |
161
|
|
|
|
|
|
|
|
162
|
|
|
|
|
|
|
Running an App: |
163
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
$ squatting App |
165
|
|
|
|
|
|
|
Please contact me at: http://localhost:4234/ |
166
|
|
|
|
|
|
|
|
167
|
|
|
|
|
|
|
Check out our ASCII art logo: |
168
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
$ squatting --logo |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
What a basic App looks like: |
172
|
|
|
|
|
|
|
|
173
|
|
|
|
|
|
|
# STEP 1 => Use Squatting for your App |
174
|
|
|
|
|
|
|
{ |
175
|
|
|
|
|
|
|
package App; # <-- I hope it's obvious that this name can whatever you want. |
176
|
|
|
|
|
|
|
use Squatting; |
177
|
|
|
|
|
|
|
our %CONFIG; # <-- standard app config goes here |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
# STEP 2 => Define the App's Controllers |
181
|
|
|
|
|
|
|
{ |
182
|
|
|
|
|
|
|
package App::Controllers; |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# Setup a list of controller objects in @C using the C() function. |
185
|
|
|
|
|
|
|
our @C = ( |
186
|
|
|
|
|
|
|
C( |
187
|
|
|
|
|
|
|
Home => [ '/' ], |
188
|
|
|
|
|
|
|
get => sub { |
189
|
|
|
|
|
|
|
my ($self) = @_; |
190
|
|
|
|
|
|
|
my $v = $self->v; |
191
|
|
|
|
|
|
|
$v->{title} = 'A Simple Squatting Application'; |
192
|
|
|
|
|
|
|
$v->{message} = 'Hello, World!'; |
193
|
|
|
|
|
|
|
$self->render('home'); |
194
|
|
|
|
|
|
|
}, |
195
|
|
|
|
|
|
|
post => sub { } |
196
|
|
|
|
|
|
|
), |
197
|
|
|
|
|
|
|
); |
198
|
|
|
|
|
|
|
} |
199
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# STEP 3 => Define the App's Views |
201
|
|
|
|
|
|
|
{ |
202
|
|
|
|
|
|
|
package App::Views; |
203
|
|
|
|
|
|
|
|
204
|
|
|
|
|
|
|
# Setup a list of view objects in @V using the V() function. |
205
|
|
|
|
|
|
|
our @V = ( |
206
|
|
|
|
|
|
|
V( |
207
|
|
|
|
|
|
|
'html', |
208
|
|
|
|
|
|
|
layout => sub { |
209
|
|
|
|
|
|
|
my ($self, $v, $content) = @_; |
210
|
|
|
|
|
|
|
"$v->{title}". |
211
|
|
|
|
|
|
|
"$content"; |
212
|
|
|
|
|
|
|
}, |
213
|
|
|
|
|
|
|
home => sub { |
214
|
|
|
|
|
|
|
my ($self, $v) = @_; |
215
|
|
|
|
|
|
|
"$v->{message}" |
216
|
|
|
|
|
|
|
}, |
217
|
|
|
|
|
|
|
), |
218
|
|
|
|
|
|
|
); |
219
|
|
|
|
|
|
|
} |
220
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# Models? |
222
|
|
|
|
|
|
|
# - The whole world is your model. ;-) |
223
|
|
|
|
|
|
|
# - I have no interest in defining policy here. |
224
|
|
|
|
|
|
|
# - Use whatever works for you. |
225
|
|
|
|
|
|
|
|
226
|
|
|
|
|
|
|
=head1 DESCRIPTION |
227
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
Squatting is a web microframework based on Camping. |
229
|
|
|
|
|
|
|
It originally used L as its foundation, |
230
|
|
|
|
|
|
|
but it has since been generalized such that it can |
231
|
|
|
|
|
|
|
squat on top of any Perl-based web framework (in theory). |
232
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
=head2 What does this mean? |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
=over 4 |
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
=item B |
238
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
_why did a really good job designing Camping's API so that you could get the |
240
|
|
|
|
|
|
|
B done with the B amount of code possible. I loved Camping's API |
241
|
|
|
|
|
|
|
so much that I ported it to Perl. |
242
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
=item B |
244
|
|
|
|
|
|
|
|
245
|
|
|
|
|
|
|
The core of Squatting (which includes Squatting, Squatting::Controller, and |
246
|
|
|
|
|
|
|
Squatting::View) can be squished into less than 4K of obfuscated perl. Also, |
247
|
|
|
|
|
|
|
the number of Perl module dependencies has been kept down to a minimum. |
248
|
|
|
|
|
|
|
|
249
|
|
|
|
|
|
|
=item B |
250
|
|
|
|
|
|
|
|
251
|
|
|
|
|
|
|
Controllers are objects (not classes) that are made to look like HTTP |
252
|
|
|
|
|
|
|
resources. Thus, they respond to methods like get(), post(), put(), and |
253
|
|
|
|
|
|
|
delete(). |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
=item B |
256
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
Stateful continuation-based code can be surprisingly useful (especially for |
258
|
|
|
|
|
|
|
COMET), so we try to make RESTless controllers easy to express as well. (B<*>) |
259
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
=item B |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
Views are also objects (not classes) whose methods represent templates to be |
263
|
|
|
|
|
|
|
rendered. An app can also have more than one view. Changing a Squatting app's |
264
|
|
|
|
|
|
|
look and feel can be as simple as swapping out one view object for another. |
265
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
=item B |
267
|
|
|
|
|
|
|
|
268
|
|
|
|
|
|
|
You can take multiple Squatting apps and compose them into a single app. For |
269
|
|
|
|
|
|
|
example, suppose you built a site and decided that you'd like to add a forum. |
270
|
|
|
|
|
|
|
You could take a hypothetical forum app written in Squatting and just mount |
271
|
|
|
|
|
|
|
it at an arbitrary path like /forum. |
272
|
|
|
|
|
|
|
|
273
|
|
|
|
|
|
|
=item B |
274
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
Already using another framework? No problem. You should be able to embed |
276
|
|
|
|
|
|
|
Squatting apps into apps written in anything from CGI on up to Catalyst. |
277
|
|
|
|
|
|
|
B |
278
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
=item B |
280
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
You may use any templating system you want, and you may use any ORM you |
282
|
|
|
|
|
|
|
want. We only have a few rules on how the controller code and the view code |
283
|
|
|
|
|
|
|
should be organized, but beyond that, you are free as you want to be. |
284
|
|
|
|
|
|
|
|
285
|
|
|
|
|
|
|
=back |
286
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
B<*> RESTless controllers currently only work when you're L. |
288
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
=head1 API |
290
|
|
|
|
|
|
|
|
291
|
|
|
|
|
|
|
=head2 Use as a Base Class for Squatting Applications |
292
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
package App; |
294
|
|
|
|
|
|
|
use Squatting; |
295
|
|
|
|
|
|
|
our %CONFIG = (); |
296
|
|
|
|
|
|
|
1; |
297
|
|
|
|
|
|
|
|
298
|
|
|
|
|
|
|
Just C |
299
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
=over 4 |
301
|
|
|
|
|
|
|
|
302
|
|
|
|
|
|
|
=item App becomes a subclass of Squatting. |
303
|
|
|
|
|
|
|
|
304
|
|
|
|
|
|
|
=item App::Controllers is given this app's R() and C() functions. |
305
|
|
|
|
|
|
|
|
306
|
|
|
|
|
|
|
=item App::Views is given this app's R() and V() functions. |
307
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
=back |
309
|
|
|
|
|
|
|
|
310
|
|
|
|
|
|
|
=head3 App->service($controller, @args) |
311
|
|
|
|
|
|
|
|
312
|
|
|
|
|
|
|
Every time an HTTP request comes in, this method is called with a controller |
313
|
|
|
|
|
|
|
object and a list of arguments. The controller will then be invoked with the |
314
|
|
|
|
|
|
|
HTTP method that was requested (like GET or POST), and it will return the |
315
|
|
|
|
|
|
|
content of the response as a string. |
316
|
|
|
|
|
|
|
|
317
|
|
|
|
|
|
|
B: If you want to do anything before, after, or around an HTTP request, |
318
|
|
|
|
|
|
|
this is the method you should override in your subclass. |
319
|
|
|
|
|
|
|
|
320
|
|
|
|
|
|
|
=head3 App->init |
321
|
|
|
|
|
|
|
|
322
|
|
|
|
|
|
|
This method takes no parameters and initializes some internal variables. |
323
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
B: You can override this method if you want to do more things when |
325
|
|
|
|
|
|
|
the App is initialized. |
326
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
=head3 App->mount($AnotherApp => $prefix) |
328
|
|
|
|
|
|
|
|
329
|
|
|
|
|
|
|
XXX - The C has been moved out of the core and into |
330
|
|
|
|
|
|
|
L. Furthermore, Squatting::With::Mount has |
331
|
|
|
|
|
|
|
been implemented using L. |
332
|
|
|
|
|
|
|
|
333
|
|
|
|
|
|
|
This method will mount another Squatting app at the specified prefix. |
334
|
|
|
|
|
|
|
|
335
|
|
|
|
|
|
|
App->mount('My::Blog' => '/my/ridiculous/rantings'); |
336
|
|
|
|
|
|
|
App->mount('Forum' => '/forum'); |
337
|
|
|
|
|
|
|
App->mount('ChatterBox' => '/chat'); |
338
|
|
|
|
|
|
|
|
339
|
|
|
|
|
|
|
B: You can only mount an app once. Don't try to mount it again |
340
|
|
|
|
|
|
|
at some other prefix, because it won't work. This is a consequence |
341
|
|
|
|
|
|
|
of storing so much information in package variables and a strong argument |
342
|
|
|
|
|
|
|
for going all objects all the time. |
343
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
=head3 App->relocate($prefix) |
345
|
|
|
|
|
|
|
|
346
|
|
|
|
|
|
|
This method will relocate a Squatting app to the specified prefix. It's useful |
347
|
|
|
|
|
|
|
for embedding a Squatting app into apps written in other frameworks. |
348
|
|
|
|
|
|
|
|
349
|
|
|
|
|
|
|
This also has a side-effect of setting C<$CONFIG{relocated}> to C<$prefix>. |
350
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
=head2 Use as a Helper for Controllers |
352
|
|
|
|
|
|
|
|
353
|
|
|
|
|
|
|
In this package, you will define a list of L objects in C<@C>. |
354
|
|
|
|
|
|
|
|
355
|
|
|
|
|
|
|
package App::Controllers; |
356
|
|
|
|
|
|
|
use Squatting ':controllers'; |
357
|
|
|
|
|
|
|
our @C = ( |
358
|
|
|
|
|
|
|
C(...), |
359
|
|
|
|
|
|
|
C(...), |
360
|
|
|
|
|
|
|
C(...), |
361
|
|
|
|
|
|
|
); |
362
|
|
|
|
|
|
|
|
363
|
|
|
|
|
|
|
=head3 C($name => \@urls, %methods) |
364
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
This is a shortcut for: |
366
|
|
|
|
|
|
|
|
367
|
|
|
|
|
|
|
Squatting::Controller->new( |
368
|
|
|
|
|
|
|
$name => \@urls, |
369
|
|
|
|
|
|
|
app => $App, |
370
|
|
|
|
|
|
|
%methods |
371
|
|
|
|
|
|
|
); |
372
|
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
=head3 R($name, @args, [ \%params ]) |
374
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
R() is a URL generation function that takes a controller name and a list of |
376
|
|
|
|
|
|
|
arguments. You may also pass in a hashref representing CGI variables as the |
377
|
|
|
|
|
|
|
very last parameter to this function. |
378
|
|
|
|
|
|
|
|
379
|
|
|
|
|
|
|
B: Given the following controllers, R() would respond like this. |
380
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
# Example Controllers |
382
|
|
|
|
|
|
|
C(Home => [ '/' ]); |
383
|
|
|
|
|
|
|
C(Profile => [ '/~(\w+)', '/~(\w+)\.(\w+)' ]); |
384
|
|
|
|
|
|
|
|
385
|
|
|
|
|
|
|
# Generated URLs |
386
|
|
|
|
|
|
|
R('Home') # "/" |
387
|
|
|
|
|
|
|
R('Home', { foo => 1, bar => 2}) # "/?foo=1&bar=2" |
388
|
|
|
|
|
|
|
R('Profile', 'larry') # "/~larry" |
389
|
|
|
|
|
|
|
R('Profile', 'larry', 'json') # "/~larry.json" |
390
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
As you can see, C<@args> represents the regexp captures, and C<\%params> |
392
|
|
|
|
|
|
|
represents the CGI query parameters. |
393
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
=head2 Use as a Helper for Views |
395
|
|
|
|
|
|
|
|
396
|
|
|
|
|
|
|
In this package, you will define a list of L objects in C<@V>. |
397
|
|
|
|
|
|
|
|
398
|
|
|
|
|
|
|
package App::Views; |
399
|
|
|
|
|
|
|
use Squatting ':views'; |
400
|
|
|
|
|
|
|
our @V = ( |
401
|
|
|
|
|
|
|
V( |
402
|
|
|
|
|
|
|
'html', |
403
|
|
|
|
|
|
|
home => sub { "Home" }, |
404
|
|
|
|
|
|
|
), |
405
|
|
|
|
|
|
|
); |
406
|
|
|
|
|
|
|
|
407
|
|
|
|
|
|
|
=head3 V($name, %methods) |
408
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
This is a shortcut for: |
410
|
|
|
|
|
|
|
|
411
|
|
|
|
|
|
|
Squatting::View->new($name, %methods); |
412
|
|
|
|
|
|
|
|
413
|
|
|
|
|
|
|
=head3 R($name, @args, [ \%params ]) |
414
|
|
|
|
|
|
|
|
415
|
|
|
|
|
|
|
This is the same R() function that the controllers get access to. |
416
|
|
|
|
|
|
|
Please use it to generate URLs so that your apps may be composable |
417
|
|
|
|
|
|
|
and embeddable. |
418
|
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
=head1 SEE ALSO |
420
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
=over 4 |
422
|
|
|
|
|
|
|
|
423
|
|
|
|
|
|
|
=item B: |
424
|
|
|
|
|
|
|
|
425
|
|
|
|
|
|
|
L, L, |
426
|
|
|
|
|
|
|
L, L, |
427
|
|
|
|
|
|
|
L, L, L, |
428
|
|
|
|
|
|
|
L, L, |
429
|
|
|
|
|
|
|
L, L, |
430
|
|
|
|
|
|
|
L |
431
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
L, |
433
|
|
|
|
|
|
|
L, |
434
|
|
|
|
|
|
|
L |
435
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
=item B: |
437
|
|
|
|
|
|
|
|
438
|
|
|
|
|
|
|
L |
439
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
=item B: |
441
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
L - a nice way to browse through the POD for your locally |
443
|
|
|
|
|
|
|
installed perl modules. |
444
|
|
|
|
|
|
|
|
445
|
|
|
|
|
|
|
L - a simple COMET server. (DEPRECATED. Use Web::Hippie or Plack::Middleware::SocketIO instead.) |
446
|
|
|
|
|
|
|
|
447
|
|
|
|
|
|
|
L - a simple CPAN-friendly blogging system for Perl. |
448
|
|
|
|
|
|
|
|
449
|
|
|
|
|
|
|
=back |
450
|
|
|
|
|
|
|
|
451
|
|
|
|
|
|
|
=head2 Google Group: squatting-framework |
452
|
|
|
|
|
|
|
|
453
|
|
|
|
|
|
|
A Google Group has been setup so that people can discuss Squatting. |
454
|
|
|
|
|
|
|
If you have questions about the framework, this is the place to ask. |
455
|
|
|
|
|
|
|
|
456
|
|
|
|
|
|
|
L |
457
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
=head2 Squatting Source Code |
459
|
|
|
|
|
|
|
|
460
|
|
|
|
|
|
|
The source code is short and it has some useful comments in it, so this might |
461
|
|
|
|
|
|
|
be all you need to get going. There are also some examples in the F |
462
|
|
|
|
|
|
|
directory. |
463
|
|
|
|
|
|
|
|
464
|
|
|
|
|
|
|
L |
465
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
=head2 Bavl Source Code |
467
|
|
|
|
|
|
|
|
468
|
|
|
|
|
|
|
We're going to throw Squatting (and Continuity) into the metaphorical deep end |
469
|
|
|
|
|
|
|
by using it to implement the L. It's a site that |
470
|
|
|
|
|
|
|
will help people learn foreign languages by letting you hear the phrases you're |
471
|
|
|
|
|
|
|
interested in learning as actually spoken by fluent speakers. If you're |
472
|
|
|
|
|
|
|
looking for an example of how to use Squatting for an ambitious project, look |
473
|
|
|
|
|
|
|
at the Bavl code. |
474
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
L |
476
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
=head2 Continuity and Coro |
478
|
|
|
|
|
|
|
|
479
|
|
|
|
|
|
|
When you want to start dabbling with RESTless controllers, it would serve you |
480
|
|
|
|
|
|
|
well to understand how Continuity, Coro and Event work. To learn more, I |
481
|
|
|
|
|
|
|
recommend reading the POD for the following Perl modules: |
482
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
L, |
484
|
|
|
|
|
|
|
L, |
485
|
|
|
|
|
|
|
L. |
486
|
|
|
|
|
|
|
|
487
|
|
|
|
|
|
|
Combining coroutines with an event loop is a surprisingly powerful technique. |
488
|
|
|
|
|
|
|
|
489
|
|
|
|
|
|
|
=head2 Camping |
490
|
|
|
|
|
|
|
|
491
|
|
|
|
|
|
|
Squatting is descended from Camping, so studying the Camping API |
492
|
|
|
|
|
|
|
will indirectly teach you much of the Squatting API. |
493
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
L |
495
|
|
|
|
|
|
|
|
496
|
|
|
|
|
|
|
=head2 Prototype-based OO |
497
|
|
|
|
|
|
|
|
498
|
|
|
|
|
|
|
There were a lot of obscure Ruby idioms in Camping that were damn near |
499
|
|
|
|
|
|
|
impossible to directly translate into Perl. I got around this by resorting to |
500
|
|
|
|
|
|
|
techniques that are reminiscent of prototype-based OO. (That's why controllers |
501
|
|
|
|
|
|
|
and views are objects instead of classes.) |
502
|
|
|
|
|
|
|
|
503
|
|
|
|
|
|
|
=head3 Prototypes == Grand Unified Theory of Objects |
504
|
|
|
|
|
|
|
|
505
|
|
|
|
|
|
|
I've been coding a lot of JavaScript these days, and it has definitely |
506
|
|
|
|
|
|
|
influenced my programming style. I've come to love the simplicity of |
507
|
|
|
|
|
|
|
prototype-based OO, and I think it's a damned shame that they're introducing |
508
|
|
|
|
|
|
|
concepts like 'class' in the next version of JavaScript. It's like they missed |
509
|
|
|
|
|
|
|
the point of prototype-based OO. |
510
|
|
|
|
|
|
|
|
511
|
|
|
|
|
|
|
If you're going to add anything to JavaScript, make the prototype side of it |
512
|
|
|
|
|
|
|
stronger. Look to languages like Io, and make it easier to clone objects and |
513
|
|
|
|
|
|
|
manipulate an object's prototype chain. The beauty of prototypes is that you |
514
|
|
|
|
|
|
|
can combine it with slot-based objects to unify the functionality of objects, |
515
|
|
|
|
|
|
|
classes, and namespaces into a surprisingly simple and coherent system. Look |
516
|
|
|
|
|
|
|
at Io if you don't believe me. |
517
|
|
|
|
|
|
|
|
518
|
|
|
|
|
|
|
L |
519
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
=head1 AUTHOR |
521
|
|
|
|
|
|
|
|
522
|
|
|
|
|
|
|
John BEPPU Ebeppu@cpan.orgE |
523
|
|
|
|
|
|
|
|
524
|
|
|
|
|
|
|
Scott WALTERS (aka scrottie) gets credit for the name of this module. |
525
|
|
|
|
|
|
|
|
526
|
|
|
|
|
|
|
=head1 COPYRIGHT |
527
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
Copyright (c) 2008-9 John BEPPU Ebeppu@cpan.orgE. |
529
|
|
|
|
|
|
|
|
530
|
|
|
|
|
|
|
=head2 The "MIT" License |
531
|
|
|
|
|
|
|
|
532
|
|
|
|
|
|
|
Permission is hereby granted, free of charge, to any person |
533
|
|
|
|
|
|
|
obtaining a copy of this software and associated documentation |
534
|
|
|
|
|
|
|
files (the "Software"), to deal in the Software without |
535
|
|
|
|
|
|
|
restriction, including without limitation the rights to use, |
536
|
|
|
|
|
|
|
copy, modify, merge, publish, distribute, sublicense, and/or sell |
537
|
|
|
|
|
|
|
copies of the Software, and to permit persons to whom the |
538
|
|
|
|
|
|
|
Software is furnished to do so, subject to the following |
539
|
|
|
|
|
|
|
conditions: |
540
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
The above copyright notice and this permission notice shall be |
542
|
|
|
|
|
|
|
included in all copies or substantial portions of the Software. |
543
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
545
|
|
|
|
|
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES |
546
|
|
|
|
|
|
|
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
547
|
|
|
|
|
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
548
|
|
|
|
|
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
549
|
|
|
|
|
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING |
550
|
|
|
|
|
|
|
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
551
|
|
|
|
|
|
|
OTHER DEALINGS IN THE SOFTWARE. |
552
|
|
|
|
|
|
|
|
553
|
|
|
|
|
|
|
=cut |
554
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
# Local Variables: *** |
556
|
|
|
|
|
|
|
# mode: cperl *** |
557
|
|
|
|
|
|
|
# indent-tabs-mode: nil *** |
558
|
|
|
|
|
|
|
# cperl-close-paren-offset: -2 *** |
559
|
|
|
|
|
|
|
# cperl-continued-statement-offset: 2 *** |
560
|
|
|
|
|
|
|
# cperl-indent-level: 2 *** |
561
|
|
|
|
|
|
|
# cperl-indent-parens-as-block: t *** |
562
|
|
|
|
|
|
|
# cperl-tab-always-indent: nil *** |
563
|
|
|
|
|
|
|
# End: *** |
564
|
|
|
|
|
|
|
# vim:tabstop=8 softtabstop=2 shiftwidth=2 shiftround expandtab |