Dancer Advent Calendar 2012 http://advent.perldancer.org/ Twas the Release Before Christmas http://advent.perldancer.org/2012/24 perl http://advent.perldancer.org/2012/24 Mon, 24 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="twas_the_release_before_christmas"></a>Twas the Release Before Christmas</h1> <pre> <p>Twas the release before Christmas, when all through the house Not a dependency was stirring, not even Mouse. (actually, as you know we're using Moo, which is much better but we are in the middle of a poem, I'll talk about that later) The plugins were hung by the chimney with care, In hopes that Sukria's new release soon would be there.</p> <p>The users were nestled all snug in their beds, While visions of sugar-plums danced, blinking like so many LEDs. And the sysadmin in her 'kerchief, and I in my cap, Had just settled our brains for a long regression test's snap.</p> <p>When out on the network there arose such a clatter, I sprang from my chair to see what was the matter. Away to the window I flew like a flash, Tore open the shutters and threw up the stash. (which is silly, for the stash is used by Catalyst, not Dancer, this might not be a tech poem, but dammit, those things matter)</p> <p>The moon on the breast of the new-fallen snow Gave the lustre of mid-day to objects (did we mention Dancer 2 is now all OO?). When, what to my wondering eyes should appear, But a neatly tar'red sleigh, and eight tinny reindeer. (Obviously, moreso in high times of celebration, It's crucial to craft good compressed distributions)</p> <p>With a little old driver, very lively yet a bit fickle, I marveled for a second: "they using DBD::Oracle?" Anyway, more rapid than eagles its coursers they came, And it whistled, and shouted, and called them by name!</p> <p>"Now sukria! now, Dancer! now, bigpresh and ambs! On, yanick! On, sawyerx! on, on franckcuny and dams! To the top of CPAN! to the top of the coderwall! Now dance away! Dance away! Dance away all!"</p> <p>As dry retorts that within the wild IRC channels fly, When they meet with an obstacle, t'was mount the /dev/tty. (which I must confess is a pretty weird way to debug, and might be influenced by all the eggnog 'forehand chugg'd) So up to the house-top the coursers they flew, With the sleigh full of apps, and clever hacks too.</p> <p>And then, in a twinkling, I heard on the roof The prancing and pawing of those lovable little goofs. As I drew in my head, and was turning around, Down the chimney the heir of Sinatra came with a bound.</p> <p>He was dressed all in Perl, from his HEAD to his PUT, And his scripts were all beginning with #'s, and referring perltoot. A bundle of modules he had flung on his back, And he looked like a hacker, just opening his pack().</p> <p>His eyes-how they twinkled! his dimples how merry! His cheeks were like roses, his nose looked like this in ASCII =&gt; :o) His droll little mouth was drawn up like a bow, He was as pleased as a startup the morning of its IPO!</p> <p>The stump of a pipe he held tight in his cli command, And the smoke results encircled his head like a garland. He had a broad face and a little round belly, That shook when he laughed, like the promise of a sane API!</p> <p>He was chubby and plump, a right jolly young framework, And I laughed when I saw him, excitement tingling all the way down to my socks! A wink of his eye and a twist of his head, Carrying promises of TFMs soon written, soon read.</p> <p>He spoke not a word, but went straight to his work, And installed all the plugins, then turned with a jerk. And laying his finger aside of his nose, And giving a nod, up the protocol stack he rose!</p> <p>He sprang to his sleigh, to his team gave a whistle, And away they all flew, one day to be back fo shizzle. But I heard him exclaim, &#xe2;&#x80;&#x98;ere he traced out of sight, "Happy Christmas to all, and to all a good-night!"</p> </pre> <p>... yes, Christmas is nearly here, and Santa is heading out to visit each and every CPAN mirror; he's been told that all the CPAN mirror admins and users have been nice, so he's leaving a developer copy of Dancer 2 for each and every one!</p> <p>Documentation and rough edges are being polished ready for a stable release early in 2013, but in the meantime:</p> <p><a href="https://metacpan.org/release/SUKRIA/Dancer-1.9999_02">https://metacpan.org/release/SUKRIA/Dancer-1.9999_02</a></p> <p>... a new, cleaner, more elegant style of Dancing is upon us!</p> <p>The Dancer core development team would like to wish a wonderful Christmas and a happy and prosperous 2013 to each and every Dancer user and reader.</p> <p>David Precious (bigpresh) would also like to apologise for his occasional tardiness in preparing posts for this year's calendar; perhaps preparation for next year's should start in, say, July :)</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Yanick Champoux for the Perl Dancer Advent Calendar 2012.</p> </div> Dancer::Plugin::Adapter http://advent.perldancer.org/2012/23 perl http://advent.perldancer.org/2012/23 Sun, 23 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer__plugin__adapter"></a>Dancer::Plugin::Adapter</h1> <p>Today's article introduces David Golden's new plugin, <a href="https://metacpan.org/module/Dancer::Plugin::Adapter">Dancer::Plugin::Adapter</a>, designed to provide a clean DSL keyword to obtain a suitably configured instance of a class, in a similar way to, for example, the <code>database</code> keyword from <a href="https://metacpan.org/module/Dancer::Plugin::Database">Dancer::Plugin::Database</a>.</p> <p>The problem: you want to use some perl class in your Dancer app, but there's no plugin for it.</p> <p>The solution: as long as the class needs only static data to construct an object, then <code>Dancer::Plugin::Adapter</code> can do the wrapping for you. Think of it as a "just-in-time" plugin (or maybe a poor-man's <a href="https://metacpan.org/module/Bread::Board">Bread::Board</a>).</p> <p>Here's an example: you want to send emails via <a href="http://postmarkapp.com">Postmark</a> using <a href="https://metacpan.org/module/WWW::Postmark">WWW::Postmark</a>.</p> <p>In your config.yml, you put this:</p> <pre class="prettyprint">plugins: Adapter: postmark: class: WWW::Postmark options: POSTMARK_API_TEST</pre> <p>In your production config.yml, you can replace 'POSTMARK_API_TEST' with your real Postmark API key.</p> <p>Then, in your application, here's how you use it:</p> <pre class="prettyprint">get '/' =&gt; sub { eval { service("postmark")-&gt;send( from =&gt; 'me@domain.tld', to =&gt; 'you@domain.tld, them@domain.tld', subject =&gt; 'an email message', body =&gt; "hi guys, what's up?" ); }; return $@ ? "Error: $@" : "Mail sent"; };</pre> <p><code>Dancer::Plugin::Adapter</code> takes care of constructing and caching the <a href="https://metacpan.org/module/WWW::Postmark">WWW::Postmark</a> object based on the configuration data, and lets you access the object with the <code>service()</code> function.</p> <h1><a name="configuration"></a>CONFIGURATION</h1> <p>One or more objects are defined by <code>NAME =&gt; HASHREF</code> pairs. The hash reference for each NAME must contain a 'class' key, whose value is the class to wrap.</p> <p>If the hash reference contains an 'options' key, its value will be dereferenced (if it is a hash or array reference) and passed to <code>new()</code> when the object is created. Note that if the class requires a reference for the constructor, you have to wrap it in an extra array. E.g.</p> <pre class="prettyprint"># config.yml: plugins: Adapter: foo: class: Foo::Bar options: - wibble: wobble biff: boff # constructor called as: Foo::Bar-&gt;new( { wibble =&gt; wobble, biff =&gt; boff } );</pre> <p>If the class does not use 'new' as the name of its constructor, an alternate can be specified with the 'constructor' key.</p> <pre class="prettyprint"># config.yml: plugins: Adapter: tmpdir: class: File::Temp constructor: newdir # constructor called as: File::Temp-&gt;newdir()</pre> <h1><a name="usage"></a>USAGE</h1> <h2><a name="service"></a>service</h2> <pre class="prettyprint">service($name);</pre> <p>This function returns the object corresponding to the name defined in the configuration file. The object is created on demand and cached for future use.</p> <h1><a name="author"></a>AUTHOR</h1> <p>David Golden (dagolden / xdg)</p> </div> Tutorial: Shrinkr, an URL shortener http://advent.perldancer.org/2012/22 perl http://advent.perldancer.org/2012/22 Sat, 22 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="tutorial__shrinkr__an_url_shortener"></a>Tutorial: Shrinkr, an URL shortener</h1> <p>This article will guide you through the writing of working URL shortener written with Dancer.</p> <h2><a name="required_perl_modules"></a>Required Perl modules</h2> <p>In addition to Dancer, you'll also need <a href="https://metacpan.org/module/Template">Template</a>, <a href="https://metacpan.org/module/DBD::SQLite">DBD::SQLite</a>, <a href="https://metacpan.org/module/Math::Base36">Math::Base36</a>, <a href="https://metacpan.org/module/File::Slurp">File::Slurp</a>. You can install these using your CPAN client.</p> <pre class="prettyprint">cpan Dancer Template DBD::SQLite Math::Base36 File::Slurp</pre> <p>Because I'm using a regular expression named capture (one of my favourite Python regex features) in this demo, you <b>must</b> use Perl 5.10 or later to run this example.</p> <p>I've tested the application on Windows 7, Fedora 13 Linux, and Mac OS 10.6, so it should work "out of the box" for you. If not, patches are welcome!</p> <h2><a name="setting_defaults_and_configuration"></a>Setting defaults and configuration</h2> <pre class="prettyprint">set 'database' =&gt; File::Spec-&gt;tmpdir() . '/shrinkr.db'; set 'template' =&gt; 'template_toolkit'; set 'logger' =&gt; 'console'; set 'log' =&gt; 'debug'; set 'show_errors' =&gt; 1; layout 'main'; before_template sub { my $tokens = shift; $tokens-&gt;{'base'} = request-&gt;base(); $tokens-&gt;{'css_url'} = 'css/style.css'; };</pre> <p>For this tutorial, I've decided to put all of the configuration settings into the main application file. For a more complex application it would be a better idea to use a separate YAML file to hold the configuration directives. (This technique is well documented in the primary Dancer docs.)</p> <p>The first line is not specific to Dancer, only to this application. It specifies the location of the SQLite database and mainly serves to point out that you can add your own arbitrary settings on top of the ones which Dancer already has defined.</p> <p>The second line tells Dancer to use <a href="https://metacpan.org/module/Template">Template Toolkit</a> as its template engine because the default Dancer template engine is a bit too simple for most applications. There are several other Dancer template engines if you prefer a different one.</p> <p>The third line tells Dancer to use the console for log output (instead of a file). We want the logger to output at the 'debug' level or higher, so that's the fourth line.</p> <p>In the last setting line, we tell Dancer to output errors directly to the web client. This is a fantastic option for development as it gives you a great stack trace and loads of context around the error, but its probably not a great option for production sites.</p> <p>There is also a <code>layout</code> directive which tells Dancer to look in <code>views/layouts</code> for a file named <i>main.tt</i>. Once the template engine renders the specified layout template, it will insert a specific view into a tag named &lt;% content %&gt;. This helps give your application a very consistent look and feel across all of the views in it.</p> <p>We also specify the default values for every template using the <code>before_template</code> directive which sets a value for a <code>base</code> value and the <code>css_url</code>.</p> <p>Note that the web-viewable location is <code>css/style.css</code> but the file location is <i>public/css/style.css</i> - make sure you omit the <i>public</i> part of the file path when you're constructing your templates or static page route handlers.</p> <h2><a name="database_set_up"></a>Database set up</h2> <pre class="prettyprint">create table if not exists link ( id integer primary key, code string not null, url string not null, count integer not null );</pre> <p>This is the schema for our database. We have an <code>id</code> field, a <code>code</code> field, a <code>url</code> field, and a <code>count</code> field. If this were a more sophisticated application the <code>count</code> field might be a part of an <code>analytics</code> table, but we're all about keeping things simple, so it's just tacked on to our simple table design here.</p> <p>Inside the application, the database routines are straightforward.</p> <pre class="prettyprint">sub connect_db { my $dbh = DBI-&gt;connect("dbi:SQLite:dbname=".setting('database')) or die $DBI::errstr; return $dbh; }</pre> <p>Here we define a routine to establish a connection to our database instance. Notice how the <code>database</code> setting is consumed here.</p> <pre class="prettyprint">my $id = 0; sub init_db { my $db = connect_db(); my $sql = read_file("./schema.sql"); $db-&gt;do($sql) or die $db-&gt;errstr; $sql = "SELECT MAX(id) FROM link"; my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute() or die $sth-&gt;errstr; ($id) = $sth-&gt;fetchrow_array() or die $sth-&gt;errstr; }</pre> <p>We define a global variable called <code>$id</code> and then execute our initial table set up and initialise the <code>$id</code> variable as the largest ID value from the database.</p> <pre class="prettyprint">sub get_next_id { return ++$id; }</pre> <p>Here we set up a routine to return a new ID value when a prospective URL is entered by a user. This is simple enough that it could be an inline function but we could enhance this function later with additional error checking or an alternate id generation scheme.</p> <h2><a name="the_____route_handler"></a>The '/' route handler</h2> <p>Let's unpack the root URL (/) route handler line by line.</p> <pre class="prettyprint">any ['get', 'post'] =&gt; '/' =&gt; sub {</pre> <p>We tell Dancer that this route handler works with both GET and POST requests. Next we specify the '/' URL to match and finally, begin an anonymous subroutine to do something when the first two conditions are met.</p> <pre class="prettyprint">my $msg; my $err; if ( request-&gt;method() eq "POST" ) {</pre> <p>Here we're going to process POST requests - these requests will be the user input from the form in the <code>template</code> directive below.</p> <pre class="prettyprint">my $uri = URI-&gt;new( params-&gt;{'url'} ); if ( $uri-&gt;scheme !~ /https?/ ) { $err = 'Error: Only HTTP or HTTPS URLs are accepted.'; }</pre> <p>We check the supplied URL to make sure it's something we want to add to the database - if the user inputs something like <code>ssh://example.com</code> we want to reject that input with a message explaining what we're looking for.</p> <pre class="prettyprint">else { my $nid = get_next_id(); my $code = encode_base36($nid); my $sql = 'INSERT INTO link (id, code, url, count) VALUES (?, ?, ?, 0)'; my $db = connect_db(); my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute( $nid, $code, $uri-&gt;canonical() ) or die $sth-&gt;errstr;</pre> <p>Hopefully this is all standard <a href="https://metacpan.org/module/DBI">DBI</a> programming for you. Nothing tremendously mysterious going on here.</p> <pre class="prettyprint">$msg = $uri-&gt;as_string . " has been shrunk to " . request-&gt;base() . $code;</pre> <p>We want to send a message to our user telling her that the URL she supplied has been added to the database with whatever code was next in our ID assignment scheme.</p> <pre class="prettyprint"> } } template 'add.tt', { 'err' =&gt; $err, 'msg' =&gt; $msg, };</pre> <p>Here we use the <code>template</code> directive to render the <i>add.tt</i> view supplying the <code>err</code> and <code>msg</code> values as appropriate. If we fell through from our <code>if</code> statement above, both values are blank (which is fine because the <i>add.tt</i> template tests to see if <code>err</code> or <code>msg</code> have values before they're rendered.)</p> <pre class="prettyprint">};</pre> <p>Note the semicolon after the closing curly brace. This is required because the subroutine above is actually a coderef.</p> <h2><a name="processing_a_shortened_url"></a>Processing a shortened URL</h2> <p>Next we're going to write a route handler to do something when a user tries to use a shortened URL code.</p> <pre class="prettyprint">get qr|\A\/(?&lt;code&gt;[A-Za-z0-9]+)\Z| =&gt; sub {</pre> <p>Like all Dancer handlers, we start by stating which HTTP verb we want to handle, a GET in this case. Next we define a regular expression the GET request must match.</p> <p>This regular expression specifies a route that starts with a '/' and is followed by one or more of the following characters 0-9, a-z, or A-Z. Notice the <code>?&lt;code&gt;</code> construction? This is the syntax for creating a named regular expression match in Perl 5.10 (or later) - instead of using the positional variables like <code>$1</code> and the like, we can directly specify a name for the match we want to save.</p> <pre class="prettyprint">my $decode = decode_base36(uc captures-&gt;{'code'});</pre> <p>In this example, the match (if any) is stored in a special hash (<code>%+</code> generally, or the <code>captures</code> directive in Dancer) with a key of <code>code</code>. We make sure to upper case the code value because <a href="https://metacpan.org/module/Math::Base36">Math::Base36</a> uses only uppercase letters.</p> <pre class="prettyprint">if ( $decode &gt; $id ) { send_error(404); }</pre> <p>If the decoded value is greater than the current id value, we know it won't exist in the database, so we send the user a 404 error instead of trying to process the request any further.</p> <pre class="prettyprint">my $db = connect_db(); my $sql = 'SELECT url, count FROM link WHERE id = ?'; my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute($decode) or die $sth-&gt;errstr; my ($url, $count) = $sth-&gt;fetchrow_array() or die $sth-&gt;errstr; $sql = 'UPDATE link SET count = ? WHERE id = ?'; $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute(++$count, $decode);</pre> <p>More DBI programming, now. We update the database entry by incrementing the <code>count</code> counter for this request.</p> <pre class="prettyprint"> redirect $url; };</pre> <p>Finally, we tell Dancer to redirect the user to the specified URL and close the handler.</p> <h2><a name="link_stats"></a>Link stats</h2> <p>Since we're collecting the number of visits to specific links, we need to display those to a user somehow. Let's look at the handler for that.</p> <pre class="prettyprint">get '/:code/stats' =&gt; sub {</pre> <p>Another GET request, this time going to a special Dancer construction <code>:code</code> which will match anything preceded by '/' and followed by a '/stats' pattern. This is a much less restrictive regular expression than the one above, but I wanted to show a different way to do the same thing - although to be truly defensive here, much better parameter validation would be required on the <code>:code</code> input.</p> <pre class="prettyprint">my $decode = decode_base36(uc params-&gt;{'code'}); if ( $decode &gt; $id ) { send_error(404); }</pre> <p>This is the same code block as above, except this time the <code>:code</code> capture is stored inside of the the <code>params</code> Dancer construction, rather than the <code>captures</code> routine.</p> <pre class="prettyprint">my $sql = 'SELECT id, code, url, count FROM link WHERE id = ?'; my $db = connect_db(); my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute($decode) or die $sth-&gt;errstr;</pre> <p>This section retrieves the appropriate information from our database.</p> <pre class="prettyprint">my $prevl; my $nextl; unless ( ( $decode - 1 ) &lt; 0 ) { $prevl = encode_base36( $decode - 1 ); } unless ( ( $decode + 1 ) &gt; $id ) { $nextl = encode_base36( $decode + 1 ); }</pre> <p>I wanted to put some navigation links in the statistical display so a user could move around in them. This code section generates the appropriate bounded links to do that.</p> <pre class="prettyprint"> template 'stats.tt', { 'stats' =&gt; $sth-&gt;fetchall_hashref('id'), 'nextl' =&gt; $nextl, 'prevl' =&gt; $prevl, }; };</pre> <p>And here we call the <code>template</code> method, and hand off the database query results, and the navigation links as appropriate. The <i>stats.tt</i> template will check to see if <code>nextl</code> or <code>prevl</code> have values before rendering them so it's OK to pass in a value which isn't defined.</p> <h2><a name="showing_all_link_stats"></a>Showing all link stats</h2> <p>I also wanted a way to show a user all of the links stored in the database, so this handler does that.</p> <pre class="prettyprint">get '/all_stats' =&gt; sub { my $sql = 'SELECT id, code, url, count FROM link'; my $db = connect_db(); my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute() or die $sth-&gt;errstr; template 'stats.tt', { 'stats' =&gt; $sth-&gt;fetchall_hashref('id'), }; };</pre> <p>This handler is even simpler than the one above it but it does basically the same thing. Notice I'm using the same template to display the data, the main differences being that in the single link case, there's navigation links and there aren't any such links here.</p> <h2><a name="putting_it_all_together"></a>Putting it all together</h2> <p>Here's the entire script from start to finish.</p> <pre class="prettyprint">use 5.010_000; use Dancer; use Template; use DBI; use Math::Base36 ':all'; use File::Spec; use File::Slurp; use URI; set 'database' =&gt; File::Spec-&gt;tmpdir() . '/shrinkr.db'; set 'template' =&gt; 'template_toolkit'; set 'logger' =&gt; 'console'; set 'log' =&gt; 'debug'; set 'show_errors' =&gt; 1; layout 'main'; before_template sub { my $tokens = shift; $tokens-&gt;{'base'} = request-&gt;base(); $tokens-&gt;{'css_url'} = 'css/style.css'; }; sub connect_db { my $dbh = DBI-&gt;connect("dbi:SQLite:dbname=".setting('database')) or die $DBI::errstr; return $dbh; } my $id = 0; sub init_db { my $db = connect_db(); my $sql = read_file("./schema.sql"); $db-&gt;do($sql) or die $db-&gt;errstr; $sql = "SELECT MAX(id) FROM link"; my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute() or die $sth-&gt;errstr; ($id) = $sth-&gt;fetchrow_array() or die $sth-&gt;errstr; } sub get_next_id { return ++$id; } any ['get', 'post'] =&gt; '/' =&gt; sub { my $msg; my $err; if ( request-&gt;method() eq "POST" ) { my $uri = URI-&gt;new( params-&gt;{'url'} ); if ( $uri-&gt;scheme !~ 'http' ) { $err = 'Error: Only HTTP or HTTPS URLs are accepted.'; } else { my $nid = get_next_id(); my $code = encode_base36($nid); my $sql = 'INSERT INTO link (id, code, url, count) VALUES (?, ?, ?, 0)'; my $db = connect_db(); my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute( $nid, $code, $uri-&gt;canonical() ) or die $sth-&gt;errstr; $msg = $uri-&gt;as_string . " has been shrunk to " . request-&gt;base() . $code; } } template 'add.tt', { 'err' =&gt; $err, 'msg' =&gt; $msg, }; }; get qr|\A\/(?&lt;code&gt;[A-Za-z0-9]+)\Z| =&gt; sub { my $decode = decode_base36(uc captures-&gt;{'code'}); if ( $decode &gt; $id ) { send_error(404); } my $db = connect_db(); my $sql = 'SELECT url, count FROM link WHERE id = ?'; my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute($decode) or die $sth-&gt;errstr; my ($url, $count) = $sth-&gt;fetchrow_array() or die $sth-&gt;errstr; $sql = 'UPDATE link SET count = ? WHERE id = ?'; $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute(++$count, $decode); redirect $url; }; get '/:code/stats' =&gt; sub { my $decode = decode_base36(uc params-&gt;{'code'}); if ( $decode &gt; $id ) { send_error(404); } my $sql = 'SELECT id, code, url, count FROM link WHERE id = ?'; my $db = connect_db(); my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute($decode) or die $sth-&gt;errstr; my $prevl; my $nextl; unless ( ( $decode - 1 ) &lt; 0 ) { $prevl = encode_base36( $decode - 1 ); } unless ( ( $decode + 1 ) &gt; $id ) { $nextl = encode_base36( $decode + 1 ); } template 'stats.tt', { 'stats' =&gt; $sth-&gt;fetchall_hashref('id'), 'nextl' =&gt; $nextl, 'prevl' =&gt; $prevl, }; }; get '/all_stats' =&gt; sub { my $sql = 'SELECT id, code, url, count FROM link'; my $db = connect_db(); my $sth = $db-&gt;prepare($sql) or die $db-&gt;errstr; $sth-&gt;execute() or die $sth-&gt;errstr; template 'stats.tt', { 'stats' =&gt; $sth-&gt;fetchall_hashref('id'), }; }; init_db(); start;</pre> <h2><a name="author"></a>Author</h2> <p>This article has been written by Mark R. Allen for the Perl Dancer Advent Calendar.</p> <h2><a name="copyright"></a>Copyright</h2> <p>Copyright (C) 2010 by Mark R. Allen.</p> </div> Writing a Quick NaNoWriMo Graphing Web Application with Dancer http://advent.perldancer.org/2012/21 perl http://advent.perldancer.org/2012/21 Fri, 21 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="writing_a_quick_nanowrimo_graphing_web_application_with_dancer"></a>Writing a Quick NaNoWriMo Graphing Web Application with Dancer</h1> <p>Just as December is the month of the Wreath, November is the month of the Write. For the last twelve years, writers of all calibers, aspirations and genres participate in a writing marathon named the <a href="http://www.nanowrimo.org/">National Novel Writing Month</a> (also referenced to as <i>NaNoWriMo</i>). The challenge is simple: starting on November 1st, write fifty thousand words before the end of the month.</p> <p>Although the contest is purely personal, many persons find additional motivation by pitting their scores against each other. Lagging behind is one thing, but lagging behind while your nemesis is 2,000 words ahead of you? Ah! <i>unacceptable</i>!</p> <p>In this perspective, wouldn't be nice to have a little web application that takes in the wordcount of a group of contestants, and displays a chart of everybody's progress? Well, let's see how hard it is to get such an application up and running, shall we?</p> <h2><a name="the_specs"></a>The Specs</h2> <p>The graph is only going to be used for one month, so there's no need to get terribly fancy. We'll use a CSV format looking like</p> <pre class="prettyprint">2010-11-01,Andy,0 2010-11-01,Bernadette,0 2010-11-01,Claude,0 2010-11-02,Bernadette,120</pre> <p>That is, each wordcount entry is made of a timestamp, the writer's name and his or her entered count. It's simple, and very easy to edit manually if anyone make a boo-boo during the month.</p> <p>The application we need around that is very simple. Basically, we need:</p> <ul> <li><a name="item_A_main_page__showing_a_graph_and_a_form_to_enter_new_results_"></a><b>A main page, showing a graph and a form to enter new results.</b> </li> <li><a name="item_An_auxiliary_page_to_process_the_input_of_a_new_word_count_"></a><b>An auxiliary page to process the input of a new word count.</b> </li> </ul> <p>Got it? Now, let's get cracking.</p> <h2><a name="creating_the_app"></a>Creating the App</h2> <p>Creating the skeleton of a new application in Dancer is incredibly complicated. First, we have to do</p> <pre class="prettyprint">$ dancer -a ournowrimo</pre> <p>and then, uh, we're done. Okay, so maybe it's not that complicated after all. :-)</p> <p>For the templating system, we'll use <a href="https://metacpan.org/module/Mason">Mason</a>, so we edit the configuration file and change the default templating to <a href="https://metacpan.org/module/Dancer::Template::Mason">Dancer::Template::Mason</a>:</p> <pre class="prettyprint">logger: "file" appname: "ournowrimo" template: mason</pre> <h2><a name="adding_the_actions"></a>Adding the Actions</h2> <p>By now, we have an application that is already in working order. It won't do anything, but if we were to launch it by running</p> <pre class="prettyprint">$ ./ournowrimo.pl</pre> <p>it would do it just fine.</p> <h3><a name="the_main_page"></a>The Main Page</h3> <p>For the main page, we don't do any heavy processing, we just want to invoke a template:</p> <pre class="prettyprint">get '/graph' =&gt; sub { template 'index', { wrimoers =&gt; get_wrimoers() }; };</pre> <p>That's it. For the url <code>/graph</code>, Dancer will render the template <code>views/index.mason</code>, passing it the argument <code>wrimoers</code> (which is conveniently populated by the function <code>get_wrimoers()</code>).</p> <p>I'll not show the Mason template here, as it's a fairly mundane HTML affair, but you can peek at it at the application's Github repo (link below). It is, however, using the <a href="http://code.google.com/p/flot/">Flot</a> jQuery plotting library to generate the graph, and it expects to get its data from an AJAX call. Which means that we need a new AJAX route for our application.</p> <h3><a name="feeding_graph_data_via_ajax"></a>Feeding graph data via AJAX</h3> <p>For the graph, we need the url <code>/data</code> to return a JSON representation of the wordcount data. Nicely enough, Dancer has a <code>to_json()</code> function that takes care of the JSON encapsulation. All that is left for us to do, really, is to do the real data munging:</p> <pre class="prettyprint">get '/data' =&gt; sub { open my $fh, '&lt;', $count_file; my %contestant; while (&lt;$fh&gt;) { chomp; my ( $date, $who, $count ) = split '\s*,\s*'; my $epoch = DateTime::Format::Flexible-&gt;parse_datetime($date)-&gt;epoch; my $time = 1000 * $epoch; $contestant{$who}{$time} = $count; } my @json; # data structure that is going to be JSONified while ( my ( $peep, $data ) = each %contestant ) { push @json, { label =&gt; $peep, hoverable =&gt; \1, # so that it becomes JavaScript's 'true' data =&gt; [ map { [ $_, $data-&gt;{$_} ] } sort { $a &lt;=&gt; $b } keys %$data ], }; } my $beginning = DateTime::Format::Flexible-&gt;parse_datetime( "2010-11-01")-&gt;epoch; my $end = DateTime::Format::Flexible-&gt;parse_datetime( "2010-12-01")-&gt;epoch; push @json, { label =&gt; 'de par', data =&gt; [ [$beginning * 1000, 0], [ DateTime-&gt;now-&gt;epoch * 1_000, 50_000 * (DateTime-&gt;now-&gt;epoch - $beginning) / ($end - $beginning) ] ], }; to_json( \@json ); };</pre> <p>For more serious AJAX interaction, there's also <a href="https://metacpan.org/module/Dancer::Plugin::Ajax">Dancer::Plugin::Ajax</a> that adds an <code>ajax</code> route handler to the mix, but in our case a simple <code>get</code> is perfectly satisfactory.</p> <h3><a name="processing_new_entries"></a>Processing New Entries</h3> <p>For the entry of a new word count, we are taking in a form request with two parameters, <i>who</i> and <i>count</i>:</p> <pre class="prettyprint">get '/add' =&gt; sub { open my $fh, '&gt;&gt;', $count_file; say $fh join ',', DateTime-&gt;now, params-&gt;{who}, params-&gt;{count}; close $fh; redirect '/'; };</pre> <p>Seriously, could things get any easier?</p> <h3><a name="bonus_feature__throwing_in_an_atom_feed"></a>Bonus Feature: Throwing in an Atom Feed</h3> <p>Since everything else resulted in a ridiculously small amount of code, I decided to add a feed to the application to let everybody know of wordcount updates. Surely that will require a lot more coding?</p> <pre class="prettyprint">get '/feed' =&gt; sub { content_type 'application/atom+xml'; # $feed is a XML::Atom::SimpleFeed object my $feed = generate_feed(); return $feed-&gt;as_string; };</pre> <p>... Seemingly not, it won't.</p> <h2><a name="deployment"></a>Deployment</h2> <p>Dancer can be deployed a gazillion different ways. As a standalone server (development heaven), as CGI (likely to be <i>sloooow</i>, but nice to it's there if everything else fail), as FastCGI, and as a <a href="https://metacpan.org/module/Plack">Plack</a> application.</p> <p>For example, to deploy is at a fastcgi talking to an Apache server, we can launch the app as a plack-backed fastcgi</p> <pre class="prettyprint">plackup -s FCGI --listen /tmp/ournowrimo.socket ournowrimo.pl</pre> <p>configure Apache to treat it as an external fastcgi server</p> <pre class="prettyprint">Alias /wrimo/ /tmp/ournowrimo.fcgi/ FastCgiExternalServer /tmp/ournowrimo.fcgi -socket /tmp/ournowrimo.socket</pre> <p>and everything should nicely begin to work together.</p> <h2><a name="the_result"></a>The Result</h2> <img src="http://babyl.dyndns.org/techblog/entry/ournowrimo/files/ournowrimo.png" /> <h2><a name="peek_at_the_code_on_github"></a>Peek at the Code on Github</h2> <p>The full application is available on <a href="http://github.com/yanick/ournowrimo">GitHub</a>.</p> <h2><a name="author"></a>Author</h2> <p>This article was originally a <a href="http://babyl.dyndns.org/techblog/entry/ournowrimo">blog entry</a> by Yanick Champoux, modified for the Perl Dancer Advent Calendar 2010.</p> <h2><a name="reviewers"></a>Reviewers</h2> <h2><a name="copyright"></a>Copyright</h2> <p>Copyright (C) 2010 by Yanick Champoux <code>&lt;yanick@cpan.org&gt;</code></p> </div> Using DBIx::Class within a Dancer application http://advent.perldancer.org/2012/20 perl http://advent.perldancer.org/2012/20 Thu, 20 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="using_dbix__class_within_a_dancer_application"></a>Using DBIx::Class within a Dancer application</h1> <p><code>DBIx::Class</code>, also known as <code>DBIC</code>, is one of the many Perl ORM (<i>Object Relational Mapper</i>), but it's commonly recognised as the best and most widely used.</p> <p>This is a nice presentation from Leo : <a href="http://www.slideshare.net/ranguard/dbixclass-beginners-presentation">http://www.slideshare.net/ranguard/dbixclass-beginners-presentation</a></p> <p>Basically, <code>DBIC</code> allows you to interact with your SQL Database without writing any SQL.</p> <p>To do that, you need a set of <b>Schema classes</b> that describes your database structure. Then you can use DBIC to create, update, delete, search, and do many more things on the data that are in your database.</p> <p>From a Dancer web application, it is very easy to use DBIC, thanks to <code>Dancer::Plugin::DBIC</code>. This article will implement a simple web application to demonstrate the use of <code>Dancer::Plugin::DBIC</code>.</p> <p><b>Note</b> : Although this article only skims the surface of <code>DBIX::Class</code>, it won't explain how to use it. We recommend you to have a look at <code>DBIx::Class::Manual::Intro</code> or <code>DBIx::Class::Manual::Example</code> if needed.</p> <h2><a name="the_bookstore_example"></a>The bookstore example</h2> <p>Let's consider a simple Dancer application that allows to search for authors or books. The application is connected to a database, that contains authors, and their books. The website will have one single page with a form, that allows to query books or authors, and display the results.</p> <p>To keep this article short, the HTML will be simplistic, and the implementation as well. However, we'll try to explain how to properly use <code>Dancer::Plugin::DBIC</code>.</p> <p>The application will be structured as follow:</p> <ul> <li> <p>a Dancer route <b>/search</b> to handle the request, and decide if there is any search to perform, and send the results to the view</p> </li> <li> <p>a view, that will display the search form, and the results if any.</p> </li> <li> <p>a set of models, linked to a database, that will contain the books and authors. These models will be created using DBIC</p> </li> </ul> <h2><a name="the_basics"></a>The basics</h2> <h3><a name="create_the_application"></a>Create the application</h3> <p>Okay, that's easy enough:</p> <pre class="prettyprint">$&gt; dancer -a bookstore</pre> <h3><a name="change_template_type"></a>Change template type</h3> <p>We'll want to loop on results and display authors and books, and it's easier to use Template Toolkit to do that, rather than the default <code>Dancer::Template::Simple</code>.</p> <p>So let's specify in the configuration that we'll use Template Toolkit as template engine:</p> <pre class="prettyprint"># add in bookstore/config.yml template: template_toolkit</pre> <h3><a name="create_a_view"></a>Create a view</h3> <p>We need a view to display the search form, and below, the results, if any. The results will be fed by the route to the view as an <code>arrayref</code> of results. Each result is a <i>hashref</i>, with a <code>author</code> key containing the name of the author, and a <code>books</code> key containing an <i>arrayref</i> of strings : the books names.</p> <p>That explanation is probably hard to follow, so here is an example, much easier:</p> <pre class="prettyprint"># example of a list of results [ { author =&gt; 'author 1', books =&gt; [ 'book 1', 'book 2' ], }, { author =&gt; 'author 2', books =&gt; [ 'book 3', 'book 4' ], } ]</pre> <p>So, what will the view look like? Here is a simple example, displaying the search form, and the results, if any. It's written in Template Toolkit, but Dancer changes the default <code>[&#xe2;&#x80;&#xb0; %]</code> format to be <code>&lt;% %&gt;</code> instead.</p> <pre class="prettyprint"># bookstore/views/search.tt &lt;p&gt; &lt;form action="/search"&gt; Search query: &lt;input type="text" name="query" /&gt; &lt;/form&gt; &lt;/p&gt; &lt;br&gt; &lt;% IF query.length %&gt; &lt;p&gt;Search query was : &lt;% query %&gt;.&lt;/p&gt; &lt;% IF results.size %&gt; Results: &lt;ul&gt; &lt;% FOREACH result IN results %&gt; &lt;li&gt;Author: &lt;% result.author.replace("((?i)$query)", '&lt;b&gt;$1&lt;/b&gt;') %&gt; &lt;ul&gt; &lt;% FOREACH book IN result.books %&gt; &lt;li&gt;&lt;% book.replace("((?i)$query)", '&lt;b&gt;$1&lt;/b&gt;') %&gt; &lt;% END %&gt; &lt;/ul&gt; &lt;% END %&gt; &lt;% ELSE %&gt; No result &lt;% END %&gt; &lt;% END %&gt;</pre> <h3><a name="create_a_route"></a>Create a route</h3> <p>Let's create a simple Dancer route, to be added in the <code>bookstore.pm</code> module:</p> <pre class="prettyprint"># add in bookstore/lib/bookstore.pm get '/search' =&gt; sub { my $query = params-&gt;{query}; my @results = (); if (length $query) { @results = _perform_search($query); } template 'search', { query =&gt; $query, results =&gt; \@results, }; };</pre> <p>It's rather simple: get the parameter called <i>query</i>, if it exists perform the search, and in any case, call the <code>search</code> view.</p> <p>So, as you can see, we need to write the <code>_perform_search()</code> method. But before we do that, let's create the database.</p> <h3><a name="create_a_database"></a>Create a database</h3> <p>We'll go with SQLite, as it fits well with the aim of simplicity of this example. Let's create the SQLite file database:</p> <pre class="prettyprint">$&gt; sqlite3 bookstore.db CREATE TABLE author( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, firstname text default '' not null, lastname text not null); CREATE TABLE book( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, author INTEGER REFERENCES author (id), title text default '' not null );</pre> <p>Simple stuff: we have 2 tables, one for authors, and one for books, that points to the author table.</p> <h3><a name="populate_with_some_data"></a>Populate with some data</h3> <p>Let's write a script to populate the database with some data. We'll use <code>DBIX::Class</code>, and let it discover our simple database schema.</p> <pre class="prettyprint"># populate_database.pl package My::Bookstore::Schema; use base qw(DBIx::Class::Schema::Loader); package main; my $schema = My::Bookstore::Schema-&gt;connect('dbi:SQLite:dbname=bookstore.db'); $schema-&gt;populate('Author', [ [ 'firstname', 'lastname'], [ 'Ian M.', 'Banks' ], [ 'Richard', 'Matheson'], [ 'Frank', 'Herbert' ], ]); my @books_list = ( [ 'Consider Phlebas', 'Banks' ], [ 'The Player of Games', 'Banks' ], [ 'Use of Weapons', 'Banks' ], [ 'Dune', 'Herbert' ], [ 'Dune Messiah', 'Herbert' ], [ 'Children of Dune', 'Herbert' ], [ 'The Night Stalker', 'Matheson' ], [ 'The Night Strangler', 'Matheson' ], ); # transform author names into ids $_-&gt;[1] = $schema-&gt;resultset('Author')-&gt;find({ lastname =&gt; $_-&gt;[1] })-&gt;id foreach (@books_list); $schema-&gt;populate('Book', [ [ 'title', 'author' ], @books_list, ]);</pre> <p>Then run it in the directory where <i>bookstore.db</i> sits:</p> <pre class="prettyprint">perl populate_database.db</pre> <p>And that's our database populated !</p> <h2><a name="use_dancer__plugin__dbic"></a>Use Dancer::Plugin::DBIC</h2> <p>Let's go back to our Dancer application now. Instead of interacting with the database using SQL, let's configure <code>DBIX::Class</code>. DBIC needs to understand how your data is organised in your database. There are two ways of letting DBIC know:</p> <ul> <li> <p>either by writing a set of Perl modules, called schema modules: they will describe the database schema, each module describing one entity,</p> </li> <li> <p>or by letting DBIC connect to the database, explore it, and generate the schema itself.</p> </li> </ul> <p>We'll demonstrate the use of the two solutions. The author of this article (dams) is not a big fan of the detection method: on complex database, it doesn't get everything right, so one needs to help DBIC. Describing the schema manually in proper Perl classes seems a cleaner option. But hey, TIMTOWTDI.</p> <h3><a name="use_auto_detection"></a>Use auto-detection</h3> <p>Let's add some configuration in our Dancer application. We want to indicate that we want to use the <code>Dancer::Plugin::DBIC</code> plugin, and how we want to use it. We also want to define a new DBIC schema, that we will call <code>bookstore</code>. And we need to indicate that this schema is connected to the SQLite database we created.</p> <pre class="prettyprint"># add in bookstore/config.yml plugins: DBIC: bookstore: dsn: "dbi:SQLite:dbname=bookstore.db"</pre> <p>We could potentially define more schemas, by adding more fields under the <code>DBIC:</code> entry.</p> <p><b>Note</b> : you've noticed that we have only described which database to link the schema to. That way, we let <code>Dancer::Plugin::DBIC</code> connect to the database and discover its schema, and make it available for us</p> <p>Now that the configuration is done, let's see what needs to be done in the code.</p> <p>First of all, we need to indicate to Dancer that we want to use <code>Dancer::Plugin::DBIC</code>. That's easily done:</p> <pre class="prettyprint"># add in bookstore/lib/bookstore.pm use Dancer::Plugin::DBIC;</pre> <p>And now we can implement <code>_perform_search</code> using <code>Dancer::Plugin::DBIC</code>. The plugin gives you access to an additional keyword called <b>schema</b>, which you give the name of schema you want to retrieve. It returns a <code>DBIx::Class::Schema::Loader</code> (because we let the plugin discover the schema for us). This returned object can then be used to get a resultset and perform searches, as per standard usage of <code>DBIX::Class</code>.</p> <pre class="prettyprint"># add in bookstore/lib/bookstore.pm sub _perform_search { my ($query) = @_; my $bookstore_schema = schema 'bookstore'; my @results; # search in authors my @authors = $bookstore_schema-&gt;resultset('Author')-&gt;search({ -or =&gt; [ firstname =&gt; { like =&gt; "%$query%" }, lastname =&gt; { like =&gt; "%$query%" }, ] }); push @results, map { { author =&gt; join(' ', $_-&gt;firstname, $_-&gt;lastname), books =&gt; [], } } @authors; my %book_results; # search in books my @books = $bookstore_schema-&gt;resultset('Book')-&gt;search({ title =&gt; { like =&gt; "%$query%" }, }); foreach my $book (@books) { my $author_name = join(' ', $book-&gt;author-&gt;firstname, $book-&gt;author-&gt;lastname); push @{$book_results{$author_name}}, $book-&gt;title; } push @results, map { { author =&gt; $_, books =&gt; $book_results{$_}, } } keys %book_results; return @results; }</pre> <p>We needed to do some data fiddling so that the books results are gathered by authors.</p> <h3><a name="use_home_made_schema_classes"></a>Use home-made schema classes</h3> <p>Writing your own DBIC schema classes goes a bit beyond this article, but here are the basics. You can either have the <code>.pm</code> files be generated from you using <code>dbicdump</code> (see <code>DBIx::Class::Schema::Loader</code>), and then you can modify them to fit your needs. Or you can write them yourself from scratch, as explained in the DBIC documentation.</p> <p>A third option is to use the nice <code>DBIx::Class::MooseColumns</code> that let's you write the DBIC schema classes using <code>Moose</code>. This way of doing make it look more like ActiveRecord declarations.</p> <p>You should put your schema classes in a place that Dancer will find. A good place is in <i>bookstore/lib/</i>.</p> <p>Once your schema classes are in place, all you need to do is modify <i>config.yml</i> to specify that you want to use them, instead of the default auto-detection method:</p> <pre class="prettyprint"># change in bookstore/config.yml plugins: DBIC: bookstore: schema_class: My::Bookstore::Schema dsn: "dbi:SQLite:dbname=bookstore.db"</pre> <p>The rest will work exactly the same.</p> <h2><a name="start_the_application"></a>Start the application</h2> <p>Our bookstore lookup application can now be started using the built-in server:</p> <pre class="prettyprint"># start the web application bookstore/bin/app.pl</pre> <p>Now if we search for <code>The</code>, here is what we get :</p> <img src="/images/dancer_dbic.png" /> <p>As you can see, the page presents 4 results. The first one is an author's match, <i>Richard Ma<b>the</b>son</i>. The next 2 ones are 2 of his books. The last one is <i><b>The</b> Player of Games</i> (a great book by the way...).</p> <h2><a name="conclusion"></a>Conclusion</h2> <p>Well that was a rather long article, but we wanted to show a real example of using DBIC in Dancer. Most of the Dancer Plugins have the same spirit in common: be as simple as possible, and don't get in the way of the user. <code>Dancer::Plugin::DBIC</code> is exactly that, and we hope we demonstrated it to you.</p> <h2><a name="author"></a>AUTHOR</h2> <p>dams ( Damien Krotkine <code>&lt;dams@cpan.org&gt;</code> )</p> </div> Meet Dancer users - The Game Crafter http://advent.perldancer.org/2012/19 perl http://advent.perldancer.org/2012/19 Wed, 19 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="meet_dancer_users___the_game_crafter"></a>Meet Dancer users - The Game Crafter</h1> <img src="/images/2012/19/tgc-250x160.png"> <p><a href="http://www.thegamecrafter.com/">The Game Crafter</a> is the world's first web-to-print game publishing company and offers a print on demand game publishing service. TGC empowers game designers around the world by allowing them to make a board game, card game, and custom playing cards through a website. Using the latest web technology, TGC developed a website platform that simplifies the design, manufacturing, and retail processes related to tabletop games.</p> <p>The Game Crafter has a community of over 20,000 users, growing at a rate of over 1,000 users a month, and selling around 2,000 games every month.</p> <p>With that growth, and praised by TechCrunch, Wired and Mashable among others, it's clear to see that The Game Crafter is going places. The Game Crafter's founder, JT Smith helpfully agreed to a short interview on how Dancer has helped The Game Crafter to succeed:</p> <ul> <li><a name="item_What_drew_you_to_Dancer_originally_"></a><b>What drew you to Dancer originally?</b> <p>I had just finished writing <a href="http://www.lacunaexpanse.com/">Lacuna Expanse</a>, which used bare-metal Plack. I figured there were some better tools out there to give me routing and a few other things, so I started experimenting. Catalyst was way too big and some of the other micro web frameworks seemed to be not much more than bare-metal Plack, so it quickly came down to Dancer and Mojo. I'd like to say that there was some cool architectural reason I chose Dancer over Mojo, but really it came down to style. Dancer just fit in to my design sense better than Mojo.</p> </li> <li><a name="item_What_benefits_has_Dancer_brought_to_The_Game_Crafter_"></a><b>What benefits has Dancer brought to The Game Crafter?</b> <p>Initially it was simply how easy it was to pick up Dancer and just get *something* working. When I picked Dancer for TGC I was at the beginning of a full rewrite of TGC, so being able to go from blank canvas to anything at all in a few minutes was a huge win for productivity.</p> <p>The other two big things Dancer gives us are the easy plugin system and the hooks system. When I picked Dancer I wasn't looking for a tool that did everything for me out of the box. I was looking for something I could grow into and build the app the way I wanted to build it. Dancer is easily extensible without getting in my way or limiting me, and that's a big win.</p> </li> <li><a name="item_What_features_of_Dancer__or_the_community_or_ecosystem__especially_stand_out_to_you_"></a><b>What features of Dancer (or the community or ecosystem) especially stand out to you?</b> <p>Right off the bat when I started using Dancer I ran into a design limitation with the existing hooks. I posted a quick patch to resolve the problem I was having and it was accepted quickly. But that evolved into a larger discussion with the core dev team about how to handle hooks in a more generic sense. Having a responsive and forward-thinking team means Dancer has a very bright future.</p> <p>The plug-in ecosystem is really something special about Dancer. If you're just looking to whip up a quick app in an afternoon you can almost certainly find a plugin that will out-of-the-box do exactly what you need. Likewise, if you want to build something bigger, the plug-in system gives you the way to extend Dancer.</p> <p>I believe so much in Dancer that I've given talks about it at local Perl Monger's groups, have taught a few people how to use it, and have even built my next generation web services framework at Plain Black on top of it. So pretty much every new app we build these days, whether at TGC or one of my other companies, is being built on top of Dancer.</p> </li> <li><a name="item_What_would_you_like_to_see_changed_"></a><b>What would you like to see changed?</b> <p>Honestly the big thing I would change about Dancer is already well underway. It's all covered in Dancer 2, which is going to a better and more accessible object system. Dancer 2 is looking spectacular to me, and I'm looking forward to transitioning my code-base to it early in 2013.</p> <p>The other thing that I would change is that Dancer::Test currently doesn't have a way of posting a file. I've resorted to using Plack::Test to test file uploads. I should probably just submit a patch to make it work, but I haven't done that yet.</p> </li> <li><a name="item_Are_there_any_particular_opinions__quotes_soundbites_etc_expressed_by_any_of_your_team_about_Dancer_"></a><b>Are there any particular opinions, quotes/soundbites etc expressed by any of your team about Dancer?</b> <p>My team at TGC is very small...just me and one other person. However, everyone I've shown the codebase to can't believe how tiny everything is. You need to write so little code to do so much.</p> </li> <li><a name="item_How_many_developers_do_you_have___How_many_use_Dancer_"></a><b>How many developers do you have? How many use Dancer?</b> <p>At The Game Crafter we have only two developers and we both use Dancer.</p> </li> <li><a name="item_How_many_total_employees_do_you_have_"></a><b>How many total employees do you have?</b> <p>There are 14 employees at The Game Crafter.</p> </li> <li><a name="item_How_many_TGC_users_are_there_"></a><b>How many TGC users are there?</b> <p>We have 20,000 users.</p> </li> <li><a name="item_How_many_different_games_have_been_produced_by_TGC_users_"></a><b>How many different games have been produced by TGC users?</b> <p>We sell about 2,000 games per month.</p> </li> <li><a name="item_How_many_copies_does_each_game_sell_on_average_"></a><b>How many copies does each game sell on average?</b> <p>Most games aren't publicly available in the shop, and a lot of games that are available in the shop don't get any publicity from their designer's, so therefore only sell a few copies. Of those games that get promoted by their designers, they sell 10 to 50 copies of their game on average. Our best selling games usually sell 100 or 200 copies and then get picked up by another publisher.</p> </li> <li><a name="item_What_is_the_average_cost_of_a_game_"></a><b>What is the average cost of a game?</b> <p>An average card game goes for about $15, while your average board game goes for about $25. However, we also have more complicated games that sell well at $40 per copy.</p> </li> <li><a name="item_What_is_the_most_popular_game_so_far_in_terms_of_sales_"></a><b>What is the most popular game so far in terms of sales?</b> <p>Our current best selling game is called <a href="https://www.thegamecrafter.com/games/plague-the-card-game">Plague: The Card Game</a></p> </li> <li><a name="item_What_is_your_favourite_game_so_far_"></a><b>What is your favourite game so far?</b> <p>My favorite game is one that I've developed called <a href="https://www.thegamecrafter.com/games/merc">MERC</a>. However, my favorite game not developed by me is called <a href="https://www.thegamecrafter.com/games/scarborough-fair">Scarborough Fair</a>.</p> </li> <li><a name="item_How_does_the_production_process_work_"></a><b>How does the production process work?</b> <p>Basically a designer comes up with an idea, uploads their artwork and rules to our web site, and about a week later they get a physical copy of their game in the mail.</p> <p>In more technical terms we use Dancer to generate a publishing interface to the user which walks them through the process of uploading and proofing their artwork. Then we have a bunch of background processes that turn that artwork into the files needed to run through our printing system. We manage those processes and a lot of other stuff through a bunch of administrative applications written on top of Dancer. The user is able to track every stage of this process through our web shop, which is also written in Dancer. Basically every aspect of our business from sales to publishing to inventory management to historical trends is handled through a series of custom-built Dancer applications.</p> </li> <li><a name="item_When_was_TGC_founded_"></a><b>When was TGC founded?</b> <p>The Game Crafter came into existence in January of 2001. The publishing service we offer now was launched in July 2009. The Dancer version of the site went live July 2011.</p> </li> </ul> <p>So, thanks JT for your time!</p> <h1><a name="author"></a>AUTHOR</h1> <p>David Precious (BIGPRESH)</p> </div> Meet Dancer users - Crowdtilt http://advent.perldancer.org/2012/18 perl http://advent.perldancer.org/2012/18 Tue, 18 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="meet_dancer_users___crowdtilt"></a>Meet Dancer users - Crowdtilt</h1> <img src="/images/2012/18/crowdtilt_RGB_gradient_medium.png"> <p>Will Wolf of the successful startup <a href="http://www.crowdtilt/">Crowdtilt</a> kindly agreed to take part in a mini-interview on how Dancer has helped play a part in their success.</p> <p>Crowdtilt was founded back in February, and within six weeks, CrowdTilt users had raised over $1 million, and has continued to grow ever since.</p> <p>Crowdtilt allows users to successfully group-fund projects and activities, with nobody paying until the drive "tilts" - reaches the target level.</p> <p>A couple of fun campaigns include <a href="https://www.crowdtilt.com/campaigns/halloween-yacht-party-oct-31st">a spectacular Halloween yacht party</a> and <a href="https://www.crowdtilt.com/campaigns/up-all-night-las-vegas-nye-2013">New Years Eve in Las Vegas with a private jet, limos and high-class party</a>.</p> <p>There are plenty of more serious campaigns which Crowdtilt has made possible, too - including <a href="https://www.crowdtilt.com/campaigns/donations-for-captain-edward-wesley-klein">a campaign to raise donations for Captain Edward Wesley Klein</a>, a valiant US soldier severely injured by an IED, suffering major injuries - he has lost both legs, his right arm, two fingers on his left hand, and has a broken pelvis. The campaign was to help his family with the medical expenses. Amanda, the organiser of the campaign, set the "tilt" amount at $200 USD. The campaign raised a staggering <i>$30,142.50</i> USD.</p> <p>Crowdtilt now have special arrangements for charitable fundraising, including options to automatically send tax-deductible receipts to donors.</p> <p>In the aftermath of Hurricane Sandy, Crowdtilt waived their fees for fundraisers to help with the recovery - the Crowdtilt community collectively raised more than $180,000 for those affected by the Hurricane.</p> <p>Crowdtilt have appeared in the media plenty of times. Forbes stated that "The network is so strong, and the tools themselves are really easy to use and well-designed".</p> <h2><a name="so__crowdtilt_use_dancer"></a>So, CrowdTilt use Dancer?</h2> <p>Yes - Crowdtilt's system is developed by a team of five developers using Perl and Dancer.</p> <p>I asked Will a few quick questions on how Dancer has contributed to Crowdtilt's success.</p> <ul> <li><a name="item_What_drew_you_to_Dancer_originally_"></a><b>What drew you to Dancer originally?</b> <p>I loved the simplicity and straight-forwardness of Dancer. It's so dead-simple and just gets out of your way. At a startup, you just want to pick tools that let you hit the ground running, and Dancer definitely gave us that.</p> </li> <li><a name="item_What_benefits_has_Dancer_brought_to_CrowdTilt_"></a><b>What benefits has Dancer brought to CrowdTilt?</b> <p>The Dancer community has always been responsive to us when we've had questions, or pull requests, or issues. That's really important in any software project or piece of technology you use. As I mentioned, Dancer does have a pretty awesome plugin ecosystem, giving us things like <a href="https://metacpan.org/module/Dancer::Plugin::DBIC">Dancer::Plugin::DBIC</a>, <a href="https://metacpan.org/module/Dancer::Plugin::Stomp">Dancer::Plugin::Stomp</a>, etc. Not having to reinvent the wheel is always nice.</p> </li> <li><a name="item_What_would_you_like_to_see_changed_"></a><b>What would you like to see changed?</b> <p>As our Dancer apps have grown bigger, we've had some growing pains with Dancer, but the code base is so simple that it's generally fairly trivial for us to jump in and figure out what needs to be done. We're really looking forward to Dancer2 and think that its OO/Moose core will help a *lot* with some of the stumbling blocks we've run into along the way.</p> <p>One of the issues we've run into is with the error hooks. They don't provide an elegant way currently to manipulate all of your error responses as much as we'd like. We have some internal workarounds in place, and would like to eventually push some of those changes back into Dancer. I think these issues should be addressed pretty nicely by Dancer2 as well.</p> <p>Another thing I'd like to see is Dancer having support for globally unique id's per request. This is pretty much required in an environment with multiple servers, and trying to track down specific requests in the logs, etc. Currently dancer can give you the "hit #" in the log, which is just a numeric counter, but this resets whenever you restart the app, and will obviously have duplicate entries for different requests across machines and different dancer processes. We currently generate our own guid's per request, but would like to see that functionality moved into Dancer itself.</p> <p>Overall, I think Dancer2 will address a lot of the growing pains we've seen, and also that Dancer itself has seen. It's definitely a step in the right direction for the Dancer community, and the work that's been done recently to make the transition to Dancer2 as easy as possibly has been really exciting to watch.</p> </li> <li><a name="item_Are_there_any_particular_opinions__quotes_soundbites_etc_expressed_by_any_of_your_team_about_Dancer_"></a><b>Are there any particular opinions, quotes/soundbites etc expressed by any of your team about Dancer?</b> <p>Every day I'm a Dancin'.</p> </li> <li><a name="item_How_many_people_are_responsible_for_development_at_Crowdtilt___How_many_of_those_work_on_code_using_Dancer_"></a><b>How many people are responsible for development at Crowdtilt? How many of those work on code using Dancer?</b> <p>We currently have 5 developers who all touch code using Dancer every day.</p> </li> <li><a name="item_How_many_projects_campaigns_have_been_funded_through_Crowdtilt_to_date_"></a><b>How many projects/campaigns have been funded through Crowdtilt to date?</b> <p>Our founders have asked us not to disclose growth figures or numbers right now, but we'll have a big announcement about them coming soon! I can say that we're very happy with our growth and the direction things are headed. We're having a big impact by enabling lots of people to do and accomplish things they otherwise wouldn't be able to do.</p> </li> <li><a name="item_What_is_the_average_campaign_fund_"></a><b>What is the average campaign fund?</b> <p>Again, without going into specifics, I can say that we have campaigns ranging from $20 all the way to $50,000+. Very frequently, a 'small' campaign will raise orders of magnitude more than they expect. In fact, our campaigns on average raise 188% of their goal, or 'tilt' amount.</p> </li> <li><a name="item_What_is_the_approximate_total_funds_raised_through_Crowdtilt_to_date_"></a><b>What is the approximate total funds raised through Crowdtilt to date?</b> <p>Six weeks after our launch back in February our users had raised over $1 million, and we've been very happy with our growth and the amount of users we've been able to help since then.</p> </li> <li><a name="item_Are_there_any_specific_campaigns_you_think_ought_to_be_highlighted_"></a><b>Are there any specific campaigns you think ought to be highlighted?</b> <p>There are so many great campaigns that flow through Crowdtilt, it's hard to pick out specific gems. Of course the <a href="https://www.crowdtilt.com/campaigns/halloween-yacht-party-oct-31st">Halloween Yacht Party</a> was a fun one, and if you haven't seen our New Years Eve Party campaign that just tilted on Monday (in a matter of hours, I might add), you can check it out here:</p> <p><a href="https://crowdtilt.com/campaigns/up-all-night-las-vegas-nye-2013">https://crowdtilt.com/campaigns/up-all-night-las-vegas-nye-2013</a></p> <p>Those types of campaigns are definitely fun, and a common use case for Crowdtilters, but we really love to see the campaigns that truly have a meaningful impact on people's lives. One of the more recent examples that I love is this one:</p> <p><a href="https://www.crowdtilt.com/campaigns/donations-for-captain-edward-wesley-klein">https://www.crowdtilt.com/campaigns/donations-for-captain-edward-wesley-klein</a></p> <p>Amanda set out to raise a modest $200 to just get any help she could with medical bills for Captain Klein, and was overwhelmed by the amazing support she received. She ended up raising over $30,000, well beyond what she ever could have imagined.</p> <p>Here's another great one put together by the Reddit community, re-uniting a Comic Book writer with his collection:</p> <p><a href="https://reddit.crowdtilt.com/campaigns/help-karl-kesel-keep-his-comic-collection">https://reddit.crowdtilt.com/campaigns/help-karl-kesel-keep-his-comic-collection</a></p> </li> <li><a name="item_Thanks_for_taking_part_in_this_brief_interview_"></a><b>Thanks for taking part in this brief interview!</b> <p>Thanks for taking the time to check out our company, and for being a big part of the Dancer community!</p> </li> </ul> <h2><a name="do_you_use_dancer_want_to_share_your_story"></a>Do you use Dancer? Want to share your story?</h2> <p>If you're interested in taking part in a simple interview in how Dancer has helped you for a future advent calendar post, it's not too late, there are still slots left - just drop me a mail!</p> <h1><a name="author"></a>AUTHOR</h1> <p>David Precious (BIGPRESH)</p> </div> Create your own Dancer 2 DSL http://advent.perldancer.org/2012/17 perl http://advent.perldancer.org/2012/17 Mon, 17 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="create_your_own_dancer_2_dsl"></a>Create your own Dancer 2 DSL</h1> <p>As the previous articles explained, Dancer 2 is a nice and shiny rewrite of Dancer 1, with a lot of added benefits and strengths. One of them is that it's properly object oriented, thanks to <a href="https://metacpan.org/module/Moo">Moo</a>.</p> <p>Thanks to that redesign of the framework, Dancer's core elements are now objects, roles, or some kind of light factories. That is also true for Dancer DSL, which means that it can be extended or replaced altogether by one of your own!</p> <p>But first, let's step back a bit.</p> <h2><a name="dsl_what"></a>DSL-what?</h2> <p>DSL means Domain-Specific Language (<a href="http://en.wikipedia.org/wiki/Domain-specific_language">see wikipedia</a>). It's basically what makes Dancer so easy to use. In our case it's a collection of keywords, which allows the developer to tell to Dancer what to do.</p> <p>Let's look at a bit of code:</p> <pre class="prettyprint">prefix '/debug'; get '/display_env' =&gt; sub { to_dumper(request-&gt;env); }; hook before =&gt; sub { dancer_app-&gt;environment ne 'development' and halt; }</pre> <p>The example above uses no less than 7 keywords! Dancer's DSL is everywhere in a Dancer-powered application. If you actually wonder what the DSL keywords are in this example, they are : <code>prefix</code>, <code>get</code>, <code>to_dumper</code>, <code>request</code>, <code>hook</code>, <code>dancer_app</code> and <code>halt</code>.</p> <p>So the idea is to extend or change Dancer DSL per web application, without writing too much code. To be able to do that, let's look at what the default Dancer's DSL is.</p> <h2><a name="dsl_how_"></a>DSL-how ?</h2> <p>How's the default DSL implemented? It very simply lies in the package <a href="https://metacpan.org/module/Dancer::Core::DSL">Dancer::Core::DSL</a>. You can look at the code <a href="https://github.com/PerlDancer/Dancer2/blob/master/lib/Dancer/Core/DSL.pm">here</a>.</p> <p>But, basically, it's a Moo class consuming the <a href="https://metacpan.org/module/Dancer::Core::Role::DSL">Dancer::Core::Role::DSL</a> role, and defining a list of keywords and their implementations. Keywords are names, and the implementations are methods of the DSL class. The keywords can be of two kinds, the ones that can be called anywhere, and the ones that can be called only inside a route. The implementations make heavily usage of the other Dancer's concept, like the app, settings, sessions, settings, and so on.</p> <h2><a name="extend_the_default_dsl"></a>Extend the default DSL</h2> <p>So, what we want to do here is introduce new keywords for some fun. As an example, we are going to translate some keywords in french. Namely:</p> <ul> <li><a name="item__code_get__code_"></a><b><code>get</code></b> <p>will also be availabe as <code>prend</code></p> </li> <li><a name="item__code_post__code_"></a><b><code>post</code></b> <p>will also be availabe as <code>envoie</code></p> </li> <li><a name="item__code_halt__code_"></a><b><code>halt</code></b> <p>will also be availabe as <code>stop</code></p> </li> <li><a name="item__code_header__code_"></a><b><code>header</code></b> <p>will also be availabe as <code>entete</code></p> </li> <li><a name="item__code_cookie__code_"></a><b><code>cookie</code></b> <p>will also be availabe as <code>gateau</code></p> </li> </ul> <p>Not very useful and not very christmas related, but it'll still do!</p> <h2><a name="create_a_new_dsl"></a>Create a new DSL</h2> <p>We can't just <b>extend</b> the default DSL, we need to create a <b>new</b> DSL module, which will itself extend the default one. So, from what we have said and from the source code of the core DSL, what we need is to create a new class <code>FrenchDSL</code>, that extends <code>Dancer::Core::DSL</code>:</p> <pre class="prettyprint">package MyDancerDSL; use Moo; extends 'Dancer::Core::DSL';</pre> <p>Pretty easy, right? Now, let's see. We still want to benefit of all the default keywords, but we want to add our translated ones. The keywords are defined by the <code>dsl_keyword</code> method that returns them as an Array of Arrays. We can use the <code>around</code> method modifier of Moo to add our keywords at the end:</p> <pre class="prettyprint">around dsl_keywords =&gt; sub { my $orig = shift; my $keywords = $orig-&gt;(@_); push @$keywords, [ gateau =&gt; 0 ], [ moteur =&gt; 1 ], [ stop =&gt; 0 ], [ prend =&gt; 1 ], [ envoie =&gt; 1 ], [ entete =&gt; 0 ]; return $keywords; };</pre> <p>The 1's and 0's indicate if the keywords are global or can only be called from within route code.</p> <p>Third step: let's implement our new keywords. That's very easy because we are just aliasing existing keywords:</p> <pre class="prettyprint">sub gateau { goto &amp;Dancer::Core::DSL::cookie }; sub moteur { goto &amp;Dancer::Core::DSL::engine }; sub stop { goto &amp;Dancer::Core::DSL::halt }; sub prend { goto &amp;Dancer::Core::DSL::get }; sub envoie { goto &amp;Dancer::Core::DSL::post }; sub entete { goto &amp;Dancer::Core::DSL::header }; 1;</pre> <p>Pretty easy. Adding a final true value and save the whole in <i>FrenchDSL.pm</i> in a place where it can be <code>use</code>'d.</p> <h2><a name="use_your_new_dsl"></a>use your new DSL</h2> <p>Now that we have a new Dancer DSL that extends the default one, how do we ask to Dancer to use this one instead of the default one ? By specifying it at <code>use</code> time, in our application script:</p> <pre class="prettyprint">use strict; use warnings; use Dancer dsl =&gt; 'FrenchDSL'; envoie '/' =&gt; sub { "That's the / route, you GET command!"; }; prend '/' =&gt; sub { "A POST has been sent to / !"; }; dance;</pre> <p>We are coming to the end of the article. We hope that you have discovered that Dancer 2 allows easy and flexible extending, and that its guts are not that complicated. The best advice would be: use Dancer 2 in new web developments, and look at the code! it's not that big, and it's quite straightforward.</p> <h1><a name="author"></a>AUTHOR</h1> <p>Damien "dams" Krotkine</p> </div> Redesigning Dancer::Plugin::Auth::Extensible http://advent.perldancer.org/2012/16 perl http://advent.perldancer.org/2012/16 Sun, 16 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="redesigning_dancer__plugin__auth__extensible"></a>Redesigning Dancer::Plugin::Auth::Extensible</h1> <p>Earlier in this advent calendar, <a href="http://advent.perldancer.org/2012/2">I wrote</a> about <a href="https://metacpan.org/module/Dancer::Plugin::Auth::Extensible">Dancer::Plugin::Auth::Extensible</a>, a flexible authentication / authorisation framework for Dancer apps.</p> <p>The original design of this plugin used subroutine attributes to denote what is required to access specific roles (e.g. "must be logged in", or "must have a certain role"). This, I think, was a fairly clean and nice design, but several people (whose opinions I trust) pointed out that subroutine attributes can be fragile, and present some problems (including thread safety, issues running under the debugger / <a href="https://metacpan.org/module/Devel::Cover">Devel::Cover</a> etc).</p> <p>So, tonight I have redesigned the API for this plugin, to remove the use of subroutine attributes and instead use new keywords - <code>require_login</code>, <code>require_role</code>, <code>require_any_role</code> and <code>require_all_roles</code>.</p> <p>Using the plugin now looks like:</p> <pre class="prettyprint">get '/members' =&gt; require_login sub { ... }; get '/beer' =&gt; require_role BeerDrinker =&gt; sub { ... };</pre> <p>This approach should be much more solid, and is, I think, as readable as before, and perhaps "more Dancerish" too.</p> <h2><a name="so__how_does_it_work"></a>So, how does it work?</h2> <p>The various <code>require_*</code> keywords return a coderef which checks that the requirements are met (e.g. the user is logged in, has the appropriate role(s)), then executes the coderef given to them - i.e. your route handler.</p> <p>This is more robust than the previous approach, and should work just fine under threads, when debugging, when running Devel::Cover etc.</p> <h2><a name="tested"></a>Tested?</h2> <p>I have tested it myself, and added some more tests to the plugin's test suite; all tests pass correctly. I would very much value reports from anyone who wants to try it out, and would also appreciate any other tests that people think should be added to the module's tests.</p> <h1><a name="author"></a>AUTHOR</h1> <p>David Precious (BIGPRESH)</p> </div> Dancer As A Persistent Interpreter Node http://advent.perldancer.org/2012/15 perl http://advent.perldancer.org/2012/15 Sat, 15 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer_as_a_persistent_interpreter_node"></a>Dancer As A Persistent Interpreter Node</h1> <p>Dancer is first and foremost a web framework. But lightness and flexibility allows it to be much, much more than that. In a way, Dancer is a lot like rum during the Holidays: it can be savored for its own goodness, or it can be used to spike the eggnog, or saturate the fruitcake with yumminess, or as a tool to get the children to sleep, or to bribe the Child Support Representant who comes to inquire about those unethical sleep-inducing methods.</p> <p>For example, let's say that we have scripts that are heavy on the compile-time department. To make them more palatable, we would like to have a daemon that would stay alive between script runs so that the compile time penalty is only hit once. Well, a Dancer app is persistent, and it has this nifty HTTP protocol to talk with clients. So let's use it in a little daemon called <i>santa.pl</i>:</p> <pre class="prettyprint">package Santa; use Path::Class; use Dancer 2 ':syntax'; post '/**' =&gt; sub { my $path = dir('/')-&gt;subdir( map @$_ =&gt; splat )-&gt;stringify; return send_error( "path '$path' is not an executable file" ) unless -x $path; local *STDOUT; open STDOUT, '&gt;', \my $output; eval { do $path; 1 } or return send_error( $@, 500 ); return $output; }; dance;</pre> <p>To interact with that daemon, let's build ourselves an <i>elf.pl</i>:</p> <pre class="prettyprint">#!/usr/bin/env perl use 5.10.0; use Path::Class; my( $script, @args ) = @ARGV; my $santa_url = $ENV{NORTH_POLE} or run_local(@ARGV); require LWP::UserAgent; my $agent = LWP::UserAgent-&gt;new; my $response = $agent-&gt;post($santa_url . file($script)-&gt;absolute, { @args }); unless ( $response-&gt;is_success ) { warn "Santa failed us, we are on our own\n"; run_local(@ARGV); } say $response-&gt;content; sub run_local { warn "running locally\n"; exec @_; }</pre> <p>And we are done. If we fire up <i>santa.pl</i> on our machine, we can now harness the power of Christmas to run our scripts:</p> <pre class="prettyprint">$ time ./elf.pl /home/yanick/work/perl-modules/Galuga/harvest_entries.pl running locally &lt;html&gt;&lt;body&gt;well, hello there&lt;/body&gt;&lt;/html&gt; real 0m1.383s user 0m1.312s sys 0m0.072s $ time NORTH_POLE=http://localhost:3000 ./elf.pl /home/yanick/work/perl-modules/Galuga/harvest_entries.pl &lt;html&gt;&lt;body&gt;well, hello there&lt;/body&gt;&lt;/html&gt; real 0m1.299s user 0m0.108s sys 0m0.016s $ time NORTH_POLE=http://localhost:3000 ./elf.pl /home/yanick/work/perl-modules/Galuga/harvest_entries.pl &lt;html&gt;&lt;body&gt;well, hello there&lt;/body&gt;&lt;/html&gt; real 0m0.159s user 0m0.112s sys 0m0.012s</pre> <p>This extremely scientific sample of three script runs shows that the team of the elves and Santa are doing exactly what we want. Without a server available, the elf runs the script directly (which in this example takes 1.3s). If the server is there, the first run has a comparable time (as the code is interpreted for the first time), but the subsequent runs see their running time dropping from 1.3 seconds to 0.159 seconds. Not too shabby an improvement, for a full server/client system taking less than 40 lines of code...</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Yanick Champoux for the Perl Dancer Advent Calendar 2012.</p> </div> Building a Search Web App with Dancer and Sphinx http://advent.perldancer.org/2012/14 perl http://advent.perldancer.org/2012/14 Fri, 14 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="building_a_search_web_app_with_dancer_and_sphinx"></a>Building a Search Web App with Dancer and Sphinx</h1> <p>In this article, we'll develop a basic search application using Dancer and <a href="http://sphinxsearch.com/">Sphinx</a>. Sphinx is an open source search engine that's fairly easy to use, but powerful enough to be deployed in high-traffic sites, such as Craigslist and Dailymotion.</p> <p>In keeping with this year's Dancer Advent Calendar trend, the example app will be built on Dancer 2, but it should work just as well with Dancer 1.</p> <p>Alright, let's get to work.</p> <h2><a name="the_data"></a>The Data</h2> <p>Our web application will be used to search through documents stored in a MySQL database. We'll use a simple table with the following structure:</p> <pre class="prettyprint">CREATE TABLE documents ( id int NOT NULL AUTO_INCREMENT, title varchar(200) NOT NULL, contents_text text NOT NULL, contents_html text NOT NULL, PRIMARY KEY (id) );</pre> <p>Each document has an unique ID, a title, and contents, stored as both plain text and as HTML. We need the two formats for different purposes -- HTML will be used to display the document in the browser, while plain text will be fed to the indexing mechanism of the search engine (because we do not want to index the HTML tags, obviously).</p> <p>We can populate the database with any kind of document data -- for my test version, I used a simple script to fill the database with POD documentation extracted from Dancer distribution. The script is included at the end of this article, in case you'd like to use it yourself.</p> <h2><a name="installation_and_configuration_of_sphinx"></a>Installation and Configuration of Sphinx</h2> <p>Sphinx can be installed pretty easily, using one of the pre-compiled <code>.rpm</code> or <code>.deb</code> packages, or the source tarball. These are available at the <a href="http://sphinxsearch.com/downloads/release/">download page at SphinxSearch.com</a> -- grab the one that suits you and follow the <a href="http://sphinxsearch.com/docs/current.html#installation">installation instructions</a>.</p> <p>When Sphinx is installed, it needs to be configured before we can play with it. Its main configuration file is usually located at <code>/etc/sphinx/sphinx.conf</code>. For our purposes, a very basic setup will do -- we'll put the following in the <code>sphinx.conf</code> file:</p> <pre class="prettyprint">source documents { type = mysql sql_host = localhost sql_user = user sql_pass = hunter1 sql_db = docs sql_query = \ SELECT id, title, contents_text FROM documents } index test { source = documents charset_type = utf-8 path = /usr/local/sphinx/data/test }</pre> <p>This defines one <i>source</i>, which is what Sphinx uses to gather data, and one <i>index</i>, which will be created by processing the collected data and will then be queried when we perform the searches. In our case, the source is the documents database that we just created. The <code>sql_query</code> directive defines the <code>SELECT</code> query that Sphinx will use to pull the data, and it includes all the fields from the <code>documents</code> table, except <code>contents_html</code> -- like we said, HTML is not supposed to be indexed.</p> <p>That's all that we need to start using Sphinx. After we make sure the <code>searchd</code> daemon is running, we can proceed with indexing the data. We call <code>indexer</code> with the name of the index:</p> <pre class="prettyprint">$ indexer test</pre> <p>It should spit out some information about the indexing operation, and when it's done, we can do our first search:</p> <pre class="prettyprint">$ search "plugin" index 'test': query 'plugin ': returned 8 matches of 8 total in 0.002 sec displaying matches: 1. document=19, weight=2713 2. document=44, weight=2694 3. document=20, weight=1713 4. document=2, weight=1672 5. document=1, weight=1640 6. document=13, weight=1640 7. document=27, weight=1601 8. document=28, weight=1601</pre> <p>Apparently, there are 8 documents in the Dancer documentation with the word <i>plugin</i>, and the one with the ID of 19 is the highest ranking result. Let's see which document that is:</p> <pre class="prettyprint">mysql&gt; SELECT title FROM documents WHERE id = 19; +----------------------------------------------------+ | title | +----------------------------------------------------+ | Dancer::Plugin - helper for writing Dancer plugins | +----------------------------------------------------+</pre> <p>It's the documentation for <a href="https://metacpan.org/module/Dancer::Plugin">Dancer::Plugin</a>, and it makes total sense that this is the first result for the word <i>plugin</i>. Sphinx setup is thus ready and we can get to the web application part of our little project.</p> <h2><a name="the_basic_application"></a>The Basic Application</h2> <p>We'll start with a simple web application (let's call it <code>DancerSearch</code>) that just shows a search form, and then we'll extend it with more features. It will be using Dancer 2.0, and the <a href="https://metacpan.org/module/Dancer::Plugin::Database">Dancer::Plugin::Database</a> plugin (we'll use it to access the documents database). The code below is the initial <code>lib/DancerSearch.pm</code> file:</p> <pre class="prettyprint">package DancerSearch; use Dancer 2.0; use Dancer::Plugin::Database; get '/' =&gt; sub { template 'index'; }; 1;</pre> <p>We're also going to need a little startup script, <code>bin/app.pl</code>:</p> <pre class="prettyprint">#!/usr/bin/env perl use Dancer 2.0; use DancerSearch; start;</pre> <p>And a simple layout -- <code>views/layouts/main.tt</code>:</p> <pre class="prettyprint">&lt;!doctype html&gt; &lt;html&gt; &lt;head&gt; &lt;title&gt;Dancer Search Engine&lt;/title&gt; &lt;link rel="stylesheet" href="css/style.css"&gt; &lt;/head&gt; &lt;body&gt; &lt;h1&gt;Dancer Search Engine&lt;/h1&gt; &lt;div id="content"&gt; [% content %] &lt;/div&gt; &lt;/body&gt; &lt;/html&gt;</pre> <p>And, of course, a template for our index page. For now it'll just contain a search form -- <code>views/index.tt</code>:</p> <pre class="prettyprint">&lt;form action="/" method="get"&gt; Search query: &lt;input type="text" name="query"&gt; &lt;input type="submit" value="Search"&gt; &lt;/form&gt;</pre> <p>Last but not least, we need a configuration file to tell our app which layout we want to use, and how to connect to our documents database using the Dancer::Plugin::Database plugin. This goes into <code>config.yml</code>:</p> <pre class="prettyprint">layout: main plugins: Database: driver: mysql host: localhost database: docs username: user password: hunter1</pre> <p>We can now launch the application, and it will greet us with a search form. Which, unsurprisingly, doesn't work yet. Let's wire it up to Sphinx.</p> <h2><a name="the_sphinx__search_cpan_module"></a>The Sphinx::Search CPAN Module</h2> <p>There is a CPAN module called <a href="https://metacpan.org/module/Sphinx::Search">Sphinx::Search</a> that provides a Perl interface to Sphinx, and we're going to use it in our app. We put <code>use Sphinx::Search</code> in <code>DancerSearch.pm</code>, and add the following piece of code before the <code>get '/'</code> route handler:</p> <pre class="prettyprint"># Create a new Sphinx::Search instance my $sph = Sphinx::Search-&gt;new; # Match all words, sort by relevance, return the first 10 results $sph-&gt;SetMatchMode(SPH_MATCH_ALL); $sph-&gt;SetSortMode(SPH_SORT_RELEVANCE); $sph-&gt;SetLimits(0, 10);</pre> <p>This creates a new instance of Sphinx::Search (which will be used to talk to the Sphinx daemon and do the searches), and sets up a few basic options, such as how many results should be returned and in what order. Now comes the most interesting part -- actually performing a search in our application. We insert this chunk of code at the beginning of the <code>get '/'</code> route handler:</p> <pre class="prettyprint">if (my $phrase = params('query')-&gt;{'phrase'}) { # Send the search query to Sphinx my $results = $sph-&gt;Query($phrase); my $retrieved_count = 0; my $total_count; my $documents = []; if ($total_count = $results-&gt;{'total_found'}) { $retrieved_count = @{$results-&gt;{'matches'}}; # Get the array of document IDs my @document_ids = map { $_-&gt;{'doc'} } @{$results-&gt;{'matches'}}; # Join the IDs to use in SQL query (the IDs come from Sphinx, so we # can trust them to be safe) my $ids_joined = join ',', @document_ids; # Select documents, in the same order as returned by Sphinx # (the contents of $ids_joined comes from Sphinx) my $sth = database-&gt;prepare('SELECT id, title FROM documents ' . "WHERE id IN ($ids_joined) ORDER BY FIELD(id, $ids_joined)"); $sth-&gt;execute; # Fetch all results as an arrayref of hashrefs $documents = $sth-&gt;fetchall_arrayref({}); } # Show search results page return template 'index', { phrase =&gt; encode_entities($phrase), retrieved_count =&gt; $retrieved_count, total_count =&gt; $total_count, documents =&gt; $documents }; }</pre> <p>Let's go through what is happening here. First, we check if there was actually a search phrase in the query string (<code>params('query')-&gt;{'phrase'}</code>). If there was one, we pass it to the <code>$sph-&gt;Query()</code> method, which queries Sphinx and returns the search results (the returned data structure is briefly explained in the description of the Query method in Sphinx::Search documentation).</p> <p>We then check the number of results (<code>$results-&gt;{'total_found'}</code>), and if it's greater than zero, it means we found something and we need to retrieve the documents data from the database. Sphinx only returns the IDs of the matching documents (as shown earlier in the test search that we did using the command line), so we need to send a query to the database to get the actual data, such as document titles that we want to display in the results (note that we're using the <code>ORDER BY FIELD</code> construct in the <code>SELECT</code> query to maintain the same order as the list returned by Sphinx).</p> <p>When we have the documents data ready, we pass it along with other information (such as the total number of results) to be displayed in our index template. But, hold on a second -- the template is not yet ready to display the results, it only shows the search form. Let's fix that now -- below the search form, we add the following code:</p> <pre class="prettyprint">[% IF phrase %] &lt;p&gt;Search results for &lt;strong&gt;&amp;quot;[% phrase %]&amp;quot;&lt;/strong&gt;&lt;/p&gt; [% IF total_count %] &lt;p&gt; Found [% total_count %] hits. Showing results 1 - [% retrieved_count %]. &lt;/p&gt; &lt;ol&gt; [% FOREACH document IN documents %] &lt;li&gt; &lt;a href="/document/[% document.id %]"&gt;[% document.title %]&lt;/a&gt; &lt;/li&gt; [% END %] &lt;/ol&gt; [% ELSE %] &lt;p&gt; No hits -- try again! &lt;/p&gt; [% END %] [% END %]</pre> <p>This displays the phrase that was submitted, the number of hits, and a list of results (or a "no hits" message if there weren't any).</p> <p>And you know what? We're now ready to actually do a search in the browser:</p> <img src="/images/2012/14/search-results.png"> <p>Neat, we have a working search application! We're just missing one important thing, and that is being able to access a document that was found. The results link to <code>/document/:document_id</code>, but that route isn't recognized by our app. No worries, we can fix that easily:</p> <pre class="prettyprint"># Get the document with the specified ID get '/document/:id' =&gt; sub { my $sth = database-&gt;prepare('SELECT contents_html FROM documents ' . 'WHERE id = ?'); $sth-&gt;execute(params-&gt;{'id'}); if (my $document = $sth-&gt;fetchrow_hashref) { return $document-&gt;{'contents_html'}; } else { status 404; return "Document not found"; } };</pre> <p>This route handler is pretty straightforward, we grab the ID from the URL, use it in a <code>SELECT</code> query to the documents table, and return the HTML contents of the matching document (or a 404 page, if there's no document with that ID).</p> <h2><a name="the_complete_application"></a>The Complete Application</h2> <p>This is the complete <code>DancerSearch.pm</code> file:</p> <pre class="prettyprint">package DancerSearch; use Dancer 2.0; use Dancer::Plugin::Database; use HTML::Entities qw( encode_entities ); use Sphinx::Search; # Create a new Sphinx::Search instance my $sph = Sphinx::Search-&gt;new; # Match all words, sort by relevance, return the first 10 results $sph-&gt;SetMatchMode(SPH_MATCH_ALL); $sph-&gt;SetSortMode(SPH_SORT_RELEVANCE); $sph-&gt;SetLimits(0, 10); get '/' =&gt; sub { if (my $phrase = params('query')-&gt;{'phrase'}) { # Send the search query to Sphinx my $results = $sph-&gt;Query($phrase); my $retrieved_count = 0; my $total_count; my $documents = []; if ($total_count = $results-&gt;{'total_found'}) { $retrieved_count = @{$results-&gt;{'matches'}}; # Get the array of document IDs my @document_ids = map { $_-&gt;{'doc'} } @{$results-&gt;{'matches'}}; # Join the IDs to use in SQL query (the IDs come from Sphinx, so we # can trust them to be safe) my $ids_joined = join ',', @document_ids; # Select documents, in the same order as returned by Sphinx my $sth = database-&gt;prepare('SELECT id, title FROM documents ' . "WHERE id IN ($ids_joined) ORDER BY FIELD(id, $ids_joined)"); $sth-&gt;execute; # Fetch all results as an arrayref of hashrefs $documents = $sth-&gt;fetchall_arrayref({}); } # Show search results page return template 'index', { phrase =&gt; encode_entities($phrase), retrieved_count =&gt; $retrieved_count, total_count =&gt; $total_count, documents =&gt; $documents }; } else { # No search phrase -- show just the search form template 'index'; } }; # Get the document with the specified ID get '/document/:id' =&gt; sub { my $sth = database-&gt;prepare('SELECT contents_html FROM documents ' . 'WHERE id = ?'); $sth-&gt;execute(params-&gt;{'id'}); if (my $document = $sth-&gt;fetchrow_hashref) { return $document-&gt;{'contents_html'}; } else { status 404; return "Document not found"; } }; 1;</pre> <h2><a name="conclusion"></a>Conclusion</h2> <p>What we've built is still a very basic application, lacking many features -- the most obvious one that's missing is pagination, and being able to access results further down the list, not just the first ten. However, the code can be easily extended, thanks to the flexibility and ease of use of both Dancer and Sphinx. With a bit of effort, it can be made into an useful search app for a knowledge base site, or a wiki.</p> <p>I think this application is a good example of how Dancer benefits from being part of the Perl ecosystem, giving web developers the ability to make use of the thousands of modules in CPAN (like we just did with Sphinx::Search). This allows to build working prototypes of web applications and implement complex features in a very short time.</p> <h2><a name="the_pod_extraction_script"></a>The POD Extraction Script</h2> <p>As promised, this is the script that I used to extract the POD from Dancer distribution and store it in the MySQL database:</p> <pre class="prettyprint">#!/usr/bin/env perl package MyParser; use strict; use vars qw(@ISA); use Pod::Simple::PullParser (); BEGIN { @ISA = ('Pod::Simple::PullParser') } use DBI; use File::Find; use Pod::Simple::Text; use Pod::Simple::HTML; # Variables to hold the text and HTML produced by POD parsers my ($text, $html); # Create parser objects and tell them where their output will go (my $parser_text = Pod::Simple::Text-&gt;new)-&gt;output_string(\$text); (my $parser_html = Pod::Simple::HTML-&gt;new)-&gt;output_string(\$html); # Initialize database connection my $dbh = DBI-&gt;connect("dbi:mysql:dbname=docs;host=localhost", "user", "hunter1") or die $!; sub run { my $self = shift; my (@tokens, $title); while (my $token = $self-&gt;get_token) { push @tokens, $token; # We're looking for a "=head1 NAME" section if (@tokens &gt; 5) { if ($tokens[0]-&gt;is_start &amp;&amp; $tokens[0]-&gt;tagname eq 'head1' &amp;&amp; $tokens[1]-&gt;is_text &amp;&amp; $tokens[1]-&gt;text =~ /^name$/i &amp;&amp; $tokens[4]-&gt;is_text) { $title = $tokens[4]-&gt;text; # We have the title, so we can ignore the remaining tokens last; } shift @tokens; } } # No title means no POD -- we're done with this file return if !$title; print "Adding: $title\n"; $parser_text-&gt;parse_file($self-&gt;source_filename); $parser_html-&gt;parse_file($self-&gt;source_filename); # Add the new document to the database $dbh-&gt;do("INSERT INTO documents (title, contents_text, " . "contents_html) VALUES(?, ?, ?)", undef, $title, $text, $html); # Clear the content variables and reinitialize parsers $text = $html = ""; $parser_text-&gt;reinit; $parser_html-&gt;reinit; } my $parser = MyParser-&gt;new; find({ wanted =&gt; sub { if (-f and /\.pm$|\.pod$/) { $parser-&gt;parse_file($File::Find::name); $parser-&gt;reinit; } }, no_chdir =&gt; 1 }, shift || '.');</pre> <p>You can run it with one argument, which is the location of the directory that will be scanned (recursively) for .pm/.pod files, or with no arguments, in which case the script will work with the current directory.</p> <p>(Note: The script makes use of <a href="https://metacpan.org/module/Pod::Simple">Pod::Simple</a>, which I'm not very familiar with, so it's possible that I'm doing something stupid with it -- if that's the case, please let me know.)</p> <h1><a name="author"></a>Author</h1> <p><a href="http://odyniec.net/">Michal Wojciechowski</a>, <code>&lt;odyniec@odyniec.net&gt;</code></p> </div> The Gift Of Auto-Completion http://advent.perldancer.org/2012/13 perl http://advent.perldancer.org/2012/13 Thu, 13 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="the_gift_of_auto_completion"></a>The Gift Of Auto-Completion</h1> <p>A good programmer, just like a good North Pole elf, needs good tools to perform his craft. One of the classics is code snippet auto-completion. The <i>vim</i> editor has a few of them. Two of the best-known ones are <i>snipMate</i>, <a href="https://github.com/garbas/vim-snipmate">https://github.com/garbas/vim-snipmate</a>, inspired by the much liked <i>TextMate</i> editor (<a href="http://macromates.com">http://macromates.com</a>), and the newly-emerging <i>UltiSnips</i> (<a href="https://github.com/SirVer/ultisnips">https://github.com/SirVer/ultisnips</a>).</p> <p>Up to recently, I only knew (and loved quite dearly) <i>snipMate</i>, but after discovering <i>UltiSnips</i>, I must say that I'm quite impressed by it. ... ah well, there is no point to make you kids wait to unwrap your present: you can go and get it at <a href="https://gist.github.com/4273473">https://gist.github.com/4273473</a>. Yes, it's a snippet file for the whole of Dancer's DSL. Not as exciting as a Rocket-Raptor Sky Armageddon action figure, granted, but much better than a rainbow-colored pullover.</p> <h2><a name="let_s_have_some_milk_with_those_cookies"></a>Let's Have Some Milk With Those Cookies</h2> <p>With <i>UltiSnips</i> and the snippet file given above, the whole Dancer DSL is at your fingertip by just issuing the vi command</p> <pre class="prettyprint">:UltiSnipsAddFiletypes dancer</pre> <p>Which is awesome. But this is Dancer; awesome is our bread and butter. Can we somewhat make this ultrawesome? Well, how about have <i>UltiSnips</i> discover all by itself if a file is Dancer-related, and active its snippets automatically by adding to our <i>.vimrc</i>:</p> <pre class="prettyprint">au BufNewFile,BufRead *.pl,*.pm call s:detectTypeOfPerl() fun! s:detectTypeOfPerl() perl &lt;&lt;EOF for ( 0..$curbuf-&gt;Count ) { if ( $curbuf-&gt;Get($_) =~ /use Dancer/ ) { VIM::DoCommand(':UltiSnipsAddFiletypes dancer'); return; } } EOF endfun</pre> <p>Aaaah yes. Just like a little swig of rum in a mug of eggnog, this is making what was already a treat into something truly decadent... Enjoy!</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Yanick Champoux for the Perl Dancer Advent Calendar 2012.</p> </div> Pluggable Route Handlers for Dancer 2 http://advent.perldancer.org/2012/12 perl http://advent.perldancer.org/2012/12 Wed, 12 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="pluggable_route_handlers_for_dancer_2"></a>Pluggable Route Handlers for Dancer 2</h1> <p>With Dancer 2 you can declare route handlers in a new manner so that they can be shared among applications easily. It's actually like packaging a route handler in its own package. It's called simply <i>Handler</i>, this article will show you how it works, how to write your own handlers and how to use them in your app.</p> <h2><a name="the_concept"></a>The concept</h2> <p>The idea is simple: what about a generic route, a route that would make sense in many applications, a route that is agnostic of the business logic the app? Plugin you say? Indeed, a plugin that contains the route would work, but what about something lighter, more "in the core" and configurable?</p> <p>That's what a handler is. For instance, in Dancer 2, there are two handlers shipped with the core. The first one is responsible for serving pages that match an existing template (the <code>auto_page</code> setting) and one is here to serve static files.</p> <p>Let's see how this work, you'll see how straight forward it is.</p> <h2><a name="an_example__the_autopage_handler"></a>An example: the AutoPage handler</h2> <p>It's easier to describe what a handler is with a real-life example, let's take the <code>auto_page</code> feature. You probably know it already, it's a core feature of Dancer 1, the idea is simple: if a view exists with a name that matches the requested path, Dancer should process the request by processing the template.</p> <p>For instance, if I hit <code>/foo</code> and <code>views/foo.tt</code> exists, the response should be the same as the one provided by the route:</p> <pre class="prettyprint">get '/foo' =&gt; sub { template 'foo' };</pre> <p>That's the <code>auto_page</code> feature, in Dancer 1 it's hard-coded in the core, in Dancer 2, it's a <i>handler</i> that is set by default.</p> <p>A route handler is a class that consumes the <code>Dancer::Core::Role::Handler</code> role. The class must implement a set of methods: <code>methods</code>, <code>regexp</code> and <code>code</code> which will be used to declare the route.</p> <p>Let's look at <code>Dancer::Handler::AutoPage</code>.</p> <p>First, the matching methods are <code>get</code> and <code>head</code>, of course:</p> <pre class="prettyprint">sub methods { qw(head get) }</pre> <p>Then, the regexp or the <i>path</i> we want to match:</p> <pre class="prettyprint">sub regexp { '/:page' }</pre> <p>Anything will be matched by this route, yes because we want a chance to look if there's a view named with the value of the <code>page</code> token. If not, the route needs to <code>pass</code>, letting the dispatching flow to proceed further.</p> <pre class="prettyprint">sub code { sub { my $ctx = shift; my $template = $ctx-&gt;app-&gt;config-&gt;{template}; if (! defined $template) { $ctx-&gt;response-&gt;has_passed(1); return; } my $page = $ctx-&gt;request-&gt;params-&gt;{'page'}; my $view_path = $template-&gt;view($page); if (! -f $view_path) { $ctx-&gt;response-&gt;has_passed(1); return; } my $ct = $template-&gt;process($page); $ctx-&gt;response-&gt;header('Content-Length', length($ct)); return ($ctx-&gt;request-&gt;method eq 'GET') ? $ct : ''; }; }</pre> <p>As you can see, the <code>code</code> method is passed the <code>Dancer::Core::Context</code> object which provides access to anything needed to process the request.</p> <p>A <code>register</code> is then implemented to add the route to the registry, and it's done, as you can see, register does nothing if the <code>auto_page</code> setting is off.</p> <p>So that's even better than in Dancer 1: if the setting is off, the code for auto_page does not even exist in the dispatcher tree.</p> <pre class="prettyprint">sub register { my ($self, $app) = @_; return unless $app-&gt;config-&gt;{auto_page}; $app-&gt;add_route( method =&gt; $_, regexp =&gt; $self-&gt;regexp, code =&gt; $self-&gt;code, ) for $self-&gt;methods; }</pre> <p>And <i>voila</i>, we have a route handler.</p> <h2><a name="what_about_shipping_your_own"></a>What about shipping your own</h2> <p>I have the feeling that route handlers could become a new kind of plugins, a more advanced way of sharing knowledge between applications, maybe more subtle. But you may ask at this point what's the best way to <i>add</i> a random handler into your app.</p> <p>Well, the config parser will look for a <code>route_handlers</code> section. Any handler defined here will be loaded. For instance, the default config for any Dancer 2 application is as follows:</p> <pre class="prettyprint">route_handlers: File: public_dir: /path/to/public AutoPage: 1</pre> <h2><a name="going_further"></a>Going further</h2> <p>Route handlers are a very new way to share knowledge between applications. It's another way of doing things, no DSL here, everything should be done with the internal API of the core. I'm sure we've only scratched the surface here, and I'd be very interested to see other things done this way.</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Alexis Sukrieh for the Perl Dancer Advent Calendar 2012.</p> </div> Simple, but more flexible, CRUD http://advent.perldancer.org/2012/11 perl http://advent.perldancer.org/2012/11 Tue, 11 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="simple__but_more_flexible__crud"></a>Simple, but more flexible, CRUD</h1> <p>I've <a href="http://advent.perldancer.org/2011/2">wrote last year</a> about my <a href="https://metacpan.org/module/Dancer::Plugin::SimpleCRUD">Dancer::Plugin::SimpleCRUD</a>, which offers simple and easy automatic CRUD operations for Dancer apps.</p> <p>So, if you don't know about it yet, have a read of that post first; there seems no point in duplicating its content here.</p> <h2><a name="so__what_s_new"></a>So, what's new?</h2> <p>A quick rundown of some of the useful new features added to the SimpleCRUD plugin this year, by myself and contributors:</p> <ul> <li><a name="item_custom_columns_feature"></a><b>custom_columns feature</b> <p>Courtesy of Michael J South (msouth), this new feature allows the record listing screen to include custom columns, built up using SQL statements if required or simple column names, with transformations to be passed to <a href="https://metacpan.org/module/HTML::Table::FromDatabase">HTML::Table::FromDatabase</a> to transform the contents easily (for instance, wrapping email addresses/URLs in anchor tags, formatting, etc).</p> </li> <li><a name="item_add_edit_row_hook"></a><b>add_edit_row hook</b> <p>A new feature suggested / requested by Rene on IRC; a hook which fires right before a new row is created / an existing row is edited, letting you massage the data before it hits the database.</p> </li> <li><a name="item_basic_foreign_key_support"></a><b>basic foreign key support</b> <p>A new option to let you specify that certain columns are foreign keys, and describe which table they refer to, and which columns in the foreign table should be used as the key and the label, respectively.</p> <p>For an example, from the example app shipped with the plugin (some settings omitted for brevity, see the <a href="https://github.com/bigpresh/Dancer-Plugin-Auth-Extensible/blob/master/example/config.yml">full config on GitHub</a>:</p> <pre class="prettyprint">simple_crud( db_table =&gt; 'people', ... foreign_keys =&gt; { employer_id =&gt; { table =&gt; 'employer', key_column =&gt; 'id', label_column =&gt; 'name', }, }, ... );</pre> <p>The above example means that the column <code>employer_id</code> in the <code>people</code> table refers to the key column <code>id</code> of the <code>employer</code> table, and that the <code>name</code> column of the <code>employer</code> table should be used for the labels when selecting a user's employer when adding/editing records.</p> <p>In a future version, it may be possible for this to be done automatically by inspecting the database schema, but I'm not sure how it could decide which column(s) of the foreign table is the human-friendly name/description/whatever.</p> </li> <li><a name="item_new_input_types_option"></a><b>new input_types option</b> <p>Default form field types are selected automatically, and will usually be appropriate, but the <code>input_types</code> option lets you override the default choices if you wish.</p> </li> <li><a name="item_auto_pretty_headers"></a><b>auto_pretty_headers</b> <p>Uses a new feature in <a href="https://metacpan.org/module/HTML::Table::FromDatabase">HTML::Table::FromDatabase</a>, <code>auto_pretty_headers</code>, to automatically make table headers prettier when displaying records - for instance, <code>first_name</code> would become <code>First Name</code>.</p> </li> <li><a name="item_various_bugfixes"></a><b>various bugfixes</b> <p>Various bug fixes, including issues if the prefix changes after the <code>simple_crud</code> call, and working properly with named database connections.</p> </li> </ul> <p>Many thanks to the contributors mentioned above for their valued contributions!</p> <h2><a name="sorry_for_lateness___"></a>Sorry for lateness...</h2> <p>Unfortunately, work, personal commitments and hangovers conspired against me, and I ended up being late writing this post. Sorry.</p> <h1><a name="author"></a>AUTHOR</h1> <p>David Precious (bigpresh)</p> </div> Dancecard http://advent.perldancer.org/2012/10 perl http://advent.perldancer.org/2012/10 Mon, 10 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="dancecard"></a>Dancecard</h1> <p>It has been mentioned before that a web framework is nothing without its ecosystem. Let's face it, <i>Dancer</i> just wouldn't be half as nice without all its plugins and interfaces to template systems. Likewise, web development with <i>Dancer</i> (or any other framework) would be much more of a chore without all the nifty JavaScript libraries out there. This is why <i>jQuery</i> comes pre-packaged with <i>Dancer</i>: if the cast majority of projects will use it, it just makes sense to have it already there.</p> <p>But that about all the other JavaScript librabries that you're using? Wouldn't be nice to keep a registry of them, and be able to quickly add them to any new project? Yes it would. So let's take a few moments, and see if we can record all those potential dance partners on a dance card...</p> <h2><a name="building_the_registry"></a>Building the Registry</h2> <p>First, we want our little utility to store the different projects are where to get them. For this first iteration, let's assume that all those projects will be plucked from Git repositories.</p> <pre class="prettyprint">use Path::Class; use File::HomeDir; use Config::INI::Reader; my $config_file = file( File::HomeDir-&gt;my_home, '.dancecard' ); add_repo(@ARGV); sub add_repo { my( $url, $name ) = @_; unless ( $name ) { $name = $1 if $url =~ m#([^/]+?)(?:\.git)?$#; } open my $config_fh, '&gt;&gt;', $config_file-&gt;stringify; print $config_fh &lt;&lt;"END_REMOTE"; [$name] url = $url END_REMOTE say "library '$name' added" }</pre> <p>Primitive, but for the moment it'll do.</p> <h2><a name="adding_the_libraries_to_the_current_project"></a>Adding the Libraries to the Current Project</h2> <p>Now, let's add the possibility to add any of those libraries to the current project. Let's be fancy and let's do it through an interactive menu. Oh, and let's show in green libraries that we can add, and in blue libraries that are already present:</p> <pre class="prettyprint">use Term::Prompt; use Term::ANSIColor; sub select_libs { $config_file-&gt;touch; my $config = Config::INI::Reader-&gt;read_file($config_file-&gt;stringify); die "no library added yet" unless %$config; die "no 'public/' directory found, are we in a Dancer project?\n" unless -d 'public'; while ( 1 ) { my @menu_items = map { colored( $_, -d dir('public', $_ ) ? 'blue' : 'green' ); } keys %$config; my @results = prompt( 'm', { prompt =&gt; 'libraries to add', title =&gt; "\ndev libaries", items =&gt; \@menu_items, accept_empty_selection =&gt; 1, accept_multiple_selections =&gt; 1, }, 'empty to exit', undef ); last unless @results; for( (keys %$config)[@results] ) { copy_lib( $_ =&gt; $config-&gt;{$_} ); } } } sub copy_lib { my( $name, $data ) = @_; require Git::Repository; say "\ncloning '$name'..."; Git::Repository-&gt;run( clone =&gt; $data-&gt;{url}, "public/".$name ); say 'done'; }</pre> <h2><a name="putting_the__code_dancecard__code__together"></a>Putting the <code>dancecard</code> together</h2> <p>And now, we put it all together:</p> <pre class="prettyprint">#!/usr/bin/env perl use 5.10.0; use strict; use warnings; use Path::Class; use Term::Prompt; use Term::ANSIColor; use File::HomeDir; use Config::INI::Reader; my $config_file = file( File::HomeDir-&gt;my_home, '.dancecard' ); if ( @ARGV ) { add_repo(@ARGV); } else { select_libs(); } sub add_repo { my( $url, $name ) = @_; unless ( $name ) { $name = $1 if $url =~ m#([^/]+?)(?:\.git)?$#; } open my $config_fh, '&gt;&gt;', $config_file-&gt;stringify; print $config_fh &lt;&lt;"END_REMOTE"; [$name] url = $url END_REMOTE say "library '$name' added" } sub select_libs { $config_file-&gt;touch; my $config = Config::INI::Reader-&gt;read_file($config_file-&gt;stringify); die "no library added yet" unless %$config; die "no 'public/' directory found, are we in a Dancer project?\n" unless -d 'public'; while ( 1 ) { my @menu_items = map { colored( $_, -d dir('public', $_ ) ? 'blue' : 'green' ); } keys %$config; my @results = prompt( 'm', { prompt =&gt; 'libraries to add', title =&gt; "\ndev libaries", items =&gt; \@menu_items, accept_empty_selection =&gt; 1, accept_multiple_selections =&gt; 1, }, 'empty to exit', undef ); last unless @results; for( (keys %$config)[@results] ) { copy_lib( $_ =&gt; $config-&gt;{$_} ); } } } sub copy_lib { my( $name, $data ) = @_; require Git::Repository; say "\ncloning '$name'..."; Git::Repository-&gt;run( clone =&gt; $data-&gt;{url}, "public/".$name ); say 'done'; }</pre> <p>The result (without, alas, the full color display):</p> <pre class="prettyprint">$ dancecard git://github.com/Seldaek/slippy.git library 'slippy' added $ dancecard git://github.com/twitter/bootstrap.git library 'bootstrap' added $ dancecard git://github.com/FortAwesome/Font-Awesome.git library 'Font-Awesome' added $ dancer -a myapp &amp;&amp; cd myapp $ dancecard dev libaries ------------- 1) Font-Awesome 2) bootstrap 3) slippy libraries to add (empty to exit) 1 cloning 'Font-Awesome'... done dev libaries ------------- 1) *Font-Awesome* 2) bootstrap 3) slippy libraries to add (empty to exit) 2 cloning 'bootstrap'... done dev libaries ------------- 1) *Font-Awesome* 2) *bootstrap* 3) slippy libraries to add (empty to exit)</pre> <h2><a name="conclusion"></a>Conclusion</h2> <p>This little <code>dancecard</code> script is already a handy little thing. But it only scratches the surface of what could be done: there could be an option to only download the latest version of a library instead of the whole Git history, direct http links instead of git urls, ways to group libraries together, a check that would update the current libraries to their latest versions, etc. This, however, is feature creep that will have to wait for another day...</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Yanick Champoux for the Perl Dancer Advent Calendar 2012.</p> </div> Dancer training at LPW2012 http://advent.perldancer.org/2012/9 perl http://advent.perldancer.org/2012/9 Sun, 9 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer_training_at_lpw2012"></a>Dancer training at LPW2012</h1> <p>Andrew Solomon again ran a helpful training session at this year's London Perl Workshop, <a href="http://act.yapc.eu/lpw2012/talk/4425">Web development using Dancer</a> which was a hands-on session to introduce Perl newbies to how easy web development can be using Perl and Dancer.</p> <p>The session covered:</p> <ul> <li><a name="item_learning_to_use_the_Dancer_framework"></a><b>learning to use the Dancer framework</b> </li> <li><a name="item_learning_to_use_Template_Toolkit"></a><b>learning to use Template Toolkit</b> </li> <li><a name="item_understanding_the_concept_of_Model_View_Controller"></a><b>understanding the concept of Model-View-Controller</b> </li> <li><a name="item_how_to_structure_code_for_maintainability"></a><b>how to structure code for maintainability</b> </li> <li><a name="item_gaining_experience_using_object_oriented_Perl_modules"></a><b>gaining experience using object oriented Perl modules</b> </li> </ul> <p>There were 16 attendees, and feedback on the course was generally very positive.</p> <p>Dancer also appeared in the content of people's talks at LPW2012 too, which was great to see.</p> <h1><a name="author"></a>AUTHOR</h1> <p>David Precious (BIGPRESH)</p> </div> Porting Dancer Template Modules to Dancer 2 http://advent.perldancer.org/2012/8 perl http://advent.perldancer.org/2012/8 Sat, 8 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="porting_dancer_template_modules_to_dancer_2"></a>Porting Dancer Template Modules to Dancer 2</h1> <p>As many advent entries have mentioned so far, Dancer is awesome, and Dancer 2 is awesome squared. But realistically that core -- the framework itself -- is only the huge, basted-to-perfection turkey sitting in the middle of the Dancer application feast. It wouldn't just be the same without all the condiments (let's call them <i>plugins</i>), trimmings (or <i>templates systems</i>) and delicious side-dishes (<i>session managers</i> and other things).</p> <p>Now, it makes perfect sense that authors of smashing appetizers and nibblers for Dancer 1 would love to bring their delicacies to the Dancer 2 table. But there's a catch: the guts of Dancer 1 and Dancer 2 are quite different and the port will require a little bit of work (think of it as taking a recipe made for the part of family who are into Christmas, and adapting it for the part who are celebrating Hanukkah). The good news, though, is that for most cases there is really only a minimal amount of work involved -- something as easy as putting a little bit of cranberry sauce over those nice warm latkes, so to speak.</p> <p>Today, we'll cover templates, and I'll show you how I tweaked <a href="https://metacpan.org/module/Dancer::Template::Mustache">Dancer::Template::Mustache</a> to be compatible with Dancer 2.</p> <h2><a name="the_ghost_of_christmas_past"></a>The Ghost Of Christmas Past</h2> <p>Dancer template modules are usually a very thin interface between the Dancer framework and the "real" work-horse module implementing the templating system. <a href="https://metacpan.org/module/Dancer::Template::Mustache">Dancer::Template::Mustache</a> doesn't balk the trend. As per its last release, it was looking like so</p> <pre class="prettyprint">package Dancer::Template::Mustache; use strict; use warnings; use Template::Mustache; use Dancer::Config 'setting'; use base 'Dancer::Template::Abstract'; sub default_tmpl_ext { "mustache" }; my $_mustache; my $_template_path; sub init { my $self = shift; my %config = %{$self-&gt;config || {}}; $_mustache = Template::Mustache-&gt;new( %config ); $_template_path = setting( 'views' ) || $FindBin::Bin . '/views'; } sub render { my ($self, $template, $tokens) = @_; local $Template::Mustache::template_path = $_template_path; # remove the views part $template =~ s#^\Q$_template_path\E/?##; local $Template::Mustache::template_file = $template; return $_mustache-&gt;render($tokens); } 1;</pre> <h2><a name="the_ghost_of_christmas_present"></a>The Ghost Of Christmas Present</h2> <p>Before jumping into Dancer 2 conversion, let's tidy up and prepare the code a little bit. As Dancer 2 is <a href="https://metacpan.org/module/Moo">Moo</a>-based, we could take advantage of it and shed those closure variables:</p> <pre class="prettyprint">package Dancer::Template::Mustache; use strict; use warnings; use Template::Mustache; use Dancer::Config 'setting'; use FindBin; use Moo; extends 'Dancer::Template::Abstract'; sub default_tmpl_ext { "mustache" }; has _engine =&gt; ( is =&gt; 'ro', lazy =&gt; 1, default =&gt; sub { Template::Mustache-&gt;new( %{ $_[0]-&gt;config } ); }, ); has _template_path =&gt; ( is =&gt; 'ro', lazy =&gt; 1, default =&gt; sub { setting( 'views' ) || $FindBin::Bin . '/views'; }, ); sub render { my ($self, $template, $tokens) = @_; my $_template_path = $self-&gt;_template_path; local $Template::Mustache::template_path = $_template_path; # remove the views part $template =~ s#^\Q$_template_path\E/?##; local $Template::Mustache::template_file = $template; return $self-&gt;_engine-&gt;render($tokens); } 1;</pre> <p>There, already looking better. And thanks to the laziness of the attributes, we were even able to do away with the c&lt;init()&gt; method. Sweet.</p> <h2><a name="the_ghost_of_christmases_yet_to_come"></a>The Ghost of Christmases Yet to Come</h2> <p>The big difference for templates between the Dancers is that a template was a sub-class of <i>Dancer::Template::Abstract</i> in Dancer 1, and becomes a class consuming the role <i>Dancer::Core::Role::Template</i> in Dancer 2. The good news? With Moo (and Moose), sub-classing or assigning roles are done at run-time, so we'll be able to juggle between the two fairly easily. Just watch:</p> <pre class="prettyprint">require Dancer; use Moo; if ( Dancer-&gt;VERSION &gt;= 2 ) { with 'Dancer::Core::Role::Template'; } else { require Dancer::Config; Dancer::Config-&gt;import( 'setting' ); extends 'Dancer::Template::Abstract'; }</pre> <p>Not too hard, is it? Second difference is the way we access the <code>views</code> directory value:</p> <pre class="prettyprint">has api_version =&gt; ( is =&gt; 'ro', lazy =&gt; 1, default =&gt; sub { int Dancer-&gt;VERSION }, ); has _template_path =&gt; ( is =&gt; 'ro', lazy =&gt; 1, default =&gt; sub { ( $_[0]-&gt;api_version == 1 ? setting( 'views' ) : $_[0]-&gt;views ) || $FindBin::Bin . '/views'; }, );</pre> <p>Oh, and I almost forgot: Dancer 2 also wants to know the name of the template module:</p> <pre class="prettyprint">sub _build_name { 'Dancer::Template::Mustache' }</pre> <p>(that part will not even be required soon, if I can have my wicked, patchy ways).</p> <p>And that's it. Put those three changes together with the original code, and we have our multi-dancing template module done:</p> <pre class="prettyprint">package Dancer::Template::Mustache; use strict; use warnings; use Template::Mustache; use FindBin; require Dancer; use Moo; if ( Dancer-&gt;VERSION &gt;= 2 ) { with 'Dancer::Core::Role::Template'; } else { require Dancer::Config; Dancer::Config-&gt;import( 'setting' ); extends 'Dancer::Template::Abstract'; } sub _build_name { 'Dancer::Template::Mustache' } sub default_tmpl_ext { "mustache" }; has api_version =&gt; ( is =&gt; 'ro', lazy =&gt; 1, default =&gt; sub { int Dancer-&gt;VERSION }, ); has _engine =&gt; ( is =&gt; 'ro', lazy =&gt; 1, default =&gt; sub { Template::Mustache-&gt;new( %{ $_[0]-&gt;config } ); }, ); has _template_path =&gt; ( is =&gt; 'ro', lazy =&gt; 1, default =&gt; sub { ( $_[0]-&gt;api_version == 1 ? setting( 'views' ) : $_[0]-&gt;views ) || $FindBin::Bin . '/views'; }, ); sub render { my ($self, $template, $tokens) = @_; $self-&gt;name; my $_template_path = $self-&gt;_template_path; local $Template::Mustache::template_path = $_template_path; # remove the views part $template =~ s#^\Q$_template_path\E/?##; local $Template::Mustache::template_file = $template; return $self-&gt;_engine-&gt;render($tokens); } 1;</pre> <h2><a name="boxing_day_events"></a>Boxing Day Events</h2> <p>For the conversion of <code>Dancer::Template::Mustache</code>, two other tweaks had to be done to the distribution. The invocations of c&lt;Dancer::Test&gt; had to include the name of the test application:</p> <pre class="prettyprint">{ package MyApp; use Dancer ':syntax'; ...; } use Dancer::Test 'MyApp'; response_content_like [ GET =&gt; '/' ], qr/Welcome manly mustached man/, "template file found";</pre> <p>And the views had to be moved under <code>t/views</code>, as Dancer 2 seems not to understand custom views locations yet (although, in all fairness, that could be more a case of yours truly being dense).</p> <h2><a name="conclusion"></a>Conclusion</h2> <p>Converting template modules to the new Dancer is nowhere as tough as one could have dreaded. With a little bit of dedication and elbow grease, we could have this part of the eco-system ready to make the jump to Dancer 2 (while keeping a solid footing in Dancer 1) real soon. And with the help of eggnogs and a few nights by the fireplace, maybe sooner still...</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Yanick Champoux for the Perl Dancer Advent Calendar 2012.</p> </div> A first app with Dancer 2 http://advent.perldancer.org/2012/7 perl http://advent.perldancer.org/2012/7 Fri, 7 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="a_first_app_with_dancer_2"></a>A first app with Dancer 2</h1> <p>In the previous article we've seen how concrete Dancer&nbsp;2 is, already. It's not something in the air, it's real and is powering the website you're reading right now.</p> <p>The ecosystem still needs to be fully ported but it doesn't prevent you from starting a webapp with Dancer&nbsp;2. Actually, that's the best way you could help Dancer&nbsp;2 moving forward!</p> <p>In this article we'll see how to build an application with Dancer2, from scratch. We'll try to show off here one of the most important benefit of the design change in Dancer's core: its application scoping.</p> <p>I'm going to be a little bit harsh towards Dancer&nbsp;1 in this demonstration - but as we say in French: <i>Qui aime bien chatie bien</i>, which translates roughly to tough love, or being cruel to be kind.</p> <p>Anyway! The main idea behind this article is to show off how the design changes of Dancer&nbsp;2 will make your life easier, as an app developer. So we'll do something simple with Dancer&nbsp;2 and see how Dancer&nbsp;1 deals with it. That's&nbsp;-&nbsp;I think&nbsp;-&nbsp;a good way to understand <i>why</i> the rewrite was necessary.</p> <p>But first, we need an app!</p> <h2><a name="let_s_start_with_a_single_file_app"></a>Let's start with a single-file app</h2> <p>First, we need to create the structure for the application dir. Of course, we can keep it simple, the <i>micro-framework</i> way:</p> <pre class="prettyprint">$ cat &gt; app.pl #!/usr/bin/env perl use Dancer 2.0; get '/' =&gt; sub { 'hello 2!' }; start;</pre> <p>It's a valid Dancer&nbsp;2 app, of course, but that's not very interesting, it's a hello world, and as Dancer&nbsp;2's DSL is fully compatible with Dancer&nbsp;1, there is nothing new here.</p> <p>Let's create a real appdir structure. We could use the <code>dancer</code> helper provided with Dancer&nbsp;1, but I'd like to show you how to do that by hand, there is clearly not much to do.</p> <h2><a name="a_more_structured_application"></a>A more structured application</h2> <p>First of all, the structure:</p> <pre class="prettyprint">mkdir -p lib bin views/layouts public t</pre> <p>Now, let's create a basic layout and a view.</p> <pre class="prettyprint">cat &gt; views/layouts/main.tt &lt;html&gt; &lt;head&gt;&lt;title&gt;DemoApp&lt;/title&gt;&lt;/head&gt; &lt;body&gt; &lt;h1&gt;DemoApp&gt;/h1&gt; [% content %] &lt;/body&gt; &lt;/html&gt;</pre> <p>If you know Dancer&nbsp;1, you should have spotted something already. Got it? Yes, the <a href="https://metacpan.org/module/Template">Template</a> tags! We're back to TT's default: <code>[%</code> and <code>%]</code>. That was one of the most wanted change since Dancer has been out. Dancer&nbsp;2 is a good opportunity to drop that cosmetic change.</p> <p>OK, we have a layout, let's create the first view.</p> <pre class="prettyprint">cat &gt; views/index.tt Hello Dancer 2</pre> <p>Now, the main app, to create the first route.</p> <pre class="prettyprint">cat &gt; lib/DemoApp.pm package DemoApp; use Dancer 2.0; get '/' =&gt; sub { template 'index'; }; 1;</pre> <p>And finally, the starter script.</p> <pre class="prettyprint">cat &gt; bin/app.pl #!/usr/bin/env perl use Dancer 2.0; use DemoApp; start;</pre> <p>Again, here, you cannot tell the difference with Dancer&nbsp;1. It's just the same. So that's not very interesting. Hmm, wait, if we want to see a bit of the guts of Dancer&nbsp;2, we already can.</p> <p>Let's start the application with the core debug flag:</p> <pre class="prettyprint">$ DANCER_DEBUG_CORE=1 perl -I../Dancer2/lib ./bin/app.pl core: binding import method to main core: binding app to main core: exporting DSL symbols for main core: binding import method to DemoApp core: binding app to DemoApp core: exporting DSL symbols for DemoApp core: [DemoApp] -&gt; get(/, CODE(0x2170aa0)) core: [main] -&gt; start() HTTP::Server::Simple::PSGI: You can connect to your server at http://localhost:3000/</pre> <p>That's interesting because you can see here how everything is scoped properly. There are already two "apps" (<code>Dancer::Core::App</code> objects), one for main (<code>bin/app.pl</code>) and one for <code>DemoApp.pm</code>.</p> <p>Any call to a DSL keyword will be logged, with its arguments. Like you can see above for <code>get</code> and <code>start</code>. We also see from which app they are called. Imagine all the help it can provide when debugging your app.</p> <p>If I hit my app, I should see a call to template, as my <code>/</code> route uses it:</p> <pre class="prettyprint">$ curl http://0:3000/ core: [DemoApp] -&gt; template(index) ...</pre> <p>We clearly see that everything is scoped, let's try to demonstrate the benefits of that.</p> <h2><a name="scoped_applications"></a>Scoped applications</h2> <p>Let's say that I want my application to provide a set of routes which share a common set configuration. For instance, I want all the routes here to disable the layout and to have a prefix.</p> <p>Also I want all these routes to be available only under a specific environment.</p> <p>A simple and straight-forward example is to use a <i>debug</i> namespace: We want a set of route for our debugging purposes, so we're going to add <code>DemoApp::DebugRoutes</code>:</p> <pre class="prettyprint">mkdir -p lib/DemoApp cat &gt; lib/DemoApp/DebugRoutes.pm package DemoApp::DebugRoutes; use Dancer 2.0; # First we add a prefix prefix '/debug'; # Make sure we don't have a layout set here set layout =&gt; undef; # Filter requestes for developnment env only hook 'before' =&gt; sub { if (dancer_app-&gt;environment ne 'development') { status 404; return halt; } }; # a first route get '/env' =&gt; sub { to_dumper(request-&gt;env); }; 1;</pre> <p>Now, use that new set of routes in the main app.</p> <pre class="prettyprint"># in lib/DemoApp.pm ... use DemoApp::DebugRoutes;</pre> <p>Great, let's test that. We should see the first route as before, and of course we should see <code>DebugRoutes</code> in the debug output:</p> <pre class="prettyprint">$ DANCER_DEBUG_CORE=1 perl -I../Dancer2/lib ./bin/app.pl [...] core: [DemoApp::DebugRoutes] -&gt; prefix(/debug) core: [DemoApp::DebugRoutes] -&gt; set(layout, &lt;undef&gt;) core: [DemoApp::DebugRoutes] -&gt; hook(before, CODE(0x1f54cd8)) core: [DemoApp::DebugRoutes] -&gt; get(/env, CODE(0x1f7b588)) core: [DemoApp] -&gt; get(/, CODE(0x1f32320)) core: [main] -&gt; start() HTTP::Server::Simple...</pre> <p>Looks great, let's test it. The first route should remain unchanged.</p> <pre class="prettyprint">$ curl -i http://0:3000/ core: [DemoApp] -&gt; template(index) HTTP/1.0 200 OK Server: Perl Dancer Content-Length: 85 Content-Type: text/html; charset=UTF-8 &lt;html&gt; &lt;head&gt; &lt;title&gt;DemoApp&lt;/title&gt; &lt;/head&gt; &lt;body&gt; Hello Dancer 2 &lt;/body&gt; &lt;/html&gt;</pre> <p>OK, so far so good. Now, let's try the <code>/debug/env</code> route we added. It's under the prefix <code>/debug</code>, should not provide the layout and should only be available under development env (which is the case now):</p> <pre class="prettyprint">$ curl http://0:3000/debug/env core: [DemoApp::DebugRoutes] -&gt; dancer_app() core: [DemoApp::DebugRoutes] -&gt; request() core: [DemoApp::DebugRoutes] -&gt; to_dumper(HASH(0x2148ed0)) HTTP/1.0 200 OK [...] $VAR1 = { 'psgi.multiprocess' =&gt; 0, 'SERVER_NAME' =&gt; '0.0.0.0', 'SCRIPT_NAME' =&gt; '', 'PATH_INFO' =&gt; '/debug/env', [...]</pre> <p>Looks to work!</p> <p>Now, let's restart the app under a different environment, the <code>before</code> hook should block the route while not doing it for the main app.</p> <pre class="prettyprint">$ DANCER_ENVIRONMENT=prod \ DANCER_DEBUG_CORE=1 \ perl -I../Dancer2/lib ./bin/app.pl &amp; [...] $ curl -i http://0:3000/debug/env core: [DemoApp::DebugRoutes] -&gt; dancer_app() core: [DemoApp::DebugRoutes] -&gt; status(404) core: [DemoApp::DebugRoutes] -&gt; halt() HTTP/1.0 404 Not Found Server: Perl Dancer Content-Length: 0 Content-Type: text/html</pre> <p>And the main route still works (meaning the filter doesn't get into our way elsewhere):</p> <pre class="prettyprint">$ curl http://0:3000/ -I core: [DemoApp] -&gt; template(index) HTTP/1.0 200 OK</pre> <p>Yup. It works. But <i>it's the same with Dancer&nbsp;1</i> you say? Well let's see. I'm going to restart the app with Dancer&nbsp;1 and do the same tests.</p> <p>Any clue what will mess up?</p> <p>Well, no need to go very far, the <code>/</code> route has disappeared because the <code>prefix</code> statement is global (as everything you do in Dancer&nbsp;1). So if you load <code>DemoApp::DebugRoutes</code> <i>before</i> defining the <code>/</code> route, the <code>prefix</code> will propagate to all the remaining route to define...</p> <p>So with Dancer&nbsp;1, the <code>/</code> route became... <code>/debug</code> (a prefix <code>/debug</code> combined with a <code>/</code> route):</p> <pre class="prettyprint">$ curl -i http://0:3000/debug [...] X-Powered-By: Perl Dancer 1.311 Hello Dancer 2</pre> <p>And of course the layout has disappeared as well, as it's disabled in the <code>DebugRoutes</code> package.</p> <p>As you can expect, the <code>/</code> is now a <code>404</code>:</p> <pre class="prettyprint">$ curl -I http://0:3000/ [...] HTTP/1.0 404 Not Found</pre> <p>Broken!</p> <p>This kind of loading time sequencing is crucial in a Dancer&nbsp;1 application. In Dancer&nbsp;2, thanks to the proper scoping of the core, it's not a problem anymore.</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by <a href="http://twitter.com/sukria">Alexis Sukrieh</a> for the Perl Dancer Advent Calendar 2012.</p> </div> The Errors of Our Way http://advent.perldancer.org/2012/6 perl http://advent.perldancer.org/2012/6 Thu, 6 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="the_errors_of_our_way"></a>The Errors of Our Way</h1> <p>This is a preview of the error throwing mechanism that is coming with <i>Dancer 2</i>. Things have been tidied a little bit, objectified in a classy manner (or classified in an objectionable manner? Anyway, turned into nice little <a href="https://metacpan.org/module/Moo">Moo</a> classes), and made (or so we hope) both easy to use and pleasantly versatile.</p> <h2><a name="at_the_core"></a>At the Core</h2> <p>In <i>Dancer 2</i>, <code>Dancer::Core::Error</code> is the ringleader as far as error handling is concerned. Its mission is fairly simple: take in booboos, and report on them in the most sensible way possible.</p> <p>Within Dancer's code, creating a new error is done by, well, creating a new <code>Dancer::Core::Error</code>:</p> <pre class="prettyprint">my $oopsie = Dancer::Core::Error-&gt;new( status =&gt; 418, message =&gt; "This is the Holidays. Tea not acceptable. We want eggnog.", context =&gt; $context, );</pre> <p>If that was not simple enough, all those attributes are actually optional. If not given, the status code defaults to a good old <i>500</i>, there is no need for a message if we feel taciturn, and while the <i>$context</i> (which is a <code>Dancer::Core::Context</code> object holding all the pieces of information related to the current request) is needed if we want to take advantage of the templates, we can also do without.</p> <p>The important thing to know, however, is that creating an error doesn't mean that it's automatically seen by the end-user. For that to happen, you have to...</p> <h2><a name="throw_it_around"></a>Throw It Around</h2> <p>Or, put another way, we have to populate the <code>Dancer::Core::Response</code> object with the error's data. That is done via</p> <pre class="prettyprint">$oopsie-&gt;throw($response);</pre> <p>Or, if we want to use the response object already present in the <i>$context</i> (which is usually the case):</p> <pre class="prettyprint">$oopsie-&gt;throw;</pre> <p>This populates the status code of the response, sets its content, and throws a <code>halt()</code> in the dispatch process.</p> <h2><a name="what_the_error_going_to_look_like"></a>What The Error Going To Look Like?</h2> <p>Quick answer: whatever you want it to. The error object has quite a few ways to generate its content.</p> <p>First, it can be explicitly given</p> <pre class="prettyprint">my $oopsie = Dancer::Core::Error-&gt;new( content =&gt; '&lt;html&gt;&lt;body&gt;&lt;h1&gt;OMG&lt;/h1&gt;&lt;/body&gt;&lt;/html&gt;', );</pre> <p>but that's boring.</p> <p>If the <i>$context</i> was given, the error will check if there's a template by the name of the status code (so, say you're using Template Toolkit, <i>418.tt</i>) and will use it to generate the content, passing it the error's <i>$message</i>, <i>$status</i> code and <i>$title</i> (which, if not specified, will be the standard http error definition for the status code).</p> <p>If there is no template, the error will then look for a static page (to continue with our example, <i>418.html</i>) in the <code>public/</code> directory.</p> <p>And finally, if all of that failed, the error object will fall back on an internal template.</p> <h2><a name="throwing_errors_in_routes"></a>Throwing Errors In Routes</h2> <p>Now, how do we use all that stuff in our routes? In the simpliest way:</p> <pre class="prettyprint">get '/xmas/gift/:gift' =&gt; sub { die "sorry, we're all out of ponies\n" if param('gift') eq 'pony'; };</pre> <p>The <code>die</code> will be intercepted by Dancer's inner magic, converted into an error (status code <i>500</i>, message set to the dying words) and shoved into the response.</p> <p>In the cases where more control is required, <code>send_error()</code> is the way to go:</p> <pre class="prettyprint">get '/glass/eggnog' =&gt; sub { send_error "Sorry, no eggnog here", 418; };</pre> <p>And if <i>total</i> control is needed:</p> <pre class="prettyprint">get '/xmas/wishlist' =&gt; sub { Dancer::Core::Error-&gt;new( response =&gt; response(), status =&gt; 406, message =&gt; "nothing but coal for you, I'm afraid", template =&gt; 'naughty/index', )-&gt;throw unless user_was_nice(); ...; };</pre> <h2><a name="in_conclusion"></a>In Conclusion</h2> <p>At last news, <code>Dancer::Core::Error</code> is in a working state. Some of its details are likely to change a little bit, but it's already nicely settling into a pattern that should make everyone happy. Well, as happy as anybody can be while errors are being thrown, anyway.</p> <h2><a name="author"></a>AUTHOR</h2> <p>Yanick Champoux (YANICK)</p> </div> Dancer::Session::Redis - Writing a session engine for Dancer 2 http://advent.perldancer.org/2012/5 perl http://advent.perldancer.org/2012/5 Wed, 5 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer__session__redis___writing_a_session_engine_for_dancer_2"></a>Dancer::Session::Redis - Writing a session engine for Dancer 2</h1> <p>In order to continue presenting all the good things about Dancer 2, I wanted a good reason to show how the session handling is done. On the other hand, I didn't want to explain how the code is designed without something concrete to explain. So I came with the idea of writing a new session engine for Dancer 2, explaining all the steps here.</p> <p>Moreover, after having searched a bit, it appears that there is no Redis backend for storing sessions so that's a perfect opportunity to write it! Let's go!</p> <h2><a name="a_word_about_redis"></a>A word about Redis</h2> <p>I suppose you already know Redis, if you don't, well, you should! It's a blazing fast NoSQL key/value store. It can reach amazing performances like a rate of 100,000 requests per second. Using it as a session storage to make sure multiple interface servers can share the same information is very common.</p> <p>Anything stored in Redis is serialized as a JSON string.</p> <h2><a name="session_engines_in_dancer_2"></a>Session engines in Dancer 2</h2> <p>We've already seen that Dancer 2 is entirely written with <a href="https://metacpan.org/module/Moo">Moo</a>. So you won't be surprised if I tell you that in Dancer 2, the engines are actually roles. So if we want to write a session backend for Dancer 2, we need to consume the <code>Dancer::Core::Role::SessionFactory</code> role. Why not <code>Session</code> instead of <code>SessionFactory</code> you ask? Well, it's the major design change (which I'm very happy with) if you compare to how things are done in Dancer 1.</p> <p>In Dancer 1, we have one object, a Dancer::Session::Something. That object will contain the session itself (expiration time, ID, content) and the session storage itself (for instance for YAML, you'll find here a <code>session_dir</code> attribute).</p> <p>It's probably not shocking at first sight, I presume because Dancer 1 still works like that and I never heard someone complaning about it, after almost 3 years.</p> <p>But recently, I was facing an issue with sessions in Dancer 2 and I started thinking about how things are done, and I realized the design could be better by far. Indeed, it came to my mind that we have actually two very different things here: the session itself, and its storage backend.</p> <p>There are two concepts: a <code>Session</code> class, that contains generic information like an expiration time, a content, and ID and that is agnostic of how it is stored; and a SessionFactory class, which describes how to create, update and delete a <code>Session</code> object.</p> <p>So in Dancer 2, a session backend consumes the role <code>Dancer::Core::Role::SessionFactory</code>.</p> <p>Let's do that!</p> <h2><a name="dancer__session__redis"></a>Dancer::Session::Redis</h2> <p>First thing to do is to create the class for our session engine, we'll name it, abviously <code>Dancer::Session::Redis</code>:</p> <pre class="prettyprint">package Dancer::Session::Redis; use Moo; with 'Dancer::Core::Role::SessionFactory';</pre> <p>First, we want our backend to have a handle over a Redis connection. To do that, we'll create an attribute <code>redis</code> that will do all the boring bits for us, lazily.</p> <pre class="prettyprint">use JSON; use Redis; use Dancer::Core::Types; # brings helper for types has redis =&gt; ( is =&gt; 'rw', isa =&gt; InstanceOf['Redis'], lazy =&gt; 1, builder =&gt; '_build_redis', );</pre> <p>The lazy attribute is very interesting, it says to Moo that this attribute will be built (initialized) only when called the first time. It means that the connection to Redis won't be opened until necessary.</p> <pre class="prettyprint">sub _build_redis { my ($self) = @_; Redis-&gt;new( server =&gt; $self-&gt;server, password =&gt; $self-&gt;password, encoding =&gt; undef, ); }</pre> <p>As you can see, we want to create two more attributes: <code>server</code> and <code>password</code>. Dancer 2 will pass anything defined in the config to the engine creation. So for instance, if we have:</p> <pre class="prettyprint"># config.yml ... engines: session: Redis: server: foo.mydomain.com password: S3Cr3t</pre> <p>The server and password entries will be passed to the constructor of the Redis session engine, as expected. That's another sign of the proper design of Dancer 2, no more settings shared among all the packages, the Redis engine only knows about itself, it has absolutely no idea of what a Dancer config is.</p> <pre class="prettyprint">has server =&gt; (is =&gt; 'ro', required =&gt; 1); has password =&gt; (is =&gt; 'ro');</pre> <p>The role requires that we implement <code>_retrieve</code>, <code>_flush</code>, <code>_destroy</code> and <code>_sessions</code>.</p> <p>The first one, <code>_retrieve</code> is supposed to return a session object for a session ID it's passed. In our case, it's pretty easy, our sessions are going to be stored in Redis, the session ID will be the key, the session the value. So retrieving is as easy as doing a get and decoding the JSON string returned:</p> <pre class="prettyprint">sub _retrieve { my ($self, $session_id) = @_; my $json = $self-&gt;redis-&gt;get($session_id); my $hash = from_json( $json ); return bless $hash, 'Dancer::Core::Session'; }</pre> <p>Now we should implement the <code>_flush</code> method, which is called by Dancer when the session needs to be stored in the backend. That's actually a write to Redis. The method receives a <code>Dancer::Core::Session</code> object and is supposed to store it.</p> <pre class="prettyprint">sub _flush { my ($self, $session) = @_; my $json = to_json( { %{ $session } } ); $self-&gt;redis-&gt;set($session-&gt;id, $json); }</pre> <p>Now, the <code>_destroy</code> method that is supposed to remove a session from the backend, the session ID is passed.</p> <p>Easy one, we should delete the key from Redis.</p> <pre class="prettyprint">sub _destroy { my ($self, $session_id) = @_; $self-&gt;redis-&gt;del($session_id); }</pre> <p>And finally, the last one, the <code>_sessions</code> method which is supposed to list all the session IDs currently stored in the backend. It's in our case as stupid as listing all the keys that Redis has.</p> <pre class="prettyprint">sub _sessions { my ($self) = @_; my @keys = $self-&gt;redis-&gt;keys('*'); return \@keys; }</pre> <p>Voil&#xc3;&#xa0;. The session engine is ready. Let's play with it now.</p> <h2><a name="behind_the__code_session__code__keyword"></a>Behind the <code>session</code> keyword</h2> <p>It should be clear now that a session storage system is not the same thing as a session itself. Let's look at how things work when you use the <code>session</code> keyword in Dancer.</p> <p>First of all, keep in mind that when Dancer 2 executes a route handler to process a request, it creates a <code>Dancer::Core::Context</code> object. That object is designed to handle all the volatile information of the current request: the environment, the request object, the newborn response object, the cookies...</p> <p>This context is passed to all the components of Dancer that can play with it, to build the response. For instance, a before filter will receive that context object.</p> <p>That where we'll find the session handle for the current client, in the context. The beauty of it is that it's actually a lazy attribute, and its builder has one thing to do: look if the client has a <code>dancer.session</code> cookie, and if so, try to <code>retrieve</code> the session from the storage engine, with the value of the cookie (the session ID).</p> <pre class="prettyprint">has session =&gt; ( is =&gt; 'rw', isa =&gt; Session, lazy =&gt; 1, builder =&gt; '_build_session', ); sub _build_session { my ($self) = @_; my $session; # Find the session engine my $engine = $self-&gt;app-&gt;setting('session'); croak "No session engine defined, cannot use session." if ! defined $engine; # find the session cookie if any my $session_id; my $session_cookie = $self-&gt;cookie('dancer.session'); if (defined $session_cookie) { $session_id = $session_cookie-&gt;value; } # if we have a session cookie, try to retrieve the session if (defined $session_id) { eval { $session = $engine-&gt;retrieve(id =&gt; $session_id) }; croak "Fail to retreive session: $@" if $@ &amp;&amp; $@ !~ /Unable to retrieve session/; } # create the session if none retrieved return $session ||= $engine-&gt;create(); }</pre> <p>So the very first time <code>session</code> is called, the object is either retrieved from the backend, or a new <code>Dancer::Core::Session</code> is created, and stored in the context.</p> <p>Then, a <code>before</code> filter makes sure a cookie <code>dancer.session</code> is added to the headers.</p> <pre class="prettyprint"># Hook to add the session cookie in the headers, if a session is defined $self-&gt;add_hook(Dancer::Core::Hook-&gt;new( name =&gt; 'core.app.before_request', code =&gt; sub { my $context = shift; # make sure an engine is defined, if not, nothing to do my $engine = $self-&gt;setting('session'); return if ! defined $engine; # push the session in the headers $context-&gt;response-&gt;push_header('Set-Cookie', $context-&gt;session-&gt;cookie-&gt;to_header); } ));</pre> <p>At this time, the user's code comes into play, using the <code>session</code> keyword, which is actually that simple:</p> <pre class="prettyprint">sub session { my ($self, $key, $value) = @_; my $session = $self-&gt;context-&gt;session; croak "No session available, a session engine needs to be set" if ! defined $session; # return the session object if no key return $session if @_ == 1; # read if a key is provided return $session-&gt;read($key) if @_ == 2; # write to the session $session-&gt;write($key =&gt; $value); }</pre> <p>And to conclude, an <code>after</code> filter is set to call the <code>flush</code> method of the storage backend. Indeed, the <code>write</code> calls you see above do not change the sesssion in the storage system, they only alter the object itself. That's a major improvement if you compare to Dancer 1, because as everything is mixed and less well decoupled, in Dancer 1, a write to the session is also a flush to the backend system.</p> <p>Here, we know that we flush exactly once per request, when the response is ready.</p> <pre class="prettyprint"># Hook to flush the session at the end of the request, this way, we're sure we # flush only once per request $self-&gt;add_hook( Dancer::Core::Hook-&gt;new( name =&gt; 'core.app.after_request', code =&gt; sub { # make sure an engine is defined, if not, nothing to do my $engine = $self-&gt;setting('session'); return if ! defined $engine; return if ! defined $self-&gt;context; $engine-&gt;flush(session =&gt; $self-&gt;context-&gt;session); }, ) );</pre> <h2><a name="code_for_this_article"></a>Code for this article</h2> <p>The code of the session backend described here has been released on <a href="https://github.com/sukria/Dancer-Session-Redis/blob/master/lib/Dancer/Session/Redis.pm">GitHub</a>.</p> <p>It's not released on CPAN yet because we'll wait for Dancer 2 to be there first, for obvious reasons.</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by <a href="http://twitter.com/sukria">Alexis Sukrieh</a> for the Perl Dancer Advent Calendar 2012.</p> </div> Dancer 2, what about using it? Now! http://advent.perldancer.org/2012/4 perl http://advent.perldancer.org/2012/4 Tue, 4 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer_2__what_about_using_it_now_"></a>Dancer 2, what about using it? Now!</h1> <h2><a name="a_look_back"></a>A look back</h2> <p>It's been one year and four months since I did my first commit on Dancer 2. The first commits, besides setting up a dist.ini file are about the <code>Dancer::Core::Route</code> class.</p> <p>If you want to refresh your memories about why I decided to embark upon a rewrite, you can jump to <a href="http://advent.perldancer.org/2011/8">this article</a> of the previous advent calendar.</p> <p>Funny to see that when starting this rewrite marathon, I chose to begin with the more essential part of the core: the route handler class. It's certainly not a random choice, but I'm pretty sure it was not really made on purpose.</p> <p>Anyway, almost a year and a half has passed and it's now time to shed some light on Dancer 2. I remember Franck Cuny telling me in November 2010 - when we were starting the first advent calendar iteration - "<i>You'll see, 2011 will be the Dancer year</i>". He couldn't have been more right. Many great things happened in 2011 for Dancer. So I was asking myself: what about 2012 and the upcoming year, 2013?</p> <p>If I should name it, I'd say that 2012 was "<i>the Dancer 2 development year</i>" because most of the brainpower was used to grow Dancer 2, to mature it and to make it real. Of course Dancer 1 continued to evolve a bit, but at some point we froze it, to make sure we'll release 2.</p> <p>So what will 2013 be? You already know it right? My bet goes on the "Dancer 2 year". Everything is ready, Dancer 2 is beautifully designed, efficient and extensible yet very compatible with the Dancer 1 ecosystem. You'll love it, and you should use it. Now. Here is why, and how.</p> <h2><a name="what_is_dancer_2_at_the_time_of_this_writing"></a>What is Dancer 2 at the time of this writing?</h2> <p>Before we start, let's clarify where we are today, about Dancer 2. Don't look for it on CPAN, it's not there quite yet - when we've finished polishing it up enough we'll certainly roll a DEVELOPER release but we're not quite yet ready (although we are very close).</p> <p>Dancer 2 is a GitHub project, you can browse the code on its page: <a href="https://github.com/PerlDancer/Dancer2/">https://github.com/PerlDancer/Dancer2/</a> and you can grab the source like so:</p> <pre class="prettyprint">git clone https://github.com/PerlDancer/Dancer2.git</pre> <p>Although it's not sitting on CPAN, it's a stable source tree, we have more than 500 unit tests already and I'm able to say that Dancer 2 is also able to run on production because, well, that's what I do at work.</p> <p>The only area where you should be cautious is with plugins or engines, everything is in place to port them, but most of them aren't yet. So it's likely that one extension of your app will need to be ported a bit to work with Dancer 2. It's an easy job to do though, thanks to all the work that has been done on Dancer::Plugin to assure inter-operability. Some of the most commonly-used plugins have already been ported, and we are in the process of reaching out to plugin authors to help them tweak their plugin to support both Dancer versions easily.</p> <h2><a name="design_decisions_for_d2"></a>Design decisions for D2</h2> <h3><a name="pure_oo_code__based_on_moo"></a>Pure OO code, based on Moo</h3> <p>Before listing all the benefits of using Dancer 2, let's take a look at its core, seeing what makes it so different from Dancer 1.</p> <p>First, Dancer 2 is written in pure object-oriented code, with <a href="https://metacpan.org/module/Moo">Moo</a> as its object system.</p> <p>Why Moo? Well, it's Moose philosophy with efficiency in mind, written in pure Perl5. It's fast and right to the point. How could another meta-class layer fit Dancer's philosophy better?</p> <p>Thanks to Moo, and more generally to the Moose approach, Dancer 2's core gets very powerful tools in its backbone like laziness construction, role composition and method modifiers. That helped a lot to enhance the way things are implemented.</p> <p>I could almost say that Dancer 1 is written in Perl5 and Dancer 2 is in Moo.</p> <h3><a name="no_more_shared_singletons"></a>No more shared singletons</h3> <p>The most annoying design choice I wanted to get rid of, when starting the Dancer rewrite, was the singletons usage. You know the drill, globals are evil. That was a mistake I made in the early stage of development of Dancer 1, we lived with it, I think pretty well, but Dancer 2 needed to go a step further. Without singletons.</p> <p>Without globals, the code is properly decoupled, things know only about themselves and their direct neighbours, in fact, the code base now respects truthfully the <a href="http://en.wikipedia.org/wiki/Law_of_Demeter">Law of Demeter</a>.</p> <p>This will for sure be a lot better for future evolution and code maintenance.</p> <h3><a name="strict_app_scoping"></a>Strict app scoping</h3> <p>Another major change I wanted with Dancer 2, which I couldn't do in 1 because of the globals limitation mentioned above, was the per-app encapsulation.</p> <p>I wanted that anything I did in a package was scoped there, for instance being able to set a serializer in <code>MyApp::API</code> without setting one <code>MyApp::Blog</code>. In Dancer 1 that's impossible, because the settings registry is global to the process (remember? <i>Globals are evil</i>).</p> <p>With Dancer 2, any package that <code>use</code>s Dancer will be scoped into its own Dancer::Core::App instance, where the settings registry will live, and the route handlers, and anything you could imagine to do within a Dancer package.</p> <p>In Dancer 2, you can consider each of your packages in your application as a jail where everything is nicely isolated from the outside. No more apps collision, no more settings leaks from a part of the application to the other.</p> <h2><a name="where_are_we_with_dancer_2"></a>Where are we with Dancer 2?</h2> <p>First of all, let's make it clear: the whole DSL is supported. It means that whatever you do when you use Dancer, you can do it with Dancer 2. It's the same syntax, you won't even notice it.</p> <p>Because of the major design changes explained above, plugins cannot work magically with Dancer 2. The change to apply are minimal for most plugins, but still, a plugin needs to be adapted slightly. Dancer::Plugin provides everything to make sure a plugin can run smoothly with Dancer 1 or 2, transparently.</p> <p>At the time of this writing, most of the plugins in the ecosystem should be easy to port, all we need is volunteers to help us test them, port the code and submit pull requests to plugin authors.</p> <p>On the core side, we have one thing to do to make the whole ecosystem ready for Dancer 2: allowing the same kind of transition for engines (template, session, etc). In Dancer 1, an engine just needs to "extends" a base "abstract" class. In Dancer 2, as we're running with Moo, this has been changed to roles composition.</p> <p>Finally, the documentation needs a lot of work to get it up to the expected standard.</p> <h2><a name="should_you_switch_from_d1_to_d2__and_how"></a>Should you switch from D1 to D2, and how?</h2> <p>It depends on what you want to do. If your application can be written with a pure Dancer distribution, without plugins or engines, then yes, you definitely can switch today.</p> <p>If you're using plugins in your app, your help is welcome: test your application, report any plugin you're using that doesn't work and help us port them.</p> <p>To power your app with Dancer 2, as it's not released yet, here is what I suggest:</p> <p>In your application, create a git submodule in vendor/Dancer 2:</p> <pre class="prettyprint">$ cd MyProject $ git submodule add https://github.com/PerlDancer/Dancer2.git vendor/Dancer2</pre> <p>Then add this little snippet in your bin/app.pl to make sure you load any lib from your vendor directory:</p> <pre class="prettyprint">BEGIN { use FindBin; while ( my $libdir = glob("${FindBin::Bin}/../vendor/*/lib") ) { unshift @INC, $libdir; } } use Dancer 2.0;</pre> <p>This way, when the app worker will start, it will push all <code>lib</code> directory it an find in <code>./vendor/*</code> as possible location for modules. Hence Dancer 2 will be found there.</p> <p>Works like a charm, and can be used as well for plugins you want to patch for Dancer 2.</p> <p>Also, if you want to make sure your Dancer 2 copy in vendor is up to date:</p> <pre class="prettyprint">$ git submodule update</pre> <p>I'm using this technique at work for a Dancer 2 app we've deployed to production, it's working great.</p> <p>Oh, and the page you're reading now has been served by Dancer 2 as well!</p> <p>And while we're at it, see <a href="https://github.com/PerlDancer/Dancer-Plugin-Feed/commit/1399073ebc192c02c8eddfc83ebf6fc07675191b">this commit</a> to see how simple it was to port Dancer::Plugin::Feed (which is used by the Advent Calendar app) to support Dancer 2.</p> <p>If the plugin you're using is just playing with the DSL, porting it to Dancer 2 is as easy as using <code>plugin_args</code> in registered subs to unroll <code>@_</code> and as declaring the supported versions of Dancer when calling <code>plugin_register</code>.</p> <h2><a name="will_you_dance_a_second_time"></a>Will you dance a second time?</h2> <p>So, will you give it a try? I'm sure you have that little app somewhere that would love to make the jump!</p> <p>And if you happen to use a plugin which complains about not being compatible with Dancer 2, fork it, vendorize it and patch it. And then, submit a pull request to the author.</p> <p>That's the way you can help!</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by <a href="http://twitter.com/sukria">Alexis Sukrieh</a> for the Perl Dancer Advent Calendar 2012.</p> </div> Dancer deployment: Apache's mod_proxy and Nginx http://advent.perldancer.org/2012/3 perl http://advent.perldancer.org/2012/3 Mon, 3 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer_deployment__apache_s_mod_proxy_and_nginx"></a>Dancer deployment: Apache's mod_proxy and Nginx</h1> <h2><a name="notice"></a>Notice</h2> <p>I'm not a networking guru. What follows is what I think I've learn asking in <code>#plack</code> and <code>#dancer</code> (<a href="irc://irc.perl.org">irc://irc.perl.org</a>, thanks people, you really rock) and reading the documentation on the net. I apologize in advance if something is not 100% correct.</p> <h2><a name="scenario"></a>Scenario</h2> <p>So, you used the <a href="https://metacpan.org/module/Dancer">Dancer</a> framework to build your new shiny web application. Now it's time to deploy it. You have a server with a very common setup: Apache2 with mod_php, which serves other sites. You read <a href="https://metacpan.org/module/Dancer::Deployment">Dancer::Deployment</a> and think: OK, no problem, just use Apache's mod_proxy and <a href="https://metacpan.org/module/Starman">Starman</a> and you will be fine.</p> <p>Wrong! You're not fine. You're indeed in big troubles.</p> <h2><a name="the_problems__first_part_"></a>The problems (first part)</h2> <p>First, mod_proxy and Starman seem not to play well together: from the <a href="https://metacpan.org/module/starman">starman</a>'s documentation:</p> <blockquote> <p>--disable-keepalive</p> <p>Disable Keep-alive persistent connections. It is an useful workaround if you run Starman behind a broken frontend proxy that tries to pool connections more than a number of backend workers (i.e. Apache mpm_prefork + mod_proxy).</p> </blockquote> <p>From Apache's <a href="http://httpd.apache.org/docs/2.2/mod/mod_proxy.html#envsettings">documentation</a>:</p> <blockquote> <p>For circumstances where mod_proxy is sending requests to an origin server that doesn't properly implement keepalives or HTTP/1.1, there are two environment variables that can force the request to use HTTP/1.0 with no keepalive.</p> </blockquote> <p>Ok, no problem, there are the workarounds, but it doesn't look like a promising setup.</p> <h2><a name="the_problems__second_part_"></a>The problems (second part)</h2> <p>The prefork servers (like Starman and Apache mpm-prefork) work in a straightforward way: they accept a connection, serve the file or the output, when the transmission is over they pick up another one. To avoid the creation of bottlenecks they spawn more processes.</p> <p>As you may see, there is a big problem. If you have pages or files which are long, say 500kb, or a few Mb, which is not really uncommon, and it's <i>very</i> common in my case, if the clients have slow connections, the processes will not pick up another connection until the page is served. The <a href="http://lwn.net/Articles/338407/">slowloris</a>) issue is known (well, you'll know it when you encounter the problem) and there are this kind of malicious attacks. There are some workarounds (again), but you're forgetting something: the 56k people. Maybe they are not so common, but why they should block your server? Are you going to ban them? No way!</p> <p>But, you think, you put a proxy in front of Starman exactly for this reason: to avoid binding Starman for too long. It's the whole point of the proxy: the backend works fast, transmits the data, and next! It's a proxy's matter to work with the crowd out there.</p> <p>There are 2, big problems with mod_proxy + mpm-prefork:</p> <ul> <li><a name="item_It_s_a__i_prefork__i_"></a><b>It's a <i>prefork</i></b> <p>So if the connections are slow, the bottleneck is there. You can create a Denial of Service with a one-liner from the shell over a 56k. You could increase the number of workers, but it's always a finite number.</p> </li> <li><a name="item_Its__i_buffering__i__does_not_work_as_expected"></a><b>Its <i>buffering</i> does not work as expected</b> <p>It doesn't buffer <i>at all</i> and Starman's workers (which are always less than the Apache's workers because they are more memory-hungry) are tied until the request is served. This defeats the whole point of having a proxy.</p> </li> </ul> <p>The workarounds just scale the problem: increase the workers until the machine have enough memory for them. Believe me, it's easy to eat all the memory with a couple of applications, down this road.</p> <p>Or, let Starman to serve only little dynamic pages, let Apache do the rest (if possible) and hope for the best. The problem is still there, anyway.</p> <p>To test this problem is trivial:</p> <pre class="prettyprint">for i in `seq 0 100` ; do wget -q --limit-rate 1 -O /dev/null \ http://target.tld &amp; 2&gt; /dev/null ; done</pre> <p>Open a browser, and I bet you can't open the page.</p> <p>This is a malicious attack, but the same problem happens with the 56k users or requests for big files. It's all working as expected, but the prefork problem and the lack (or just ridiculously low level) of buffering are just blocking the web-server.</p> <p>You think: yes, OK, nothing new, my php applications already have this problem, and I can survive. Wrong! Because you usually have a Starman server for each application, and a machine without huge memory have limited resources. So, count, how many children Starman can spawn? They are usually less then the Apache's workers. When Apache is proxying, it binds them until the request is served.</p> <p>Well, I hope you get the point. You can continue with workarounds or do the right thing: drop Apache as proxy, and use a real one: Nginx</p> <h2><a name="nginx_as_proxy"></a>Nginx as proxy</h2> <p>Why Nginx?</p> <p>Because Nginix doesn't suffer the slowloris problem <b>and</b> does a proper buffering, so the 2 problems are gone now. Plack and Nginx play very well together, and it's the commonly recommended setup.</p> <p>The setup is explained in detail in <a href="https://metacpan.org/module/Dancer::Deployment">Dancer::Deployment</a>, so I won't repeat it here.</p> <h2><a name="legacy_php_sites"></a>Legacy php sites</h2> <p>But now you have to manage the old php sites, which can't run on nginx. There are various setups, but the <b>quickest</b> is something like this:</p> <ul> <li> <p>Make Apache to listen to 127.0.0.1:8080</p> </li> <li> <p>For each server add this Nginx configuration:</p> <pre class="prettyprint">server { listen 80; server_name your.hostname.tld root /your/root/ location / { proxy_set_header Host $http_host; proxy_set_header X-Forwarded-Host $http_host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://127.0.0.1:8080; } }</pre> <p>Which basically pass <i>everything</i> to Apache. You can optimize it by letting nginx to serve the static files, like the images, of course. Read the doc, if you're interested. I found this interesting: <a href="http://kbeezie.com/view/apache-with-nginx/">http://kbeezie.com/view/apache-with-nginx/</a></p> </li> </ul> <h2><a name="author"></a>Author</h2> <p>This article has been written by Marco Pessotto (a.k.a. melmothX) in 2012 as a personal note and reviewed for the Perl Dancer Advent Calendar 2012. The original is located at <a href="http://laltromondo.dynalias.net/~iki/informatica/soft/dancer_and_apache/">LAltroMondo Wiki</a></p> <p>No copyright, public domain.</p> </div> New user authentication and authorisation framework http://advent.perldancer.org/2012/2 perl http://advent.perldancer.org/2012/2 Sun, 2 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="new_user_authentication_and_authorisation_framework"></a>New user authentication and authorisation framework</h1> <p><a href="https://metacpan.org/module/Dancer::Plugin::Auth::Extensible">Dancer::Plugin::Auth::Extensible</a> was released recently, to offer a simple to use yet flexible framework for user authentication and role-based authorisation for Dancer-powered webapps.</p> <p><a href="https://metacpan.org/module/Dancer::Plugin::Auth::RBAC">Dancer::Plugin::Auth::RBAC</a> was already out there and available, but I never really got on with it, felt it was a little cumbersome to use, and needed a fair amount of work to bring it up to scratch; so, I decided instead to start a new project with the aims of making user authentication (and role-based authorisation, where needed) simple.</p> <p>So, what makes <a href="https://metacpan.org/module/Dancer::Plugin::Auth::Extensible">Dancer::Plugin::Auth::Extensible</a> different?</p> <h2><a name="it_s_simple"></a>It's simple</h2> <p>Denoting routes for which the user must be logged in is easy - subroutine attributes are used to indicate that the user must be logged in, or must have a particular role:</p> <pre class="prettyprint">get '/secret' =&gt; sub :RequireLogin { return "User must be logged in to see this."; }; post '/user' =&gt; sub :RequireRole(Admin) { # User must be logged in and have the 'Admin' role in order to hit this # route };</pre> <p>Simple, eh? The auth framework will deal with ensuring the user is logged in (and has the appropriate role, if you're using roles and the route requires a specific role), and if not, will present them a login page (the framework even provides a basic login page so you can get up and running quickly, but you can of course override that).</p> <p>For the most simple cases, you might not need roles, simply the requirement that a user be logged in - that's fine, you can disable roles entirely.</p> <h2><a name="flexible_authentication_sources"></a>Flexible authentication sources</h2> <p>The auth framework plugin ships with a few "authentication provider" classes to authenticate against different sources:</p> <ul> <li><a name="item_Account_details_in_the_app_s_config"></a><b>Account details in the app's config</b> <p>The <code>Config</code> auth provider reads usernames, passwords (preferably crypted) and optionally lists of roles from the app's config. This is a simple and easy way to get started.</p> </li> <li><a name="item_Account_details_in_a_database_via__a_href__https___metacpan_org_module_Dancer__Plugin__Database__Dancer__Plugin__Database__a_"></a><b>Account details in a database via <a href="https://metacpan.org/module/Dancer::Plugin::Database">Dancer::Plugin::Database</a></b> <p>The <code>Database</code> provider reads usernames, passwords, optionally roles and other details from a database, using <a href="https://metacpan.org/module/Dancer::Plugin::Database">Dancer::Plugin::Database</a>.</p> </li> <li><a name="item_Unix_system_accounts"></a><b>Unix system accounts</b> <p>The <code>Unix</code> provider authenticates against Linux/Unix system user accounts; user groups are used for roles, so you can e.g. require a user be in the <code>wheel</code> group, or whatever other user groups you have defined.</p> </li> <li><a name="item_Accounts_in_a_database__via_DBIC"></a><b>Accounts in a database, via DBIC</b> <p>An auth provider to authenticate against a database using DBIC via <a href="https://metacpan.org/module/Dancer::Plugin::DBIC">Dancer::Plugin::DBIC</a> is coming soon.</p> </li> </ul> <p>If the authentication source you wish to use isn't already supported, it's pretty trivial to write a new authentication provider; they are simply subclasses of <a href="https://metacpan.org/module/Dancer::Plugin::Auth::Extensible::Provider::Base">Dancer::Plugin::Auth::Extensible::Provider::Base</a> which implement the required methods.</p> <p>Further providers are planned for e.g. <code>LDAP</code>, <code>IMAP</code>, <code>ActiveDirectory</code> etc.</p> <h2><a name="it_s_flexible"></a>It's flexible</h2> <p>Multiple authentication realms are supported, so you can configure the framework to, for example, first check a database using the <code>Database</code> provider, then check against an LDAP server (when that provider is available). You can use the same provider in multiple realm configurations, too - for instance, if you needed to check against multiple databases.</p> <h2><a name="secure_password_handling_is_easy"></a>Secure password handling is easy</h2> <p><a href="https://metacpan.org/module/Crypt::SaltedHash">Crypt::SaltedHash</a> is used to handle crypted passwords for you, so it's easy to handle passwords securely (you wouldn't want to be storing passwords in plain text, would you? (I sincerely hope not.))</p> <h2><a name="convenient_keywords_are_provided"></a>Convenient keywords are provided</h2> <p>Convenient keywords are provided:</p> <ul> <li><a name="item__code_logged_in_user__code_"></a><b><code>logged_in_user</code></b> <p>If the user is logged in, returns a hashref of details about the currently logged-in user. What you get back will depend on the provider in use, but is flexible - for instance, if you're using the <code>Database</code> provider, all columns of the user table will be returned, so if you e.g. have columns containing the user's name and email address, you'll find them here.</p> </li> <li><a name="item__code_user_has_role__code_"></a><b><code>user_has_role</code></b> <p>Allows you to manually check if the logged-in-user has a role you're interested in - for example:</p> <pre class="prettyprint">if (user_has_role('BeerDrinker')) { pour_beer(); }</pre> </li> <li><a name="item__code_user_roles__code_"></a><b><code>user_roles</code></b> <p>Returns a list of the currently logged-in user's roles.</p> </li> <li><a name="item__code_authenticate_user__code_"></a><b><code>authenticate_user</code></b> <p>Usually you'll want to let the built-in login handling code deal with authenticating users, but in case you need to do it yourself, this keyword accepts a username and password, and checks whether they are valid.</p> </li> </ul> <h2><a name="give_it_a_try_"></a>Give it a try!</h2> <p>The framework is still new, but is in a state where it's ready to use. If it is of interest to you, have a play with it, and do feel free to submit issues or pull requests on GitHub if you find any issues or would like to suggest new features:</p> <p><a href="https://github.com/bigpresh/Dancer-Plugin-Auth-Extensible">https://github.com/bigpresh/Dancer-Plugin-Auth-Extensible</a></p> <h2><a name="author"></a>AUTHOR</h2> <p>David Precious (BIGPRESH)</p> </div> Another year of Dancing... http://advent.perldancer.org/2012/1 perl http://advent.perldancer.org/2012/1 Sat, 1 Dec 2012 00:00:00 -0000 <div class="pod-document"><h1><a name="another_year_of_dancing___"></a>Another year of Dancing...</h1> <p>Welcome to the Dancer advent calendar, for the third year running! Previous year's calendars have been well received, let's hope this one can live up to them.</p> <h2><a name="more_and_more_dancers___"></a>More and more dancers...</h2> <p>More and more people are using Dancer to power their sites - recent additions to the <a href="http://dancer.pm/dancefloor">dancefloor</a> include <a href="http://www.crowdtilt.com/">CrowdTilt</a>, <a href="http://www.daystack.com/">Daystack</a>, <a href="http://elibraryusa.state.gov/">eLibraryUSA</a> (US Department of State), <a href="http://www.maxi-tip.cz/">Maxi Tip</a>, <a href="http://fbsdmon.org">FreeBSD Monitor</a>, <a href="http://danceb.in/">Dancebin</a>, <a href="http://theanarchistlibrary.org/">The Anarchist Library</a> and others. (Using Dancer, and happy to have your site featured? Let us know!)</p> <h2><a name="dancer2___almost_"></a>Dancer2 - almost!</h2> <p>Dancer2 has been coming along well, and is already being used in production. We're at the point now it needs polish, documentation improvements and some migration guides, and it can hit CPAN! Dancer2 is a complete rewrite of Dancer's core, using much better designs, for better maintainability and extendability, whilst maintaining backwards compatibility with Dancer1's API - so migrating to Dancer 2 should be quite straightforward.</p> <p>Expect further articles in the upcoming days with plenty more information on Dancer 2!</p> <h2><a name="new_features_in_dancer"></a>New features in Dancer</h2> <p>Most of our development effort has been going towards Dancer 2, Dancer 1 however has had many minor improvements and bugfixes, but a few more notable changes culled from the changelog:</p> <ul> <li><a name="item_Wallflower_now_shipped_separately"></a><b>Wallflower now shipped separately</b> <p>BooK's 'wallflower' tool for generating static websites has been removed from the Dancer distribution, and is now distributed separately as a more general-purpose tool under the name <a href="https://metacpan.org/module/App::Wallflower">App::Wallflower</a>.</p> </li> <li><a name="item_dancer_response_can_take_HTTP__Headers"></a><b>dancer_response can take HTTP::Headers</b> <p>You can now pass a <a href="https://metacpan.org/module/HTTP::Headers">HTTP::Headers</a> object to <code>dancer_response</code></p> </li> <li><a name="item_Dancer2_compatibility_features_in_Dancer__Plugin"></a><b>Dancer2 compatibility features in Dancer::Plugin</b> <p>Some new keywords are added to Dancer::Plugin to make writing plugins which can work on both Dancer and Dancer 2 a little easier. (There are a few minor differences in how plugins work in Dancer 2; expect an upcoming post on how to make a plugin compatible with both versions. Some of the more popular Dancer plugins have already been updated in readyness.)</p> </li> <li><a name="item_new_test_function_response_redirect_location_is"></a><b>new test function response_redirect_location_is</b> <p>Dancer::Test now provides a <code>response_redirect_location_is</code>, which does as the name would suggest - tests that the response to a request is a redirect to the expected location.</p> </li> </ul> <h2><a name="new_dancer_plugins"></a>New Dancer plugins</h2> <p>The Dancer plugin ecosystem continues to grow, with several new Dancer plugins appearing in 2012:</p> <ul> <li><a name="item__a_href__https___metacpan_org_module_Dancer__Plugin__Auth__Extensible__Dancer__Plugin__Auth__Extensible__a_"></a><b><a href="https://metacpan.org/module/Dancer::Plugin::Auth::Extensible">Dancer::Plugin::Auth::Extensible</a></b> <p>User authentication and authorisation is a common requirement; we had the old RBAC plugin, but it was in need of some serious work to make it pleasant to use. In the end, I decided to rock up a new authentication framework plugin for Dancer, designed to be simple and easy to use, and also easy to extend to support various authentication sources. A full article on this will appear in the upcoming days.</p> </li> <li><a name="item__a_href__https___metacpan_org_module_Dancer__Plugin__DictionaryCheck__Dancer__Plugin__DictionaryCheck__a_"></a><b><a href="https://metacpan.org/module/Dancer::Plugin::DictionaryCheck">Dancer::Plugin::DictionaryCheck</a></b> <p>A new plugin appearing just before this post went up, it's designed to make it easy to check a password to see if it is a dictionary word, to help stop users supplying insecure passwords.</p> </li> <li><a name="item__a_href__https___metacpan_org_module_Dancer__Plugin__CDN__Dancer__Plugin__CDN__a_"></a><b><a href="https://metacpan.org/module/Dancer::Plugin::CDN">Dancer::Plugin::CDN</a></b> <p>Provides URLs for static files including a content hash so that the URLs will change when the content changes, to deal with caching. Uses <a href="https://metacpan.org/module/HTTP::CDN">HTTP::CDN</a> which can be configured to perform automatic minification/compiling of CSS/JS/LESS.</p> </li> <li><a name="item__a_href__https___metacpan_org_module_Dancer__Plugin__Res__Dancer__Plugin__Res__a_"></a><b><a href="https://metacpan.org/module/Dancer::Plugin::Res">Dancer::Plugin::Res</a></b> <p>Provides syntactic sugar to easily set the response status and return a response in one go, rather than two steps.</p> </li> <li><a name="item__a_href__https___metacpan_org_module_Dancer__Plugin__I18N__Dancer__Plugin__I18N__a_"></a><b><a href="https://metacpan.org/module/Dancer::Plugin::I18N">Dancer::Plugin::I18N</a></b> <p>Provides internationalisation support for your Dancer apps, using <a href="https://metacpan.org/module/Locale::Maketext::Simple">Locale::Maketext::Simple</a>.</p> </li> <li><a name="item__a_href__https___metacpan_org_module_Dancer__Plugin__EmptyGIF__Dancer__Plugin__EmptyGIF__a_"></a><b><a href="https://metacpan.org/module/Dancer::Plugin::EmptyGIF">Dancer::Plugin::EmptyGIF</a></b> <p>Allows you to easily respond with an empty GIF image, with the headers set appropriately. This is useful for tracking purposes, if you want to embed an <code>IMG</code> tag in your HTML to call a route with certain params; in the route, you can then do whatever logging/other actions you need, then simply return an empty GIF to the browser.</p> </li> </ul> <h2><a name="dancer_at_lpw2012"></a>Dancer at LPW2012</h2> <p>Andrew Solomon ran a free two-hour training course at this year's London Perl Workshop - 13 people were registered to attend, but I haven't yet heard how many did attend. Andrew's course was a hands-on training session to develop a website with dynamic content using Dancer, during which the attendees would:</p> <ul> <li><a name="item_learn_to_use_the_Dancer_framework"></a><b>learn to use the Dancer framework</b> </li> <li><a name="item_learn_to_use_Template_Toolkit"></a><b>learn to use Template Toolkit</b> </li> <li><a name="item_understand_the_concept_of_Model_View_Controller"></a><b>understand the concept of Model-View-Controller</b> </li> <li><a name="item_experience_structuring_code_for_maintainability"></a><b>experience structuring code for maintainability</b> </li> <li><a name="item_experience_using_object_oriented_Perl_modules"></a><b>experience using object oriented Perl modules</b> </li> </ul> <h2><a name="dancer_powered_documentation"></a>Dancer-powered documentation</h2> <p>James Aitken (LoonyPandora) gave a talk at LPW 2012 on <a href="https://speakerdeck.com/loonypandora/documentation-for-fun-and-profit">Documentation For Fun and Profit</a> highlighting why good high-level documentation is important, and what you can do to make it even more useful. He gave a sneak peak of the REST API documentation and test console we use at UK2 - more details on this should be coming in a future post.</p> <h2><a name="dancer_irc_channel_community_grows"></a>Dancer IRC channel community grows</h2> <p>The community of helpful users on the Dancer IRC channel continues to grow! You can find us on <code>irc.perl.org</code> in <code>#dancer</code> - or <a href="irc://irc.perl.org/dancer">irc://irc.perl.org/dancer</a> if your system is configured to support <code>irc://</code> links.</p> <p>If you do not regularly use IRC but wish to join us, there is also a web IRC client available at <a href="http://dancer.pm/irc">http://dancer.pm/irc</a> for ease of use.</p> <p>At the time of writing, there are 98 people present in <code>#dancer</code>!</p> <h2><a name="dancer_repository_move"></a>Dancer repository move</h2> <p>Dancer's GitHub repository has been moved to the PerlDancer organisation on GitHub, to make sharing management of the repository and distributing commit bits where needed far easier - the repository is now found at:</p> <p><a href="https://github.com/PerlDancer/Dancer">https://github.com/PerlDancer/Dancer</a></p> <h2><a name="dancer_mailing_list_move"></a>Dancer mailing list move</h2> <p>Some users were finding that their posts didn't always reach the old mailing list. The list has been moved to a new list server, with a new list posting address: <a href="mailto:dancer-users@dancer.pm">dancer-users@dancer.pm</a>. The old address redirects to the new list.</p> <h2><a name="conclusion"></a>Conclusion</h2> <p>Well, this was a hastily-put-together look back at 2012; the following days will have various articles regarding Dancer which I hope will be of interest. Not all days are filled yet, so if you wish to write a post for the advent calendar, please contact me - all assistance would be greatly appreciated!</p> <h2><a name="author"></a>AUTHOR</h2> <p>David Precious (BIGPRESH)</p> </div>