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.


Side Effect Free Retrieval Pattern

Comments

A colleague of mine and I were discussing good verb replacements for “GetOrCreate” data retrieval patterns, where the “Create” part is responsible for the instantiation of a new instance of something.  While the pattern I present here did not solve his particular problem, its at least worth sharing.  Its obvious, and probably in use commonly already, but I figure its worth noting.

Essentially the goal is to get away from a “GetOrCreate” master method call and just have two methods: Get(args) and Get(args, Foo default) and then leave the responsibility of default generation up to Foo.  This way there’s no unintended side effects and you don’t have to explicitly coalesce (though with this pattern you could as easily coalesce; strictly speaking it isn’t as “discoverable”)…

var foo = FooService.Get(23) ?? new Foo { ... };

So here’s the pattern defined in code:

public class Foo : ICloneable
{
    private readonly static Foo __default =
        new Foo { Bar = "...", Baz = Int32.MinValue };

    public string Bar { get; set; }
    public int Baz { get; set; }

    public static Foo Default()
    {
        return (Foo)__default.Clone();
    }

    public object Clone()
    {
        return new Foo { Bar = this.Bar, Baz = this.Baz };
    }
}

public class FooService
{
    public Foo Get(int baz)
    {
        // retrieve from data store...
        return null;
    }

    public Foo Get(int baz, Func<Foo> @default)
    {
        return Get(baz) ?? @default.Invoke();
    }
}

You’ll see that any associated overhead with Clone only occurs if Get(baz) returns null.  I have specifically stepped around the common naming standard of “GetDefault()” for the function – its named like a property - because its intended usage is as follows:

var foo = new FooService().Get(12, Foo.Default);


An ICloneable reference type is pretty much required, though any immutable reference type could get by without cloning because any changes gives you a new copy of that type.


Khorne Berzerkers

Comments

Some Khorne Berzerkers I finished up after having them assembled and sitting in a box for over six years.  Also trying out my new camera and figuring out how to take pictures.

 
Khorne Berzerkers Khorne Berzerkers (All)