The kata for today is quite simple but following alone with the TDD cycle show me some wrong assumtions that I’ve made; without further ado here is the problem statement:

Sum an arbitrary lenght of items.

Pretty straight forward statement, here is my first spec and implementation:

describe Sumer do 
  context '#call' do 
    it 'sums an arbitrary amount of numbers' do 
      expect(Sumer.(1,2,3)).to eql 6
    end
  end
end
module Sumer 
  extend self

  def call(*items)
    items.inject(:+)
  end
end

I started to use this type of implementation following RubyLove style; because actually I’m not handling state for this type of operation is just input get into the function and an output get thrown out. All good; now let’s make things a little bit difficult what would happen if I sent an string in the items?

describe Sumer do 
  context '#call' do 
    it 'sums an arbitrary amount of numbers' do 
      expect(Sumer.(1,2,'3')).to eql 6
    end
  end
end

So as I expected I can’t use + with Strings and Fixnum so I have to change my implementation.

module Sumer 
  extend self

  def call(*items)
    items.map(&:to_i).inject(:+)
  end
end

So far so good; but what will happen if I happen to pass a character to that method? What I found out is that Ruby coerce any character to 0 calling it’s to_i method so that’s a realive. But what would happen if I pass an Array well I found a problem with my implementation and now I need to fix it.

module Sumer 
  extend self

  def call(*items)
    items.select { |item| item.respond_to? :to_i }.map(&:to_i).inject(:+)
  end
end

Now I’m green again. This doesn’t look to me as a very elegant implementation; I think I’m breaking the Law of Demeter So that leads me to make some research and found an explanation from a great book from Sandi Metz; in the book she says that you are not breaking the law as long as you are working on the same data structure; so as you can see at first I’m working against an Array and using the #select method is returning another Array and then #map is returning another Array and at the very end is when I switch out to returning a single value. So far so good :-)

What I’ve learned from this Kata?

  • Calling to_i to String in Ruby return 0
  • Always test the smaller happy path scenario
  • The law of demeter is only break if the data structure in the chain changes