diff --git a/lib/mongo/connection.rb b/lib/mongo/connection.rb index 25f021a..67d60e0 100644 --- a/lib/mongo/connection.rb +++ b/lib/mongo/connection.rb @@ -448,7 +448,7 @@ module Mongo if num_received == 1 && (error = docs[0]['err'] || docs[0]['errmsg']) close if error == "not master" error = "wtimeout" if error == "timeout" - raise Mongo::OperationFailure, docs[0]['code'].to_s + ': ' + error + raise OperationFailure.new(docs[0]['code'].to_s + ': ' + error, docs[0]['code'], docs[0]) end docs[0] diff --git a/lib/mongo/cursor.rb b/lib/mongo/cursor.rb index d8e347a..971c418 100644 --- a/lib/mongo/cursor.rb +++ b/lib/mongo/cursor.rb @@ -122,10 +122,10 @@ module Mongo # connection. The next request will re-open on master server. if err == "not master" @connection.close - raise ConnectionFailure, err + raise ConnectionFailure.new(err, doc['code'], doc) end - raise OperationFailure, err + raise OperationFailure.new(err, doc['code'], doc) end if @transformer.nil? @@ -175,7 +175,7 @@ module Mongo response = @db.command(command) return response['n'].to_i if Mongo::Support.ok?(response) return 0 if response['errmsg'] == "ns missing" - raise OperationFailure, "Count failed: #{response['errmsg']}" + raise OperationFailure.new("Count failed: #{response['errmsg']}", response['code'], response) end # Sort this cursor's results. diff --git a/lib/mongo/db.rb b/lib/mongo/db.rb index c358711..b25ecb9 100644 --- a/lib/mongo/db.rb +++ b/lib/mongo/db.rb @@ -123,13 +123,14 @@ module Mongo auth['user'] = username auth['nonce'] = nonce auth['key'] = Mongo::Support.auth_key(username, password, nonce) - if ok?(self.command(auth, :check_response => false, :socket => opts[:socket])) + if ok?(doc = self.command(auth, :check_response => false, :socket => opts[:socket])) if save_auth @connection.add_auth(@name, username, password) end true else - raise(Mongo::AuthenticationError, "Failed to authenticate user '#{username}' on db '#{self.name}'") + message = "Failed to authenticate user '#{username}' on db '#{self.name}'" + raise Mongo::AuthenticationError.new(message, doc['code'], doc) end end @@ -506,7 +507,13 @@ module Mongo if result.nil? raise OperationFailure, "Database command '#{selector.keys.first}' failed: returned null." elsif (check_response && !ok?(result)) - raise OperationFailure, "Database command '#{selector.keys.first}' failed: #{result.inspect}" + message = "Database command '#{selector.keys.first}' failed: (" + message << result.map do |key, value| + "#{key}: '#{value}'" + end.join('; ') + message << ').' + code = result['code'] || result['assertionCode'] + raise OperationFailure.new(message, code, result) else result end diff --git a/lib/mongo/exceptions.rb b/lib/mongo/exceptions.rb index 875eeeb..2403cf9 100644 --- a/lib/mongo/exceptions.rb +++ b/lib/mongo/exceptions.rb @@ -22,7 +22,20 @@ module Mongo class MongoRubyError < StandardError; end # Raised when MongoDB itself has returned an error. - class MongoDBError < RuntimeError; end + class MongoDBError < RuntimeError + + # @return The entire failed command's response object, if available. + attr_reader :result + + # @return The failed command's error code, if availab.e + attr_reader :error_code + + def initialize(message=nil, error_code=nil, result=nil) + @error_code = error_code + @result = result + super(message) + end + end # Raised when configuration options cause connections, queries, etc., to fail. class ConfigurationError < MongoRubyError; end diff --git a/test/db_test.rb b/test/db_test.rb index 2bb9108..910d2c8 100644 --- a/test/db_test.rb +++ b/test/db_test.rb @@ -206,8 +206,17 @@ class DBTest < Test::Unit::TestCase def test_check_command_response command = {:forceerror => 1} - assert_raise OperationFailure do + raised = false + begin @@db.command(command) + rescue => ex + raised = true + assert ex.message.include?("forced error"), + "error message does not contain 'forced error'" + assert_equal 10038, ex.error_code + assert_equal 10038, ex.result['assertionCode'] + ensure + assert raised, "No assertion raised!" end end