Archive for the ‘Ruby’ Category

I’m Dangerous with Ruby!

Comments

My solution was selected as the winning solution for RubyLearning.com’s RPCFN #2 “Average Arrival Time for A Flight.”

This challenge involved averaging times of the day without the actual day in the context.  When I first started to tackle the problem I thought to myself that this will be very easy.  Then I hit the “no day” context and realized that this problem was much tougher than I anticipated.  When I finally saw the posted solutions including Chris Strom’s (blog) it was like decades old high school math came rushing back to me.  I would have never thought of plotting points on a graph but now that I’ve been exposed I’m certain I will never forget it!

My friend [and commuting body] Matt and I talked through the problem during our drive home as we sat in Virginia I-495 outer loop and I-95S traffic.  He had some ideas about plotting the problem linearly around 0 but ultimately I ended up going with making assumptions about how close the provided times were to midday and midnight.

require 'time'

SECONDS_IN_DAY = 86400
MIDNIGHT = Time.parse("12:00AM").to_i
MIDDAY = Time.parse("12:00PM").to_i

def average_time_of_day(times)
  seconds = []
  times.each {|time| seconds << Time.parse(time).to_i}
  seconds.sort!
  if (seconds.first - MIDNIGHT) < (seconds.last - MIDDAY)
    seconds.map! {|s| s < MIDDAY ? s += SECONDS_IN_DAY : s }
  end
  Time.at(seconds.inject { |sum,n| sum += n }.to_f / seconds.length).strftime("%I:%M%p").downcase
end

 

Gist: https://gist.github.com/5b371226faf83af50d7e

Interview: http://rubylearning.com/blog/2009/10/22/charles-feduke-winner-rpcfn-2/


Temp Files and Ruby 1.8.6 on Windows

Comments

Working on the RPCFN: Shift Subtitle I found myself having to work with files input as a stream (or anyway that’s how I wanted to approach the problem; streams are efficient to me).  In order to give my code any sort of unit testing justice I needed to mock the file system.  The challenge expressly forbids any Ruby gems from being used in the script itself – and maybe by extension the unit tests as well – but I could not see devoting the time necessary to write a mocking framework for the file system.

I found a gem that does precisely what I needed named Construct.  Unfortunately there is a bug with Ruby 1.8.6 on Windows in regards to clean up of temp files.  The problem is that when attempting to clean up a Errno::EACCES is raised causing the unit test to fail (or you to write a lot of rescue blocks).

A workaround I came up with was to replace the rmtree method in the Pathname class within my unit test to perform no clean up.  Not the best approach I am sure, but it let me get on with my work.

# something_test.rb
require "test/unit"
require 'construct'
require 'something'

class Pathname
  # windows has problems with temp files created by Ruby
  # http://redmine.ruby-lang.org/issues/show/1494
  def rmtree
    nil
  end
end

class SomethingTest < Test::Unit::TestCase
   # test methods...
end

Ruby: Mocking Kernel Exit

Comments

I’m working on the RubyLearning blog’s Ruby Programming Challenge for Newbies #1 to learn the language – I’ve done a bit with Rails and some admin scripts so I could use the exposure.  Since I love TDD approaching Ruby development through RSpec is only natural, but it was a pain in the ass trying to find how I could have RSpec verify that my program properly exited when certain conditions were met.  Here’s how I solved it, reposted here:

# something.rb
class Something
    def initialize(kernel=Kernel)
        @kernel = kernel
    end

    def process_arguments(args)
        @kernel.exit
    end
end

# something_spec.rb
require 'something'
describe Something do
    before :each do
        @mock_kernel = mock(Kernel)
        @mock_kernel.stub!(:exit)
    end

    it "should exit cleanly" do
        s = Something.new(@mock_kernel)
        @mock_kernel.should_receive(:exit)
        s.process_arguments(["-h"])
    end
end

What I learned was that you can define a constructor with optional arguments (in this case, initialize(kernel=Kernel) and then proceed to use @kernel’s methods instead of the methods that Kernel provides when you do not specify a class instance.  With a properly mocked and stubbed exit method in my spec things operate as expected.