All Posts

New Site and a Newsletter

After finishing my most recent post, I realized that I wanted to focus on questions of code design and refactoring. I easily could have done so on this site, but why not start up another site. Domains are cheap and a new site would let me curse at a different CMS.

The new site is http://designisrefactoring.com/.

And, to go along with the new site I’m kicking off a newsletter which you can subscribe to at TinyLetter. The first newsletter will go out on January 1, 2015 and I hope to publish it weekly.

written in

Exercism: The RNA Transcription Exercise

The Readme

The Test Suite

As with the Gigasecond exercise, it doesn’t take much to get this to pass. The example solution is:

1
2
3
4
5
6
7
8
9
class Complement
  def self.of_dna(strand)
    strand.tr('CGTA', 'GCAU')
  end

  def self.of_rna(strand)
    strand.tr('GCAU', 'CGTA')
  end
end

(An aside: Did you know that Exercism has example solutions in their Git repo? I did not. I was wondering why 40% of people’s solutions looked exactly the same. Copying and pasting ain’t learning, folks)

written in Read on →

Exercism: The Gigasecond Exercise

The Readme

The Test Suite

Honestly, as the tests are written there’s not much to dig into here. Nearly every first pass at this code looks something like mine:

1
2
3
4
5
6
7
8
9
10
11
12
class Gigasecond
  def initialize(start_date)
    self.start_date = start_date
  end

  def date
    start_date + 11574 #number of days in a gigasecond
  end

  private
  attr_accessor :start_date
end

Some people convert the Date to Time and then add 1 billion seconds to it, but the concept is the same. Ruby already has good date operators; there’s no reason to reinvent the wheel.

So, this works and is totally readable. We could probably just leave it at that. But then I wouldn’t get to type a rambling blog post. That’s no fun. Let’s dig deeper.

The test suite only includes Date objects. Even though we know that Ruby has other ways of representing time, let’s set that aside for now and assume we’re only going to get Dates.

1
2
3
4
5
6
require 'delegate'
class Gigasecond < SimpleDelegator
  def date
    getobj + 11574 #number of days in a gigasecond
  end
end

With this our tests still pass and we’ve saved a few lines of code. That tiny refactoring done, we revisit the problem of Time and DateTime, Ruby’s other classes for representing dates.

As written, this code will work with Date and DateTime, as they both use the same implementation for the + operator. They add days. But Time’s + operator adds seconds.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
d = Date.today
#=> #<Date: 2014-06-09 ((2456818j,0s,0n),+0s,2299161j)>
d + 1
#=> #<Date: 2014-06-10 ((2456819j,0s,0n),+0s,2299161j)>

#######

dt = DateTime.now
#=> #<DateTime: 2014-06-09T13:46:20-05:00 ((2456818j,67580s,427220000n),-18000s,2299161j)>
dt + 1
#=> #<DateTime: 2014-06-10T13:46:20-05:00 ((2456819j,67580s,427220000n),-18000s,2299161j)>

#######

t = Time.now
#=> 2014-06-09 13:46:37 -0500
t + 1
#=> 2014-06-09 13:46:38 -0500

So, if we want to support Time, we’ll have to handle that difference. Of course we want to support Time! But let’s make sure Date works first:

1
2
3
4
5
6
7
8
9
10
11
12
rubyrequire 'delegate'
class Gigasecond < SimpleDelegator
  def date
    getobj.gigaseconds_since
  end
end

class Date
  def gigaseconds_since
    self + 11574
  end
end

The gigaseconds_since method naming follows the convention of Date helpers in Rails. Ruby doesn’t have helper methods like this, but I figured people would be familiar with the Ralis methods, so I stuck to similar naming.

I don’t have to implement DateTime because it inherits from Date. So now I just need to add Time support. Easy peasy.

1
2
3
4
5
class Time
  def gigaseconds_since
    self + 1_000_000_000
  end
end

It was about here when I looked at the initial test suite and realized that it didn’t exercise DateTime or Time objects. Also, I think its use of static dates is a liability. Random dates in the tests might find weird edge cases. So I wrote a test like the following for Time, Date and DateTime:

1
2
3
4
5
6
7
8
9
10
def test_date
  1000.times do |x|
    random_date = Time.at(rand * Time.now.to_i).to_date
    expected = random_date + 11574

    gs = Gigasecond.new(random_date)
    assert_equal expected, gs.date
    assert_equal expected, random_date.gigaseconds_since(1)
  end
end

This test makes sure my Gigasecond.new syntax works, as well as checking the gigaseconds_since syntax. And, sure, why not run it 1000 times? These 3000 tests still pass in 0.1 seconds, so I’m not concerned with how ridiculous it looks. If there is some weird date that breaks my implementation, this approach is more likely to find it. But, yes, it’s ridiculous. I’m not ashamed.

Now we can easily tell people what Time/DateTime/Date is 1 gigasecond after the Time/DateTime/Etc. they provide. But why just 1 gigasecond? Who doesn’t immediately start thinking about 2, 3, 1000 gigaseconds? Only people with no joy in their hearts, that’s who.

The Rails _since methods that I copied accept parameters. Time.now.hours_since(5) will return a Time 5 hours in the future. So let’s take that same approach.

1
2
3
4
5
6
7
8
9
10
11
class Date
  def gigaseconds_since(multiple)
    self + (11574  multiple)
  end
end

class Time
  def gigaseconds_since(multiple)
    self + ((10**9)  multiple)
  end
end

And we hardcode the Gigasecond implementation to always use just 1 lowly gigasecond.

1
2
3
4
5
class Gigasecond < SimpleDelegator
  def date
    getobj.gigaseconds_since(1)
  end
end

Now we have to change our tests. First we need to show that the Date/Time implementation can use any number of gigaseconds, and we should show that Gigasecond is just an alias for gigaseconds_since(1)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def test_gigasecond_wraps_date_methods
  random_date = Minitest::Mock.new
  gs = Gigasecond.new(random_date)
  random_date.expect(:gigaseconds_since, 1) { true }
  gs.date
end

def test_date
  1000.times do |x|
    random_date = Time.at(rand  Time.now.to_i).to_date
    random_gigaseconds = rand(1000)
    expected = random_date + (10**9  random_gigaseconds / (24  60  60))

    assert_equal expected, random_date.gigaseconds_since(random_gigaseconds)
  end
end

### similar tests for DateTime and Time

Now it’s time to tackle a problem that’s been lingering in the bacground, the Magic Numbers. What is the meaning of 11574? What is 10**9? What is 24 * 60 * 60?

In the context of the code these numbers aren’t that hard to figure out. But if we can reduce cognitive overhead, we should. We can inspiration from Rails again and look at the methods it adds to Numeric.

1
2
3
4
t = Time.now
#=> 2014-06-09 15:43:14 -0500
t + 1.hour
#=> 2014-06-09 16:43:14 -0500

Methods like hour return a Duration instance, which is probably more than we need. We can keep this pretty simple.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class Date
  SECONDS_PER_DAY = 86400
  def gigaseconds_since(multiple)
    self + (multiple.gigaseconds / SECONDS_PER_DAY)
  end
end

class Time
  def gigaseconds_since(multiple)
    self + multiple.gigaseconds
  end
end

class Numeric
  def gigaseconds
    self * 1_000_000_000
  end
  alias :gigasecond :gigaseconds
end

The simplicity of our approach forces us to use the SECONDS_PER_DAY constant. The gigaseconds method will only return seconds and we need to convert it to days. We could do the math in the method, but a named constant helps us remember what 86400 means.

If we had implemented something like Duration we could have added a handy to_days methods as a way around this problem. But, as it is, I don’t think that extra code is worth the effort.

An as-yet undiscussed side-effect of the changes we’ve made is that we can now work with fractional gigaseconds. Time.now.gigaseconds_since(1.33) will work. It worked in earlier implementations as well, but we could have easily broken the functionality had we monkey patched Integer instead of Numeric.

And that’s that. Well. Almost. I was happy with this code until I realized that I’d left myself a surprise:

1
2
3
4
5
6
d = Gigasecond.new(Date.today)
#=> #<Date: 2014-06-09 ((2456818j,0s,0n),+0s,2299161j)>
irb(main):011:0> d.date
#=> #<Date: 2046-02-15 ((2468392j,0s,0n),+0s,2299161j)>
irb(main):012:0> d.to_time
#=> 2014-06-09 00:00:00 -0500

So date gives me a date 32 years in the future, but to_time gives me today. That’s the little bomb I planted for myself when I used SimpleDelegator, which happily forwards any method it doesn’t know about to the object I instantiated it with.

Delegation only hurts us, so let’s go back to a simpler approach.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Gigasecond
  def initialize(start_date)
    self.start_date = start_date
  end

  def date
    start_date.gigaseconds_since(1)
  end

  private

  attr_accessor :start_date
end

Put everything together and here’s the final implementation:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
require 'delegate'
class Gigasecond
  def initialize(start_date)
    self.start_date = start_date
  end

  def date
    start_date.gigaseconds_since(1)
  end

  private

  attr_accessor :start_date
end

class Date
  SECONDS_PER_DAY = 86400
  def gigaseconds_since(multiple)
    self + (multiple.gigaseconds / SECONDS_PER_DAY)
  end
end

class Time
  def gigaseconds_since(multiple)
    self + multiple.gigaseconds
  end
end

class Numeric
  def gigaseconds
    self * 1_000_000_000
  end
  alias :gigasecond :gigaseconds
end

Summary

And that’s how you take a dead-simple problem and talk about it for 1000+ words. For the problem as stated, the initial implementation is just fine. In my quick survey of the solutions on Exercism, nearly everyone does it exactly that way. But that doesn’t mean you have to leave it there. That code solves only one problem, how to add 1 gigasecond to a Date. If you were a client of that code, would you be surprised that it was so limited? I would.

There’s certainly an argument to be made for YAGNI. No one has asked for time, or for multiple gigaseconds, so why bother? But I’d argue that the initial implmenation was unfinished. It was like finding a huge cookbook that only contained a single recipe, “How to make toast.” We’re not so much adding unnecessary features as we are adding the implementation that I would expect if I used this code. The final version more fully completes the gigasecond functionality so that you don’t have to keep rewriting it to handle new features. An extra hour’s worth of work at the beginning might save you a ton of time down the road.

written in

Exercism: The Hamming Exercise

The Readme

The Test Suite

We need to find the differences between two strings. But those strings are also very array-like; as in, “On this DNA strand there’s a G at the first position.” And Ruby already has a lot of nice syntax for comparing arrays. So I started thinking about this problem as one with an array-focused solution.

I also thought about the Ruby Set library, but that won’t work because Sets want unique objects, so a “G” in the first position and a “G” in the 8th position would be squished down to just one “G”.

1
2
3
4
strand = %w(g a t t a g)
=> ["g", "a", "t", "t", "a", "g"]
strand.to_set
=> #<Set: {"g", "a", "t"}>

That won’t work.

So, following my standard approach, I did the easiest fastest thing that got the tests passing. And it looked like this

1
2
3
4
5
6
7
8
9
10
11
class Hamming
  def self.compute(a,b)
    a = a.chars
    b = b.chars
    max = (a.count < b.count) ? a.count : b.count
    a = a[0,max]
    b = b[0,max]
    y = a.zip(b)
    y.inject(0) {|ret, h| ret += 1 if h.first != h.last; ret}
  end
end

Oh, yeah, that’s nasty. The duplicate logic; the weird, meaningless sorting and trimming of arrays; the block that takes a few seconds to puzzle out. It’s rough stuff.

But, it passed my tests, and that’s all I wanted. With tests green, I can refactor. And refactor I did! Removing the array trimming was an easy fix. Then I began to pull out Strand behaviors

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Strand
  include Enumerable

  attr_accessor :collection

  def self.parse(strand_string)
    self.new(strand_string)
  end

  def initialize(strand_string)
    self.collection = strand_string.chars
  end

  def each(&block)
    collection.each(&block)
  end
end

Like I said, I quickly thought of a Strand as being array-ish. So Strand has a way of parsing a string into an array and then acts as a thin wrapper around the array.

But if an array for each Strand is good, then surely one more array must be better! After noticing the behaviors that required two Strands, I decided to contain that logic in a Strands object.

This was a mistake. And even when I tried to make it better it was still a mistake. The compute method became nedlessly complex and tied to its knowledge of both Strand and Strands. Time to ditch this and revisit how I originally wanted to solve this problem.

I was initially drawn to a Strand as an array, largely because of Ruby’s implementation of array subtraction/diffing/etc. The Ruby implementaition of subtraction doesn’t work for Hamming, but I still liked that syntax. In my perfect world, this would work:

1
hamming_differences = Strand.parse('GAT')  Strand.parse('AAT')

Your own desires are a great design tool. This is the syntax you want to see? Write code that makes it happen. Don’t know how? Learn!

So I ditched Strands. As I say in the commit:

Strands didn’t really give me anything beyond a way to manipulate two Strand objects. It seemed unecessary if I could have a Strand know how to do the maniuplation. Like, if I want to add 1 + 2, I don’t have an Integers object that does the math, the Integer object knows how to do it.

And, after I googled “What the hell is Hamming?” and discovered that it is a very generic idea used all the time, I also decide to separate the Hamming logic in such a way that I can add it to any class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Hamming
  def self.compute(a,b)
    (Strand.parse(a)  Strand.parse(b)).count
  end
end

module Hammable
  def (other)
    sorted = [self,other].sort_by { |x| x.count }
    combined = sorted.first.zip(sorted.last)
    x = combined.select {|strand_set| strand_set.first != strand_set.last}
    self.class.new(x)
  end
end

class Strand
  include Enumerable
  include Hammable

  def self.parse(strand_string)
    self.new(strand_string.chars)
  end

  def initialize(strand_array)
    self.collection = Array(strand_array)
  end

  def each(&block)
    collection.each(&block)
  end

  private

  attr_accessor :collection
end

Now I have a Hammable module I can add to whatever. The - method implementaiton is ugly, but it works.

I started to doubt the wisdom of overriding the array - method. It is probably a really dumb idea. Imagine the unexepcted surprises. Want to subtract two normal arrays, it works one way. Subtract two hammable arrays and it’s totally different. Yikes! And if you had one hammable and one normal array, the behavior would change depending on the order you used. Terrible. So a better approach is to define a new method, say hamming_difference

1
Strand.parse(a).hamming_difference(Strand.parse(b))

It’s a better idea, but it’s not anywhere as nice to read. Oh, well. Sometimes we can’t have everything we want.

And there’s the question of what type of object it should return. Right now it will return a Strand as it was called by a Strand. But a better approach might be to have it return a HammingDifference object (itself just a thin wrapper around an array).

1
2
3
4
5
6
7
8
9
10
module Hammable
  def hamming_difference(other)
    sorted = [self,other].sort_by { |x| x.count }
    combined = sorted.first.zip(sorted.last)
    x = combined.select {|strand_set| strand_set.first != strand_set.last}
    HammingDifference.new(x)
  end
end

class HammingDifference < SimpleDelegator; end

But what does that give us? Is there an appreciable difference between Hamming and HammingDifference? If so, I’m not seeing it. So now, maybe after all this code re-arranging, we should move the Hamming logic back under Hamming.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Hamming
  def self.compute(a, b, type = Strand)
    self.new(a, b, type).count
  end

  def initialize(a, b, type)
    self.one = type.parse(a)
    self.two = type.parse(b)
  end

  def count
    difference.count
  end

  def difference
    one.zip(two).select {|pair| Comparison.different?(pair)}
  end

  class Comparison
    def self.different?(couple)
      couple.first != couple.last &&
      !couple.last.nil? &&
      !couple.first.nil?
    end
  end

  private

  attr_accessor :one, :two
end

require 'delegate'
class Strand < SimpleDelegator
  def self.parse(strand_string)
    self.new(strand_string.chars)
  end
end

And finally, maybe, I’m ok with the code. Well, the attributes inside of Hamming are badly named, but that’s minor. compute has become a declarative builder method that instantiates an instance of Hamming and call’s the instance’s count method.

Gone is Hammable, since its logic now lives in Hamming. We no longer have to mix that code into Strand, though we do now have to tell what kind of objects Hamming should create. I’ve defaulted it to Strand here, but Hamming could easily work with other strings we want to find the Hamming distance of.

Comparison lives inside of the Hamming namespace, which looks a little weird. Why not just have difference? be a method in Hamming? Well, I wanted to encapsulate the logic of what constitutes a difference between two strands: both strands have to have a letter and those letters have to be different from one another. And to encapsulate the logic inside of a Hamming method seemed weird. Because somewhere in the code we’nd end up calling self.different?(pair) and that wasn’t a method I wanted a Hamming instance to have. For some reason. Even I’m a little unclear on why I didn’t like it. Further proof that code design is frequently more an art that a science.

Summary

I think the code here is more reasonable than my work on the Bob problem. There’s less cleverness for cleverness’ sake. Hamming works easily with Strand objects, but can work with other objects. The code is simple enough to understand. I can see this implementation being one that I’d be happy to work with.

Possible dowsides are that the array manipulation will make this slow. I’m sure there’s a much better algorithm out there for figuring out the hamming distance. But Exercism isn’t about performance tuning, so I’m not going to worry about that.

written in

RSS Fixed

If you had tried to get to this site through an RSS feed, you probably encountered some problems. Should be fixed now. Sorry about that.

written in

Exercism: The Bob Exercise

The exercise’s readme

The exercise’s test suite

This is the first exercise you’ll get if you’re working on Ruby in Exercism. And now’s probably a good a time as any to figure out what you want to do with Exercism. Are you using the exercises as a way to learn the language? Or are you trying to improve something else?

With Ruby, I use Exercism as a way to experiment with designs. I’m pretty comfortable with the language (though I learn new things every day), so my interest is more focused on improving the expressiveness and maintainability of my code. That’s my personal focus. It may not be yours. But since I’ll be talking about my code, I should be clear about what my coding goals are.

I’ve found a pretty good approach for working through these exercises, and it’s one I learned from Exercism’s creator, Katrina Owen: Get the tests passing as quickly as possible, then refactor while never letting the tests fail again. The initial code is going to be terrible, and that’s fine. Its main goal is to get your tests passing. With that safety net in place, you can start to fix the most egregious mistakes. Keep chipping away at the parts of the code you dislke. Eventually you’ll find that you like it. And, bonus, your tests will still pass.

I’d like to include specific Git commits in these posts, but for the Bob exercise I didn’t commit until the very end. So I’ll have to recurstruct.

My initial solution, like many on Exercism, started off like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Bob
  def hey(statement)
    if statement.strip.empty?
      "Fine. Be that way!"
    elsif statement.upcase == statement && statement.downcase != statement
      "Woah, chill out!"
    elsif statement.end_with?("?")
      "Sure."
    else
      "Whatever."
    end
  end
end

Some people use more regex, while I like to stick to sugar methods like end_with (which I learned about thanks to this exercise!). Whichever. The end point is the same, you have an if statement that handles your 4 repsonses. Tests passing and we got here pretty quickly.

Based on a totally unrandom sampling, I’d say this is where most people on Exercism stop. That might be because they don’t get nitpicks, or because they like their solutions. I couldn’t say. But since I want to nitpick my designs and try new approaches, I kept going and started to rewrite the code I don’t like.

My first target is the conditionals. What is special about statement.strip.empty??. It doesn’t communicate its meaning.

Refactoring 1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class Bob
  def hey(statement)
    if silence?(statement)
      "Fine. Be that way!"
    elsif yelling?(statement)
      "Woah, chill out!"
    elsif question?(statement)
      "Sure."
    else
      "Whatever."
    end
  end

  def silence?(statement)
    statement.strip.empty?
  end

  def yelling?(statement)
    statement.upcase == statement && statement.downcase != statement
  end

  def question?(statement)
    statement.end_with?("?")
  end
end

And now I know why Bob is responding “Woah, chill out!”. It’s because someone was yelling.

But who is yelling?. If you saw the code Bob.new.yelling?, would you think Bob heard someone else yell? Or that Bob was yelling? Would you expect a the method String.new.empty? to tell you that some other random object was empty? No, obviously not. Bob isn’t yelling, Bob is respondiing to yelling. And…hey, those three switches on statement all look pretty similar. So maybe Bob has too many responsibilites in this verision. Mabye Bob needs to respond to different types of statements.

Refactoring 2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
class Bob
  def hey(statement)
    statement = StatementParser.parse(statement)
    if statement.silence?
      "Fine. Be that way!"
    elsif statement.yelling?
      "Woah, chill out!"
    elsif statement.question?
      "Sure."
    else
      "Whatever."
    end
  end
end

class StatementParser
  def self.parse(statement)
    self.new(statement).parse
  end

  def initialize(statement)
    self.statement = statement
  end

  def parse
    if silence?
      Silence.new
    elsif yelling?
      Yelling.new
    elsif question?
      Question.new
    else
      Statement.new
    end
  end

  private

  def silence?
    statement.strip.empty?
  end

  def yelling?
    statement.upcase == statement && statement.downcase != statement
  end

  def question?
    statement.end_with?("?")
  end

  attr_accessor :statement
end

class Statement
  def silence?
    false
  end

  def yelling?
    false
  end

  def question?
    false
  end
end

class Question < Statement
  def question?
    true
  end
end

class Yelling < Statement
  def yelling?
    true
  end
end

class Silence < Statement
  def silence?
    true
  end
end

And that’s…quite a bit bigger. But we have 3 distinct sets of responsibilities now. There’s Bob, who responds to statements. There’s StatementParser that figures out what a statment is. And there’s a small family of Statement objects that answer expressive questions about their nature.

There are some downsides here. You can’t really have Statements with two roles. Like, you couldn’t have a Yelling Question. Given the test suite, that doesn’t seem to be a problem. But it is a trade-off to be aware of. And you can’t have Bob be anything but a surly teenager. Again, the test suite doesn’t indicate that you’ll ever want Bob to to ever grow up. But, let’s imagine that this is the next step. Bob has grown up and now gives the responses of a college student, not a teenager.

Refactoring 3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
class Bob
  attr_accessor :response
  def initialize(response = TeenagerResponse)
    self.response = response
  end

  def hey(query)
    response.to(StatementParser.parse(query))
  end
end

class Response
  def self.to(statement)
    self.new(statement).say
  end

  def initialize(statement)
    self.statement = statement
  end

  def say
    self.send("to#{statement.class.to_s.downcase}".to_sym)
  end

  def method_missing(method_name, *arguments, &block)
    if method_name.to_s =~ /to(.*)/
      to_everything
    else
      super
    end
  end

  private

  attr_accessor :statement
end

class TeenagerResponse < Response
  private

  def to_silence
    "Fine. Be that way!"
  end

  def to_yelling
    "Woah, chill out!"
  end

  def to_question
    "Sure."
  end

  def to_everything
    "Whatever."
  end
end

class CollegeResponse < Response
  def to_silence
    "Broseph!"
  end

  def to_yelling
    "Bro!"
  end

  def to_question
    "Bro?"
  end

  def to_everything
    "Bro."
  end
end

class StatementParser
  def self.parse(statement)
    self.new(statement).parse
  end

  def initialize(statement)
    self.statement = statement
  end

  def parse
    if silence?
      Silence.new
    elsif yelling?
      Yelling.new
    elsif question?
      Question.new
    else
      NullStatement.new
    end
  end

  private

  def silence?
    statement.strip.empty?
  end

  def yelling?
    statement.upcase == statement && statement.downcase != statement
  end

  def question?
    statement.end_with?("?")
  end

  attr_accessor :statement
end

class NullStatement
end

class Question
end

class Yelling
end

class Silence
end

I work at a university, so I can vouch for the total accuracy of those responses.

And, wow, that’s a lot of code. A couple things to note between the last version and this one. We’ve taken the responsibility of responding and put it into Response objects. When we create an instance of Bob, we can set what kind of responses Bob will give. Teenager is still the default as no one ever fully outgrows those teenage years.

And we’ve (kinda) adressed the limitation of implementing a YellingQuestion statement. Define a YellingQuestion class, update the Parser and then give a Response the ability to handle to_yellingquestion and you’re set. It’s not an easy point of extension, but it’s also not a point of extension that we really plan on using.

There are some clear drawbacks. There’s method_missing, for example. I never like it when I reach for the method_missing stick, but it felt OK here. If a set of responses can’t handle the statement, it falls back to its to_everything method. A big downside here is that if I create a set of responses but don’t imlement to_everything then stuff starts breaking.

And there’s that ungainly if statement in the parse method. I tell myself that it’s not too bad because the conditionals are expressive But I’m sure there’s a better solution there. Something that’s easier to maintain and more extensible.

Also worrying are the empty Yelling/Question/Etc. classes. I did this so that I could do a dynamic message sending in the say method. But looking at it now, I think it’s cleverness for its own sake. But I don’t have a good replacement in mind, so let’s just leave that as is.

Summary

The initial implementation was 12 lines of code. The last solution is 123 lines containing 9 classes. One goal of Exercism is to “practice writing expressive code”. Did I do that here? I’m not sure. I have a solution that is obviously more flexible. And I think the responsibilities in my final code are more clear. But there’s no doubt that tracing through the code is harder.

This is in no way the ‘right’ solution. Or, possibly, even a ‘good’ solution. But it is a solution. And it’s an example of the way you can use the problems in Exercism to push your code in suprising directions.

Up next, the Hamming exercise, which both has a great name and a really fun implementation!

written in

Exercism.io Nitpicking, Introduction

You may be familiar with exercism.io. If not, they do a pretty good job of describing their goals:

Exercism provides a space to think deeply about simple, expressive, and readable code, and experiment and discuss what good looks like.

After joining, you can pick what languages you want to work in. Then you get exercises in those languages, usually just a Readme and a test suite. Then you implement code that passes the tests. When you finish coding, you push your files to Exercism and people who have done the same exercise get to ‘nitpick’ your work. Again, Exercism does a great job explaining how to nitpick

Nitpicking is the killer feature of Exercsim. In most cases the exercises are not brain-busting challenges. If you want those, check out Euler. The problems are simple enough that I can get the Ruby ones passing in a short amount of time — though this was not true of the Elixir problems, as I barely know Elixir. The goal of Exercism is not to prove what a great coder you are, but to try to make you a more ‘expressive and readable’ coder.

And, of course, your code is always expressive and readable to you, right? You’re the one that wrote it, so I would hope that you know what it’s doing. But when you get a nitpick that asks why you’e doing some crazy RegEx expression when you could just use downcase, that’s when you learn. On the flip side, nitpicking the exercises of others is illuminating. First, it helps me learn the difficult skill of providing constructive feedback instead of just saying “No, do it this way.” And I learn quite a bit from other programmers’ implementations. Like the chars method. Didn’t even know this existed.

1
2
"xyz".chars
#=> ['x','y','z']

Neat!

The power of nitpicking makes it all the more frustrating that no one does it. There are reasons for that, highest probably being:

  • Most programmers like writing code more than reviewing someone else’s code.
  • Most solutions are really similar, making nitpicking kind of repetitive.
  • Exercism allows people to go to the next exercise before nitpicking or being nitpicked.

I know that Katrina is aware of this problem and trying to solve it, but it’s tricky. The current default solution would be to ‘gamify’ it, which I jokingly suggested to her. This joke did not go over well. So don’t expect “Exercism achiement unlocked!” badges any time soon.

I’m trying to help by doing some nitpicking as I have free time. Another approach I’m going to try is to nitpick my code (alongside the most common solutions) right here on my site. This will let me go a little more in depth.

written in

On Presenters and Being OK With Being Dumb

A page in our app has a lot of tooltips and help text. All of this text is static. But then came this customer request:

“If the record is a Course Fee, show ‘some text’. Otherwise, show ‘other text’.”

written in Read on →

Learnings of March 31

Hey Ian, whad’ya learn today?

That extracting a thorny view/controller combo to a Presenter is painful the first time. But the end result is always worth it.

That two pepole probably sholudn’t separately refactor the same code at the same time. Unless you love merge conflicts, I guess.

That I should always practice presentations before giving them. Which I am doing this time. I just should have done this all the other times.

Sadly I didn’t write any interesting code today, just merged up stuff that I’d already written. So no cool examples to show off.

written in

Validating Associated Plain Ruby Objects

In an application I’m working on, people can leave comments on a record. When they comment, they can also choose to email that comment to their co-workers. The client wanted the application to validate these email addresses and show an error if the commenter entered a bad email address.

written in Read on →

Accepting the Code of Others

For the first 10+ years of my programming career I worked on my own code almost exclusively. Sometimes I’d read the code of others, but I rarely had to alter it.

For a couple of years I worked with another co-worker, but we paired regularly and the code we wrote was usually designed by both of us. So while it wasn’t totally my code, it was code I understood and agreed with.

written in Read on →

Totally Bogus Doubles for Tests

I love me some mocks and stubs. Like many people, I didn’t really get the idea until I did some reading about the technique. And then I didn’t really get it until I did some more reading. And even then I didn’t really get it until I re-wrote a suite of tests like 3 times, each time getting closer to that coding nirvana: a blazingly fast group of tests that exposes dependencies and is resilient to change.

written in Read on →

Creating Images From JSON in Rails

In a recent project I was asked to dynamically generate images that would show the events scheduled in a room. The event data is stored in an external system which I could get access to through its API.

written in Read on →

Closures in Ruby

I’ve been working my way through a presentation that my friend Paul Cantrell made to Ruby.MN a few years ago. It’s a great run-down of closures/blocks/procs/lambdas in Ruby. What are the differences between them, how do they work, what are the pitfalls, etc.

written in Read on →

Semantic Jekyll

Although Jekyll Bootstrap is a great way to get a blog up and running quickly, I’m still someone who is going to tweak the HTML until I get a document structure I want.

written in Read on →

Beginning

A few years ago I had to implement some securty feature, it had to integrate with some Java platform or had XSLT or something…I forget. I couldn’t figure it out, the documentation was opaque and all the blog posts on the subject were unhelpful, dry and lacked any sense of community.

I bemoaned the state of these blogs to some co-workers. In the Ruby world, I said, I could search almost any problem and find a helpful, educational and fun blog post written by “some hipster kid holding a can of PBR.”

Well, I’m not young and I don’t drink PBR, but I might as well have a Ruby blog. Energized both by the Ruby Rogues and my upcoming change of employers, I decided to get a blog rolling.

For ease of use I went with Jekyll Bootstrap and GitHub Pages. This may change if I find some technology I want to dig into, but I’ve been around enough blogging platforms (e.g., I helped Blogger buy a real server back in 2000) to know that lightweight and easy is what I want. Jekyll Bootstrap certainly seems to fit this bill.

Ok, introduction out of the way. Up next, some thoughts on Readme Driven Development, about which I’ll be presenting to Ruby.MN in a couple of weeks. That presentation, should you care to run it yourself in Reveal.JS, is here.

written in