accurate and lazy config errors

What is better than being exactly right? What about being lazy? How about both?

This was a thought experiment that has migrated through code and into this post.

Here’s the thought: I write a ruby library that you have to configure, but I don’t want to pre-check all the config. I also want the exception that I through to come from the bad config value, not from some weird-o spot in your and my code where we both have a hard time figuring out what the problem is.

Can we pull off being lazy and accurate?

Here’s one way to do it:

class Config
  def driver=(d)
    if((exc = callcc { |cont| @driver_ptr = cont }) && exc.is_a?(Exception))
      raise exc
    end
    @driver = d
  end

  def try
    @driver_ptr.call ArgumentError.new("Driver cannot be :fail") if @driver == :fail
    puts @driver.inspect
  end
end

config = Config.new

config.driver = :ok
config.try

config.driver = :yes
config.try

config.driver = :fail
puts "Note that the error happens the line before this one (this line = #{__LINE__})"
config.try

config.driver = :not_reached
config.try

And, the output:

:ok
:yes
Note that the error happens the line before this one (this line = 24)
wacky.rb:4:in `driver=': Driver cannot be :fail (ArgumentError)
        from wacky.rb:23

I don’t think I’d use this on a general purpose library, because of the ease of creating an infinite loop…

begin
  config.driver = :fail
rescue => e
  puts e
end
config.try

… but it was fun to try it out.