mongo 1.3.0 → 1.12.5

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 (185) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data/{LICENSE.txt → LICENSE} +1 -1
  4. data/README.md +122 -271
  5. data/Rakefile +25 -209
  6. data/VERSION +1 -0
  7. data/bin/mongo_console +31 -9
  8. data/lib/mongo/bulk_write_collection_view.rb +387 -0
  9. data/lib/mongo/collection.rb +576 -269
  10. data/lib/mongo/collection_writer.rb +364 -0
  11. data/lib/mongo/connection/node.rb +249 -0
  12. data/lib/mongo/connection/pool.rb +340 -0
  13. data/lib/mongo/connection/pool_manager.rb +320 -0
  14. data/lib/mongo/connection/sharding_pool_manager.rb +67 -0
  15. data/lib/mongo/connection/socket/socket_util.rb +37 -0
  16. data/lib/mongo/connection/socket/ssl_socket.rb +95 -0
  17. data/lib/mongo/connection/socket/tcp_socket.rb +87 -0
  18. data/lib/mongo/connection/socket/unix_socket.rb +39 -0
  19. data/lib/mongo/connection/socket.rb +18 -0
  20. data/lib/mongo/connection.rb +7 -875
  21. data/lib/mongo/cursor.rb +403 -117
  22. data/lib/mongo/db.rb +444 -243
  23. data/lib/mongo/exception.rb +145 -0
  24. data/lib/mongo/functional/authentication.rb +455 -0
  25. data/lib/mongo/functional/logging.rb +85 -0
  26. data/lib/mongo/functional/read_preference.rb +183 -0
  27. data/lib/mongo/functional/scram.rb +556 -0
  28. data/lib/mongo/functional/uri_parser.rb +409 -0
  29. data/lib/mongo/functional/write_concern.rb +66 -0
  30. data/lib/mongo/functional.rb +20 -0
  31. data/lib/mongo/gridfs/grid.rb +30 -24
  32. data/lib/mongo/gridfs/grid_ext.rb +6 -10
  33. data/lib/mongo/gridfs/grid_file_system.rb +38 -20
  34. data/lib/mongo/gridfs/grid_io.rb +84 -75
  35. data/lib/mongo/gridfs.rb +18 -0
  36. data/lib/mongo/legacy.rb +140 -0
  37. data/lib/mongo/mongo_client.rb +697 -0
  38. data/lib/mongo/mongo_replica_set_client.rb +535 -0
  39. data/lib/mongo/mongo_sharded_client.rb +159 -0
  40. data/lib/mongo/networking.rb +372 -0
  41. data/lib/mongo/{util → utils}/conversions.rb +29 -8
  42. data/lib/mongo/{util → utils}/core_ext.rb +28 -18
  43. data/lib/mongo/{util → utils}/server_version.rb +4 -6
  44. data/lib/mongo/{util → utils}/support.rb +29 -31
  45. data/lib/mongo/utils/thread_local_variable_manager.rb +25 -0
  46. data/lib/mongo/utils.rb +19 -0
  47. data/lib/mongo.rb +51 -50
  48. data/mongo.gemspec +29 -32
  49. data/test/functional/authentication_test.rb +39 -0
  50. data/test/functional/bulk_api_stress_test.rb +133 -0
  51. data/test/functional/bulk_write_collection_view_test.rb +1198 -0
  52. data/test/functional/client_test.rb +627 -0
  53. data/test/functional/collection_test.rb +2175 -0
  54. data/test/functional/collection_writer_test.rb +83 -0
  55. data/test/{conversions_test.rb → functional/conversions_test.rb} +47 -3
  56. data/test/functional/cursor_fail_test.rb +57 -0
  57. data/test/functional/cursor_message_test.rb +56 -0
  58. data/test/functional/cursor_test.rb +683 -0
  59. data/test/functional/db_api_test.rb +835 -0
  60. data/test/functional/db_connection_test.rb +25 -0
  61. data/test/functional/db_test.rb +348 -0
  62. data/test/functional/grid_file_system_test.rb +285 -0
  63. data/test/{grid_io_test.rb → functional/grid_io_test.rb} +72 -11
  64. data/test/{grid_test.rb → functional/grid_test.rb} +88 -15
  65. data/test/functional/pool_test.rb +136 -0
  66. data/test/functional/safe_test.rb +98 -0
  67. data/test/functional/ssl_test.rb +29 -0
  68. data/test/functional/support_test.rb +62 -0
  69. data/test/functional/timeout_test.rb +60 -0
  70. data/test/functional/uri_test.rb +446 -0
  71. data/test/functional/write_concern_test.rb +118 -0
  72. data/test/helpers/general.rb +50 -0
  73. data/test/helpers/test_unit.rb +476 -0
  74. data/test/replica_set/authentication_test.rb +37 -0
  75. data/test/replica_set/basic_test.rb +189 -0
  76. data/test/replica_set/client_test.rb +393 -0
  77. data/test/replica_set/connection_test.rb +138 -0
  78. data/test/replica_set/count_test.rb +66 -0
  79. data/test/replica_set/cursor_test.rb +220 -0
  80. data/test/replica_set/insert_test.rb +157 -0
  81. data/test/replica_set/max_values_test.rb +151 -0
  82. data/test/replica_set/pinning_test.rb +105 -0
  83. data/test/replica_set/query_test.rb +73 -0
  84. data/test/replica_set/read_preference_test.rb +219 -0
  85. data/test/replica_set/refresh_test.rb +211 -0
  86. data/test/replica_set/replication_ack_test.rb +95 -0
  87. data/test/replica_set/ssl_test.rb +32 -0
  88. data/test/sharded_cluster/basic_test.rb +203 -0
  89. data/test/shared/authentication/basic_auth_shared.rb +260 -0
  90. data/test/shared/authentication/bulk_api_auth_shared.rb +249 -0
  91. data/test/shared/authentication/gssapi_shared.rb +176 -0
  92. data/test/shared/authentication/sasl_plain_shared.rb +96 -0
  93. data/test/shared/authentication/scram_shared.rb +92 -0
  94. data/test/shared/ssl_shared.rb +235 -0
  95. data/test/test_helper.rb +53 -94
  96. data/test/threading/basic_test.rb +120 -0
  97. data/test/tools/mongo_config.rb +708 -0
  98. data/test/tools/mongo_config_test.rb +160 -0
  99. data/test/unit/client_test.rb +381 -0
  100. data/test/unit/collection_test.rb +89 -53
  101. data/test/unit/connection_test.rb +282 -32
  102. data/test/unit/cursor_test.rb +206 -8
  103. data/test/unit/db_test.rb +55 -13
  104. data/test/unit/grid_test.rb +43 -16
  105. data/test/unit/mongo_sharded_client_test.rb +48 -0
  106. data/test/unit/node_test.rb +93 -0
  107. data/test/unit/pool_manager_test.rb +111 -0
  108. data/test/unit/read_pref_test.rb +406 -0
  109. data/test/unit/read_test.rb +159 -0
  110. data/test/unit/safe_test.rb +69 -36
  111. data/test/unit/sharding_pool_manager_test.rb +84 -0
  112. data/test/unit/write_concern_test.rb +175 -0
  113. data.tar.gz.sig +3 -0
  114. metadata +227 -216
  115. metadata.gz.sig +0 -0
  116. data/docs/CREDITS.md +0 -123
  117. data/docs/FAQ.md +0 -116
  118. data/docs/GridFS.md +0 -158
  119. data/docs/HISTORY.md +0 -244
  120. data/docs/RELEASES.md +0 -33
  121. data/docs/REPLICA_SETS.md +0 -72
  122. data/docs/TUTORIAL.md +0 -247
  123. data/docs/WRITE_CONCERN.md +0 -28
  124. data/lib/mongo/exceptions.rb +0 -71
  125. data/lib/mongo/gridfs/grid_io_fix.rb +0 -38
  126. data/lib/mongo/repl_set_connection.rb +0 -342
  127. data/lib/mongo/test.rb +0 -20
  128. data/lib/mongo/util/pool.rb +0 -177
  129. data/lib/mongo/util/uri_parser.rb +0 -185
  130. data/test/async/collection_test.rb +0 -224
  131. data/test/async/connection_test.rb +0 -24
  132. data/test/async/cursor_test.rb +0 -162
  133. data/test/async/worker_pool_test.rb +0 -99
  134. data/test/auxillary/1.4_features.rb +0 -166
  135. data/test/auxillary/authentication_test.rb +0 -68
  136. data/test/auxillary/autoreconnect_test.rb +0 -41
  137. data/test/auxillary/fork_test.rb +0 -30
  138. data/test/auxillary/repl_set_auth_test.rb +0 -58
  139. data/test/auxillary/slave_connection_test.rb +0 -36
  140. data/test/auxillary/threaded_authentication_test.rb +0 -101
  141. data/test/bson/binary_test.rb +0 -15
  142. data/test/bson/bson_test.rb +0 -649
  143. data/test/bson/byte_buffer_test.rb +0 -208
  144. data/test/bson/hash_with_indifferent_access_test.rb +0 -38
  145. data/test/bson/json_test.rb +0 -17
  146. data/test/bson/object_id_test.rb +0 -154
  147. data/test/bson/ordered_hash_test.rb +0 -204
  148. data/test/bson/timestamp_test.rb +0 -24
  149. data/test/collection_test.rb +0 -910
  150. data/test/connection_test.rb +0 -309
  151. data/test/cursor_fail_test.rb +0 -75
  152. data/test/cursor_message_test.rb +0 -43
  153. data/test/cursor_test.rb +0 -483
  154. data/test/db_api_test.rb +0 -726
  155. data/test/db_connection_test.rb +0 -15
  156. data/test/db_test.rb +0 -287
  157. data/test/grid_file_system_test.rb +0 -243
  158. data/test/load/resque/load.rb +0 -21
  159. data/test/load/resque/processor.rb +0 -26
  160. data/test/load/thin/load.rb +0 -24
  161. data/test/load/unicorn/load.rb +0 -23
  162. data/test/load/unicorn/unicorn.rb +0 -29
  163. data/test/replica_sets/connect_test.rb +0 -94
  164. data/test/replica_sets/connection_string_test.rb +0 -32
  165. data/test/replica_sets/count_test.rb +0 -35
  166. data/test/replica_sets/insert_test.rb +0 -53
  167. data/test/replica_sets/pooled_insert_test.rb +0 -55
  168. data/test/replica_sets/query_secondaries.rb +0 -96
  169. data/test/replica_sets/query_test.rb +0 -51
  170. data/test/replica_sets/replication_ack_test.rb +0 -66
  171. data/test/replica_sets/rs_test_helper.rb +0 -27
  172. data/test/safe_test.rb +0 -68
  173. data/test/support/hash_with_indifferent_access.rb +0 -186
  174. data/test/support/keys.rb +0 -45
  175. data/test/support_test.rb +0 -18
  176. data/test/threading/threading_with_large_pool_test.rb +0 -90
  177. data/test/threading_test.rb +0 -87
  178. data/test/tools/auth_repl_set_manager.rb +0 -14
  179. data/test/tools/load.rb +0 -58
  180. data/test/tools/repl_set_manager.rb +0 -266
  181. data/test/tools/sharding_manager.rb +0 -202
  182. data/test/tools/test.rb +0 -4
  183. data/test/unit/pool_test.rb +0 -9
  184. data/test/unit/repl_set_connection_test.rb +0 -59
  185. data/test/uri_test.rb +0 -91
@@ -0,0 +1,372 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Networking
17
+
18
+ STANDARD_HEADER_SIZE = 16
19
+ RESPONSE_HEADER_SIZE = 20
20
+
21
+ # Counter for generating unique request ids.
22
+ @@current_request_id = 0
23
+
24
+ # Send a message to MongoDB, adding the necessary headers.
25
+ #
26
+ # @param [Integer] operation a MongoDB opcode.
27
+ # @param [BSON::ByteBuffer] message a message to send to the database.
28
+ #
29
+ # @option opts [Symbol] :connection (:writer) The connection to which
30
+ # this message should be sent. Valid options are :writer and :reader.
31
+ #
32
+ # @return [Integer] number of bytes sent
33
+ def send_message(operation, message, opts={})
34
+ if opts.is_a?(String)
35
+ warn "MongoClient#send_message no longer takes a string log message. " +
36
+ "Logging is now handled within the Collection and Cursor classes."
37
+ opts = {}
38
+ end
39
+
40
+ add_message_headers(message, operation)
41
+ packed_message = message.to_s
42
+
43
+ sock = nil
44
+ pool = opts.fetch(:pool, nil)
45
+ begin
46
+ if pool
47
+ #puts "send_message pool.port:#{pool.port}"
48
+ sock = pool.checkout
49
+ else
50
+ sock ||= checkout_writer
51
+ end
52
+ send_message_on_socket(packed_message, sock)
53
+ rescue SystemStackError, NoMemoryError, SystemCallError => ex
54
+ close
55
+ raise ex
56
+ ensure
57
+ if sock
58
+ sock.checkin
59
+ end
60
+ end
61
+ true
62
+ end
63
+
64
+ # Sends a message to the database, waits for a response, and raises
65
+ # an exception if the operation has failed.
66
+ #
67
+ # @param [Integer] operation a MongoDB opcode.
68
+ # @param [BSON::ByteBuffer] message a message to send to the database.
69
+ # @param [String] db_name the name of the database. used on call to get_last_error.
70
+ # @param [String] log_message this is currently a no-op and will be removed.
71
+ # @param [Hash] write_concern write concern.
72
+ #
73
+ # @see DB#get_last_error for valid last error params.
74
+ #
75
+ # @return [Hash] The document returned by the call to getlasterror.
76
+ def send_message_with_gle(operation, message, db_name, log_message=nil, write_concern=false)
77
+ docs = num_received = cursor_id = ''
78
+ add_message_headers(message, operation)
79
+
80
+ last_error_message = build_get_last_error_message(db_name, write_concern)
81
+ last_error_id = add_message_headers(last_error_message, Mongo::Constants::OP_QUERY)
82
+
83
+ packed_message = message.append!(last_error_message).to_s
84
+ sock = nil
85
+ begin
86
+ sock = checkout_writer
87
+ send_message_on_socket(packed_message, sock)
88
+ docs, num_received, cursor_id = receive(sock, last_error_id)
89
+ rescue ConnectionFailure, OperationFailure, OperationTimeout => ex
90
+ raise ex
91
+ rescue SystemStackError, NoMemoryError, SystemCallError => ex
92
+ close
93
+ sock = nil
94
+ raise ex
95
+ ensure
96
+ checkin(sock) if sock
97
+ sock = nil
98
+ end
99
+
100
+ if num_received == 1
101
+ error = docs[0]['err'] || docs[0]['errmsg']
102
+ if error && error.include?("not master")
103
+ close
104
+ raise ConnectionFailure.new(docs[0]['code'].to_s + ': ' + error, docs[0]['code'], docs[0])
105
+ elsif (!error.nil? && note = docs[0]['jnote'] || docs[0]['wnote']) # assignment
106
+ code = docs[0]['code'] || Mongo::ErrorCode::BAD_VALUE # as of server version 2.5.5
107
+ raise WriteConcernError.new(code.to_s + ': ' + note, code, docs[0])
108
+ elsif error
109
+ code = docs[0]['code'] || Mongo::ErrorCode::UNKNOWN_ERROR
110
+ error = "wtimeout" if error == "timeout"
111
+ raise WriteConcernError.new(code.to_s + ': ' + error, code, docs[0]) if error == "wtimeout"
112
+ raise OperationFailure.new(code.to_s + ': ' + error, code, docs[0])
113
+ end
114
+ end
115
+
116
+ docs[0]
117
+ end
118
+
119
+ # Sends a message to the database and waits for the response.
120
+ #
121
+ # @param [Integer] operation a MongoDB opcode.
122
+ # @param [BSON::ByteBuffer] message a message to send to the database.
123
+ # @param [String] log_message this is currently a no-op and will be removed.
124
+ # @param [Socket] socket a socket to use in lieu of checking out a new one.
125
+ # @param [Boolean] command (false) indicate whether this is a command. If this is a command,
126
+ # the message will be sent to the primary node.
127
+ # @param [Symbol] read the read preference.
128
+ # @param [Boolean] exhaust (false) indicate whether the cursor should be exhausted. Set
129
+ # this to true only when the OP_QUERY_EXHAUST flag is set.
130
+ # @param [Boolean] compile_regex whether BSON regex objects should be compiled into Ruby regexes.
131
+ #
132
+ # @return [Array]
133
+ # An array whose indexes include [0] documents returned, [1] number of document received,
134
+ # and [3] a cursor_id.
135
+ def receive_message(operation, message, log_message=nil, socket=nil, command=false,
136
+ read=:primary, exhaust=false, compile_regex=true)
137
+ request_id = add_message_headers(message, operation)
138
+ packed_message = message.to_s
139
+ opts = { :exhaust => exhaust,
140
+ :compile_regex => compile_regex }
141
+
142
+ result = ''
143
+
144
+ begin
145
+ send_message_on_socket(packed_message, socket)
146
+ result = receive(socket, request_id, opts)
147
+ rescue ConnectionFailure => ex
148
+ socket.close
149
+ checkin(socket)
150
+ raise ex
151
+ rescue SystemStackError, NoMemoryError, SystemCallError => ex
152
+ close
153
+ raise ex
154
+ rescue Exception => ex
155
+ if defined?(IRB)
156
+ close if ex.class == IRB::Abort
157
+ end
158
+ raise ex
159
+ end
160
+ result
161
+ end
162
+
163
+ private
164
+
165
+ def receive(sock, cursor_id, opts={})
166
+ exhaust = !!opts.delete(:exhaust)
167
+
168
+ if exhaust
169
+ docs = []
170
+ num_received = 0
171
+
172
+ while(cursor_id != 0) do
173
+ receive_header(sock, cursor_id, exhaust)
174
+ number_received, cursor_id = receive_response_header(sock)
175
+ new_docs, n = read_documents(number_received, sock, opts)
176
+ docs += new_docs
177
+ num_received += n
178
+ end
179
+
180
+ return [docs, num_received, cursor_id]
181
+ else
182
+ receive_header(sock, cursor_id, exhaust)
183
+ number_received, cursor_id = receive_response_header(sock)
184
+ docs, num_received = read_documents(number_received, sock, opts)
185
+
186
+ return [docs, num_received, cursor_id]
187
+ end
188
+ end
189
+
190
+ def receive_header(sock, expected_response, exhaust=false)
191
+ header = receive_message_on_socket(16, sock)
192
+
193
+ # unpacks to size, request_id, response_to
194
+ response_to = header.unpack('VVV')[2]
195
+ if !exhaust && expected_response != response_to
196
+ raise Mongo::ConnectionFailure, "Expected response #{expected_response} but got #{response_to}"
197
+ end
198
+
199
+ unless header.size == STANDARD_HEADER_SIZE
200
+ raise "Short read for DB response header: " +
201
+ "expected #{STANDARD_HEADER_SIZE} bytes, saw #{header.size}"
202
+ end
203
+ nil
204
+ end
205
+
206
+ def receive_response_header(sock)
207
+ header_buf = receive_message_on_socket(RESPONSE_HEADER_SIZE, sock)
208
+ if header_buf.length != RESPONSE_HEADER_SIZE
209
+ raise "Short read for DB response header; " +
210
+ "expected #{RESPONSE_HEADER_SIZE} bytes, saw #{header_buf.length}"
211
+ end
212
+
213
+ # unpacks to flags, cursor_id_a, cursor_id_b, starting_from, number_remaining
214
+ flags, cursor_id_a, cursor_id_b, _, number_remaining = header_buf.unpack('VVVVV')
215
+
216
+ check_response_flags(flags)
217
+ cursor_id = (cursor_id_b << 32) + cursor_id_a
218
+ [number_remaining, cursor_id]
219
+ end
220
+
221
+ def check_response_flags(flags)
222
+ if flags & Mongo::Constants::REPLY_CURSOR_NOT_FOUND != 0
223
+ raise Mongo::OperationFailure, "Query response returned CURSOR_NOT_FOUND. " +
224
+ "Either an invalid cursor was specified, or the cursor may have timed out on the server."
225
+ elsif flags & Mongo::Constants::REPLY_QUERY_FAILURE != 0
226
+ # Mongo query reply failures are handled in Cursor#next.
227
+ end
228
+ end
229
+
230
+ def read_documents(number_received, sock, opts)
231
+ docs = []
232
+ number_remaining = number_received
233
+ while number_remaining > 0 do
234
+ buf = receive_message_on_socket(4, sock)
235
+ size = buf.unpack('V')[0]
236
+ buf << receive_message_on_socket(size - 4, sock)
237
+ number_remaining -= 1
238
+ docs << BSON::BSON_CODER.deserialize(buf, opts)
239
+ end
240
+ [docs, number_received]
241
+ end
242
+
243
+ def build_command_message(db_name, query, projection=nil, skip=0, limit=-1)
244
+ message = BSON::ByteBuffer.new("", max_message_size)
245
+ message.put_int(0)
246
+ BSON::BSON_RUBY.serialize_cstr(message, "#{db_name}.$cmd")
247
+ message.put_int(skip)
248
+ message.put_int(limit)
249
+ message.put_binary(BSON::BSON_CODER.serialize(query, false, false, max_bson_size).to_s)
250
+ message.put_binary(BSON::BSON_CODER.serialize(projection, false, false, max_bson_size).to_s) if projection
251
+ message
252
+ end
253
+
254
+ # Constructs a getlasterror message. This method is used exclusively by
255
+ # MongoClient#send_message_with_gle.
256
+ def build_get_last_error_message(db_name, write_concern)
257
+ gle = BSON::OrderedHash.new
258
+ gle[:getlasterror] = 1
259
+ if write_concern.is_a?(Hash)
260
+ write_concern.assert_valid_keys(:w, :wtimeout, :fsync, :j)
261
+ gle.merge!(write_concern)
262
+ gle.delete(:w) if gle[:w] == 1
263
+ end
264
+ gle[:w] = gle[:w].to_s if gle[:w].is_a?(Symbol)
265
+ build_command_message(db_name, gle)
266
+ end
267
+
268
+ # Prepares a message for transmission to MongoDB by
269
+ # constructing a valid message header.
270
+ #
271
+ # Note: this method modifies message by reference.
272
+ #
273
+ # @return [Integer] the request id used in the header
274
+ def add_message_headers(message, operation)
275
+ headers = [
276
+ # Message size.
277
+ 16 + message.size,
278
+
279
+ # Unique request id.
280
+ request_id = get_request_id,
281
+
282
+ # Response id.
283
+ 0,
284
+
285
+ # Opcode.
286
+ operation
287
+ ].pack('VVVV')
288
+
289
+ message.prepend!(headers)
290
+
291
+ request_id
292
+ end
293
+
294
+ # Increment and return the next available request id.
295
+ #
296
+ # return [Integer]
297
+ def get_request_id
298
+ request_id = ''
299
+ @id_lock.synchronize do
300
+ request_id = @@current_request_id += 1
301
+ end
302
+ request_id
303
+ end
304
+
305
+ # Low-level method for sending a message on a socket.
306
+ # Requires a packed message and an available socket,
307
+ #
308
+ # @return [Integer] number of bytes sent
309
+ def send_message_on_socket(packed_message, socket)
310
+ begin
311
+ total_bytes_sent = socket.send(packed_message)
312
+ if total_bytes_sent != packed_message.size
313
+ packed_message.slice!(0, total_bytes_sent)
314
+ while packed_message.size > 0
315
+ byte_sent = socket.send(packed_message)
316
+ total_bytes_sent += byte_sent
317
+ packed_message.slice!(0, byte_sent)
318
+ end
319
+ end
320
+ total_bytes_sent
321
+ rescue => ex
322
+ socket.close
323
+ raise ConnectionFailure, "Operation failed with the following exception: #{ex}:#{ex.message}"
324
+ end
325
+ end
326
+
327
+ # Low-level method for receiving data from socket.
328
+ # Requires length and an available socket.
329
+ def receive_message_on_socket(length, socket)
330
+ begin
331
+ message = receive_data(length, socket)
332
+ rescue OperationTimeout, ConnectionFailure => ex
333
+ socket.close
334
+
335
+ if ex.class == OperationTimeout
336
+ raise OperationTimeout, "Timed out waiting on socket read."
337
+ else
338
+ raise ConnectionFailure, "Operation failed with the following exception: #{ex}"
339
+ end
340
+ end
341
+ message
342
+ end
343
+
344
+ def receive_data(length, socket)
345
+ message = new_binary_string
346
+ socket.read(length, message)
347
+
348
+ raise ConnectionFailure, "connection closed" unless message && message.length > 0
349
+ if message.length < length
350
+ chunk = new_binary_string
351
+ while message.length < length
352
+ socket.read(length - message.length, chunk)
353
+ raise ConnectionFailure, "connection closed" unless chunk.length > 0
354
+ message << chunk
355
+ end
356
+ end
357
+ message
358
+ end
359
+
360
+ if defined?(Encoding)
361
+ BINARY_ENCODING = Encoding.find("binary")
362
+
363
+ def new_binary_string
364
+ "".force_encoding(BINARY_ENCODING)
365
+ end
366
+ else
367
+ def new_binary_string
368
+ ""
369
+ end
370
+ end
371
+ end
372
+ end
@@ -1,20 +1,17 @@
1
- # encoding: UTF-8
2
-
3
- # --
4
- # Copyright (C) 2008-2011 10gen Inc.
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
5
2
  #
6
3
  # Licensed under the Apache License, Version 2.0 (the "License");
7
4
  # you may not use this file except in compliance with the License.
8
5
  # You may obtain a copy of the License at
9
6
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
11
8
  #
12
9
  # Unless required by applicable law or agreed to in writing, software
13
10
  # distributed under the License is distributed on an "AS IS" BASIS,
14
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
12
  # See the License for the specific language governing permissions and
16
13
  # limitations under the License.
17
- # ++
14
+
18
15
  module Mongo #:nodoc:
19
16
 
20
17
  # Utility module to include when needing to convert certain types of
@@ -24,8 +21,31 @@ module Mongo #:nodoc:
24
21
  ASCENDING_CONVERSION = ["ascending", "asc", "1"]
25
22
  DESCENDING_CONVERSION = ["descending", "desc", "-1"]
26
23
 
24
+ # Allows sort parameters to be defined as a Hash.
25
+ # Does not allow usage of un-ordered hashes, therefore
26
+ # Ruby 1.8.x users must use BSON::OrderedHash.
27
+ #
28
+ # Example:
29
+ #
30
+ # <tt>hash_as_sort_parameters({:field1 => :asc, "field2" => :desc})</tt> =>
31
+ # <tt>{ "field1" => 1, "field2" => -1}</tt>
32
+ def hash_as_sort_parameters(value)
33
+ if RUBY_VERSION < '1.9' && !value.is_a?(BSON::OrderedHash)
34
+ raise InvalidSortValueError.new(
35
+ "Hashes used to supply sort order must maintain ordering." +
36
+ "Use BSON::OrderedHash."
37
+ )
38
+ else
39
+ order_by = value.inject({}) do |memo, (key, direction)|
40
+ memo[key.to_s] = sort_value(direction)
41
+ memo
42
+ end
43
+ end
44
+ order_by
45
+ end
46
+
27
47
  # Converts the supplied +Array+ to a +Hash+ to pass to mongo as
28
- # sorting parameters. The returned +Hash+ will vary depending
48
+ # sorting parameters. The returned +Hash+ will vary depending
29
49
  # on whether the passed +Array+ is one or two dimensional.
30
50
  #
31
51
  # Example:
@@ -67,7 +87,7 @@ module Mongo #:nodoc:
67
87
  { str => 1 }
68
88
  end
69
89
 
70
- # Converts the +String+, +Symbol+, or +Integer+ to the
90
+ # Converts the +String+, +Symbol+, or +Integer+ to the
71
91
  # corresponding sort value in MongoDB.
72
92
  #
73
93
  # Valid conversions (case-insensitive):
@@ -77,6 +97,7 @@ module Mongo #:nodoc:
77
97
  #
78
98
  # If the value is invalid then an error will be raised.
79
99
  def sort_value(value)
100
+ return value if value.is_a?(Hash)
80
101
  val = value.to_s.downcase
81
102
  return 1 if ASCENDING_CONVERSION.include?(val)
82
103
  return -1 if DESCENDING_CONVERSION.include?(val)
@@ -1,20 +1,16 @@
1
- # encoding: UTF-8
2
-
3
- # --
4
- # Copyright (C) 2008-2011 10gen Inc.
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
5
2
  #
6
3
  # Licensed under the Apache License, Version 2.0 (the "License");
7
4
  # you may not use this file except in compliance with the License.
8
5
  # You may obtain a copy of the License at
9
6
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
11
8
  #
12
9
  # Unless required by applicable law or agreed to in writing, software
13
10
  # distributed under the License is distributed on an "AS IS" BASIS,
14
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
12
  # See the License for the specific language governing permissions and
16
13
  # limitations under the License.
17
- # ++
18
14
 
19
15
  #:nodoc:
20
16
  class Object
@@ -27,17 +23,6 @@ class Object
27
23
 
28
24
  end
29
25
 
30
- #:nodoc:
31
- module Enumerable
32
-
33
- #:nodoc:
34
- def each_with_object(memo)
35
- each { |element| yield(element, memo) }
36
- memo
37
- end unless [].respond_to?(:each_with_object)
38
-
39
- end
40
-
41
26
  #:nodoc:
42
27
  class Hash
43
28
 
@@ -45,7 +30,7 @@ class Hash
45
30
  def assert_valid_keys(*valid_keys)
46
31
  unknown_keys = keys - [valid_keys].flatten
47
32
  raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
48
- end
33
+ end unless instance_methods.include?(:assert_valid_keys)
49
34
 
50
35
  end
51
36
 
@@ -58,3 +43,28 @@ class String
58
43
  end
59
44
 
60
45
  end
46
+
47
+ #:nodoc:
48
+ class Class
49
+ def mongo_thread_local_accessor name, options = {}
50
+ m = Module.new
51
+ m.module_eval do
52
+ class_variable_set :"@@#{name}", Hash.new {|h,k| h[k] = options[:default] }
53
+ end
54
+ m.module_eval %{
55
+
56
+ def #{name}
57
+ @@#{name}[Thread.current.object_id]
58
+ end
59
+
60
+ def #{name}=(val)
61
+ @@#{name}[Thread.current.object_id] = val
62
+ end
63
+ }
64
+
65
+ class_eval do
66
+ include m
67
+ extend m
68
+ end
69
+ end
70
+ end
@@ -1,21 +1,19 @@
1
- # encoding: UTF-8
2
-
3
- # --
4
- # Copyright (C) 2008-2011 10gen Inc.
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
5
2
  #
6
3
  # Licensed under the Apache License, Version 2.0 (the "License");
7
4
  # you may not use this file except in compliance with the License.
8
5
  # You may obtain a copy of the License at
9
6
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
11
8
  #
12
9
  # Unless required by applicable law or agreed to in writing, software
13
10
  # distributed under the License is distributed on an "AS IS" BASIS,
14
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
12
  # See the License for the specific language governing permissions and
16
13
  # limitations under the License.
17
- # ++
14
+
18
15
  module Mongo
16
+
19
17
  # Simple class for comparing server versions.
20
18
  class ServerVersion
21
19
  include Comparable
@@ -1,50 +1,23 @@
1
- # encoding: UTF-8
2
-
3
- # --
4
- # Copyright (C) 2008-2011 10gen Inc.
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
5
2
  #
6
3
  # Licensed under the Apache License, Version 2.0 (the "License");
7
4
  # you may not use this file except in compliance with the License.
8
5
  # You may obtain a copy of the License at
9
6
  #
10
- # http://www.apache.org/licenses/LICENSE-2.0
7
+ # http://www.apache.org/licenses/LICENSE-2.0
11
8
  #
12
9
  # Unless required by applicable law or agreed to in writing, software
13
10
  # distributed under the License is distributed on an "AS IS" BASIS,
14
11
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
12
  # See the License for the specific language governing permissions and
16
13
  # limitations under the License.
17
- # ++
18
-
19
- require 'digest/md5'
20
14
 
21
15
  module Mongo
22
16
  module Support
17
+
23
18
  include Mongo::Conversions
24
19
  extend self
25
20
 
26
- # Generate an MD5 for authentication.
27
- #
28
- # @param [String] username
29
- # @param [String] password
30
- # @param [String] nonce
31
- #
32
- # @return [String] a key for db authentication.
33
- def auth_key(username, password, nonce)
34
- Digest::MD5.hexdigest("#{nonce}#{username}#{hash_password(username, password)}")
35
- end
36
-
37
- # Return a hashed password for auth.
38
- #
39
- # @param [String] username
40
- # @param [String] plaintext
41
- #
42
- # @return [String]
43
- def hash_password(username, plaintext)
44
- Digest::MD5.hexdigest("#{username}:mongo:#{plaintext}")
45
- end
46
-
47
-
48
21
  def validate_db_name(db_name)
49
22
  unless [String, Symbol].include?(db_name.class)
50
23
  raise TypeError, "db_name must be a string or symbol"
@@ -61,6 +34,7 @@ module Mongo
61
34
 
62
35
  def format_order_clause(order)
63
36
  case order
37
+ when Hash, BSON::OrderedHash then hash_as_sort_parameters(order)
64
38
  when String, Symbol then string_as_sort_parameters(order)
65
39
  when Array then array_as_sort_parameters(order)
66
40
  else
@@ -69,6 +43,29 @@ module Mongo
69
43
  end
70
44
  end
71
45
 
46
+ def normalize_seeds(seeds)
47
+ pairs = Array(seeds)
48
+ pairs = [ seeds ] if pairs.last.is_a?(Fixnum)
49
+ pairs = pairs.collect do |hostport|
50
+ if hostport.is_a?(String)
51
+ if hostport[0,1] == '['
52
+ host, port = hostport.split(']:') << MongoClient::DEFAULT_PORT
53
+ host = host.end_with?(']') ? host[1...-1] : host[1..-1]
54
+ else
55
+ host, port = hostport.split(':') << MongoClient::DEFAULT_PORT
56
+ end
57
+ [ host, port.to_i ]
58
+ else
59
+ hostport
60
+ end
61
+ end
62
+ pairs.length > 1 ? pairs : pairs.first
63
+ end
64
+
65
+ def is_i?(value)
66
+ return !!(value =~ /^\d+$/)
67
+ end
68
+
72
69
  # Determine if a database command has succeeded by
73
70
  # checking the document response.
74
71
  #
@@ -76,7 +73,8 @@ module Mongo
76
73
  #
77
74
  # @return [Boolean] true if the 'ok' key is either 1 or *true*.
78
75
  def ok?(doc)
79
- doc['ok'] == 1.0 || doc['ok'] == true
76
+ ok = doc['ok']
77
+ ok == 1 || ok == 1.0 || ok == true
80
78
  end
81
79
  end
82
80
  end
@@ -0,0 +1,25 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ #:nodoc:
16
+ module Mongo
17
+ module ThreadLocalVariableManager
18
+ def thread_local
19
+ Thread.current[:mongo_thread_locals] ||= Hash.new do |hash, key|
20
+ hash[key] = Hash.new unless hash.key? key
21
+ hash[key]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,19 @@
1
+ # Copyright (C) 2009-2013 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'mongo/utils/conversions'
16
+ require 'mongo/utils/core_ext'
17
+ require 'mongo/utils/server_version'
18
+ require 'mongo/utils/support'
19
+ require 'mongo/utils/thread_local_variable_manager'