Commit | Line | Data |
83cea649 |
1 | =head1 NAME |
2 | |
3 | Catalyst::Manual::Tutorial - Getting started with Catalyst |
4 | |
5 | =head1 DESCRIPTION |
6 | |
7 | This document aims to get you up and running with Catalyst. |
8 | |
83cea649 |
9 | =head2 Installation |
10 | |
61b1e958 |
11 | The first step is to install Catalyst, and the simplest way to do this |
12 | is to install the Catalyst bundle from CPAN: |
83cea649 |
13 | |
d538823f |
14 | $ perl -MCPAN -e 'install Task::Catalyst' |
83cea649 |
15 | |
61b1e958 |
16 | This will retrieve Catalyst and a number of useful extensions and |
17 | install them for you. |
83cea649 |
18 | |
19 | |
20 | =head2 Setting up your application |
21 | |
61b1e958 |
22 | Catalyst includes a helper script, C<catalyst.pl>, that will set up a |
23 | skeleton application for you: |
83cea649 |
24 | |
387e4c50 |
25 | $ catalyst MyApp |
26 | |
27 | created "MyApp" |
28 | created "MyApp/script" |
29 | created "MyApp/lib" |
30 | created "MyApp/root" |
31 | created "MyApp/root/static" |
32 | created "MyApp/root/static/images" |
33 | created "MyApp/t" |
34 | created "MyApp/t/Model" |
35 | created "MyApp/t/View" |
36 | created "MyApp/t/Controller" |
37 | created "MyApp/lib/MyApp" |
38 | created "MyApp/lib/MyApp/Model" |
39 | created "MyApp/lib/MyApp/View" |
40 | created "MyApp/lib/MyApp/Controller" |
41 | created "MyApp/lib/MyApp.pm" |
42 | created "MyApp/Build.PL" |
43 | created "MyApp/Makefile.PL" |
44 | created "MyApp/README" |
45 | created "MyApp/Changes" |
46 | created "MyApp/t/01app.t" |
47 | created "MyApp/t/02pod.t" |
48 | created "MyApp/t/03podcoverage.t" |
49 | created "MyApp/root/static/images/catalyst_logo.png" |
50 | created "MyApp/root/static/images/btn_120x50_built.png" |
51 | created "MyApp/root/static/images/btn_120x50_built_shadow.png" |
52 | created "MyApp/root/static/images/btn_120x50_powered.png" |
53 | created "MyApp/root/static/images/btn_120x50_powered_shadow.png" |
54 | created "MyApp/root/static/images/btn_88x31_built.png" |
55 | created "MyApp/root/static/images/btn_88x31_built_shadow.png" |
56 | created "MyApp/root/static/images/btn_88x31_powered.png" |
57 | created "MyApp/root/static/images/btn_88x31_powered_shadow.png" |
58 | created "MyApp/root/favicon.ico" |
59 | created "MyApp/script/myapp_cgi.pl" |
60 | created "MyApp/script/myapp_fastcgi.pl" |
61 | created "MyApp/script/myapp_server.pl" |
62 | created "MyApp/script/myapp_test.pl" |
63 | created "MyApp/script/myapp_create.pl" |
83cea649 |
64 | |
61b1e958 |
65 | This creates the directory structure shown, populated with skeleton |
66 | files. |
83cea649 |
67 | |
83cea649 |
68 | =head2 Testing out the sample application |
69 | |
70 | You can test out your new application by running the server script that |
07e73f82 |
71 | Catalyst provides: |
83cea649 |
72 | |
387e4c50 |
73 | $ cd MyApp |
74 | $ script/myapp_server.pl |
b33ed88c |
75 | |
b460ad78 |
76 | [...] [catalyst] [debug] Debug messages enabled |
387e4c50 |
77 | [...] [catalyst] [debug] Loaded plugins: |
78 | .------------------------------------------------------------------------------. |
79 | | Catalyst::Plugin::Static::Simple | |
80 | '------------------------------------------------------------------------------' |
b33ed88c |
81 | [...] [catalyst] [debug] Loaded dispatcher "Catalyst::Dispatcher" |
61b1e958 |
82 | [...] [catalyst] [debug] Loaded engine "Catalyst::Engine::HTTP" |
387e4c50 |
83 | [...] [catalyst] [debug] Found home "/home/users/me/MyApp" |
84 | [...] [catalyst] [debug] Loaded Private actions: |
85 | .--------------------------------------+---------------------------------------. |
86 | | Private | Class | |
87 | +--------------------------------------+---------------------------------------+ |
88 | | /default | MyApp | |
89 | '--------------------------------------+---------------------------------------' |
90 | |
91 | [...] [catalyst] [info] MyApp powered by Catalyst 5.5 |
92 | You can connect to your server at http://localhost:3000 |
b33ed88c |
93 | |
94 | (Note that each line logged by Catalyst begins with a timestamp, which has |
61b1e958 |
95 | been replaced here with "C<...>" so that the text fits onto the lines.) |
b460ad78 |
96 | |
61b1e958 |
97 | The server is now waiting for you to make requests of it. Try using |
98 | telnet to manually make a simple GET request of the server (when |
99 | telnet responds with "Escape character is '^]'.", type "GET / HTTP/1.0" |
100 | and hit return twice): |
83cea649 |
101 | |
102 | $ telnet localhost 3000 |
103 | Trying 127.0.0.1... |
104 | Connected to localhost. |
105 | Escape character is '^]'. |
106 | GET / HTTP/1.0 |
107 | |
387e4c50 |
108 | HTTP/1.0 200 OK |
109 | Date: Mon, 07 Nov 2005 14:57:39 GMT |
110 | Content-Length: 5525 |
111 | Content-Type: text/html; charset=utf-8 |
83cea649 |
112 | Status: 200 |
387e4c50 |
113 | X-Catalyst: 5.5 |
83cea649 |
114 | |
387e4c50 |
115 | [...] |
83cea649 |
116 | Connection closed by foreign host. |
117 | $ |
118 | |
387e4c50 |
119 | You can see the full welcome message by visting |
120 | http://localhost:3000/ with your browser. |
121 | |
83cea649 |
122 | More trace messages will appear in the original terminal window: |
123 | |
387e4c50 |
124 | [...] [catalyst] [debug] ********************************** |
125 | [...] [catalyst] [debug] * Request 1 (0.063/s) [2148] |
126 | [...] [catalyst] [debug] ********************************** |
127 | [...] [catalyst] [debug] Arguments are "" |
61b1e958 |
128 | [...] [catalyst] [debug] "GET" request for "" from localhost |
387e4c50 |
129 | [...] [catalyst] [info] Request took 0.046883s (21.330/s) |
130 | .------------------------------------------------------------------+-----------. |
131 | | Action | Time | |
132 | +------------------------------------------------------------------+-----------+ |
133 | | /default | 0.000000s | |
134 | '------------------------------------------------------------------+-----------' |
83cea649 |
135 | |
136 | The server will continue running until you interrupt it. |
137 | |
138 | The application can also be tested from the command line using the generated |
387e4c50 |
139 | helper script, C<script/myapp_test.pl>. |
83cea649 |
140 | |
8d47005f |
141 | =head2 Getting started |
83cea649 |
142 | |
8d47005f |
143 | So you picked Catalyst. Good choice. I assume you've installed it as |
144 | well. For this tutorial you will also need the following modules: |
83cea649 |
145 | |
8d47005f |
146 | L<Catalyst::Plugin::Session> |
83cea649 |
147 | |
8d47005f |
148 | L<Catalyst::Plugin::Session::Store::File> |
83cea649 |
149 | |
8d47005f |
150 | L<Catalyst::Plugin::Session::State::Cookie> |
83cea649 |
151 | |
8d47005f |
152 | L<Catalyst::Plugin::Authentication> |
83cea649 |
153 | |
8d47005f |
154 | L<Catalyst::Plugin::Authorization::Roles> |
83cea649 |
155 | |
8d47005f |
156 | L<DBD::SQLite> |
83cea649 |
157 | |
8d47005f |
158 | ... |
159 | |
160 | To get started all you need to do is type: |
161 | |
162 | B<catalyst.pl tutorial> |
163 | |
164 | This should create a directory called F<tutorial> and fill it with the |
165 | default (standard) Catalyst installation. Change to this directory |
166 | because we will be running all further commands from inside the |
167 | F<tutorial> directory. |
168 | |
169 | If you now run the built-in mini-server with |
170 | B<script/tutorial_server.pl>, it will show some standard debug messages |
171 | in the console screen (more about those in a minute), and then inform |
172 | you that you can now connect to the test server on port 3000. Point your |
173 | browser at localhost:3000 to see the built-in catalyst welcome screen. |
174 | |
175 | The other important thing catalyst.pl did was create your root |
176 | controller. This file is a standard perl module like all the other |
177 | controllers that you might add to your application. It lives in the |
178 | F<lib/> directory, and will have the same name as you supplied to the |
179 | command above, in our case it is F<tutorial.pm>. Alongside this file is |
180 | a directory of the same name, which is the top level namespace for the |
181 | entire application. Thus every other module we create will be |
182 | "tutorial::something"; |
183 | |
184 | The root controller is used to load plugins, to configure the |
185 | application and its plugins, and for generic private actions. We will |
186 | explain more about those later. |
187 | |
188 | =head2 Debugging |
189 | |
190 | The simplest way to debug your Catalyst application is to run it using |
191 | the built-in mini-server as described in L</Getting Started>. |
192 | |
193 | If you want to output any debugging information to the console, then |
194 | call C<< $context->log->debug() >>, passing it a string to output. For |
195 | data structures, use Data::Dumper and call C<< |
196 | $context->log->debug(Dumper($structure)) >> |
197 | |
198 | =head2 Model/View/Controller |
199 | |
200 | The recommended method for code organization in a Catalyst application |
201 | is known as the "Model View Controller" design pattern (also referred to |
202 | "MVC"). The point of the MVC pattern is to divorce the dependencies of |
203 | parts of the application from each other, and give them standard |
204 | interfaces. Following this theory of organization should give your code |
205 | all the benefits of modularity. The main benefits are interchangability |
206 | of parts and reusable code. |
207 | |
208 | Thus you could replace your file data storage with a database or your |
209 | oracle database with a mysql database and not have to change any of your |
210 | controlling or view logic. Or you could later decide to output |
211 | information from your application as RSS instead of HTML just by adding |
212 | a new view module. |
213 | |
214 | =head3 Model |
215 | |
216 | Models deal with the storage of data. For a complex website, you may |
217 | need multiple varied data sources, each will have it's own model class |
218 | that provides an abstracted interface to it. In this tutorial we are |
219 | going to be using a simple database. |
220 | |
221 | =head3 View |
222 | |
223 | Views are used to display information to the user. In a web framework, |
224 | it is generally used to output HTML to the browser. As mentioned |
225 | previously, views can also be used to output RSS or any other kind of |
226 | data format. One easy way to do this with Catalyst is to use a |
227 | templating system such as Template Toolkit. If outputting HTML is all |
228 | you are going to do, then you will probably only need one view. |
229 | |
230 | =head3 Controller |
231 | |
232 | A controller deals with reacting to user choices, and thus controls what |
233 | the application does. Since this is a web framework, Catalyst |
234 | controllers are frequently used to react directly to URLs requested by |
235 | the user. This tutorial will describe the simplest way of using |
236 | controllers, where each path or part of a path is assigned its own |
237 | action (or subroutine). More complex controlling mechanisms will be |
238 | mentioned briefly, and can be read about in detail in the manual. |
239 | |
240 | |
241 | =head2 Controlling |
242 | |
243 | Now lets write our first bit of application code. First, we would like |
244 | our application to greet our users. We'll assume for now that our users |
245 | will be sent to the I<users/greet> URL. To create a controller that |
246 | serves the I<users> namespace, we run the following command in our |
247 | F<tutorial> directory: |
248 | |
249 | B<script/tutorial_create.pl controller Users> |
250 | |
251 | This will create a Users.pm in F<lib/tutorial/Controller>. Open this |
252 | file in an editor and take a look. You will notice there is some |
253 | commented out code which we will ignore for now. To make something |
254 | happen when our URL is visited, we will write a "greet" action which |
255 | looks like this: |
256 | |
257 | sub greet : Local |
258 | { |
259 | my ($self, $context) = @_; |
260 | |
261 | my $name = $context->req->params->{name}; |
262 | $context->log->debug("Got name: $name\n"); |
263 | |
264 | if(!$name) |
265 | { |
266 | $context->stash->{message} = 'Please fill in a name!'; |
61b1e958 |
267 | } |
8d47005f |
268 | else |
269 | { |
270 | $context->stash->{message} = "Hello $name!"; |
271 | } |
272 | $context->stash->{template} = 'greet.tt'; |
273 | } |
274 | |
275 | Whew! So, what does all this do? Lets take it one step at a time. |
276 | The subroutine declaration gives the action a name. To the right of the |
277 | name there is an attribute type that looks like this: " : Local". That |
278 | defines which URIs will translate to this action. "Local", matches |
279 | exactly one URI: /users/greet. The URI matched by "Local" is composed |
280 | from the namespace minus the tutorial::controller portion, that is |
281 | common to all controllers, and the action name itself. Because it is a |
282 | URI, we use forward slashes instead of double colons. So, in summary, |
283 | when a user requests http://localhost:3000/users/greet" the "greet" |
284 | action defined above in the users controller will be executed. |
285 | |
286 | The second line retrieves the parameters Catalyst gives us when it calls |
287 | our method. The first is the instance of our Users class, and the second |
288 | is commonly called the context. The context is the magical object |
289 | containing any information you need from catalyst, or want to send to |
290 | it. You will see it used frequently in Catalyst applications, and a list |
291 | of all its methods is available in the L<Catalyst> POD. |
292 | |
293 | On the third line we use the ->params method of the $context request |
294 | object to retrieve one of the query parameters, just like in L<CGI>. |
295 | |
296 | On the fourth, we make a debug output of this object on the server console. |
297 | (NB where does ->debug go if we're running under CGI or mod_perl?) |
298 | (NB what is $self good for under CGI/mod_perl/anything?) |
299 | |
300 | Next, we check if the name field contains anything (or is "true"), if it |
301 | isnt, we assign an error message to a "message" field in the stash. The |
302 | stash is yet another method of the context object, it allows us to pass |
303 | data on to other methods we call later, most usefully the View modules. |
304 | |
305 | If the username did contain a value, then we just set our message to |
306 | greet the user by name. |
307 | |
308 | Finally, we set the special "template" variable in the stash to the name |
309 | of the template we want our view to use to display this page. |
310 | (NB doing the param name checking this way will also complain when we |
311 | visit the form for the first time, check for presence of params at all?) |
312 | |
313 | =head2 Viewing |
314 | |
315 | Ok, so reacting and checking the users data is all fine, but how do we |
316 | actually display the page/form in the first place, and our results? As |
317 | previously mentioned, we'll use Template Toolkit for our viewing. To |
318 | create out TT based view, just run the following command: |
319 | |
320 | B<script/tutorial_create.pl view TToolkit TT> |
321 | |
322 | Notice that this time we not only gave it the type of module we wanted |
323 | to create (a view), and a name, but also a third argument, "TT". This is |
324 | a Catalyst helper module, which will make a standard template toolkit |
325 | module for you. And that's all you need to do there. |
326 | |
327 | To use the view, the easiest way is to set up a standard "end" action. |
328 | This a private action which will not be matched to a path like our |
329 | "greet" action, but instead will be called after all other processing is |
330 | done. Only one end action will be called, if there is one in a |
331 | controller, it will be prefered over one in the application module, and |
332 | so on. |
333 | |
334 | Since we're writing a simple application, just add an end action like |
335 | this to F<tutorial.pm>: |
336 | |
337 | sub end : Private |
338 | { |
339 | my ($self, $context) = @_; |
340 | $context->forward('tutorial::View::TToolkit'); |
341 | } |
342 | |
343 | The first line declares the end sub, and marks it as a Private action. |
344 | (The second and last attribute type we'll be using). The second line |
345 | collects our standard parameters as shown in the controller's greet action. |
346 | |
347 | The third line directs Catalyst to pass processing on to our TToolkit |
348 | view. The forward method, when just passed a class name, calls process |
349 | on that classs. The standard TT view's process method renders the |
350 | template named in the templare variable in the stash, using all the |
351 | other variables in the stash as values to fill it in. |
352 | |
353 | NB: This is such a common way to end you processing that there is a |
354 | plugin which does it for you: L<Catalyst::Plugin::DefaultEnd>. |
355 | |
356 | Template Toolkit also has access to the entire context object via "c", |
357 | for example, using [% c.config.name %] in our template will output |
358 | "tutorial", our project name. |
359 | |
360 | All that remains is to create a simple template called "greet.tt", |
361 | containing a form with a text field called "name", and place it in the |
362 | F<root> directory. By default, templates are searched for here, but we |
363 | can change that, which brings us to.. |
364 | |
365 | (NB Can we ask catalyst which paths exist to provide a dynamic list of |
366 | them? (eg greet, age etc .. regexed paths??) |
367 | |
368 | =head2 Configuring |
369 | |
370 | As previously mentioned, the configuration of modules, plugins and so on |
371 | is done in the main application file. This is especially true for bits |
372 | which need to be done before an instance of them is created, for example |
373 | Template Toolkit. |
374 | |
375 | The TT View looks for its templates in the F<root> directory by default. |
376 | Since this is also the directory that static files go in, we'd rather |
377 | have a separate F<templates> directory. To do this, change the config |
378 | call in F<tutorial.pm> like this: |
379 | |
380 | __PACKAGE__->config( name => 'tutorial', |
381 | 'View::TToolkit' => { |
382 | 'INCLUDE_PATH' => __PACKAGE__->path_to('templates') |
383 | } |
384 | ); |
385 | |
386 | And move the F<greet.tt> file from F<root> to the F<templates> directory |
387 | (after creating it). |
388 | |
389 | Now we can run our application again by killing (ctrl-c) and restarting |
390 | B<script/tutorial_server.pl>. Try connecting to |
391 | I<localhost:3000/users/greet> with a browser and see what happens. What |
392 | happens if you try to visit I<localhost:3000/users> ? |
393 | |
394 | =head2 Users and Authenticating |
395 | |
396 | One of the many reasons to write dynamic websites instead of just using static |
397 | HTML, is to allow us to produce different content for different users, as well |
398 | as just restricting access to pages (which we could do with just Apaches |
399 | htpasswd system). |
400 | |
401 | In this tutorial, we will just be using basic authentication, when writing |
402 | a real application, you'll want to use a database or other secure store to |
403 | contain your user data. |
404 | |
405 | To add authentication, all we need to do is add the |
406 | L<Catalyst::Plugin::Authentication> module to our main application file. Then |
407 | we need to pick a storage method (one of the |
408 | L<Catalyst::Plugin::Authentication::Store> modules), and a method of verifying |
409 | the users credentials (one of the |
410 | L<Catalyst::Plugin::Authentication::Credential> modules), so just edit |
411 | F<tutorial.pm> to look like this: |
412 | |
413 | use Catalyst qw/-Debug Static::Simple Authentication |
414 | Authentication::Store::Minimal |
415 | Authentication::Credential::Password/; |
416 | |
417 | To configure, add some users to the config call, for example: |
418 | |
419 | authentication => { 'users' => |
420 | { 'fred' => |
421 | { 'password' => 'fred1234', |
422 | } |
423 | } |
424 | } |
425 | |
426 | Generally, setting up configuration data for plugins is done based on the |
427 | type of plugin. Check the documentation of the plugin for exact details. The |
428 | details of this one are in L<Catalyst::Plugin::Authentication::Store::Minimal>. |
429 | |
430 | Since our user data is in the config, we can update it at runtime, and thus |
431 | add users dynamically. (Of course, to keep them permanently we'll need to |
432 | export our data to disk and read it back into the config on startup) |
433 | |
434 | To allow creation of new users we'll add a create action to our Users |
435 | controller. |
436 | |
437 | sub create : Local |
438 | { |
439 | my ($self, $context) = @_; |
440 | my ($username, $passwd1, $passwd2) = map { $context->req->param($_)} |
441 | ('username', 'password', 'passwordverify'); |
442 | |
443 | if($username && $passwd1 && $passwd2) |
444 | { |
445 | if($context->config->{authentication}{users}{$username}) |
446 | { |
447 | $context->stash->{message} = 'Sorry that user already exists'; |
448 | $context->stash->{username} = $username; |
449 | } |
450 | elsif($passwd1 eq $passwd2) |
451 | { |
452 | $context->config->({%{$context->config}, |
453 | ($username => { password => $passwd1}}); |
454 | $context->stash->{message} = 'User created!'; |
455 | } |
456 | else |
457 | { |
458 | $context->stash->{username} = $username; |
459 | $context->stash->{message} = 'Passwords don't match!'; |
460 | } |
461 | } |
462 | $context->stash->{template} = 'usercreate.tt'; |
463 | } |
464 | |
465 | All this is doing is checking that all the appropriate fields are filled, |
466 | the password fields contain the same data, and then adding the user to the |
467 | config hash. All the checks produce a message which can be displayed to |
468 | the user via the View. |
469 | |
470 | So our that users can login, we need a login page: |
471 | |
472 | sub login : Local |
473 | { |
474 | my ($self, $context) = @_; |
475 | $context->stash->{template} = 'userlogin.tt'; |
476 | if(!$context->login()) |
477 | { |
478 | $context->stash->{message} = 'Login failed.'; |
61b1e958 |
479 | } |
8d47005f |
480 | } |
481 | |
482 | Verrrry simple. Since Credential::Password's "login" call extracts the |
483 | username/password data from the query itself (assuming we use a standard |
484 | name for our form fields), we don't have to do anything but call it. |
485 | |
486 | To keep the user logged in, all we need to do is add the Session modules to |
487 | our collection, and the Auth modules will automatically use them; |
488 | |
489 | use Catalyst qw/-Debug Static::Simple Authentication |
490 | Authentication::Store::Minimal |
491 | Authentication::Credential::Password |
492 | Session Session::Store::File Session::State::Cookie/; |
493 | |
494 | Magic! |
495 | |
496 | =head2 Authorising |
497 | |
498 | Authentication is about verifying users, Authorisation is about allowing |
499 | them to do things. Catalyst currently has two Authorisation modules, |
500 | Roles and ACL. The Roles module allows you to define groups which you |
501 | can assign your users to, and then allow access to areas of your website |
502 | to the groups. The ACL module lets you do more fine grained |
503 | access/restriction by allowing of denying access however you like. (It |
504 | also supports Roles as done by the roles module.) |
505 | |
506 | Adding Roles via the Minimal store we are already using is quite simple, |
507 | we just add a roles key to each user, defining the names of the roles |
508 | they belong to. |
509 | |
510 | authentication => { 'users' => |
511 | { 'fred' => |
512 | { 'password' => 'fred1234', |
513 | 'roles' => ['admin'] |
514 | } |
515 | } |
516 | } |
517 | |
518 | We need an interface for our admins to administer the roles, i.e. assign |
519 | the users to groups. To restrict access to certain actions, we just need |
520 | to call C<< $context->check_user_roles() >> in each action. So we can |
521 | make a restricted I<localhost:3000/users/groups> page like this: |
522 | |
523 | sub groups : Local |
524 | { |
525 | my ($self, $context) = @_; |
526 | if($context->check_user_roles('admin')) |
527 | { |
528 | # Now we can do things only an admin will see |
529 | if(my $params = $context->req->params) |
530 | { |
531 | my $users = $context->config->{authentication}{users}; |
532 | foreach my $u (keys %$params) |
533 | { |
534 | $users->{$u}{roles} = $params->{$u} if($users->{$u}); |
535 | } |
536 | $context->stash->{message} = 'Updated user roles!'; |
537 | } |
538 | else |
539 | { |
540 | $context->stash->{users} = $context->config->{authentication}; |
541 | } |
542 | $context->stash->{template} = 'usersgroups.tt'; |
61b1e958 |
543 | } |
8d47005f |
544 | else |
545 | { |
546 | $context->stash->{message} = 'Admins Only!'; |
547 | $context->stash->{template} = 'error.tt'; |
548 | } |
549 | } |
550 | |
551 | What we are doing here is checking whether the logged in user (used by |
552 | default in the check_user_roles method), is a member of the admin group. |
553 | If it is, then we display the usergroups template, and update the users |
554 | hash as required. Otherwise, we just show the user an error page. |
555 | |
556 | And that's all there is to it. |
557 | |
558 | =head2 Data Storage (Modelling) |
559 | |
560 | Whether we want our users to be able to contribute to our website, or just |
561 | create it from changeable data, we need to store the data somewhere. Generally |
562 | this is done using a database, models can also be other data sources, for |
563 | example another website, or RSS feeds. |
564 | |
565 | If you have or want a database, there are still choices to be made, there are |
566 | several modules about for accessing databases via OO. The best known are |
567 | probably L<Class::DBI> and L<DBIx::Class>. Catalyst supports making models |
568 | using either of these. |
569 | |
570 | For a simple example, we will allow our users to store their favourite |
571 | greeting in our database. Create a table called "greetings" in a database, |
572 | that contains a "user" field and a "greeting" field. The simplest way to |
573 | create a model of your database is to use these helper modules, for example |
574 | with L<DBIx::Class>: |
575 | |
576 | B<script/tutorial_create.pl model UserData DBIC dbi:SQLite:/path/to/mydb.db> |
577 | |
578 | This will cause the DBIx::Class Loader to inspect your database, and create a |
579 | module in the Model::UserData namespace for each table in your database. |
580 | |
581 | Now we need a form for our users to enter/edit their personal greetings in, |
582 | we'll make a I<localhost:3000/users/editgreeting> page: |
583 | |
584 | sub editgreeting : Local |
585 | { |
586 | my ($self, $context) = @_; |
587 | if($context->req->params->{greeting}) |
588 | { |
589 | if(!$context->user_exists) |
590 | { |
591 | $context->stash->{message} = "You're not logged in!"; |
592 | } |
593 | else |
594 | { |
595 | my $grtable = $context->model('UserData::Greetings'); |
596 | my $record = $grtable->find_or_create(user => $context->user->id); |
597 | $record->greeting($context->req->params->{greeting}; |
598 | $record->update; |
599 | $context->stash->{message} = 'Greeting updated'; |
600 | } |
601 | } |
602 | $context->stash->{template} = 'usersgreeting.tt'; |
603 | } |
83cea649 |
604 | |
8d47005f |
605 | Using C<< $context->user_exists >> from the Authentication plugin, this checks |
606 | whether the user is logged in already. If they are, if they are, and they have |
607 | entered a new greeting, we use DBIx::Class' C<find_or_create> to fetch or |
608 | create a new record in the greetings table for the user. Once we have the |
609 | record, we change the value of the greeting field, and call C<update> to store |
610 | the new value in the database. |
eff5f524 |
611 | |
8d47005f |
612 | =head2 Engines (Apache and FastCGI) |
83cea649 |
613 | |
8d47005f |
614 | Now that we have the basics together, we can try running our application on a |
615 | "real" server instead of just using the test server that catalyst comes |
616 | with. L<Catalyst::Engine> is the module used to implement various types of |
617 | servers to run it on. The currect popular ones are Apache and FastCGI. To |
618 | force the use of a particular engine we can use the -Engine flag to Catalyst: |
b460ad78 |
619 | |
8d47005f |
620 | use Catalyst qw/-Engine=Apache/; |
b460ad78 |
621 | |
8d47005f |
622 | or |
b460ad78 |
623 | |
8d47005f |
624 | use Catalyst qw/-Engine=FastCGI/; |
b460ad78 |
625 | |
8d47005f |
626 | =head3 Apache |
b460ad78 |
627 | |
8d47005f |
628 | Apache also needs configuring, we need to tell it to load your |
629 | application. You can either use Catalyst for your entire website, or |
630 | subsections. Use the Location directive to choose a path to run your |
631 | application under: |
b460ad78 |
632 | |
8d47005f |
633 | <Location /> |
634 | SetHandler perl-script |
635 | PerlResponseHandler MyApp |
636 | </Location> |
b460ad78 |
637 | |
8d47005f |
638 | You will need to install the perl modules of your application into one of |
639 | perls library directories, as listed by B<perl -V>, so that Apache can find |
640 | them. Alternatively you can use the C<PerlSwitches> directive to tell Apache |
641 | where to look: |
b460ad78 |
642 | |
8d47005f |
643 | PerlSwitches -I/path/to/MyApp/ |
b460ad78 |
644 | |
8d47005f |
645 | These instructions are for using Apache2 and mod_perl 2.0. If you are using |
646 | mod_perl 1.3 or 1.99, please refer to either L<Catalyst::Engine::Apache::MP13> |
647 | or L<Catalyst::Engine::Apache2::MP19> for slightly different ways to do it. |
b460ad78 |
648 | |
8d47005f |
649 | If you wish to ensure that Apache pre-loads your application, use the |
650 | PerlModule directive. This means that there will be less of a delay when your |
651 | application is accessed. |
b460ad78 |
652 | |
8d47005f |
653 | PerlModule MyApp |
b460ad78 |
654 | |
8d47005f |
655 | =head3 FastCGI |
b460ad78 |
656 | |
8d47005f |
657 | These instructions apply to the use of C<mod_fastcgi> under Apache (either 1 or 2 series). |
b460ad78 |
658 | |
8d47005f |
659 | There are 3 ways to attach a program to a URL with C<mod_fastcgi>; we'll examine all of them, and explain how to avoid having the C<myapp_fastcgi.pl> substring in the user-visible URLs. |
b460ad78 |
660 | |
8d47005f |
661 | In all of these examples, we assume that the C<DocumentRoot> is C</var>, that our app is called C<MyApp> and is kept in C</usr>, that you want the users to access the app either from the root of the server-uri-space, or from C</theapp>. We also assume that the general FastCGI settings (C<FastCgiIpcDir>, loading the module) are already correct (they don't depend on Catalyst or your application layout). |
83cea649 |
662 | |
8d47005f |
663 | =head4 static application |
664 | |
665 | In this setup, you tell C<mod_fastcgi> that a particular I<file> is to be run as a FastCGI handler. Put this somewhere in Apache's configuration: |
666 | |
667 | FastCgiServer /usr/apps/MyApp/script/myapp_fastcgi.pl |
668 | Alias / /usr/apps/MyApp/script/myapp_fastcgi.pl/ |
669 | |
670 | If you want your app under C</theapp>, change the C<Alias> line to: |
671 | |
672 | Alias /theapp /usr/apps/MyApp/script/myapp_fastcgi.pl |
673 | |
674 | Note the detail of the trailing C</ >: this is a general rule af the C<Alias> directive, both sides must end with C</ >, or both must not; you can't have one with C</ > and the other without, or strange things happen. |
675 | |
676 | =head4 dynamic application |
677 | |
678 | In this setup, you tell C<mod_fastcgi> that certain files are to be treated as FastCGI handlers, in the same way you have to tell C<mod_cgi>. Put this in the configuration: |
679 | |
680 | FastCgiConfig -autoUpdate |
681 | |
682 | <Directory /usr/apps/MyApp/script> |
683 | Options +ExecCGI |
684 | <Files *_fastcgi.pl> |
685 | SetHandles fastcgi-script |
686 | </Files> |
687 | </Directory> |
83cea649 |
688 | |
8d47005f |
689 | Alias / /usr/apps/MyApp/script/myapp_fastcgi.pl/ |
b460ad78 |
690 | |
8d47005f |
691 | Again, if you want your app under C</theapp>, change the C<Alias> line to: |
b460ad78 |
692 | |
8d47005f |
693 | Alias /theapp /usr/apps/MyApp/script/myapp_fastcgi.pl |
b460ad78 |
694 | |
8d47005f |
695 | =head4 external server |
b460ad78 |
696 | |
8d47005f |
697 | In this setup, the application is started separately from Apache, and communicates via a socket with C<mod_fastcgi>. This can be useful if you need to have a particular environment for your application (maybe different between applications), or you want to run them on different machines, or under different users for security reasons. |
b460ad78 |
698 | |
8d47005f |
699 | If you want to use a UNIX socket (on the filesystem), put this in Apache's configuration: |
b460ad78 |
700 | |
8d47005f |
701 | FastCgiExternalServer /tmp/somewhere -socket /tmp/myapp-socket |
702 | Alias / /tmp/somewhere/ |
b460ad78 |
703 | |
8d47005f |
704 | Note that C</tmp> should I<not> exist: it's just a name to connect the two parts. |
b460ad78 |
705 | |
8d47005f |
706 | Again, if you want your app under C</theapp>, change the C<Alias> line to: |
b460ad78 |
707 | |
8d47005f |
708 | Alias /theapp /tmp/somewhere |
709 | |
710 | Then start your Catalyst application: |
711 | |
712 | $ cd /usr/apps/MyApp |
713 | $ ./script/myapp_fastcgi -l /tmp/myapp-socket |
714 | |
715 | If you want to use a TCP socket, simply change the C</tmp> to a C<host:port> pair, both in Apache's configuration and on the command line of your application. |
716 | |
717 | =head2 Upgrading |
718 | |
719 | Upgrading your application to newer Catalyst versions is quite simple. After |
720 | installing the new Catalyst package, just run: |
721 | |
722 | B<catalyst.pl -scripts> |
723 | |
724 | One level above your application directory. This will update the scripts |
725 | directory only, and leave the rest of your app alone, If you wish to make use |
726 | of other parts of Catalyst that have been updated, leave off the B<-scripts> |
727 | argument, this will cause .new files to appear, for each module that has |
728 | either been updated, or is different to the original because you have changed |
729 | it. To find out what these changes are, type: |
730 | |
731 | B<diff MyApp/lib/MyApp/View/TT.pm MyApp/lib/MyApp/View/TT.pm.new> |
732 | |
733 | for each of the changed files. (This is a Unix command, Windows users will |
734 | need to find some equivalent). Copy any changes you need into your original |
735 | file, then remove the .new files. (This makes life less complicated when the |
736 | next upgrade comes around.) |
737 | |
738 | =head1 AUTHOR |
739 | |
740 | Jess Robinson, C<jrobinson@cpan.org> |
741 | Andrew Ford, C<A.Ford@ford-mason.co.uk> |
742 | Marcus Ramberg, C<mramberg@cpan.org> |
b460ad78 |
743 | |
744 | Please send comments, corrections and suggestions for improvements to |
8d47005f |
745 | jrobinson@cpan.org |
746 | |
747 | =head1 TODO |
748 | |
749 | Add template examples. |
750 | |
751 | Many other things.. |
b460ad78 |
752 | |
83cea649 |
753 | =head1 COPYRIGHT |
754 | |
61b1e958 |
755 | This program is free software, you can redistribute it and/or modify |
756 | it under the same terms as Perl itself. |