Upgrading to Rails 3: Beware of the Object#tap pattern

September 2nd, 2010 at 2:23 pm • permalink2 comments

The advent of Object#returning first, and Object#tap consequently, quickly lead to a very common and elegant pattern in the Rails community to scope a variable to a block in your template code.

Here’s a simple .erb file.

<% "Simone".tap |string| %>
Hello, <%= string %>.
<% end %>

Once executed, in Rails 2.3 the template renders the following content

Hello, Simone.

However, due to some internal changes in the way how Rails 3 handles block helpers, the same template in Rails 3 renders the following content.

Hello, Simone.Simone

Here’s a live example, yielding the StringUSD“.

The template displays a USD string, after the Ruby block

The same statement also causes the following deprecation warning:

DEPRECATION WARNING: <% %> style block helpers are deprecated. Please use <%= %>.

The problem here is the Rails 3 compatibility layer which is responsible to help you migrate your code from Rails 2.3 to the new Rails 3 syntax. Apparently, whenever you are using a block which returns a not nil value, the compatibility layer classifies this behavior as a legacy block helper style.

Rails 3 showing deprecation warnings for Block Helpers

In the meantime of a real solution, you can use the following workaround.

I defined a new method, called Object#yielding, which relies on Object#tap but returns nil instead of self.

class Object

  # Object#yielding is similar to Object#tap,
  # but returns nil instead of self.
  def yielding(&block)
    tap(&block)
    nil
  end

end

Here’s a basic test suite.

require 'test_helper'

class ExtensionsRubyObjectTest < ActiveSupport::TestCase

  test "#yielding should return nil" do
    assert_equal nil, "Hello!".yielding { |string| string }
  end

  test "#yielding should yield self" do
    "Hello!".yielding { |string| assert_equal "Hello!", string }
  end

end

Then, replace your template to use Object#yielding.

<% "Simone".yielding |string| %>
Hello, <%= string %>.
<% end %>

Voilà. The USD string no longer appears in the template.

The template doesn’t display any USD string.

As expected, the deprecation warnings also disappear.

Rails 3 showing no deprecation warnings for Block Helpers

  1. Upgrading to Rails 3: You are using the old router DSL which will be removed in Rails 3.1
  2. Upgrading Rails 2 application to Rails 3 (screencast)
  3. Ruby Proxy Pattern and Dynamic Delegation with ActiveSupport BasicObject
  4. The Road to Rails 3: make your Rails 2.3 project more Rails 3 oriented
  5. RSpec Rails doesn’t render Rails views by default

Filed in Programming • Tags: , , , ,

Comments

[...] been reading doesn’t have a compatibility mode. For example, from Simone Carletti’s The Road to Rails 3: make your Rails 2.3 project more Rails 3 oriented: Don’t try to upgrade to Rails 3 unless you have a reasonable code coverage. And this is not [...]

Gennady Bystritsky says:

Kind of late in the game, yet as I just saw this post here’s what you can do to solve the problem without resorting to “yielding”:

Hello, .

Add a Comment




Follow Me
    Random Quote