How can I handle exceptions in its own method instead of this?
items.each do |item|
begin
url = item.url
page = Nokogiri::HTML(open(url).read)
rescue Exception => e
puts "Couldn't read \"#{ url }\": #{ e }"
else
title = get_title(page)
end
end
Something like:
def get_page(url)
begin
Nokogiri::HTML(open(url).read)
rescue Exception => e
puts "Couldn't read \"#{ url }\": #{ e }"
end
end
#and then call:
items.each do |item|
get_page(url)
title = get_title(page)
end
Where should I place the else clause?
To start, you almost never want to rescue from Exception. Instead rescue from StandardError (or the specific errors). Exception is an ancestor of StandardError and includes errors that are most likely not recoverable (out of memory error, syntax error, for example).
You can use
rescue => e
to rescue from standard error or
rescue StandardError => e
Any type of Nokogiri parsing error should inherit from StandardError. Net/HTTP is a little more problematic. See "What’s the best way to handle exceptions from Net::HTTP?", but you can just rescue them individually.
Now to your question. You could return nil from your get_page method and check to see if the result of get_page is nil before getting the title:
def get_page(url)
Nokogiri::HTML(open(url).read)
rescue => e
puts "Couldn't read \"#{ url }\": #{ e }"
nil
end
items.each do |item|
url = item.url
page = get_page(url)
if page
title = get_title(page)
end
end
Related
I have the following code:
def payload
begin
#payload ||= Warden::JWTAuth::TokenDecoder.new.call(token)
rescue JWT::ExpiredSignature => e
Rollbar.warning(e)
end
end
From brief reading of a few blogs I'm supposed to be using begin rescue and end to handle the error as I'm doing above, however I'm getting a redundant 'begin' rubocop warning.
Is begin only used when specifying a bit of code that may cause an error within a larger block? And is it therefore redundant here?
Thanks in advance
EDIT: And if I don't need it, is it written as
def payload
#payload ||= Warden::JWTAuth::TokenDecoder.new.call(token)
rescue JWT::ExpiredSignature => e
Rollbar.warning(e)
end
?
Do this when the begin would be the first thing in your method
def payload
#payload ||= Warden::JWTAuth::TokenDecoder.new.call(token)
rescue JWT::ExpiredSignature => e
Rollbar.warning(e)
end
Method bodies, block bodies, and lambda bodies are implicit exception blocks. You don't need to wrap the entire code of a method body, block body, or lambda body in a begin / rescue / else / ensure / end exception block, since it is already implicitly one. So, whenever you have something like
def foo
begin
rescue
end
end
or
foo do
begin
rescue
end
end
or
-> do
begin
rescue
end
end
you can replace it with just
def foo
rescue
end
or the equivalent for blocks and lambdas.
I have the following method:
def fetch_something
#fetch_something ||= array_of_names.inject({}) do |results, name|
begin
results[name] = fetch_value!(name)
rescue NoMethodError, RuntimeError
next
end
results
end
end
To its purpose: It fetches a value for a given name that can raise an error, in which case it will ignore the name and try the next one.
While this works fine I get an error from Rubocop that says:
Lint/NextWithoutAccumulator: Use next with an accumulator argument in
a reduce.
Googling that error leads me to http://www.rubydoc.info/gems/rubocop/0.36.0/RuboCop/Cop/Lint/NextWithoutAccumulator where it says to not omit the accumulator, which would result in a method looking like this:
def fetch_something
#fetch_something ||= array_of_names.inject({}) do |results, name|
begin
results[name] = fetch_value!(name)
rescue NoMethodError, RuntimeError
next(name)
end
results
end
end
The problem is, that this change breaks the otherwise working method. Any ideas on how to tackle that?
Update: Demonstrational example:
array_of_names = ['name1','name2','name3']
def fetch_value!(name)
# some code that raises an error if the name doesn't correspond to anything
end
fetch_something
# => {'name1' => {key1: 'value1', ...}, 'name3' => {key3: 'value3', ...}}
# 'name2' is missing since it wasn't found durcing the lookup
Using your example code, it seems that next(results) does fix the issue for me. I've used some test code that throws a KeyError instead of NoMethodError or RuntimeError, but the idea is still the same:
#array_of_names = ['name1','name2','name3']
def fetch_value!(name)
{'name1' => 'n1', 'name3' => 'n3'}.fetch(name)
end
def fetch_something
#fetch_something ||= #array_of_names.inject({}) do |results, name|
begin
results[name] = fetch_value!(name)
rescue NoMethodError, RuntimeError, KeyError
next(results)
end
results
end
end
p fetch_something
This code outputs:
{"name1"=>"n1", "name3"=>"n3"}
Also, I agree with #Алексей Кузнецов that each_with_object is probably the way to go whenever your block code mutates the data structure you're trying to build. So my preferred implementation of fetch_something might be more like this:
def fetch_something
#fetch_something ||= #array_of_names.each_with_object({}) do |name, results|
begin
results[name] = fetch_value!(name)
rescue NoMethodError, RuntimeError, KeyError
# error handling
end
end
end
Note that in my example the begin/end block is outside the assignment to results[name] in contrast to #Алексей Кузнецов's example which will assign nil to the hash every time an exception occurs.
Just use each_with_object
def fetch_something
#fetch_something ||= array_of_names.each_with_object({}) do |name, results|
results[name] ||= begin
fetch_value!(name)
rescue NoMethodError, RuntimeError
end
end
end
I'm completely "fresh" to Ruby and coming from a .NET background.
I'm trying to create a really simple rake task to import file with json objects into my database.
My IDE is raising an error when I'm trying to rescue inside the code block of each_line
File.open('etc/metrics_json.log').each_line do |line|
metric_hash = JSON.parse(line)
Metric.create(metric_hash)
rescue RuntimeError => e
puts e.message
end
the error is "syntax error, unexpected keyword_rescue, expecting keyword_end"
what am I doing wrong?
You could use it like
File.open('etc/metrics_json.log').each_line do |line|
begin
metric_hash = JSON.parse(line)
Metric.create(metric_hash)
rescue RuntimeError => e
puts e.message
end
end
you can use method def as begin. For eg
def example_method
....
rescue
....
end
if not, you need to use begin end.
begin
metric_hash = JSON.parse(line)
Metric.create(metric_hash)
rescue RuntimeError => e
puts e.message
end
In sidekiq_batch.rb,
def sidekiq_status
begin
something
rescue => e
Rails.logger.error("\nRESCUENIL in sidekiq_status #{e.class} #{e.message} in #{e.backtrace}")
# FIXME RESCUENIL
nil
end
end
In checkin.rb,
def attached_receipt_image
begin
something else
rescue => e
Rails.logger.error("\nRESCUENIL in attached_receipt_image #{e.class} #{e.message} in #{e.backtrace}")
# FIXME RESCUENIL
nil
end
end
In barcode.rb,
def receipt_check?
begin
some code
rescue => e
Rails.logger.error("\nRESCUENIL in receipt_check #{e.class} #{e.message} in #{e.backtrace}")
# FIXME RESCUENIL
nil
end
end
Need to DRY up the code. How can I write a common error-logging routine for all of these methods in my models?
You can write an abstraction for that, but you cannot return from there. You can write:
def with_log(name)
begin
yield
rescue => exc
Rails.logger.error("\nRESCUENIL in #{name} #{exc.class} #{exc.message} in #{exc.backtrace}")
false
end
end
with_log(:sidekiq_status) do
something
true # not needed if something returns a boolean with the success status
end or return
This true can also be moved to with_log, it depends on how you plan to use it.
How do I rescue from a
undefined method
error in this code
for user in #users do
#customer = Stripe::Customer.retrieve(user.stripe_customer_token)
if #customer.subscription.nil?
elsif #customer.subscription.plan.id == 2
user.silver_reset
elsif #customer.subscription.plan.id == 3
user.gold_reset
end
end
I have tried a plain rescue call, but rake isn't liking it.
What is way to rescue from the error?
UPDATE:
The way I was doing it
for user in #users do
#customer = Stripe::Customer.retrieve(user.stripe_customer_token)
rescue_from Exception => exception
# Logic
end
if #customer.subscription.nil?
elsif #customer.subscription.plan.id == 2
user.silver_reset
elsif #customer.subscription.plan.id == 3
user.gold_reset
end
end
The Error
/home/user/rails_projects/assignitapp/lib/tasks/daily.rake:25: syntax error, unexpected keyword_rescue, expecting keyword_end
rescue Exception => exception
Rake 0.9.2.2
Rails 3.2.5
Use try to wrap the problem method and return nil if it doesn't exist. E.g.:
unless #customer = Stripe::Customer.try(:retrieve, user.stripe_customer_token)
# Logic
end
Alternatively, this catches more errors:
unless #customer = Stripe::Customer.retrieve(user.stripe_customer_token) rescue nil
# Logic
end
Or this is more what you were trying for:
#users.each do |user|
begin
#customer = Stripe::Customer.retrieve(user.stripe_customer_token)
rescue StandardError => error
# handle error
end
end
I still haven't enough reputation to comment, but regarding the above answer's third option: Don't rescue from Exception!
Exception is the root of Ruby's exception hierarchy, so when you rescue Exception you rescue from everything, including subclasses such as SyntaxError, LoadError, and Interrupt.
If you want to learn more, check out Why is it a bad style to `rescue Exception => e` in Ruby?