1# PODNAME: Dancer::Tutorial 2# ABSTRACT: An example to get you dancing 3 4__END__ 5 6=pod 7 8=encoding UTF-8 9 10=head1 NAME 11 12Dancer::Tutorial - An example to get you dancing 13 14=head1 VERSION 15 16version 1.3513 17 18=head1 What is Dancer? 19 20Dancer is a "micro" web framework which is modeled after a Ruby framework called L<Sinatra|http://www.sinatrarb.com> 21that constructs web applications by building a list of HTTP verbs, URLs (called routes) and methods to handle 22that type of traffic to that specific URL. 23 24 use Dancer; 25 26 get '/' => sub { 27 return 'Hello World!'; 28 }; 29 30 start; 31 32This example shows a single HTTP verb "GET" followed by the root URL "/" and an anonymous subroutine which returns 33the string C<"Hello World!"> If you were to run this example, it would display "Hello World!" when you point your 34browser at C<http://localhost:3000>. 35 36=head1 How about a little more involved example? 37 38That's the reason I wrote this tutorial. While I was investigating some Python web frameworks like L<Flask|http://flask.pocoo.org/> 39or L<Bottle|http://bottle.paws.de/docs/dev/index.html> I enjoyed the way they explained step by step how to build an example application 40which was a little more involved that a trivial example. 41 42Using the 43L<Flaskr|https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/> 44sample application as my inspiration (OK, shamelessly plagiarised) I 45translated that application to the Dancer framework so I could better understand how Dancer worked. (I'm learning 46it too!) 47 48So "Dancr" was born. 49 50Dancr is a simple "micro" blog which uses the L<SQLite|http://www.sqlite.org> database engine for simplicity's sake. 51 52=head1 Required perl modules 53 54Obviously you need L<Dancer>. You also need the L<Template Toolkit|Template>, L<File::Slurp>, and L<DBD::SQLite>. 55These all can be installed using your CPAN client, as in: 56 57 cpan Dancer Template File::Slurp DBD::SQLite 58 59=head1 The database 60 61We're not going to spend a lot of time on the database, as it's not really the point of this particular 62tutorial. 63 64Create the database by running the follow from the shell: 65 66 $ cat - | sqlite3 database 67 create table if not exists entries ( 68 id integer primary key autoincrement, 69 title string not null, 70 text string not null 71 ); 72 ^D 73 74The above creates a single table with three columns: I<id>, I<title>, and 75I<text>. The 'I<id>' field is the primary key and will 76automatically get an ID assigned by the database engine when a row is inserted. 77 78We want our application to initialize the database automatically for us when we start it, so open your favorite 79L<text editor|http://www.vim.org> and create a file called 'dancr.pl'. We're going to put the following subroutines 80in that file: 81 82 sub connect_db { 83 my $dbh = DBI->connect("dbi:SQLite:dbname=".setting('database')) or 84 die $DBI::errstr; 85 86 return $dbh; 87 } 88 89 sub init_db { 90 my $db = connect_db(); 91 my $schema = read_file('./schema.sql'); 92 $db->do($schema) or die $db->errstr; 93 } 94 95Nothing too fancy in here, I hope. Standard DBI except for the C<setting('database')> thing - more on that in a bit. 96For now, just assume that the expression evaluates to file location for the database file. 97 98(Note that you may want to look at the L<Dancer::Plugin::Database> module for an 99easy way to configure and manage database connections for your Dancer apps, but 100the above will suffice for this tutorial.) 101 102=head1 Our first route handler 103 104Let's tackle our first route handler now, the one for the root URL '/'. This is what it looks like: 105 106 get '/' => sub { 107 my $db = connect_db(); 108 my $sql = 'select id, title, text from entries order by id desc'; 109 my $sth = $db->prepare($sql) or die $db->errstr; 110 $sth->execute or die $sth->errstr; 111 template 'show_entries.tt', { 112 'msg' => get_flash(), 113 'add_entry_url' => uri_for('/add'), 114 'entries' => $sth->fetchall_hashref('id'), 115 }; 116 }; 117 118As you can see, the handler is created by specifying the HTTP verb 'get' and 119the URL to match, '/' and finally a subroutine to do something once those 120conditions have been satisfied. Something you might not notice right away is 121the semicolon at the end of the route handler. Since the subroutine actually 122is a coderef, it requires a semicolon. 123 124Let's take a closer look at the subroutine. The first few lines are standard 125DBI. The only new concept as part of Dancer is that C<template> directive at 126the end of the handler. That tells Dancer to process the output through one of 127its templating engines. In this case, we're using L<Template Toolkit|Template> 128which offers a lot more flexibility than the simple default Dancer template 129engine. 130 131Templates all go into the C<views/> directory. Optionally, you can create a 132"layout" template which provides a consistent look and feel for all of your 133views. We'll construct our own layout template cleverly named F<main.tt> a 134little later in this tutorial. 135 136What's going on with the hashref as the second argument to the template 137directive? Those are all of the parameters we want to pass into our template. 138We have a C<msg> field which displays a message to the user when an event 139happens like a new entry is posted, or the user logs in or out. It's called a 140"flash" message because we only want to display it one time, not every time the 141/ URL is rendered. 142 143The C<uri_for> directive tells Dancer to provide a URI for that specific route, 144in this case, it is the route to post a new entry into the database. You might 145ask why we don't simply hardcode the C</add> URI in our application or 146templates. The best reason B<not> to do that is because it removes a layer of 147flexibility on where to "mount" the web application. Although the application 148is coded to use the root URL C</> it might be better in the future to locate it 149under its own URL route (maybe C</dancr>?) - at that point we'd have to go 150through our application and the templates and update the URLs and hope we 151didn't miss any of them. By using the C<uri_for> Dancer method, we can easily 152load the application wherever we like and not have to modify the application at 153all. 154 155Finally, the C<entries> field contains a hashref with the results from our 156database query. Those results will be rendered in the template itself, so we 157just pass them in. 158 159So what does the F<show_entries.tt> template look like? This: 160 161 <% IF session.logged_in %> 162 <form action="<% add_entry_url %>" method=post class=add-entry> 163 <dl> 164 <dt>Title: 165 <dd><input type=text size=30 name=title> 166 <dt>Text: 167 <dd><textarea name=text rows=5 cols=40></textarea> 168 <dd><input type=submit value=Share> 169 </dl> 170 </form> 171 <% END %> 172 <ul class=entries> 173 <% IF entries.size %> 174 <% FOREACH id IN entries.keys.nsort %> 175 <li><h2><% entries.$id.title %></h2><% entries.$id.text %> 176 <% END %> 177 <% ELSE %> 178 <li><em>Unbelievable. No entries here so far</em> 179 <% END %> 180 </ul> 181 182Again, since this isn't a tutorial specifically about Template Toolkit, I'm 183going to gloss over the syntax here and just point out the section which starts 184with C<E<lt>ul class=entriesE<gt>> - this is the section where the database 185query results are displayed. You can also see at the very top some discussion 186about a session - more on that soon. 187 188=head1 Other HTTP verbs 189 190There are 8 defined HTTP verbs defined in L<RFC 1912616|http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9>: OPTIONS, GET, 192HEAD, POST, PUT, DELETE, TRACE, CONNECT. Of these, the majority of web 193applications focus on the verbs which closely map to the CRUD (Create, 194Retrieve, Update, Delete) operations most database driven applications need to 195implement. 196 197In addition, the C<PATCH> verb was defined in 198L<RFC5789|http://tools.ietf.org/html/rfc5789>, and is intended as a 199"partial PUT" - sending just the changes required to the entity in question. 200How this would be handled is down to your app, it will vary depending on the 201type of entity in question and the serialization in use. 202 203Dancer currently supports GET, PUT/PATCH, POST, DELETE, OPTIONS which map to 204Retrieve, Update, Create, Delete respectively. Let's take a look now at the 205C</add> route handler which handles a POST operation. 206 207 post '/add' => sub { 208 if ( not session('logged_in') ) { 209 send_error("Not logged in", 401); 210 } 211 212 my $db = connect_db(); 213 my $sql = 'insert into entries (title, text) values (?, ?)'; 214 my $sth = $db->prepare($sql) or die $db->errstr; 215 $sth->execute(params->{'title'}, params->{'text'}) or die $sth->errstr; 216 217 # note: 'flash' keyword imported by Dancer::Plugin::FlashMessage, 218 # not part of Dancer core 219 flash message => 'New entry posted!'; 220 221 redirect '/'; 222 }; 223 224As before, the HTTP verb begins the handler, followed by the route, and a 225subroutine to do something - in this case, it will insert a new entry into the 226database. 227 228The first check in the subroutine is the make sure the user sending the data is 229logged in. If not, the application sends back an error and stops processing. 230Otherwise, we have standard DBI stuff. Let me insert (heh, heh) a blatant plug 231here for always, always using parameterized INSERTs in your application SQL 232statements. It's the only way to be sure your application won't be vulnerable 233to SQL injection. (See L<http://www.bobby-tables.com> for correct INSERT 234examples in multiple languages.) Here we're using the C<params> convenience 235method to pull in the parameters in the current HTTP request. (You can see the 236'title' and 'text' form parameters in the F<show_entries.tt> template above.) 237Those values are inserted into the database, then we set a flash message for 238the user and redirect her back to the root URL. 239 240=head1 Logins and sessions 241 242Dancer comes with a simple in-memory session manager out of the box. It 243supports a bunch of other session engines including YAML, memcached, browser 244cookies and others. For this application we're going to stick with the 245in-memory model which works great for development and tutorials, but won't 246persist across server restarts or scale very well in "real world" production 247scenarios. 248 249=head2 Configuration options 250 251To use sessions in our application, we have to tell Dancer to activate the 252session handler and initialize a session manager. To do that, we add some 253configuration directives toward the top of our dancr.pl file. But there are 254more options than just the session engine we want to set. 255 256 set 'session' => 'Simple'; 257 set 'template' => 'template_toolkit'; 258 set 'logger' => 'console'; 259 set 'log' => 'debug'; 260 set 'show_errors' => 1; 261 set 'startup_info' => 1; 262 set 'warnings' => 1; 263 set 'database' => database; 264 265Hopefully these are fairly self-explanatory. We want the Simple session engine, 266the Template Toolkit template engine, logging enabled (at the 'debug' level 267with output to the console instead of a file), we want to show errors to the 268web browser, log access attempts and log Dancer warnings (instead of silently 269ignoring them) 270 271In a more sophisticated application you would want to put these configuration 272options into a YAML file, but for this tutorial, we're going to keep it simple. 273Dancer also supports the notion of application environments meaning you can 274create a configuration file for your development instance, and another config 275file for the production environment (with things like debugging and showing 276errors disabled perhaps.) Dancer also doesn't impose any limits on what 277parameters you can set using the C<set> syntax. For this application we're 278going to embed our single username and password into the application itself. 279 280 set 'username' => 'admin'; 281 set 'password' => 'password'; 282 283Hopefully no one will ever guess our clever password! Obviously, you will want 284a more sophisticated user authentication scheme in any sort of non-tutorial 285application but this is good enough for our purposes. 286 287=head2 Logging in 288 289Now that Dancr is configured to handle sessions, let's take a look at the URL 290handler for the C</login> route. 291 292 any ['get', 'post'] => '/login' => sub { 293 my $err; 294 295 if ( request->method() eq "POST" ) { 296 # process form input 297 if ( params->{'username'} ne setting('username') ) { 298 $err = "Invalid username"; 299 } 300 elsif ( params->{'password'} ne setting('password') ) { 301 $err = "Invalid password"; 302 } 303 else { 304 session 'logged_in' => true; 305 set_flash('You are logged in.'); 306 return redirect '/'; 307 } 308 } 309 310 # display login form 311 template 'login.tt', { 312 'err' => $err, 313 }; 314 }; 315 316This is the first handler which accepts two different verb types, a GET for a 317human browsing to the URL and a POST for the browser to submit the user's input 318to the web application. Since we're handling two different verbs, we check to 319see what verb is in the request. If it's B<not> a POST, we drop down to the 320C<template> directive and display the F<login.tt> template. 321 322 <h2>Login</h2> 323 <% IF err %><p class=error><strong>Error:</strong> <% err %><% END %> 324 <form action="<% login_url %>" method=post> 325 <dl> 326 <dt>Username: 327 <dd><input type=text name=username> 328 <dt>Password: 329 <dd><input type=password name=password> 330 <dd><input type=submit value=Login> 331 </dl> 332 </form> 333 334This is even simpler than our F<show_entries.tt> template - but wait - there's 335a C<login_url> template parameter and we're only passing in the C<err> 336parameter. Where's the missing parameter? It's being generated and sent to the 337template in a C<before_template_render> hook - we'll come back to that in a 338moment or two. 339 340So the user fills out the F<login.tt> template and submits it back to the 341C</login> route handler. We now check the user input against our application 342settings and if they're incorrect, we alert the user, otherwise the application 343starts a session and sets the C<logged_in> session parameter to the C<true()> 344value. Dancer exports both a C<true()> and C<false()> convenience method which 345we use here. After that, it's another flash message and back to the root URL 346handler. 347 348=head2 Logging out 349 350And finally, we need a way to clear our user's session with the customary 351logout procedure. 352 353 get '/logout' => sub { 354 session->destroy; 355 set_flash('You are logged out.'); 356 redirect '/'; 357 }; 358 359C<session-E<gt>destroy;> is Dancer's way to remove a stored session. We notify 360the user she is logged out and route her back to the root URL once again. 361 362=head1 Layout and static files 363 364We still have a missing puzzle piece or two. First, how can we use Dancer to 365serve our CSS stylesheet? Second, where are flash messages displayed? Third, 366what about the C<before_template_render> hook? 367 368=head2 Serving static files 369 370In Dancer, static files should go into the C<public/> directory, but in the 371application be sure to omit the C<public/> element from the path. For example, 372the stylesheet for Dancr lives in C<dancr/public/css/style.css> but is served 373from L<http://localhost:3000/css/style.css>. 374 375If you wanted to build a mostly static web site you could simply write route 376handlers like this one: 377 378 get '/' => sub { 379 send_file 'index.html'; 380 }; 381 382where index.html would live in your C<public/> directory. 383 384C<send_file> does exactly what it says: it loads a static file, then sends the 385contents of that file to the user. 386 387=head2 Layouts 388 389I mentioned near the beginning of this tutorial that it is possible to create a 390C<layout> template. In Dancr, that layout is called C<main> and it's set up by 391putting in a directive like this: 392 393 set layout => 'main'; 394 395near the top of your web application. What this tells Dancer's template engine 396is that it should look for a file called F<main.tt> in C<dancr/views/layouts/> 397and insert the calls from the C<template> directive into a template parameter 398called C<content>. 399 400For this web application, the layout template looks like this. 401 402 <!doctype html> 403 <html> 404 <head> 405 <title>Dancr</title> 406 <link rel=stylesheet type=text/css href="<% css_url %>"> 407 </head> 408 <body> 409 <div class=page> 410 <h1>Dancr</h1> 411 <div class=metanav> 412 <% IF not session.logged_in %> 413 <a href="<% login_url %>">log in</a> 414 <% ELSE %> 415 <a href="<% logout_url %>">log out</a> 416 <% END %> 417 </div> 418 <% IF msg %> 419 <div class=flash> <% msg %> </div> 420 <% END %> 421 <% content %> 422 </div> 423 </body> 424 </html> 425 426Aha! You now see where the flash message C<msg> parameter gets rendered. You 427can also see where the content from the specific route handlers is inserted 428(the fourth line from the bottom in the C<content> template parameter.) 429 430But what about all those other C<*_url> template parameters? 431 432=head2 Using C<before_template_render> 433 434Dancer has various L<hooks|http://en.wikipedia.org/wiki/Hooking> which provide 435additional flexibility and power. The hooks available are documented in the 436documentation for the L<hook keyword|Dancer/hook>; the one we're interested in 437here is C<before_template_render> which provides a way to manipulate the template 438parameters before they're passed to the engine for processing. 439 440Using this hook, we can generate and set the URIs for the C</login> and 441C</logout> route handlers and the URI for the stylesheet. This is handy for 442situations like this where there are values which are re-used consistently 443across all (or most) templates. 444This cuts down on code-duplication and makes your app easier to maintain over 445time since you only need to update the values in this one place instead of 446everywhere you render a template. 447 448 hook 'before_template_render' => sub { 449 my $tokens = shift; 450 451 $tokens->{'css_url'} = request->base . 'css/style.css'; 452 $tokens->{'login_url'} = uri_for('/login'); 453 $tokens->{'logout_url'} = uri_for('/logout'); 454 }; 455 456Here again I'm using C<uri_for> instead of hardcoding the routes. This code 457block is executed before any of the templates are processed so that the 458template parameters have the appropriate values before being rendered. 459 460=head1 Putting it all together 461 462The complete tutorial code is available on GitHub: 463 464L<https://github.com/PerlDancer/dancer-tutorial> 465 466Assuming you have Git installed, you can clone the code: 467 468 git clone git://github.com/PerlDancer/dancer-tutorial.git 469 470... then run C<dancer.pl>. 471 472=head1 Advanced route moves 473 474There's a lot more to route matching than shown here. For example, you can 475match routes with regular expressions, or you can match pieces of a route like 476C</hello/:name> where the C<:name> piece magically turns into a named parameter 477in your handler for manipulation. 478 479=head1 Happy dancing! 480 481I hope this effort has been helpful and interesting enough to get you exploring 482Dancer on your own. The framework is still under heavy development but it's 483definitely mature enough to use in a production project. Additionally, there 484are now a lot of great Dancer plugins which extend and enhance the capabilities 485of the platform. 486 487Happy dancing! 488 489=head1 SEE ALSO 490 491=over 4 492 493=item * 494 495L<http://perldancer.org> 496 497=item * 498 499L<https://github.com/PerlDancer/Dancer> 500 501=item * 502 503L<Dancer::Plugin> 504 505=back 506 507=head1 COPYRIGHT AND LICENSE 508 509Copyright (C) 2010 by Mark R. Allen. 510 511This is free software; you can redistribute it and/or modify it under the terms of either the Artistic License 2.0 512or the GNU Public License version 2. 513 514The CSS stylesheet is copied verbatim from the Flaskr example application and is subject to their license: 515 516Copyright (c) 2010 by Armin Ronacher and contributors. 517 518Some rights reserved. 519 520Redistribution and use in source and binary forms of the software as well 521as documentation, with or without modification, are permitted provided 522that the following conditions are met: 523 524=over 4 525 526=item * 527 528Redistributions of source code must retain the above copyright 529notice, this list of conditions and the following disclaimer. 530 531=item * 532 533Redistributions in binary form must reproduce the above 534copyright notice, this list of conditions and the following 535disclaimer in the documentation and/or other materials provided 536with the distribution. 537 538=item * 539 540The names of the contributors may not be used to endorse or 541promote products derived from this software without specific 542prior written permission. 543 544=back 545 546THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND 547CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT 548NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 549A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 550OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 551EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 552PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 553PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 554LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 555NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 556SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH 557DAMAGE. 558 559=head1 AUTHOR 560 561Dancer Core Developers 562 563=head1 COPYRIGHT AND LICENSE 564 565This software is copyright (c) 2010 by Alexis Sukrieh. 566 567This is free software; you can redistribute it and/or modify it under 568the same terms as the Perl 5 programming language system itself. 569 570=cut 571