Or simply, how could this code be written in a less repeating version? Or maybe more efficiently?
if block_given?
render(*options, &block)
else
render(*options)
end
Use block parameter. It handles both situations.
def foo(*options, &block)
bar(*options, &block)
end
Example:
def bar(*options)
p options
p yield if block_given?
end
foo(1)
# >> [1]
foo(2) { 'hello' }
# >> [2]
# >> "hello"
Related
I often memoize Ruby methods using the begin ... end block syntax:
$memo = {}
def calculate(something)
$memo[something] ||= begin
perform_calculation(something)
end
end
However, there's a gotcha here. If I return early from the begin ... end block via a guard clause, the result is not memoized:
$memo = {}
def calculate(something)
$memo[something] ||= begin
return 'foo' if something == 'bar'
perform_calculation(something)
end
end
# does not memoize 'bar'; method will be run each time
I can avoid this by avoiding the return statement:
$memo = {}
def calculate(something)
$memo[something] ||= begin
if something == 'bar'
'foo'
else
perform_calculation(something)
end
end
end
This works, but I don't love it because:
it's easy to forget I'm not allowed to use return in this case.
with many conditions it clutters up the code as opposed to the guard clause.
Is there a better idiom for this besides just avoiding return?
As far as I know, begin...end cannot be short-circuited. You can do exactly what you're trying to do with procs though:
$memo = {}
def calculate(something)
$memo[something] ||= -> do
return 'foo' if something == 'bar'
perform_calculation(something)
end.call
end
That being said, I've never seen this done before, so it's certainly not idiomatic.
I'd add another layer:
def calculate(something)
$memo[something] ||= _calculate(something)
end
def _calculate(something)
return if something == 'bar'
perform_calculation(something) # or maybe inline this then
end
This has the additional benefit of providing you with a method you could call whenever you want to be sure to get a freshly computed result. I would spend some more time on the method naming though.
One way to tackle this is with meta-programming where you wrap the method after it's defined. This preserves any behaviour in it:
def memoize(method_name)
implementation = method(method_name)
cache = Hash.new do |h, k|
h[k] = implementation.call(*k)
end
define_method(method_name) do |*args|
cache[args]
end
end
This creates a closure variable which acts as a cache. That avoids the ugly global, but it also means you can't really clear out that cache if you need to, so if you pass in a large number of different arguments it could end up consuming a lot of memory. Be cautious! That functionality could be added if necessary by defining some auxiliary method like x_forget for any given method x.
Here's how it works:
def calculate(n)
return n if (n < 1)
n + 2
end
memoize(:calculate)
Then you can see:
10.times do |i|
p '%d=%d' % [ i % 5, calculate(i % 5) ]
end
# => "0=0"
# => "1=3"
# => "2=4"
# => "3=5"
# => "4=6"
# => "0=0"
# => "1=3"
# => "2=4"
# => "3=5"
# => "4=6"
I fear I don't understand the question correctly, as it would seem something quite simple would do.
$memo = {}
def calculate(something)
$memo[something] ||= something == 'bar' ? 'foo' : perform_calculation(something)
end
Let's try it.
def perform_calculation(something)
'baz'
end
calculate('bar')
#=> "foo"
$memo
#=> {"bar"=>"foo"}
calculate('baz')
#=> "baz"
$memo
#=> {"bar"=>"foo", "baz"=>"baz"}
calculate('bar')
#=> "foo"
$memo
#=> {"bar"=>"foo", "baz"=>"baz"}
I don't know a solution using return but for the guard clause in your example I would use case.
$memo = {}
def calculate(something)
$memo[something] ||= case something
when 'foo' then 'bar'
else perform_calculation(something)
end
end
You could use break statements in a #tap block:
def val
#val ||= default.tap do
break val1 if cond1
break val2 if cond2
break val3 if cond3
end
end
You could also use 1.6's #then or 1.5's #yield_self, but then don't forget to return the default value at the end of the block, or it'll default to nil. In your example it's not a problem since the default is only evaluated at the end:
def calculate(something)
#calculate ||= {}
return #calculate[something] if #calculate.key?(something)
#calculate[something] = something.then do |something|
break 'foo' if something == 'bar'
perform_calculation(something)
end
end
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"
I am writing a program that utilizes define_method, but I don't understand how I could define a method like this one:
def mymethod(variable) do
puts variable
puts yield
end
Which can be called by:
mymethod("hi") do
# this is yield
end
You cannot use yield. You need to receive it as a proc object.
define_method(:mymethod) do |variable, &block|
puts variable
puts block.call
end
mymethod("foo"){"bar"}
# foo
# bar
mymethod("foo") do "bar" end
# foo
# bar
define_method :my_method do |str, &block|
# Do something here
yield # or block.call
# Do something here
end
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
class Setter
attr_accessor :foo
def initialize
#foo = "It aint easy being cheesy!"
end
def set
self.instance_eval { yield if block_given? }
end
end
options = Setter.new
# Works
options.instance_eval do
p foo
end
# Fails
options.set do
p foo
end
Why does the 'set' method fail?
EDIT
Figured it out...
def set
self.instance_eval { yield if block_given? }
end
Needed to be:
def set(&blk)
instance_eval(&blk)
end
Yep - yield evaluates in the context within which it was defined.
Good write up here, but a simple example shows the problem:
>> foo = "wrong foo"
>> options.set do
?> p foo
>> end
"wrong foo"