How to pass block to sub method? - ruby

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

Related

Is there any difference in using `yield self` in a method with parameter `&block` and `yield self` in a method without a parameter `&block`?

I understand that
def a(&block)
block.call(self)
end
and
def a()
yield self
end
lead to the same result, if I assume that there is such a block a {}. My question is - since I stumbled over some code like that, whether it makes any difference or if there is any advantage of having (if I do not use the variable/reference block otherwise):
def a(&block)
yield self
end
This is a concrete case where i do not understand the use of &block:
def rule(code, name, &block)
#rules = [] if #rules.nil?
#rules << Rule.new(code, name)
yield self
end
The only advantage I can think of is for introspection:
def foo; end
def bar(&blk); end
method(:foo).parameters #=> []
method(:bar).parameters #=> [[:block, :blk]]
IDEs and documentation generators could take advantage of this. However, it does not affect Ruby's argument passing. When calling a method, you can pass or omit a block, regardless of whether it is declared or invoked.
The main difference between
def pass_block
yield
end
pass_block { 'hi' } #=> 'hi'
and
def pass_proc(&blk)
blk.call
end
pass_proc { 'hi' } #=> 'hi'
is that, blk, an instance of Proc, is an object and therefore can be passed to other methods. By contrast, blocks are not objects and therefore cannot be passed around.
def pass_proc(&blk)
puts "blk.is_a?(Proc)=#{blk.is_a?(Proc)}"
receive_proc(blk)
end
def receive_proc(proc)
proc.call
end
pass_proc { 'ho' }
blk.is_a?(Proc)=true
#=> "ho"

How do I pass a block with other arguments?

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"

define_method doesn't receive block as a parameter ruby

I'm currently doing second week assignment 1 from this metaprogramming tutorial
and have some problems with sending block for using it with define_method. The program simply doesn't see the block, returning false when I call block_given? even though I provide a block.
Here's the file that sends the block:
require_relative 'dog'
lassie, fido, stimpy = %w[Lassie Fido Stimpy].collect{|name| Dog.new(name)}
lassie.can :dance, :poo, :laugh
fido.can :poo
stimpy.can :dance
stimpy.can(:cry){"#{name} cried AHHHH"} # the block that I can't receive
puts lassie.name
p lassie.dance
p lassie.poo
p lassie.laugh
puts
p fido.dance
p fido.poo
p fido.laugh
puts
p stimpy.dance
p stimpy.poo
p stimpy.laugh
p stimpy.cry # method call
And the file that receives:
Dog = Class.new do
MESSAGES = { dance: "is dancing", poo: "is a smelly doggy!", laugh: "finds this hilarious" }
define_method :initialize do |name|
instance_variable_set(:#name, name)
end
define_method :name do
instance_variable_get :#name
end
define_method :can do |*args, &block|
puts block_given? # false
if block_given?
define_method args.to_sym do
yield
end
else
args.each do |ability|
self.class.instance_eval do
define_method "#{ability}".to_sym do
#name + " " + MESSAGES[ability]
end
end
end
end
end
define_method :method_missing do |arg|
puts "#{#name} doesn't understand #{arg}"
end
end
I believe (but haven't checked) block_given? refers to a block being passed to the method defined by the closest lexically enclosing method definition, i.e. def, and does not work inside methods defined with define_method.
I know for a fact that yield only yields to a block being passed to the method defined by the closest lexically enclosing method definition, i.e. def, and does not yield from a block (which, after all, define_method is, it's just a method like any other method which takes a block, and just like any other taking a block, yield yields to the block of the method, not some other block).
It's kind of strange to combine yield and block_given? with explicitly named block-Procs anyway. If you have the name, there is no need for anonymity, you can just say
if block
define_method(args.to_sym) do block.() end
end
Or did you mean to pass the block to define_method to be used as the implementation of the method? Then it would be
if block
define_method(args.to_sym, &block)
end
Not sure if you can pass arguments and block to something that just gets defined. read this
define_method(symbol, method) → symbol
define_method(symbol) { block } → symbol
Instead of define_method :can do |*args, &block| try the explicit def can(*args, &block)
It's weird to do it like that anyway..

Difference between yield self and yield?

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.

How to pass a block to another in Ruby?

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

Resources