how to handle mongodb's E11000 duplicate key error in ruby - ruby

Is there any good example of handling mongodb related exceptions in ruby?
In this case I have:
/home/askar/.rvm/gems/ruby-1.9.3-p429/gems/mongo-1.8.6/lib/mongo/networking.rb:89:in `send_message_with_gle': 11000: E11000 duplicate key error index: somedb.somecoll.$_id_ dup key: { : "some_id" } (Mongo::OperationFailure)
from /home/askar/.rvm/gems/ruby-1.9.3-p429/gems/mongo-1.8.6/lib/mongo/collection.rb:1108:in `block in insert_documents'
from /home/askar/.rvm/gems/ruby-1.9.3-p429/gems/mongo-1.8.6/lib/mongo/util/logging.rb:33:in `block in instrument'
from /home/askar/.rvm/gems/ruby-1.9.3-p429/gems/mongo-1.8.6/lib/mongo/util/logging.rb:65:in `instrument'
from /home/askar/.rvm/gems/ruby-1.9.3-p429/gems/mongo-1.8.6/lib/mongo/util/logging.rb:32:in `instrument'
from /home/askar/.rvm/gems/ruby-1.9.3-p429/gems/mongo-1.8.6/lib/mongo/collection.rb:1106:in `insert_documents'
from /home/askar/.rvm/gems/ruby-1.9.3-p429/gems/mongo-1.8.6/lib/mongo/collection.rb:375:in `insert'
from lib/tasks/getorders.rb:47:in `block in <main>'
from lib/tasks/getorders.rb:25:in `each'
from lib/tasks/getorders.rb:25:in `<main>'
I'm having this error because I'm trying to insert a document with the id that already exists in mongodb database, I just want to know how to handle mongodb related exceptions in ruby.
For example, if an exception occurs, then I would change the id of the hash and then re-try to insert it.
How rescue block would look like?

The ruby block would look something like :
begin
# your operation
rescue Mongo::OperationFailure => e
if e.message =~ /^11000/
puts "Duplicate key error #{$!}"
# do something to recover from duplicate
else
raise e
end
end
# the rest of the exceptions follow ..
# if you just care about the dup error
# then ignore them
#rescue Mongo::MongoRubyError
# #Mongo::ConnectionError, Mongo::ConnectionTimeoutError, Mongo::GridError, Mongo::InvalidSortValueError, Mongo::MongoArgumentError, Mongo::NodeWithTagsNotFound
# puts "Ruby Error : #{$!}"
#rescue Mongo::MongoDBError
# # Mongo::AuthenticationError, Mongo::ConnectionFailure, Mongo::InvalidOperation, Mongo::OperationFailure
# puts "DB Error : #{$!}"
#rescue Mongo::OperationTimeout
# puts "Socket operation timeout Error : #{$!}"
#rescue Mongo::InvalidNSName
# puts "invalid collection or database Error : #{$!}"
#end
But, if you are updating a record that already exists, why not you use an upsert.
If you are creating a new record then why not let the mongod create the _id ?

Can also use write concerns.
#mongo_client.save({:doc => 'foo'}, {:w => 0}) # writes are not acknowledged
This is not as good as the rescue though.

If your ruby driver is >= 2.0.0, you should use Mongo::Error class for most of mongodb related exceptions. Here is how your rescue block should look like:
begin
# Document insert code
rescue Mongo::Error => e
if e.message.include? 'E11000'
# Change the id of the hash & re-try to insert it
end
end

Related

knowing if file is YAML or not

I would like to expect that YAML.load_file(foo) of ruby YAML module returns null if foo is not a YAML file. But I get exception:
did not find expected alphabetic or numeric character while scanning an alias at line 3 column 3 (Psych::SyntaxError)
from /usr/lib/ruby/2.4.0/psych.rb:377:in `parse_stream'
from /usr/lib/ruby/2.4.0/psych.rb:325:in `parse'
from /usr/lib/ruby/2.4.0/psych.rb:252:in `load'
from /usr/lib/ruby/2.4.0/psych.rb:473:in `block in load_file'
from /usr/lib/ruby/2.4.0/psych.rb:472:in `open'
from /usr/lib/ruby/2.4.0/psych.rb:472:in `load_file'
from ./select.rb:27:in `block in selecting'
from ./select.rb:26:in `each'
from ./select.rb:26:in `selecting'
from ./select.rb:47:in `block (2 levels) in <main>'
from ./select.rb:46:in `each'
from ./select.rb:46:in `block in <main>'
from ./select.rb:44:in `each'
from ./select.rb:44:in `<main>'
How can I triage if a file is a YAML file or not without a exception? In my case, I navigate to a directory and process markdown files: I add to a list markdown files with a key output: word and I return that list
mylist = Array.new
mylist = []
for d in (directory - excludinglist)
begin
info = YAML.load_file(d)
if info
if info.has_key?('output')
if info['output'].has_key?(word)
mylist.push(d)
end
end
end
rescue Psych::SyntaxError => error
return []
end
end
return mylist
When I catch exceptions, the bucle does not continue to push elements on my list.
The short answer: you can't.
Because YAML is just a text file, the only way to know whether a given text file is YAML or not is to parse it. The parser will try to parse the file, and if it is not valid YAML, it will raise an error.
Errors and exceptions are a common part of Ruby, especially in the world of IO. There's no reason to be afraid of them. You can easily rescue from them and continue on your way:
begin
yaml = YAML.load_file(foo)
rescue Psych::SyntaxError => e
# handle the bad YAML here
end
You mentioned that the following code will not work because you need to handle multiple files in a directory:
def foo
mylist = []
for d in (directory - excludinglist)
begin
info = YAML.load_file(d)
if info
if info.has_key?('output')
if info['output'].has_key?(word)
mylist.push(d)
end
end
end
rescue Psych::SyntaxError => error
return []
end
return mylist
end
The only issue here is that when you hit an error, you respond by returning from the function early. If you don't return, the for-loop will continue and you will get your desired functionality:
def foo
mylist = []
for d in (directory - excludinglist)
begin
info = YAML.load_file(d)
if info
if info.has_key?('output')
if info['output'].has_key?(word)
mylist.push(d)
end
end
end
rescue Psych::SyntaxError => error
# do nothing!
# puts "or your could display an error message!"
end
end
return mylist
end
Psych::SyntaxError gets raised by Psych::Parser#parse, the source for which is written in C. So unless you want to work with C, you can't write a patch for the method in Ruby to prevent the exception from getting raised.
Still, you could certainly rescue the exception, like so:
begin
foo = YAML.load_file("not_yaml.txt")
rescue Psych::SyntaxError => error
puts "bad yaml"
end

Editing Registry Key Error

I am trying to create a registry key called foo if it doesn't exist, if it does exist I am trying to read its value. If value >0 I am trying to decrement it by one as a way to track trial uses of a piece of software, but I am getting this error:
$ ruby _RUNME.rb
no key exists
c:/Ruby200-x64/lib/ruby/2.0.0/win32/registry.rb:261:in `call': no implicit conversion of String into Integer (TypeError)
from c:/Ruby200-x64/lib/ruby/2.0.0/win32/registry.rb:261:in `CreateKey'
from c:/Ruby200-x64/lib/ruby/2.0.0/win32/registry.rb:412:in `create'
from c:/Ruby200-x64/lib/ruby/2.0.0/win32/registry.rb:503:in `create'
from _RUNME.rb:18:in `rescue in <main>'
from _RUNME.rb:5:in `<main>'
My code is:
begin
Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\foo') do |reg|
value = reg['foo']
if(value.to_i>0)
reg['foo'] = value.to_i-1
else
puts "no trial uses left"
sleep(10)
Kernel.exit(false)
end
end
rescue
puts "no key exists"
Win32::Registry::HKEY_CURRENT_USER.create("software\\foo","Win32::Registry::KEY_ALL_ACCESS")
Win32::Registry::HKEY_CURRENT_USER.open('SOFTWARE\foo') do |reg|
reg['foo']="55"
puts "wrote new key foo with value 55"
end
end
using Ruby version 2.0.0

Variables not recognized within Rescue in Ruby

I have the following code:
rescue Timeout::Error, StandardError => e
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
......
Before this rescue piece I have defined log and email_ids_all. However, neither of these are recognized within the ruby script. If i do this:
rescue Timeout::Error, StandardError => e
File.open(rescuelogfile, 'w') do |log| #setup log to write response codes.
puts "Caught exception: #{e.message}".red
log.puts("#{e.backtrace}")
email_ids_all.each do |email_delete|
call= "/api/v2/emails/#{email_delete}/"
....
log works fine, which makes sense. It would take a lot of writing to redefine the email_ids_all array and other variables contained inside my rescue block.
Is there anyway to allow variables to be recognized inside the rescue? Basically my code is laid out like this:
begin
#some code
rescue
#above code
end
I am using ruby 1.9.3.
EDIT----
log starts right after my begin statement :
begin
File.open(logfile, 'w') do |log| #setup log to write response codes.
log.puts works throughout the entire code except when an error is thrown, and then it runs the rescue script where log is not available.
The same goes for email_ids_all. There is an API call that generates about 10,000 emails and each of them is added to the array email_ids_all. The script is receiving an error about halfway through generating these emails, and so I need the rescue script to delete all email ids in the email_ids_all array. But for whatever reason, I get the following error:
FS_Test_Env.rb:762:in `block in <main>': undefined local variable or method `email_ids_all' for main:Object (NameError)
from FS_Test_Env.rb:759:in `open'
from FS_Test_Env.rb:759:in `rescue in <main>'
from FS_Test_Env.rb:7:in `<main>'
Any thoughts?
The way you put it, it should work, for example:
irb(main):001:0> begin
irb(main):002:1* x = 1
irb(main):003:1> x / 0
irb(main):004:1> rescue Exception => e
irb(main):005:1> p x
irb(main):006:1> end
1
=> 1
So it looks like the exception is thrown before your variables are defined.
The scope of the block parameter log is limited to that block. This is the whole point of the open with block.
Maybe you want to do:
begin
log = File.open('logfile', 'w')
...
rescue
...
ensure
log.close
end
Note that this does not cover errors when opening the logfile.
Regarding email_ids_all, I guess (!) you have the exception in a statement like:
email_ids_all = ... a long and complex calculation which raises an exception
If yes, the problem is that the assignment happens only after the whole right-hand side is calculated. The var email_ids_all is not yet created when the exception happens.
In order to access the elements created before the exception, you have to keep track of them, e.g.
begin
email_ids = []
10000.times do
email_ids << ... # create element eventually raising an exception
end
rescue
... # treat the already created elements
end

How to search message in mailbox with net/imap in Ruby?

I have some script on Ruby 1.9.3:
require "net/imap"
imap = Net::IMAP.new(mail_imap_server)
imap.login(mail_login, mail_password)
imap.select("INBOX")
puts imap.search(["FROM", "homer#simpson.com"])
imap.logout
imap.disconnect
If the desired message is present, then all is well.
If the desired message is missing, an error:
/opt/local/lib/ruby1.9/1.9.1/net/imap.rb:1332:in `block in search_internal': undefined method `[]' for nil:NilClass (NoMethodError)
from /opt/local/lib/ruby1.9/1.9.1/monitor.rb:211:in `mon_synchronize'
from /opt/local/lib/ruby1.9/1.9.1/net/imap.rb:1326:in `search_internal'
from /opt/local/lib/ruby1.9/1.9.1/net/imap.rb:752:in `search'
from ./mail.rb:12:in `mail'
from ./mail.rb:26:in `<main>'
How can I solve this problem?
first check if there are messages in the result, use a rescue just in case
require "net/imap"
imap = Net::IMAP.new(mail_imap_server)
imap.login(mail_login, mail_password)
imap.select("INBOX")
begin
messages = imap.search(["FROM", "homer#simpson.com"])
puts messages if messages.length > 0
rescue
puts "Error while retrieving the message"
end
imap.logout
imap.disconnect

Ruby NameError undefined local variable or method `e

class TwitterProfile < ActiveRecord::Base
def send_status_update(status_update)
if publish?
client = Twitter::Client.new( :oauth_token => authentication.token,
:oauth_token_secret => authentication.secret)
client.update(status_update.to_twitter_string)
end
rescue Exception => e
logger.info "Error publishing to twitter: #{e.to_s}"
end
end
There is a StatusUpdate model and an observer that reposts them to Twitter in after_create. I sometimes get the following exception:
NameError (undefined local variable or method `e' for #<TwitterProfile:0x00000004e44ab8>):
app/models/twitter_profile.rb:23:in `rescue in send_status_update'
app/models/twitter_profile.rb:18:in `send_status_update'
app/models/status_update_observer.rb:6:in `block in after_create'
app/models/status_update_observer.rb:4:in `after_create'
app/models/workout_observer.rb:5:in `after_update'
app/controllers/frames_controller.rb:76:in `update'
app/controllers/application_controller.rb:24:in `call'
app/controllers/application_controller.rb:24:in `block (2 levels) in <class:ApplicationController>'
app/controllers/application_controller.rb:10:in `block in <class:ApplicationController>'
What am I missing here?
I have one thing I know and one that's just a wild guess.
The thing I know is that you don't need to call to_s on an overall #{} expression; that will happen automatically. But it does no harm.
My wild guess is that your test case is not really running the code you have posted. What happens if you change e to f?
I should note that rescuing Exception itself is usually a bad idea. You should rescue RuntimeError or StandardError at the highest, and preferably something more specific. You can get fairly strange errors when rescuing Exception because you interfere with threads and interpreter-level events.
You're missing the 'begin' block of the begin/rescue clause.

Resources