Pass block passed to method to another method in Ruby
You can reference the block explicitly
def discard(&block)
self - self.keep(&block)
end
or implicitly
def discard
self - self.keep(&Proc.new {})
end
In your case, I would suggest the first approach.
Passing block from one method to another
Short answer: Always use yield
, unless you have a good reason to explicitly reference &block
.
See: Why blocks make ruby methods 439% slower
With &block
, you get a reified Proc on which you can do all kinds of stuff and which you can move around. However, with a yield
and an implicit block, you are limited to only calling the block.
By using yield
, the interpreter can bypass all the Proc reification as it knows the developer won't be able to use it; hence it can keep just a C-level structure instead of having to set up a Ruby-level object.
How to pass a block to another in Ruby?
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()
.
Passing block into a method - Ruby
The difference between do .. end
and curly braces is that the curly braces bind to the rightmost expression, while do .. end
bind to the leftmost one. Observe the following examples:
def first(x=nil)
puts " first(#{x.inspect}): #{block_given? ? "GOT BLOCK" : "no block"}"
"f"
end
def second(x=nil)
puts " second(#{x.inspect}): #{block_given? ? "GOT BLOCK" : "no block"}"
"s"
end
first second do |x| :ok end # second(nil): no block
# first("s"): GOT BLOCK
first second {|x| :ok } # second(nil): GOT BLOCK
# first("s"): no block
In the first case, the block made with do..end
will be bound to the first function (leftmost). In the second case the block made with curly brackets will be bound to the second function (rightmost).
Usually it's good idea to use parentheses if you have two functions and a block - just for readability and to avoid mistakes.
It's very easy to accidentally pass a block to puts
method, just as in your question.
Why can't we pass a {} block with other parameters to a method in Ruby
You can only pass other objects as arguments, and blocks are not objects in Ruby. Blocks are syntactic constructs.
But they can easily be wrapped in objects, objects of Proc
class. There is even a special shorthand operator for that:
method4(true, 4, &->{puts 'Hello World'})
A short explanation of above code:
->{}
creates a Proc
object around given block. As method4
requires a block, and not an object, you need to "unwrap" it to block once more, and that's why ampersand is there. If the signature of method4
were instead:
def method4(condition, attribute, proc) # note no ampersand
you would be able to omit the ampersand in method call too.
Ruby - Passing Blocks To Methods
The code by David will work fine, but this is an easier and shorter solution:
foo = Proc.new { |prompt| prompt.echo = false }
new_pass = ask("Enter your new password: ", &foo)
verify_pass = ask("Enter again to verify: ", &foo)
You can also use an ampersand to assign a block to a variable when defining a method:
def ask(msg, &block)
puts block.inspect
end
Is there a way to pass a block into Class::new as an argument?
As both the documentation of Class::new
and the error message clearly show, the block is passed one argument [bold emphasis mine]:
new(super_class=Object) { |mod| ... }
→a_class
[…]
If a block is given, it is passed the class object, and the block is evaluated in the context of this class like class_eval.
Also, in the error message you posted:
ArgumentError (wrong number of arguments (given 1, expected 0))
The error message is saying that your lambda was passed one argument, but it is expecting none.
The fix is easy: add a parameter to your lambda:
class_body = -> * do
def foo
'bar'
end
end
klass = Class.new(&class_body)
Or, if you don't absolutely require a lambda, you could use a non-lambda Proc
:
class_body = proc do
def foo
'bar'
end
end
klass = Class.new(&class_body)
Passing a block to a dynamically created method
If I understood you correctly, you can solve this by saving passed block to an instance variable on class object and then evaling that in instance methods.
bl.call
won't do here, because it will execute in the original context (that of a class) and you need to execute it in scope of this current instance.
module MyMod
def is_my_modiable(&block)
class_eval do
@stored_block = block # back up block
def new_method
bl = self.class.instance_variable_get(:@stored_block) # get from class and execute
instance_eval(&bl) if bl
self.mod = true
self.save!
end
end
end
end
class MyClass
extend MyMod
is_my_modiable do
puts "in my modiable block"
self.something_special
end
def something_special
puts "in something special"
end
attr_accessor :mod
def save!; end
end
MyClass.new.new_method
# >> in my modiable block
# >> in something special
Related Topics
Best Practices For Reusing Code Between Controllers in Ruby on Rails
Unresolved Specs During Gem::Specification.Reset:
How to Write Postgresql Functions on Ruby on Rails
Learning Insertion Sort in Ruby
Convert Time from One Time Zone to Another in Rails
How to Set Up the Database.Yml File in Rails
How to Use Activerecord in a Ruby Script Outside Rails
How to Wrap Link_To Around Some HTML Ruby Code
How to Use Bundler Behind a Proxy
Rails4 Unknown Encoding Name - Cp720
What Is the Use of Gemfile in Rails
Difference Between Print and Puts
How to Install Therubyracer Gem on 10.10 Yosemite
Convert To/From Datetime and Time in Ruby