Get name of Block while in Block (self) Ruby - ruby

I am wondering how to get the name of a block/proc while in the block that will then be passed to a method. I need the name of a block like so:
method("hello") do
puts "My name is #{self}"
end
Which would print out something like when the method runs the block:
"My name is #<Proc:0xa3de668#/path/to/file.rb:8>"

You can get a reference to the implicitly passed block inside the method yield-ing it, by calling Proc.new (inside the method) without supplying a block. For instance:
def speak
puts yield
block = Proc.new # Creates a proc object from the implictly passed block.
puts block.call
end
speak { "Hello, from implicit block!" }

Can't do this for blocks, but for procs...
def hello
puts yield
end
my_proc = Proc.new {"I am #{my_proc}"}
method("hello").call(&my_proc)
#I am <#Proc:0x0...#...>

Related

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

Yielding a block to a proc (or creating a method that accepts a block from a proc that yields)

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.

Is it possible to declare a method with block as default value?

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

Why doesn't `block_given?` work in this dynamically-defined method?

When I write methods that take an optional block, I typically use something like
block.call if block_given?
However, in method defined dynamically like the one below, block_given? doesn't seem to work.
class Foo
%w[bar baz].each do |method_name|
define_singleton_method(method_name) do |&block|
puts "Was #{method_name} given a block? #{block_given?}"
puts block.call
end
end
end
Foo.bar { puts 'I am a block' }
The block is called as expected, but block_given? returns false.
Why is this?
Blocks are closures, so they remember local variables (eg method_name). They also remember blocks: yield and block_given? are looking for the block that was active at the time that define_method was called, not the block passed to bar. There wasn't one, so block given returns false.
A better illustration of this is
def create_method
define_singleton_method('foo') do |&block|
puts "Was given a block? #{block_given?}"
puts yield
puts block.call
end
end
create_method {'block passed to create_method'}
foo {'block passed to the created method'}
which outputs
Was given a block? true
block passed to create_method
block passed to the created method

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