Consulting. Training. Development.

Make love, not Ruby core extensions

It all started from this comment in Devise’s issue tracker. As you can see “logging in” feature didn’t work for the user (well it’s a main feature of the Devise gem and this should work for sure). And I decided to take a deep look at this issue.

So as you can see from the backtrace the error was about order method:

1
ArgumentError (The method .order() must contain arguments):

Yeah, this ArgumentError was added to the Rails 4.0 and it raises when you’re trying to call order method with blank arguments. Honestly, I don’t see any reason for adding such “feature” to Rails (you can find an explanation of this behaviour here). Anyway let’s go further.

If you didn’t know Devise uses orm_adapter gem to access different databases with different ORMs. If you look at the backtrace you’ll see this line of code. So we’re calling order with some params. After some time spent on debugging I figured that order_clause(order) returns blank string ("") in our Devise-specific case. But the thing is that order method accepts a blank string since order method uses splat operator and our blank string becomes an array with a blank string ([""]) which returns false for blank?. So why the hell it doesn’t work?

And then I looked at the lib app and I saw this (you can find that app here):

1
2
3
4
5
class String
  def to_a
    split(',').map(&:strip)
  end
end

Yeah, that’s it. Just a simple Ruby core extension to the String class. What bad can happen?

But let’s look closer to the RubySpec of splat operator:

it assigns the splatted object contained into an array when the splatted object doesn’t respond to to_a

What does it mean? Let’s try splat operator in IRB:

1
2
1.9.3-p286 :001 > a = *""
 => [""]

and with that extension:

1
2
1.9.3-p286 :001 > a = *""
 => []

See the difference? With this extension it becomes a blank array [] which returns true for blank? test.

Why? According to the RubySpec

it assigns the returned value of to_a when the splatted object responds to to_a

Since we have method to_a defined on String object it’s predicted behaviour for the Ruby but not for our app and Rails in particular. And ActiveRecord fails because it knows nothing about this.

So please, be careful with extensions to the Ruby core classes!

Comments