views:

322

answers:

1

Is it possible to make yield keyword work inside a block given to define_method? Simple example:

class Test
  define_method :test do |&b|
    puts b    # => #<Proc:...>
    yield
  end
end

Test.new.test {
  puts "Hi!"
}

This code produces following error in both Ruby 1.8.7 and 1.9.0:

test.rb:4:in `test': no block given (LocalJumpError) from test.rb:8

The strange thing is the b block variable != nil but block_given? returns false. Is it intentional Ruby behaviour not to recognize blocks by Proc objects?

Edit: Regards to Beerlington's answer: b.call() is is not what I am looking for. Block variable was used only to indicate that block is actually given, and is not detected inside define_method.

Reason why I need use yield instead of block.call

I am willing to write some extension to the way how new classes are defined in Ruby, thus any code You can write in pure Ruby should be accepted when I use my extension.

So similar semantics cannot be taken into consideration, because this forces users of my library to use only one proper way to pass a block. This breaks the TIMTOWTDI rule, and does not make my library transparent.

Real life example

Code below can be simplified to code above since my_def uses define_method:

require 'my_library'

class Test
  # client can write 'my_def' instead of 'def' since
  # my_library extends Class class
  my_def :test, "some parameter" do
    yield        # oh no, error :(
  end
end

Test.new.test {
  puts "Hi!"
}
+6  A: 

I think this is what you're looking for:

class Test
  define_method :test do |&b|
    b.call
  end
end

Test.new.test {
  puts "Hi!"
}

More at http://coderrr.wordpress.com/2008/10/29/using-define_method-with-blocks-in-ruby-18/

Beerlington
Unfortunately this is not what I am looking for. I know I can call block this way, but I need to use yield. b variable was used only to indicate that block is actually given.
Dejw
Wayne Conrad
@Wayne: You may think this is the solution but is not. I did not write the purpose of using `yield` instead of `block.call` and the reason _why_ is in my question now.
Dejw
@Dejw, Thanks for the added explanation. I have to admit that I'm still confused: block.call not only has the same semantics as yield, it has the exact same syntax for the caller (see Beerlington's code: He's not passing the block in as an argument). The caller can't tell which one the method is using. What am I missing?
Wayne Conrad
The problem is that _the caller_ may write `yield` instead of `block.call`. The code I have given is possible caller's code. Extended method definition in my library can be simplified to my code above. Client provides block passed to define_method (body of a method), so he/she can write there anything. Especially `yield`. I can write in documentation that `yield` simply does not work, but I am trying to avoid that, and make my library 100% compatible with Ruby (alow to use any language syntax, not only a subset).
Dejw
@Dejw, Thanks again for your patience in explaining this.
Wayne Conrad