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