LSRC Name Picker (#129)

As most of you have probably heard, the Lone Star Rubyconf is on the horizon:

Lone Star Rubyconf

What you haven't heard before now is that the conference organizers need our help. As is typical for these events, several gifts will be given away to the attendees. Names for the prize recipients need to be selected at random.

That's where this quiz comes in.

The LSRC organizers would like us to build name picking applications they can choose from. The functionality is very basic:

* Your application needs to accept input as a list of attendees.
At a minimum, you should accept names, but it might be nice to
allow for additional details like the attendee's organization.

* Each time your application is run, it should select and display
a single name from the list. Your application should remember
previously selected names and not choose them a second time.

The real quiz is how fancy you can make that process. The LSRC organizers are looking for a lot of sex appeal in their name picker. Solutions that incorporate the conference name or stars will be given extra consideration. The goal is something that will have a room full of attendees entranced when run on the projector. Any interface that meets the above criteria will do.

The conference organizers will recognize the author(s) of any selected solution(s) at the event (your output can also include a byline), which will surely lead to unending fame. Fortune may have to follow at a later date.


Quiz Summary

Everyone who had my name in their example prize list deserves extra credit. I won millions in cash and prizes as I examined the solutions this week. I can't wait until the checks start coming in the mail.

I was occasionally beat out by some Yukihiro Matsumoto guy though. Weird.

As usual, there were a lot of great solutions. We had everything from clever ASCII art to full GUI applications. There were also a lot of clever ways to taunt the attendees before a name is picked.

I'm going to look at Carl Porth's solution because the code is compact and pretty approachable. I also laughed out-loud when I ran his code. Do play around with the other solutions though or you will be missing a lot of fun applications and code.

Carl's solution is a Camping application. If you aren't too familiar with Camping, as I wasn't, Carl's solution makes a nice gentle introduction. Here's how it begins:

ruby
#!/usr/bin/env ruby -wKU

require "rubygems"
require "camping"
require "RMagick"

module Enumerable
def rand
entries[Kernel.rand(entries.size)]
end
end

Camping.goes :NamePicker

# ...

First we see that Carl loads Camping and RMagick as his weapons of choice. Then we have a simple helper method for choosing a random member of some Enumerable object.

The final line is how you always begin a Camping application. It tells the framework to create a namespace for your application in a module called NamePicker. The code then goes on to fill in that namespace:

ruby
# ...

module NamePicker::Controllers
class Index < R '/'
def get
render :index
end
end

class Stylesheet < R '/style.css'
def get
@headers['Content-Type'] = 'text/css'
File.read(__FILE__).gsub(/.*__END__/m, '')
end
end

class StaticImage < R '/images/(.*)'
def get(static_name)
@headers['Content-Type'] = "image/jpg"
@headers['X-Sendfile'] = "#{current_dir}/images/#{static_name}"
end
end

class PickedNameImage < R '/picked_names/(.*?)\.gif'
def get(name)
make_image(name)
@headers['Content-Type'] = "image/gif"
@headers['X-Sendfile'] = "#{current_dir}/picked_names/#{name}.gif"
end
end

class Page < R '/(\w+)'
def get(page_name)
render page_name
end
end
end

# ...

These are the controllers for this application. They manage all of the traffic through the pages.

The main point of interest in these is Camping's R() routing method. As you can see, it builds classes these controllers can inherit from. The regular expression you pass to it are the URLs that controller will service.

The first controller covers index requests. The next two serve up static file and image content. PickedNameImage also serves images, but these are dynamically generated from a selected name using the make_image() helper method. Both types of images are handled by Camping just by the code setting some headers about the file to send. The final controller handles all other page requests.

You can see that the Stylesheet controller makes use of some content embedded later in the file. It looks like this:

ruby
# ...

__END__

body {
background-color:black;
text-align:center;
font-size:30px;
font-family:impact;
color:red;
letter-spacing:3px;
}
a { color:red }
p { margin:80px }

Now, unlike Rails, Camping views are just simple Ruby methods. Here's a look at those:

ruby
# ...

module NamePicker::Views
def layout
html do
head do
title "LOLPIXNAMES"
link :href=> R(Stylesheet), :rel=>'stylesheet', :type=>'text/css'
end
body { self << yield }
end
end

def index
p { img :src => R(StaticImage, "icanpixname.jpg") }
p { a "PIX A NAME", :href => '/pick_name' }
end

def pick_name
all_names = open('names').readlines.map do |e|
e.gsub(/[^a-zA-Z 0-9]/,'')
end.reject { |e| e.empty? }

picked_names = Dir["picked_names/*.gif"].map do |e|
e.sub(/picked_names\/(.*?)\.gif$/,'\\1')
end

unpicked_names = all_names - picked_names
name = unpicked_names.rand

p do
img :src => R(StaticImage, "ipixedname.jpg")
br
img :src => "picked_names/#{name}.gif"
end
p { a "I CAN PIX YR NAME AGAIN", :href => '/pick_name' }
p { a "KTHXBYE", :href => '/credits' }
end

def credits
h1 "CREDITZ"
ul do
li "http://flickr.com/photos/mag3737/296800129/"
li "http://flickr.com/photos/brian-fitzgerald/608882248/"
li "http://www.ocf.berkeley.edu/~gordeon/fonts.html"
end
p "Carl Porth: badcarl@gmail.com"
end
end

# ...

The point of interest in these methods is the appearance of Markaby, which is used to build HTML pages. Markaby is very similar to Builder, if you are familiar with that library, save that it doesn't need the explicit receiver.

The only action that does some work here is pick_name(). It pulls in names from a name file, cross-checks those against what it has previously generated, and selects an unpicked name. Then it renders some HTML to show the selection.

All we have left at this point are the helper methods:

ruby
# ...

module NamePicker::Helpers
def make_image(text)
gif = Magick::ImageList.new

decode_name(text.upcase).each do |frame_text|
frame = Magick::Image.new(30*frame_text.size, 52) do
self.background_color = 'black'
end

Magick::Draw.new.annotate(frame, 0,0,0,0, frame_text) do
self.font = 'Impact.ttf'
self.pointsize = 50
self.fill = 'white'
self.stroke = 'black'
self.stroke_width = 2
self.gravity = Magick::CenterGravity
end

gif << frame
end

gif.delay = 15
gif.iterations = 1

gif.write("picked_names/#{text}.gif")
end

def encode_name(name, indexes=[])
return [] if name.size == indexes.size + 1

new_index = ((0...name.size).to_a - indexes).rand
random_words(name, indexes) + encode_name(name, indexes << new_index)
end

def random_words(word, indexes_to_replace,
number_of_words=indexes_to_replace.size)
(0..number_of_words).to_a.map do
new_word = word.dup
indexes_to_replace.each { |i| new_word[i] = ("A".."Z").rand }
new_word
end
end

def decode_name(name)
encode_name(name).reverse
end

def current_dir
File.expand_path(File.dirname(__FILE__))
end
end

All of these methods work together to generate the selected name image shown when the application runs. The process builds an animated Gif image where the frames show the name in various degrees of being scrambled. As the animation plays out, the name unscrambles to reveal a winner.

The make_image() method does the heavy lifting by iterating over variations of the name and using RMagick to build frames for them. The other methods scramble the name by replacing certain indices with random letters.

That's about all there is to Carl's code, save some humor. Fire it up in your browser to make sure you get a taste of that. "KTHXBYE!"

My thanks to all who solved the quiz, made me laugh, and, most importantly, to those who awarded me prizes.

Tomorrow we have a problem using a c__ss_c g_me...