Internal redirects in Dancer

I really like the forward function in Catalyst. It lets you chain actions together and create a clean flow. I decided I want that in Dancer. We now have it.

Some background:
I was working with my brother on a project with Dancer. We wanted a client to be able to go to a personal demo page of the website to be able to try out editing features without destroying the actual website. The original (awful) code did this by copying all the (CGI) code into a different folder and making that available.

Our idea was to have a demo database that starts as a replication of the existing one and then can be mutated and twisted with editing tests. The data is no longer important, just that there's something to play with.

We wanted to be able to provide the client a route to reach (such as "/demo/articles") which will set the schema variable to the demo database and run the originally wanted request ("/articles"). This could be done cleanly using Dancer's sweet regex route declaration ( get qr{ /demo/(.+) }x => sub {...} ), but the problem was introducing an internal redirect.

The main problem is that unlike most MVC frameworks that use namespace matching to reach named subroutines, Dancer (and most micro-frameworks of the likes in other languages) uses anonymous subroutines without namespace matching. That means that I can't really point to a specific subroutine and say "now run this".

However, with a bit of trickery, I was able to write the new forward command in about 20 minutes, including tests.

Using:
While Dancer 1.2 is coming out very soon and won't be carrying this, it will be present in the next version of Dancer, where you'll be able to enjoy it.

get qr{ /demo/(.+) }x => sub {
my ($page) = splat; # get the data from the match
# replace the following with something nicer if you're using
# Dancer::Plugin::Database or Dancer::Plugin::DBIC
$schema = connect_to_demo_db();
return forward("/$page");
};

(of course, the return statement at the end there isn't necessary since it's already the last statement but it's still a good habit)

How's that? :)

On performance:
Since this doesn't run a namespace match, it's more costly. However, with route caching you can bring it to a minimum since it translates to a hash key match, so that's not too bad! Considering the framework (as a micro-framework) does less than most (if not all) full-blown frameworks, it's safe to assume it might be overall faster.

On a lighter note, I suspect the Python Flask people might be able to push such a feature using pure namespace matching because of a flawed design. Funny how things work like that. Then again, the decorator might fsck it up. C'mon, don't laugh... :)

3 Comments

Nice...I am looking at Dancer more and more. Haven't implemented anything yet though.

It is a very good feature indeed. Thank you.

One quick question: I don't quite understand your $schema example here. Does it mean the $schema will change to demo db for the whole app just because someone happens to request on this link, since $schema is a global variable? How do you implement a more "request-based" approach?

Leave a comment

About Sawyer X

user-pic Gots to do the bloggingz