Dancer 2, or Why I Rewrote Everything

If you've been following developments in the Perl Dancer ecosystem recently, you might be aware that a complete rewrite of the core is on the way.

I haven't advertised what I did, what happened and what is the plan, so this Advent calendar is a perfect opportunity to do it.

In this article, I'll explain the reasons behind this decision and what were the most important targets of this rewrite.

Firstly, though, it should be made clear that there's no need to panic - Dancer 2 will strive for backwards compatibility, providing the same user-friendly DSL as Dancer, so your existing Dancer apps should work with Dancer 2 with only minimal changes.

A bit of history

Maybe I should start with how the development of Dancer started in the first place. It was in summer 2009, I was working at Yoolink at that time and we were making a lot of use of the Ruby language.

I discovered the Sinatra micro-framework at that time and found it really exciting. After a quick glance, it appeared that there was nothing similar in the Perl ecosystem at that time, so I decided it would be an interesting challenge to port it to Perl.

Thus, the first Dancer prototype was born.

The prototype evolved quickly and after a couple of months, the project was up available on CPAN and the source code hosted was pushed on GitHub.

A community of motivated users emerged and after a year, Dancer was well-known and had quite a good reputation of being very easy to use and making web development fun with Perl.

Needless to say that when you are powered by your community's energy, it's very hard to stop the flow for changing major design decisions.

When I look back at what happened exactly, I'd say that Dancer was a very well-received and successful prototype of the easiest web DSL we could do with Perl.

From that perspective, I'm very proud of Dancer, because from an end-user point of view, it's really kicking asses: it is intuitive as hell, it stays out of the way and gives you all the freedom to think of the one thing which matters most: your application.

But, you know the drill, perfect software does not exist, there is always something we can do better. In the case of Dancer, there were a couple of design decisions which needed to be taken care of.

Actually, when I took the time to think about it, I realized there were two major drawbacks in the design that would make it difficult for Dancer to continue its epic growth:

  • Globals and singletons

    The major source of issues with edge-cases, scalability and maintenance in Dancer is the use of globals. Many developers who happened to hack around the core know that.

    I wanted Dancer to get rid of that design flaw.

  • Consistent and object-oriented core API

    As Dancer has been built iteratively, the Core API was never a real target for me, that means hacking around the core is quite difficult, and not consistent. I wanted Dancer to be a clean DSL over a complete object-oriented API.

As a side-effect of these tow main decisions, I also wanted the core to follow the Law of Demeter, in order to ensure a sane class/method hierarchy.

I also wanted a strong concept of scope to be implemented in the rewrite, so that everything that happens within a caller would be scoped there (no more apps collisions, support for multiple engines per app, per-apps settings, etc).

Dancer2, the origins

All this was in my mind for quite a long time and I was wondering how I could do these changes while maintaining Dancer's evolutions.

I should also mention that among the contributors of Dancer, there were a couple of guys very productive and motivated, and I granted them a commit bit quite quickly. Soon enough, we created what we called the Core Team, a group of developers who knows Dancer well and who are able to process pull requests.

Thanks to them (SawyerX, Franck Cuny, David Precious, Damien Krotkine and Alberto Simões) Dancer had a strong team to take care of daily maintenance duties. I also made SawyerX a co-maintainer on CPAN so that he could become our Release Manager.

All this setup was perfect for the Dancer 2 project: I had all in place to step aside, let the maintenance in the Core Team hands and focus entirely on the rewrite.

Then, I decided to create a new repo and experiment these design ideas on a complete new prototype, to see where I could end up. At that time, I went to discuss with Matt S Trout (mst) on IRC, in order to brainstorm the new design. Matt also happened to become my design architect mentor, giving me ideas, tips and suggestions when I faced subtle design issues to solve.

Matt sounded encouraging and provided lots of useful and enlightened suggestions to help me start.

One of his suggestions was to use Moo to get a complete Moose-ish object system for the core yet fast and lightweight. That happened to be one of the more helpful choices to enforce a better design.

My strategy was to start a new project, with one target in mind: support the DSL of Dancer, but make it a true DSL: a layer over a complete, consistent and object-oriented core.

Mainly the idea was that I could write a Dancer app with raw objects if I wanted to. This would open new doors and allow strong flexibility.

Where are we?

I won't give you more details on the Core API in this article, neither will I give a roadmap for Dancer 2, because these topics will be covered in other articles, but I'll just give you a quick overview of Dancer 2 as of today.

The repository is public on Github, you're very welcome to clone it if you want to test your app with Dancer 2. If you do so, please send me an email with your feedback, that will help a lot!

Let's give it a try:

$ echo "use Dancer; get '/' => sub { 2 }; start" > test.pl
$ perl -Ilib ./test.pl &
HTTP::Server::Simple::PSGI: You can connect to your server at http://localhost:3000/ 
$ curl http://0:3000/
2

Nothing special here, we have our hello world app powered by Dancer2. If you look more precisely, you'll see that nearly all the DSL of Dancer 1 is already supported in Dancer 2.

To get a quick teaser about the internal changes we have, let's enable the core debug messages:

$ DANCER_DEBUG_CORE=1 perl -Ilib ./test.pl &
core: binding app to main
core: [main] -> get(/, CODE(0x172a1b0))
core: [main] -> start()
HTTP::Server::Simple::PSGI: You can connect to your server at http://localhost:3000/
$

As you can guess, with 2, for each package that uses Dancer, we have a Dancer::Core::App object (here for main). The DSL is just syntactic sugar over method calls to that object.

Well, that's all for this introduction, again, you're welcome to test Dancer 2, just clone the repo and run your app with it, just don't forget to mail me or the list about how it went, that would be helpful.

In the next article, I'll go over all the core API design, explaining all the entities we have and how it fits with the DSL.

AUTHOR

This article was written by Alexis Sukrieh and reviewed by David Precious, for the Perl Dancer Advent Calendar 2011.