1NAME
2 Web::Simple - A quick and easy way to build simple web applications
3
4SYNOPSIS
5 #!/usr/bin/env perl
6
7 package HelloWorld;
8 use Web::Simple;
9
10 sub dispatch_request {
11 GET => sub {
12 [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
13 },
14 '' => sub {
15 [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
16 }
17 }
18
19 HelloWorld->run_if_script;
20
21 If you save this file into your cgi-bin as "hello-world.cgi" and then
22 visit:
23
24 http://my.server.name/cgi-bin/hello-world.cgi/
25
26 you'll get the "Hello world!" string output to your browser. At the same
27 time this file will also act as a class module, so you can save it as
28 HelloWorld.pm and use it as-is in test scripts or other deployment
29 mechanisms.
30
31 Note that you should retain the ->run_if_script even if your app is a
32 module, since this additionally makes it valid as a .psgi file, which
33 can be extremely useful during development.
34
35 For more complex examples and non-CGI deployment, see
36 Web::Simple::Deployment. To get help with Web::Simple, please connect to
37 the irc.perl.org IRC network and join #web-simple.
38
39DESCRIPTION
40 The philosophy of Web::Simple is to keep to an absolute bare minimum for
41 everything. It is not designed to be used for large scale applications;
42 the Catalyst web framework already works very nicely for that and is a
43 far more mature, well supported piece of software.
44
45 However, if you have an application that only does a couple of things,
46 and want to not have to think about complexities of deployment, then
47 Web::Simple might be just the thing for you.
48
49 The only public interface the Web::Simple module itself provides is an
50 "import" based one:
51
52 use Web::Simple 'NameOfApplication';
53
54 This sets up your package (in this case "NameOfApplication" is your
55 package) so that it inherits from Web::Simple::Application and imports
56 strictures, as well as installs a "PSGI_ENV" constant for convenience,
57 as well as some other subroutines.
58
59 Importing strictures will automatically make your code use the "strict"
60 and "warnings" pragma, so you can skip the usual:
61
62 use strict;
63 use warnings FATAL => 'all';
64
65 provided you 'use Web::Simple' at the top of the file. Note that we turn
66 on *fatal* warnings so if you have any warnings at any point from the
67 file that you did 'use Web::Simple' in, then your application will die.
68 This is, so far, considered a feature.
69
70 When we inherit from Web::Simple::Application we also use Moo, which is
71 the the equivalent of:
72
73 {
74 package NameOfApplication;
75 use Moo;
76 extends 'Web::Simple::Application';
77 }
78
79 So you can use Moo features in your application, such as creating
80 attributes using the "has" subroutine, etc. Please see the documentation
81 for Moo for more information.
82
83 It also exports the following subroutines for use in dispatchers:
84
85 response_filter { ... };
86
87 redispatch_to '/somewhere';
88
89 Finally, import sets
90
91 $INC{"NameOfApplication.pm"} = 'Set by "use Web::Simple;" invocation';
92
93 so that perl will not attempt to load the application again even if
94
95 require NameOfApplication;
96
97 is encountered in other code.
98
99 One important thing to remember when using
100
101 NameOfApplication->run_if_script;
102
103 At the end of your app is that this call will create an instance of your
104 app for you automatically, regardless of context. An easier way to think
105 of this would be if the method were more verbosely named
106
107 NameOfApplication->run_request_if_script_else_turn_coderef_for_psgi;
108
109DISPATCH STRATEGY
110 Web::Simple despite being straightforward to use, has a powerful system
111 for matching all sorts of incoming URLs to one or more subroutines.
112 These subroutines can be simple actions to take for a given URL, or
113 something more complicated, including entire Plack applications,
114 Plack::Middleware and nested subdispatchers.
115
116 Examples
117 sub dispatch_request {
118 (
119 # matches: GET /user/1.htm?show_details=1
120 # GET /user/1.htm
121 'GET + /user/* + ?show_details~ + .htm|.html|.xhtml' => sub {
122 my ($self, $user_id, $show_details) = @_;
123 ...
124 },
125 # matches: POST /user?username=frew
126 # POST /user?username=mst&first_name=matt&last_name=trout
127 'POST + /user + ?username=&*' => sub {
128 my ($self, $username, $misc_params) = @_;
129 ...
130 },
131 # matches: DELETE /user/1/friend/2
132 'DELETE + /user/*/friend/*' => sub {
133 my ($self, $user_id, $friend_id) = @_;
134 ...
135 },
136 # matches: PUT /user/1?first_name=Matt&last_name=Trout
137 'PUT + /user/* + ?first_name~&last_name~' => sub {
138 my ($self, $user_id, $first_name, $last_name) = @_;
139 ...
140 },
141 '/user/*/...' => sub {
142 my $user_id = $_[1];
143 (
144 # matches: PUT /user/1/role/1
145 'PUT + /role/*' => sub {
146 my $role_id = $_[1];
147 ...
148 },
149 # matches: DELETE /user/1/role/1
150 'DELETE + /role/*' => sub {
151 my $role_id = $_[1];
152 ...
153 },
154 );
155 },
156 );
157 }
158
159 The dispatch cycle
160 At the beginning of a request, your app's dispatch_request method is
161 called with the PSGI $env as an argument. You can handle the request
162 entirely in here and return a PSGI response arrayref if you want:
163
164 sub dispatch_request {
165 my ($self, $env) = @_;
166 [ 404, [ 'Content-type' => 'text/plain' ], [ 'Amnesia == fail' ] ]
167 }
168
169 However, generally, instead of that, you return a set of route/target
170 pairs:
171
172 sub dispatch_request {
173 my $self = shift;
174 (
175 '/' => sub { redispatch_to '/index.html' },
176 '/user/*' => sub { $self->show_user($_[1]) },
177 'POST + %*' => 'handle_post',
178 ...
179 );
180 }
181
182 Well, a sub is a valid PSGI response too (for ultimate streaming and
183 async cleverness). If you want to return a PSGI sub you have to wrap it
184 into an array ref.
185
186 sub dispatch_request {
187 [ sub {
188 my $respond = shift;
189 # This is pure PSGI here, so read perldoc PSGI
190 } ]
191 }
192
193 If you return a string followed by a subroutine or method name, the
194 string is treated as a match specification - and if the test is passed,
195 the subroutine is called as a method and passed any matched arguments
196 (see below for more details).
197
198 You can also return a plain subroutine which will be called with just
199 $env - remember that in this case if you need $self you must close over
200 it.
201
202 If you return a normal object, Web::Simple will simply return it upwards
203 on the assumption that a response_filter (or some arbitrary
204 Plack::Middleware) somewhere will convert it to something useful. This
205 allows:
206
207 sub dispatch_request {
208 my $self = shift;
209 (
210 '.html' => sub { response_filter { $self->render_zoom($_[0]) } },
211 '/user/*' => sub { $self->users->get($_[1]) },
212 );
213 }
214
215 An alternative to using string + suborutine to declare a route is to use
216 the sub prototype -
217
218 sub dispatch_request {
219 my $self = shift;
220 (
221 sub (.html) { response_filter { $self->render_zoom($_[0]) } },
222 sub (/user/) { $self->users->get($_[1]) },
223 $self->can('handle_post'), # if declared as 'sub handle_post (...) {'
224 )
225 }
226
227 This can be useful sugar, especially if you want to keep method-based
228 dispatchers' route specifications on the methods.
229
230 to render a user object to HTML, if there is an incoming URL such as:
231
232 http://myweb.org/user/111.html
233
234 This works because as we descend down the dispachers, we first match
235 "sub (.html)", which adds a "response_filter" (basically a specialized
236 routine that follows the Plack::Middleware specification), and then
237 later we also match "sub (/user/*)" which gets a user and returns that
238 as the response. This user object 'bubbles up' through all the wrapping
239 middleware until it hits the "response_filter" we defined, after which
240 the return is converted to a true html response.
241
242 However, two types of objects are treated specially - a
243 "Plack::Component" object will have its "to_app" method called and be
244 used as a dispatcher:
245
246 sub dispatch_request {
247 my $self = shift;
248 (
249 '/static/...' => sub { Plack::App::File->new(...) },
250 ...
251 );
252 }
253
254 A Plack::Middleware object will be used as a filter for the rest of the
255 dispatch being returned into:
256
257 ## responds to /admin/track_usage AND /admin/delete_accounts
258
259 sub dispatch_request {
260 my $self = shift;
261 (
262 '/admin/**' => sub {
263 Plack::Middleware::Session->new(%opts);
264 },
265 '/admin/track_usage' => sub {
266 ## something that needs a session
267 },
268 '/admin/delete_accounts' => sub {
269 ## something else that needs a session
270 },
271 );
272 }
273
274 Note that this is for the dispatch being returned to, so if you want to
275 provide it inline you need to do:
276
277 ## ALSO responds to /admin/track_usage AND /admin/delete_accounts
278
279 sub dispatch_request {
280 my $self = shift;
281 (
282 '/admin/...' => sub {
283 (
284 sub {
285 Plack::Middleware::Session->new(%opts);
286 },
287 '/track_usage' => sub {
288 ## something that needs a session
289 },
290 '/delete_accounts' => sub {
291 ## something else that needs a session
292 },
293 );
294 }
295 );
296 }
297
298 And that's it - but remember that all this happens recursively - it's
299 dispatchers all the way down. A URL incoming pattern will run all
300 matching dispatchers and then hit all added filters or
301 Plack::Middleware.
302
303 Web::Simple match specifications
304 Method matches
305 'GET' => sub {
306
307 A match specification beginning with a capital letter matches HTTP
308 requests with that request method.
309
310 Path matches
311 '/login' => sub {
312
313 A match specification beginning with a / is a path match. In the
314 simplest case it matches a specific path. To match a path with a
315 wildcard part, you can do:
316
317 '/user/*' => sub {
318 $self->handle_user($_[1])
319
320 This will match /user/<anything> where <anything> does not include a
321 literal / character. The matched part becomes part of the match
322 arguments. You can also match more than one part:
323
324 '/user/*/*' => sub {
325 my ($self, $user_1, $user_2) = @_;
326
327 '/domain/*/user/*' => sub {
328 my ($self, $domain, $user) = @_;
329
330 and so on. To match an arbitrary number of parts, use "**":
331
332 '/page/**' => sub {
333 my ($self, $match) = @_;
334
335 This will result in a single element for the entire match. Note that you
336 can do
337
338 '/page/**/edit' => sub {
339
340 to match an arbitrary number of parts up to but not including some final
341 part.
342
343 Note: Since Web::Simple handles a concept of file extensions, "*" and
344 "**" matchers will not by default match things after a final dot, and
345 this can be modified by using "*.*" and "**.*" in the final position,
346 e.g.:
347
348 /one/* matches /one/two.three and captures "two"
349 /one/*.* matches /one/two.three and captures "two.three"
350 /** matches /one/two.three and captures "one/two"
351 /**.* matches /one/two.three and captures "one/two.three"
352
353 Finally,
354
355 '/foo/...' => sub {
356
357 Will match "/foo/" on the beginning of the path and strip it. This is
358 designed to be used to construct nested dispatch structures, but can
359 also prove useful for having e.g. an optional language specification at
360 the start of a path.
361
362 Note that the '...' is a "maybe something here, maybe not" so the above
363 specification will match like this:
364
365 /foo # no match
366 /foo/ # match and strip path to '/'
367 /foo/bar/baz # match and strip path to '/bar/baz'
368
369 Almost the same,
370
371 '/foo...' => sub {
372
373 Will match on "/foo/bar/baz", but also include "/foo". Otherwise it
374 operates the same way as "/foo/...".
375
376 /foo # match and strip path to ''
377 /foo/ # match and strip path to '/'
378 /foo/bar/baz # match and strip path to '/bar/baz'
379
380 Please note the difference between "sub(/foo/...)" and "sub(/foo...)".
381 In the first case, this is expecting to find something after "/foo" (and
382 fails to match if nothing is found), while in the second case we can
383 match both "/foo" and "/foo/more/to/come". The following are roughly the
384 same:
385
386 '/foo' => sub { 'I match /foo' },
387 '/foo/...' => sub {
388 (
389 '/bar' => sub { 'I match /foo/bar' },
390 '/*' => sub { 'I match /foo/{id}' },
391 );
392 }
393
394 Versus
395
396 '/foo...' => sub {
397 (
398 '~' => sub { 'I match /foo' },
399 '/bar' => sub { 'I match /foo/bar' },
400 '/*' => sub { 'I match /foo/{id}' },
401 );
402 }
403
404 You may prefer the latter example should you wish to take advantage of
405 subdispatchers to scope common activities. For example:
406
407 '/user...' => sub {
408 my $user_rs = $schema->resultset('User');
409 (
410 '~' => sub { $user_rs },
411 '/*' => sub { $user_rs->find($_[1]) },
412 );
413 }
414
415 You should note the special case path match "sub (~)" which is only
416 meaningful when it is contained in this type of path match. It matches
417 to an empty path.
418
419 Naming your patch matches
420 Any "*", "**", "*.*", or "**.*" match can be followed with ":name" to
421 make it into a named match, so:
422
423 '/*:one/*:two/*:three/*:four' => sub {
424 "I match /1/2/3/4 capturing { one => 1, two => 2, three => 3, four => 4 }"
425 }
426
427 '/**.*:allofit' => sub {
428 "I match anything capturing { allofit => \$whole_path }"
429 }
430
431 In the specific case of a simple single-* match, the * may be omitted,
432 to allow you to write:
433
434 '/:one/:two/:three/:four' => sub {
435 "I match /1/2/3/4 capturing { one => 1, two => 2, three => 3, four => 4 }"
436 }
437
438 "/foo" and "/foo/" are different specs
439 As you may have noticed with the difference between '/foo/...' and
440 '/foo...', trailing slashes in path specs are significant. This is
441 intentional and necessary to retain the ability to use relative links on
442 websites. Let's demonstrate on this link:
443
444 <a href="bar">bar</a>
445
446 If the user loads the url "/foo/" and clicks on this link, they will be
447 sent to "/foo/bar". However when they are on the url "/foo" and click
448 this link, then they will be sent to "/bar".
449
450 This makes it necessary to be explicit about the trailing slash.
451
452 Extension matches
453 '.html' => sub {
454
455 will match .html from the path (assuming the subroutine itself returns
456 something, of course). This is normally used for rendering - e.g.:
457
458 '.html' => sub {
459 response_filter { $self->render_html($_[1]) }
460 }
461
462 Additionally,
463
464 '.*' => sub {
465
466 will match any extension and supplies the extension as a match argument.
467
468 Query and body parameter matches
469 Query and body parameters can be match via
470
471 '?<param spec>' => sub { # match URI query
472 '%<param spec>' => sub { # match body params
473
474 The body spec will match if the request content is either
475 application/x-www-form-urlencoded or multipart/form-data - the latter of
476 which is required for uploads - see below.
477
478 The param spec is elements of one of the following forms:
479
480 param~ # optional parameter
481 param= # required parameter
482 @param~ # optional multiple parameter
483 @param= # required multiple parameter
484 :param~ # optional parameter in hashref
485 :param= # required parameter in hashref
486 :@param~ # optional multiple in hashref
487 :@param= # required multiple in hashref
488 * # include all other parameters in hashref
489 @* # include all other parameters as multiple in hashref
490
491 separated by the "&" character. The arguments added to the request are
492 one per non-":"/"*" parameter (scalar for normal, arrayref for
493 multiple), plus if any ":"/"*" specs exist a hashref containing those
494 values. If a parameter has no value, i.e. appears as '?foo&', a value of
495 1 will be captured.
496
497 Please note that if you specify a multiple type parameter match, you are
498 ensured of getting an arrayref for the value, EVEN if the current
499 incoming request has only one value. However if a parameter is specified
500 as single and multiple values are found, the last one will be used.
501
502 For example to match a "page" parameter with an optional "order_by"
503 parameter one would write:
504
505 '?page=&order_by~' => sub {
506 my ($self, $page, $order_by) = @_;
507 return unless $page =~ /^\d+$/;
508 $order_by ||= 'id';
509 response_filter {
510 $_[1]->search_rs({}, { page => $page, order_by => $order_by });
511 }
512 }
513
514 to implement paging and ordering against a DBIx::Class::ResultSet
515 object.
516
517 Another Example: To get all parameters as a hashref of arrayrefs, write:
518
519 '?@*' => sub {
520 my ($self, $params) = @_;
521 ...
522
523 To get two parameters as a hashref, write:
524
525 '?:user~&:domain~' => sub {
526 my ($self, $params) = @_; # params contains only 'user' and 'domain' keys
527
528 You can also mix these, so:
529
530 '?foo=&@bar~&:coffee=&@*' => sub {
531 my ($self, $foo, $bar, $params) = @_;
532
533 where $bar is an arrayref (possibly an empty one), and $params contains
534 arrayref values for all parameters not mentioned and a scalar value for
535 the 'coffee' parameter.
536
537 Note, in the case where you combine arrayref, single parameter and named
538 hashref style, the arrayref and single parameters will appear in @_ in
539 the order you defined them in the prototype, but all hashrefs will merge
540 into a single $params, as in the example above.
541
542 Upload matches
543 '*foo=' => sub { # param specifier can be anything valid for query or body
544
545 The upload match system functions exactly like a query/body match,
546 except that the values returned (if any) are "Web::Dispatch::Upload"
547 objects.
548
549 Note that this match type will succeed in two circumstances where you
550 might not expect it to - first, when the field exists but is not an
551 upload field and second, when the field exists but the form is not an
552 upload form (i.e. content type "application/x-www-form-urlencoded"
553 rather than "multipart/form-data"). In either of these cases, what
554 you'll get back is a "Web::Dispatch::NotAnUpload" object, which will
555 "die" with an error pointing out the problem if you try and use it. To
556 be sure you have a real upload object, call
557
558 $upload->is_upload # returns 1 on a valid upload, 0 on a non-upload field
559
560 and to get the reason why such an object is not an upload, call
561
562 $upload->reason # returns a reason or '' on a valid upload.
563
564 Other than these two methods, the upload object provides the same
565 interface as Plack::Request::Upload with the addition of a stringify to
566 the temporary filename to make copying it somewhere else easier to
567 handle.
568
569 Combining matches
570 Matches may be combined with the + character - e.g.
571
572 'GET + /user/*' => sub {
573
574 to create an AND match. They may also be combined with the | character -
575 e.g.
576
577 'GET|POST' => sub {
578
579 to create an OR match. Matches can be nested with () - e.g.
580
581 '(GET|POST + /user/*)' => sub {
582
583 and negated with ! - e.g.
584
585 '!/user/foo + /user/*' => sub {
586
587 ! binds to the immediate rightmost match specification, so if you want
588 to negate a combination you will need to use
589
590 '!(POST|PUT|DELETE)' => sub {
591
592 and | binds tighter than +, so
593
594 '(GET|POST) + /user/*' => sub {
595
596 and
597
598 'GET|POST + /user/*' => sub {
599
600 are equivalent, but
601
602 '(GET + /admin/...) | (POST + /admin/...)' => sub {
603
604 and
605
606 'GET + /admin/... | POST + /admin/...' => sub {
607
608 are not - the latter is equivalent to
609
610 'GET + (/admin/...|POST) + /admin/...' => sub {
611
612 which will never match!
613
614 Whitespace
615 Note that for legibility you are permitted to use whitespace:
616
617 'GET + /user/*' => sub {
618
619 but it will be ignored. This is because the perl parser strips
620 whitespace from subroutine prototypes, so this is equivalent to
621
622 'GET+/user/*' => sub {
623
624 Accessing parameters via %_
625 If your dispatch specification causes your dispatch subroutine to
626 receive a hash reference as its first argument, the contained named
627 parameters will be accessible via %_.
628
629 This can be used to access your path matches, if they are named:
630
631 'GET + /foo/:path_part' => sub {
632 [ 200,
633 ['Content-type' => 'text/plain'],
634 ["We are in $_{path_part}"],
635 ];
636 }
637
638 Or, if your first argument would be a hash reference containing named
639 query parameters:
640
641 'GET + /foo + ?:some_param=' => sub {
642 [ 200,
643 ['Content-type' => 'text/plain'],
644 ["We received $_{some_param} as parameter"],
645 ];
646 }
647
648 Of course this also works when all you are doing is slurping the whole
649 set of parameters by their name:
650
651 'GET + /foo + ?*' => sub {
652 [ 200,
653 ['Content-type' => 'text/plain'],
654 [exists($_{foo}) ? "Received a foo: $_{foo}" : "No foo!"],
655 ],
656 }
657
658 Note that only the first hash reference will be available via %_. If you
659 receive additional hash references, you will need to access them as
660 usual.
661
662 Accessing the PSGI env hash
663 In some cases you may wish to get the raw PSGI env hash - to do this,
664 you can either use a plain sub:
665
666 sub {
667 my ($env) = @_;
668 ...
669 }
670
671 or use the "PSGI_ENV" constant exported to retrieve it from @_:
672
673 'GET + /foo + ?some_param=' => sub {
674 my $param = $_[1];
675 my $env = $_[PSGI_ENV];
676 }
677
678 but note that if you're trying to add a middleware, you should simply
679 use Web::Simple's direct support for doing so.
680
681EXPORTED SUBROUTINES
682 response_filter
683 response_filter {
684 # Hide errors from the user because we hates them, preciousss
685 if (ref($_[0]) eq 'ARRAY' && $_[0]->[0] == 500) {
686 $_[0] = [ 200, @{$_[0]}[1..$#{$_[0]}] ];
687 }
688 return $_[0];
689 };
690
691 The response_filter subroutine is designed for use inside dispatch
692 subroutines.
693
694 It creates and returns a special dispatcher that always matches, and
695 calls the block passed to it as a filter on the result of running the
696 rest of the current dispatch chain.
697
698 Thus the filter above runs further dispatch as normal, but if the result
699 of dispatch is a 500 (Internal Server Error) response, changes this to a
700 200 (OK) response without altering the headers or body.
701
702 redispatch_to
703 redispatch_to '/other/url';
704
705 The redispatch_to subroutine is designed for use inside dispatch
706 subroutines.
707
708 It creates and returns a special dispatcher that always matches, and
709 instead of continuing dispatch re-delegates it to the start of the
710 dispatch process, but with the path of the request altered to the
711 supplied URL.
712
713 Thus if you receive a POST to "/some/url" and return a redispatch to
714 "/other/url", the dispatch behaviour will be exactly as if the same POST
715 request had been made to "/other/url" instead.
716
717 Note, this is not the same as returning an HTTP 3xx redirect as a
718 response; rather it is a much more efficient internal process.
719
720CHANGES BETWEEN RELEASES
721 Changes between 0.004 and 0.005
722 * dispatch {} replaced by declaring a dispatch_request method
723
724 dispatch {} has gone away - instead, you write:
725
726 sub dispatch_request {
727 my $self = shift;
728 (
729 'GET /foo/' => sub { ... },
730 ...
731 );
732 }
733
734 Note that this method is still returning the dispatch code - just
735 like "dispatch" did.
736
737 Also note that you need the "my $self = shift" since the magic $self
738 variable went away.
739
740 * the magic $self variable went away.
741
742 Just add "my $self = shift;" while writing your "sub
743 dispatch_request {" like a normal perl method.
744
745 * subdispatch deleted - all dispatchers can now subdispatch
746
747 In earlier releases you needed to write:
748
749 subdispatch sub (/foo/...) {
750 ...
751 [
752 sub (GET /bar/) { ... },
753 ...
754 ]
755 }
756
757 As of 0.005, you can instead write simply:
758
759 sub (/foo/...) {
760 ...
761 (
762 sub (GET /bar/) { ... },
763 ...
764 )
765 }
766
767 Changes since Antiquated Perl
768 * filter_response renamed to response_filter
769
770 This is a pure rename; a global search and replace should fix it.
771
772 * dispatch [] changed to dispatch {}
773
774 Simply changing
775
776 dispatch [ sub(...) { ... }, ... ];
777
778 to
779
780 dispatch { sub(...) { ... }, ... };
781
782 should work fine.
783
784DEVELOPMENT HISTORY
785 Web::Simple was originally written to form part of my Antiquated Perl
786 talk for Italian Perl Workshop 2009, but in writing the bloggery example
787 I realised that having a bare minimum system for writing web
788 applications that doesn't drive me insane was rather nice and decided to
789 spend my attempt at nanowrimo for 2009 improving and documenting it to
790 the point where others could use it.
791
792 The Antiquated Perl talk can be found at
793 <http://www.shadowcat.co.uk/archive/conference-video/> and the slides
794 are reproduced in this distribution under Web::Simple::AntiquatedPerl.
795
796COMMUNITY AND SUPPORT
797 IRC channel
798 irc.perl.org #web-simple
799
800 No mailing list yet
801 Because mst's non-work email is a bombsite so he'd never read it anyway.
802
803 Git repository
804 Gitweb is on http://git.shadowcat.co.uk/ and the clone URL is:
805
806 git clone git://git.shadowcat.co.uk/catagits/Web-Simple.git
807
808AUTHOR
809 Matt S. Trout (mst) <mst@shadowcat.co.uk>
810
811CONTRIBUTORS
812 Devin Austin (dhoss) <dhoss@cpan.org>
813
814 Arthur Axel 'fREW' Schmidt <frioux@gmail.com>
815
816 gregor herrmann (gregoa) <gregoa@debian.org>
817
818 John Napiorkowski (jnap) <jjn1056@yahoo.com>
819
820 Josh McMichael <jmcmicha@linus222.gsc.wustl.edu>
821
822 Justin Hunter (arcanez) <justin.d.hunter@gmail.com>
823
824 Kjetil Kjernsmo <kjetil@kjernsmo.net>
825
826 markie <markie@nulletch64.dreamhost.com>
827
828 Christian Walde (Mithaldu) <walde.christian@googlemail.com>
829
830 nperez <nperez@cpan.org>
831
832 Robin Edwards <robin.ge@gmail.com>
833
834 Andrew Rodland (hobbs) <andrew@cleverdomain.org>
835
836 Robert Sedlacek (phaylon) <r.sedlacek@shadowcat.co.uk>
837
838 Hakim Cassimally (osfameron) <osfameron@cpan.org>
839
840 Karen Etheridge (ether) <ether@cpan.org>
841
842COPYRIGHT
843 Copyright (c) 2011 the Web::Simple "AUTHOR" and "CONTRIBUTORS" as listed
844 above.
845
846LICENSE
847 This library is free software and may be distributed under the same
848 terms as perl itself.
849
850