Question

I'm trying to upgrade a Ruby 1.8 app to 1.9 and hit a bit of a roadblock here. In Ruby 1.8.7, I can pass on a block to Builder 3.0.0 and it gets called as expected:

1.8.7 :003 > @builder = Builder::XmlMarkup.new
1.8.7 :004 > block = lambda { puts "foo" }
1.8.7 :005 > @builder.tag(&block)
foo

But in 1.9, I get this error:

1.9.3p0 :002 > @builder = Builder::XmlMarkup.new
1.9.3p0 :003 > block = lambda { puts "foo" }
1.9.3p0 :004 > @builder.content(&block)
ArgumentError: wrong number of arguments (1 for 0)
  from (irb):3:in `block in irb_binding'
  from /Users/dev/.bundle/ruby/1.9.1/gems/builder-3.0.0/lib/builder/xmlbase.rb:155:in `call'
  ...

And rewriting that as a stabby lambda (which is just syntactic sugar, right?) doesn't help:

1.9.3p0 :006 > block = -> { puts "foo" }
1.9.3p0 :007 > @builder.content(&block)
ArgumentError: wrong number of arguments (1 for 0)

Passing an actual block instead of a reference to one does work:

1.9.3p0 :008 > @builder.content { puts "foo" }
foo

Help?

Was it helpful?

Solution

That is actually because in ruby 1.9, lambda and proc behave subtly differently. Think of lambda, being mathematically precise, requiring the exact number of arguments specified, while proc exhibits the more permissive behavior of ruby 1.8. For example,

a = lambda {|v| p v }
a.call()     # ArgumentError: wrong number of arguments (0 for 1)
a.call(1)    # prints "1"
a.call(1, 2) # ArgumentError: wrong number of arguments (2 for 1)

b = proc {|v| p v }
b.call()     # prints "nil"
b.call(1)    # prints "1"
b.call(1, 2) # prints "1"

Note that both objects are of type Proc, but can be distinguished from each other by the .lambda? method.

a.class   # => Proc
a.lambda? # => true
a.arity   # => 1  (number of parameters)
b.class   # => Proc
b.lambda? # => false
b.arity   # => 1  (number of parameters)

OTHER TIPS

Ooh, figured it out. The line causing the problem in Builder is this:

block.call(self)

In other words, it passes itself as an argument to the block. In Ruby 1.8, the block is free to ignore this, but in 1.9, it must declare all arguments. Thus:

1.9.3p0 :023 > block = lambda { |dummy| puts "foo" }
1.9.3p0 :024 > @builder.content(&block)
foo

Yay!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top