Routing Speed 6
So, since I couldn’t sleep, I’ve been hacking on the routing drop in replacement.
I’ve reached the point where things are mostly working and I can at least run the benchmark tests.
On this machine, Rails recognizes routes at a rate of around 22K url/s.
At the start of the night, with the recognizer rparsec, to do the recognition, my new routing system was running at around 5K url/s, which isn’t exactly great. So, I rejigged things to build a regular expression and use that to parse the request URL and the recognition rate jumped to 12K url/s. I’m really pleased with that number for the time being.
Rails goes faster because it’s doing some hairy eval STRING optimization that makes things rather hard to follow. If I can get up to better than half that speed without the hairiness, I’m happy.
For now.
What happened to the routing rework?
Those of you who’ve been reading attentively will be aware that I’ve been working on a drop in replacemen for a chunk of Rails’ routing subsystem. You will also be aware that things have gone a bit quiet on that front.
I got a job.
Which is lovely, and fabulous, and safeguards my mortgage payments and all that good stuff. But it has rather put dampeners on any non paying work.
Finding the balance
What with throwing myself into the new job and spending last weekend visiting friends, family and the Brighton Coding Dojo mob, I’ve been neglecting other coding commitments. This should not be a permanent state of affairs, I’m hoping to return to the new routing project over the weekend. It’s all a matter of finding the balance.
That was fun 6
On Monday I was down in Brighton for a Brighton Coding Dojo where I had a crack at doing Kata four in Smalltalk.
It took a while to find the balance, but once we got going I think it went well.
We stuck in what seemed like the strangest places though. At one point, I had a method that did almost exactly what I wanted for a new method I was writing so I called up the method in the browser, changed the selector and the few bits that needed fixing up and accepted the changes.
Uproar! “Wha? What’ve you done to testGetMnT?”
“It’s still there, look.” I said, pulling testGetMnT up in the browser.
“But…!”
People were impressed by OmniBrowser’s refactoring tools and slightly boggled by the sheer number of instance methods on Object.
Because we dived straight in, people got a wee bit stuck on the syntax as well. Next time I do something like this, I’ll spend more time walking through what’s going on in each line of code, until people get a bit more secure, and I’ll start handing the keyboard off to other pairs way sooner. Once I did that, it became far more apparent which bits were sticking points.
The session certainly confirmed my opinion that you can read all you like about Smalltalk, but you won’t really get it until you see it in motion.
So, once I have some tuits of the appropriate shape, I’m planning on making a longish screencast of me running through Kata four, with commentary. It won’t be an exemplary example of a Smalltalk user getting the very best out of the toolset; I’m very much a beginner myself, but I hope it’ll give you a feel for why you should at least try Smalltalk for yourself. I also hope that any experienced Smalltalkers watching the screencast will be able to give me some tips on better ways of using the tool.
Rails, CRUD, REST and the importance of adjectives 8
Consider a web request:
GET /articles/2007/04/17/adjectives-rule;edit
“What does it mean?”
If we pass it through the prism of Rails routing, it breaks apart as follows:
GET- The
:method, sometimes called the verb. /articles- The
:controller, the thing that’s going to handle this request /2007/04/17/adjectives-rule- Parameters, passed to the controller method that handles this request
;edit- The
:action, some way of specifying what this request does
It’s certainly one way of looking at it, but if you do I think you’re limiting your understanding of what it means to program in a RESTful fashion. It’s an obvious fit for Rails where the dispatcher builds an appropriate controller and then does controller.send params(:action)[1] but it’s a very ‘imperative’ view and that’s not necessarily a good view. The problem is that :action at the end.
What’s happening is that Rails’s implementation is leaking into the abstraction that is the URL. If you let that happen you end up programming with CRUDdy blinkers.
Let’s try another way of breaking that request up into ‘parts of speech’. Here’s what I think the request means: “Get me the editor for the article posted on 2007-04-17 called ‘adjectives-rule’”. ;edit isn’t an action selector, it’s a view selector.
“Isn’t this just navel gazing?”
At the London Ruby Users’ Group on Monday, there was an occasionally heated discussion of RESTful programming in which several people expressed the view that REST was limiting because they “needed more than 4 verbs” for their application.
Much of the discussion of RESTful programming on Rails has been about how to break things down so that you can think of your application in CRUD terms, and that’s a terribly limiting way to view things.
Let’s look at another request and see how to interpret it.
POST /articles/new;preview
Content:
article[title]=Adjectives+Rule;
article[body]=...
If you break this request up along the lines suggested by rails, then it seems that this isn’t a RESTful request. It’s POSTing something, which means its creating a new resource, but that resource isn’t going in the datatabase. Wha?
I think this is perfectly RESTful though. What we’re saying is “Make me a new preview of the article described in the body of the request”. The POST part of the request implies ‘make something’, the rest of the request describes what to make. Our controller knows that articles created for preview are not persistent, so they don’t go in the database. The preview you create and then throw away is just as much a resource as the finished article, the only difference being that every preview gets thrown away.
Looked at like this, it’s apparent RESTful programming is much richer than the basic 4 CRUD actions. The important principles to bear in mind are:
- GET must not alter any resource
- This is the really, really, really big one. If you’re ever tempted to write a method that interprets a GET request and changes the state of the resource being fetched… well, think again.
- Statelessness
- This is the idea that the request itself contains all the information the server needs to build the response. No session cookies. There are those who argue for no cookies at all, but until browsers handle HTTP authentication better I think we’re stuck with cookie based authentication, but the cookie should really be self contained and not an opaque reference into a catch all session table.
POST,PUTandDELETEshould act appropriatelyPOSTmakes new stuff,PUTchanges old stuff,DELETEremoves old stuff. Not that hard to understand. The only wrinkle is that browsers don’t know how to makePUTandDELETErequests. Your AJAX actions will make them happily, but if use them in a form field, the browser will be confused. Because you can’t simply use JavaScript everywhere, you’re stuck tunneling those request types through POST. Rails has mechanisms to hide the tunnelling from you.- Persistent URLs
- If you GET something from
/some/where/or/othertoday, you should be able to get it there tomorrow unless it’s been deleted. If the resource has been moved, the right thing to do is to respond with a redirect (permanent or temporary as appropriate). Serving up an entirely new resource is right out. Which doesn’t mean you can’t have a resource like/articles/at_random– the resource you’re getting isn’t the article you receive, but ‘a randomly chosen article’. What would be bad would be having/articles/at_randomnot expire for 10 days.
“You haven’t mentioned adjectives yet…”
This article is a write up of something that occurred to me during a conversation after Monday’s LRUG meeting. Someone (whose name I either didn’t get or forgot) asked me what was the point of using RESTful principles in the first place. During the course of my answer, it suddenly clicked that what Rails calls actions aren’t really actions; they are adjectives. So, when I sat down to write this, I was expecting that that’s what I was going to say. But the trouble with late night pub conversation insights is that they aren’t always accurate or complete. As I wrote, I realised that my basic insight that an :action isn’t really an action was okay, but an :action is actually a view (I think it might be a controller when you’re POSTing though, just not a Rails controller).
“I’m curious now, what is the point of using RESTful principles?”
RESTful programming is a discipline. It’s a set of practices and patterns that help you make good decisions about how to solve problems in your application domain in a consistent way. The hope is that, by being rigorous in your application of the principles, you’ll end up with a website that’s easy to use, easy to cache and easy to scale. Certainly many RESTful practices are just out and out good practices.
There are other disciplines. Seaside is one. As Avi has shown recently, Seaside isn’t a smalltalk application framework, it’s a state of mind, and a weird one at that.
A thought
Here’s a little something to consider:
Thesis
Rails
Antithesis
Seaside
Synthesis
I dunno, but I’m looking forward to it.
Coming up
I plan to take a closer look at some of the principles and patterns of RESTful architecture and discuss how to apply them in Rails.
1 Almost certainly not exactly what happens, but close enough for jazz.
Soundbite
An argument for peace and love is always an argument for self-preservation.
Go Giles Bowkett, you speak truth. You also speak it way better than I managed a couple of years ago.
Mmm... parsers 1
So, in my quest to get Rails routes to accept routes like:
articles/:comment[article]/comments/:comment[id]
I’ve been playing with parsers for the first time in my programming career. Quite how I’ve managed to get this far without them I leave as an exercise for the interested reader.
At the moment I have a parser that will parse
articles/2007/05/12/slug
and give back an ActionSelector that yamlizes as
--- !ruby/object:ActionSelector
controller: articles
action: show
params:
:article:
:year: 2007
:month: 05
:mday: 12
:slug: slug
Which can be easily turned into the kind of params hash that rails wants from its routing system in order to dispatch the request to the right place.
For my next trick, I need refactor the parser I have so that it’s generated via a set of parser builders. Then I need to write a parser for routing specifiers that can use those builders to build a URL parser.
It’s a simple matter of programming I tell you.
Updates
RouteBuilder works (2007041608:00ish)
I now have a RouteBuilder that generates routes and has the same interface as ActionController::Routing::RouteBuilder.
My Route doesn’t have the same interface as AC::Routing::Route yet, but it’s not far off. The fun part is working out which bits of the interface are essential and which can be safely ignored. I’m hoping that I won’t have to rewrite AC::Routing::RouteSet as well, but I’m not sure how complicit it is with the innards of AC::Route.
Routes can generate paths from options (2007041612:30)
context "Given a default route" do
setup do
@route = RouteBuilder.new.build('/:controller/:action/:id')
end
def gen(hash)
# Rails interface is a bit surprising
@route.generate(hash.dup, hash.dup, {})
end
specify "generation works" do
gen(:controller => 'accounts', :action => 'show', :id => 1) \
.should == '/accounts/show/1'
gen(:controller => 'accounts', :id => 1) \
.should == '/accounts/index/1'
gen(:controller => 'accounts', :action => ':index') \
.should == '/accounts'
end
endIt’s nowhere near complete, but I’m being gung ho and starting to integrate my RouteBuilder with Rails. Then I can start pulling the Rails routing tests into my own test suite and get some confidence with its robustness before I start adding the ability to handle routes like /books/:tune[book][id]/tunes/:tune[id], which is why I started this in the first place.
With any luck and some good train hacking time on the way to London I should have something good to show at LRUG this evening.
Wanted: module/starter.rb 5
I sometimes think that one of the reasons that CPAN is such a huge advantage for Perl is the ease with which you can contribute to it. It’s all very well having a tool for installing libraries from the archive, any fool can do that, but CPAN has tools for getting started with a new library too.
If I want to start a new Perl project, I do:
module-starter --module=AuthenticationFairy
and the script goes away and builds me an AuthenticationFairy directory complete with all the boilerplate stuff filled in. I know that when I look in AuthenticationFairy I’ll find a sensible directory structure, a Makefile.PL that will do the right thing, a lib/AuthenticationFairy.pm with the documentation boiler plate filled in and a test directory. And I rejoice because my yaks have been sh aved and I can get on with the interesting business of writing my first test and making sure it fails.
I’ve not found an equivalent for Ruby. Yet.
I have found sow, which is part of the Hoe package, which is definitely a start.
Are there any other tools along the lines of Module::Starter that I’ve missed?
Cunning Typo Sidebar Tricks 2
If you’re reading this on the website rather than through a feedreader and you look to the left you’ll see either a bunch of links to cited books or an Amazon ‘self optimizing links’ banner (though, if it keeps ‘optimizing’ like it has being I’ll be changing it to something else). This is implemented using a mildly hacked version of the standard typo amazon_sidebar plugin.
“But,” I hear you say, “The sidebar’s over on the right, and Typo can only support one sidebar.”
And you’re right, Typo’s admin interface can only handle configuring one sidebar, and I’m not entirely sure how to make it work with too – hopefully our new maintainer and usability advocate, Frédéric de Villamil has some ideas along those lines. So, how does it work.
Let’s take a look at a fragment of my layouts/default.rhtml file shall we?
Typo Brain Dump 3
In case anyone’s interested, I’ve written a list of some of the things I’m currently mulling doing with Typo below the fold.
You are in a maze of little, twisty evals, all similar. 2
Rails routing is making my head hurt. It’s starting to remind me of the guts of perl’s regular expression engine, which was said to be understood by three people, plus or minus five.
I just want to write routes like /articles/:art[year]/:art[month]/:art[mday]/:art[slug].
I’m seriously considering starting again from scratch, which can’t be right.
