1

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?).



niton
  • 8,771
  • 21
  • 32
  • 52
Croplio
  • 3,433
  • 6
  • 31
  • 37

4 Answers4

4

Something like:

def call_me_3_times &block
  yields &block
end
Daniel O'Hara
  • 13,307
  • 3
  • 46
  • 68
  • 1
    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 Aug 09 '10 at 09:33
  • but I mean that when I use the 'yields',I don't need to pass it a '&block', just like the usage of 'yield'(You don't have to even mustn't pass the '&block' to 'yield'). – Croplio Aug 09 '10 at 09:37
  • Yes, David understands what I want to say; – Croplio Aug 09 '10 at 09:39
2

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
  • 998
  • 7
  • 17
1

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
  • 46,608
  • 11
  • 99
  • 105
  • 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 Aug 09 '10 at 09:29
  • The usage of 'yields' should exactly the same as 'yield', except the '3 times'; – Croplio Aug 09 '10 at 09:32
1

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
  • 28,126
  • 11
  • 70
  • 86