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