def test(args,&block)
yield
end
test 1, {puts "hello"}
Last line doesn't work. How do I pass a block with other arguments?
test(1){ puts "hello" }
or
test(1) do
puts "hello"
end
or
blk = proc{ puts "hello" }
test(1, &blk)
You can check out this https://pine.fm/LearnToProgram/chap_10.html
As #Cary Swoveland suggested we can go slightly deeper.
Any Ruby method can implicitly accept a block. And even though you didn't define it in your method signature you still can capture it and pass further.
So, considering this idea we can do following manipulations with your method:
def test(args, &block)
yield
end
is the same as
def test(args)
yield
end
and the same as
def test(args)
block = Proc.new
block.call
end
When you have this implicit block capturing you'd probably want to add extra check:
def test(args)
if block_given?
block = Proc.new
block.call
else
"no block"
end
end
or
def test(args)
if block_given?
yield
else
"no block"
end
end
So calling these methods will return following:
test("args")
#=> no block
test("args"){ "Hello World" }
#=> "Hello World"
Related
I'm currently working on an interface that allows me to wrap arbitrary method calls with a chain of procs. Without going into too much detail, I currently have an interface that accepts something like this:
class Spy
def initialize
#procs = []
end
def wrap(&block)
#procs << block
end
def execute
original_proc = Proc.new { call_original }
#procs.reduce(original_proc) do |memo, p|
Proc.new { p.call &memo }
end.call
end
def call_original
puts 'in the middle'
end
end
spy = Spy.new
spy.wrap do |&block|
puts 'hello'
block.call
end
spy.wrap do |&block|
block.call
puts 'goodbye'
end
spy.execute
What I'd like to do though is remove the |&block| and block.call from my API and use yield instead.
spy.wrap do
puts 'hello'
yield
end
This didn't work and raised a LocalJumpError: no block given (yield) error.
I've also tried creating methods by passing the proc the define_singleton_method in the reduce, but I haven't had any luck.
def execute
original_proc = Proc.new { call_original }
#procs.reduce(original_proc) do |memo, p|
define_singleton_method :hello, &p
Proc.new { singleton_method(:hello).call(&memo) }
end.call
end
Is there another approach I can use? Is there anyway to yield from a Proc or use the Proc to initialize something that can be yielded to?
Using yield in your wrap block does not make much sense unless you passed a block to the caller itself:
def foo
spy.wrap do
puts "executed in wrap from foo"
yield
end
end
If you call foo without a block it will raise the exception since yield can't find a block to execute. But if you pass a block to foo method then it will be invoked:
foo do
puts "foo block"
end
Will output
executed in wrap from foo
foo block
In conclusion I think you misunderstood how yield works and I don't think it is what you want to achieve here.
Say I have the following code:
def a(n, m, &block)
yield if block_given?
end
def a
# My question is here. When a is called, block might be or might not be
# given. Below line is obvious wrong. How to call b and properly pass
# block to b?
b(1, 2, &block)
end
a # call a without block
a { # call a with a block
puts "in block"
}
Write a() to accept a block. It is implied to be optional, and as Andrew Marshall noted, will be passed along as &nil if not given.
def b(n, m, &block)
yield if block_given?
puts "no block" if !block_given?
end
def a( &block )
b(1, 2, &block)
end
a # call a without block
a { # call a with a block
puts "in block"
}
Output:
no block
in block
I want to write a method which takes a block and if no block given it should use a default block. So I want to have something like this:
def say_hello(name, &block = ->(name) { puts "Hi, #{name}" })
# do something
end
But when I'm trying to do so I'm getting the syntax error.
I know I can deal with my problem using block_given?. But I am interested in first approach.
Am I missing something or this is just not possible?
Some answers suggest using block_given?, but since there is no possibility that a block would be nil or false when it is given, you can simply use ||=.
def say_hello(name, &block)
block ||= ->(name){puts "Hi, #{name}"}
# do something
end
You cannot declare a default block in the method definition, however you can use a little trick to use a custom block if none is given.
def say_hello(name)
block = block_given? ? Proc.new : ->(name) { puts "Hi, #{name}" }
block.call(name)
end
# This example uses a custom block
say_hello('weppos') { |name| puts "Hello, #{name}!" }
# => Hello, weppos!
# This example fallbacks to the default
say_hello('weppos')
# => Hi, weppos!
Let me explain it a little bit. Let's start from a more readable version.
def say_hello(name, &block)
block = block ? block : ->(name) { puts "Hi, #{name}" }
block.call(name)
end
You define the method to accept a block, then you check if block is defined. If not, you assign a custom block. Finally, you execute the block.
Let's enhance it a little bit. You can use block_given? to check if a block is passed
def say_hello(name, &block)
block = block_given? ? block : ->(name) { puts "Hi, #{name}" }
block.call(name)
end
This also allows you to skip the declaration of the block (&block) in the method definition.
def say_hello(name)
if block_given?
yield name
else
# This is rendundant, but it's for clarity
block = ->(name) { puts "Hi, #{name}" }
block.call(name)
end
end
But, at this point, you can also use the Proc.new to assign the block to a variable.
def say_hello(name)
block = block_given? ? Proc.new : ->(name) { puts "Hi, #{name}" }
block.call(name)
end
As a final word, I'm trying to understand when this approach would make sense. In most cases, you can probably wrap the code in a class or module and pass it as argument. It's probably better.
You can do it with regular lambdas.
def say_hello(name, block = ->(name) { puts "Hi, #{name}" })
block.call(name)
end
say_hello("Sergio")
say_hello("Ivan", ->(name) { puts "Where are you from, #{name}?"})
# >> Hi, Sergio
# >> Where are you from, Ivan?
Not sure if you can do this with blocks, though. A block is not an ordinary parameter.
No, you can't provide a default block value in a method definition. You can, however, achieve the equivalent behavior through the use of block_given? within the body of the method, as follows:
def say_hello(name, &block)
block = ->(name) { puts "Hi, #{name}" } unless block_given?
# do something
end
However, in this scenario you can't utilize yield to invoke any block that is passed in, since it won't be there in the default case. You'll have to invoke the block Proc object, as in block.(name).
Could anyone please help me to understand the difference between "yield self" and "yield"?
class YieldFirstLast
attr_accessor :first, :last
def initialize(first = nil, last = nil)
#first = first
#last = last
yield self if block_given?
end
def hello
puts "#{#first} #{#last} says hello!"
end
end
In the case of yield self, self is the argument passed to the block. With simply yield, no argument is passed. self is not special here, anything could be yielded, e.g.
class Foo
def a() yield self end
def b() yield end
def c() yield "Bar" end
def d() yield 1, 2, "scuba" end
def to_s() "A!" end
end
Foo.new.a {|x| puts x } #=> A!
Foo.new.b {|x| puts x } #=> (a blank line, nil was yielded)
Foo.new.c {|x| puts x } #=> Bar
Foo.new.d {|x, y, z| puts z } #=> scuba
yield self enters block, associated with method call, passing current object as argument to the block, plain yield just enters the block without passing any arguments.
Think of yield as invoking your block and yield self is invoking your block with the current instance as the parameter.
Assuming I have the following proc:
a = Proc.new do
puts "start"
yield
puts "end"
end
Also assuming I pass a to another method which subsequently calls instance_eval on another class with that block, how can I now pass a block onto the end of that method which gets yielded in a.
For example:
def do_something(a,&b)
AnotherClass.instance_eval(&a) # how can I pass b to a here?
end
a = Proc.new do
puts "start"
yield
puts "end"
end
do_something(a) do
puts "this block is b!"
end
Output should of course should be:
start
this block is b!
end
How can I pass the secondary block to a in the instance_eval?
I need something like this for the basis of a Ruby templating system I'm working on.
You can't use yield in a. Rather, you have to pass a Proc object. This would be the new code:
def do_something(a,&b)
AnotherClass.instance_exec(b, &a)
end
a = Proc.new do |b|
puts "start"
b.call
puts "end"
end
do_something(a) do
puts "this block is b!"
end
yield is only for methods. In this new code, I used instance_exec (new in Ruby 1.9) which allows you to pass parameters to the block. Because of that, we can pass the Proc object b as a parameter to a, which can call it with Proc#call().
a=Proc.new do |b|
puts "start"
b.call
puts "end"
end
def do_something(a,&b)
AnotherClass.instance_eval { a.call(b) }
end