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
|
|
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 |
|
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:
What does it mean? Let’s try splat operator in IRB:
1 2 |
|
and with that extension:
1 2 |
|
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!