Alright, here we go. This is the first post of my blog, but I promised myself that I wouldn’t bore anyone with a whole big self-congratulatory introduction post until I had some substance. I do have a lot of cool ideas for things to talk about on this blog, and I’ll probably get around to an introductory/coming soon post before too long.

What I want to talk about today, however, is the code kata that I worked on last week. My goal is to do a coding exercise along the same line roughly every week, and then do a little writeup/retro about what I learned. I worked on the Evercraft Kata in Ruby. My repo is here, if you’re interested in reading - or, even better, reviewing - my code.

I’ve done a little bit of Ruby here and there, but hadn’t really dived in until recently. The tooling took a little bit of getting used to. I’ve been doing the majority of my at-home hacking around on a xubuntu 15.10 vm, and this didn’t have any ruby installed on it yet. From my Googling around, rvm seemed like the way to go, so I got myself set up with that. I knew that Pillar (the company I work for) had some template projects on their site, so I cloned the Ruby skeleton project. I stripped out the spec-helper stuff, just because I didn’t really know what it did, and I wanted the most basic setup possible.

One new thing I tried with this kata was taking some more detailed notes in my commit log. Wouldn’t do this on a real project, but it was kind of a good way to track various questions and thoughts that I had as I was doing the kata. My first commit has a note where I’m wondering about the Gemfile vs the Rakefile, and what their purposes are. I knew that a Gemfile was used by Bundler to manage dependencies, and I knew that I could pull down whatever was specified in that file with bundle install. I didn’t really know anything about rake except that it’s Ruby’s version of make, but a glance at the Rakefile made me think that running rake would run the tests, and that was true (although the output is horrific, learning to tweak that is definitely on my short list).

OK, so great. At this point I’ve been screwing around for an hour or so, but now I have something that I know how to work with - src/ and test/ directories. Well, not exactly, but lib/ and spec/ directories. And I’ve got a command to run them. That’s enough to start writing some code.

The Evercraft Kata basically fleshes out a D&D style RPG game. The first few features are pretty much no brainers, but by about halfway through the first iteration, I had to think a bit about how best to implement the various features.

The first feature is dirt simple - “Character can have a name.” My first test looked like this:

  it "can set its name" do
    expected_name = "Nathan Explosion"
    subject.name = expected_name 
    expect(subject.name).to eq(expected_name)
  end

Which is so “basic TDD” that I wouldn’t bother to mention it, but I had learned a little Ruby trick earlier that day on CodeWars that made the implentation super easy:

class Character
  attr_accessor :name
end

(commit)

My college CS coursework was 90% in C++, so I’m used to having to write accessors and mutators manually. But shit, it’s 2016, and who wants to do that anyway? 2011 me would be impressed. It was cool to have encountered this earlier the same day and be able to put it to use so soon.

The next feature was alignment, and the first test and first pass code I wrote for that were basically identical to the code above. The next thing I wanted to drive in was validation for the alignment value. Valid values are “Good”, “Evil”, and “Neutral”. This was my first attempt at a test, but it didn’t feel quite right:

  it "raises ArgumentError when given an invalid alignment" do
    expect{subject.alignment = "Decent"}.to raise_error(ArgumentError)
  end

And here’s the code I wrote to make it pass:

class Character
  attr_accessor :name
  attr_reader :alignment

  def alignment=(alignment) 
    valid_alignments = ['Good', 'Evil', 'Neutral']
    if not valid_alignments.include? alignment.capitalize
      raise ArgumentError
    end
    @alignment = alignment.capitalize
  end
end

(commit)

You can see that I was taking a few liberties with “Only write the necessary code to make the test pass.” I fixed the lack of a test for the capitalization of the input to the setter in the next commit, but the real problem was that test didn’t really feel like it necessitated the 3 valid alignments. Here’s the test I ended up using:

  it "raises ArgumentError for invalid alignment" do
    expect{subject.alignment = "Good"}.not_to raise_error
    expect{subject.alignment = "Evil"}.not_to raise_error
    expect{subject.alignment = "Neutral"}.not_to raise_error
    expect{subject.alignment = "Nice"}.to raise_error(ArgumentError)
    expect{subject.alignment = "Literally Hitler"}.to raise_error(ArgumentError)
  end

(commit)

I talked to a coworker (DJ), who agreed that this style was better - the phrase he used was “executable documentation,” which is one of the things I like best about TDD. I do have a slight sticking point with this kind of test though - I don’t feel it fully characterizes the possible behavior of this validation.

The test as shown only tells me 3 values that are valid, and two that aren’t, so all that I know for sure from reading it are that “Good”, “Evil”, and “Neutral” are valid values, and that “Nice” and “Literally Hitler” are not. Here’s where a reader needs to be able to understand the intent of the person writing the test, and I think it’s a reasonable expectation for me to have - that the reader will get my point. But if we think about it from a pure logic standpoint, it’s incomplete.

Is there a way to write a test condition such that it only can pass “if these three values are the only valid values?”, short of enumerating every possbile invalid value? I’m thinking back to proofs by math induction, which lets you demonstrate that a proposition holds for all values, without having to write out every number to do it. I’d be interested to hear anyone’s thoughts about how to make something like this logically rigorous, or why that’s silly or impossible or a bad idea.

OK, rambling over. The last features I’d like to talk about are damage and attacking. These required a bit more thought than the others. In the kata, attacking is listed as a feature before damage, and works like this: if the roll passed into the attack function beats the enemy’s armor class, the attack is successful. A successful attack does 1 damage. That’s three concepts tied together - damage, attacking, and success/failure of attacks. I couldn’t figure out how to drive in attacks without having a method to damage a character. So I wrote a test for the ability of a character to take damage:

  it "can be damaged" do 
    subject.damage 1
    expect(subject.hp).to eq 4 
  end

(commit)

But that didn’t feel quite right. The problem here is that the test assumes the reader knows that a character has a default HP of 5. Even worse, it assumes that the reader knows that the describe whose scope this test is in doesn’t have a before(:each), and even worser, it assumes the reader knows that when that’s the case rspec just instantiates a test object named subject with the default initializer.

Here’s the test I settled on, which DJ agreed was a better test. It assumes less - but this introduces another question: “what happens if hp goes below 0?”

  it "loses 1 hit point when damaged by 1" do 
    expected_hp = subject.hp - 1 
    subject.damage 1
    expect(subject.hp).to eq expected_hp 
  end 

(commit)

I can’t think of a better way to write this, however. We could validate that hp hasn’t dropped below 0, but I think that would clutter the intent of the test. I know that when the test runs, the subject has 5 hp, and maybe that’s enough.

Now that I had the ability to damage a character, a test for attack becomes more simple:

it "does 1 damage to enemy when roll beats enemy armor class" do
      enemy = Character.new
      success_roll = enemy.armor_class + 1
      damaged_hp = enemy.hp - 1 

      subject.attack(enemy, success_roll)
      
      expect(enemy.hp).to eq damaged_hp 
    end

I was worried that this test conflates the concepts of attacking and a success roll a bit, but following the rule of writing the minimum code to make the test pass, my implementation looked like this:

 def attack(receiver, roll)
    receiver.damage 1
  end

(commit)

Which obviously doesn’t meet the feature’s criteria that the roll beats the enemy. This approach forces you to write another test to be able to implement the “attack success” feature. I think of this as the “other half” of the test:

    it "does no damage when roll doesn't beat enemy armor class" do 
      enemy = Character.new 
      bad_roll = enemy.armor_class - 1 
      hp_before_attack = enemy.hp

      subject.attack(enemy, bad_roll)

      expect(enemy.hp).to eq hp_before_attack
    end

This allowed me to change attack to look like this:

  def attack(receiver, roll)
    if roll > receiver.armor_class
      receiver.damage 1
    end
  end

(commit)

This is about as far as I got working a little bit each day. This is not a lot of code, but it felt good to flex my muscle. I’m trying to get in the habit of doing at least a commit to some repo or another daily, and this was a good start to that.

This post has gotten lengthier than I meant it to be, so I want to cut it off here. I’ve officially earned myself the right to write an introduction post, though, so look out for that. I have a lot of ideas for future articles, and will be working with a couple of other people so that all of us can have kickass blogs in time.

Just as a teaser, here are some of the things I’m planning on talking about in the near future:

  • This same kata in Clojure, and maybe Swift
  • Details about my personal code writin’ setup (all the hipster tools)
  • Overtone (which I’ve done a few talks about)
  • bash, boxen, jenkins, docker, and other DevOps-y type stuff
  • stuff I’m trying to do to get better at being a programmer
  • stuff I’m trying to do to get better at being an adult

If you like, follow me on Twitter. I’m not a particularly exciting social media user, but I plan to at least tweet whenever I write a blog post. Also, any critique of the code or the writing would be welcome. I’m planning on setting up comments on this blog soon (edit 5/13/16 - this blog now has comments, cool!), but for now, feel free to email constructive criticism to ecoleman@pillartechnology.com

Stay tuned, and thanks for reading.