26 February, 2008

Pesto Caesar Salad

I've been experimenting with salad dressings for a head of Romaine I got recently, and finally came up with the following. For one large salad:

  • 5 leaves Romaine (I leave the rib in; if you don't like it, use 7 rib-less leaves)
  • 1½-2 tsp lemon juice
  • 2-3 tsp olive oil
  • 8-10 drops Worcestershire sauce (instead of anchovies!)
  • a few (ok, several for me) turns of fresh-ground black pepper
  • 1 tsp pesto (I use Classico if I haven't recently made a fresh batch)
  • about 2 Tbsp. freshly shaved Parmesan (see below)
  • ¼ tsp onion powder or some finely minced white onion
  1. Put everything but the lettuce and the cheese into a large bowl.
  2. Mix vigorously
  3. Tear up the lettuce and toss in the bowl
  4. Shred the Parmesan. You can use a vegetable peeler, but I find the best method is to use the back of a paring knife to peel off shreds. Don't use a cheese grater — at least, not a fine one. The goal is about 1cm2 shreds.
  5. Eat tastiness

19 February, 2008

with_modules_unavailable and some Module helpers

I've been trying to test a new Rails plugin (stay tuned!), and I've found that I need to have certain Modules unavailable. This is useful when you need to test that missing Modules will raise errors or if you have behavior conditional on what gems are installed.

I wanted something like:

...
with_modules_unavailable(Foo, Bar::Baz::Goo) do
  test stuff
end

To get this working, just put the following in your test_helper.rb (this version requires Inflector):

Module.class_eval do
  
  def defining_module
    chain = self.to_s.split(/\:\:/)
    chain.pop
    if chain.empty?
      Object
    else
      chain.join('::').constantize
    end
  end
  
  def simple_name
    self.to_s.split(/\:\:/).pop
  end
  
end

Test::Unit::TestCase.class_eval do

  def with_modules_unavailable(*mods, &block)
    Thread.exclusive do
      mods.each do |mod|
        mod.defining_module.send :remove_const, mod.simple_name.to_sym
      end
      yield block
      mods.reverse.each do |mod|
        mod.defining_module.send :const_set, mod.simple_name.to_sym, mod
      end
    end
  end
  
end

You can test that it works with:

def test_with_modules_unavailable
  with_modules_unavailable(ActiveRecord::Base) do
    assert_raise(NameError) { ::ActiveRecord::Base.class_eval { } }
  end
  assert_nothing_raised { ::ActiveRecord::Base.class_eval { } }
end

17 February, 2008

Updates to active_support_hacks

I've made a couple of updates to my active_support_hacks

First: DateRange#include?(other)

This accepts a Date, Time or another TimeRange. It assumes inclusive range ends

4.days.ago.until(1.day.ago).include?(2.days.ago)                    #  => true
4.days.ago.until(1.day.ago).include?(3.days.ago.until(2.days.ago)   #  => true
5.hours.from_now.until(10.hours.from_now).include?(6.days.from_now) #  => false

Second: Pretty Date Formatters

These generate human-readable times and dates, like "9 hours from now" and "earlier this week"

f = ActiveSupport::CoreExtensions::Time::PrettyNumericDateFormatter.new
f.call(5.hours.ago)       # => "5 hours ago"
f.call(37.days.ago)       # => "1 month ago"

f = ActiveSupport::CoreExtensions::Time::PrettySimpleDateFormatter.new
f.call(5.hours.ago)       # => "earlier today"
f.call(55.days.from_now)  # => "later this year"

You can load these into the ActiveSupport formatting code like so:

require 'active_support/core_ext/time/pretty_numeric_date_formatter'
require 'active_support/core_ext/time/pretty_simple_date_formatter'

#each formatter must either respond to #call(Date) and #call(Time) or be a String for strftime

formatters = {
  :pretty_numeric => ActiveSupport::CoreExtensions::Time::PrettyNumericDateFormatter.new,
  :pretty_simple => ActiveSupport::CoreExtensions::Time::PrettySimpleDateFormatter.new
}
formatters[:pretty] = formatters[:pretty_numeric]
formatters[:default] = formatters[:pretty]

ActiveSupport::CoreExtensions::Time::Conversions::DATE_FORMATS.merge!(formatters)
ActiveSupport::CoreExtensions::Date::Conversions::DATE_FORMATS.merge!(formatters)

I have that in my Rails app in /config/initializers/date_formats.rb. If you don't want to override the default formatting of dates, don't include the formatters[:default] = formatters[:pretty] line. By including a [:default] in the hash, all ActionViews will automatically use this formatter.

16 February, 2008

Frustrated with Media

I'm really just tired of it all.

I've been trying all evening to watch some old TV shows on my nice Netflix unlimited subscription. Problem: I've got a Mac. I've got Parallels, and occasionally it'll run Windows, but mostly it just crashes. I'm sick and tired of Microsoft writing code that crashes constantly.

I'm sick and tired of Apple being greedy and refusing to license their DRM. Hey, Apple: you do realize that licensing it out to Netflix makes your products more valuable, right? You're sure as hell not going to get me to use your iTunes Movie Rental service; it's a load of crap. Watch one movie once for $4 or watch unlimited movies unlimited times for $15/month? No question. Apple makes a better product than Microsoft, but they're just as obnoxious about it.

I'm sick and tired of the music and movie industries treating me like a criminal. Yeah, I used to download music. You know what happened? I spent $300 in the last couple years on CDs. I spent $200 on concerts. That's $500 on music, or about $490 more than I spent in the 20 years before I started downloading. I've since stopped downloading illegally and my purchases have plummeted. I just can't find the good music any more.

So, in conclusion, to Steve Jobs, Steve Ballmer, Harry Sloan, Philippe Dauman, Sumner Redstone, Reyer Maker, and the rest of your ilk: Stick it up your fat, greasy, selfish asses

14 February, 2008

ActiveSupport additions: TimeRange and Distance

I've created some utility classes that I use in many of my projects. I've broken them out into a Rails plugin, but they should really be part of ActiveSupport.

First: TimeRange

t = 5.minutes.ago.until(1.second.ago)
s = 10.minutes.ago.until(3.minutes.ago)
overlap = t & s
overlap.start_time # => 5.minutes.ago
overlap.end_time   # => 3.minutes.ago

Second: Distance

4.miles + 6.miles  # => 10.miles
5.yards.to_feet    # => 15.feet
12.miles.in_km     # => 19.312128.km
12.miles.as_km     # => 19.312128.km
2.meters > 2.yards # => true

Eventually, I plan on moving some of GeoKit into some sort of ActiveRecord::DistanceSupport in the plugin.

If you want the plugin, you can get it at https://svn.u-presence.com/svn/plugins/active_support_hacks/ (Username guest, no password). I'd love any other suggestions or comments on the utilities.

10 February, 2008

Reliable Software in 3 Simple Steps

(or: How I Realized Intro Economics Applies to Software Engineering)

The Rules:

  1. Hire a small number of really good programmers
  2. Hire one Information Assurance (IA) superstar
  3. Hire as many testers as you can without regard to past performance

The Rationale:

  • Software development is largely a weakest-link game. If you hire one crappy programmer, he'll write crappy code that the stars have to fix. They'll be slower and more resentful. Therefore, only hire really good programmers.
  • Security and reliability measures are a best-effort game. You don't need a whole slew of IA stars to see the big picture. I'm a believer in the idea of "more heads..." thinking, so I might hire two experts if I could afford it, but this position has rapidly diminishing returns
  • Bug fixing is a sum-of-efforts game. The more eyeballs the better. This is particularly true because even really good developers make assumptions about users. Having average-Joe people doing testing will get the developers good feedback early in the life-cycle

Finally, the credits:

09 February, 2008

Hacking Rubygems' #require, #require_gem, #gem

I have some old gems in my latest Rails app, and Rubygems has changed its syntax since those gems were built. The gems have require_gem in them, but the newest version of Rubygems doesn't add that command to Kernel, so you get NoMethodErrors when the gem loads. This simple hack in config/environment.rb will fix it:

# hack rubygem's change to #require:
Kernel.class_eval do
  def require_gem(*args)
    gem *args
  end
end
I have it right before require File.join(File.dirname(__FILE__), 'boot') in case my config/environments/xxx.rb loads an old gem.

The Problem With Rails Resource Routes

Today I show how to easily solve an incredibly annoying (but small) problem in Rails Routing for Resources.

For my past few Rails projects, I've created a module called ModelLoader that I include in my Controllers to load requested Models from the database. For each of my models, I have a #load_xxx and a require_xxx!. For an app that contains People and Tickets, the model loader would look something like this:

module Utilities
  module Controller
    module ModelLoader
      
      def load_person
        safe_load :@person, Person, :person_id
      end

      def require_person!
        require_exists! :@person, Person, :person_id
      end

      def load_ticket
        safe_load :@ticket, Ticket, :ticket_id
      end

      def require_ticket!
        require_exists! :@ticket, Ticket, :ticket_id
      end

      private

      def safe_load(variable_name, klass, parameter_name)
        begin
          instance_variable_set(variable_name, klass.find(params[parameter_name]))
        rescue ActiveRecord::RecordNotFound => e
          nil #swallow it; must have a statement here for coverage to see the line
        end
      end
      
      def require_exists!(variable_name, klass, parameter_name)
        raise error_for(klass, parameter_name) unless instance_variable_get(variable_name)
      end
      
      def error_for(klass, parameter_name)
        if params[parameter_name]
          msg = "Could not find #{klass} with id #{params[parameter_name]}"
        else
          msg = "Parameter #{parameter_name} is required"
        end
        ActiveRecord::RecordNotFound.new(msg)
      end
      
    end
  end
end

In my Controllers, I just do something like append_before_filter :load_person, :only => [:foo, :bar]

This is almost perfect. The problem is that some of my actions are accessible via more than one route. In particular, a nested- and non-nested version of the same resource:

  map.resources :people do |people|
    people.resources :tickets
  end
  map.resources :tickets
gives routes like
  • /people/:id/edit
  • /people/:person_id/tickets/:id
  • /tickets/:id

That means that somestimes :id is a Person#id and sometimes it's a Ticket#id. This wreaks havoc on my model loader. (It's also a problem for Sutto's similar, but more elegant solution.)

The solution is simple:

module ActionController
  module Resources
    class Resource
      def member_path
        @member_path ||= "#{path}/:#{singular}_id"
      end
    end
  end
end

Now all route segments have the class name in them:
  • /people/:person_id/edit
  • /people/:person_id/tickets/:ticket_id
  • /tickets/:ticket_id