Prototyping presentations

25 September 2006

Recently, I was lucky enough to give a talk at Railsconf Europe, which I co-presented with my colleague Gavin Bell. Now, I’ve never presented a talk with anyone else before, so there was going to need to be a degree of co-operation between the two of us to make the talk and the presentation work out OK.

Fortunately, the division-of labour in the talk itself wasn’t a problem: we both had directions we wanted to take the talk in, and, after a few meetings over lunch, we had worked out a way of brining everything together under one roof. The content wasn’t going to be a problem. However, piecing together the presentation in Keynote was going to be a little more complex than usual, given that each of us was writing our parts of the talk seperately. However, I managed to devise a very simple way to rapidly prototype the presentation without wasting too much time in Keynote. Of course, when I say “devised”, I am aware that many other people might also do this; however, I hadn’t read anyone describe this technique, so I thought it’d be worth sharing.

All you need to do this is a notebook, some wide post-it notes, and a pen. Split the deck of post-it notes between you and your co-presenter. Each post-it note represents a slide. As you’re writing your talk, write the title of each slide (if you want, you can also add notes on what you’d like to appear on the slide) on a post-it note, and place it on one side. Then, once you’ve both got all the post-it notes done, you can sit down together with the notebook and start to place them in order – one post-it note on every other page.

“Every other page” is important to the success of this, because you’ll often find you want to rejig the presentation, or add extra slides, and by leaving a page blank between each slide, you’ve got one extra slide’s leeway before you have to re-jig too much.

I also put the opening/closing slides at the very front and very back of the book, as they were pretty much immutable. Once we’d got all the “slides” in the right order in the book, it was very easy to draw them up in Keynote on my own and be pretty confident that they wouldn’t need much changing. In the end, we altered a few lines of text and the order of one or two slides, but that was it.

That’s probably all best explained with a video, so Youtube here we come:
I’m probably going to go as far as using this technique for future talks on my own. With this process, instead of trying to write the talk, write the slides, and design them all at once, you can focus on the content first, and, once that’s finalised, concentrate on the design later. It certainly worked well for Gavin and I trying to write a talk together. And, whilst we’re on the subject of presentations, Tom Carden raises some good points about the tools you do them with.

Finally, as a sample of the finished product, this is one of my favourite slides that I’ve ever made:

So, Matt pointed out that when you do things like that, the scores for different Models are all ranked seperately because they’re coming out of different indexes. The trick, obviously, is to use the multi_search method, which generates a multi-model-index, and that’s probably a better approach. This approach is very well documented in the RDoc. So all it shows is: probably should have RTFM. The thing below isn’t by any means bad, it’s just a little like re-inventing the wheel.

Never mind, eh?

(This mail from Jonas is also worth a read on this matter).

If you’re building a Rails applciation with search, chances are you’ve run across acts_as_ferret. And if you haven’t, check it out – it allows any model to be searchable by Ferret, the Ruby port of Lucene. Ferret’s a pretty nifty search engine – reasonably fast, pretty accurate – and it’s nice to be able to use it so simply in your Rails app.

Of course, what makes acts_as_ferret really handy is that it’s neatly designed to perform multi-model searches. Or, rather, the interface to do so is there, you just have to glue the results together. After some rough stumbling about, here’s what I came up with.

Firstly: don’t use find_by_contents, that’s not much use. find_by_contents is a wrapper around find_id_by_contents. find_id_by_contents is useful because it returns you only three things: a relative score, a model name, and a model id. That means you can merge everything into a big list, and then perform individual queries on the relevant models.

So how did I implement this?

My first stab was this (borrowing some real code from what I’m working on):

articles = Article.find_id_by_contents(query)
authors = Author.find_id_by_contents(query)
matches = (articles + authors).sort_by {|match| match[:score] }

This gives you an array of model/score/id hashes, which you can then do lookups on. The problem is it’s not very DRY, and it requires editing every time you make a new model that’s Ferretable. This is my more final solution, which I came up with. This time, I’ll walk you through each step.

First, somewhere like environment.rb, define a constant array:

FERRETABLE_MODELS = %w[Article Author]

That means we can update things at a later date easily. Then, in your search controller action, start with this:

klasses = FERRETABLE_MODELS.collect {|klass| Kernelt.const_get klass}

That should give you an array containing the Article and Author objects – or whatever ActiveRecord objects you’ve chosen in your constant array. Next, let’s get the array that we arrived at last time:

matches = klasses.inject([]) {|out, klass| out << klass.find_id_by_contents(query)}.flatten

Bit more complex, but also more succinct. All this does is iterate over each klass, using an inject method with an empty array passed in, and tells each class to call find_id_by_contents, passing in the query. It then flattens that lot, so that the array is only one-deep. We're now where we were before.

Finally: let's generate an array of the actual objects we referred to, sorted by ranking. I'm going to generate an array of hashes. Each hash has two keys: :object, the actual data object we want; and :score, the rank that ferret assigned it. We get that out like so:

results = matches.collect {|match|
   :score => match[:score], 
   :object => (Kernel.const_get(match[:model]).find(match[:id]))
}.sort_by {|o| o[:score]}.reverse

Again, possibly a bit ugly. :score remains the same; we run find on the appropriate ActiveRecord model, passing in the appropriate id to obtain the :object. Finally, we sort the array by :score and flip it, so that results[0] is the most popular search record. Obviously, you can pass extra parameters into that "find" method.

All that remains is to display that lot in your view, perhaps paginate it, and build a conditional to determine how to display each kind of object.

And that's it. I made a few changes to my dummy code when typing this up, so if something's broken, tell me and I'll fix it. I think that's a more maintainable way of searching across a range of models with ferret, and it takes advantage of some useful Ruby dynamics - finding objects through Kernel.const_get, in particular. That's my Ruby fun for today, then.

Update

My colleague Ben proposes this much tidier (but untested) solution:

results = []

FERRETABLE_MODELS.each  do |klass|
  k = Kernel.const_get klass
  k.find_id_by_contents(query).each do |m|
    results.push {
       :score => m[:score], 
       :object => k.find(match[:id]) 
     }
  end
end

results = results.sort{ |a,b| b[:score] <=> a[:score] }

I like the nested loop much more - should have thought of that myself - and will admit to being lazy wrt the sort_by and .reverse trick.

So I’m only the umpteenth person to say that I’ve just ordered a whole bunch of MOO minicards. Having seen them from some friends already, I can tell you that the quality is great, the size is lovely, and the colour repro is spot on.

What I find really interesting about them, though, is that MOO describe them not as “business cards” but as “calling cards”. I really like that; it’s an important distinction, and a nice reminder of the origins of what we now call business cards. They’re tools of etiquette, ways of seeking permission to see someone and also permission to engage with them. They’re also a signifier; in the old-fashioned usage (seeking permission to see someone) the card with a name on is presented before the card’s owner is even permitted inside the house.

And, as Wikipedia points out, we exchange business cards to swap not only contact details, but business interests. When I look at the cards I have, even though many of them are from friends, it’s still a reminder of what they do rather than who they are. We all have so many ways to contact us now – mobile, email, IM, VOIP, MySpace – that it makes sense to resurrect the idea of cards precisely for personal usage. No business strings attached, no implicit sell; just me, my name, my number.

That’s why I really enjoyed reading the comments on this Techcrunch post about MOO, which Tom linked to.
Handing out photographs as business cards is confusing, difficult to read and unprofessional,” writes one commenter – but he misses the point, because these aren’t really intended as “professional” tools. The fact they can be used as business cards is, in fact, a nice side effect, and several commenters in that thread point out the value of photographic cards for trade purposes. But really, they’re just ways to remember who people are, and perhaps how to contact them.

I love the revival of a simple, physical identifier in an age when we’re drowning in digital identifiers that we keep losing. I also love that it’s designed for constant, ongoing delight (rather than just an immediate “wow!”). I guess that comes from the certain satisfaction in pulling out a physical thing you’ve had a part in making. Chris remarks that “the best thing is that each picture has memories and stories attached”. He’s spot-on. Every time you give one away, you’re not only giving away an identifier – my name, my number – but also a descriptor – “here is a photograph I like, which I took, which in part describes me through my taste”.

I’m looking forward to receiving my free set, and then I’ll set about getting some more. Demand will no doubt be huge, but I’m sure Stef and the team will cope. More to the point, I can’t wait to see what else MOO are going to do in this field. There’s so much exciting content around the web (besides text), waiting to be dumped out – I commented on the value of hard copy at Reboot in June. As such, surely there’s never been a better time to be in the print business?

24

19 September 2006

So: I’ve had lots of posts in my head to write and not a lot of time. I’ve still got post-match analysis on d.construct (which I attended) and Railsconf Europe (which I spoke at) to come. I’ve also been busy with at least one out-of-work project which is going quite nicely, and means I get to play with Capistrano soon.

The other reason I’ve been a bit lax is that it is birthday season for many of my friends, so the weekend was spent first in boozy fun, and then going to see the Tindersticks at the Barbican (which was great). And that lot all culminates today, because it’s my birthday.

I’m 24, which isn’t the most exciting age, but Wikipedia seems pretty convinced it’s quite exciting. All I could think of was Kiefer Sutherland and (1 x 2 x 3 x 4), the latter of which is more satisfying. 23 was fun, incidentally. Lots and lots of things happened, I got busy, I got stressed, I just about surfed over it. All in all: a good year. Here’s to another one.