send_as BEER => 'Christmas Cheer'

One difference between Dancer and Dancer2 is that serializers are "Always or never". Sawyer X's 2014 advent article explains why this change was important.

However one question that arose several times during 2015 from keen Dancer2 users was

"How do you return HTML from a specific route when we have a
serialization engine configured?"

Back then, the best advice was to "split" your app into two distinct applications and use Plack::Builder to glue them back together.

# bin/app.psgi
use Plack::Builder;
use MyApp;
use MyApp::API;

builder {
    mount '/api' => MyApp::API->to_app;
    mount '/'    => MyApp->to_app;
};

However, if you have a small number of routes that required a template engine (or a small number that needed a serialization engine), the effort involved was onerous.

Other suboptimal solutions (including the use of eldritch horrors) were proposed in issues on github prior to a suggestion to use a combination of Dancer2's send_file and to_json using an in-memory file handle:

set serializer => 'JSON';
set template   => 'template_toolkit';

get '/html/route' => sub {
    my $html = encode( 'UTF-8', template('awesome_template') );
    content_type => ''text/html; charset=UTF-8';
    send_file \$html;     # sends html
};

Which just works; but involves excessive boilerplate and one needs to take care with text encodings. That's not the Dancer way!

A prototype solution: Dancer2::Plugin::SendAs

Conferences have many benefits! There is the awesome talks, the hallway track, the benefits of getting a group together and discussing a problem face-to-face, or just having some time away to think about a particular problem.

During some conference related downtime in 2015, Dancer2::Plugin::SendAs was spec'd and implemented to encapsulate the previous patten into a simple to use c<send_as> keyword:

use Dancer2::Plugin::SendAs;

get '/api/thing' => sub {
    send_as JSON => { thing => { to => [ 'return' ] } };
};

Any Dancer2 serializer was supported, as well as HTML output:

get '/api' => sub {
    send_as HTML => template( 'instructions' );
};

Simple. Easy. Neat. Now that's the Dancer way!

From Plugin to Core

The SendAs plugin solved a problem many Dancers' encountered. The core team decided to move the plugins' functionality into core in early 2016. (Did you know the to_json and from_json keywords started as a plugin too?)

After cleaning up some edge cases and tweaking features, c<send_as> became a core keyword in the c<0.200000> release.

The key differences in the core implemantation are

  • Serializers must use the appropriate casing of their names. eg. JSON or Sereal. HTML is special cased for returning HTML content, and must be in upper case.
  • Serializers will load settings from the applications configuration.

Like send_file, the send_as keyword returns immediately from a route, allowing send_as to short-circuit route logic, even when a serializer is defined:

set serializer => 'JSON';
set template   => 'template_toolkit';

post '/api/:id' => sub {
    my $id = request->route_parameter->get('id');
    if ( $id !~ m/^[0-9]+$/ ) {
        # send HTML error page
        status 500;
        send_as HTML => template('error');
    }
    # Continue with the rest of route logic if id was an integer
    # Returns JSON content
    ...
};

Dancer2::Plugin::SendAs remains on the CPAN (deprecated) for posterity.

Author

This article has been written by Russell (veryrusty) Jenkins for the Perl Dancer Advent Calendar 2016.

Copyright

No copyright retained. Enjoy.

2016 // Russell (veryrusty) Jenkins