Using error raising to control if/else statement - ruby

Learning ruby here, how can I use if an error was raised to control an if/else statement. In ruby pseudocode it would be something like.
if block.call raise?
#if block doesn't exist and an error is raised do this
else
#if block does exist do this
end

You could use an inline rescue like this:
if block.call rescue false
else
end
Though you shouldn't do this. The reason being that this will rescue any error in the block. Say you you mispelled some word in the block definition. Your code will act like this is intended, and you might have a hard time tracking it down.
In this case, to check if the block was given you can simply say if block. If you're using yield instead of the &block parameter, you can use the special method if block_given?.
Of course you can achieve conditional logic using rescues, but that's not what they're intended for, and it's not making things much simpler in most cases.

Related

Can you yield inside a code block in Ruby?

I'm currently learning Ruby and doing so by reading the popular book "The well-grounded Rubyist". I do understand code blocks pretty decently, or so I thought, until I hit this code example from the book on page 191:
open_user_file do |filename|
fh = File.open(filename)
yield fh
fh.close
rescue
puts "Couldn't open your file"
end
Now the thing I do no quite get here is, whom do I yield to when yielding within a code block? The way I understood it is that if you call a method that can yield and you provided a code block, the method will yield to your code block (maybe even with arguments), your code block executes and then gives control back to the method. But here in this code example we do not yield within the method, but in the code block. Could someone explain to me how this works and how a construct like this may look like? Any clarifications are appreciated!
(P.S. please don't tell me "You shouldn't do this". I am not asking because I want to do this in production code, I merely want to understand the inner workings of Ruby in-depth.)
The code you have there does in fact not work, because there is no block to yield to.
You will get a LocalJumpError, which gets swallowed by the catch-all rescue, and thus it will look like there is a problem with the file, when in fact, there is actually a programming error. (Teachable moment: never ever do a blanket catch-all rescue, always rescue only exactly those exceptions you want to handle.)

Ruby - What are the differences between checking if block_given? and !block.nil?

I have a ruby method that needs to check if a block was passed to it.
A colleague is suggesting that simply checking if block.nil? is slightly faster in performance and works for named blocks. This is already quite annoying since he is using the named block and invoking it using block.call rather than yield which has been shown to be significantly faster, since named blocks are more easy to understand in terms of readability.
Version 1:
def named_block &block
if block.nil?
puts "No block"
else
block.call
end
end
Version 2:
def named_block &block
if !block_given?
puts "No block"
else
block.call
end
end
Benchmarking shows that version 1 is slightly faster than version 2, however a quick look at the source code seems to suggest that block_given? is thread safe.
What are the main differences between the two approaches? Please help me prove him wrong!
First off, while a single nil? check might be faster than block_given?, capturing the block is slow. So unless you were going to capture it anyway, the performance argument is invalid.
Secondly, it's easier to understand. Whenever you see block_given?, you know exactly what is up. When you have x.nil?, you have to stop and think what x is.
Thirdly, it's an idiom. In my experience, the overwhelming majority of Ruby developers will prefer block_given?. When in Rome...
Lastly, you can keep it consistent. If you always use block_given? the problem is solved for you. If you use nil? checks, you have to have the block captured.
There is a performance overhead.
It's more verbose, something Rubyists try to avoid.
Naming things is one of the hardest things in programming. :) Can you think of a good name for the block Enumerable#map will get for example?
"Grepability" is a desirable trait for a codebase to have. If you want to find all the places where you check if you were given a block, doing nil? checks can prove difficult.
I think that the main difference is that block_given? can be used without explicitly defining &block in method definition:
def named_block
if !block_given?
puts "No block"
else
yield
end
end
Which version is better when it goes for readability? Sometimes explicitly naming the block can be more readable and sometimes yield can be more readable. It's also mater of personal preferences.
When it goes to speed, in benchmarks, that you've included, the yield is faster. That is because Ruby doesen't have to initialize new object (Proc) for the block and assign it to variable.
There is another way to accomplish this:
def named_block
(Proc.new rescue puts("No block") || ->{}).call
end
▶ named_block
#⇒ No block
▶ named_block { puts 'BLOCK!' }
#⇒ BLOCK!
please don’t take this too seriously
UPD: as noted by #Lukas in comments, it fails on the block, that raises an exception ⇒ FIXED

Shortest Ruby one-liner to execute a statement exactly once

I'm looking for the shortest, most simple Ruby one-liner to execute a statement exactly once. Idea is to use this while debugging to quickly add a debug statement to a loop that gets executed exactly once.
Best I've come up with so far:
puts "do something interesting exactly once!" if (once ||= "0").next! == "1"
Can you come up with something even shorter?
Added for clarification:
The idea for the questions was to focus on the "do it once" part and not so much on the "do something interesting" part. It should be assumed that the code do be executed once could be anything, not just a puts statement.
The ideal solution would also work in different kinds of loop constructs. E.g. as was pointed out my initial solution only works if the once variable is already defined outside the loop context or if the loop context used doesn't create a new lexical scope.
The original use case that triggered this question was slightly different - it looked more like below. But I though the above, simpler example would more easily explain the kind of solution I was looking for.
def process
do_some_preprocessing()
raise SomeError if <once> # added temp. for debugging purposes - the <once> part is what this question is about!
dangerous_operation() # this can raise SomeError under certain conditions
rescue SomeError
attempt_to_rescue() and retry
end
Well, you could abuse lambdas and closures.
->{puts "do something interesting exactly once!";x=->{x}}[][]
#=> do something interesting exactly once!
#=> #<Proc:0x5465282c#(irb):10 (lambda)>
The original contents of the lambda are only run once; any subsequent invocations will simply return an empty proc.
You could alternately abuse globals for a more true "one-liner", but it's awful.
$x ||= puts("do something interesting exactly once!") || 1
debug = ["do something interesting exactly once!"]
puts debug.pop # "do something interesting exactly once!"
puts debug.pop # nil
(answer edited to reflect the discussion in comments)
Your code won't do what you want it to do, it will depend of the looping construct you use.
This will work:
puts "do something interesting exactly once!" if once = once.nil?
But with this one, you'll have to define once before: once = nil (same thing for your own code). This is because otherwise, the scope of the once variable will be restrained to the block within an each loop, causing it to fail. This would work just fine within a for loop (the way you must have tested it):
(1..3).each do # 3.times would behave just the same
puts "once has not been defined before! Won't work!" if once = once.nil?
end
# >once has not been defined before! Won't work!
# once has not been defined before! Won't work!
# once has not been defined before! Won't work!
for i in 1..3 do
puts "Works because of the way for loops treat vars within loop" if once = once.nil?
end
# >Works because of the way for loops treat vars within loop
To avoid that problem without having to initialize the variable first, you can make once global:
(1..3).each do
puts "$once's scope is not restrained to the 'each' loop! Works!" if $once = $once.nil?
end
# >$once's scope is not restrained to the 'each' loop! Works!
The original idea generates code-smell. It results in code that will leave someone else scratching their head, which isn't a good thing. Generating code that is obvious and easy to understand will make your, and other programmer's, job easier.
Writing code that takes a while to figure out will take you a while to figure out in the future if you're debugging so be kind to your future self.
I'd stick with the standard way, using a simple flag:
once = false
2.times do
puts "do something interesting exactly once!" unless once
once ||= true
end
Which results in this output:
# >> do something interesting exactly once!

Can variables be passed after a do/end block?

I am working with a custom testing framework and we are trying to expand some of the assert functionality to include a custom error message if the assert fails. The current assert is called like this:
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end
and we want something with the functionality of:
assert_compare(first_term, :neq, second_term, error_message) do
puts 'foobar'
end
so that if the block fails the error message will describe the failure. I think this is ugly, however, as the framework we are moving away from did this and i have to go through a lot of statements that look like:
assert.compare(variable_foo['ARRAY1'][2], variable_bar['ARRAY2'][2], 'This assert failed because someone did something unintelligent when writing the test. Probably me, since in am the one writing this really really long error statement on the same line so that you have to spend a quarter of your day scrolling to the side just to read it')
This type of method call makes it difficult to read, even when using a variable for the error message. I feel like a better way should be possible.
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end on_fail: 'This is a nice error message'
This, to me, is the best way to do it but i don't know how or if it is even possible to accomplish this in ruby.
The goal here is to make it as aesthetic as possible. Any suggestions?
You could make on_fail a method of whatever assert_compare returns and write
assert_compare(first_term, :neq, second_term) do
puts 'foobar'
end.on_fail: 'This is a nice error message'
In short, no. Methods in ruby take a block as the final parameter only. As Chuck mentioned you could attempt to make the on_fail method a method of whatever assert_compare returns and that is a good solution. The solution I've come up with is not what you are looking for, but it works:
def test block, param
block.call
puts param
end
test proc { puts "hello"}, "hi"
will result in
"hello"
"hi"
What I've done here is create a Proc (which is essentially a block) and then passed it as a regular parameter.

Ruby Exceptions in a loop

I have a ruby script that loops through a list of shortened urls (around 2,000 - 3,000 at a time). At the moment everything is peachy until a hit a url that is malformed, timedout etc. When an error occurs my script dies. How can I setup my loop to skip to the next record when/if such an error occurs.
my loop looks like this:
blah.foo do |barurl|
mymethod(barurl)
my mymethod looks like this:
def mymethod(barurl)
begin
stuff
...
return expandedurl
rescue
return "Problem expanding link"
end
end
Should my begin/end logic be wrapped around my loop instead of the method?
Because you need to skip the malformed url, you should use the exception message to control the loop
blah.foo do |barurl|
begin
mymethod(barurl)
rescue YourTypeOfException
next
end
end
and inside the method raise the exception
def mymethod(barurl)
stuff
...
raise YourTypeOfException, "this url is not valid"
...
end
I found the existing answers unsatisfying, and reading the documentation suggests to me that the OP had something more like the example suggested there in mind:
[0, 1, 2].map do |i|
10 / i
rescue ZeroDivisionError
nil
end
#=> [nil, 10, 5]
The docs specifically note that a rescue block permits the loop to continue on a caught exception (as indicated by the example).
Yes. All your method does is consume the exception and return another arbitrary object in order to indicate an error.
Your method shouldn't handle its own exceptional conditions. It is just rude on its part to make assumptions about how the caller will react.
blah.foo do |url|
begin
my_method url
rescue
next
end
end
Whether to skip to the next URL or print a message is not a decision the method should be making. Its only concern should be working with the URL.
With that said, you should simply let it propagate and only rescue from it when you can actually deal with it. Don't rescue from a TimeoutError if all you can do is return :timeout.
Do rescue when you need to clean up resources or simply let the user know an error occurred.
Also, rescuing from every possible error just to make them go away is a nice way to introduce bugs. Be as specific as possible.
having exception handling within your method is proper way of doing it, so your implementation is fine
i can only point some ruby sytax sugar to you:
def some_method
# here goes the code
rescue Exception => e
# here goes specific exception/error handling
rescue
# here goes error handling (not Exception handling though!)
else
# do this block when no exceptions are raised
ensure
# do this every time
end
btw you don't need return statements, last value of code block is always returned implicitly
ah i guess i misread your question in the "how to skip next record"
if you want to skip the record after current one that was incorrect you would have to return error code from your parsing method and set up skipping within your loop using break or next keywords
It should be inside the loop, so the loop structure isn't exited on an exception. But it looks like it already is--if you're rescuing inside the method that causes the exception, the loop should already continue normally, because it shouldn't be seeing the exception.

Resources