Is there a Way To Exit Two Loops by Single Command In Ruby? - ruby

Here is the Sample code,
while true
while true
exit all loops when condition true
end
end
Can someone tell me if is it possible here to exit first loop when second loop breaks, but then I want to use only one break command and no raise.

You know what's better than using only one break? Not using any at all! :)
Little-used throw/catch is good here
catch(:done) do
while cond1
while cond2
throw :done if condition
end
end
end
For more information, see the docs on throw and catch.

Alright, so apparently boolean flags are a no-go. Oops.
The other thing that pops to mind is catching an error, but you said you don't want to do that, or wrap it in a method and return. Honestly, there doesn't seem to be a way, but here's the simplest I could come up with:
catch (:exit) do
while true
while true
throw :exit if condition
end
end
end
You could also throw an exception, but that seems dirty. Here's the code to do it, though:
begin
while true
while true
raise "Exiting loops" if condition
end
end
rescue
#Code to go after the loop
end
Lastly, you could wrap the whole thing in a method and return from that method:
def my_descriptive_method_name()
while true
while true
return if condition
end
end
end

Related

How to convert this if condition to unless?

if array.present?
puts "hello"
end
There is no else part to this.
How to write the above if condition using unless.
I'm asking this question because of this lint error:
Use a guard clause instead of wrapping the code inside a conditional expression
Regarding your comment:
I'm asking this question because of this lint error
Use a guard clause instead of wrapping the code inside a conditional expression
This means that instead of:
def foo(array)
if array.present?
puts "hello"
end
end
You are supposed to use:
def foo(array)
return unless array.present?
puts "hello"
end
See https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals
If this is a Rails question (is it?), you can also use blank?:
def foo(array)
return if array.blank?
puts "hello"
end
There's no reason to.
Remember: unless is the inverse of if (or !if if you rather), and is only intended to make your code easier to read.
Using unless with your expression would be incredibly awkward, because you're now moving the actual body of work to an else statement...
unless array.present?
return
else
puts "hello"
end
...which doesn't make your code any easier to read if you had stuck with a negated if:
if !array.present?
return
else
puts "hello"
end
Don't use unless here. You lose readability in exchange for virtually nothing.
One-liner:
puts "hello" unless !array.present?
However, I would recommend:
puts "hello" if array.present?
unless array.present?
return
else
puts "hello"
end
OP requested one-liner modification:
Pseudocode:
something unless condition
Therefore:
puts "hello" unless !array.present?

Return from a loop from a different method

My understanding is you can exit from a program with a return. How do you return from a loop? When I run return_method as in the following, I want to exit the loop with "RETURNING" returned.
def return_method
return "RETURNING"
end
loop do
puts "Enter:"
answer = gets.chomp
if answer == 'run'
return_method
end
break if answer == 'y'
end
break doesn't work within my method.
A typical way to escape from nested loops of from nested method calls is to use catch ... throw.
RETURNING = "RETURNING"
def return_method
throw RETURNING
end
catch(RETURNING) do
loop do
puts "Enter:"
answer = gets.chomp
if answer == 'run'
return_method
end
break if answer == 'y'
end
end
It's generally not the case that a method call forces the calling method to do something as abrupt as return. That's what exceptions are for, they will bubble up if not caught, but that's seriously heavy-handed for this sort of thing. Instead make your method return a truthy value if you want to break the loop:
def return_method
puts "RETURNING"
true
end
loop do
puts "Enter:"
answer = gets.chomp
case (answer)
when 'run'
break if return_method
when 'y'
break
end
end

Use break keyword in the method body

I have two loops here:
loop do
prompt(messages('APR_amt', LANGUAGE))
APR_amt = Kernel.gets.chomp
if valid_number?(APR_amt)
break
else
prompt(messages('not_valid_num', LANGUAGE))
end
end
loan_duration = ''
loop do
prompt(messages('loan_duration', LANGUAGE))
loan_duration = Kernel.gets().chomp()
if valid_number?(loan_duration)
break
else
prompt(messages('not_valid_num', LANGUAGE))
end
end
This part keeps on repeating for every loop:
if valid_number?(loan_duration)
break
else
prompt(messages('not_valid_num', LANGUAGE))
end
Just different variable passing by on it.
Now what I did is that I created a method for it to shortened my codes:
def check_number(varname)
if valid_number?(varname)
break
else
prompt(messages('not_valid_num', LANGUAGE))
end
end
But this one did not work. Instead I got an error that pertains to break.
How can I create a method that will work on all of my variables?
You can raise StopIteration instead of calling break. But note that they are not equivalent:
raise StopIteration jumps of a loop created with the loop statement. The exception is not handled by loops created with statements such as while, for, or methods such as each. It has a dynamic scope, which means that it goes up the call stack until it finds the loop (which can be defined in a completely different place in the code).
break jumps out of any block. It has a lexical scope, which means that the block must enclose the break statement in the code. In your code there is no block around break (a method is not a block), and that's the reason you got an error.

Ruby skips items from list tasks

I am trying to make an app which if give the option to type, it types false then it skips the certain element from the list and it jumps to the next executing the same task.
That is the basic idea of the following code:
string["items"].each do |item|
p continue.to_s + "<- item"
begin
Anemone.crawl("http://" + item["displayLink"] + "/") do |anemone|
anemone.on_every_page do |page|
if continue.chomp.to_bool == false
raise "no more please"
end
request = Typhoeus::Request.new(page.url, followlocation: true)
response = request.run
email = /[-0-9a-zA-Z.+_]+#[-0-9a-zA-Z.+_]+\.[a-zA-Z]{2,4}/.match(response.body)
if email.nil?
else
p email
begin
continue = Timeout::timeout(2) do
p "insert now false/nothing"
gets
end
rescue Timeout::Error
continue = "true"
end
end
end
end
rescue
continue = true
next
end
p "---------------------------------------------------------"
end
As the code shows, if the user types false when prompted the app should skip the item and go to the next one. However what it does is: when the user types false the app skips the current item and then doesn't execute any of the code that should be executed for all of the other items except the printing ( the second line of code );
Here is how the output looks like:
$ruby main.rb
"1"
"true<- item"
#<MatchData "support#keycreative.com">
"insert now false/nothing"
false
"true<- item"
"true<- item"
"true<- item"
As I'm doing my best to show after false is entered the code does skip the certain item from the list but it also never ever executes code for the other items as it should since it is an each loop
First I thought that maybe the continue is false however as you can see from the output the continue is true which makes me wonder why does ruby skip my code?
UPDATE
Here is where the to_bool method comes from:
class String
def to_bool()
return true if self == "true"
return false if self == "false"
return nil
end
end
In your last rescue statement add:
rescue => e
puts e.message
continue = true
next
end
and inspect the output. Most likely your code is throwing an exception other than "no more please" (I expect undefined method to_bool for true:TrueClass). Note that using exception for skipping the loop element is a terrible idea. Why can't you just get rid of this rescue and do:
if continue.chomp.to_bool == false
continue = true
next
end
There are a lot of things in this code which makes it very un-ruby-like. If you want to improve it please paste it to StackExchange CodeReview page. (link in the comment).
UPDATE:
My bad, you are in nested loop, so the if statement won't work. You might look at sth similar to raise/rescue bit, namely throw/catch, see example here: How to break from nested loops in Ruby?. I still think you should post it to codereview though for refactoring advises.
As to your actual code (without refactoring). You are calling to_bool method on continue, and in your rescue block you assign true instead of 'true'. Hence your to_bool method raises exception which is then rescued same way as 'no more please' exception.

Break out a loop from within a (yielded) block inside the loop

jobs.each do |job|
msg job.name do
break if stop_all_jobs?
job.run!
end
end
def msg(msg, &block)
puts 'START ' + msg
yield
puts 'END ' + msg
end
In the above example break does not break out of the loop as expected. It only breaks out of the msg code block.
This seems a little odd, but I guess it is based on context, that said, how do I break out of the loop from code which is within a yielded code block?
One way is to use throw/catch. No, not exceptions, Ruby has a separate control-of-flow feature that works a bit like exceptions, without all the overhead (although I must admit I'm not sure that there isn't any overhead in using it):
catch :stop_all_jobs do
msg job.name do
throw :stop_all_jobs if stop_all_jobs?
job.run!
end
end
You can even pass a value as the second argument to throw which will be the result of the catch block.
A potentially more readable solution would, of course, be to pack the code up in a method and use return in place of break. But that wouldn't be as fun.
Use next instead of break.

Resources