As I mentioned last week, I’ve started working with Dancer in earnest. This week, I climbed three learning curves at the same time: Dancer, Xslate and Bootstrap.
Skeleton before template 🔗︎
I started off with the incredibly-handy “Dancer Cowbell” template by A. Gordon, which is a complete skeleton app that brings together Dancer, Template::Toolkit, Bootstrap, and Font Awesome.
It comes with scripts to download Bootstrap, etc., from various sources. Unfortunately, the day I tried it, GitHub (the source of Bootstrap’s custom downloads) was f*cked, so I wound up cobbling together the various bits from other Bootstrap experiments I’d downloaded in the past. Not fun, but not A. Gordon’s fault.
Xslate is xcellent 🔗︎
My next challenge was converting it from Template::Toolkit to Xslate. I’d been intrigued by Xslate’s design, particularly its speed. Xslate is fast, at least if you have a C compiler and a persistent application. It also has some interesting template composition capabilities, more akin to “roles” than just bare includes.
For example, here’s what one of my view templates looks like:
:cascade wrapper with macros, header, footer : around title -> { About :} : around pagestyle -> { : stylesheet("about") :} : around content -> { <h1>About my project</h1> <p>Blah blah blah...</p> :}
The interesting thing is that the “cascade” command combines four other templates, the HTML wrapper, some header HTML, some footer HTML, and macro definitions (the “stylesheet()” function). Then my page template just defines things to replace at various places in the cascade. The “title” and “pagestyle” blocks get inserted into the HTML header section. The “pagestyle” is defined using a macro from the macro sheet (and maps to a stylesheet link with a static path to “about.css” for page-specific layout). And the “content” block gets dropped into the HTML body section in the wrapper.
What about that “header” – where does that go? Check out the header template:
: before content -> { <div class="masthead row"> <!-- Bootstrap navigation would go here --> </div> <hr> :}
See the “before content” directive? It does what you would think, composes the header before the content block in the wrapper. The footer uses an “after” directive the same way.
That’s a long digression on Xslate, but I’m really happy with it so far. My view templates are stripped down to the bare minimum of information related to that view, yet can stuff information into any other spot in the composed template. It took a while to wrap my head around what that means.
I think about it like this: the wrapper template defines where things go, but doesn’t actually pull them in the way a template would “include” another template traditionally. It’s purely a framework of placeholders. Then, other templates customize those placeholders, saying if they go before, after or replace them.
If I want a different header for some page, I don’t need a different wrapper to include it (which would duplicate things in the wrapper I might need to then keep in sync), I just need to compose a different header template in the page template cascade. Cool!
But what about Dancer? 🔗︎
Even before I fully wrapped my head around Xslate, I got down to business with Dancer. I spent a while reading various Dancer tutorials:
- The “official” Dancer::Tutorial
- Perl Training’s “Building a website with Dancer” tutorial
- Gabor Szabo’s “Building a blog engine with Dancer” tutorial
I decided to start with the static pages, and created simple routes with matching template names.. This was trivially easy, the “hello world” of web development.
get '/' => sub { template 'index'; }; for my $p (qw/about faq tos privacy help/) { get "/$p" => eval qq|sub { template '$p' }|; }
I intentionally did not use Dancer’s ‘auto_page’ config option, which will match simple routes to matching templates in the view directory because I don’t want all the decomposed xslate templates to become valid routes.
After copying and pasting some examples from the Bootstrap site, I finally got the static pages looking loosely like what I envisioned (albeit with boilerplate text), and I could click around the navigation and the whole thing started looking like a real site. Woohoo!
Users and passwords and hashes, oh my! 🔗︎
With the static shell done, I turned to the next logical task: user registration and login/logout. I already had a half-done user model from some backend prototyping, but it had no concept of password (hashed, of course), so that had to be added.
I spent a little while getting up to speed on bcrypt options on CPAN. There was an interesting Dancer plugin, Dancer::Plugin::Passphrase that I considered, but ultimately I decided that the controller really shouldn’t be handling password hashing and that it should live within the model.
I considered Authen::Passphrase and loved everything about it except for all its dependencies – it gives you tools for every reasonable (and unreasonable) password hashing scheme, even if you only need one. Ugh.
So then I looked at how Dancer::Plugin::Passphrase was using Crypt::Eksblowfish::Bcrypt to see if I could just crib that… and then I decided I was spending way too much time in the weeds and chose to use Authen::Passphrase (BlowfishCrypt algorithm) after all.
Fold, spindle, mutiliate 🔗︎
Forms and form building modules looked like another tarpit and I decided to go around. I worked up simple registration and login forms by hand using some Bootstrap sample code, skipped all validation for now (naughty!), and created some POST handlers. Eventually, I’ll go back and look to form builders and validators, but it’s easy to lose a day browsing CPAN and I didn’t have a day to waste.
In case any other HTML newbie is out there doing things by hand, make sure your inputs have a “name” property if you want them submitted. For whatever reason the code samples omitted them and I spent a long while wishing I had hair to tear out in frustration when my form handling code didn’t work. (Yes, a decent form builder would have saved me this embarrassment. Oh, well.)
With that fix, user registration worked.
Doing sessions the not-so-easy way 🔗︎
By definition, “logging in” means a change in state, so I needed sessions to manage state. And of course, Dancer had plenty of session managers to choose from. Dancer::Session::Cookie was the first I tried. I’ve been intrigued by the concept of maintaining state with clients instead of a server and wanted to give it a try. (No, I won’t get drawn into arguments over that here. Trust that I’ve read the relevant papers about it and have a sense of pros and cons and caveats.)
It worked great, right up until I wanted to log out. I couldn’t log out without manually deleting the cookie!. WTF? Turns out it’s a bug in how Dancer::Session::Cookie does session destruction. I filed a pull request with a trivial fix and then switched to the other quick-and-dirty developer-friendly option, keeping state with files. I picked Dancer::Session::JSON. Boom! Login and out working.
[After spelunking in the Dancer codebase, I also took some time to explore what the not-yet-released Dancer 2 code is doing for sessions, filed half a dozen issues to address problems I saw, and also filed a pull request to improve the abstraction model.]
Now that I had logged-in and logged-out states, I started tweaking the header templates to change the navigation depending on the users’ logged-in or logged-out status. E.g. toggling between “login” and “logout” links and so on.
After that, it’s still not much of a site, but it’s definitely not “Hello World” anymore.
What’s next 🔗︎
I’m now starting to work on email verification and password reset. Both of those have a similar process model requiring a token to be emailed to a user and taking action based on the resulting click. I signed up with Postmark and tested it out with WWW::Postmark. I’ll use that for emailing the tokens. At first, it will probably be direct from the app, but eventually, I’ll move that into an offline process.
Once that’s done, I’ll be turning to the “logged in” experience, connecting the site up to the backend data.
Summary 🔗︎
I’m feeling really good about how quickly things came together. After starting from almost zero, here’s what I’ve got after my first week with Dancer, Xslate and Bootstrap:
- Static pages
- User registration
- User login/logout (with navigation changes based on state)
- Password change
- Well-factored templates
- Half a dozen or issues filed or patches sent for things discovered along the way
See you on #dancer…