more renaming and cleanup
[catagits/Web-Simple.git] / lib / Web / Simple.pm
1 package Web::Simple;
2
3 use strict;
4 use warnings FATAL => 'all';
5
6 sub setup_all_strictures {
7   strict->import;
8   warnings->import(FATAL => 'all');
9 }
10
11 sub setup_dispatch_strictures {
12   setup_all_strictures();
13   warnings->unimport('syntax');
14   warnings->import(FATAL => qw(
15     ambiguous bareword digit parenthesis precedence printf
16     prototype qw reserved semicolon
17   ));
18 }
19
20 sub import {
21   setup_dispatch_strictures();
22   my ($class, $app_package) = @_;
23   $class->_export_into($app_package);
24 }
25
26 sub _export_into {
27   my ($class, $app_package) = @_;
28   {
29     no strict 'refs';
30     *{"${app_package}::dispatch"} = sub {
31       $app_package->_setup_dispatcher(@_);
32     };
33     *{"${app_package}::filter_response"} = sub (&) {
34       $app_package->_construct_response_filter($_[0]);
35     };
36     *{"${app_package}::redispatch_to"} = sub {
37       $app_package->_construct_redispatch($_[0]);
38     };
39     *{"${app_package}::default_config"} = sub {
40       $app_package->_setup_default_config(@_);
41     };
42     *{"${app_package}::self"} = \${"${app_package}::self"};
43     require Web::Simple::Application;
44     unshift(@{"${app_package}::ISA"}, 'Web::Simple::Application');
45   }
46   (my $name = $app_package) =~ s/::/\//g;
47   $INC{"${name}.pm"} = 'Set by "use Web::Simple;" invocation';
48 }
49
50 =head1 NAME
51
52 Web::Simple - A quick and easy way to build simple web applications
53
54 =head1 WARNING
55
56 This is really quite new. If you're reading this from git, it means it's
57 really really new and we're still playing with things. If you're reading
58 this on CPAN, it means the stuff that's here we're probably happy with. But
59 only probably. So we may have to change stuff.
60
61 If we do find we have to change stuff we'll add a section explaining how to
62 switch your code across to the new version, and we'll do our best to make it
63 as painless as possible because we've got Web::Simple applications too. But
64 we can't promise not to change things at all. Not yet. Sorry.
65
66 =head1 SYNOPSIS
67
68   #!/usr/bin/perl
69
70   use Web::Simple 'HelloWorld';
71
72   {
73     package HelloWorld;
74
75     dispatch [
76       sub (GET) {
77         [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
78       },
79       sub () {
80         [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
81       }
82     ];
83   }
84
85   HelloWorld->run_if_script;
86
87 If you save this file into your cgi-bin as hello-world.cgi and then visit
88
89   http://my.server.name/cgi-bin/hello-world.cgi/
90
91 you'll get the "Hello world!" string output to your browser. For more complex
92 examples and non-CGI deployment, see below.
93
94 =head1 WHY?
95
96 While I originally wrote Web::Simple as part of my Antiquated Perl talk for
97 Italian Perl Workshop 2009, I've found that having a bare minimum system for
98 writing web applications that doesn't drive me insane is rather nice.
99
100 The philosophy of Web::Simple is to keep to an absolute bare minimum, for
101 everything. It is not designed to be used for large scale applications;
102 the L<Catalyst> web framework already works very nicely for that and is
103 a far more mature, well supported piece of software.
104
105 However, if you have an application that only does a couple of things, and
106 want to not have to think about complexities of deployment, then Web::Simple
107 might be just the thing for you.
108
109 The Antiquated Perl talk can be found at L<http://www.shadowcat.co.uk/archive/conference-video/>.
110
111 =head1 DESCRIPTION
112
113 The only public interface the Web::Simple module itself provides is an
114 import based one -
115
116   use Web::Simple 'NameOfApplication';
117
118 This imports 'strict' and 'warnings FATAL => "all"' into your code as well,
119 so you can skip the usual
120
121   use strict;
122   use warnings;
123
124 provided you 'use Web::Simple' at the top of the file. Note that we turn
125 on *fatal* warnings so if you have any warnings at any point from the file
126 that you did 'use Web::Simple' in, then your application will die. This is,
127 so far, considered a feature.
128
129 Calling the import also makes NameOfApplication isa Web::Simple::Application
130 - i.e. does the equivalent of
131
132   {
133     package NameOfApplication;
134     use base qw(Web::Simple::Application);
135   }
136
137 It also exports the following subroutines:
138
139   default_config(
140     key => 'value',
141     ...
142   );
143
144   dispatch [ sub (...) { ... }, ... ];
145
146   filter_response { ... };
147
148   redispatch_to '/somewhere';
149
150 and creates a $self global variable in your application package, so you can
151 use $self in dispatch subs without violating strict (Web::Simple::Application
152 arranges for dispatch subroutines to have the correct $self in scope when
153 this happens).
154
155 Finally, import sets
156
157   $INC{"NameOfApplication.pm"} = 'Set by "use Web::Simple;" invocation';
158
159 so that perl will not attempt to load the application again even if
160
161   require NameOfApplication;
162
163 is encountered in other code.
164
165 =head1 EXPORTED SUBROUTINES
166
167 =head2 default_config
168
169   default_config(
170     one_key => 'foo',
171     another_key => 'bar',
172   );
173
174   ...
175
176   $self->config->{one_key} # 'foo'
177
178 This creates the default configuration for the application, by creating a
179
180   sub _default_config {
181      return (one_key => 'foo', another_key => 'bar');
182   }
183
184 in the application namespace when executed. Note that this means that
185 you should only run default_config once - calling it a second time will
186 cause an exception to be thrown.
187
188 =head2 dispatch
189
190   dispatch [
191     sub (GET) {
192       [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ]
193     },
194     sub () {
195       [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ]
196     }
197   ];
198
199 The dispatch subroutine calls NameOfApplication->_setup_dispatcher with
200 the subroutines passed to it, which then creates your Web::Simple
201 application's dispatcher from these subs. The prototype of the subroutine
202 is expected to be a Web::Simple dispatch specification (see
203 L</DISPATCH SPECIFICATIONS> below for more details), and the body of the
204 subroutine is the code to execute if the specification matches. See
205 L</DISPATCH STRATEGY> below for details on how the Web::Simple dispatch
206 system uses the return values of these subroutines to determine how to
207 continue, alter or abort dispatch.
208
209 Note that _setup_dispatcher creates a
210
211   sub _dispatcher {
212     return <root dispatcher object here>;
213   }
214
215 method in your class so as with default_config, calling dispatch a second time
216 will result in an exception.
217
218 =head2 response_filter
219
220   response_filter {
221     # Hide errors from the user because we hates them, preciousss
222     if (ref($_[1]) eq 'ARRAY' && $_[1]->[0] == 500) {
223       $_[1] = [ 200, @{$_[1]}[1..$#{$_[1]}] ];
224     }
225     return $_[1];
226   };
227
228 The response_filter subroutine is designed for use inside dispatch subroutines.
229
230 It creates and returns a special dispatcher that always matches, and calls
231 the block passed to it as a filter on the result of running the rest of the
232 current dispatch chain.
233
234 Thus the filter above runs further dispatch as normal, but if the result of
235 dispatch is a 500 (Internal Server Error) response, changes this to a 200 (OK)
236 response without altering the headers or body.
237
238 =head2 redispatch_to
239
240   redispatch_to '/other/url';
241
242 The redispatch_to subroutine is designed for use inside dispatch subroutines.
243
244 It creates and returns a special dispatcher that always matches, and instead
245 of continuing dispatch re-delegates it to the start of the dispatch process,
246 but with the path of the request altered to the supplied URL.
247
248 Thus if you receive a POST to '/some/url' and return a redipstch to
249 '/other/url', the dispatch behaviour will be exactly as if the same POST
250 request had been made to '/other/url' instead.
251
252 =head1 DISPATCH STRATEGY
253
254 =head2 Description of the dispatcher object
255
256 Web::Simple::Dispatcher objects have three components:
257
258 =over 4
259
260 =item * match - an optional test if this dispatcher matches the request
261
262 =item * call - a routine to call if this dispatcher matches (or has no match)
263
264 =item * next - the next dispatcher to call
265
266 =back
267
268 When a dispatcher is invoked, it checks its match routine against the
269 request environment. The match routine may provide alterations to the
270 request as a result of matching, and/or arguments for the call routine.
271
272 If no match routine has been provided then Web::Simple treats this as
273 a success, and supplies the request environment to the call routine as
274 an argument.
275
276 Given a successful match, the call routine is now invoked in list context
277 with any arguments given to the original dispatch, plus any arguments
278 provided by the match result.
279
280 If this routine returns (), Web::Simple treats this identically to a failure
281 to match.
282
283 If this routine returns a Web::Simple::Dispatcher, the environment changes
284 are merged into the environment and the new dispatcher's next pointer is
285 set to our next pointer.
286
287 If this routine returns anything else, that is treated as the end of dispatch
288 and the value is returned.
289
290 On a failed match, Web::Simple invokes the next dispatcher with the same
291 arguments and request environment passed to the current one. On a successful
292 match that returned a new dispatcher, Web::Simple invokes the new dispatcher
293 with the same arguments but the modified request environment.
294
295 =head2 How Web::Simple builds dispatcher objects for you
296
297 In the case of the Web::Simple L</dispatch> export the match is constructed
298 from the subroutine prototype - i.e.
299
300   sub (<match specification>) {
301     <call code>
302   }
303
304 and the 'next' pointer is populated with the next element of the array,
305 expect for the last element, which is given a next that will throw a 500
306 error if none of your dispatchers match. If you want to provide something
307 else as a default, a routine with no match specification always matches, so -
308
309   sub () {
310     [ 404, [ 'Content-type', 'text/plain' ], [ 'Error: Not Found' ] ]
311   }
312
313 will produce a 404 result instead of a 500 by default. You can also override
314 the L<Web::Simple::Application/_build_final_dispatcher> method in your app.
315
316 Note that the code in the subroutine is executed as a -method- on your
317 application object, so if your match specification provides arguments you
318 should unpack them like so:
319
320   sub (<match specification>) {
321     my ($self, @args) = @_;
322     ...
323   }
324
325 =head2 Web::Simple match specifications
326
327 =head3 Method matches
328
329   sub (GET ...) {
330
331 A match specification beginning with a capital letter matches HTTP requests
332 with that request method.
333
334 =head3 Path matches
335
336   sub (/login) {
337
338 A match specification beginning with a / is a path match. In the simplest
339 case it matches a specific path. To match a path with a wildcard part, you
340 can do:
341
342   sub (/user/*) {
343     $self->handle_user($_[1])
344
345 This will match /user/<anything> where <anything> does not include a literal
346 / character. The matched part becomes part of the match arguments. You can
347 also match more than one part:
348
349   sub (/user/*/*) {
350     my ($self, $user_1, $user_2) = @_;
351
352   sub (/domain/*/user/*) {
353     my ($self, $domain, $user) = @_;
354
355 and so on. To match an arbitrary number of parts, use -
356
357   sub (/page/**) {
358
359 This will result in an element per /-separated part so matched. Note that
360 you can do
361
362   sub (/page/**/edit) {
363
364 to match an arbitrary number of parts up to but not including some final
365 part.
366
367 =head3 Extension matches
368
369   sub (.html) {
370
371 will match and strip .html from the path (assuming the subroutine itself
372 returns something, of course). This is normally used for rendering - e.g.
373
374   sub (.html) {
375     filter_response { $self->render_html($_[1]) }
376   }
377
378 =head3 Combining matches
379
380 Matches may be combined with the + character - e.g.
381
382   sub (GET+/user/*) {
383
384 Note that for legibility you are permitted to use whitespace -
385
386   sub (GET + /user/*) {
387
388 but it will be ignored.
389
390 =cut
391
392 1;