Previous and next weekday in Ruby and RSpec

Lately I’ve been doing some Ruby coding as well as some Ruby on Rails, and enjoying it a lot. It is definitely very different than doing C# and ASP.NET MVC 3, in that the syntax is a lot more concise. You end up typing a lot less code for doing the same thing. Although I am still not sure if your productivity is actually higher or not. When it comes to productivity, there are too many factors to consider to make an objective analysis. I also have no experience working in a large scale project with Ruby, where I imagine there would be many other factors in play as well.

I needed to write some additional utility methods to the Date class in Ruby, to find the next and previous weekdays excluding weekends. In Ruby this possible to do this just by “amending” the class definition (in C# world they would have been called extension methods). It’s possible to open up any class and add methods to it. I still feel pretty much a Ruby novice though, and unlike the C# world there usually seems to be several ways to do the same thing in Ruby. This can be overwhelming at first, but I think the more you learn the more you get comfortable with it.

It was also a good exercise to get some practice with RSpec, which is a testing framework for Ruby that allows you to do Behavior-Driven Development. BDD is a form of Test-Driven Development but the focus is on making the tests reflect the behaviour of the software as well as making tests readable almost in a natural language. Ruby is a good fit for BDD due to the clarity of the syntax.

As always with BDD, you write the tests first. I won’t go over the whole development process and instead just put the resulting test suite here:

require "rspec"
require 'date_ext'

describe "Date extensions" do
  before :all do
    @thursday, @friday, @saturday, @sunday, @monday =
        (17..21).map { |n| Date.new(2011,11,n) }
  end

  context "prev_weekday" do
    it "should find thursday before friday" do
      @friday.prev_weekday.should eq @thursday
    end
    it "should find friday before sunday" do
      @monday.prev_weekday.should eq @friday
    end
  end
  context "next_weekday" do
    it "should find friday after thursday" do
      @thursday.next_weekday.should eq @friday
    end
    it "should find monday after friday" do
      @friday.next_weekday.should eq @monday
    end
  end
end

You can see how the tests almost read like plain English. Having code that is easy to read without clutter is one of the principal pillars of the Ruby language.

Here is the implementation that makes all the tests pass:

require 'date'

class Date
  def weekend?
    saturday? || sunday?
  end

  def prev_weekday
    previous = yesterday
    while previous.weekend?
      previous = previous.yesterday
    end
    previous
  end

  def next_weekday
    nextday = tomorrow
    while nextday.weekend?
      nextday = nextday.tomorrow
    end
    nextday
  end
end
Advertisements