views:

326

answers:

1

I ran in to an interesting problem while using the 'tap' method on objects of type 'String'.

"abc".tap { |o| o = "xyz" } # this line returns "abc" instead of "xyz"

The 'tap' method works on objects of other types.

[].tap { |o| o << "xyz" } # this line returns ["xyz"] as expected

I am using Rails 2.3.2 and Ruby 1.8.6 on Windows XP.

What am I missing here?

Update 1: I resolved this issue. It was an usage error on my part. In the first scenario I was re-assigning the value to the block parameter instead of modifying it. I was able to rewrite the code and get the expected result.

"abc".tap { |o| o.replace "xyz" }

Update 2: Code used here is just to demonstrate the problem. My actual code does not look like this.

A: 

Object#tap always returns the original object passed into it after executing the block, even if the block returns something different.

The benefit here is that you can put tap into the middle of a chain and test the value of an object without modifying the original value.

This means that if you pass "abc" into tap, it'll execute the block and return "abc". Your assignment to o doesn't mean anything due to scoping. Likewise, if you pass an empty array into tap, it would return an empty array back. However, in your second example, you modified the original value. Operations that modify the original such as << or gsub! or likewise will modify the object before its returned, therefore you get a different value back.

Check out http://moonbase.rydia.net/mental/blog/programming/eavesdropping-on-expressions for some more cool examples about how to use tap. Don't use it for assignment or modification or you'll get back wonky results.

Update: To your edit, why does that resolve your issue? I don't understand why you'd call tap to do a replace when you can just call replace on the object itself. What benefit is tap providing you?

John Pignata
Yes, it doesn't make sense to use 'tap' to do a simple replace. My actual code does not look like this.Instead of posting a big chunk of code, I came up with this example to reproduce the problem.I tend to use 'tap' for debugging. Recently I have started using 'tap' in-place of 'returning'.Once I re-read the Rails source code for tap method, my error became evident.
KandadaBoggu