mongo 1.1.5 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. data/README.md +15 -15
  2. data/Rakefile +38 -17
  3. data/docs/FAQ.md +4 -0
  4. data/docs/HISTORY.md +59 -0
  5. data/docs/RELEASES.md +33 -0
  6. data/docs/REPLICA_SETS.md +13 -16
  7. data/lib/mongo/collection.rb +157 -69
  8. data/lib/mongo/connection.rb +189 -65
  9. data/lib/mongo/cursor.rb +43 -29
  10. data/lib/mongo/db.rb +63 -43
  11. data/lib/mongo/exceptions.rb +4 -1
  12. data/lib/mongo/gridfs/grid.rb +1 -1
  13. data/lib/mongo/gridfs/grid_ext.rb +1 -1
  14. data/lib/mongo/gridfs/grid_file_system.rb +1 -1
  15. data/lib/mongo/gridfs/grid_io.rb +89 -8
  16. data/lib/mongo/gridfs/grid_io_fix.rb +1 -1
  17. data/lib/mongo/repl_set_connection.rb +72 -20
  18. data/lib/mongo/test.rb +20 -0
  19. data/lib/mongo/util/conversions.rb +1 -1
  20. data/lib/mongo/util/core_ext.rb +11 -1
  21. data/lib/mongo/util/pool.rb +67 -15
  22. data/lib/mongo/util/server_version.rb +1 -1
  23. data/lib/mongo/util/support.rb +1 -1
  24. data/lib/mongo/util/uri_parser.rb +127 -13
  25. data/lib/mongo.rb +38 -2
  26. data/test/async/collection_test.rb +224 -0
  27. data/test/async/connection_test.rb +24 -0
  28. data/test/async/cursor_test.rb +162 -0
  29. data/test/async/worker_pool_test.rb +99 -0
  30. data/test/auxillary/fork_test.rb +30 -0
  31. data/test/auxillary/repl_set_auth_test.rb +58 -0
  32. data/test/auxillary/threaded_authentication_test.rb +101 -0
  33. data/test/bson/bson_test.rb +140 -28
  34. data/test/bson/byte_buffer_test.rb +18 -0
  35. data/test/bson/object_id_test.rb +14 -1
  36. data/test/bson/ordered_hash_test.rb +7 -0
  37. data/test/bson/timestamp_test.rb +24 -0
  38. data/test/collection_test.rb +104 -15
  39. data/test/connection_test.rb +78 -2
  40. data/test/conversions_test.rb +10 -11
  41. data/test/cursor_fail_test.rb +1 -1
  42. data/test/cursor_message_test.rb +1 -1
  43. data/test/cursor_test.rb +33 -4
  44. data/test/db_api_test.rb +30 -52
  45. data/test/db_test.rb +3 -3
  46. data/test/grid_file_system_test.rb +0 -1
  47. data/test/grid_io_test.rb +72 -1
  48. data/test/grid_test.rb +16 -16
  49. data/test/load/resque/load.rb +21 -0
  50. data/test/load/resque/processor.rb +26 -0
  51. data/test/load/thin/load.rb +24 -0
  52. data/test/load/unicorn/load.rb +23 -0
  53. data/test/load/unicorn/unicorn.rb +29 -0
  54. data/test/replica_sets/connect_test.rb +11 -1
  55. data/test/replica_sets/connection_string_test.rb +32 -0
  56. data/test/replica_sets/query_secondaries.rb +16 -0
  57. data/test/replica_sets/query_test.rb +10 -0
  58. data/test/replica_sets/replication_ack_test.rb +2 -0
  59. data/test/replica_sets/rs_test_helper.rb +9 -11
  60. data/test/support/hash_with_indifferent_access.rb +0 -13
  61. data/test/support_test.rb +0 -1
  62. data/test/test_helper.rb +27 -8
  63. data/test/tools/auth_repl_set_manager.rb +14 -0
  64. data/test/tools/load.rb +58 -0
  65. data/test/tools/repl_set_manager.rb +34 -9
  66. data/test/tools/sharding_manager.rb +202 -0
  67. data/test/tools/test.rb +3 -12
  68. data/test/unit/collection_test.rb +20 -24
  69. data/test/unit/connection_test.rb +4 -18
  70. data/test/unit/cursor_test.rb +16 -6
  71. data/test/unit/db_test.rb +10 -11
  72. data/test/unit/repl_set_connection_test.rb +0 -23
  73. data/test/unit/safe_test.rb +3 -3
  74. data/test/uri_test.rb +91 -0
  75. metadata +49 -12
  76. data/docs/1.0_UPGRADE.md +0 -21
@@ -1,7 +1,7 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  # --
4
- # Copyright (C) 2008-2010 10gen Inc.
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -48,3 +48,13 @@ class Hash
48
48
  end
49
49
 
50
50
  end
51
+
52
+ #:nodoc:
53
+ class String
54
+
55
+ #:nodoc:
56
+ def to_bson_code
57
+ BSON::Code.new(self)
58
+ end
59
+
60
+ end
@@ -1,7 +1,7 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  # --
4
- # Copyright (C) 2008-2010 10gen Inc.
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -22,14 +22,14 @@ module Mongo
22
22
 
23
23
  # Create a new pool of connections.
24
24
  #
25
- def initialize(connection, host, port, options={})
25
+ def initialize(connection, host, port, opts={})
26
26
  @connection = connection
27
27
 
28
28
  @host, @port = host, port
29
29
 
30
30
  # Pool size and timeout.
31
- @size = options[:size] || 1
32
- @timeout = options[:timeout] || 5.0
31
+ @size = opts[:size] || 1
32
+ @timeout = opts[:timeout] || 5.0
33
33
 
34
34
  # Mutex for synchronizing pool access
35
35
  @connection_mutex = Mutex.new
@@ -37,7 +37,11 @@ module Mongo
37
37
  # Condition variable for signal and wait
38
38
  @queue = ConditionVariable.new
39
39
 
40
+ # Operations to perform on a socket
41
+ @socket_ops = Hash.new { |h, k| h[k] = [] }
42
+
40
43
  @sockets = []
44
+ @pids = {}
41
45
  @checked_out = []
42
46
  end
43
47
 
@@ -46,12 +50,12 @@ module Mongo
46
50
  begin
47
51
  sock.close
48
52
  rescue IOError => ex
49
- warn "IOError when attempting to close socket connected "
50
- + "to #{@host}:#{@port}: #{ex.inspect}"
53
+ warn "IOError when attempting to close socket connected to #{@host}:#{@port}: #{ex.inspect}"
51
54
  end
52
55
  end
53
56
  @host = @port = nil
54
57
  @sockets.clear
58
+ @pids.clear
55
59
  @checked_out.clear
56
60
  end
57
61
 
@@ -73,21 +77,63 @@ module Mongo
73
77
  socket = TCPSocket.new(@host, @port)
74
78
  socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
75
79
  rescue => ex
76
- raise ConnectionFailure, "Failed to connect socket: #{ex}"
80
+ raise ConnectionFailure, "Failed to connect to host #{@host} and port #{@port}: #{ex}"
77
81
  end
82
+
83
+ # If any saved authentications exist, we want to apply those
84
+ # when creating new sockets.
85
+ @connection.apply_saved_authentication(:socket => socket)
86
+
78
87
  @sockets << socket
88
+ @pids[socket] = Process.pid
79
89
  @checked_out << socket
80
90
  socket
81
91
  end
82
92
 
93
+ # If a user calls DB#authenticate, and several sockets exist,
94
+ # then we need a way to apply the authentication on each socket.
95
+ # So we store the apply_authentication method, and this will be
96
+ # applied right before the next use of each socket.
97
+ def authenticate_existing
98
+ @connection_mutex.synchronize do
99
+ @sockets.each do |socket|
100
+ @socket_ops[socket] << Proc.new do
101
+ @connection.apply_saved_authentication(:socket => socket)
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ # Store the logout op for each existing socket to be applied before
108
+ # the next use of each socket.
109
+ def logout_existing(db)
110
+ @connection_mutex.synchronize do
111
+ @sockets.each do |socket|
112
+ @socket_ops[socket] << Proc.new do
113
+ @connection.db(db).issue_logout(:socket => socket)
114
+ end
115
+ end
116
+ end
117
+ end
118
+
83
119
  # Checks out the first available socket from the pool.
84
120
  #
121
+ # If the pid has changed, remove the socket and check out
122
+ # new one.
123
+ #
85
124
  # This method is called exclusively from #checkout;
86
125
  # therefore, it runs within a mutex.
87
126
  def checkout_existing_socket
88
127
  socket = (@sockets - @checked_out).first
89
- @checked_out << socket
90
- socket
128
+ if @pids[socket] != Process.pid
129
+ @pids[socket] = nil
130
+ @sockets.delete(socket)
131
+ socket.close
132
+ checkout_new_socket
133
+ else
134
+ @checked_out << socket
135
+ socket
136
+ end
91
137
  end
92
138
 
93
139
  # Check out an existing socket or create a new socket if the maximum
@@ -110,14 +156,20 @@ module Mongo
110
156
  checkout_new_socket
111
157
  end
112
158
 
113
- return socket if socket
159
+ if socket
160
+
161
+ # This calls all procs, in order, scoped to existing sockets.
162
+ # At the moment, we use this to lazily authenticate and
163
+ # logout existing socket connections.
164
+ @socket_ops[socket].reject! do |op|
165
+ op.call
166
+ end
114
167
 
115
- # Otherwise, wait
116
- if @logger
117
- @logger.warn "MONGODB Waiting for available connection; " +
118
- "#{@checked_out.size} of #{@size} connections checked out."
168
+ return socket
169
+ else
170
+ # Otherwise, wait
171
+ @queue.wait(@connection_mutex)
119
172
  end
120
- @queue.wait(@connection_mutex)
121
173
  end
122
174
  end
123
175
  end
@@ -1,7 +1,7 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  # --
4
- # Copyright (C) 2008-2010 10gen Inc.
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -1,7 +1,7 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  # --
4
- # Copyright (C) 2008-2010 10gen Inc.
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -1,7 +1,7 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  # --
4
- # Copyright (C) 2008-2010 10gen Inc.
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
5
  #
6
6
  # Licensed under the Apache License, Version 2.0 (the "License");
7
7
  # you may not use this file except in compliance with the License.
@@ -17,28 +17,101 @@
17
17
  # ++
18
18
 
19
19
  module Mongo
20
- module URIParser
20
+ class URIParser
21
21
 
22
22
  DEFAULT_PORT = 27017
23
- MONGODB_URI_MATCHER = /(([-_.\w\d]+):([-_\w\d]+)@)?([-.\w\d]+)(:([\w\d]+))?(\/([-\d\w]+))?/
23
+ MONGODB_URI_MATCHER = /(([-.\w]+):([^@]+)@)?([-.\w]+)(:([\w]+))?(\/([-\w]+))?/
24
24
  MONGODB_URI_SPEC = "mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/database]"
25
+ SPEC_ATTRS = [:nodes, :auths]
26
+ OPT_ATTRS = [:connect, :replicaset, :slaveok, :safe, :w, :wtimeout, :fsync]
25
27
 
26
- extend self
28
+ OPT_VALID = {:connect => lambda {|arg| ['direct', 'replicaset'].include?(arg)},
29
+ :replicaset => lambda {|arg| arg.length > 0},
30
+ :slaveok => lambda {|arg| ['true', 'false'].include?(arg)},
31
+ :safe => lambda {|arg| ['true', 'false'].include?(arg)},
32
+ :w => lambda {|arg| arg =~ /^\d+$/ },
33
+ :wtimeout => lambda {|arg| arg =~ /^\d+$/ },
34
+ :fsync => lambda {|arg| ['true', 'false'].include?(arg)}
35
+ }
36
+
37
+ OPT_ERR = {:connect => "must be 'direct' or 'replicaset'",
38
+ :replicaset => "must be a string containing the name of the replica set to connect to",
39
+ :slaveok => "must be 'true' or 'false'",
40
+ :safe => "must be 'true' or 'false'",
41
+ :w => "must be an integer specifying number of nodes to replica to",
42
+ :wtimeout => "must be an integer specifying milliseconds",
43
+ :fsync => "must be 'true' or 'false'"
44
+ }
45
+
46
+ OPT_CONV = {:connect => lambda {|arg| arg},
47
+ :replicaset => lambda {|arg| arg},
48
+ :slaveok => lambda {|arg| arg == 'true' ? true : false},
49
+ :safe => lambda {|arg| arg == 'true' ? true : false},
50
+ :w => lambda {|arg| arg.to_i},
51
+ :wtimeout => lambda {|arg| arg.to_i},
52
+ :fsync => lambda {|arg| arg == 'true' ? true : false}
53
+ }
54
+
55
+ attr_reader :nodes, :auths, :connect, :replicaset, :slaveok, :safe, :w, :wtimeout, :fsync
27
56
 
28
57
  # Parse a MongoDB URI. This method is used by Connection.from_uri.
29
58
  # Returns an array of nodes and an array of db authorizations, if applicable.
30
59
  #
31
- # @private
32
- def parse(string)
60
+ # Note: passwords can contain any character except for a ','.
61
+ #
62
+ # @core connections
63
+ def initialize(string)
33
64
  if string =~ /^mongodb:\/\//
34
65
  string = string[10..-1]
35
66
  else
36
67
  raise MongoArgumentError, "MongoDB URI must match this spec: #{MONGODB_URI_SPEC}"
37
68
  end
38
69
 
39
- nodes = []
40
- auths = []
41
- specs = string.split(',')
70
+ hosts, opts = string.split('?')
71
+ parse_hosts(hosts)
72
+ parse_options(opts)
73
+ configure_connect
74
+ end
75
+
76
+ def connection_options
77
+ opts = {}
78
+
79
+ if (@w || @wtimeout || @fsync) && !@safe
80
+ raise MongoArgumentError, "Safe must be true if w, wtimeout, or fsync is specified"
81
+ end
82
+
83
+ if @safe
84
+ if @w || @wtimeout || @fsync
85
+ safe_opts = {}
86
+ safe_opts[:w] = @w if @w
87
+ safe_opts[:wtimeout] = @wtimeout if @wtimeout
88
+ safe_opts[:fsync] = @fsync if @fsync
89
+ else
90
+ safe_opts = true
91
+ end
92
+
93
+ opts[:safe] = safe_opts
94
+ end
95
+
96
+ if @slaveok
97
+ if @connect == 'direct'
98
+ opts[:slave_ok] = true
99
+ else
100
+ opts[:read_secondary] = true
101
+ end
102
+ end
103
+
104
+ opts[:rs_name] = @replicaset if @replicaset
105
+
106
+ opts
107
+ end
108
+
109
+ private
110
+
111
+ def parse_hosts(hosts)
112
+ @nodes = []
113
+ @auths = []
114
+ specs = hosts.split(',')
42
115
  specs.each do |spec|
43
116
  matches = MONGODB_URI_MATCHER.match(spec)
44
117
  if !matches
@@ -52,8 +125,8 @@ module Mongo
52
125
  if !(port.to_s =~ /^\d+$/)
53
126
  raise MongoArgumentError, "Invalid port #{port}; port must be specified as digits."
54
127
  end
55
- port = port.to_i
56
- db = matches[8]
128
+ port = port.to_i
129
+ db = matches[8]
57
130
 
58
131
  if uname && pwd && db
59
132
  auths << {'db_name' => db, 'username' => uname, 'password' => pwd}
@@ -62,10 +135,51 @@ module Mongo
62
135
  "and db if any one of these is specified."
63
136
  end
64
137
 
65
- nodes << [host, port]
138
+ @nodes << [host, port]
66
139
  end
140
+ end
141
+
142
+ # This method uses the lambdas defined in OPT_VALID and OPT_CONV to validate
143
+ # and convert the given options.
144
+ def parse_options(opts)
145
+ # initialize instance variables for available options
146
+ OPT_VALID.keys.each { |k| instance_variable_set("@#{k}", nil) }
147
+
148
+ return unless opts
67
149
 
68
- [nodes, auths]
150
+ separator = opts.include?('&') ? '&' : ';'
151
+ opts.split(separator).each do |attr|
152
+ key, value = attr.split('=')
153
+ key = key.to_sym
154
+ value = value.strip.downcase
155
+ if !OPT_ATTRS.include?(key)
156
+ raise MongoArgumentError, "Invalid Mongo URI option #{key}"
157
+ end
158
+
159
+ if OPT_VALID[key].call(value)
160
+ instance_variable_set("@#{key}", OPT_CONV[key].call(value))
161
+ else
162
+ raise MongoArgumentError, "Invalid value for #{key}: #{OPT_ERR[key]}"
163
+ end
164
+ end
165
+ end
166
+
167
+ def configure_connect
168
+ if @nodes.length > 1 && !@connect
169
+ @connect = 'replicaset'
170
+ end
171
+
172
+ if !@connect
173
+ if @nodes.length > 1
174
+ @connect = 'replicaset'
175
+ else
176
+ @connect = 'direct'
177
+ end
178
+ end
179
+
180
+ if @connect == 'direct' && @replicaset
181
+ raise MongoArgumentError, "If specifying a replica set name, please also specify that connect=replicaset"
182
+ end
69
183
  end
70
184
  end
71
185
  end
data/lib/mongo.rb CHANGED
@@ -1,9 +1,25 @@
1
1
  # encoding: UTF-8
2
+ #
3
+ # --
4
+ # Copyright (C) 2008-2011 10gen Inc.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ # ++
2
18
 
3
19
  $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
4
20
 
5
21
  module Mongo
6
- VERSION = "1.1.5"
22
+ VERSION = "1.3.0"
7
23
  end
8
24
 
9
25
  module Mongo
@@ -11,6 +27,8 @@ module Mongo
11
27
  DESCENDING = -1
12
28
  GEO2D = '2d'
13
29
 
30
+ DEFAULT_MAX_BSON_SIZE = 4 * 1024 * 1024
31
+
14
32
  module Constants
15
33
  OP_REPLY = 1
16
34
  OP_MSG = 1000
@@ -58,4 +76,22 @@ if RUBY_PLATFORM =~ /java/
58
76
  end
59
77
  require 'mongo/gridfs/grid_file_system'
60
78
 
61
-
79
+ # Use SystemTimer on Ruby 1.8
80
+ if !defined?(RUBY_ENGINE) || (RUBY_ENGINE == 'ruby' && RUBY_VERSION < '1.9.0')
81
+ begin
82
+ require 'system_timer'
83
+ if SystemTimer.method(:timeout).arity.abs != 2
84
+ raise LoadError
85
+ end
86
+ Mongo::TimeoutHandler = SystemTimer
87
+ rescue LoadError
88
+ warn "Could not load SystemTimer >= v1.2.0. Falling back to timeout.rb. " +
89
+ "SystemTimer is STRONGLY recommended for timeouts in Ruby 1.8.7. " +
90
+ "See http://ph7spot.com/blog/system-timer-1-2-release for details."
91
+ require 'timeout'
92
+ Mongo::TimeoutHandler = Timeout
93
+ end
94
+ else
95
+ require 'timeout'
96
+ Mongo::TimeoutHandler = Timeout
97
+ end
@@ -0,0 +1,224 @@
1
+ require 'test/test_helper'
2
+
3
+ class TestCollection < Test::Unit::TestCase
4
+ @@connection ||= Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost', ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT)
5
+ @@db = @@connection.db(MONGO_TEST_DB)
6
+ @@test = @@db.collection("test")
7
+ @@version = @@connection.server_version
8
+
9
+ def setup
10
+ @@test.remove
11
+ end
12
+
13
+ def wait_for_async
14
+ sleep 0.2
15
+ end
16
+
17
+ def test_async_update
18
+ id1 = @@test.save("x" => 5)
19
+ failsafe = mock('failsafe will get called in block', :call => true)
20
+
21
+ @@test.update({}, {"$inc" => {"x" => 1}}, :async => true) do |error, result|
22
+ assert_nil error
23
+ assert result
24
+ failsafe.call
25
+ end
26
+ wait_for_async
27
+
28
+ assert_equal 1, @@test.count()
29
+ assert_equal 6, @@test.find_one(:_id => id1)["x"]
30
+ end
31
+
32
+ if @@version >= "1.1.3"
33
+ def test_async_multi_update
34
+ failsafe = mock('failsafe will get called in block', :call => true)
35
+
36
+ @@test.save("num" => 10)
37
+ @@test.save("num" => 10)
38
+ @@test.save("num" => 10)
39
+ assert_equal 3, @@test.count
40
+
41
+ @@test.update({"num" => 10}, {"$set" => {"num" => 100}}, :multi => true, :async => true) do |error, result|
42
+ assert_nil error
43
+ assert result
44
+ failsafe.call
45
+ end
46
+ wait_for_async
47
+ end
48
+ end
49
+
50
+ def test_async_upsert
51
+ failsafe = mock('failsafe will get called in block')
52
+ failsafe.expects(:call).times(2)
53
+
54
+ @@test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true, :async => true) do |error, result|
55
+ assert_nil error
56
+ assert result
57
+ failsafe.call
58
+ end
59
+
60
+ @@test.update({"page" => "/"}, {"$inc" => {"count" => 1}}, :upsert => true, :async => true) do |error, result|
61
+ assert_nil error
62
+ assert result
63
+ failsafe.call
64
+ end
65
+ wait_for_async
66
+
67
+ assert_equal 1, @@test.count()
68
+ assert_equal 2, @@test.find_one()["count"]
69
+ end
70
+
71
+ def test_async_save
72
+ failsafe = mock('failsafe will get called in block', :call => true)
73
+
74
+ # note that the first parameter has explicit curly brackets around
75
+ # the hash; without those brackets as a delimiter, the :async key is
76
+ # viewed as part of the required +document+ parameter
77
+ @@test.save({"hello" => "world"}, :async => true) do |error, result|
78
+ assert_nil error
79
+ assert result
80
+ failsafe.call
81
+ end
82
+ wait_for_async
83
+
84
+ assert_equal "world", @@test.find_one()["hello"]
85
+ end
86
+
87
+ def test_async_save_with_exception
88
+ failsafe = mock('failsafe will get called in block', :call => true)
89
+
90
+ @@test.create_index("hello", :unique => true)
91
+ @@test.save("hello" => "world")
92
+
93
+ # all async calls on collections occur in :safe mode
94
+ @@test.save({"hello" => "world"}, :async => true) do |error, result|
95
+ assert error
96
+ assert error.instance_of?(OperationFailure)
97
+ assert_nil result
98
+ failsafe.call
99
+ end
100
+ wait_for_async
101
+
102
+ assert_equal 1, @@test.count()
103
+ @@test.drop
104
+ end
105
+
106
+ def test_async_remove
107
+ failsafe = mock('failsafe will get called in block', :call => true)
108
+
109
+ @conn = Connection.new
110
+ @db = @conn[MONGO_TEST_DB]
111
+ @test = @db['test-async-remove']
112
+ @test.save({:a => 50})
113
+ @test.remove({}, :async => true) do |error, result|
114
+ assert_nil error
115
+ assert result
116
+ failsafe.call
117
+ end
118
+ wait_for_async
119
+
120
+ @test.drop
121
+ end
122
+
123
+ def test_async_count
124
+ failsafe = mock('failsafe will get called in block', :call => true)
125
+
126
+ @@test.drop
127
+
128
+ @@test.save("x" => 1)
129
+ @@test.save("x" => 2)
130
+
131
+ @@test.count(:async => true) do |error, result|
132
+ assert_nil error
133
+ assert_equal 2, result
134
+ failsafe.call
135
+ end
136
+ wait_for_async
137
+ end
138
+
139
+ # Note: #size is just an alias for #count.
140
+ def test_async_size
141
+ failsafe = mock('failsafe will get called in block', :call => true)
142
+
143
+ @@test.drop
144
+
145
+ @@test.save("x" => 1)
146
+ @@test.save("x" => 2)
147
+
148
+ @@test.size(:async => true) do |error, result|
149
+ assert_nil error
150
+ assert_equal 2, result
151
+ failsafe.call
152
+ end
153
+ wait_for_async
154
+ end
155
+
156
+ def test_async_find_one
157
+ failsafe = mock('failsafe will get called in block', :call => true)
158
+
159
+ id = @@test.save("hello" => "world", "foo" => "bar")
160
+
161
+ @@test.find_one({}, :async => true) do |error, result|
162
+ assert_nil error
163
+ assert_equal @@test.find_one(id), result
164
+ failsafe.call
165
+ end
166
+ wait_for_async
167
+ end
168
+
169
+ def test_async_insert
170
+ failsafe = mock('failsafe will get called in block', :call => true)
171
+
172
+ doc = {"hello" => "world"}
173
+ @@test.insert(doc, :async => true) do |error, result|
174
+ assert_nil error
175
+ assert result
176
+ failsafe.call
177
+ end
178
+ wait_for_async
179
+
180
+ assert_equal 1, @@test.count
181
+ end
182
+
183
+ def test_async_find
184
+ assert_raise RuntimeError do
185
+ @@test.find({}, :async => true)
186
+ end
187
+ end
188
+
189
+ if @@version > "1.3.0"
190
+ def test_async_find_and_modify
191
+ failsafe = mock('failsafe will get called in block', :call => true)
192
+
193
+ @@test << { :a => 1, :processed => false }
194
+ @@test << { :a => 2, :processed => false }
195
+ @@test << { :a => 3, :processed => false }
196
+
197
+ @@test.find_and_modify(:query => {}, :sort => [['a', -1]], :update => {"$set" => {:processed => true}}, :async => true) do |error, result|
198
+ assert_nil error
199
+ assert result
200
+ failsafe.call
201
+ end
202
+ wait_for_async
203
+
204
+ assert @@test.find_one({:a => 3})['processed']
205
+ end
206
+
207
+ def test_async_find_and_modify_with_invalid_options
208
+ failsafe = mock('failsafe will get called in block', :call => true)
209
+
210
+ @@test << { :a => 1, :processed => false }
211
+ @@test << { :a => 2, :processed => false }
212
+ @@test << { :a => 3, :processed => false }
213
+
214
+ @@test.find_and_modify(:blimey => {}, :async => true) do |error, result|
215
+ assert error
216
+ assert error.instance_of?(OperationFailure)
217
+ assert_nil result
218
+ failsafe.call
219
+ end
220
+ wait_for_async
221
+ end
222
+ end
223
+
224
+ end
@@ -0,0 +1,24 @@
1
+ require 'test/test_helper'
2
+ include Mongo
3
+
4
+ class ConnectionTest < Test::Unit::TestCase
5
+ context "Initialization: " do
6
+
7
+ context "given async connection options" do
8
+
9
+ should "default the workers pool to 1" do
10
+ Async::WorkerPool.expects(:new).with(1)
11
+
12
+ Connection.new('localhost', 27017)
13
+ end
14
+
15
+ should "override the workers pool size with the :worker_pool_size key" do
16
+ size = 6
17
+ Async::WorkerPool.expects(:new).with(size)
18
+
19
+ Connection.new('localhost', 27017, :worker_pool_size => size)
20
+ end
21
+ end # context 'given async connection options'
22
+
23
+ end # context 'Initialization'
24
+ end