Dancer Advent Calendar 2018 http://advent.perldancer.org/ What's next for Dancer? (looking into the future) http://advent.perldancer.org/2018/24 perl http://advent.perldancer.org/2018/24 Mon, 24 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="what_s_next_for_dancer__looking_into_the_future_"></a>What's next for Dancer? (looking into the future)</h1> <p>It's been a busy year, and we got a lot to be happy about. But there is always more work to be done, and always more improvements to be made.</p> <p>This list is by no means exhaustive, but it should give you a little glimpse for some of the things we have in store for 2019:</p> <ul> <li><a name="item_New_Documentation"></a><b>New Documentation</b> <p>You asked for it, and I made reference to it on day 1, all new documentation is coming in 2019. While many of you love the docs, a lot of you don't, and thanks to your comments in last year's survey, we think we have a good understanding of why.</p> <p>The new documentation branch is well underway, and addresses the shortcomings of the existing documentation by explaining important concepts in a clear and simple way. You'll have a better understanding of how and why Dancer2 does the things it does.</p> <p>New users will get some grounding in web development techniques, too. There's a lot of great changes for everyone!</p> </li> <li><a name="item_New_Website"></a><b>New Website</b> <p>There's a lot of great information on the Dancer website, but it's looking tired. There's a lot of new information to be added, and with a fresh coat of paint, Dancer's website will be as great as the software powering it.</p> </li> <li><a name="item_More_configuration_options"></a><b>More configuration options</b> <p>Configuration in Dancer2 leaves a lot to be desired (and I'll admit that I am a big part of the reason for why that is). Look for an overhaul of the configuration system in 2019, which starts with a barebones configuration system and allows for pluggable configuration engines (much like loggers and sessions in Dancer2). Since we are dedicated to not breaking working code, all of the existing configuration logic would be packaged as a plugin and enabled out of the box.</p> </li> <li><a name="item_Inline_parameter_type_checking"></a><b>Inline parameter type checking</b> <p>Very similar in many ways to Sawyer's <a href="https://metacpan.org/pod/Dancer2::Plugin::ParamTypes">Dancer2::Plugin::ParamTypes</a> plugin, but provided as core functionality. SysPete is actively developing a way to provide (optional) type checking of parameters within the core of Dancer2.</p> <p>In case you haven't read <a href="http://advent.perldancer.org/2018/22">Sawyer's article</a> about type checking parameters passed to your Dancer applications, I highly recommend it. It will change the way you do data validation.</p> </li> <li><a name="item_Continued_support_for_Dancer__1_"></a><b>Continued support for Dancer (1)</b> <p>While most all of our effort is spent on Dancer2 these days, there are no plans to sunset Dancer (1) as of now. Many of the core developers are still actively maintaining applications written in Dancer, as is much of our community.</p> <p>It is still our hope, however, that with an easy migration path from Dancer to Dancer2 that more of you will make the switch in 2019.</p> </li> </ul> <h2><a name="happy_holidays_"></a>Happy Holidays!</h2> <p>We hope all of you have a safe and happy holiday season, and we look forward to a great 2019! Until then, keep on Dancing!</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Jason Crome (CromeDome) for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>Copyright</h2> <p>No copyright retained. Enjoy.</p> <p>Jason A. Crome</p> </div> Dancer2::Logger::Console::Colored http://advent.perldancer.org/2018/23 perl http://advent.perldancer.org/2018/23 Sun, 23 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer2__logger__console__colored"></a>Dancer2::Logger::Console::Colored</h1> <p>During development the console output of your app is often one of the most important tools. But sometimes there is just too much noise, and changing the log settings all the time is not convenient. Of course you can grep the log or scroll, but wouldn't it be nice to get visual clues for what you're looking for?</p> <p>With <a href="https://metacpan.org/module/Dancer2::Logger::Console::Colored">Dancer2::Logger::Console::Colored</a> you can do that. It's a drop-in replacement for the default console logger, but with color. It will make the message appear in color depending on the levels. You can turn it on by setting your logger in your environment config file:</p> <pre class="prettyprint">logger: "Console::Colored"</pre> <p>Your log will instantly become cheerful and seasonal according to this default configuration:</p> <pre class="prettyprint"># config.yml (these are the defaults) engines: logger: Console::Colored: colored_origin: "cyan" colored_levels: core: "bold bright_white" debug: "bold bright_blue" info: "bold green" warning: "bold yellow" error: "bold yellow on_red" colored_messages: core: "bold bright_white" debug: "bold bright_blue" info: "bold green" warning: "bold yellow" error: "bold yellow on_red"</pre> <img src="/images/2018/23/log-1.png"> <p>The <code>colored_origin</code> refers to the part of the message that shows the package that this message originated in, as well as the file name and line.</p> <pre class="prettyprint">&gt;&gt; Dancer2 v0.207000 server 28764 listening on http://0.0.0.0:3000 [main:28764] debug @2018-12-19 20:31:06&gt; Hello World in test.pl l. 6 ^^^^ ^^^^^^^ ^</pre> <p>The <code>colored_levels</code> are the log levels themselves.</p> <pre class="prettyprint">[main:28764] debug @2018-12-19 20:31:06&gt; Hello World in test.pl l. 6 ^^^^^</pre> <p>And the <code>colored_messages</code> refer to the actual message.</p> <pre class="prettyprint">[main:28764] debug @2018-12-19 20:31:06&gt; Hello World in test.pl l. 6 ^^^^^^^^^^^</pre> <p>The colors are the same kind of color strings used by <a href="https://metacpan.org/module/Term::ANSIColor">Term::ANSIColor</a>. The first color refers to the foreground, and the second one to the background. You can add an optional <code>bold</code>.</p> <pre class="prettyprint">[bold] [foreground] [on_background]</pre> <h2><a name="changing_the_log_message"></a>Changing the log message</h2> <p>If you don't like the default log format, you can change it with the <code>log_format</code> setting, which is documented in detail in <a href="https://metacpan.org/module/Dancer2::Core::Role::Logger">Dancer2::Core::Role::Logger</a>. You can just stick it in with the other configuration.</p> <pre class="prettyprint"># config.yml (this is the default) engines: logger: Console::Colored: log_format: "[%a:%P] %L @%T&gt; %m in %f l. %l"</pre> <p>Usually you would run a single-worker server during development, so there is no need for request IDs. And since the message is now colored, we can also drop the log level. How about a simple format like the following:</p> <pre class="prettyprint">%t %f:%l&gt; %m # 20/Dec/2018 16:41:07 MyApp.pm:22&gt; Hello World</pre> <h2><a name="i_know_regular_expressions_"></a>I know regular expressions!</h2> <p>Sometimes just reading the log is not enough. You're looking for a specific piece of information. There are three pages of log for a single request, and debugging is hard work. Grepping is an option, but you need to look for a complex pattern. Maybe a product SKU, a date or some other piece of information.</p> <p>With Dancer2::Logger::Console::Colored you can actually make it come out with a background color so you can spot it easily, without having to change your code (or your added debugging log statements). Instead, you can put a regular expression into your configuration file.</p> <pre class="prettyprint"># config.yml engines: logger: Console::Colored: colored_regex: - re: "customer number \d+" color: "bold red"</pre> <p>This will find customer numbers according to the pattern and make them bold and red. You can supply a regular expression pattern and a color setting.</p> <img src="/images/2018/23/log-2.png"> <p>Note that there is a list under the <code>colored_regex</code> key. This means you can actually have multiple patterns. Even really simple ones that make <code>print</code> style debugging easy and attractive.</p> <pre class="prettyprint"># config.yml engines: logger: Console::Colored: colored_regex: - re: "customer number \d+" color: "bold red" - re: "\bhere\b" color: "white on_green"</pre> <p>If you want to highlight an entire log message in a specific color, you can set a pattern that captures everything. However, this works on the message only, and not on the entire formatted line of log output. (Patches welcome!)</p> <pre class="prettyprint"># config.yml engines: logger: Console::Colored: colored_regex: - re: ".+here.+" color: "white on_red"</pre> <img src="/images/2018/23/log-3.png"> <p>This opens up a lot of possibilities, for debugging as well as for general logging during development.</p> <h2><a name="what_else"></a>What else?</h2> <p>There are still some feature ideas in the backlog, but it's already pretty useful. If you would like to see additional features, please feel free to [open an issue on github](https://github.com/simbabque/Dancer2-Logger-Console-Colored/issues).</p> <p>And if you happen to use Mojolicious as well and need something similar, take a look at <a href="https://metacpan.org/module/Mojo::Log::Colored">Mojo::Log::Colored</a>, which is in the early stages of development, but works quite well.</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Julien Fiegehenn (simbabque) for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>Copyright</h2> <p>No copyright retained. Enjoy.</p> </div> Parameter testing with Dancer2::Plugin::ParamTypes http://advent.perldancer.org/2018/22 perl http://advent.perldancer.org/2018/22 Sat, 22 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="parameter_testing_with_dancer2__plugin__paramtypes"></a>Parameter testing with Dancer2::Plugin::ParamTypes</h1> <p>The most common web code you will ever write is testing your parameters. You might as well have a good way to do this.</p> <h2><a name="in_the_old_ages"></a>In the old ages</h2> <p>Way back then, we used to write code to check all of our arguments.</p> <p>If we had a route that includes some ID, we would check that we received it and that it matches the type we want. We would then decide what to do if it doesn't match. Over time, we would clean up and refactor, and try to reuse the checking code.</p> <p>For example:</p> <pre class="prettyprint">use Dancer2; get '/:id' =&gt; sub { my $id = route_parameters-&gt;{'id'}; if ( $id !~ /^[0-9]+$/ ) { send_error 'Bad ID' =&gt; 400; } # optional my $action = query_parameters-&gt;{'action'}; unless ( defined $action &amp;&amp; length $action ) { send_error 'Bad Action' =&gt; 400; } # use $id and maybe $action };</pre> <p>The more parameters we have, the more annoying it is to write these tests.</p> <p>But what's more revealing here is that this validation code is not actually part of our web code. It's input validation <i>for</i> our web code.</p> <h2><a name="a_different_perspective"></a>A different perspective</h2> <p>What if - instead of having to write all of this code - we maintained the Dancer2 spirit and allowed you to <b>declare</b> what your validation rules are, and have Dancer2 do the work for you?</p> <p>Lucky you! We have done just that with <a href="https://metacpan.org/module/Dancer2::Plugin::ParamTypes">Dancer2::Plugin::ParamTypes</a>!</p> <h3><a name="register_your_own_types"></a>Register your own types</h3> <p>There are normally two options that type check syntax give you:</p> <blockquote> <p>We didn't want to create our own type system or type validations. There are already plenty of good ones.</p> <blockquote> <p>And we didn't want to tie the plugin with a specific type system because it might not suit you.</p> </blockquote> <p>Instead, we picked a third option: Allowing you to connect it with whatever you want.</p> <pre class="prettyprint">use Dancer2; use Dancer2::Plugin::ParamTypes; # register an 'Int' register_type_check 'Int' =&gt; sub { $_[0] =~ /^[0-9]+$/ };</pre> <p>You could also register existing type systems:</p> <pre class="prettyprint">use MooX::Types::MooseLike::Base qw&lt; Int &gt;; reigster_type_checks( 'Int' =&gt; sub { Int()-&gt;( $_[0] ) }, );</pre> </blockquote> <h3><a name="using_your_now_available_types"></a>Using your now-available types</h3> <p>Once you register all the types you want, you could use them in your code with a simple stanza.</p> <pre class="prettyprint">use Dancer2::Plugin::ParamTypes; register_type_check(...); # Indented to make it more readable get '/:id' =&gt; with_types [ [ 'route', 'id', 'Int' ], 'optional =&gt; [ 'query', 'action', 'Str' ], ] =&gt; sub { my $id = route_parameters-&gt;{'id'}; # do something with $id because we know it exists and validated if ( my $action = query_parameters-&gt;{'action'} ) { # if it exists, we know it's validated } };</pre> <h2><a name="reusability__reusability__reusability"></a>Reusability, reusability, reusability</h2> <p><a href="https://metacpan.org/module/Dancer2::Plugin::ParamTypes">Dancer2::Plugin::ParamTypes</a> was built with a company code-abase in mind, where you would like to have common types available. You could easily accomplish that by subclassing it.</p> <pre class="prettyprint">package Dancer2::Plugin::MyCommonTypes; use Dancer2::Plugin; # Subclass the main plugin extends('Dancer2::Plugin::ParamTypes'); # Provide your own 'with_types' keyword plugin_keywords('with_types'); # Make our keyword call the parent plugin sub with_types { my $self = shift; return $self-&gt;SUPER::with_types(@_); } # Register all of our own type checks at build time sub BUILD { my $self = shift; $self-&gt;register_type_check 'Str' =&gt; sub {...}; $self-&gt;register_type_check 'ShortStr' =&gt; sub {...}; $self-&gt;register_type_check 'PositiveInt' =&gt; sub {...}; $self-&gt;register_type_check 'SHA1' =&gt; sub {...}; # Maybe more? ... }</pre> <p>Now we have our own plugin that also provides <code>with_types</code> which uses the original plugin with a set of registered type checks.</p> <pre class="prettyprint">package MyApp; use Dancer2; use Dancer2::Plugin::MyCommonTypes; post '/:entity/update/:id' =&gt; with_types [ [ 'route', 'entity', 'Str' ], [ 'route', 'id', 'PositiveInt' ], [ 'body', 'message', 'Str' ], 'optional' =&gt; [ 'body', 'sid', 'SHA1' ], ] =&gt; sub { my ( $entity, $id ) = @{ route_parameters() }{qw&lt; id entity &gt;}; my $message = body_parameters-&gt;{'message'}; my $sid = body_parameters-&gt;{'sid'} || ''; # everything is validated and required parameters are checked ... };</pre> <h2><a name="could_i_do_more_with_it"></a>Could I do more with it?</h2> <p>Absolutely!</p> <h3><a name="handle_multiple_sources"></a>Handle multiple sources</h3> <p>Normally, you would dictate to a user how they should send their paramters (in the query, in the body, or as part of the path - in the route), but sometimes you cannot control this. Maybe you're maintaining an old interface or supporting outdated APIs.</p> <p><a href="https://metacpan.org/module/Dancer2::Plugin::ParamTypes">Dancer2::Plugin::ParamTypes</a> is flexible enough to support multiple sources for an argument:</p> <pre class="prettyprint">any [ 'get', 'post' ] =&gt; '/:entity/:id' =&gt; with_types [ [ 'route', 'entity', 'Str' ], [ 'route', 'id', 'PositiveInt' ], [ [ 'query', 'body' ], 'format', 'Str' ], 'optional' =&gt; [ 'body', 'sid', 'SHA1' ], ] =&gt; sub {...};</pre> <p>In this form, the parameter <code>format</code> can be provided either in the query string or in the body, because your route might be either a <b>GET</b> or a <b>POST</b>.</p> <h3><a name="register_type_actions"></a>Register type actions</h3> <p>Type checking itself is the main role of this plugin, but you can also control how it behaves.</p> <p>The default action to perform when a type check fails is to error out, but you can decide to act differently by registering a different action.</p> <pre class="prettyprint">register_type_action 'SoftError' =&gt; sub { my ( $self, $details ) = @_; warning "Parameter $details-&gt;{'name'} from $details-&gt;{'source'} " . "failed checking for type $details-&gt;{'type'}, called " . "action $details-&gt;{'action'}"; return; }; get '/:id' =&gt; with_types [ [ 'query', 'age', 'Int', 'SoftError', ] =&gt; sub {...};</pre> <p>On a bad <code>age</code> parameter, it will print out the following warning:</p> <pre class="prettyprint">Parameter age from query failed checking for type Int, called action SoftError</pre> <p>This means you can also register a set of actions that you want to call in different cases.</p> <h2><a name="conclusion"></a>Conclusion</h2> <p><a href="https://metacpan.org/module/Dancer2::Plugin::ParamTypes">Dancer2::Plugin::ParamTypes</a> allows you to define your own types and your own actions, to create your own plugin that helps you maintain reusability and consistency across your application with fewer code duplication and less effort.</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Sawyer X for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>Copyright</h2> <p>No copyright retained. Enjoy.</p> <p>2018 // Sawyer X <code>&lt;xsawyerx@cpan.org&gt;</code></p> </div> Spam and bot prevention without the use of CAPTCHAs http://advent.perldancer.org/2018/21 perl http://advent.perldancer.org/2018/21 Fri, 21 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="spam_and_bot_prevention_without_the_use_of_captchas"></a>Spam and bot prevention without the use of CAPTCHAs</h1> <h2><a name="the_problem"></a>The problem</h2> <p>I was writing a contact form for my LLC, and having been the victim of an automated form-filler in the past, I wanted to prevent getting spammed by my own contact form. My first instinct told me to add a CAPTCHA to the form as a means to ensure there's a human at the keyboard. But as it turns out, this isn't a great solution for a variety of reasons.</p> <h2><a name="a_little_background"></a>A little background</h2> <p>I worked with my friend Job a few contracts back, and he is passionate about developing accessible software and educating other developers about accessibility issues. Job really opened my eyes to a lot of bad development practices, and the problems they caused for users with accessibility needs. So when he heard I wanted to put a CAPTCHA on my form, he nearly had a stroke. When the initial shock wore off, we talked about how I could accomplish my goal, but do it in an accessible way.</p> <p>CAPTCHAs come in a number of shapes and sizes. At the time I wrote this, reCAPTCHA was the latest hotness. It featured text that was really hard to read, and on occasion, featured audio for those who couldn't see or read the text. They were also unfortunately stupidly easy to crack, and thanks to Mechanical Turk and other tricks, they quickly became a useless piece of nuisance for your average web user.</p> <p>Why are CAPTCHAs bad for accessibility? I don't know about you, but even with good vision, I have a really hard time reading the text on many CAPTCHAs. I cannot begin to imagine how much more difficult it would be for a sighted user to attempt to read one. But many have audio, you say! Have you listened to a CAPTCHA that's read to you? I am slightly hard of hearing, and I am unable to make them out many times. And some clever developers have even found ways to circumvent the audio CAPTCHAs now. With more modern CAPTCHAs, users with screen readers and other assistive devices/settings can often exhibit some of the same signs that bots do, but unfortunately, you're not allowed to declare or prove your humanity - it's all up to a machine learning algorithm, and once you're marked a bot, things are out of your control.</p> <p>Even before I learned about the accessibility issues with CAPTCHAs, I knew adding one to my form was just bad UX. It's shifting the burden of your problem onto your users. But when the only tool you think you possess is a hammer, all of your problems start to look a lot like nails.</p> <p>The solution Job encouraged me to pursue was to have two form fields on my contact form that should never get filled in: one that is a standard form field, but using CSS, is visually hidden from the user, and the other is to use a hidden form field. If any one of those fields is filled in, then you know you are dealing with a spambot, and you can take the appropriate action.</p> <h2><a name="backend_code"></a>Backend code</h2> <p>Making this work in Dancer2 is really, really easy:</p> <pre class="prettyprint">post '/contact' =&gt; sub { my $spam_1 = body_parameters-&gt;get( 'spam_one' ); my $spam_2 = body_parameters-&gt;get( 'spam_two' ); redirect 'https://en.wikipedia.org/wiki/Three_Laws_of_Robotics' if $spam_1 || $spam_2; # Ok, we seem to be a real human, so do something... my $contact; my $name = body_parameters-&gt;get( 'name' ); my $email = body_parameters-&gt;get( 'email' ); my $subject = body_parameters-&gt;get( 'subject' ); my $message = body_parameters-&gt;get( 'message' ); # Now, your code to do something with the form info: # - Put it in a database # - email it # - etc. etc. };</pre> <p>We look to see if either of the spam-catching form fields is populated. If either one of them is populated, we educate the bot in <a href="https://en.wikipedia.org/wiki/Three_Laws_of_Robotics">Asimov's Three Laws of Robotics</a>. If not, we are reasonably certain we are dealing with a human, and we continue on our merry way.</p> <h2><a name="front_end_magic"></a>Front-end magic</h2> <p>The front end is where things get a little more interesting (note: I have deliberately omitted any styling for the sake of brevity):</p> <pre class="prettyprint">&lt;form method="post" action="/contact" id="contact"&gt; &lt;label for="name"&gt;Name&lt;/label&gt; &lt;input type="text" name="name" id="name" placeholder="First and last name"&gt; &lt;label for="email"&gt;Email Address&lt;/label&gt; &lt;input type="text" name="email" id="email" placeholder="someone@example.com"&gt; &lt;label for="subject"&gt;Subject&lt;/label&gt; &lt;input type="text" name="subject" id="subject" placeholder="Nature of inquiry?"&gt; &lt;label for="message"&gt;Message&lt;/label&gt; &lt;textarea name="message" id="message" placeholder="Message text"&gt;&lt;/textarea&gt; &lt;label class="visuallyhidden"&gt;Don't fill this in!&lt;input type="text" name="spam_one" tabindex="-1"&gt;&lt;/label&gt; &lt;input type="hidden" name="spam_two"&gt; &lt;input type="submit"&gt;Send Request&lt;/input&gt; &lt;/form&gt;</pre> <p>For the most part, it's a standard HTML form with a couple of notable exceptions:</p> <pre class="prettyprint">&lt;label class="visuallyhidden"&gt;Don't fill this in!&lt;input type="text" name="spam_one" tabindex="-1"&gt;&lt;/label&gt;</pre> <p>This uses a special CSS class, <code>visuallyhidden</code>, to make this form field invisible to the user. By giving it a <code>tabindex="-1"</code>, we make sure the user cannot accidentally tab to the field. There are still a few ways that field could accidentally get focus however, so we help the user here by giving it a label that says "Don't fill this in!"</p> <p>What does <code>visuallyhidden</code> look like?</p> <pre class="prettyprint">.visuallyhidden { border: 0; clip: rect(0 0 0 0); height: 1px; margin: -1px; overflow: hidden; padding: 0; position: absolute; width: 1px; }</pre> <p>The second preventative measure simply puts a hidden field on the page:</p> <pre class="prettyprint">&lt;input type="hidden" name="spam_two"&gt;</pre> <p>Screen readers know to ignore this because it's hidden, and because it's hidden, the page won't show this field. As a user, you won't ever have to fill it in.</p> <p>Thankfully, a lot of spam bots are stupid, and blindly fill in any form field they see. They can't parse the CSS in such a way that they can tell one of the fields is unusably hidden, and they often don't pay attention to the hidden attribute... so they fall right into our trap. Enjoy your free education, spambot!</p> <h2><a name="final_thoughts"></a>Final thoughts</h2> <p>Is it foolproof? No, not by a longshot. Anyone who really wants to is going to find a way to spam you anyhow, given a sufficient amount of time and motivation. But this will slow many down, and prevent a bunch, and you won't drive sighted users away from your site. In the three and a half years I have used this, only two spambots have made it through, and my server logs indicate that many others have tried.</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Jason Crome (CromeDome) for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>Copyright</h2> <p>No copyright retained. Enjoy.</p> <p>Jason A. Crome</p> </div> Testing Dancer with Test::Mojo http://advent.perldancer.org/2018/20 perl http://advent.perldancer.org/2018/20 Thu, 20 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="testing_dancer_with_test__mojo"></a>Testing Dancer with Test::Mojo</h1> <p>Authors of Dancer (and other) PSGI applications are probably accustomed to <a href="https://metacpan.org/pod/distribution/Dancer2/lib/Dancer2/Manual.pod#TESTING">testing</a> with <a href="https://metacpan.org/pod/Plack::Test">Plack::Test</a>, and while that is a venerated option, it is pretty bare-bones.</p> <p>During advent last year, I wrote about <a href="https://mojolicious.org/perldoc/Test/Mojo">Test::Mojo</a>, showing the many easy and (dare I say) fun ways that you can use it to test your Mojolicious applications. If you missed it, go <a href="https://mojolicious.io/blog/2017/12/09/day-9-the-best-way-to-test/">check it out</a>.</p> <p>I expect there are at least a few of you out there who read that and think, "I'd love to use that, but I don't use Mojolicious!"; well, you're in luck! With just a little role to bridge the gap, you can use Test::Mojo to test your PSGI applications too!</p> <h2><a name="mounting_psgi_applications"></a>Mounting PSGI Applications</h2> <p>Mojolicious itself doesn't use the <a href="https://metacpan.org/pod/PSGI">PSGI</a> protocol, owing to certain features that it doesn't provide and which are necessary for certain asynchronous operations. That said, you can serve a Mojolicious application on a PSGI server by using <a href="https://mojolicious.org/perldoc/Mojo/Server/PSGI">Mojo::Server::PSGI</a>. This Mojolicious-core module is automatically used for you when your Mojolicious-based app detects that it has started under a PSGI server (e.g. plackup or Starman).</p> <p>While translating between a Mojo app and a PSGI server is core functionality, doing the opposite, translating between a PSGI app and a Mojolicious server (or app, as you'll see) is available as a third party module. <a href="https://metacpan.org/pod/Mojolicious::Plugin::MountPSGI">Mojolicious::Plugin::MountPSGI</a>, as it's name implies, can mount a PSGI application into a Mojolicious-based one. To do so, it builds a new, empty Mojolicious application that translates all requests to PSGI environments before dispatching to it as with any <a href="https://mojolicious.org/perldoc/Mojolicious/Plugin/Mount">mount</a>-ed application.</p> <h2><a name="testing_using_test__mojo"></a>Testing using Test::Mojo</h2> <p>Once you can do that, it is trivial to take a PSGI application, wrap it with MountPSGI, and set it as the application for use with Test::Mojo. Still, to make it even easier, that has all been done for you in <a href="https://metacpan.org/pod/Test::Mojo::Role::PSGI">Test::Mojo::Role::PSGI</a>.</p> <p>Like any <a href="https://mojolicious.io/blog/2017/12/13/day-13-more-about-roles/">Mojolicious Role</a>, we can use <code>with_roles</code> to create a (mostly anonymous) subclass with the role applied. You can use the shortcut <code>+</code> to stand in for <code>Test::Mojo::Role::</code>.</p> <pre class="prettyprint">use Test::Mojo; my $class = Test::Mojo-&gt;with_roles('+PSGI');</pre> <p>Then you instantiate that role with the path to the PSGI application, or else the PSGI application itself.</p> <p>Since you're using roles, which are all about composition, you can also apply other roles that you might <a href="https://metacpan.org/search?q=%22Test%3A%3AMojo%3A%3ARole%22">find on CPAN</a>.</p> <h2><a name="an_example"></a>An Example</h2> <p>As an example, let's say we have a simple application script (named <code>app.psgi</code>) that can render a <code>"hello world"</code> or <code>"hello $user"</code> in several formats. I'll allow a plain text response, JSON, and templated HTML (using the <a href="https://metacpan.org/pod/Dancer2::Template::Simple">simple</a> template to keep this concise).</p> <pre class="prettyprint">use Dancer2; set template =&gt; 'simple'; set views =&gt; '.'; any '/text' =&gt; sub { my $name = param('name') // 'world'; send_as plain =&gt; "hello $name"; }; any '/data' =&gt; sub { my $name = param('name') // 'world'; send_as JSON =&gt; { hello =&gt; $name }; }; any '/html' =&gt; sub { my $name = param('name') // 'world'; template 'hello' =&gt; { name =&gt; $name }; }; start;</pre> <p>And the template (<code>hello.tt</code>) is</p> <pre class="prettyprint">&lt;dl id="data"&gt; &lt;dt id="hello"&gt;hello&lt;/dt&gt; &lt;dd&gt;&lt;% name %&gt;&lt;/dd&gt; &lt;/dl&gt;</pre> <p>The <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dl">dl</a>, <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dt">dt</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Element/dd">dd</a> tags are a semantic way to markup key-value pairs in HTML, so it is almost the same as the JSON form above it. The HTML I've built, while nice for display isn't necessarily nice for querying programmatically, this is on purpose for the example.</p> <h2><a name="the_tests"></a>The Tests</h2> <p>Of course we could start the application with <a href="https://metacpan.org/pod/distribution/Plack/script/plackup">plackup</a> but that's not what we're trying to do. I'll break the test script down a bit but if you want to see any of these files look at the <a href="https://github.com/MojoliciousDotIO/mojolicious.io/tree/master/blog/2018/12/20/testing-dancer/ex">blog repo</a> for a full listing. Instead, let's load this into a test script.</p> <pre class="prettyprint">use Mojo::Base -strict;</pre> <p>Now if you aren't familiar, <code>use Mojo::Base -strict</code> is a quick way to say</p> <pre class="prettyprint">use strict; use warnings; use utf8; use IO::Handle; use feature ':5.10';</pre> <p>but saves a lot of typing. Next we load the necessary testing libraries. Then make an instance of <code>Test::Mojo</code> composed with the <code>PSGI</code> role and make a new instance that points to the app we want to test.</p> <pre class="prettyprint">use Test::More; use Test::Mojo; my $t = Test::Mojo-&gt;with_roles('+PSGI')-&gt;new('app.psgi');</pre> <p>With that out of the way, on to the tests! In our first tests we'll focus on the plain text endpoint <code>/text</code>.</p> <pre class="prettyprint">$t-&gt;get_ok('/text') -&gt;status_is(200) -&gt;content_type_like(qr[text/plain]) -&gt;content_is('hello world');</pre> <p>Each of the above method calls is a test. The first, <code>get_ok</code>, builds a transaction and requests the resource. Since the url is relative, it is handled by the app (if we wanted we could request and web resource too using a fully qualified url). The transaction is stored in the tester object (<code>$t</code>) and all following tests will check it until it is replaced by the next request.</p> <p>The remaining tests are reasonably self-explanatory, we check that the response status was 200, that we got a content type header that we expected and that its content is as we expect. The content has already been utf-8 decoded, and the script has implicitly <code>use utf8</code>, so if you expected unicode, you can compare them easily. The tests return the tester object so chaining is possible, making for visually clean sets of tests.</p> <p>The next test is similar but this one uses the standard <a href="https://mojolicious.org/perldoc/Mojo/UserAgent">Mojo::UserAgent</a> style request generation to build a query string naming Santa for our greeting. The tests are all the same except of course that it checks that the content greets Santa.</p> <pre class="prettyprint">$t-&gt;get_ok('/text', form =&gt; { name =&gt; 'santa' }) -&gt;status_is(200) -&gt;content_type_like(qr[text/plain]) -&gt;content_is('hello santa');</pre> <p>Moving on we request the data endpoint, both without and with a query, then similarly test the responses.</p> <pre class="prettyprint">$t-&gt;get_ok('/data') -&gt;status_is(200) -&gt;content_type_like(qr[application/json]) -&gt;json_is('/hello' =&gt; 'world'); $t-&gt;post_ok('/data' =&gt; form =&gt; { name =&gt; 'rudolph' }) -&gt;status_is(200) -&gt;content_type_like(qr[application/json]) -&gt;json_is('/hello' =&gt; 'rudolph');</pre> <p>You can see we use the <code>json_is</code> method to test the responses. Now, the test could have been <code>-&gt;json_is({hello =&gt; 'rudolph'})</code> if had wanted to test the entire document. By passing a <a href="https://mojolicious.org/perldoc/Mojo/JSON/Pointer">JSON Pointer</a> I can inspect only the portions I'm interested in.</p> <p>Finally I'm going to test the HTML endpoint. As I said above, the result resists easy parsing. We want to test the <code>dd</code> tag contents that follows a <code>dt</code> tag with the id <code>hello</code>, all inside a <code>dl</code> tag with the id <code>data</code>. That would be a monstrous regexp (hehe). However it is a piece of cake using <a href="https://mojolicious.org/perldoc/Mojo/DOM/CSS">CSS Selectors</a>.</p> <pre class="prettyprint">$t-&gt;get_ok('/html') -&gt;status_is(200) -&gt;content_type_like(qr[text/html]) -&gt;text_is('dl#data dt#hello + dd', 'world'); $t-&gt;post_ok('/html' =&gt; form =&gt; { name =&gt; 'grinch' }) -&gt;status_is(200) -&gt;content_type_like(qr[text/html]) -&gt;text_is('dl#data dt#hello + dd', 'grinch'); done_testing;</pre> <p>In this year's Mojolicious advent calendar, we've already seen <a href="https://mojolicious.io/blog/2018/12/05/compound-selectors/">some</a> <a href="https://mojolicious.io/blog/2018/12/14/a-practical-example-of-mojo-dom/">great</a> <a href="https://mojolicious.io/blog/2018/12/15/practical-web-content-munging/">examples</a> of the power of CSS selectors so I won't go into too much detail. The point remains however, testing HTML responses with CSS selectors allows you to make your tests targetd in a way that allows you to write more and better tests since you don't have to hack around extracting the bits you want.</p> <h2><a name="testing_websockets"></a>Testing WebSockets</h2> <p>Ok so that's great and all, but of course now it comes to the point you've all been waiting for: can you test WebSockets? As Jason Crome mentioned in his <a href="http://advent.perldancer.org/2018/13">Twelve Days of Dancer</a> "State of Dancer", you can now dance with WebSockets via <a href="https://metacpan.org/pod/Dancer2::Plugin::WebSocket">Dancer2::Plugin::WebSocket</a>, so can Test::Mojo test them?</p> <p>Well, so far not via the role I showed above. It might be possible, but it would involve learning deep PSGI magick that I'm not sure I'm smart enough to do; patches welcome obviously :D.</p> <p>Still I mentioned above that Test::Mojo can test anything it can access via an fully qualified URL, so let's just start up a server and test it! I'll use the <a href="https://github.com/yanick/Dancer2-Plugin-WebSocket/tree/releases/example">example bundled with the plugin</a> for simplicty.</p> <pre class="prettyprint">use Mojo::Base -strict; use EV; use Test::More; use Test::Mojo; use Twiggy::Server; use Plack::Util; my $app = Plack::Util::load_psgi('bin/app.psgi'); my $url; my $twiggy = Twiggy::Server-&gt;new( host =&gt; '127.0.0.1', server_ready =&gt; sub { my $args = shift; $url = "ws://$args-&gt;{host}:$args-&gt;{port}/ws"; }, ); $twiggy-&gt;register_service($app);</pre> <p>This starts Twiggy bound to localhost on a random port and starts the application using it. When the server starts, the actual host and port are passed to the <code>server_ready</code> callback which we use to build the test url. Now you just create a Test::Mojo instance as normal but this time open a websocket to the fully-qualified url that we built above.</p> <pre class="prettyprint">my $t = Test::Mojo-&gt;new; $t-&gt;websocket_ok($url) -&gt;send_ok({json =&gt; {hello =&gt; 'Dancer'}}) -&gt;message_ok -&gt;json_message_is({hello =&gt; 'browser!'}) -&gt;finish_ok; done_testing;</pre> <p>Unlike the previous examples, this time the connection stays open (but blocked) between method calls. Per the protocol of the example, we first send a greeting to the Dancer app as a JSON document. Since so much real-world websocket usage is just serialized JSON messages, Mojolicious provides many JSON-over-WebSocket conveniences. One such convenience is a virtual websocket frame type that takes a data structure and serializes it as JSON before actually sending it as a text frame.</p> <p>We then wait to get a message in response with <code>message_ok</code>. In this case, we expect the application to greet us by calling us "browser!". Oh well, it doesn't know any better! We can the test that JSON reply with <code>json_message_is</code> (like <code>json_is</code> above but for websocket messages). Finally we close the connection, testing that it closes correctly.</p> <p>Testing WebSockets, even from a Dancer application, is easy!</p> <h2><a name="conclusion"></a>Conclusion</h2> <p>Although there are some great testing options in the PSGI space, Test::Mojo has lots of benefits for Dancer and PSGI users. By using Test::Mojo::Role::PSGI or by running against a locally-bound server, Test::Mojo can be a tool in the toolbox of any PSGI developer.</p> <h2><a name="author"></a>AUTHOR</h2> <p>Joel Berger (<a href="https://twitter.com/joelaberger">@joelaberger</a>) has Ph.D. in Physics from the University of Illinois at Chicago. He is an avid Perl user, <a href="https://metacpan.org/author/JBERGER">author</a>, and is a member of the Mojolicious Core Team.</p> <h2><a name="copyright"></a>COPYRIGHT</h2> <p>Copyright (c) 2018 Joel Berger</p> </div> Logging with Dancer2::Logger::Log4perl http://advent.perldancer.org/2018/19 perl http://advent.perldancer.org/2018/19 Wed, 19 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="logging_with_dancer2__logger__log4perl"></a>Logging with Dancer2::Logger::Log4perl</h1> <h2><a name="history"></a>History</h2> <p>It's late summer 2018, and there has been a hole in the logging ecosystem of Dancer2 for some time now - we've been missing a Log4perl plugin. There are certainly a lot of great logging options available (<code>Dancer2::Logger::LogAny</code>, <code>Dancer2::Logger::Syslog</code>, and <code>Dancer2::Logger::LogReport</code> to name a few), and a couple of them even include appenders for Log4perl. In looking at our own needs, however, they all seemed to be overkill.</p> <p>At <code>$work</code>, everything is based on Log4perl (and, in our client-facing Java apps, Log4j), so there has never been need for another logger. We'd been happily using <code>Dancer::Logger::Log4perl</code> for years, and wanted to continue to use something familar to us. Before investing some time in writing our own Log4perl plugin for Dancer2, we expanded our search beyond metacpan, and were rewarded in doing so.</p> <p>We stumbled across a project from Ryan Larscheidt and Jon Miner at the University of Wisconsin. They wrote a Log4perl plugin for Dancer2 with the intent of releasing it, but as their priorities and projects shifted, it never made its way to CPAN. I used to go to MadMongers (Madison Perl Mongers), so before long, I was able to track them down and they gave me their blessing to release it.</p> <p>A little packaging and a few tests later, <code>Dancer2::Logger::Log4perl</code> was on its way to CPAN!</p> <h2><a name="configuration"></a>Configuration</h2> <p>First, we need to create a basic Log4perl configuration. Create a new Dancer2 application (I'll call mine <code>TestLog4perl</code>), and in the application directory create a <i>log4perl.conf</i> file with the following:</p> <pre class="prettyprint">log4perl.rootLogger = DEBUG, LOG1 log4perl.appender.LOG1 = Log::Log4perl::Appender::File log4perl.appender.LOG1.filename = logs/mylog.log log4perl.appender.LOG1.mode = append log4perl.appender.LOG1.layout = Log::Log4perl::Layout::PatternLayout log4perl.appender.LOG1.layout.ConversionPattern = %d %p %m %n</pre> <p>The first one defines our root application logger. The first parameter after the equals sign says what is the minimum level we should log. Since we are saying the minimum log level should be <code>DEBUG</code>, any messages from Dancer2 itself, and anything logged at the <code>core</code> level will be ignored.</p> <p>After the minimum log level is a comma-separated list of appenders to write to. For now, we will create a single appender named <code>LOG1</code> (we will see how to add a second appender below). This will write to a file in the <i>logs/</i> directory named <i>mylog.log</i>, using the <a href="https://metacpan.org/pod/Log::Log4perl::Appender::File">Log::Log4perl::Appender::File</a> appender. When the app is started, it will append to an existing log file of that name (or create the file if necessary), and will write to it with a format specified by <a href="https://metacpan.org/pod/Log::Log4perl::Layout::PatternLayout">Log::Log4perl::Layout::PatternLayout</a>.</p> <p>Each appender can have its own configuration directives. Please consult the pod for each appender for a list of its configuration parameters.</p> <p>Next, we have to tell our application that we are using <code>Dancer2::Logger::Log4perl</code> as our preferred logger. Edit your <i>environments/development.yml</i> file, and comment out the <code>logger: "console"</code> line. Replace it with the following:</p> <pre class="prettyprint">logger: log4perl log: core engines: logger: log4perl: config_file: log4perl.conf</pre> <p>This tells Dancer2 to use <code>Dancer2::Logger::Log4perl</code> as its logging engine, and to send all levels of message to it. Finally, Log4perl should look for its configuration file in the root application directory in a file called <i>log4perl.conf</i>.</p> <h2><a name="usage"></a>Usage</h2> <p>Using Log4perl is simple: use the logging keywords you are already familiar with in Dancer:</p> <pre class="prettyprint">get '/' =&gt; sub { debug "I'M IN UR INDEX"; template 'index' =&gt; { 'title' =&gt; 'TestLog4perl' }; };</pre> <p>Start your application and visit <code>http://localhost:5000/</code>. You will see the following in your <i>logs/mylog.log</i> file:</p> <pre class="prettyprint">2018/12/18 21:36:02 DEBUG I'M IN UR INDEX</pre> <h2><a name="hey__i_can_t_see_my_log_messages_on_the_screen_"></a>Hey, I can't see my log messages on the screen!</h2> <p>That's because we didn't add a screen appender! With Log4perl, adding another appender is easy. Let's add another section to our <i>log4perl.conf</i> file:</p> <pre class="prettyprint">log4perl.appender.SCREEN = Log::Log4perl::Appender::Screen log4perl.appender.SCREEN.stderr = 0 log4perl.appender.SCREEN.layout = Log::Log4perl::Layout::PatternLayout log4perl.appender.SCREEN.layout.ConversionPattern = %m %n</pre> <p>This creates another appender named <code>SCREEN</code>. We then need to tell our root logger to use this appender as well:</p> <pre class="prettyprint">log4perl.rootLogger = DEBUG, LOG1, SCREEN</pre> <p>Now, restart your application, and visit a route that has logging installed, and you will see your log message not only goes to the <i>logs/mylog.log</i> file, but also displays on the console running your application. Easy!</p> <h2><a name="some_gotchas"></a>Some Gotchas</h2> <p>There are a couple of important nuances you should be aware of:</p> <ul> <li><a name="item_Environment_configuration_replaces_logging_configuration"></a><b>Environment configuration replaces logging configuration</b> <p>If you put your logging configuration in <i>config.yml</i> rather than one of your environment-specific configuration files (such as <i>environments/development.yml</i>), you stand a good chance of not using the logging configuration you think you are using. The default configuration file for the development environment, for example, logs only to the console. If you put your Log4perl configuration in <i>config.yml</i> and don't change your development configuration file, your Log4perl configuration will be passed over for the default console logger.</p> <p>From my own experience, <b>always</b> configure your logger in your environment-specific configuration, unless you use the same configuration across all environments (I don't endorse this practice).</p> </li> <li><a name="item_Core_level_messages_are_passed_as_log_level_trace__but_will_not_be_passed_unless_Dancer2_s_log_level_is_core_"></a><b>Core level messages are passed as log level trace, but will not be passed unless Dancer2's log level is core.</b> <p>Since <code>core</code> doesn't have a good corresponding level in Log4perl, <code>core</code> level messages are sent over to Log4perl at the <code>trace</code> log level. This <b>only</b> happens when you set Dancer2's log level in your <i>config.yml</i> file to <code>core</code> however. So your preferred log level setting is respected, even if <code>core</code> level messages have to be reported at a different level.</p> </li> <li><a name="item__code_log__code__should_be_set_a_lower_priority_than_the_lowest_priority_as_set_in_your_Log4perl_configuration_"></a><b><code>log</code> should be set a lower priority than the lowest priority as set in your Log4perl configuration.</b> <p>If it isn't, the log messages will not be passed to Log4perl.</p> </li> </ul> <h2><a name="conclusion"></a>Conclusion</h2> <p>If Log4perl is all the logging you need in your Dancer2 applications, then <code>Dancer2::Logger::Log4perl</code> is well worth a look. It gives you much of the functionality available to Log4perl while using the logging syntax built into Dancer2. This article should give you sufficient grounding to get going with Log4perl in your Dancer2 applications.</p> <h2><a name="further_reading"></a>Further Reading</h2> <ul> <li><a name="item__a_href__https___metacpan_org_pod_Dancer2__Logger__Log4perl__Dancer2__Logger__Log4perl__a_"></a><b><a href="https://metacpan.org/pod/Dancer2::Logger::Log4perl">Dancer2::Logger::Log4perl</a></b> </li> <li><a name="item__a_href__https___metacpan_org_pod_Log__Log4perl__Log4perl__a_"></a><b><a href="https://metacpan.org/pod/Log::Log4perl">Log4perl</a></b> </li> <li><a name="item__a_href__https___www_perl_com_pub_2002_09_11_log4perl_html___Log4Perl_Tutorial__a_"></a><b><a href="https://www.perl.com/pub/2002/09/11/log4perl.html/">Log4Perl Tutorial</a></b> </li> </ul> <h2><a name="author"></a>Author</h2> <p>This article has been written by Jason Crome (CromeDome) for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>Copyright</h2> <p>No copyright retained. Enjoy.</p> <p>Jason A. Crome</p> </div> Customizing and extending your Dancer2 application generation http://advent.perldancer.org/2018/18 perl http://advent.perldancer.org/2018/18 Tue, 18 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="customizing_and_extending_your_dancer2_application_generation"></a>Customizing and extending your Dancer2 application generation</h1> <p>Dancer2 provides a useful command line that helps you generate a Dancer2 application skeleton without having to write it yourself.</p> <p>For example, creating an app called <code>My::Web::App</code>, you can run the following:</p> <pre class="prettyprint">dancer2 gen -a My::Web::App</pre> <p>The <code>dancer2</code> command line has a few more options, which you can see, if you run <code>dancer2 gen --help</code>.</p> <h2><a name="changing_the_skeleton"></a>Changing the skeleton</h2> <p>Dancer2 generates a skeleton that it useful for most developers, but if you are a seasoned Dancer2 developer, you might have a set of preferences not represented in the default skeleton.</p> <p>If it's different file setup that you want to have, you could partially achieve it with the <code>dancer2 gen -s DIRECTORY</code>, indicating a different skeleton directory. But that doesn't fix all of it.</p> <p>To have full control over the entire scaffolding operation, you will need to have control of the command line implementation. Let me show you how.</p> <h2><a name="extending_in_a_class"></a>Extending in a class</h2> <p>To extend the application in a class, you will need to write a new class with a new command. That class will then need to be loaded in your environment for you to enjoy it.</p> <p>There are two options:</p> <ul> <li> <p>Write a new distribution with your new module and install it locally. Done.</p> </li> <li> <p>Write a new module and make the directory in which it sits available in your <code>$PERL5LIB</code> environment variable.</p> <p>This option is more useful for companies that have a big library directory that is always available in the include directories list.</p> <p>An example of this:</p> <pre class="prettyprint"># Creating the directory $ mkdir -p /opt/perl/lib/Dancer2/CLI/Command/ $ cd /opt/perl/lib/Dancer2/CLI/Command/ # Putting a mostly-empty file $ echo -e "package Dancer2::CLI::Command::new;\n1;" &gt;&gt; new.pm # Now making sure this path is in PERL5LIB # (replace the bashrc file path with your system's path) echo "export PERL5LIB="/opt/perl/lib/:$PERL5LIB" &gt;&gt; /etc/bash.bashrc</pre> </li> </ul> <h2><a name="writing_your_own_command"></a>Writing your own command</h2> <p><a href="https://metacpan.org/module/Dancer2">Dancer2</a> uses <a href="https://metacpan.org/module/App::Cmd">App::Cmd</a> to implement the <code>dancer2</code> command line utility. This means you can introduce additional commands by just implementing a class.</p> <h3><a name="writing_a_new_command"></a>Writing a new command</h3> <pre class="prettyprint">package Dancer2::CLI::Command::activate use strict; use warnings; use Path::Tiny qw&lt; path &gt;; use App::Cmd::Setup -command; sub description { 'Activating our application' } sub opt_desc { return ( [ 'directory|d', 'Application directory' ], # More options... ); } sub validate_args { my ( $self, $opt, $args ) = @_; $opts-&gt;{'directory'} or $self-&gt;usage_error('You did not provide a directory'); path( $opt-&gt;{'directory'} )-&gt;is_dir or $self-&gt;usage_error('Path provided is not a directory'); } sub execute { my ( $self, $opt, $args ) = @_; my $dir = $opts-&gt;{'directory'}; # Implement the application activation # (Whatever that means...) } 1;</pre> <p>In this example, we introduce a new command to <code>dancer2</code>. As long as this class is available in your path (such as via your <code>$PERL5LIB</code> environment variable), you will be able to run the following:</p> <pre class="prettyprint">$ dancer2 activate --directory foo/</pre> <p>(The implementation of what "activation" means in this context is left to the reader.)</p> <p>But what if you want to provide an alteration of an existing command - the generation of the Dancer2 application?</p> <h3><a name="writing_a_new_command"></a>Writing a new command</h3> <p>Let's say you have a set of adjustments you keep doing to your [company's] Dancer2 applications and you want to make these a default.</p> <p>You can write it as a new command or you can subclass the existing command and do whatever alterations you want before, during, and after the generation of the skeleton.</p> <pre class="prettyprint">package Dancer2::CLI::Command::mygen; use strict; use warnings; use Cwd (); # Our own dependencies # Subclass the existing "gen" command use parent 'Dancer2::CLI::Command::gen'; sub execute { my ( $self, $opt, $args ) = @_; # Do whatever you want in this area, before we generate # For example, let's make sure the application # matches a certain naming convention my $app_name = $opt-&gt;{'application'}; $app_name =~ /^My::Company::App::/ or $self-&gt;usage_error('App must be prefixed by "My::Company::App"); # Maybe check we are only scaffolding in a particular directory cwd() eq '/opt/my_company/webapps/' or $self-&gt;usage_error('Only create apps in our webapps directory'); # At this point, we can run the original scaffolding $self-&gt;SUPER::execute( $opt, $args ); # Now we finished generating, but we can contineu customizing what we have } 1;</pre> <p>Writing your own generation on top of the existing generation allows you to manage the input (including additional validation) and the output, giving you full control over the scaffolding process.</p> <p>Some examples on which customizations you might want to perform:</p> <ul> <li><a name="item_Add_additional_default_imported_classes"></a><b>Add additional default imported classes</b> </li> <li><a name="item_Change_the_output_directory_name"></a><b>Change the output directory name</b> </li> <li><a name="item_Update_a_database_that_we_have_a_new_application"></a><b>Update a database that we have a new application</b> </li> <li><a name="item_Update_your_team_with_an_email_or_IRC_Slack_message"></a><b>Update your team with an email or IRC/Slack message</b> </li> <li><a name="item_Remove_files_that_are_not_applicable_for_your_setup_and_add_new_ones"></a><b>Remove files that are not applicable for your setup and add new ones</b> </li> <li><a name="item_Write_helpful_output_for_the_developer_who_scaffolded_the_app"></a><b>Write helpful output for the developer who scaffolded the app</b> </li> </ul> <h2><a name="conclusion"></a>Conclusion</h2> <p>I hope you find these techniques useful to introduce customization for your home-grown Dancer2 application setup. I know I do. :)</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Sawyer X for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>Copyright</h2> <p>No copyright retained. Enjoy.</p> <p>2018 // Sawyer X <code>&lt;xsawyerx@cpan.org&gt;</code></p> </div> Dancer and Email http://advent.perldancer.org/2018/17 perl http://advent.perldancer.org/2018/17 Mon, 17 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer_and_email"></a>Dancer and Email</h1> <p>Web applications regularly need to send email to its users, e.g. receipts or password reset links. The <a href="https://metacpan.org/pod/Dancer2::Plugin::Email">Email</a> plugin for Dancer2 simplifies this task by providing the <code>email</code> keyword and a sane default configuration.</p> <p>So the unavoidable "Hello world" example would look like:</p> <pre class="prettyprint">email { from =&gt; 'foo@perl.dance', to =&gt; 'bar@perl.dance', subject =&gt; 'Hello world', text =&gt; 'Welcome to the dancefloor!', };</pre> <p>The more common case would be to use a template from your web application and turn it into a HTML email.</p> <p>Instead of using the <code>template</code> keyword to return the HTML from your route to the browser, you generate HTML with a specific layout, store in a variable and send the email.</p> <pre class="prettyprint">post '/welcome' =&gt; { my $html = template $template, $tokens, { layout =&gt; 'email' }; email { from =&gt; 'foo@perl.dance', to =&gt; 'bar@perl.dance', subject =&gt; 'Welcome to the dancefloor!', type =&gt; 'html', body =&gt; $html, } redirect '/home'; }</pre> <h2><a name="utf_8"></a>UTF-8</h2> <p>Take care to encode your content and mail headers if it may contain UTF-8 characters. It might even look good in your email client if you don't do this, but not necessarily for other ones.</p> <pre class="prettyprint">email { from =&gt; 'foo@perl.dance', to =&gt; 'bar@perl.dance', subject =&gt; encode('MIME-Header', '&#xdc;ber uns'), type =&gt; 'html', body =&gt; encode('UTF-8', 'Die Geschichte unseres Projekts begann mit dem franz&#xf6;sischen Entwicker Alexis Sukrieh ...'), };</pre> <h2><a name="inline_images"></a>Inline images</h2> <p>You can simply deliver emails with links to images, but usually email clients would not load them without user interaction. It is possible though to attached the images to the email and reference them in the email body with a custom HTML tag:</p> <pre class="prettyprint"> email { from =&gt; 'foo@perl.dance', to =&gt; 'bar@perl.dance', subject =&gt; 'Welcome to the dancefloor!', body =&gt; q{&lt;p&gt;Image embedded: &lt;img src="cid:mycid"/&gt;&lt;/p&gt;}, type =&gt; 'html', attach =&gt; [ { Id =&gt; 'mycid', Path =&gt; '/dancefloor/dcr-header-logo.png' }], multipart =&gt; 'related' };</pre> <h2><a name="providing_plain_text_part"></a>Providing plain text part</h2> <p><a href="https://metacpan.org/pod/HTML::FormatText::WithLinks">HTML::FormatText::WithLinks</a> makes it easy to provide a plain text version of your HTML email:</p> <pre class="prettyprint">my $html = template $template, $tokens, { layout =&gt; 'email' }; my $f = HTML::FormatText::WithLinks-&gt;new; my $text = $f-&gt;parse($html); email { from =&gt; 'foo@perl.dance', to =&gt; 'bar@perl.dance', subject =&gt; 'Welcome to the dancefloor!', body =&gt; $text, attach =&gt; { Data =&gt; $html, Type =&gt; "text/html" }, multipart =&gt; 'alternative', };</pre> <h2><a name="configuration"></a>Configuration</h2> <h3><a name="transports"></a>Transports</h3> <p>Under the hood the plugin uses <a href="https://metacpan.org/pod/Email::Sender">Email::Sender</a>, so you can utilize its transports instead of the "sendmail" one (local mail server).</p> <p>This is just a matter of adjusting your configuration:</p> <pre class="prettyprint">plugins: Email: transport: SMTP: ssl: 1 host: 'mail.perl.dance' port: 465 sasl_username: 'foo@perl.dance' sasl_password: 'nevairbe'</pre> <p>In development you want to <b>prevent</b> email going out to <b>real users</b>.</p> <p>This can be done with the <a href="https://metacpan.org/pod/Email::Sender::Transport::Redirect">Redirect</a> transport:</p> <pre class="prettyprint">plugins: Email: transport: Sendmail: redirect_address: racke@perl.dance</pre> <p>All email will be send to the specificied address, but with extra headers added with the original recipients:</p> <pre class="prettyprint">X-Intercepted-To: "Bar" &lt;bar@perl.dance&gt; X-Intercepted-Cc: "Baz" &lt;baz@perl.dance&gt;</pre> <p>Note: it's the presence of the <code>redirect_address</code> parameter which tells the plugin you want mails redirected to that address, this will work with whatever transport you wish to use.</p> <h3><a name="preseed_headers"></a>Preseed headers</h3> <p>If you have a standard email address and/or you want to have extra email headers, you can specify these in the configuration as well:</p> <pre class="prettyprint">plugins: Email: sender: "2019@perl.dance" from: "Perl Dancer Conference 2019 &lt;2019@perl.dance&gt;" X-Mailer: "PerlDance2018 4.0"</pre> <h2><a name="author"></a>Author</h2> <p>This article has been written by racke for the Perl Dancer Advent Calendar 2018.</p> </div> Using Minion in Dancer Apps http://advent.perldancer.org/2018/16 perl http://advent.perldancer.org/2018/16 Sun, 16 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="using_minion_in_dancer_apps"></a>Using Minion in Dancer Apps</h1> <p>At <code>$work</code>, we have built an API with Dancer that generates PDF documents and XML files. This API is a critical component of an insurance enrollment system: PDFs are generated to deliver to the client in a web browser immediately, and the XML is delivered to the carrier as soon as it becomes available. Since the XML often takes a significant amount of time to generate, the job is generated in the background so as not to tie up the application server for an extended amount of time. When this was done, a homegrown process management system was developed, and works by <code>fork()</code>ing a process, tracking its pid, and hoping we can later successfully reap the completed process.</p> <p>There have been several problems with this approach:</p> <ul> <li><a name="item_it_s_fragile"></a><b>it's fragile</b> </li> <li><a name="item_it_doesn_t_scale"></a><b>it doesn't scale</b> </li> <li><a name="item_it_s_too_easy_to_screw_something_up_as_a_developer"></a><b>it's too easy to screw something up as a developer</b> </li> </ul> <p>In 2019, we have to ramp up to take on a significantly larger workload. The current solution simply will not handle the amount of work we anticipate needing to handle. Enter <a href="https://metacpan.org/pod/Minion">Minion</a>.</p> <p><b>Note:</b> The techniques used in this article work equally well with Dancer or Dancer2.</p> <h2><a name="why_minion"></a>Why Minion?</h2> <p>We looked at several alternatives to Minion, including <a href="https://beanstalkd.github.io/">beanstalkd</a> and <a href="http://www.celeryproject.org/">celeryd</a>. Using either one of these meant involving our already over-taxed infrastructure team, however; using Minion allowed us to use expertise that my team already has without having to burden someone else with assisting us. From a development standpoint, using a product that was developed in Perl gave us the quickest time to implementation.</p> <p>Scaling our existing setup was near impossible. It's not only difficult to get a handle on what resources are consumed by processes we've forked, but it was impossible to run the jobs on more than one server. Starting over with Minion also gave us a much needed opportunity to clean up some code in sore need of refactoring. With a minimal amount of work, we were able to clean up our XML rendering code and make it work from Minion. This cleanup allowed us to more easily get information as to how much memory and CPU was consumed by an XML rendering job. This information is vital for us in planning future capacity.</p> <h2><a name="accessing_minion"></a>Accessing Minion</h2> <p>Since we are a Dancer shop, and not Mojolicious, a lot of things you'd get from Mojolicious for working with Minion isn't as available to us. Given we are also sharing some Minion-based code with our business models, we had to build some of our own plumbing around Minion:</p> <pre class="prettyprint">package MyJob::JobQueue; use Moose; use Minion; use MyJob::Models::FooBar; with 'MyJob::Roles::ConfigReader'; has 'runner' =&gt; ( is =&gt; 'ro', isa =&gt; 'Minion', lazy =&gt; 1, default =&gt; sub( $self ) { $ENV{ MOJO_PUBSUB_EXPERIMENTAL } = 1; Minion-&gt;new( mysql =&gt; MyJob::DBConnectionManager-&gt;new-&gt;get_connection_uri({ db_type =&gt; 'feeds', io_type =&gt; 'rw', })); }, );</pre> <p>We wrapped a simple Moose class around Minion to make it easy to add to any class or Dancer application with the extra functionality we wanted.</p> <p>We ran into an issue at one point where jobs weren't running since we added them to a queue that no worker was configured to handle. To prevent this from happening to us again, we added code to prevent us from adding code to a queue that didn't exist:</p> <pre class="prettyprint">my @QUEUE_TYPES = qw( default InstantXML PayrollXML ChangeRequest ); sub has_invalid_queues( $self, @queues ) { return 1 if $self-&gt;get_invalid_queues( @queues ); return 0; } sub get_invalid_queues( $self, @queues ) { my %queue_map; @queue_map{ @QUEUE_TYPES } = (); my @invalid_queues = grep !exists $queue_map{ $_ }, @queues; return @invalid_queues; }</pre> <p>With that in place, it was easy for our <code>queue_job()</code> method to throw an error if the developer tried to add a job to an invalid queue:</p> <pre class="prettyprint">sub queue_job( $self, $args ) { my $job_name = $args-&gt;{ name } or die "queue_job(): must define job name!"; my $guid = $args-&gt;{ guid } or die "queue_job(): must have GUID to process!"; my $title = $args-&gt;{ title } // $job_name; my $queue = $args-&gt;{ queue } // 'default'; my $job_args = $args-&gt;{ job_args }; die "queue_job(): Invalid job queue '$queue' specified" if $self-&gt;has_invalid_queues( $queue ); my %notes = ( title =&gt; $title, guid =&gt; $guid ); return $self-&gt;runner-&gt;enqueue( $job_name =&gt; $job_args =&gt; { notes =&gt; \%notes, queue =&gt; $queue }); }</pre> <h2><a name="creating_jobs"></a>Creating Jobs</h2> <p>In our base model class (Moose-based), we would create an attribute for our job runner:</p> <pre class="prettyprint">has 'job_runner' =&gt; ( is =&gt; 'ro', isa =&gt; 'MyJob::JobQueue', lazy =&gt; 1, default =&gt; sub( $self ) { return MyJob::JobQueue-&gt;new-&gt;runner; }, );</pre> <p>And in the models themselves, creating a new queueable task was as easy as:</p> <pre class="prettyprint">$self-&gt;runner-&gt;add_task( InstantXML =&gt; sub( $job, $request_path, $guid, $company_db, $force, $die_on_error = 0 ) { $job-&gt;note( request_path =&gt; $request_path, feed_id =&gt; 2098, group =&gt; $company_db, ); MyJob::Models::FooBar-&gt;new( request_path =&gt; $request_path )-&gt;generate_xml({ pdf_guid =&gt; $guid, group =&gt; $company_db, force =&gt; $force, die_on_error =&gt; $die_on_error, }); });</pre> <h2><a name="running_jobs"></a>Running Jobs</h2> <p>Starting a job from Dancer was super easy:</p> <pre class="prettyprint">use Dancer2; use MyJob::JobQueue; sub job_queue { return MyJob::JobQueue-&gt;new; } get '/my/api/route/:guid/:group/:force' =&gt; sub { my $guid = route_parameters-&gt;get( 'guid' ); my $group = route_parameters-&gt;get( 'group' ); my $force = route_parameters-&gt;get( 'force' ); debug "GENERATING XML ONLY FOR $guid"; job_queue-&gt;queue_job({ name =&gt; "InstantXML", guid =&gt; $guid, title =&gt; "Instant XML Generator", queue =&gt; 'InstantXML', job_args =&gt; [ $self-&gt;request_path, $guid, $group, $force ], }); }</pre> <h2><a name="creating_and_configuring_the_job_queue_worker"></a>Creating and Configuring the Job Queue Worker</h2> <p>We wanted to easily configure our Minions for all hosts and environments in one spot. Since we use a lot of YAML in Dancer, specifying the Minion configuration in YAML made a lot of sense to us:</p> <pre class="prettyprint"># What port does the dashboard listen on? dashboard_port: 4000 # Add the rest later. dashboards: UNKNOWN: http://localhost:3000/ DEV: http://my.development.host.tld:8001/ # Hosts that have no entry assume the default configuration default: max_children: 4 queues: - default # Host-specific settings jcrome-precision-3510: max_children: 8 queues: - default - InstantXML - PayrollXML - ChangeRequest</pre> <p>Our job queue workers look like:</p> <pre class="prettyprint">#!/usr/bin/env perl use MyJob::Base; use MyJob::JobQueue; use MyJob::Log4p; use MyJob::Util::Logger; use MyJob::Util::SysTools qw(get_hostname); my $config = MyJob::Config-&gt;new-&gt;config; my $hostconfig = get_hostconfig(); my $minion = MyJob::JobQueue-&gt;new; my $worker = $minion-&gt;runner-&gt;worker; my $log_eng = MyJob::Log4p-&gt;new({ logger_name =&gt; "Minion" }); my $logger = MyJob::Util::Logger-&gt;new-&gt;logger($log_eng);</pre> <p>The above is mostly typical boilerplate for us. Read our configuration, and create a logger the worker can use.</p> <p>Next, when a job is dequeued, we want to log that the worker picked up a job (needed for auditing purposes) and we alter the process name so if a process hangs, we know what that process was attempting to run. If an unchecked exception occurs in a job, the worker will catch it and log it for us:</p> <pre class="prettyprint">$worker-&gt;on( dequeue =&gt; sub( $worker, $job ) { my $id = $job-&gt;id; my $notes = $job-&gt;info-&gt;{ notes }; my $title = $notes-&gt;{ title }; my $guid = $notes-&gt;{ guid }; $job-&gt;on( spawn =&gt; sub( $job, $pid ) { $0 = "$title $guid"; $logger-&gt;info( "$title: Created child process $pid for job $id by parent $$ - $guid"); }); $job-&gt;on( failed =&gt; sub( $job, $error ) { chomp $error; $logger-&gt;error( $error ); }); });</pre> <p>To help us for future capacity planning, we want our workers to tell us if they are running at peak capacity, so log when this event occurs:</p> <pre class="prettyprint">$worker-&gt;on( busy =&gt; sub( $worker ) { my $max = $worker-&gt;status-&gt;{ jobs }; $logger-&gt;log( "$0: Running at capacity (performing $max jobs)." ); });</pre> <p>Now, we apply the configuration (read below) to the worker. When the worker starts, it tells us information about how it was configured (this was really useful during development):</p> <pre class="prettyprint">my $max_jobs = $hostconfig-&gt;{ max_children }; my @queues = @{ $hostconfig-&gt;{ queues }}; if( $minion-&gt;has_invalid_queues( @queues ) ){ print "Invalid job queues specified: " . join( ',', $minion-&gt;get_invalid_queues( @queues ) ); say ". Aborting!"; exit 1; } say "Starting Job Queue Worker on " . get_hostname(); say "- Configured to run a max of $max_jobs jobs"; say "- Listening for jobs on queues: ", join(', ', @queues ); $worker-&gt;status-&gt;{ jobs } = $max_jobs; $worker-&gt;status-&gt;{ queues } = \@queues; $worker-&gt;run;</pre> <p>Remember the YAML file we used to configure things up above? This last bit pulls the information for the host this worker is running on (<code>get_hostname()</code> is a home-grown hostname function):</p> <pre class="prettyprint">sub get_hostconfig { my $minion_config = MyJob::Config-&gt;new({ filename =&gt; "environments/minions.yml" })-&gt;config; my $hostname = get_hostname(); if( exists $minion_config-&gt;{ $hostname }) { return $minion_config-&gt;{ $hostname }; } else { return $minion_config-&gt;{ default }; } }</pre> <h2><a name="monitoring_the_workers"></a>Monitoring the Workers</h2> <p>Our Minion dashboard was virtually identical to the one that @preaction posted in <a href="https://mojolicious.io/blog/2018/12/11/who-watches-the-minions/#section-2">Who Watches the Minions?</a>. If you'd like to know more, I highly recommend reading his article.</p> <h2><a name="outcome"></a>Outcome</h2> <p>Within about a two-week timespan, we went from having zero practical knowledge of Minion to having things up and running. We've made some refinements and improvements along the way, but the quick turnaround is a true testament to the simplicity of working with Minion.</p> <p>We now have all the necessary pieces in place to scale our XML rendering both horizontally and vertically: thanks to Minion, we can easily run XML jobs across multiple boxes, and can more efficiently run more jobs concurrently on the same hardware as before. This setup allows us to grow as quickly as our customer base does.</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Jason Crome (CromeDome) for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>Copyright</h2> <p>No copyright retained. Enjoy.</p> <p>Jason A. Crome</p> </div> Dancer2::Plugin::Paginator - Born Again http://advent.perldancer.org/2018/15 perl http://advent.perldancer.org/2018/15 Sat, 15 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer2__plugin__paginator___born_again"></a>Dancer2::Plugin::Paginator - Born Again</h1> <h2><a name="history"></a>HISTORY</h2> <p>I would call it re-birth. It all started during end of July 2017, I was going through the documentation of <a href="https://metacpan.org/pod/distribution/Dancer2/lib/Dancer2/Plugins.pod">Dancer2::Plugins</a> and stumbled upon <a href="https://metacpan.org/module/Dancer2::Plugin::Paginator">Dancer2::Plugin::Paginator</a>. To my surprise, I found the link broken. My initial thought was, if there was any typo in the name. All my investigation led to the dark corner of <a href="http://backpan.cpantesters.org/">BackPAN</a>. At this point, I decided to bring it live.</p> <h2><a name="rebirth"></a>REBIRTH</h2> <p>Following the <a href="http://neilb.org/2014/01/31/adoption-request.html">advise</a> from Neil Bowers, I contacted the author <b>Blabos de Blebe</b> by email, if he was happy for me take it forward. He not only encouraged me but also gave me permission to make it usable.</p> <h2><a name="challenge"></a>CHALLENGE</h2> <p>After getting the push from the author, my first challenge was to make it compatible with <a href="https://metacpan.org/pod/distribution/Dancer2/lib/Dancer2/Plugins.pod">Dancer2::Plugins</a>. Having done this with other plugins e.g. <a href="https://metacpan.org/release/Dancer2-Plugin-Captcha">Dancer2::Plugin::Captcha</a>, <a href="https://metacpan.org/release/Dancer2-Plugin-Res">Dancer2::Plugin::Res</a> and <a href="https://metacpan.org/release/Dancer2-Plugin-Chain">Dancer2::Plugin::Chain</a> earlier helped with this task. I should give credit to Sawyer X for this <a href="http://advent.perldancer.org/2014/14">gift</a>, part of <a href="http://advent.perldancer.org/2014">Perl Dancer Advent Calendar 2014</a>.</p> <h2><a name="example"></a>EXAMPLE</h2> <p>Let me show you an example as described in the official document for <a href="https://metacpan.org/release/Dancer2-Plugin-Paginator">Dancer2::Plugin::Paginator</a>.</p> <pre class="prettyprint">use Dancer2; use Dancer2::Plugin::Paginator; get '/list' =&gt; sub { my $paginator = paginator( 'curr' =&gt; $page, 'items' =&gt; rset('Post')-&gt;count, 'base_url' =&gt; '/posts/page/', ); template 'list', { paginator =&gt; $paginator }; }; true;</pre> <h2><a name="configuration"></a>CONFIGURATION</h2> <p>To bring the above example live, we need to tune the configuration slightly as below.</p> <pre class="prettyprint">plugins: Paginator: frame_size: 3 page_size: 7</pre> <h2><a name="real_hands_on"></a>REAL HANDS-ON</h2> <p>Having seen the details, now let us try real example. I have got GitHub repository that I use to try new things related to Dancer2 i.e. <a href="https://github.com/manwar/Dancer2-Cookbook">Dancer2 Cookbook</a>. You should find a link "Books" showing the pagination magic.</p> <h3><a name="example_configuration"></a>Example Configuration</h3> <pre class="prettyprint">plugins: Paginator: frame_size: 1 page_size: 3</pre> <h3><a name="action"></a>Action</h3> <pre class="prettyprint">get '/books/:page' =&gt; sub { my $books = _books(); my $curr = params-&gt;{page} || 1; template 'books' =&gt; _paginator($books, $curr); }; sub _paginator { my ($books, $curr) = @_; my $items = scalar(@$books); my $paginator = paginator( curr =&gt; $curr, items =&gt; $items, base_url =&gt; '/books', mode =&gt; 'path' ); my $page_size = $paginator-&gt;page_size; my $i = ($curr - 1) * $page_size; my $j = ($i + $page_size) - 1; my $results = [ @$books[$i..$j] ]; return { results =&gt; $results, paginator =&gt; $paginator, prev_l =&gt; $paginator-&gt;prev, next_l =&gt; $paginator-&gt;next, }; }</pre> <h3><a name="template"></a>Template</h3> <pre class="prettyprint">&lt;% IF results.size %&gt; &lt;ul&gt; &lt;% FOREACH book IN results %&gt; &lt;% IF book.name %&gt; &lt;li&gt;&lt;b&gt;&lt;% book.name %&gt;&lt;/b&gt; &amp;nbsp; &lt;a href="/edit/book/&lt;% book.id %&gt;"&gt;Edit&lt;/a&gt;&lt;/li&gt; &lt;% END %&gt; &lt;% END %&gt; &lt;/ul&gt; &lt;br/&gt; &lt;hr/&gt; &lt;a href="/books/&lt;% prev_l %&gt;"&gt;[prev]&lt;/a&gt;...&lt;a href="/books/&lt;% next_l %&gt;"&gt;[next]&lt;/a&gt; &lt;hr/&gt; &lt;% ELSE %&gt; &lt;ul&gt;&lt;li&gt;&lt;b&gt;No book found.&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt; &lt;% END %&gt;</pre> <h2><a name="conclusion"></a>CONCLUSION</h2> <p>This is all you need to get started. If you feel lucky, you can take a look at <a href="https://metacpan.org/pod/Paginator::Lite">Paginator::Lite</a> for more insights. Any questions/suggestions, feel free to get in touch mohammad.anwar@yahoo.com.</p> <h2><a name="author"></a>AUTHOR</h2> <p>This article has been written by Mohammad S Anwar for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>COPYRIGHT</h2> <p>No copyright retained. Enjoy.</p> <p>Mohammad S Anwar</p> </div> Dancer Survey Result Recap http://advent.perldancer.org/2018/14 perl http://advent.perldancer.org/2018/14 Fri, 14 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="dancer_survey_result_recap"></a>Dancer Survey Result Recap</h1> <p>Well, it's been way longer than it should have been, but I wanted to provide some information about the results of the 2017 Dancer Survey.</p> <h2><a name="statistics"></a>Statistics</h2> <p>Let's start off with some overall statistics:</p> <ul> <li><a name="item_There_were_112_responses_to_the_survey"></a><b>There were 112 responses to the survey</b> </li> <li><a name="item_86_respondents__76_8___are_currently_using_Dancer_1_"></a><b>86 respondents (76.8%) are currently using Dancer(1)</b> </li> <li><a name="item_60_5__of_those_who_responded_are_using_Dancer2"></a><b>60.5% of those who responded are using Dancer2</b> </li> <li><a name="item_89_5__of_respondents_would_recommend_Dancer_to_another_Perl_developer__and_46_5__would_recommend_it_to_a_non_Perl_developer"></a><b>89.5% of respondents would recommend Dancer to another Perl developer, and 46.5% would recommend it to a non-Perl developer</b> </li> <li><a name="item_41_2__of_Dancer_1__users_are_planning_to_migrate_Dancer2"></a><b>41.2% of Dancer(1) users are planning to migrate Dancer2</b> </li> <li><a name="item_48_2__of_you_wanted_web_sockets___and_now_you_got_them___"></a><b>48.2% of you wanted web sockets - and now you got them :)</b> </li> <li><a name="item_51_8__of_respondents_are_happy_with_the_community_"></a><b>51.8% of respondents are happy with the community.</b> </li> </ul> <p>Oddly enough, 37.2% of you love the docs, and 37.2% of you hate them.</p> <p>This does give us some good insight into the direction users of Dancer are going. There are a number of Dancer users out there, with a growing shift towards Dancer2, and by and large, Dancer developers seem pretty happy. But we aren't content with merely happy, so let's see what people like and don't like, so we can see what can be improved.</p> <h2><a name="common_reasons_for_recommending"></a>Common reasons for recommending</h2> <p>There was a common theme among those who responded as to why they liked Dancer:</p> <ul> <li><a name="item_Simplicity_ease_of_use"></a><b>Simplicity/ease of use</b> </li> <li><a name="item_Non_opinionated"></a><b>Non-opinionated</b> </li> <li><a name="item_Lightweight"></a><b>Lightweight</b> </li> <li><a name="item_Quality_of_documentation"></a><b>Quality of documentation</b> </li> <li><a name="item_Stability"></a><b>Stability</b> </li> </ul> <h2><a name="common_reasons_for_not_recommending"></a>Common reasons for not recommending</h2> <ul> <li><a name="item_Documentation"></a><b>Documentation</b> </li> <li><a name="item_Plugin_selection"></a><b>Plugin selection</b> <p>While we've seen the addition of some new plugins recently (for websockets, Log4perl, and CHI, among others), we would love to see more.</p> </li> <li><a name="item_Negative_public_perception_of_Perl"></a><b>Negative public perception of Perl</b> <p>This is a hard problem to solve, and not one that is going to be solved by us alone.</p> </li> </ul> <h2><a name="areas_of_improvement_things_to_improve"></a>Areas of improvement/things to improve</h2> <p>There were some things that our community were anxious to see improved. I've listed items that came up multiple times:</p> <ul> <li><a name="item_Deployment_docs__specifically__lack_of_IIS_"></a><b>Deployment docs (specifically, lack of IIS)</b> </li> <li><a name="item_Presentation_of_docs_on_Dancer_website"></a><b>Presentation of docs on Dancer website</b> </li> <li><a name="item_How_to_construct_larger_apps"></a><b>How to construct larger apps</b> </li> <li><a name="item_Improve_async_documentation"></a><b>Improve async documentation</b> </li> <li><a name="item_Configuration"></a><b>Configuration</b> </li> <li><a name="item_Be_more_community_active"></a><b>Be more community active</b> <p>This last item refers to the core team being more active on Stack Overflow, Perl Monks, and the like. To the best of our abilities, we will try to do so!</p> </li> </ul> <p>This comment in particular stood out to me:</p> <pre class="prettyprint">"More tutorials. Especially about deployment. I believe the greatest hurdle for new developers (as in new new, who start doing web stuff in Perl, or programming in general, with Dancer) is not to get started, but to get done. There is a lot of good content in all the major frameworks in the Perl ecosystem on how to build an application, but all of them lack in-depth tutorials with different alternatives for how to deploy them. This includes telling inexperienced users what hosting a web application means, what the different deployment types with PSGI do and which one to pick. I think having a really good guide would set Dancer apart from other frameworks."</pre> <p>This is certainly one area we have set out to improve in the documentation.</p> <h2><a name="cool_uses_of_dancer"></a>Cool uses of Dancer</h2> <p>Our users shared with us some of the cool things they have done with Dancer. I'm considering doing some writeups on the Dancer website about some of them:</p> <ul> <li><a name="item_Service_monitoring_dashboard"></a><b>Service monitoring dashboard</b> </li> <li><a name="item_VOIP_provisioning_app"></a><b>VOIP provisioning app</b> </li> <li><a name="item_Benefits_management_software"></a><b>Benefits management software</b> </li> <li><a name="item_thegamecrafter_com__and_their_family_of_sites_applications_"></a><b>thegamecrafter.com (and their family of sites/applications)</b> </li> <li><a name="item_Unattended_installation_portal"></a><b>Unattended installation portal</b> </li> </ul> <h2><a name="wrapping_it_all_up"></a>Wrapping it all up</h2> <p>We received a lot of constructive feedback from this process, and while we might not do a survey every year, from time to time, this will be a good barometer of where things are and where we need to go.</p> <p>Some of the feedback we received has already been acted upon (websockets, doc improvements), and you can look forward to us addressing your other feedback as we are able. Right now, we are working on a better manual that has more information about the things you'd most like to see. Stay tuned!</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Jason Crome (CromeDome) for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>Copyright</h2> <p>No copyright retained. Enjoy.</p> <p>Jason A. Crome</p> </div> The Twelve Days of Dancer http://advent.perldancer.org/2018/13 perl http://advent.perldancer.org/2018/13 Thu, 13 Dec 2018 00:00:00 -0000 <div class="pod-document"><h1><a name="the_twelve_days_of_dancer"></a>The Twelve Days of Dancer</h1> <p>Hello and goodbye, 2018! It seems like we hardly knew you. It's been a few years since our last calendar, and while everyone here is busy with other projects, we still wanted to give you a mini-advent-calendar this year. Welcome to the Twelve Days of Dancer!</p> <p>This year's calendar features twelve posts that cover a wide range of topics. We feature several new authors, cover some new ground (for us!) with an article on accessibility, and even have a crossover post showing how Dancer can be used with other frameworks.</p> <p>Without further ado, let's dance!</p> <h2><a name="state_of_the_dancer"></a>State of the Dancer</h2> <p>In 2017 and 2018, we saw fewer but more significant updates to Dancer and Dancer2. With Dancer (1) being in maintenance mode, updates come only when significant bugs are found, security vulnerabilities are found, or when a change is proposed that greatly improve the lives of Dancer developers. David Precious has been the light that guides Dancer(1) through the night, and has been an excellent resource for both the Dancer and Dancer2 communities on IRC and email.</p> <p>Meanwhile, Dancer2 continues to grow and evolve, though at a less frantic rate than earlier in its lifetime. Throughout the last two years, we've seen a growing list of contributions from our community, through documentation improvements, bug fixes, and new features.</p> <p>At the end of 2017, the Dancer Core Team ran a survey of Dancer developers to get community input on the Dancer project. We received an excellent response to the survey, and it is being used to help guide the future direction of Dancer and its community. The results of this survey will be discussed in tomorrow's article.</p> <h2><a name="new_features"></a>New Features</h2> <p>Several significant new features came to pass since the last Advent Calendar:</p> <h3><a name="websockets"></a>Websockets</h3> <p>You asked, and Yanick delivered! <a href="https://metacpan.org/pod/Dancer2::Plugin::WebSocket">Dancer2::Plugin::Websocket</a> enables websockets from within your Dancer2 applications. To make this work properly, a non-blocking, streaming Plack server is needed (such as Twiggy).</p> <h3><a name="mutable_serializer"></a>Mutable Serializer</h3> <p>Dancer2 can now properly deserialize and serialize content based upon the request headers. Even better, it's completely and easily configurable! Thanks to veryrusty for spearheading the effort to get this working!</p> <h3><a name="no_default_middleware"></a>No Default Middleware</h3> <p>To support ETags and other similar features, the default Plack middleware used by Dancer2 can get in the way. To get around this, veryrusty added a <code>no_default_middleware</code> configuration option to Dancer2's <i>config.yml</i> file to keep your application from using the default bundling of Plack middleware.</p> <h2><a name="better_documentation"></a>Better Documentation</h2> <p>Several dozen bug fixes, clarifications, and other enhancements have been made to the documentation, primarily by the Dancer2 community. As a result, the quality of existing docs has been greatly improved.</p> <p>Several new examples have been added to both the Dancer2 distribution as well as the tutorial.</p> <p>Sawyer has started a significant project to rebuild the Dancer2 documentation from scratch. The current docs lack a good description of why Dancer does things a certain way, and doesn't give new users a good grounding in web application design to know how and why some things should be done the way we've recommended. The new doc project aims to address this by detailing important concepts in a clear and simple way.</p> <h2><a name="performance_improvements"></a>Performance Improvements</h2> <p>Several important performance improvements were made to Dancer2, the most important of which being the migration from <code>MooX::Types</code> to <code>Type::Tiny</code>. When <code>Type::Tiny::XS</code> is used, the boost is even more significant.</p> <h2><a name="security_improvements"></a>Security Improvements</h2> <p>Two important security features were added:</p> <p>The session engine now requires a <code>validate_id()</code> method to be implemented in the various session engines. This requirement shuts down an attack vector by making session IDs conform to a known format.</p> <p>SysPete implemented a <code>change_session_id</code> keyword to easily change the current session ID. This is a common (and recommended) security practice, especially when privilege level changes within an application.</p> <h2><a name="author"></a>Author</h2> <p>This article has been written by Jason Crome (CromeDome) for the Perl Dancer Advent Calendar 2018.</p> <h2><a name="copyright"></a>Copyright</h2> <p>No copyright retained. Enjoy.</p> <p>Jason A. Crome</p> </div>