Pretty Printing Seconds in Ruby

October 16th, 2007

I must have written various versions of this code 20 times so far in my career, but never so quickly and cleanly. I love ruby.

    min, sec = sec / 60, sec % 60
    hour, min = min / 60, min % 60
    day, hour = hour / 24, hour % 24
    week, day = day / 7, day % 7
 
    [
      week > 0 ? "#{week} weeks" : nil,
      day > 0 ? "#{day} days" : nil,
      hour > 0 ? "#{hour} hours" : nil,
      min > 0 ? "#{min} minutes" : nil,
      sec > 0 ? "#{sec} seconds" : nil
    ].compact.join(", ")

The really cool thing is the multiple assignment that ruby lets you do. I haven’t used it for something exactly like this before, but I do use it often and it’s really nice.

I feel like there should be a better way to do the bottom half of this, but this is pretty damn readable, I think. You might have to have a little rubyFU to remember what compact does – which is get rid of the nils.

Anyway, this was about 15 minutes of work (and I did it test first).

Customizing Markaby – Language Level Refactorings

September 26th, 2007

It’s very easy to call out to methods in markaby, but it’d be nice if you could actually customize the dsl as well.

For example, on many of our pages we have a bottom row that has buttons that look a certain way. So on every page, we have :

  table(:width => "100%") do
    tr do
      td.left do
        previous_button
        first_button
      end
      td.center do
        print
      end
      td.right do
        next_button
        last_button
      end
    end
  end

The code for the actual buttons changes, and after a few tries to extract the whole thing into a single method, we gave up. Our efforts had made it harder to read, not easier. There was always just a little too much variance, and it didn’t feel right.

What we really wanted to write was :

  last_row do
    column do
      previous_button
      first_button
    end
    column do
      print
    end
    column do
      next_button
      last_button
    end
  end

This lets the buttons that change all stay in the view, and gets rid of the skeleton and positional stuff that doesn’t change. Furthermore, it’s DRY and puts all that positional logic in one place instead of scattered across 20 views.

How to do this?

I wrote a test (in RSpec) that looks something like :

describe ApplicationHelper do
  it "should generate a table from a buttons method" do
    last_row(:columns => 2) do
      column do
        "foo"
      end
      column do
        "bar"
      end
    end.should == '<table width="100%"><tr>' +
                    '<td class="left">foo</td>' + 
                    '<td class="right">bar</td>' +
                  '</tr></table>'
  end
end

After a bunch of fiddling and poking around, I finally made the test (and a couple others) pass with this code in my ApplicationHelper :

def last_row(options, &block)
  markaby do
    table(:width => "100%") do
      tr do
        LastRowContext.new(self, options[:columns]).
                                  instance_eval(&block)
      end
    end
  end
end
 
class LastRowContext
  def initialize(markaby, columns)
    @markaby, @column_count, @column_index = 
                            markaby, columns, 0
  end
 
  def column(&block)
    alignment = case @column_index += 1
    when 1 : :left
    when @column_count : :right
    else :center
    end
 
    @markaby.instance_eval do
      td(:class => alignment, &block)
    end
  end
end

I’m sure this could get cleaned up more; this was the work of less than an hour. In particular, if you did this often, you could extract a common MarkabyContext superclass that had some convenience methods. The point is, this is really easy to do, and we shouldn’t be scared to try “Language level refactorings” like this.

Daemonizing a Ruby Script in Rails

September 20th, 2007

This took way longer than it should have, so I thought I’d jot down what I did so it might take less time next time.

1. Install the daemons gem

sudo gem install daemons

2. Presumably you have a script that looks something like this already

#!/usr/bin/env ruby
require File.dirname(__FILE__) + "/. ./config/environment"
 
SchedulerDaemon.new.run

This is a scheduler script that lives in the scripts directory of a rails project.

3. You’re going to take this code and wrap it in daemon stuff, like so

#!/usr/bin/env ruby
RAILS_ROOT = File.expand_path(File.dirname(__FILE__) + '/. .')
 
require 'rubygems'
gem 'daemons'
require 'daemons'
 
Daemons.run_proc("scheduler", 
                 :log_output => true, 
                 :dir_mode => :normal, 
                 :dir => "#{RAILS_ROOT}/log") do
  require File.join(RAILS_ROOT, "config/environment")
  SchedulerDaemon.new.run
end

The really hard part for me was debugging it. Which meant figuring out how to get logging going. With this code, you can just tail “log/scheduler.output” and see the contents of any puts in the code. Once I started doing that, everything else was easy.

RAILS_ROOT has to be set first, because daemons changes the current working directory. Also, I use the log dir, because it’s shared by capistrano, so I can deploy, then stop / start my scheduler and not worry about losing my first pid file – plus that’s where logs are supposed to go.

4. To make my life just a little easier, I also added some debugging statements

Daemons.run_proc("scheduler", 
                 :log_output => true, 
                 :dir_mode => :normal, 
                 :dir => "#{RAILS_ROOT}/log") do
  begin
    require File.join(RAILS_ROOT, "config/environment")
 
    puts "starting scheduler at #{Time.now} for #{RAILS_ENV}"
    SchedulerDaemon.new.run
 
  ensure
    puts "ending scheduler at #{Time.now}"
  end
end

This was actually pretty easy, and next time it will take 5 minutes to create a daemon. Nice gem.

More Fun with Times, Mocks, and Closures

May 24th, 2007

I solved a complex problem in cruise today with some non-trivial mocking. Check this one out :

I needed to test that every x amount of time, we do a clean checkout. It can be every 6 hours, 2 days, whatever. How do you test this?

Well, maybe you could mock the time.

Time.stubs(:now).returns(Time.now + 2.hours)

That almost works, except that the code works by touching a file. And touch doesn’t use Time.now. Now at this point, we could go crazy, and mock FileUtils.touch, of course that means we’ll also have to mock File.exists? and then what are we really testing?

Instead, I used Mocha to temporarily replace FileUtils.touch with my own implementation that acts like the original but uses my own value of time. It looks like this so far.

  marker = sandbox.root + '/last_clean_checkout_timestamp'
 
  now = Time.now
  FileUtils.stubs(:touch).with(marker).returns(proc do
    File.open(marker, 'w') {|f| f

you’ll notice that both of these stubs are returning procs that reference now a local variable….

That’s ruby magic.

It means that I can now forget about mocks and write the rest of my test like this :

  @project.do_clean_checkout :every => 1.hour
 
  assert @project.do_clean_checkout?
  assert !@project.do_clean_checkout?
 
  now += 59.minutes
  assert !@project.do_clean_checkout?
 
  now += 2.minutes
  assert @project.do_clean_checkout?
  assert !@project.do_clean_checkout?
 
  @project.do_clean_checkout :every => 2.days
  now += 1.day + 23.hours
  assert !@project.do_clean_checkout?
 
  now += 2.hours
  assert @project.do_clean_checkout?

Instead of changing the mocks several times in between each test, I can just change my local variable now. Because of closures, the mocked out methods return the new value.

Ruby is awesome (and Mocha is pretty sweet too)

Goodbye ThoughtWorks, Hello Pivotal

April 11th, 2007

My last day at ThoughtWorks was 2 weeks ago.

It’s been a long and glorious (mostly) 6 years. I’ve learned so much and met many of the most interest

Creating Change

March 30th, 2007

on a recent e-mail thread, a friend asked for advice when introducing agile. here’s a cleaned up version of my response to him.

Solve THEIR problems, don’t push your own agenda

if you go to people and tell them agile is the key, they won’t listen to you. instead, go to people and listen to their problems. propose and implement solutions to their biggest and worst one or two. check back with them and make sure they are happy (or at least happier) rinse and repeat. if something in the “agile” toolset doesn’t scratch an itch they have, then you shouldn’t be using it. be flexible in everything except your values. be compromising. if they see you give in to what they suggest, they will be more willing to give in to what you suggest.

obviously a retrospective is a great vehicle for this. if you can get the team as a group to admit to problems that they NOT YOU are worried about, then the solutions you come to will also be owned by the team.

Start with individuals, not the “team”

as an outsider, which we are as consultants, it’s very, very hard to convert an entire team to our way of thinking. it’s hard to know what they all think, and the more you push, the more it becomes YOU vs THEM. not good. it’s much better if you can identify the change agents in the team, find the connectors, the mavens, and the salespeople (see The Tipping Point ) and talk to them. it’s much easier to convince one person, one on one that you have some good ideas, and that you might be able to solve some of their problems. do this first, and instead of 1 vs 10 it becomes 2 vs 9 or even 3 vs 7, MUCH better odds. if you can do this with the most influential people, then convincing the rest of the team almost takes care of itself.

this of course works the other way around, too. the most influential people on the team can easily turn the entire team against you, so you have to make sure that you are listening to them. addressing their problems and concerns, and making them feel heard. this is of course good advice for the whole team, but it’s so much easier when it’s one person at a time, you might as well start there.

Ask for help, and dish out the credit

it’s a funny thing, but when you ask someone for help, you usually get it. and believe me, if you’re trying to introduce agile, you’ll need all the help you can get. the easiest help to get is advice that is asked for one on one. ask your boss what the social makeup of the team is. ask those teammates for advice on what the team needs. ask someone to give a part of a presentation to management.

from their perspective, it’s a great thing to be asked for help. it means the person asking respects your opinion about something. it also means that they now owe you something and you’re less likely to hesitate if you need help from them. it means that you take ownership of the thing that you helped create.

if you can manage to make people look good in exchange for your efforts, not only will you spread out the work, but you’ll win yourself friends.

Read the Secrets of Consulting

‘Nuff said.

Object Mother…in rails?

March 6th, 2007

That’s right, the circa 2000 pattern still makes sense today. Use an “object mother” as a test factory to conveniently create objects for your unit tests to bang against. It will often default values, or have different states in which to create objects. For example, you might have a new_user, as well as a new_superuser and new_guest method all of which return users.

Read about the original pattern

But why, you ask, not just use rails’ fixtures? Glad you asked.

  1. it’s more intuitive and maintainable to setup the data you need right next to the test
  2. it’s easier to create just exactly the objects you need when you have test factory methods

But don’t take my word for it. Let’s look at some code.

First off, what does this look like in ruby? I am creating an “ObjectMother” module, which I then just mixin to my tests. A test might then look like :

  include ObjectMother
 
  def test_delete_project
    project = new_project('foo')
    post :delete, :id => project.id
    assert_raise(ActiveRecord::RecordNotFound) { Tag.find(project.id)}
  end

The magic is in “new_project”. It’s an entirely pragmatic construct. If you pass it a string, it will set everything else to acceptable defaults, and use that string as a name. It looks something like this.

module ObjectMother
  def new_project(options)
    options = {:name => options} if options.is_a? String
    options[:url_name] = options[:name].gsub(/\W/, '') if !options.has_key?(:url_name)
    Project.create!(options)
  end
 
  ...
end

Here in project, it defaults the url_name (which must be unique) from the name you’ve given it. However, you could also create a more custom project by running this :

  new_project(:name => 'garage', :url_name => 'the_garage', :description => 'foo')

This is how it works for projects, but it’s only purpose in life is to make my life easier and reduce the amount of code one has to type or read. So each type of thing it creates works a little bit differently according to our needs
——
h2. A more complicated example

I was playing with ferret last week, and wrote a test that looked like this :

  def test_across_types
    project = new_project('rabbit holes')
    post = new_post(:subject => 'a rabbit has a big head')
    user = new_user(:display_name => 'rabbit head')
 
    @search.string = 'rabbit'
    assert_find [project, post, user]
 
    @search.string = 'rabbit head'
    assert_find [post, user]
  end

My thought process was something along the lines of :

  1. I want to test that my searcher works across types
  2. I need to create a project, post, and user that all have a term in them (in different places)
  3. I want to search for the term, and make sure i get all of them
  4. I want to search for a term that maybe 2 of them have and make sure I only get those 2

Writing the test for this part literally took 30 seconds, I didn’t have to go lookup the fixtures or add a new fixture for my new case. I also didn’t have to remember all the things that it takes to make a valid project or post or user.

Helpful Additions To Test::Unit

March 6th, 2007

Doing a lot of rails work, I’m getting a good feel for testing in ruby and rails. Here are some tricks / snippets I use :

assert_raises takes a string and/or a class

I want to be able to write

one = Project.new('one')
 
projects

I’ve done this a few times, but I think cruisecontrol.rb’s implementation is the most robust :

  def assert_raises(arg1 = nil, arg2 = nil)
    expected_error = arg1.is_a?(Exception) ? arg1 : nil
    expected_class = arg1.is_a?(Class) ? arg1 : nil
    expected_message = arg1.is_a?(String) ? arg1 : arg2
    begin 
      yield
      fail "expected error was not raised"
    rescue Test::Unit::AssertionFailedError
      raise
    rescue => e
      raise if e.message == "expected error was not raised"
      assert_equal(expected_error, e) if expected_error
      assert_equal(expected_class, e.class, "Unexpected error type raised") if expected_class
      assert_equal(expected_message, e.message, "Unexpected error message") if expected_message.is_a? String
      assert_matched(expected_message, e.message, "Unexpected error message") if expected_message.is_a? Regexp
    end
  end

——
h2. assert_equal_sets

In Java, I used to push things into sets and compare them when I didn’t care about order. In ruby, I sometimes use assert_equal_sets. It does a compare of two arrays independent of order. So

  assert_equal_sets [1, 3, 5], [3, 5, 1]   # passes
  assert_equal_sets [2, 3], [3, 4]    # fails

class Array
  def reorder_like!(other)
    tmp = dup
    clear
    other.each {|x| self

——
h2. file_sandbox for testing against the file system

After dragging this code around me for the last 6 or 7 projects I’ve been on, I finally packaged it up as a gem . It lets you write code like :

in_sandbox do |sandbox|
  sandbox.new :file => 'b/a.txt', :with_contents => 'some stuff'
 
  assert_equal 'some_stuff', File.read(sandbox.root + '/b/a.txt')
end

Basically it creates a temporary directory for you to muck about in. After the block is ended (or teardown is called on your test) that directory and everything in it is guaranteed to be cleaned up. It also has a bunch of methods to make file based things easier like creating a file, etc.

Install it with “gem install file_sandbox”

Accessing Private Data in Ruby

January 29th, 2007

Of course that’s how you do it…

user.instance_eval("@password")

Every now and then you really do need to access a private variable and don’t particularly want to change the class. Especially in test code. Maybe it’s a smell, but I’d resorted to reopening classes and adding attr_accessor’s.

Moral issues aside, this is definitely a more elegant way of doing it.

Setting Today in a Test

December 20th, 2006

Ruby is awesome…

  def test_on_weekday
    today_is "May 3, 2006" do
      calendar.add("a call").at(4.pm).on(:saturday)
 
      assert_equal(Time.local(2006, 5, 6, 16),
                 calendar.appointments.first.start)
    end
  end

…playing with code examples for our book.