Wednesday 23 February 2011

Mere globs of gas...

Poets say science takes away from the beauty of stars -- mere globs of gas atoms. Nothing is "mere". I too can see stars on a desert night, and feel them. But do I see less or more? The vastness of the heavens stretches my imagination - stuck on this carousel my little eye can catch one-million-year-old light. A vast pattern -- of which I am a part -- perhaps my stuff was belched from some forgotten star, as one is belching there. Or see them with the greater eye of Palomar, rushing all apart from some common starting point when they were perhaps all together. What is the pattern, or the meaning, or the why? It does not do harm to the mystery to know a little about it. For far more marvelous is the truth than any artists of the past imagined! Why do the poets of the present not speak of it? What men are poets who can speak of Jupiter if he were like a man, but if he is an immense spinning sphere of methane and ammonia must be silent?

-- Richard Feynman

I'm reading The Feynman Lectures on Physics and had to stop and share this quote. It's so perfect.

Saturday 19 February 2011

Make it easy to be good

I'm trying to be good these days and eat a healthier diet. But doing it "the hard way" is just as difficult as going cold turkey. So I've been trying to find ways that actually make it easy to eat a healthy diet.

One way I'm doing this is through Riverford farm[1] - they deliver a box of fresh fruit and veg to your door once a week (or once a fortnight) for the same price (and much better quality) that you get in the supermarket.

I find that cooking then becomes a surprise instead of a drag. It makes eating your vegies a little bit fun. "What am I going to cook today? Lets see, what do we have here in box number 1?" :)

Something as simple as this helps with inspiration - which is often the only kick-start you really need. But I don't underestimate the "it's delivered to your door" aspect... No fuss, no muss. It really helps make it easy to be good.

On a similar vein, I also get myself graze.com[2] boxes twice a week. They send out a box full of delicious nibbles, randomly chosen from items that you've already rated as something that you've liked. It's novel, it's fun, it's generally pretty healthy. It's enough to eat for lunch and much better than the local greasy-spoon. ...but I don't do it every day, so I don't feel like I'm starving myself or denying myself all of life's pleasures. They also taste delicious. So it doesn't feel like I'm doing any stringent dieting.

Again - just making it easy to do yourself a little good.

How does this apply to programming?

Well, there are heaps of things we *ought* to be doing. Testing, code reviews, refactoring... But somehow it often seems like this stuff just never gets done.

In these cases, it really helps to make it easy to be good.

In my case, I've picked a language and framework where testing is assumed to be done as the default case. It's really easy to do proper testing in Rails. More than that - you're looked-at funny if you don't... like you're actually doing it wrong. So tests, in Rails, tend o get written more often than in other languages (where you often have a hard time justifying to people why you've got to write them).

Once you have tests in place, it's really easy to refactor - because you can do it, knowing that you have a comprehensive test suite to catch you if you break something - gives you the confidence to get it done.

and code reviews? well - if you've been regularly refactoring, then you won't be as ashamed to show off your code to other developers... because it won't be a horrible, hard-to-grok mess... and that makes it much less likely that the code reviews will be put off due to it being "too hard"

Each little step makes it easier and easier to be "good", which makes it far more likely to actually *happen*.


Notes:

[1] Riverford farm only deliver to the UK - but I strongly recommend them - I've been eating their small fruit+veg box once a fortnight (which is enough for just me), and they're always fresh and yummy.
[2]graze.com The graze link is an affiliate link - it will give you a free box so you can try it out yourself. Normally they're around £2.99 each. They ask for a CC to register - but it's easy enough to cancel.

Friday 11 February 2011

Blind spots

Some people like to hang dingly-dangly things from their car-mirrors.

I always thought it was a bad idea, and it took a little reflection to figure out why.

If you get used to seeing something swaying back and forth in a certain sector of your vision, eventually you learn to ignore movement in that sector. This means that if/when the Bad Thing happens - and suddenly there's an accident happening right in that spot... it takes longer for you to recognise, and react to that accident, because you've learned to ignore stuff happening there.

Well - I was thinking recently about things that were less obvious. Things that I'd put somewhere in my life for amusement value - or even for a good purpose... but which are also serving as a blind-spot. As a place where I "should ignore what's going on".

One of those spots (for me), is in "standing up for myself".

You see, I'm a geek... but more than that, I'm a geek *girl*. I engage in activities that are traditionally considered the province of males... and I get funny looks for doing so. Not from everybody, but enough that I've had to continually resist assumptions (and even outright contempt) all my life.

For years, I've had to work *very* hard to stand up to people that treat me like some kind of weirdo for actually having an interest in things like science, maths, science fiction, martial arts, role-playing games... the list goes on.

I treat any criticism of my choice to pursue these interests as an infringement of my individual rights... and rightly so. It's my business whether I enjoy these things or not. Your opinion does *not* count.

But I've come to notice that it does lend me to have formed a particular blindspot. It's the "don't infringe on the way I do things" blindspot.

I find I'm quicker to resist, to stand up and fight-back, to take offense at people that try to press the boundaries of "the way I do things".

Needless to say, this heuristic can sometimes be misapplied.

After all, I am not perfect, and the way I do things can very well be stupid, wrong or otherwise detrimental to the health and happiness of other people.

But when I'm "called" on it - I currently still have to fight the impulse to fight back. To resist because "I have the right to do my things my way"... and sometimes I lose that internal fight.

So - if I've ever done that to you - my apologies. I'll try to do better next time.

Sunday 6 February 2011

gotcha: Unpacked gem in vendor/gems has no specification file.

Ok, this one had me stumped for a little while. When I was running rake, I'd get these warnings:

  config.gem: Unpacked gem difftmp in vendor/gems has no specification file. Run 'rake gems:refresh_specs' to fix this.
  config.gem: Unpacked gem difftmp in vendor/gems not in a versioned directory. Giving up.

I'd seen this one before - when I'd thoughtlessly unpacked a gem into vendor/gems, then patched it. But for some reason rails doesn't like it when you unpack gems without using rake gems:unpack. I've never been in the habit of using that, because I didn't want to unpack everything. Later, of course I discovered that you can pass "GEM=blah" and it'll only unpack a single gem... but I thought I had done I this time around...

So I blindly followed the instructions to run rake gems:refresh_specs

However, that then spewed on me with an equally-familiar exception message:

rake aborted!
undefined method `installed_source_index' for #<Gem::SourceIndex:0xb76aa218>
... much stacktrace garbage follows...

I sighed deeply and googled the exception... to get the usual suspects, which suggest that I need to refresh the specification file. So I went back to the original error message to see which actual gem was causing the problem.

... then stopped.

You see it became pretty clear that I'd never actually *read* the original error message. "difftmp" is obviously a temporary diff-file showing me the patch I'd made on one of the gems. Now, you could argue that rake shouldn't be stupid and assume that a flat text file is a gem (requiring a specification)... but it was then also quite clear what to do to fix the "bug".

rm vendor/gems/difftmp

Problem solved

Friday 4 February 2011

assert_difference for any value

I like assert_difference. It does exactly what it says on the tin - and in a nice, rubyish way.

However, I noticed that you can't assert a difference in something that isn't numeric. You can assert that creating a Customer will change the Customer.count, but you can't assert that updating the customers name will actually change the customer's name. Or, conversely, that updating a customer's name with *bad* data *doesn't* change the customer's name.

In my mind, it shouldn't really matter if the thing doing the changing is an int or a string or some complicated object. If the changing-thing responds to "==", you should be able to check if it changed. As long as you don't actually care what the exact change is that occurred - just whether or not a change *has* occurred.

So I wrote one that did.

assert_any_difference/assert_not_any_difference

Example tests. Note that you can pass a whole array of tests. These should nest, just like assert_difference

    should "not change anything if the account is active" do
      @cr = customer_records(:active)
      assert_not_any_difference ['@cr.customer_number', '@cr.is_active'] do
        assert @cr.register!
      end
    end
    should "update active status if registering a non active customer" do
      @cr = customer_records(:in_active)
      assert_not_any_difference ['@cr.customer_number'] do
        assert_any_difference ['@cr.is_active'] do
          assert @cr.register!
        end
      end
      assert @cr.active?
    end

And the assertions. You'll need to add these to test_helper.rb

  def assert_any_difference(items, &block)
    actual_diff(false, items, &block)
  end
  def assert_not_any_difference(items, &block)
    actual_diff(true, items, &block)
  end

  def actual_diff(test_if_equal, items)
    # save the old values
    old_vals = {}
    items.each {|item| old_vals[item] = eval(item) }
    # run the block
    yield
    test_fn = test_if_equal ? :assert_equal : :assert_not_equal
    # check each one
    items.each do |item|
      new_val = eval(item)
      send(test_fn, old_vals[item], new_val, "should #{'not' if test_if_equal} have been a difference for #{item}, but had: #{new_val}")
    end
  end