How to test a simple rescue block in ruby - ruby

I have a custom fact in ruby that goes like this:
Facter.add(:some_random_fact) do
setcode do
output = execute_some_method
if !output.nil? then
begin
pruned_output = output.split("\n")
result = true
rescue
result = false
end
else
result = false
end
end
end
How do I write a unit test using rspec for the rescue block to raise an Exception?
EDIT: Please let me know if the below test is the correct way to test it
it "return fact as false when begin block raises exception" do
output = double(:output)
allow(output).to receive(:split).with(true).and_raise(RuntimeError.new("error occured"))
expect(Facter.fact(:some_random_fact).vallue).to eq(false)
end

The code you've shown here is weird and I get the feeling we're missing context, but in general you can stub out a method to raise an error like so:
expect(output).to receive(:split).with("\n").and_raise(RuntimeError.new("some error"))
but this is sort of an ugly way to go about things. If the error is raised conditionally depending the type of output, then it's better to find a way to set that variable to an error-producing value. How to do that, I can't tell you without seeing the test of your code.
For example, say you wrapped all this code in a def add_fact(output) - then from your tests you could intentionally pass an error-causing value for the output, and you no longer need to stub split (which is a wierd thing to do). This pattern is known as "dependency injection".

Related

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.

Is there an elegant way in Ruby which acts like `require` in Scala?

What I want to do is to make sure that arguments meet some conditions, if not, raise errors.
like this(let's say I want to make sure n > 0):
def some_method(n)
raise "some error" unless n > 0
... # other stuffs
end
There is require method in Scala which tests an expression, throwing an IllegalArgumentException if false.
if there is something acting like that in ruby?
I know ruby has assert series methods in unit test. But I don't think it is what I want.
EDITED
I just want to know if there are other ways to ensuring arguments meets some conditions, instead of raise.(The require in scala is so fit for that.)
What's wrong with your initial try? It works fine if you indeed want to throw Exceptions. You can create a method to test the requirement if you want, but it does not really do much:
def req(cond, error)
raise error if cond
end
def method(n)
req(n < 0, ArgumentError.new('YOU BROKE IT'))
# Method body
end
method(-1) # => method.rb:2:in 'req': YOU BROKE IT (ArgumentError)
If your problem is that you want to specify the error class, and want to write the condition to be satisfied rather than condition not to happen, then there is no special thing you need.
def some_method(n)
raise ArgumentError.new("some error") unless some_condition
raise ArgumentError.new("another error") unless another_condition
raise ArgumentError.new("yet another error") unless yet_another_condition
...
end

Waiting for an element without throwing an exception if timed out

I know of the method Element#wait_until_present(t), but if this method times out it throws a timeOut exception.
Is there a method that just waits for t seconds and then returns true if the element became present or false otherwise?
I know it can be done with a simple begin..rescue..end statement, but I'm looking for something that doesn't use exceptions.
You can write a short-hand rescue clause like this:
element_present = browser.element.wait_until_present rescue false
puts "element not present" unless element_present
This does however result in a false value on any Exception at all and not just with TimeoutError. I still prefer to use it since if there's any Exception at all then it would be safer to assume that the element was not present.
Looks like there is no other method that will do what i'm looking for ,
so here is the simplest method to achieve this :
#check method for Element#wait_until_present(t)
def check_if_present(element,t)
raise ArgumentError, 't must be a number ' unless t.is_a? Numeric
begin
element.wait_until_present(t)
true
rescue Watir::Wait::TimeoutError
false
rescue
raise "Something wrong with the element"
end
end
If you do not want an exception the below code can be handy:
sleep 'your time here' eg: sleep 20 - this will wait for 20 secs.
then check for your element now:
'your element'.exists? -this will return true/false
you will not get an exception this way.
Another best way is to write your wait_for_load method based on your needs.

ruby "on error resume next" function

Is there a way of doing the old "on error resume next" routine in ruby?
I've got array of value filled in dynamically from elsewhere (read from MQTT topics to be precise) then I want to do a bunch of numeric calculations on them and publish the results. The values SHOULD be numeric but are possibly missing or non-numeric.
At the moment my code looks something like
values=[]
//values get loaded here
begin
Publish('topic1',value[0]*10+value[1])
rescue TypeError,NoMethodError,ZeroDivisionError
end
begin
Publish('topic2',value[3]/value[4])
rescue TypeError,NoMethodError,ZeroDivisionError
end
//etc etc
If the calculation fails for any reason the program should just skip that step and go on.
It works but surely theres a better way than all those identical begin..rescue blocks? Ruby is about "DRY" after all..
Is there a way of re-writing the above so that a single begin..rescue construct is used while still allowing all calculations to be attempted?
UPDATED
How safe to do something like
def safe_Publish(topic,value)
return if value.nil?
Publish(topic,value)
end
and call with
safe_Publish('topic2',(value[3]/value[4] rescue nil))
The main problem is that the above catches ALL exceptions not just the ones I'm expecting which makes me a little nervous.
The on error resume next coding style is really dangerous - as it makes finding new bugs you accidentally introduce to your program very hard to find. Instead, I would just write a different version of publish that doesn't throw those exceptions:
def try_publish(topic_name)
begin
Publish('topic1',yield)
rescue TypeError,NoMethodError,ZeroDivisionError
# are you sure you don't want to do anything here? Even logging the errors
# somewhere could be useful.
end
end
You can then call this with:
try_publish('topic1') { value[0]*10+value[1] }
If TypeError,NoMethodError or ZeroDivisionError are thrown by the expression, they will be caught and ignored.
Now your original method won't require any rescues.
If you really wanted an on error resume next, you could possibly do it by monkey patching the raise method in Kernel, but that would be a horrible idea.
If you think a bit more carefully about what you are doing, and why you want on error resume next, I think you will see that you don't really need to suppress all exceptions. As the other posters pointed out, that would make it hard to find and fix bugs.
Your problem is that you have a bunch of numbers scraped from the Internet, and want to run some calculations on them, but some may be invalid or missing. For invalid/missing numbers, you want to skip over any calculations which would use those numbers.
A few possible solutions:
Pre-filter your data and remove anything which is not a valid number.
Put each calculation you want to do into a method of its own. Put a rescue Exception on the method definition.
Define "safe" wrappers for the numeric classes which don't raise exceptions on divide by zero, etc. Use these wrappers for your calculations.
The "wrappers" might look something like this (don't expect complete, tested code; this is just to give you the idea):
# This is not designed for "mixed" arithmetic between SafeNumerics and ordinary Numerics,
# but if you want to do mixed arithmetic, that can also be achieved
# more checks will be needed, and it will also need a "coerce" method
class SafeNumeric
attr_reader :__numeric__
def initialize(numeric)
#__numeric__ = numeric.is_a?(String) ? numeric.to_f : numeric
end
def zero?
#__numeric__.zero?
end
def /(other)
if other.zero? || #__numeric__.nil? || other.__numeric__.nil?
SafeNumeric.new(nil) # could use a constant for this to reduce allocations
else
SafeNumeric.new(#__numeric__ / other.__numeric__)
end
end
def to_s; #__numeric__.to_s; end
def inspect; #__numeric__.inspect; end
# methods are also needed for +, -, *
end
Then use it like:
numbers = scraped_from_net.map { |n| SafeNumeric.new(n) }
# now you can do arithmetic on "numbers" at will
This shows how to wrap a bunch of quick operations into a loop with each one being protected by a begin/rescue:
values = [1,2,3,0,4]
ops = [ ->{values[0]/values[1]}, ->{values[2]/values[3]} ]
ops.each do |op|
begin
puts "answer is #{op.call}"
rescue ZeroDivisionError
puts "cannot divide by zero"
end
end
I prefer the safe_publish method, however, as you can unit test that and it encapsulates the logic of making safe calls and handling errors in a single place:
def safe_publish(topic, &block)
begin
value = block.call
publish(topic, value)
rescue
# handle the error
end
end
and then you can call this with code like:
safe_publish 'topic0' do
value[0]*10+value[1]
end

Is there a functional version of begin...rescue...end (exception block) in ruby?

I'd like to do something like this in ruby:
safe_variable = begin
potentially_nil_variable.foo
rescue
some_other_safe_value
end
... and treat the exception block (begin/rescue/end) as a function/block. This doesn't work as written, but is there a way to get a similar result?
NB what I'm actually doing is this, which works but is IMO ugly:
begin
safe_variable = potentially_nil_variable.foo
rescue
safe_variable = some_other_safe_value
end
UPDATE
I guess I hit a corner case on ruby syntax. What I actually was doing was this:
object_safe = begin potentially_nil_variable.foo
rescue ""
end
The error was class or module required for rescue clause. Probably it thought that "" was supposed to be the placeholder for the exception result.
The form you have should work:
safe_variable = begin
potentially_nil_variable.foo
rescue
some_other_safe_value
end
A shorter form:
safe_variable = this_might_raise rescue some_other_safe_value
If you're only avoiding nil, you can look into ActiveRecord's try:
safe_variable = potentially_nil_variable.try(:foo) || some_other_safe_value
The most functional approach I know of for sending a message to an object that might be nil is something like andand. For nil, andand returns an object that will simply return nil no matter what message you send it. For other objects, it returns the original object. And pretty much anything will be more efficient than mucking around with exceptions.

Resources