Qt's Home

The Story of a french guy discovering the world

Be Careful With Ruby's Assignment Methods

| Comments

The context

So, I am in Dev Bootcamp. Yesterday, while trying to implement a customized class, I tried to make ruby respond to :

1
myClass[index]="new_value"

My buddy and I were trying to get used to Test-Driven Development (If you want more info… Thanks Wikipedia!). So I had a very simple test method that tested if 2 things were equal.
I was testing that this code would return true, just to make sure the whole code of my method was executed.

Come this test, we found out that it wanted a method called “[]=”, so we did. Our method looked something like :

1
2
3
4
def []= index,new_value
  #Do some stuff
  true
end

The problem

So, we got this method, this test, so far so good. Now, we launch the test to check if the result to that first expression is true and… No. The test said that this method just ended up returning the value that we were passing it (In this case “new_value”).

Maybe that method was not doing what it should, so we used the Debugger gem (Docs). With that we just checked step by step what it would be doing.
– First : It is calling the method. The debugger throws us right there in the middle of the method.
– Second : It is actually doing what we want. The data is changed, and it goes through the last line that returns true.
– Third : It does not care about that because on the very next step, the trace you have of this method and what it returns already became “new_value”

We then asked more experienced boots, and came up with a test where we’ld be calling the method like this :
ruby myClass.[]=(index,"new_value") Conclusion of this test : SUCCESS!
This calls the method without any problem, and that is done exactly how we want.

So at that point are conclusions were :
– The method is called
– The method does what it should, even the return
– Its behavior changes when we call it with ruby’s “shortcut”, or syntaxic sugar (More info) and we end up having the return value disappearing, and the second argument becoming that return value.

Assignments methods

Let’s pause on that particular problem for a second to talk about assignment methods.

Assignment methods can be used to assign a value to a particular item, like the name says. But in Ruby, everything is object. So assignment methods are actually methods, simple ones defined just like an initialize. What differenciate them mostly is :
– An assignment method can has any number of spaces before its = when it is called.
– When you call it with the “shortcut”, the behavior will be neglected and replaced.

Now, the important thing… Let me rephrase that : The IMPORTANT thing is how ruby selects what is and what is not an assigment method.

The answer is : It takes the last character of the name of the method, and checks if it is an equal.
If it is, your method will be considered an assignment method.

If we take our problem, the method can clearly be detected as an assignment method. That means that it can be called with spaces between the brackets and the = sign, and even more because ruby has a special shortcut for the methods with such a name (The one we used).
That also means that the behavior will be transformed.

Well, do you want to guess what behavior is the principal one concerned by that statement? You’re right, it’s the return. The rubydoc tells us that using an assignment method, so the shortcut we are talking about, will always return the argument.

So, our code did work. It did do what we wanted it to do. But unless we wanted to redefine primitive ruby behavior by diving in Ruby’s source, there is no way we can make an assignment method return anything else than the thing on the right.

So, guys, be REALLY careful when you call your method with an =. That may help you in some parts, but it may also leave you and your friends, coworkers, teachers craving for answers ;)