grand tour of the 13 railsday winners

Railsday 2006 results are out, at long last. The winning applications are impressive, and especially, very fun. There’s no universal demo server though so unless you go to quite a bit of work, you can’t see what the winning projects actually are like.

best solo project: rails wishlist

Rails Wishlist is a sort of a social feature-requester for Rails itself, by Hampton Catlin. It lets users vote up or down, Digg-style, on proposals for changes in Rails core.

front page

This entry is very interesting because it’s one of the few whose success wasn’t driven by the quality of its design; it was carried mainly by its features (I assume).

vote dropdown

request details

third prize, most useful: profilr

Profilr is a sort of account aggregator for your information-sharing sites, by Terence Haddock and Mark Chadwick. Unfortunately I couldn’t get it to work very well.

login

set up some of your accounts

get ready, go

loading… something

I never managed to get the application past this point. I don’t know if the accounts are disabled, or the sites are changed, or what. It tries to use Selenium to automate browsing and collect the relevant information.

second prize, most useful: regex tutor

Regex Tutor gives you a live environment in which to solve regular expression problems. Regex Tutor is by Ryan Bates.

Actually, this application was really fun and I wish there had been more fixtures. It has the constrained environment and instant gratification of a good puzzle game, while leading you lessons about different aspects regular expression.

get ready

level one

You have to make the input set on the left turn into the output set on the right. In the middle the results of your current attempt are updated automatically as you edit it.

whoops

if you need help

The application includes glossary and FAQ infrastructure so that it can be fleshed out into a comprehensive teaching tool.

This is my personal favorite of all the winners. Excellent functional use of Ajax.

first prize, most useful: heartbeat

Heartbeat is a server monitor. It combines an internal tool like Monit with an external uptime checker like Montastic. Heartbeat is by Charles Brian Quinn and Derek Haynes.

login

setup your servers

It connects via SSH to monitor process status.

keep an eye on your apps

third prize, best interface: c.umul.us

C.umul.us (make sure I got all the dots) generates a public view onto your iTunes collection. C.umul.us is written by Jae Hess. You upload your iTunes .xml library file. The interface is adorable and solid. Unfortunately when Railsday ended it didn’t do more than display a tag cloud of your genres and artists.

who? what?

The small cloud in the background floats around via sneaky Javascript breezes.

upload

glory in your unique and impeccable taste

Those are fixtures, not my real artists. I declined to try uploading my personal database of 25,000 songs.

But those little trees are so cute…

second prize, best interface: d20 online

D20 is a tool for keeping track of your card-based roll-playing games, by Tom Leiber, Jeff Mickey and Javier Cabrera. I don’t know anything about card-based roll-playing games so I have to keep quiet on this one. People can participate remotely in real time. Only having one of me, and not really knowing what I should be doing, I had a hard time testing that out.

ready to rumble?

create my character

participate in a game

The FAQ will help you out.

first prize, user interface: we rate stuff

We Rate Stuff is a product and service review database, although I guess you could review like your dog or something too if you wanted to. We Rate Stuff is by Frederico Oliveira, Tiago Macedo and Pedro Eduardo Lemos Freitas, who give it infinite stars (out of five).

We Rate Stuff includes full-text search via ferret, but I couldn’t get it to work.

home page

We Rate Stuff is full of beautiful fixtures and it really makes the application seem polished.

how was that latin concert, then?

or that “ruby tracks” thing

third prize, most creative: roomind.us

Roomind.us is an expense tracker and reminder sender for apartment mates. Hopefully it did not spring from a disagreement between Dominic Damian, Ben Myles and Chris Abad.

home page

pony’s gotta eat

let’s review

someone’s been slacking off

second prize, most creative: family book

Family Book is the little seed of a future comprehensive family networking site, by Lucas Carlson and John Butler. It lets you create a family-tree-like view onto your relatives, track their details, and spam your whole extended family whenever the annual reunion is coming up.

home page

The “lexical parser” bit refers to being able to type sentences like “My mother is Mom and her birthday is 10/3/1982” and have it add a node on the family tree for her. I typed “My daughter is a baby” and after that nothing showed up, so I stuck to the examples from then on. It was only one day, after all.

graph view

The graph is all in Flash. The suggestions at the bottom are slightly alarming, but do get the point across.

first prize, most creative: the awesome ninja game

The Awesome Ninja Game is a game, where you have this ninja, and he does ninja stuff. It was created by Tobias Lutke, Cody, and Daniel while they were standing in the sun. Seriously. Nobody saw it coming.

It seems like you can have your character fight with other people’s characters, just like Digg or Slashdot.

create our fierce ninja

choose moves

I don’t know the differences between them, but there are a lot to choose from.

good ninjas keep a schedule

Turns go by, and the ninjas fight, or study, or do whatever you set. Results are returned as text descriptions in the blank field to the left of the schedule, creating a little Zork-style history of your ninja’s activities.

oh, who’s the best, that’s right

This application was one of the really fun ones.

third prize overall: cuppin’

Cuppin’, which has become a real place, is a social site and review hub for coffee lovers. Peat Bakke and Raymond Brigleb are its highly-caffeinated creators. This really impressed me mainly because it’s something I would actually use, and all of the advertised features are fully implemented.

home page

i remember that one

what the faq

who’s been talking

coffee-related events

Mmmmmm. Catastrophy doesn’t sound good, though. Somebody probably ground the beans ahead of time, or maybe they were frozen (the horror!).

second prize overall: good to garden

Good to Garden is a garden simulator. Will Emigh and Rory Starks must have played a lot of Harvest Moon. I don’t think Good to Garden would be a good place to try out your growing strategies before hitting the dirt. It’s more like a vegetable Tamagotchi.

home page

i’m just testing, man; it’s really ok

the princess is in another castle?

I don’t know why this came up, but it did.

springtime

our little friend

harvest day

I’ll make some carrot cake to go with my coffee.

first prize overall: freckle

Freckle is a wedding planner, by Amy Hoy and Bryan Wood. I don’t know if they’re married or not; you’ll have to direct your inquiries elsewhere. Freckle leads you through the steps of orchestrating the big day. The app is solid although I did run into “Application error/Rails” here and there.

almost the most importantest

design your invitations? sort of

lots to do

hire people

guests

wrapping up

Many of the applications that won are basically CRUD (Cuppin’ and Freckle included). Good design seemed to carry the day. I didn’t see any really ugly apps with awesome ideas, or notice slick code tricks among the winners. This vaguely disappointed me. Ideas are hard to come by, though, and good code is certainly harder to evaluate than good design. Seriously, though, all the winners are more than worthy.

here, take this unsolicited advice

One thing all the winners did have in common was great migrations and (usually) good fixtures. I tried installing some of the non-winning applications randomly. I couldn’t get most of the ones I tried to even start properly due to bad migrations, database incompatibilities, or dependency issues. So here is what I will be keeping in mind for next year, if I compete:

  • Make it work out of the box. Consider sqlite3 for your database.
  • Have fun and interesting fixtures.
  • Think of your idea and sketch out a visual design on paper before hand.
  • Better to leave something out than expose an application error.
  • Have a good teammate with skills complimentary to your own. There’s a reason the winning apps were almost all by teams.
  • Go for plain old CRUD if in doubt. It’s the heart of Rails, after all.

make camping output a doctype properly

If you have a layout in your Camping app, like this:

  def layout
    xhtml_strict do
      # awesome stuff here
    end
  end

it will not render with a DOCTYPE, even though it’s supposed to. The solution is to wrap it in a capture block:

  def layout
    capture do
      xhtml_strict do
        # awesome standards-compliant stuff here
      end
    end
  end

This is a -Markaby– Camping bug.

in which we explore attr_writer

Attribute writers are nice, because they can do things other than just assign attributes. If we spelunk some, though, a troll pops up and startles us with unexpected return values. Let’s explore.

into the mines

irb:0> def jumping= q
irb:1>   puts "jumped #{q}"
irb:2>   "jumping is so cool"
irb:3> end
=> nil

If we invoke the method as explicitly as possible, it behaves normally:

irb:4> self.send(:jumping=, "around")
jumped around
=> "jumping is so cool"

If we invoke it via the dot, but without send, the method gets called, but the syntax parser delivers the parameter as the return value. No local variable gets assigned (jumping is still undefined), but the actual return value is lost:

irb:5> self.jumping=("around")
jumped around
=> "around"

If we invoke the method “bare”, the = syntax triggers Ruby’s local variable assignment routine. Our method is never called:

irb:6> jumping=("around")
=> "around"

Watch out for those stalagtites.

activerecord is always relevant

This seems to be why assigning to a field via field= "something" in an ActiveRecord callback doesn’t do anything. It assigns a local variable and never actually calls the accessor method.

@field= "something" doesn’t work either, for a similar reason. Even though you can access fields with instance variables, the accessor methods don’t get called on assignment, so the new value never finds its way back to the hash used to update the database.

make false be true

Let’s be evil, and fake out false with some metaprogramming. We will do this by using method_missing to delegate identity methods to another object, namely, true.

meta meta meta

In Ruby there’s only one hard-coded false object. You can’t FalseClass.new to make a new, clean one. Even if you could, false’s singleton class (also known as metaclass) is strangely identical to its regular class:

irb(main):001:0> FalseClass == (class << false; self; end)
=> true

This is unlike Fixnum or Symbol, which don’t have singleton classes at all (see here for why).

There’s no way to retrieve regular false functionality after you do this, as far as I know.

even more meta meta

Anyone know why metametaclasses inherit the methods of metaclasses?

irb(main):001:0> require 'rubygems'; require 'metaid' #=> true
irb(main):002:0> a = "something" #=> "something"
irb(main):003:0> a.metaclass.instance_eval("def ok; true; end") #=> nil
irb(main):004:0> a.ok #=> NoMethodError
irb(main):005:0> a.class.ok #=> NoMethodError
irb(main):006:0> a.metaclass.ok #=> true
irb(main):007:0> a.metaclass.metaclass.ok #=> true

That doesn’t seem right. Shouldn’t the last one be NoMethodError?

postscript, which is not meta at all

Bee is on RedHanded today; hooray.

sti support in has_many_polymorphs

At the request of Kevin Marsh, the polymorphs plugin now supports single-table inheritance.

example

the sti base class
class Person < ActiveRecord::Base
end
sti children
class Hipster < Person
end

class Groupie < Person
end
the collection parent class
class Concert < ActiveRecord::Base
  has_many_polymorphs :concert_goers,
      :from => [:hipsters, :groupies]
end

Then you can do a_concert.concert_goers, a_concert.groupies, etc., and everything will work.

download

Get it here.

redirect aliased hosts to a single canonical host with apache

We have a new Apache 2.2/mongrel server set up, and wanted people who type in host.com to be redirected to http://www.host.com, instead of merely aliasing all the same content and confusing Google. This is an obvious thing, but I still had trouble finding a straightforward how-to, so here it is.

old, bad way

All of this takes place in your httpd.conf, or your vhost.conf, or whatever file refers to your host in question in whatever of the infinite Apache configuration possibilities you have managed to implement.

Here’s what you probably had before:

<VirtualHost *:80>
  ServerName www.site.com
  ServerAlias site.com blog.site.com

  <Proxy balancer://site_com_cluster>
    BalancerMember http://127.0.0.1:3050
# etc...

new, awesome way

If you want a specific redirect type (303, permanent, etc.), you can put it after the Redirect keyword. But this way works fine, and the word on the street is that Google treats every type as a 301 anyway, so it doesn’t really matter what you put.

Here’s what you should have:

<VirtualHost *:80>
  ServerName site.com
  ServerAlias blog.site.com
  Redirect / http://www.site.com/
</VirtualHost>

<VirtualHost *:80>
  ServerName www.site.com

  <Proxy balancer://site_com_cluster>
    BalancerMember http://127.0.0.1:3050
# etc...

the end

Shed a tear for lighttpd; we were driven away by a bizarre rendering bug in Camping/fastcgi (extra linebreak before every output body, even with x-sendfile). Unfortunately Apache/mongrel does not seem super-stable at this point.

Deployment… sucks.

how to publish feeds with resource_feeder

The resource_feeder plugin, along with simply_helpful, recently snuck into edge Rails, courtesy of DHH. They’re nice. resource_feeder lets a controller return a well-formed RSS or Atom feed for any array of ActiveRecord instances. I don’t really know what simply_helpful does, but resource_feeder requires it, so whatever.

install the plugins

First, check out the plugins as svn:externals so you can keep up to date easily.

Go to whatever folder you keep Camping plugins in, or make a folder, and run:

svn propedit svn:externals .

Add these lines to the file:

resource_feeder http://dev.rubyonrails.org/svn/rails/plugins/resource_feeder
simply_helpful http://dev.rubyonrails.org/svn/rails/plugins/simply_helpful

Now, svn up. You will have the plugins.

require them

Now your Camping application needs to load them. One way to do that is this:

%w[simply_helpful resource_feeder].each do |plugin|
  $LOAD_PATH.unshift("plugins/#{plugin}/lib")
  require plugin
  $LOAD_PATH.shift
end

You also need to add a few support methods somewhere in your application (or in an external require), since this is not Rails.

require 'rubygems'
require_gem 'actionpack'
require_gem 'activesupport'

module ResourceFeeder::Atom
  #emulate Rail's request object with a singleton method on @env
  def request
    class << @env
      def host_with_port
        self['HTTP_HOST']
      end
    end
    @env
  end
end

class String
  def pluralize
      Inflector.pluralize(self)
  end
end

That it. It’s all Ruby in the end, so we can make resource_feeder think it’s operating in a Rails environment even though that’s totally a lie.

usage example

Now we can make a feed controller. Assume our Camping app is named Awesome, and that we want to use Atom and not RSS:

module Awesome::Controllers

  class FeedMe
    include ResourceFeeder::Atom

    def get
      @things_to_eat = ThingsToEat.find(:all, :limit => 15, :order => "created_at DESC")
      @things_to_eat = [ThingsToEat.new(:title=>"No results", :id => 0)] if @things_to_eat.blank?
      @headers['Content-Type'] = "application/xml"
      atom_feed_for @things_to_eat,
        {:feed => {:title => "Things to eat 4 u", # feed title
                    :ttl => 40}, # time-to-live in minutes
         :items => {:title => :title,
                    :pub_date => :created_at}}
    end

That would be it, except we also need some specially named methods to return the canonical url of the feed itself and also of the individual records.


    def awesome_models_thing_to_eats_url
       "http://www.aweso.me/" + R(ViewThingToEat)
    end

    def awesome_models_thing_to_eat_url(obj)
       "http://www.aweso.me/" + R(ViewThingToEat, obj.id)
    end

  end
end

One last thing: your models have to respond to .title and .description methods, which provide the title and body of the feed entry, respectively. Sometimes you can just point this at another field, but sometimes you will want to mess around with formatting and aggregating multiple fields into one, specially for the RSS output.

this bug juice won’t come out of my shirt

In order to be able to feed resource_feeder an empty array, you need to fix two small bugs so that we can make it work with new() records. Currently resource_feeder bombs out if there are no records, which we clearly don’t want if we are making a feed for a dynamic search or something like that, in which case an empty result set is perfectly valid.

Also, we will fix it externally so that we don’t run into merge conflicts in the future when the plugin gets updated.

First, the new record must have an id, even if it’s completely bogus, or resource_feeder complains. So we assign it 0, as above.

Second, resource_feeder likes to retrieve the xmlschema of things even if they are nil. So we will give nil its own special xmlschema:

class NilClass
  def xmlschema; ""; end
end

Of course you knew to do that already :) .

postcript

I’d like to use Remember the Milk as my scheduler now that I don’t have a corporate job anymore. But I want events to find their way wirelessly onto my Treo’s calendar and there isn’t a way to do that. I never use that Palm Desktop thing.

I realize I am probably like one of three people in the universe who would want this.

rails security note

Rails Ticket 4339 suggests that there is a denial-of-service vulnerability because of the way the Rails request handler instantiates a symbol for every incoming request method. This is technically true, but turns out not to matter, as seen below.

help i’m allocated and i can’t get released

There are only a few valid HTTP request methods: GET, POST, PUT, etc. But Rails creates the symbol before it figures out what to do about it. If you make a FOO /index.html HTTP/1.1 request, Rails will instantiate a :foo symbol. As you know each unique symbol requires ~60 bytes of memory in the Ruby intepreter and is never garbage collected. The idea is that by spamming the server with lots of bogus request methods you can exhaust its memory space.

whatever man

It’s easy to write a script to exploit this with a small change to rfuzz. I ran such a script against a localhost-mounted generic Rails app (the ever-present cookbook example). The test environment was a dual-core Intel Mac, one mongrel process, production environment, and terminal output redirected to /dev/null. I increased the number of attempted simultaneous requests until the mongrel thread reached 100% cpu usage.

Rails memory usage at start-up:
time real memory virtual memory
7:54am 21.04mb 51.24mb

After one hour of fuzzing:
8:56am 37.5mb 69.87mb

I should have counted how many requests were made, but I didn’t, for the sake of speed. We can guess, though. 35.09mb / 60b = 613,242 maximum requests.

The upshot of this is that a successful exploit of this issue entails a fierce regular-style denial-of-service attack. On a real network, the sheer number of requests will cause more issues than the possibility of eventually crashing some mongrels or fastcgi processes. So basically, it doesn’t matter.

silly strings

Also, this will be fixed completely with the new symbol handling in Ruby 1.9, since then Symbol will go out of scope and get garbage-collected like any old String.

make camping connect to mysql

By default, Camping connects to a sqlite database in an undisclosed location, similar to Dick Cheney. This confuses practically everybody. Here’s how to make your application use MySQL (or anything you have a Ruby binding for) when you are ready to move it to production.

update

Make sure to read the comments section.

preliminaries

You can’t change the database connection as long as you are using the camping command line tool. You have to bootstrap your app yourself.

This means that you have to be able to start it with ruby myapp.rb, because camping myapp.rb will never pay attention to your special configuration and continue to use a sqlite database. This database will either be the file .camping.db in your home folder on Unix-like systems, or the file camping.db in /Documents and Settings/{current_user}/Application Data/ on Windows, if I remember correctly.

with mongrel

With mongrel, you can make the harness be a postamble in your application’s main file.

put this at the end of your_app.rb
if __FILE__ == $0
  require 'mongrel/camping'

  YourApp::Models::Base.establish_connection :adapter => 'mysql',
    :database => 'camping_yourapp',
    :username => 'camper',
    :password => 'secret'
  YourApp::Models::Base.logger = Logger.new('your_app.log')
  YourApp::Models::Base.threaded_connections = false
  YourApp.create # only if you have a .create method
                 # for loading the schema

  server = Mongrel::Camping::start("0.0.0.0",80,"/", YourApp)
  puts "YourApp is running at http://localhost:80/"
  server.run.join
end

Now you can instantiate mongrels by executing your_app.rb directly, and proxy them through your webserver if necessary. Add a command-line switch to change the port if you need to instantiate a bunch of mongrels all at once.

Please also read this if you need multiple instances of your application to use a single sqlite database concurrently.

with fastcgi

If you are using fastcgi, be sure to put the harness in a separate file. If you don’t, you may get very strange superclass mismatch errors.

your_app_dispatch.rb
#!/usr/local/bin/ruby
require 'rubygems'
require 'camping/fastcgi'

Camping::Models::Base.establish_connection :adapter => 'mysql',
  :database => 'camping_yourapp',
  :username => 'camper',
  :password => 'secret'
Camping::FastCGI.serve("/path/to/your_app.rb")

Then your server can use your_app_dispatch.rb as the fastcgi executable.

Notice how the above uses Camping as the module name, and not YourApp. Also, we didn’t establish a log file here for Base, but we could, similar to the mongrel example.

done

That’s all. If your Camping app connects to Postgres instead of MySQL (:adapter => 'postgresql') you are officially hardcore.