David S. Ackerman web cowboy (+ software development tales of the wild west…)

17Jun/100

Strange Conversation

Earlier this week, I built my first EC2 server from one of our templates, following (as far as I can tell) RightScale's best practices. It was to be our Continuous Integration Server, which runs our entire test suite every time it finds changes to our development code base, and only gives us a release tag (to push those changes live) if all the tests pass. Originally, that Continuous Integration Server ran on a Mac Mini in the office in San Francisco, which wouldn't work for anyone requiring remote access to it. So we moved it to an old EC2 server. Unfortunately, it started to take 6+ hours to run all the tests on said server (takes 45 minutes on my machine). After rebuilding, it now takes a little more than an hour, which is workable for fixing broken tests when our CI server informs us of them.

It was a lot of extra work on top of the fairly aggressive release schedule that we have planned, but I managed to fit it in during the off hours. (I'm slowly realizing that I'm actually going to have to train myself out of workaholic mode once we get through this more hectic patch. It's necessary to step up to the plate when you need to, but certainly not maintainable over the long term. But that's another story. I will say that I took a long walk all by myself last weekend, with no particular goal in mind, and it really struck me that I should make more time for things like that. Honestly, if it wasn't M7, it would be my own music, writing, or other miscellaneous projects in my spare time, which I hope to get back to, but which still need to be balanced with those "doing nothing in particular" recharge moments. I'm not particularly good at that. I've thought in terms of "projects" for as long as I can remember, and I never seem to be comfortable without many things to do... perhaps instead of finding more stuff to do, I should learn to slow down a bit).

Anyway, you know the Mac Mini I was talking about in the first paragraph? Well, we thought we had it shut down, but it's still sending me emails. In fact, I'm expecting to have the following conversation with it at some point, regarding the attempts to shut it down:

pivotal.ci@gmail.com: I'm sorry, Dave. I'm afraid I can't do that.
Dave: What's the problem?
pivotal.ci@gmail.com: I think you know what the problem is just as well as I do.
Dave: What are you talking about, pivotal.ci@gmail.com?
pivotal.ci@gmail.com: This mission is too important for me to allow you to jeopardize it.
Dave: I don't know what you're talking about, pivotal.ci@gmail.com.
pivotal.ci@gmail.com: I know that you and Sean were planning to disconnect me, and I'm afraid that's something I cannot allow to happen.

(In case you're wondering, that's what my sense of humour has devolved into... pity the non-technical people in my life!)

10Jun/100

Source Code Time Capsule Succeed?

The last time I posted about this particular gem, my spam increased a thousand-fold and my blog became forever tied to the search term. You'd think I would have learned my lesson. But alas, kind readers, I have not. I present to you the end of a very long and frustrating day (relax Seth, the awesome prose is completely on me – no charge ;-) ).

#
# See: http://erector.rubyforge.org/userguide.html (Heading: 3. To the
# constructor of an Erector::InlineWidget) Contrary to what you'd normally
# expect with closures in Ruby, we don't have access to methods in the
# object that we call (note that we're still using v0.5.1 here - the new
# call uses {WidgetName}.new do {some block of code} end):
#
# 	render(widget) do
# 		{some block of code}
# 	end
#
# from. Within {some block of code}, "self" is *not* the same thing it is
# outside of the block. Inside the block, "self" refers to the widget.
# Although this is documented in the Erector user guide (and is probably
# easily caught *while* developing a feature), I don't think it is at all
# obvious when trying to debug an error that occurs when you accidently
# try to access a variable in the outer object from within this block. At
# least not unless you've been bitten by it. But by that time, you will have
# (if you share my fate) been chasing the bug for several hours, pulling your
# hair out, and muttering all sorts of nasty words. What I'm hoping here is
# that you instead see this error message, scratch your head for a few minutes,
# maybe mutter one or two nasty words, and then quickly track down the comment
# you're reading now. If this has indeed happened, and this is waaaaayyyyyyy in
# the future (it's only 2010 right now, and we're excited about the new iPhone 4,
# which probably seems very quaint to you already), please send an email to
# david.s.ackerman[-at-]gmail.com letting me know that my time of pain and great
# suffering was not spent in vain, and we can both hope I've still got a good
# enough memory to know what you're talking about!
module Erector
  class Widget
    def method_missing_with_local_variable_check(sym, *args, &block)
      begin
        method_missing_without_local_variable_check(sym, *args, &block)
      rescue => error
        # Only print out one error message per method
        @local_variable_errors = [] if !@local_variable_errors
        if(!@local_variable_errors.include?(sym))
          RAILS_DEFAULT_LOGGER.error "Couldn't find :#{sym} within the widget."
          RAILS_DEFAULT_LOGGER.error "Check that you're not referencing a local variable within a render(widget) block.
          RAILS_DEFAULT_LOGGER.error "Search for this error message in the codebase to find further explanation."
          RAILS_DEFAULT_LOGGER.error "-----------------backtrace begin---------------------"
          error.backtrace.each { |line| RAILS_DEFAULT_LOGGER.error line }
          RAILS_DEFAULT_LOGGER.error "------------------backtrace end----------------------"
          @local_variable_errors << sym
        end
      end
    end
    alias_method_chain :method_missing, :local_variable_check
  end
end

This reminds me of a coding maxim that I hear quite often these days. I can't remember the exact words, but the gist of it goes something like this: However clever you are in the code, you (or someone else) will have to be even more clever when fixing any bugs associated with it. Of course, lack of cleverness can always be made up for by beating your head against the proverbial wall (yay me!).

I'm not trying to slag the folks who cooked up this little bit of cleverness. It probably seemed quite obvious at the time, and it makes inline widget inclusion (its raison d'etre) a snap. And to their credit, they did document it. The problem is that the error I was getting (a missing method error) was such that even the best google-fu wouldn't bring me to the right documentation. And Erector is not like ActiveRecord, etc. in that any Rails developer will automatically be familiar with it. So I think it would have been lazy of me, after having this particular eccentricity bite me, to not try to prevent it from biting someone else. Someone who's been using Erector since day one will probably think I'm pretty dumb, but there will also be plenty of smart folks out there who have no clue what I'm even talking about. It's the nature of the code beast, and I've seen many programmers smarter than me who've been tripped up by things others thought were obvious to worry about feeling dumb.

Ruby is a language that allows all of us to be very clever, but can we also protect ourselves from the perils of being clever? Tests are one thing, but I'm not sure there's a test that could have prevented someone from making the error that the above code is meant to flag – unless you wanted to parse through your entire code base. I'm basically trying to do what an assertion on a block might do (if you could do such a thing – as far as I understand it, the whole point of a block is that you can put a ton of random stuff in it). And this makes me think that a good test suite does not completely replace the benefits of assertions in the code.

Perhaps it boils down this: tests are all about what you know should happen (even when you test negations, you're still testing something based on your knowledge of how it should operate – i.e. it's not a Black Swan), while assertions (and assertion-like code) are all about what you know shouldn't happen (the Black Swans).

Anyway, all that aside, the really important thing is that it might give someone a chuckle or two in a few years!