Dancer 2, what about using it? Now!

A look back

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 Dancer::Core::Route class.

If you want to refresh your memories about why I decided to embark upon a rewrite, you can jump to this article of the previous advent calendar.

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.

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 - "You'll see, 2011 will be the Dancer year". 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?

If I should name it, I'd say that 2012 was "the Dancer 2 development year" 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.

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.

What is Dancer 2 at the time of this writing?

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).

Dancer 2 is a GitHub project, you can browse the code on its page: https://github.com/PerlDancer/Dancer2/ and you can grab the source like so:

git clone https://github.com/PerlDancer/Dancer2.git

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.

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.

Design decisions for D2

Pure OO code, based on Moo

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.

First, Dancer 2 is written in pure object-oriented code, with Moo as its object system.

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?

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.

I could almost say that Dancer 1 is written in Perl5 and Dancer 2 is in Moo.

No more shared singletons

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.

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 Law of Demeter.

This will for sure be a lot better for future evolution and code maintenance.

Strict app scoping

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.

I wanted that anything I did in a package was scoped there, for instance being able to set a serializer in MyApp::API without setting one MyApp::Blog. In Dancer 1 that's impossible, because the settings registry is global to the process (remember? Globals are evil).

With Dancer 2, any package that uses 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.

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.

Where are we with Dancer 2?

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.

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.

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.

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.

Finally, the documentation needs a lot of work to get it up to the expected standard.

Should you switch from D1 to D2, and how?

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.

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.

To power your app with Dancer 2, as it's not released yet, here is what I suggest:

In your application, create a git submodule in vendor/Dancer 2:

$ cd MyProject
$ git submodule add https://github.com/PerlDancer/Dancer2.git vendor/Dancer2

Then add this little snippet in your bin/app.pl to make sure you load any lib from your vendor directory:

BEGIN {
    use FindBin;

    while ( my $libdir = glob("${FindBin::Bin}/../vendor/*/lib") ) {
        unshift @INC, $libdir;
    }
}

use Dancer 2.0;

This way, when the app worker will start, it will push all lib directory it an find in ./vendor/* as possible location for modules. Hence Dancer 2 will be found there.

Works like a charm, and can be used as well for plugins you want to patch for Dancer 2.

Also, if you want to make sure your Dancer 2 copy in vendor is up to date:

$ git submodule update

I'm using this technique at work for a Dancer 2 app we've deployed to production, it's working great.

Oh, and the page you're reading now has been served by Dancer 2 as well!

And while we're at it, see this commit to see how simple it was to port Dancer::Plugin::Feed (which is used by the Advent Calendar app) to support Dancer 2.

If the plugin you're using is just playing with the DSL, porting it to Dancer 2 is as easy as using plugin_args in registered subs to unroll @_ and as declaring the supported versions of Dancer when calling plugin_register.

Will you dance a second time?

So, will you give it a try? I'm sure you have that little app somewhere that would love to make the jump!

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.

That's the way you can help!

Author

This article has been written by Alexis Sukrieh for the Perl Dancer Advent Calendar 2012.