views:

89

answers:

4

If I need to define a method called 'yields' which will call yiled 3 times:


def yields
  3.times do
    yield
  end
end

And then I will use it in an other method:


def call_me_3_times
  yields
end

In the console or irb:


>> call_me_3_times { puts 'me'} # => Cause error
=>  LocalJumpError: no block given (yield)
 from (irb):32:in `yields'
    from (irb):35:in `call_me_3_times'

I hope you can read what I want;
And how to make the 'yields' autolly capture the block given?


I mean that when we use the 'yields',we don't need to pass it a '&block', just like the usage of 'yield'(we don't have to even mustn't pass the '&block' to 'yield', need we?).



+1  A: 

You need given a block to you yields method or avoid yield if no block

no yield if no block :

def yields
  3.times do
    yield if block_given?
  end
end

Pass a block to your yields methods

def call_me_3_times
  yields { puts 'hello' }
end
shingara
Yes, thanks! But I want to go still further more; I need the 'yields' method automatically capture from the 'call_me_3_times', but not pass the block directly to the 'yields' its self. Any idea? : )
Croplio
The usage of 'yields' should exactly the same as 'yield', except the '3 times';
Croplio
+3  A: 

Something like:

def call_me_3_times &block
  yields &block
end
floatless
I upvoted your answer, but actually, I think you don't answer his question : how to do this automatically, the same way as the yield methods does it ?
David
Croplio
Yes, David understands what I want to say;
Croplio
+2  A: 

I tried to look at yield implementation to see if we could reproduce its behaviour, but I think it is a keyword, so there's no way to look at the implementation.

I tried with block_given, and looking at the implementation from the ruby core rdocs, I found that block_given? is implemented this way :

rb_f_block_given_p()
{
  if (ruby_frame->prev && ruby_frame->prev->iter == ITER_CUR && ruby_block)
    return Qtrue;
  return Qfalse;
}

As you see, it's C, so it's too low-level implementation. We can't do the same.

If *block_given?* methods needs to rely on C implementation to just check that a block is given, I can't see how we could get that block and call it within ruby code.

So I think there's no way to do what you want.

David
+1  A: 

A solution to this can be created using the techniques described in this blog post http://weblog.raganwald.com/2008/06/what-does-do-when-used-as-unary.html

def call_me_three_times
  yields &(Proc.new) if block_given?
end

When you define a method as def some_method(&block) ruby will expect you to pass a block to the method. It will convert that block to a Proc and store it in the block variable.

If you prefix a Proc object with an & it will convert it to a block.

If you call Proc.new within a method and do not provide it with a block then it will create a Proc from the block passed to it.

Some test results are below

def yields
  puts "Tripling"
  3.times do 
    yield 
  end
end

def call_me_three_times 
  yields &(Proc.new) if block_given?
end

x="Foo"
call_me_three_times { puts x }
x="Bar"
call_me_three_times { puts x }
call_me_three_times

Output

Tripling
Foo
Foo
Foo
Tripling
Bar
Bar
Bar
Steve Weet