implement !
[catagits/Web-Simple.git] / lib / Web / Simple.pm
CommitLineData
5c33dda5 1package Web::Simple;
2
3use strict;
4use warnings FATAL => 'all';
5
44db8e76 6sub setup_all_strictures {
5c33dda5 7 strict->import;
8 warnings->import(FATAL => 'all');
44db8e76 9}
10
11sub setup_dispatch_strictures {
12 setup_all_strictures();
5c33dda5 13 warnings->unimport('syntax');
14 warnings->import(FATAL => qw(
15 ambiguous bareword digit parenthesis precedence printf
16 prototype qw reserved semicolon
17 ));
44db8e76 18}
19
20sub import {
21 setup_dispatch_strictures();
5c33dda5 22 my ($class, $app_package) = @_;
23 $class->_export_into($app_package);
24}
25
26sub _export_into {
27 my ($class, $app_package) = @_;
28 {
29 no strict 'refs';
30 *{"${app_package}::dispatch"} = sub {
44db8e76 31 $app_package->_setup_dispatcher(@_);
5c33dda5 32 };
33 *{"${app_package}::filter_response"} = sub (&) {
34 $app_package->_construct_response_filter($_[0]);
35 };
39119082 36 *{"${app_package}::redispatch_to"} = sub {
37 $app_package->_construct_redispatch($_[0]);
38 };
5c33dda5 39 *{"${app_package}::default_config"} = sub {
44db8e76 40 $app_package->_setup_default_config(@_);
5c33dda5 41 };
42 *{"${app_package}::self"} = \${"${app_package}::self"};
43 require Web::Simple::Application;
44 unshift(@{"${app_package}::ISA"}, 'Web::Simple::Application');
45 }
b7063124 46 (my $name = $app_package) =~ s/::/\//g;
47 $INC{"${name}.pm"} = 'Set by "use Web::Simple;" invocation';
5c33dda5 48}
49
7401408e 50=head1 NAME
51
52Web::Simple - A quick and easy way to build simple web applications
53
54=head1 WARNING
55
56This is really quite new. If you're reading this from git, it means it's
57really really new and we're still playing with things. If you're reading
58this on CPAN, it means the stuff that's here we're probably happy with. But
59only probably. So we may have to change stuff.
60
61If we do find we have to change stuff we'll add a section explaining how to
62switch your code across to the new version, and we'll do our best to make it
63as painless as possible because we've got Web::Simple applications too. But
64we 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
87If 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
91you'll get the "Hello world!" string output to your browser. For more complex
92examples and non-CGI deployment, see below.
93
94=head1 WHY?
95
96While I originally wrote Web::Simple as part of my Antiquated Perl talk for
97Italian Perl Workshop 2009, I've found that having a bare minimum system for
98writing web applications that doesn't drive me insane is rather nice.
99
100The philosophy of Web::Simple is to keep to an absolute bare minimum, for
101everything. It is not designed to be used for large scale applications;
102the L<Catalyst> web framework already works very nicely for that and is
103a far more mature, well supported piece of software.
104
105However, if you have an application that only does a couple of things, and
106want to not have to think about complexities of deployment, then Web::Simple
107might be just the thing for you.
108
109The Antiquated Perl talk can be found at L<http://www.shadowcat.co.uk/archive/conference-video/>.
110
111=head1 DESCRIPTION
112
113The only public interface the Web::Simple module itself provides is an
114import based one -
115
116 use Web::Simple 'NameOfApplication';
117
118This imports 'strict' and 'warnings FATAL => "all"' into your code as well,
119so you can skip the usual
120
121 use strict;
122 use warnings;
123
124provided you 'use Web::Simple' at the top of the file. Note that we turn
125on *fatal* warnings so if you have any warnings at any point from the file
126that you did 'use Web::Simple' in, then your application will die. This is,
127so far, considered a feature.
128
129Calling 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
137It 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
44db8e76 150and creates a $self global variable in your application package, so you can
7401408e 151use $self in dispatch subs without violating strict (Web::Simple::Application
152arranges for dispatch subroutines to have the correct $self in scope when
153this happens).
154
b7063124 155Finally, import sets
156
157 $INC{"NameOfApplication.pm"} = 'Set by "use Web::Simple;" invocation';
158
159so that perl will not attempt to load the application again even if
160
161 require NameOfApplication;
162
163is encountered in other code.
164
7401408e 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
178This 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
184in the application namespace when executed. Note that this means that
44db8e76 185you should only run default_config once - calling it a second time will
186cause an exception to be thrown.
7401408e 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
44db8e76 199The dispatch subroutine calls NameOfApplication->_setup_dispatcher with
200the subroutines passed to it, which then creates your Web::Simple
7401408e 201application's dispatcher from these subs. The prototype of the subroutine
202is expected to be a Web::Simple dispatch specification (see
203L</DISPATCH SPECIFICATIONS> below for more details), and the body of the
204subroutine is the code to execute if the specification matches. See
205L</DISPATCH STRATEGY> below for details on how the Web::Simple dispatch
206system uses the return values of these subroutines to determine how to
207continue, alter or abort dispatch.
208
44db8e76 209Note that _setup_dispatcher creates a
7401408e 210
44db8e76 211 sub _dispatcher {
212 return <root dispatcher object here>;
7401408e 213 }
214
215method in your class so as with default_config, calling dispatch a second time
44db8e76 216will result in an exception.
7401408e 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
228The response_filter subroutine is designed for use inside dispatch subroutines.
229
44db8e76 230It creates and returns a special dispatcher that always matches, and calls
231the block passed to it as a filter on the result of running the rest of the
232current dispatch chain.
233
234Thus the filter above runs further dispatch as normal, but if the result of
235dispatch is a 500 (Internal Server Error) response, changes this to a 200 (OK)
236response without altering the headers or body.
237
238=head2 redispatch_to
239
240 redispatch_to '/other/url';
241
242The redispatch_to subroutine is designed for use inside dispatch subroutines.
243
244It creates and returns a special dispatcher that always matches, and instead
245of continuing dispatch re-delegates it to the start of the dispatch process,
246but with the path of the request altered to the supplied URL.
247
248Thus 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
250request had been made to '/other/url' instead.
7401408e 251
3583ca04 252=head1 DISPATCH STRATEGY
253
81a5b03e 254=head2 Description of the dispatcher object
255
256Web::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
268When a dispatcher is invoked, it checks its match routine against the
269request environment. The match routine may provide alterations to the
270request as a result of matching, and/or arguments for the call routine.
271
272If no match routine has been provided then Web::Simple treats this as
273a success, and supplies the request environment to the call routine as
274an argument.
275
276Given a successful match, the call routine is now invoked in list context
277with any arguments given to the original dispatch, plus any arguments
278provided by the match result.
279
280If this routine returns (), Web::Simple treats this identically to a failure
281to match.
282
283If this routine returns a Web::Simple::Dispatcher, the environment changes
284are merged into the environment and the new dispatcher's next pointer is
285set to our next pointer.
286
287If this routine returns anything else, that is treated as the end of dispatch
288and the value is returned.
289
290On a failed match, Web::Simple invokes the next dispatcher with the same
291arguments and request environment passed to the current one. On a successful
292match that returned a new dispatcher, Web::Simple invokes the new dispatcher
293with the same arguments but the modified request environment.
294
295=head2 How Web::Simple builds dispatcher objects for you
296
297In the case of the Web::Simple L</dispatch> export the match is constructed
298from the subroutine prototype - i.e.
299
300 sub (<match specification>) {
301 <call code>
302 }
303
304and the 'next' pointer is populated with the next element of the array,
305expect for the last element, which is given a next that will throw a 500
306error if none of your dispatchers match. If you want to provide something
307else 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
313will produce a 404 result instead of a 500 by default. You can also override
314the L<Web::Simple::Application/_build_final_dispatcher> method in your app.
315
316Note that the code in the subroutine is executed as a -method- on your
317application object, so if your match specification provides arguments you
318should 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
15dfe701 329 sub (GET ...) {
330
331A match specification beginning with a capital letter matches HTTP requests
332with that request method.
333
81a5b03e 334=head3 Path matches
335
15dfe701 336 sub (/login) {
337
338A match specification beginning with a / is a path match. In the simplest
339case it matches a specific path. To match a path with a wildcard part, you
340can do:
341
342 sub (/user/*) {
343 $self->handle_user($_[1])
344
345This will match /user/<anything> where <anything> does not include a literal
346/ character. The matched part becomes part of the match arguments. You can
347also 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
355and so on. To match an arbitrary number of parts, use -
356
357 sub (/page/**) {
358
359This will result in an element per /-separated part so matched. Note that
360you can do
361
362 sub (/page/**/edit) {
363
364to match an arbitrary number of parts up to but not including some final
365part.
366
81a5b03e 367=head3 Extension matches
368
15dfe701 369 sub (.html) {
370
371will match and strip .html from the path (assuming the subroutine itself
372returns something, of course). This is normally used for rendering - e.g.
373
374 sub (.html) {
375 filter_response { $self->render_html($_[1]) }
376 }
377
81a5b03e 378=head3 Combining matches
379
15dfe701 380Matches may be combined with the + character - e.g.
381
382 sub (GET+/user/*) {
383
384Note that for legibility you are permitted to use whitespace -
385
44db8e76 386 sub (GET + /user/*) {
15dfe701 387
388but it will be ignored.
389
3583ca04 390=cut
7401408e 391
5c33dda5 3921;