Jul 292007
 

Since I work for a consulting company, I travel for a living. As a result, I’ve taken quite an interest in flight statistics. For example, how often is my flight on time? What kind of delays can I expect? Has it gotten worse over time?

To answer these questions, I wrote a quick rails app using the publicly available data at the Bureau of Transportation Statistics. This app graphs the delay from each flight on a bar graph over time. I only loaded United flights to save space since the data can get quite large.

Check out the app here: Flight Delay Information. Just enter your flight number, the airport codes, and the day of the week. It is not the prettiest site, but I find the results really interesting.

Here is an example graph of a Thursday night flight from Newark to Chicago:

I hope to expand this app at some point with more graphs and information. If you’d like to help, please let me know.

Jul 202007
 

When rendering a partial with a collection, it can be useful to know the index of the item currently being rendered. It turns out that rails provides this with an undocumented counter.

For example, say we want to render a collection of people who won a contest. We might have a template line like this:

<%= render :partial => "person", :collection => @winners %>

The person partial might look like:

<div>Name: <%= person.name %></div>

Now, let’s say we want to add the person’s rank along with his/her name. Rails provides a local variable called ”#{partial_name}_counter” to provide the index of the currently rendered item in the collection. In our case, the variable is called person_counter. The counter starts at 0, so we’ll add 1 to it for a human readable ranking.

Now, our person partial looks like:

<div>
  Rank: <%= person_counter + 1 %>
  Name: <%= person.name %>
</div>

And the output is:

Rank: 1 Name: Peter
Rank: 2 Name: Paul
Rank: 3 Name: Mary

Update (6/20/08): Jan points out in the comments that the counter now starts at 1 in Rails 2.1.

Jul 022007
 

The simplest way to read a line of input is to use the gets method. However, if you try to use the arrow keys when entering text, you will see that this merely types random ^[[D strings instead of moving the cursor:

s = gets
press the left arrow^[[D
=> "press the left arrow\e[D\n"

To get around this, use readline instead:

> require 'readline'
> s = Readline.readline
now you can use the arrow keys
=> "now you can use the arrow keys"

Readline can take the prompt as an argument:

> s = Readline.readline("enter some text: ")
enter some text: here is some text
=> "here is some text"

Readline also has an optional second argument. This is a boolean which specifies whether to keep history across readline method calls. The default is false, but if you pass in true, you can use the up arrow to retrieve input from previous readline calls:

> s = Readline.readline("enter some text: ", true)
enter some text: here is some text
=> "here is some text"
> s = Readline.readline("now press the up arrow: ", true)
now press the up arrow: here is some text
=> "here is some text"
Jul 022007
 

I noticed an interesting quirk of the alias method. Normally, if we call super from a method, it calls a method with the same name on the super class. However, if we alias a method and then call the new name, it calls the old super method.

For example, lets define a parent and child class. Both have a talk method:

class Parent
  def talk
    puts "Parent is talking"
  end
end
 
class Child < Parent
  def talk
    super
  end
end

Calling talk on the child calls talk on the Parent:

> c = Child.new
> c.talk
Parent is talking
=> nil

Now, we can modify Child and alias talk to shout:

Child.class_eval do
  alias :shout :talk
end

Now, here is the strange part. When we call shout on the child, it still calls talk on the parent, even though the method has a different name:

> c = Child.new
> c.shout
Parent is talking
=> nil

It appears that when we alias a method, it merely copies the super pointer, rather than resolving it on the fly.

However, if we unbind and rebind shout, then the call fails in the expected way:

> shout = Child.instance_method :shout
> shout.bind(c).call
NoMethodError: super: no superclass method `shout'
        from (irb):33:in `talk'
Jul 022007
 

One of the things that I’m still getting used to in ruby is that there are many different ways to accomplish the same task.

The problem: We want our class to call an expensive method only once per instance.

Solution 1: ||= operator

We use an instance variable along with the ||= operator:

class Product
  def name
    @name ||= ExternalService.expensive_call_for_name(@id)
  end
end

Now, we can do

p = Product.new
p.name
p.name

The ExternalService.expensive_call_for_name is called only once, even though we call name twice.

There is a subtle bug in this solution, however. If the expensive_call_for_name returns nil, then the expensive call will be made again due to the way ||= works. This is not the desired behavior. We want to cache the value of name, even if it is nil.

Solution 2: once method

We use a once method similar to date.rb:

class Product
 
  def once(*ids) # :nodoc:
    for id in ids
      module_eval <<-"end;"
        alias_method :__#{id.to_i}__, :#{id.to_s}
        private :__#{id.to_i}__
        def #{id.to_s}(*args, &#38;block)
          (@__#{id.to_i}__ ||= [__#{id.to_i}__(*args, &#38;block)])[0]
        end
      end;
    end
  end
 
  def name
    ExternalService.expensive_call_for_name(@id)
  end
  once :name
end

This solution does not have the nil limitation of solution 1. However, it is a bit heavy weight for one time problems. If there are multiple methods that should only be called once, then I would recommend this approach. If not, I believe there are simpler solutions.

Solution 3: instance_variables check

We can modify solution 1 so it works in the nil case. We will remove the use of ||= and replace it with an instance variable check:

class Product
  def name
    if !self.instance_variables.include? "@name"
      @name = ExternalService.expensive_call_for_name(@id)
    end
    @name
  end
end

This solution works as well, but it feels a bit hackish to me. I don’t love the idea of searching the instance variables to see if something is defined. Let’s try one more solution.

Solution 4: metaprogramming

We will use a bit of metaprogramming:

class Product
  def name
    expensive_name = ExternalService.expensive_call_for_name(@id)
      class<<self;self;end.class_eval do
        define_method :name, lambda {expensive_name}
      end
    expensive_name
  end
end

In this case, we add a method with the same name to the singleton class of the instance of Product when name is called. Now, the next time name is called on our instance, it will find the method on the singleton class first which uses the cached value.

If you are doing any metaprogramming, it is useful to pull

class<<self;self;end

into its own method on Object.

class Object
  def singleton_class
    class<<self;self;end
  end
end
 
class Product
  def name
    expensive_name = ExternalService.expensive_call_for_name(@id)
    singleton_class.class_eval do
      define_method :name, lambda {expensive_name}
    end
    expensive_name
  end
end