def checkdomains
#domains = Domain.all
##domains.where(:confirmed => "yes").each do |f|
#domains.each do |f|
r = Whois.whois(f.domain)
if r.available? == true
EmailNotify.notify_email(f).deliver
end
end
end
This method crashes when it comes upon an invalid url (the whois gem gives an error), and doesn't keep on checking the rest of the domains. Is there any way I can have it continue to check the rest of the domains even if it crashes on one? At least until I can sort out phising out each domain.
#domains.each do |f|
begin
r = Whois.whois(f.domain)
if r.available? == true
EmailNotify.notify_email(f).deliver
end
rescue Exception => e
puts "Error #{e}"
next # <= This is what you were looking for
end
end
When you say
crashing out
I assume you mean that you are getting an exception raised. If this is the case then just trap the exception, do what you want with it (Store the address in a bad_email table or whatever) then carry on doing what you are doing. Your log file will tell what exception is being raised so you know what your rescue statement should be
so
begin
r = Whois.whois(f.domain)
if r.available? == true
EmailNotify.notify_email(f).deliver
rescue WhateverException
#do something here like re raise the error or store the email address in a bad_emails table or do both just simply do nothing at all
end
If you are referring to something else like the whole app dying then I haven'ty got a clue and there is not enough info to advise further. Sorry
As jamesw suggests, you can wrap the statements in an exception handler, dealing with them as they occur. Let me suggest further that, wherever your program gets these (possibly invalid) domain names, you validate them as soon as you get them, and throw out the invalid ones. That way, by the time you reach this loop, you already know you're iterating over a list of good domains.
EDIT: For domain name validation, check here.
Related
I am writing a twitter tool that harvests some data. Below is a snippet of the code
replies_without_root_tweet.each do |r|
begin
t = client.status(r.in_reply_to_status_id)
RootTweet.find_or_create(t)
rescue Twitter::Error::NotFound,Twitter::Error::Forbidden => e
puts e
end
Now the thing is, the Twitter search API has rate limit which i hit a lot. The issue here is how can i resume the process in 15 minutes if i hit this exception
Twitter::Error::TooManyRequests
As you can see i rescue from another two exceptions, if i add the too many requests exception as well, it will be a problem as i will probably hit that exception all the time unless a specified amount of time passes.
Is there a way to know when a specific exception fires up so i can sleep the process?
You can have as many rescue statements as you want, so you can do this to handle TooManyRequests differently than the others:
begin
t = client.status(r.in_reply_to_status_id)
RootTweet.find_or_create(t)
rescue Twitter::Error::NotFound, Twitter::Error::Forbidden => e
puts e
rescue Twitter::Error::TooManyRequests => e
puts e
sleep 10
end
You can also ask the error object what it is, e.g. e.is_a?(Twitter::Error::TooManyRequests).
I get a warning when running reek on a Rails project:
[36]:ArborReloaded::UserStoryService#destroy_stories has approx 8 statements (TooManyStatements)
Here's the method:
def destroy_stories(project_id, user_stories)
errors = []
#project = Project.find(project_id)
user_stories.each do |current_user_story_id|
unless #project.user_stories.find(current_user_story_id).destroy
errors.push("Error destroying user_story: #{current_user_story_id}")
end
end
if errors.compact.length == 0
#common_response.success = true
else
#common_response.success = false
#common_response.errors = errors
end
#common_response
end
How can this method be minimized?
First, I find that class and method size are useful for finding code that might need refactoring, but sometimes you really do need a long class or method. And there is always a way to make your code shorter to get around such limits, but that might make it less readable. So I disable that type of inspection when using static analysis tools.
Also, it's unclear to me why you'd expect to have an error when deleting a story, or who benefits from an error message that just includes the ID and nothing about what error occurred.
That said, I'd write that method like this, to reduce the explicit local state and to better separate concerns:
def destroy_stories(project_id, story_ids)
project = Project.find(project_id) # I don't see a need for an instance variable
errors = story_ids.
select { |story_id| !project.user_stories.find(story_id).destroy }.
map { |story_id| "Error destroying user_story: #{story_id}" }
respond errors
end
# Lots of services probably need to do this, so it can go in a superclass.
# Even better, move it to #common_response's class.
def respond(errors)
# It would be best to move this behavior to #common_response.
#common_response.success = errors.any?
# Hopefully this works even when errors == []. If not, fix your framework.
#common_response.errors = errors
#common_response
end
You can see how taking some care in your framework can save a lot of noise in your components.
Imagine this scenario (just a sample)
file = open("/file1")
file2 = open("/file2")
file3 = open("/file3")
How can i handle this situation, what i want to do is allow statements that don't rise exception run while catching any exception that they might rise, In other words i have 3 lines that can rise the same exception, how should this be handled. i can check the msg, but still if the first line throws an exception the next two lines won't run.
a relating question is can i have a nesting rescue blocks (meaning throw and rescue an exception inside a rescue block?)
You might want a loop for this specific case:
open_files = ['file1', 'file2', 'file3'].map do |file|
begin
File.open file
rescue => e
...
end
end
Well, figured it out.
not the smartest question!
just have each one in different method and call them.
Loadfile1()
Loadfile2()
Loadfile3()
loadfile1()
file1 = open("/file1")
rescue......
...
end
Hi I've this piece of code
class Place < ActiveRecord::Base
def self.find_or_create_by_latlon(lat, lon)
place_id = call_external_webapi
result = Place.where(:place_id => place_id).limit(1)
result = Place.create(:place_id => place_id, ... ) if result.empty? #!
result
end
end
Then I'd like to do in another model or controller
p = Post.new
p.place = Place.find_or_create_by_latlon(XXXXX, YYYYY) # race-condition
p.save
But Place.find_or_create_by_latlon takes too much time to get the data if the action executed is create and sometimes in production p.place is nil.
How can I force to wait for the response before execute p.save ?
thanks for your advices
You're right that this is a race condition and it can often be triggered by people who double click submit buttons on forms. What you might do is loop back if you encounter an error.
result = Place.find_by_place_id(...) ||
Place.create(...) ||
Place.find_by_place_id(...)
There are more elegant ways of doing this, but the basic method is here.
I had to deal with a similar problem. In our backend a user is is created from a token if the user doesn't exist. AFTER a user record is already created, a slow API call gets sent to update the users information.
def self.find_or_create_by_facebook_id(facebook_id)
User.find_by_facebook_id(facebook_id) || User.create(facebook_id: facebook_id)
rescue ActiveRecord::RecordNotUnique => e
User.find_by_facebook_id(facebook_id)
end
def self.find_by_token(token)
facebook_id = get_facebook_id_from_token(token)
user = User.find_or_create_by_facebook_id(facebook_id)
if user.unregistered?
user.update_profile_from_facebook
user.mark_as_registered
user.save
end
return user
end
The step of the strategy is to first remove the slow API call (in my case update_profile_from_facebook) from the create method. Because the operation takes so long, you are significantly increasing the chance of duplicate insert operations when you include the operation as part of the call to create.
The second step is to add a unique constraint to your database column to ensure duplicates aren't created.
The final step is to create a function that will catch the RecordNotUnique exception in the rare case where duplicate insert operations are sent to the database.
This may not be the most elegant solution but it worked for us.
I hit this inside a sidekick job that retries and gets the error repeatedly and eventually clears itself. The best explanation I've found is on a blog post here. The gist is that postgres keeps an internally stored value for incrementing the primary key that gets messed up somehow. This rings true for me because I'm setting the primary key and not just using an incremented value so that's likely how this cropped up. The solution from the comments in the link above appears to be to call ActiveRecord::Base.connection.reset_pk_sequence!(table_name) This cleared up the issue for me.
begin
result = Place.where(:place_id => place_id).limit(1)
result = Place.create(:place_id => place_id, ... ) if result.empty? #!
rescue ActiveRecord::StatementInvalid => error
#save_retry_count = (#save_retry_count || 1)
ActiveRecord::Base.connection.reset_pk_sequence!(:place)
retry if( (#save_retry_count -= 1) >= 0 )
raise error
end
I have this piece of code:
begin
complete_results = Timeout.timeout(4) do
results = platform.search(artist, album_name)
end
rescue Timeout::Error
puts 'Print me something please'
end
I then launch the method containing this code, and well, here is the beginning of a stack trace:
Exception message : execution expired
Exception backtrace : /***/****/.rvm/rubies/ruby-1.8.7-p302/lib/ruby/1.8/timeout.rb:64:i
So I naively thinks that my call timed out, but 'Print me something please' is never printed and complete_results which is suppose to be the timeout status return value (either true or false, as mentioned in the documentation), is definitively not a boolean.
Am I doing something wrong?
Your code is correct
require 'timeout'
begin
complete_results = Timeout.timeout(1) do
sleep(2)
end
rescue Timeout::Error
puts 'Print me something please'
end
does print out "print me something please".
Try the basic code as above. If that works, you have an issue in platform.search.
The problem is that platform.search is catching the exception that Timeout#timeout throws.
You can get around this by wrapping your inner code in another thread -- YMMV.
begin
complete_results = Timeout.timeout(4) do
Thread.new{ results = platform.search(artist, album_name) }.value
end
rescue Timeout::Error
puts 'Print me something please'
end
According to the documentation:
If the block execution terminates
before sec seconds has passed, it
returns true. If not, it terminates
the execution and raises exception
(which defaults to Timeout::Error)
This means it only returns true if it's successful, otherwise the variable will not be set (ie it's nil NOT false).
As far as your example goes, it's definitely timing out for me and getting to the rescue part...