blog.absurd:li - press play on tape
March 18th 2010
Tagged ruby, anti pattern

A few Ruby Anti-Patterns

Some things you can do with Ruby you just shouldn’t. Often one doesn’t notice until later on, when you are looking for a bug in code.

rescue everything

This is a story from real life. In our recent release of net-ldap we have this snippet of code:


  begin
    @socket = TCPSocket.new(host, port)
  rescue
    raise LdapError, "Something is wrong with your connection."
  end

So when you get the ‘Something is wrong …’ error, where do you go look? Right, you check and recheck your credentials.

Except that wasn’t the problem. We recently reorganized the project and fixed some Ruby 1.9 stuff. This was the reason for our rerelease as well. During that work, we (ok, probably I did) fat-fingered the import of ‘socket’ with the obvious NameError as a consequence.

The above code will tell you that something is wrong with the connection even if nothing is. Look at how I think this should be written:


  begin
    @conn = TCPSocket.new( server[:host], server[:port] )
  rescue SocketError
    raise LdapError, "No such address or other socket error."
  rescue Errno::ECONNREFUSED
    raise LdapError, "Server #{server[:host]} refused connection on port #{server[:port]}."
  end

We could dispute the details here, but the main point is that you should almost never rescue everything. This is an anti-pattern in Ruby and in other languages as well. If you don’t know what happened, make it scream really loud.

There is another big anti-pattern hidden in this war story: Write tests for your code. Because that would have told me what was wrong.

Extending basic Types

Let’s say I have a few derivates of Ruby basic types with strings attached:


  class StringString < String
    attr_accessor :strings
  end
  class StringArray < Array
    attr_accessor :strings
  end
  
  ary = [
    StringString.new('foo'),
    StringArray.new([StringString.new('bar')])
  ]

I want to create a clean YAML representation of this, looking something like


  ---
  - foo
  - - bar

But turning what I have into YAML I get this:


  --- 
  - !str:StringString foo
  - !seq:StringArray 
    - !str:StringString bar

And of course, turning it to YAML is just one of the problems you have. Everywhere you use inspection/reflection in your code, these StringString things won’t behave correctly.

Stop and think about how you would do it for a second. There seems to be no real easy way to do this, even when you own the StringString and StringArray types. Of course, if you knew you only had strings, you could use #to_s / #to_a and be done with it. But polymorphically? What we need is a #detach_strings method.


  def detach_strings(mess)
    mess.class.ancestors[1].new(mess)
  end

  puts detach_strings(StringString.new('bar')).to_yaml
  # => "--- bar\n"

Ugly, isn’t it? Yeah. But in my opinion that is entirely the blame of the extended basic type anti-pattern. I wont spend time on making this beautiful, but rather will transform the code so it doesn’t use extended types anymore.

Your own Anti-Patterns?

Do you have any Ruby anti-patterns from your work? I would be interested in hearing about them.