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 M. 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 P.'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:
#!/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:
…
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:
…
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:
…
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 P.: [email protected]"
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:
…
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…