Monday, June 22, 2009

A Weak Man Begs Forgiveness

Dearest Colleagues,

I write this electronically-delivered letter to you in the hopes that you will forgive me. A terrible fate has befallen my lovely daughter V_______, and I am afrid that I now share in her misfortune. To protect you all from similarly horrible destinies, I must eschew your company this day.

It all started innocently enough, with a persistent cough. My eldest daughter would delicately hack to "clear her throat". The coughs became more frequent and urgent. She developed a fever which burned her pure, innocent forehead. As time passed, she begain to writhe in agony, delirious with dreams of God knows not what Heavenly retributions and Hellish tortures.

My dearest wife E____ conveyed her to the pediatrician's office on Sunday. Her doctor (a woman, by God! Ah, well. I know not what muse guides Science, but she must hold in favor the fairer sex) diagnosed V_______ with Swine Flu. The woman practiced her Art with confidence, perscribing the most modern treatments available.

We repaired to our home, powders and poultices in hand. The cure had barely taken hold of my dear daughter when I myself began to cough. At first, I denied the obvious. What strong man would not? Having fought in The War, my thoughts were not for my own safety or bodily condition but for that of my child.

I tell you now: Mr. Poe never wrote of horrors so small. The tiny demons that ravage my body even now defy all description, and to burden you with reports of the agony they cause does not merit further thought.

The submarine-like conditions of the Juicyorange office being the breeding grounds for animalcules it is, it is best that I remain far, far away. This is why I have decided to remain in an entirely different State of the Union. My sorrow at not joining you is only mitigated by the knowledge that you will not be exposed to the fiends that wrack my body.

In the fervent hope that this missive is not rerouted to your Spam folder,

With Deepest Respect,
James J. Menard
Babbage Engine Servitor

Saturday, April 18, 2009

Using will_paginate in Rails to Submit an AJAX Form

I'm using will_paginate for a Rails project. It works great, but I wanted to use it for a sidebar that performs searches and displays the results. It's not too hard to write a custom link renderer that will submit the will_paginate link using Ajax, but my situation was a bit different: I wanted will_paginate to inject the page number into the search form and submit the form using Ajax.

Here's what I ended up doing: writing a link renderer that calls a JavaScript function that injects the page number into the form and calls the form's onsubmit function (which then submits the form using Ajax).

The code. First the Haml, which is in a partial that gets rendered on a number of pages:

%h1 Asset Search
- form_remote_tag(:html => {:id => 'ssform', :name => 'ssform'}, ...) do
  %input{:type => 'hidden', :id => 'sspage', :name => 'sspage'}
  / ...
  %input{:type => 'submit', :value => 'Search'}

  - paginated_section(sidebar_assets, :class => 'rhs_pagination', :renderer => SidebarSearchLinkRenderer) do
    %table.list
    / ...

Next, the will_paginate renderer, in the file app/helpers/sidebar_search_link_renderer.rb. The "#" is the href value, and the onclick attribute calls a JavaScript funtion.

class SidebarSearchLinkRenderer < WillPaginate::LinkRenderer

  def page_link(page, text, attributes = {})
    @template.link_to text, '#', attributes.merge(:onclick => "return sidebar_search(#{page});")
  end

end

Finally, the JavaScript function which I put in public/javascripts/application.js:

function sidebar_search(page) {
  $('sspage').value = page;
  document.forms['ssform'].onsubmit();
  return false;
}

Wednesday, February 25, 2009

Time Keeps On Slipping, So Freeze It

A friend of a friend has trouble testing timestamps. I tried replying there, but don't think my answer got through so here it is.

The short answer: all time operations should not use the wall clock, but a system time object that you create. In production, the system time object returns wall clock time. In testing, you replace/mock/modify it so that it uses the time you give it. In other words, it becomes a clock that you can freeze and re-set whenever you need to.

Here is an example SystemClock class in Ruby. All of your classes should use this class to get the current date and time instead of using the Time or Date classes directly.

# Keeper of the current system time. This class is sometimes overridden during
# testing to return different times.
class SystemClock

  def self.date
    Date.today
  end

  def self.time
    Time.new
  end

end

Here is the mock used during testing. You can set the time using this mock object and it will not change until it is set again.

# This mock lets you set the system date and time during testing. For example,
# to set the date to tomorrow,
#
# A few examples of use:
#
#   SystemClock.date = Date.today + 1 # Time also set: to tomorrow 9:10:11 am
#   SystemClock.date = nil            # Go back to using system date and time
#   SystemClock.date = Date.civil(2006, 4, 15)
#   SystemClock.time = Time.local(2006, 3, 2, 8, 42, 42) # Date changed, too
#
# When you set the date, the time is set to 9:10:11 am of the same day, local
# time. When you set the time, the date is set to the same day.

require 'models/system_clock'

class SystemClock

  @mock_date = nil
  @mock_time = nil

  def self.reset
    @mock_date = nil
    @mock_time = nil
  end

  def self.date=(date)
    @mock_date = date
    @mock_time = date == nil ? nil :
      Time.local(date.year, date.month, date.day, 9, 10, 11)
  end

  def self.time=(time)
    @mock_time = time
    @mock_date = time == nil ? nil :
      Date.civil(time.year, time.month, time.day)
  end

  def self.date
    @mock_date || Date.today
  end

  def self.time
    @mock_time || Time.new
  end

end

So in your normal production code you get the current time by calling SystemClock.time instead of Time.now. In your test code, you'd do something like this:

# Make sure to include the mock class
SystemClock.time = Time.now # Or any arbitrary time
thing = Thing.new           # Uses SystemClock to set created_at attribute
assert_equal SystemClock.time, thing.created_at

Wednesday, January 28, 2009

text2midi App Uses midilib

Gabriel G has released text2midi, a Rails app that converts—wait for it—text that you paste into the Web page to MIDI, and plays it for you and lets you download the MIDI.

text2midi uses midilib, my Ruby MIDI manipulation library.

Go, Gabriel!

Thursday, January 15, 2009

10gen Shifting Emphasis to the Database

10gen has announced that it is Shifting Emphasis to the Database. They decided to focus on the Mongo database after finding that there was much more interest in it than in their Babble cloud computing app server.

Babble is still alive, though. It has a new web site and has shifted to the Apache license.

This may all be good news for 10gen, but it's bad news for me. Since I was working on bringing Ruby and Rails to Babble, I and a number of other fine engineers at 10gen have been let go. It hurts, but I've had a blast and learned a lot at 10gen.

For the next few weeks, I'm going to be finishing up work on the Ruby Mongo driver, MongoRecord (an ActiveRecord-like framework for Mongo that is independent of Rails), and Rails ActiveRecord support for Mongo.

Oh, yeah: here's my resume.

Friday, December 26, 2008

Doing Homework With a Wii

My younger daughter just said to me, "Dad, I know how to cheat at homework with the Wii." I asked what whe meant. She said that if you had to alphabetize a list of words (which she sometimes does for homework), you could create a Mii (an in-game avatar on the Wii) for each word, and give it that word as the name. Then use the game's "sort by name" feature to display all the Miis in alphabetical order. Voila!

I'm so proud of her.

Tuesday, December 23, 2008

Rails Without ActiveRecord

When we announced Rails support for the 10gen cloud computing platform, we said that ActiveRecord support was not yet included. This led naturally to the question, "what the heck good is that?"

Actually, you can run Rails without ActiveRecord just fine. Rails has been designed that way. For an example app that uses the Mongo database, see my 10gen-rorob Rails app. You might want to use the branch that uses Rails 2.2.2 instead, because the master branch uses Rails 1.2.6 and the 2.2.2 branch also has a few more features.

On the other hand, internally we have a subset of ActiveRecord working already and are working on supporting as much of it as makes sense with a non-relational database. I already have the Rails app from "Agile Web Development With Rails" running unchanged.

On the gripping hand, you can run the 10gen cloud yourself, in which case you can run it with MySQL or any other relational database. In that case, you can use ActiveRecord unchanged.