Merge branch 'master' of github.com:mongodb/mongo-ruby-driver
This commit is contained in:
commit
a4e49d86ac
@ -87,6 +87,7 @@ module Mongo
|
|||||||
@db, @name = db, name
|
@db, @name = db, name
|
||||||
@connection = @db.connection
|
@connection = @db.connection
|
||||||
@logger = @connection.logger
|
@logger = @connection.logger
|
||||||
|
@log_duration = @connection.log_duration
|
||||||
@cache_time = @db.cache_time
|
@cache_time = @db.cache_time
|
||||||
@cache = Hash.new(0)
|
@cache = Hash.new(0)
|
||||||
unless pk_factory
|
unless pk_factory
|
||||||
@ -264,8 +265,8 @@ module Mongo
|
|||||||
# @return [OrderedHash, Nil]
|
# @return [OrderedHash, Nil]
|
||||||
# a single document or nil if no result is found.
|
# a single document or nil if no result is found.
|
||||||
#
|
#
|
||||||
# @param [Hash, ObjectId, Nil] spec_or_object_id a hash specifying elements
|
# @param [Hash, ObjectId, Nil] spec_or_object_id a hash specifying elements
|
||||||
# which must be present for a document to be included in the result set or an
|
# which must be present for a document to be included in the result set or an
|
||||||
# instance of ObjectId to be used as the value for an _id query.
|
# instance of ObjectId to be used as the value for an _id query.
|
||||||
# If nil, an empty selector, {}, will be used.
|
# If nil, an empty selector, {}, will be used.
|
||||||
#
|
#
|
||||||
@ -413,7 +414,7 @@ module Mongo
|
|||||||
# @option opts [Boolean] :upsert (+false+) if true, performs an upsert (update or insert)
|
# @option opts [Boolean] :upsert (+false+) if true, performs an upsert (update or insert)
|
||||||
# @option opts [Boolean] :multi (+false+) update all documents matching the selector, as opposed to
|
# @option opts [Boolean] :multi (+false+) update all documents matching the selector, as opposed to
|
||||||
# just the first matching document. Note: only works in MongoDB 1.1.3 or later.
|
# just the first matching document. Note: only works in MongoDB 1.1.3 or later.
|
||||||
# @option opts [Boolean] :safe (+false+)
|
# @option opts [Boolean] :safe (+false+)
|
||||||
# If true, check that the save succeeded. OperationFailure
|
# If true, check that the save succeeded. OperationFailure
|
||||||
# will be raised on an error. Note that a safe check requires an extra
|
# will be raised on an error. Note that a safe check requires an extra
|
||||||
# round-trip to the database. Safe options provided here will override any safe
|
# round-trip to the database. Safe options provided here will override any safe
|
||||||
@ -906,12 +907,12 @@ module Mongo
|
|||||||
if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D, Mongo::GEOHAYSTACK].include?(f[1])
|
if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D, Mongo::GEOHAYSTACK].include?(f[1])
|
||||||
field_spec[f[0].to_s] = f[1]
|
field_spec[f[0].to_s] = f[1]
|
||||||
else
|
else
|
||||||
raise MongoArgumentError, "Invalid index field #{f[1].inspect}; " +
|
raise MongoArgumentError, "Invalid index field #{f[1].inspect}; " +
|
||||||
"should be one of Mongo::ASCENDING (1), Mongo::DESCENDING (-1) or Mongo::GEO2D ('2d')."
|
"should be one of Mongo::ASCENDING (1), Mongo::DESCENDING (-1) or Mongo::GEO2D ('2d')."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
raise MongoArgumentError, "Invalid index specification #{spec.inspect}; " +
|
raise MongoArgumentError, "Invalid index specification #{spec.inspect}; " +
|
||||||
"should be either a string, symbol, or an array of arrays."
|
"should be either a string, symbol, or an array of arrays."
|
||||||
end
|
end
|
||||||
field_spec
|
field_spec
|
||||||
|
@ -36,7 +36,7 @@ module Mongo
|
|||||||
|
|
||||||
mongo_thread_local_accessor :connections
|
mongo_thread_local_accessor :connections
|
||||||
|
|
||||||
attr_reader :logger, :size, :auths, :primary, :safe, :host_to_try,
|
attr_reader :logger, :log_duration, :size, :auths, :primary, :safe, :host_to_try,
|
||||||
:pool_size, :connect_timeout, :pool_timeout,
|
:pool_size, :connect_timeout, :pool_timeout,
|
||||||
:primary_pool, :socket_class, :op_timeout
|
:primary_pool, :socket_class, :op_timeout
|
||||||
|
|
||||||
@ -63,6 +63,8 @@ module Mongo
|
|||||||
# to a single, slave node.
|
# to a single, slave node.
|
||||||
# @option opts [Logger, #debug] :logger (nil) A Logger instance for debugging driver ops. Note that
|
# @option opts [Logger, #debug] :logger (nil) A Logger instance for debugging driver ops. Note that
|
||||||
# logging negatively impacts performance; therefore, it should not be used for high-performance apps.
|
# logging negatively impacts performance; therefore, it should not be used for high-performance apps.
|
||||||
|
# @option opts [Boolean, #debug] :log_duration (nil) A boolean to say if you want see information about log_duration on logger
|
||||||
|
# like logging, log_duration negatively impacts performance; therefore, it should not be used for high-performance apps.
|
||||||
# @option opts [Integer] :pool_size (1) The maximum number of socket self.connections allowed per
|
# @option opts [Integer] :pool_size (1) The maximum number of socket self.connections allowed per
|
||||||
# connection pool. Note: this setting is relevant only for multi-threaded applications.
|
# connection pool. Note: this setting is relevant only for multi-threaded applications.
|
||||||
# @option opts [Float] :pool_timeout (5.0) When all of the self.connections a pool are checked out,
|
# @option opts [Float] :pool_timeout (5.0) When all of the self.connections a pool are checked out,
|
||||||
@ -348,7 +350,7 @@ module Mongo
|
|||||||
self["admin"].command(oh)
|
self["admin"].command(oh)
|
||||||
end
|
end
|
||||||
|
|
||||||
# Checks if a server is alive. This command will return immediately
|
# Checks if a server is alive. This command will return immediately
|
||||||
# even if the server is in a lock.
|
# even if the server is in a lock.
|
||||||
#
|
#
|
||||||
# @return [Hash]
|
# @return [Hash]
|
||||||
@ -383,7 +385,7 @@ module Mongo
|
|||||||
Thread.current[:socket_map] ||= {}
|
Thread.current[:socket_map] ||= {}
|
||||||
Thread.current[:socket_map][self] ||= {}
|
Thread.current[:socket_map][self] ||= {}
|
||||||
Thread.current[:socket_map][self][:writer] ||= checkout_writer
|
Thread.current[:socket_map][self][:writer] ||= checkout_writer
|
||||||
Thread.current[:socket_map][self][:reader] =
|
Thread.current[:socket_map][self][:reader] =
|
||||||
Thread.current[:socket_map][self][:writer]
|
Thread.current[:socket_map][self][:writer]
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -568,6 +570,7 @@ module Mongo
|
|||||||
|
|
||||||
if @logger
|
if @logger
|
||||||
write_logging_startup_message
|
write_logging_startup_message
|
||||||
|
@log_duration = opts[:log_duration] || false
|
||||||
end
|
end
|
||||||
|
|
||||||
should_connect = opts.fetch(:connect, true)
|
should_connect = opts.fetch(:connect, true)
|
||||||
|
@ -43,6 +43,7 @@ module Mongo
|
|||||||
@collection = collection
|
@collection = collection
|
||||||
@connection = @db.connection
|
@connection = @db.connection
|
||||||
@logger = @connection.logger
|
@logger = @connection.logger
|
||||||
|
@log_duration = @connection.log_duration
|
||||||
|
|
||||||
# Query selector
|
# Query selector
|
||||||
@selector = opts[:selector] || {}
|
@selector = opts[:selector] || {}
|
||||||
@ -199,7 +200,7 @@ module Mongo
|
|||||||
# This method overrides any sort order specified in the Collection#find
|
# This method overrides any sort order specified in the Collection#find
|
||||||
# method, and only the last sort applied has an effect.
|
# method, and only the last sort applied has an effect.
|
||||||
#
|
#
|
||||||
# @param [Symbol, Array] key_or_list either 1) a key to sort by or 2)
|
# @param [Symbol, Array] key_or_list either 1) a key to sort by or 2)
|
||||||
# an array of [key, direction] pairs to sort by. Direction should
|
# an array of [key, direction] pairs to sort by. Direction should
|
||||||
# be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
|
# be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
|
||||||
#
|
#
|
||||||
|
@ -173,12 +173,15 @@ module Mongo
|
|||||||
#
|
#
|
||||||
# @param [String] username
|
# @param [String] username
|
||||||
# @param [String] password
|
# @param [String] password
|
||||||
|
# @param [Boolean] read_only
|
||||||
|
# Create a read-only user.
|
||||||
#
|
#
|
||||||
# @return [Hash] an object representing the user.
|
# @return [Hash] an object representing the user.
|
||||||
def add_user(username, password)
|
def add_user(username, password, read_only = false)
|
||||||
users = self[SYSTEM_USER_COLLECTION]
|
users = self[SYSTEM_USER_COLLECTION]
|
||||||
user = users.find_one({:user => username}) || {:user => username}
|
user = users.find_one({:user => username}) || {:user => username}
|
||||||
user['pwd'] = Mongo::Support.hash_password(username, password)
|
user['pwd'] = Mongo::Support.hash_password(username, password)
|
||||||
|
user['readOnly'] = true if read_only;
|
||||||
users.save(user)
|
users.save(user)
|
||||||
return user
|
return user
|
||||||
end
|
end
|
||||||
|
@ -71,6 +71,9 @@ module Mongo
|
|||||||
# GridFileSystem#delete.
|
# GridFileSystem#delete.
|
||||||
# @option opts [Boolean] :safe (false) When safe mode is enabled, the chunks sent to the server
|
# @option opts [Boolean] :safe (false) When safe mode is enabled, the chunks sent to the server
|
||||||
# will be validated using an md5 hash. If validation fails, an exception will be raised.
|
# will be validated using an md5 hash. If validation fails, an exception will be raised.
|
||||||
|
# @option opts [Integer] :versions (false) deletes all versions which exceed the number specified to
|
||||||
|
# retain ordered by uploadDate. This option only works in 'w' mode. Certain precautions must be taken when
|
||||||
|
# deleting GridFS files. See the notes under GridFileSystem#delete.
|
||||||
#
|
#
|
||||||
# @example
|
# @example
|
||||||
#
|
#
|
||||||
@ -97,7 +100,12 @@ module Mongo
|
|||||||
def open(filename, mode, opts={})
|
def open(filename, mode, opts={})
|
||||||
opts = opts.dup
|
opts = opts.dup
|
||||||
opts.merge!(default_grid_io_opts(filename))
|
opts.merge!(default_grid_io_opts(filename))
|
||||||
del = opts.delete(:delete_old) && mode == 'w'
|
if mode == 'w'
|
||||||
|
versions = opts.delete(:versions)
|
||||||
|
if opts.delete(:delete_old) || (versions && versions < 1)
|
||||||
|
versions = 1
|
||||||
|
end
|
||||||
|
end
|
||||||
file = GridIO.new(@files, @chunks, filename, mode, opts)
|
file = GridIO.new(@files, @chunks, filename, mode, opts)
|
||||||
return file unless block_given?
|
return file unless block_given?
|
||||||
result = nil
|
result = nil
|
||||||
@ -105,9 +113,9 @@ module Mongo
|
|||||||
result = yield file
|
result = yield file
|
||||||
ensure
|
ensure
|
||||||
id = file.close
|
id = file.close
|
||||||
if del
|
if versions
|
||||||
self.delete do
|
self.delete do
|
||||||
@files.find({'filename' => filename, '_id' => {'$ne' => id}}, :fields => ['_id'])
|
@files.find({'filename' => filename, '_id' => {'$ne' => id}}, :fields => ['_id'], :sort => ['uploadDate', -1], :skip => (versions -1))
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -25,14 +25,15 @@ module Mongo
|
|||||||
|
|
||||||
# Execute the block and log the operation described by name and payload.
|
# Execute the block and log the operation described by name and payload.
|
||||||
def instrument(name, payload = {}, &blk)
|
def instrument(name, payload = {}, &blk)
|
||||||
|
time_elapse = @log_duration ? Time.now : nil
|
||||||
res = yield
|
res = yield
|
||||||
log_operation(name, payload)
|
log_operation(name, payload, time_elapse)
|
||||||
res
|
res
|
||||||
end
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
def log_operation(name, payload)
|
def log_operation(name, payload, time_elapse=nil)
|
||||||
@logger ||= nil
|
@logger ||= nil
|
||||||
return unless @logger
|
return unless @logger
|
||||||
msg = "#{payload[:database]}['#{payload[:collection]}'].#{name}("
|
msg = "#{payload[:database]}['#{payload[:collection]}'].#{name}("
|
||||||
@ -40,7 +41,11 @@ module Mongo
|
|||||||
msg += ".skip(#{payload[:skip]})" if payload[:skip]
|
msg += ".skip(#{payload[:skip]})" if payload[:skip]
|
||||||
msg += ".limit(#{payload[:limit]})" if payload[:limit]
|
msg += ".limit(#{payload[:limit]})" if payload[:limit]
|
||||||
msg += ".sort(#{payload[:order]})" if payload[:order]
|
msg += ".sort(#{payload[:order]})" if payload[:order]
|
||||||
@logger.debug "MONGODB #{msg}"
|
if time_elapse
|
||||||
|
@logger.debug "MONGODB (#{Time.now - time_elapse}s) #{msg}"
|
||||||
|
else
|
||||||
|
@logger.debug "MONGODB #{msg}"
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -26,6 +26,7 @@ class AuthenticationTest < Test::Unit::TestCase
|
|||||||
@admin.authenticate('bob', 'secret')
|
@admin.authenticate('bob', 'secret')
|
||||||
@db1.add_user('user1', 'secret')
|
@db1.add_user('user1', 'secret')
|
||||||
@db2.add_user('user2', 'secret')
|
@db2.add_user('user2', 'secret')
|
||||||
|
@db2.add_user('userRO', 'secret', true) # read-only
|
||||||
@admin.logout
|
@admin.logout
|
||||||
|
|
||||||
assert_raise Mongo::OperationFailure do
|
assert_raise Mongo::OperationFailure do
|
||||||
@ -53,6 +54,7 @@ class AuthenticationTest < Test::Unit::TestCase
|
|||||||
|
|
||||||
assert @db1['stuff'].insert({:a => 2}, :safe => true)
|
assert @db1['stuff'].insert({:a => 2}, :safe => true)
|
||||||
assert @db2['stuff'].insert({:a => 2}, :safe => true)
|
assert @db2['stuff'].insert({:a => 2}, :safe => true)
|
||||||
|
assert @db2['stuff'].find(:safe => true)
|
||||||
|
|
||||||
@db1.logout
|
@db1.logout
|
||||||
assert_raise Mongo::OperationFailure do
|
assert_raise Mongo::OperationFailure do
|
||||||
@ -63,6 +65,12 @@ class AuthenticationTest < Test::Unit::TestCase
|
|||||||
assert_raise Mongo::OperationFailure do
|
assert_raise Mongo::OperationFailure do
|
||||||
assert @db2['stuff'].insert({:a => 2}, :safe => true)
|
assert @db2['stuff'].insert({:a => 2}, :safe => true)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@db2.authenticate('userRO', 'secret')
|
||||||
|
assert @db2['stuff'].find(:safe => true)
|
||||||
|
assert_raise Mongo::OperationFailure do
|
||||||
|
assert @db2['stuff'].insert({:a => 2}, :safe => true)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
@ -137,6 +137,15 @@ class TestConnection < Test::Unit::TestCase
|
|||||||
assert output.string.include?("admin['$cmd'].find")
|
assert output.string.include?("admin['$cmd'].find")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_logging_duration
|
||||||
|
output = StringIO.new
|
||||||
|
logger = Logger.new(output)
|
||||||
|
logger.level = Logger::DEBUG
|
||||||
|
connection = standard_connection(:logger => logger, :log_duration => true).db(MONGO_TEST_DB)
|
||||||
|
assert output.string.index(/\(0.\d+s\)/)
|
||||||
|
assert output.string.include?("admin['$cmd'].find")
|
||||||
|
end
|
||||||
|
|
||||||
def test_connection_logger
|
def test_connection_logger
|
||||||
output = StringIO.new
|
output = StringIO.new
|
||||||
logger = Logger.new(output)
|
logger = Logger.new(output)
|
||||||
@ -148,6 +157,21 @@ class TestConnection < Test::Unit::TestCase
|
|||||||
assert output.string.include?('testing')
|
assert output.string.include?('testing')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_connection_log_duration_with_logger
|
||||||
|
output = StringIO.new
|
||||||
|
logger = Logger.new(output)
|
||||||
|
logger.level = Logger::DEBUG
|
||||||
|
connection = standard_connection(:logger => logger, :log_duration => true)
|
||||||
|
assert logger, connection.logger
|
||||||
|
assert connection.log_duration
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_connection_log_duration_without_logger
|
||||||
|
connection = standard_connection(:log_duration => true)
|
||||||
|
assert_nil connection.logger
|
||||||
|
assert !connection.log_duration
|
||||||
|
end
|
||||||
|
|
||||||
def test_drop_database
|
def test_drop_database
|
||||||
db = @conn.db('ruby-mongo-will-be-deleted')
|
db = @conn.db('ruby-mongo-will-be-deleted')
|
||||||
coll = db.collection('temp')
|
coll = db.collection('temp')
|
||||||
|
@ -151,6 +151,13 @@ class DBTest < Test::Unit::TestCase
|
|||||||
@@db.remove_user('foo:bar')
|
@@db.remove_user('foo:bar')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_authenticate_read_only
|
||||||
|
@@db.add_user('joebob', 'user', true) # read-only user
|
||||||
|
assert @@db.authenticate('joebob', 'user')
|
||||||
|
@@db.logout
|
||||||
|
@@db.remove_user('joebob')
|
||||||
|
end
|
||||||
|
|
||||||
def test_authenticate_with_connection_uri
|
def test_authenticate_with_connection_uri
|
||||||
@@db.add_user('spongebob', 'squarepants')
|
@@db.add_user('spongebob', 'squarepants')
|
||||||
assert Mongo::Connection.from_uri("mongodb://spongebob:squarepants@#{host_port}/#{@@db.name}")
|
assert Mongo::Connection.from_uri("mongodb://spongebob:squarepants@#{host_port}/#{@@db.name}")
|
||||||
|
@ -165,6 +165,18 @@ class GridFileSystemTest < Test::Unit::TestCase
|
|||||||
assert_equal 0, @db['fs.chunks'].find({'files_id' => {'$in' => @ids}}).count
|
assert_equal 0, @db['fs.chunks'].find({'files_id' => {'$in' => @ids}}).count
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "delete all versions which exceed the number of versions to keep specified by the option :versions" do
|
||||||
|
@versions = 1 + rand(4-1)
|
||||||
|
@grid.open('sample', 'w', :versions => @versions) do |f|
|
||||||
|
f.write @new_data
|
||||||
|
end
|
||||||
|
@new_ids = @db['fs.files'].find({'filename' => 'sample'}).map {|file| file['_id']}
|
||||||
|
assert_equal @versions, @new_ids.length
|
||||||
|
id = @new_ids.first
|
||||||
|
assert !@ids.include?(id)
|
||||||
|
assert_equal @versions, @db['fs.files'].find({'filename' => 'sample'}).count
|
||||||
|
end
|
||||||
|
|
||||||
should "delete old versions on write with :delete_old is passed in" do
|
should "delete old versions on write with :delete_old is passed in" do
|
||||||
@grid.open('sample', 'w', :delete_old => true) do |f|
|
@grid.open('sample', 'w', :delete_old => true) do |f|
|
||||||
f.write @new_data
|
f.write @new_data
|
||||||
|
Loading…
Reference in New Issue
Block a user