Commit | Line | Data |
5c33dda5 |
1 | package Web::Simple; |
2 | |
3 | use strict; |
4 | use warnings FATAL => 'all'; |
5 | |
44db8e76 |
6 | sub setup_all_strictures { |
5c33dda5 |
7 | strict->import; |
8 | warnings->import(FATAL => 'all'); |
44db8e76 |
9 | } |
10 | |
11 | sub 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 | |
20 | sub import { |
21 | setup_dispatch_strictures(); |
5c33dda5 |
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 { |
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 | |
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 | |
44db8e76 |
150 | and creates a $self global variable in your application package, so you can |
7401408e |
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 | |
b7063124 |
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 | |
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 | |
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 |
44db8e76 |
185 | you should only run default_config once - calling it a second time will |
186 | cause 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 |
199 | The dispatch subroutine calls NameOfApplication->_setup_dispatcher with |
200 | the subroutines passed to it, which then creates your Web::Simple |
7401408e |
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 | |
44db8e76 |
209 | Note that _setup_dispatcher creates a |
7401408e |
210 | |
44db8e76 |
211 | sub _dispatcher { |
212 | return <root dispatcher object here>; |
7401408e |
213 | } |
214 | |
215 | method in your class so as with default_config, calling dispatch a second time |
44db8e76 |
216 | will 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 | |
228 | The response_filter subroutine is designed for use inside dispatch subroutines. |
229 | |
44db8e76 |
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. |
7401408e |
251 | |
3583ca04 |
252 | =head1 DISPATCH STRATEGY |
253 | |
81a5b03e |
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 | |
15dfe701 |
329 | sub (GET ...) { |
330 | |
331 | A match specification beginning with a capital letter matches HTTP requests |
332 | with that request method. |
333 | |
81a5b03e |
334 | =head3 Path matches |
335 | |
15dfe701 |
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 | |
81a5b03e |
367 | =head3 Extension matches |
368 | |
15dfe701 |
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 | |
81a5b03e |
378 | =head3 Combining matches |
379 | |
15dfe701 |
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 | |
44db8e76 |
386 | sub (GET + /user/*) { |
15dfe701 |
387 | |
388 | but it will be ignored. |
389 | |
3583ca04 |
390 | =cut |
7401408e |
391 | |
5c33dda5 |
392 | 1; |