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 | } |
39 | } |
40 | |
7401408e |
41 | =head1 NAME |
42 | |
43 | Web::Simple - A quick and easy way to build simple web applications |
44 | |
45 | =head1 WARNING |
46 | |
47 | This is really quite new. If you're reading this from git, it means it's |
48 | really really new and we're still playing with things. If you're reading |
49 | this on CPAN, it means the stuff that's here we're probably happy with. But |
50 | only probably. So we may have to change stuff. |
51 | |
52 | If we do find we have to change stuff we'll add a section explaining how to |
53 | switch your code across to the new version, and we'll do our best to make it |
54 | as painless as possible because we've got Web::Simple applications too. But |
55 | we can't promise not to change things at all. Not yet. Sorry. |
56 | |
57 | =head1 SYNOPSIS |
58 | |
59 | #!/usr/bin/perl |
60 | |
61 | use Web::Simple 'HelloWorld'; |
62 | |
63 | { |
64 | package HelloWorld; |
65 | |
66 | dispatch [ |
67 | sub (GET) { |
68 | [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ] |
69 | }, |
70 | sub () { |
71 | [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ] |
72 | } |
73 | ]; |
74 | } |
75 | |
76 | HelloWorld->run_if_script; |
77 | |
78 | If you save this file into your cgi-bin as hello-world.cgi and then visit |
79 | |
80 | http://my.server.name/cgi-bin/hello-world.cgi/ |
81 | |
82 | you'll get the "Hello world!" string output to your browser. For more complex |
83 | examples and non-CGI deployment, see below. |
84 | |
85 | =head1 WHY? |
86 | |
87 | While I originally wrote Web::Simple as part of my Antiquated Perl talk for |
88 | Italian Perl Workshop 2009, I've found that having a bare minimum system for |
89 | writing web applications that doesn't drive me insane is rather nice. |
90 | |
91 | The philosophy of Web::Simple is to keep to an absolute bare minimum, for |
92 | everything. It is not designed to be used for large scale applications; |
93 | the L<Catalyst> web framework already works very nicely for that and is |
94 | a far more mature, well supported piece of software. |
95 | |
96 | However, if you have an application that only does a couple of things, and |
97 | want to not have to think about complexities of deployment, then Web::Simple |
98 | might be just the thing for you. |
99 | |
100 | The Antiquated Perl talk can be found at L<http://www.shadowcat.co.uk/archive/conference-video/>. |
101 | |
102 | =head1 DESCRIPTION |
103 | |
104 | The only public interface the Web::Simple module itself provides is an |
105 | import based one - |
106 | |
107 | use Web::Simple 'NameOfApplication'; |
108 | |
109 | This imports 'strict' and 'warnings FATAL => "all"' into your code as well, |
110 | so you can skip the usual |
111 | |
112 | use strict; |
113 | use warnings; |
114 | |
115 | provided you 'use Web::Simple' at the top of the file. Note that we turn |
116 | on *fatal* warnings so if you have any warnings at any point from the file |
117 | that you did 'use Web::Simple' in, then your application will die. This is, |
118 | so far, considered a feature. |
119 | |
120 | Calling the import also makes NameOfApplication isa Web::Simple::Application |
121 | - i.e. does the equivalent of |
122 | |
123 | { |
124 | package NameOfApplication; |
125 | use base qw(Web::Simple::Application); |
126 | } |
127 | |
128 | It also exports the following subroutines: |
129 | |
130 | default_config( |
131 | key => 'value', |
132 | ... |
133 | ); |
134 | |
135 | dispatch [ sub (...) { ... }, ... ]; |
136 | |
137 | filter_response { ... }; |
138 | |
139 | redispatch_to '/somewhere'; |
140 | |
141 | and creates the $self global variable in your application package, so you can |
142 | use $self in dispatch subs without violating strict (Web::Simple::Application |
143 | arranges for dispatch subroutines to have the correct $self in scope when |
144 | this happens). |
145 | |
146 | =head1 EXPORTED SUBROUTINES |
147 | |
148 | =head2 default_config |
149 | |
150 | default_config( |
151 | one_key => 'foo', |
152 | another_key => 'bar', |
153 | ); |
154 | |
155 | ... |
156 | |
157 | $self->config->{one_key} # 'foo' |
158 | |
159 | This creates the default configuration for the application, by creating a |
160 | |
161 | sub _default_config { |
162 | return (one_key => 'foo', another_key => 'bar'); |
163 | } |
164 | |
165 | in the application namespace when executed. Note that this means that |
166 | you should only run default_config once - a second run will cause a warning |
167 | that you are override the _default_config method in your application, which |
168 | under Web::Simple will of course be fatal. |
169 | |
170 | =head2 dispatch |
171 | |
172 | dispatch [ |
173 | sub (GET) { |
174 | [ 200, [ 'Content-type', 'text/plain' ], [ 'Hello world!' ] ] |
175 | }, |
176 | sub () { |
177 | [ 405, [ 'Content-type', 'text/plain' ], [ 'Method not allowed' ] ] |
178 | } |
179 | ]; |
180 | |
181 | The dispatch subroutine calls NameOfApplication->_setup_dispatchables with |
182 | the subroutines passed to it, which then create's your Web::Simple |
183 | application's dispatcher from these subs. The prototype of the subroutine |
184 | is expected to be a Web::Simple dispatch specification (see |
185 | L</DISPATCH SPECIFICATIONS> below for more details), and the body of the |
186 | subroutine is the code to execute if the specification matches. See |
187 | L</DISPATCH STRATEGY> below for details on how the Web::Simple dispatch |
188 | system uses the return values of these subroutines to determine how to |
189 | continue, alter or abort dispatch. |
190 | |
191 | Note that _setup_dispatchables creates a |
192 | |
193 | sub _dispatchables { |
194 | return (<dispatchable objects here>); |
195 | } |
196 | |
197 | method in your class so as with default_config, calling dispatch a second time |
198 | will result in a fatal warning from your application. |
199 | |
200 | =head2 response_filter |
201 | |
202 | response_filter { |
203 | # Hide errors from the user because we hates them, preciousss |
204 | if (ref($_[1]) eq 'ARRAY' && $_[1]->[0] == 500) { |
205 | $_[1] = [ 200, @{$_[1]}[1..$#{$_[1]}] ]; |
206 | } |
207 | return $_[1]; |
208 | }; |
209 | |
210 | The response_filter subroutine is designed for use inside dispatch subroutines. |
211 | |
212 | It creates and returns a response filter object to the dispatcher, |
213 | encapsulating the block passed to it as the filter routine to call. See |
214 | L</DISPATCH STRATEGY> below for how a response filter affects dispatch. |
215 | |
3583ca04 |
216 | =head1 DISPATCH STRATEGY |
217 | |
81a5b03e |
218 | =head2 Description of the dispatcher object |
219 | |
220 | Web::Simple::Dispatcher objects have three components: |
221 | |
222 | =over 4 |
223 | |
224 | =item * match - an optional test if this dispatcher matches the request |
225 | |
226 | =item * call - a routine to call if this dispatcher matches (or has no match) |
227 | |
228 | =item * next - the next dispatcher to call |
229 | |
230 | =back |
231 | |
232 | When a dispatcher is invoked, it checks its match routine against the |
233 | request environment. The match routine may provide alterations to the |
234 | request as a result of matching, and/or arguments for the call routine. |
235 | |
236 | If no match routine has been provided then Web::Simple treats this as |
237 | a success, and supplies the request environment to the call routine as |
238 | an argument. |
239 | |
240 | Given a successful match, the call routine is now invoked in list context |
241 | with any arguments given to the original dispatch, plus any arguments |
242 | provided by the match result. |
243 | |
244 | If this routine returns (), Web::Simple treats this identically to a failure |
245 | to match. |
246 | |
247 | If this routine returns a Web::Simple::Dispatcher, the environment changes |
248 | are merged into the environment and the new dispatcher's next pointer is |
249 | set to our next pointer. |
250 | |
251 | If this routine returns anything else, that is treated as the end of dispatch |
252 | and the value is returned. |
253 | |
254 | On a failed match, Web::Simple invokes the next dispatcher with the same |
255 | arguments and request environment passed to the current one. On a successful |
256 | match that returned a new dispatcher, Web::Simple invokes the new dispatcher |
257 | with the same arguments but the modified request environment. |
258 | |
259 | =head2 How Web::Simple builds dispatcher objects for you |
260 | |
261 | In the case of the Web::Simple L</dispatch> export the match is constructed |
262 | from the subroutine prototype - i.e. |
263 | |
264 | sub (<match specification>) { |
265 | <call code> |
266 | } |
267 | |
268 | and the 'next' pointer is populated with the next element of the array, |
269 | expect for the last element, which is given a next that will throw a 500 |
270 | error if none of your dispatchers match. If you want to provide something |
271 | else as a default, a routine with no match specification always matches, so - |
272 | |
273 | sub () { |
274 | [ 404, [ 'Content-type', 'text/plain' ], [ 'Error: Not Found' ] ] |
275 | } |
276 | |
277 | will produce a 404 result instead of a 500 by default. You can also override |
278 | the L<Web::Simple::Application/_build_final_dispatcher> method in your app. |
279 | |
280 | Note that the code in the subroutine is executed as a -method- on your |
281 | application object, so if your match specification provides arguments you |
282 | should unpack them like so: |
283 | |
284 | sub (<match specification>) { |
285 | my ($self, @args) = @_; |
286 | ... |
287 | } |
288 | |
289 | =head2 Web::Simple match specifications |
290 | |
291 | =head3 Method matches |
292 | |
293 | =head3 Path matches |
294 | |
295 | =head3 Extension matches |
296 | |
297 | =head3 Combining matches |
298 | |
3583ca04 |
299 | =cut |
7401408e |
300 | |
5c33dda5 |
301 | 1; |