What is use of ||= begin....end block in Ruby? - ruby

What is the difference between these snippets?
def config
#config ||= begin
if config_exists?
#config = return some value
else
{}
end
end
end
def config
#config ||= method
end
def method
if config_exists?
return some value
else
{}
end
end
I'm confused with the "begin ... end" block. Does it make any difference in the output? If not, then what is the use of the begin ... end block here?

First of all, you need to be aware that a defined method inherently includes the functionality of a begin ... end block.
In the context of exception handling, def method_name ... end is functionally equivalent to begin ... end. Both can include rescue statements for example.
The two blocks of code you have shared are actually identical, and there is no benefit in one over the other ... unless your method is needed in more than one place. In that case, you DRY up your code by putting the logic into a single method and calling it from multiple other places.

In your case, you can even omit the begin ... end block:
#config ||=
if config_exists?
return_some_value
else
{}
end
or, using the ternary if:
#config ||= config_exists? ? return_some_value : {}
Does it make any difference in output?
It could make a difference, because unlike def ... end, an begin ... end block doesn't create a new variable scope.
Here's a contrived example:
def foo
a = 456 # doesn't affect the other a
end
a = 123
b = foo
p a: a, b: b #=> {:a=>123, :b=>456}
Versus:
a = 123
b = begin
a = 456 # overwrites a
end
p a: a, b: b #=> {:a=>456, :b=>456}

Using ||= begin...end allows you to memoize the result of whatever is run in the begin...end. This is useful for caching the result of resource-intensive computation.

The only thing that will happen differently is if an exception is raised. For instance, let's say there is a problem in the config_exists? method call. If it raises an exception in the first example your #config var will be set to {}. In the second example if the same thing happens your program will crash.
As a side note, there is no need for the return keyword here. In fact the example should read as follows. This is assuming that I understand the intent.
def config
#config ||=
begin
if config_exists?
some_value
else
{}
end
rescue
{}
end
end
and
def config
#config ||= method
end
def method
if config_exists?
some_value
else
{}
end
end
Both examples are exactly the same, except if an exception is raised #config will still be set to = some_value in the first example.
Also, it should be noted that nothing will happen if #config already has a value. The ||= operators is the same as:
#config = some_value if #config.nil?
Only set the variable to this value if it is currently nil.
Hope this is helpful and that I am understanding your question correctly.

Related

Evaluate code with access to context of passed in block

I want to be able to do something like this:
def do_stuff(parameter)
raise StandardError
end
def foo(parameter)
rescuer { do_stuff(parameter) }
end
def rescuer
begin
yield # evaluate passed block
rescue StandardError
puts parameter # evaluate this with having access to `parameter` from block
end
end
foo('bar')
#=> bar
What's the least hacky way of achieving this?
There's a kind of messy way to do this that's highly situational, but this works in this narrow case:
def rescuer(&block)
begin
yield
rescue StandardError
p block.binding.local_variable_get(:parameter)
end
end
The binding on the block gives you access to any/all local variables that happen to be defined.

What's the proper idiom for short-circuiting a Ruby `begin ... end` block?

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

Using instance_exec and converting a method to a Proc

I can take a block of code, instance_exec it, and get the proper result. I would like to take a method off a different object and call one of it's methods in my scope. When I take a method from a different object, turn it into a proc, and then instance_exec it, I don't get the expected result. Code follows.
class Test1
def ohai(arg)
"magic is #{#magic} and arg is #{arg}"
end
end
class Test2
def initialize
#magic = "MAGICAL!"
end
def scope_checking
#magic
end
def do_it
ohai = Test1.new.method(:ohai)
self.instance_exec("foobar", &ohai)
end
end
describe "Test2 and scopes" do
before do
#t2 = Test2.new
end
it "has MAGICAL! in #magic" do
#t2.scope_checking.should == "MAGICAL!"
end
# This one fails :(
it "works like I expect converting a method to a proc" do
val = #t2.do_it
val.should == "magic is MAGICAL! and arg is foobar"
end
it "should work like I expect" do
val = #t2.instance_exec do
"#{#magic}"
end
val.should == "MAGICAL!"
end
end
It seems that, in Ruby, methods defined using def some_method are bound permanently to the class they're defined in.
So, when you call .to_proc on them they keep the binding of their original implementation, and you cannot rebind them. Well, you can, but only to an object of the same type as the first one. It's possible I could do some fancyness with inheritance, but I don't think so.
The solution becomes instead of using methods, I just put actual Procs into variables and use them then, as they're not bound until execution time.
not sure how good of an idea this is, but this passes your tests:
class Test1
def ohai(arg, binding)
eval('"magic is #{#magic} "', binding).to_s + "and arg is #{arg}"
end
end
class Test2
def initialize
#magic = "MAGICAL!"
end
def scope_checking
#magic
end
def get_binding
return binding()
end
def do_it
self.instance_exec(get_binding) {|binding| Test1.new.ohai("foobar", binding) }
end
end

Ruby double pipe assignment with block/proc/lambda?

It is really nice to be able to write out
#foo ||= "bar_default"
or
#foo ||= myobject.bar(args)
but I have been looking to see if there is a way to write something like
#foo ||= do
myobject.attr = new_val
myobject.other_attr = other_new_val
myobject.bar(args)
end
roughly equivalent in actually functional code to something like
#foo = if !#foo.nil?
#foo
else
myobject.attr = new_val
myobject.other_attr = other_new_val
myobject.bar(args)
end
And I suppose I could write my own global method like "getblock" to wrap and return the result of any general block, but I'm wondering if there is already a built-in way to do this.
You can use begin..end:
#foo ||= begin
# any statements here
end
or perhaps consider factoring the contents of the block into a separate method.
I usually write it like this:
#foo ||= (
myobject.attr = new_val
myobject.other_attr = other_new_val
myobject.bar(args)
)
#foo ||= unless #foo
myobject.attr = new_val
myobject.other_attr = other_new_val
myobject.bar(args)
end

block hack, how to simplify block

I have a question about Ruby blocks.
For example I have a Ruby Class:
class NewClass
def initialize
#a = 1
end
def some_method
puts #a
end
end
When I do something like that:
NewClass.new do |c|
c.some_method
end
Everything is good, but is there any possibilities to do that somehow like:
NewClass.new do
some_method
end
Any ideas?
Your current code will just ignore the block anyway since you don't yield to it. For what you are trying to do in your first example you need the yield self idiom in initialize.
For why you need a block variable in the first place, think about what the receiver for some_method would be in your second example. Without an explicit receiver it's the top level main (unless this code is part of some other class of course, where that enclosing class would be self). See Dave Thomas' blog post Changing self in Ruby (or Yehuda Katz' post as pointed out by Niklas B. in the comments) for more info on that topic (the comments clear up the "proc invocation" part).
Edit: all that said, this seems to work, but I prefer the yield self version and example 1:
class NewClass
def initialize
#a = 1
end
def some_method
puts "Hello: ##a"
end
def self.build(&block)
x = self.new
x.instance_eval(&block)
x
end
end
NewClass.build do
some_method
end
This allows you to execute the block without a block variable and will return the new instance of the class for assigning to a variable etc.
class NewClass
def initialize(&block)
#a = 1
instance_eval(&block)
end
def some_method
puts #a
end
end
NewClass.new do
some_method
end
Using instance_eval should do the job. But unless you know what you are doing (and it's not just out of laziness) I'd advice against it, and go with your original approach.
def initialize(&block)
#a = 1
self.instance_eval(&block) if block_given?
end

Resources