File Coverage

blib/lib/Squatting.pm
Criterion Covered Total %
statement 38 116 32.7
branch 1 22 4.5
condition 0 3 0.0
subroutine 10 18 55.5
pod 4 5 80.0
total 53 164 32.3


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 Cing Squatting makes a lot of magic happen. In the example above:
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