mongo 0.0.3 → 0.0.4
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.
- data/README.rdoc +15 -0
- data/Rakefile +1 -1
- data/lib/mongo.rb +6 -4
- data/lib/mongo/admin.rb +1 -1
- data/lib/mongo/collection.rb +11 -2
- data/lib/mongo/cursor.rb +53 -6
- data/lib/mongo/db.rb +31 -8
- data/lib/mongo/message/query_message.rb +3 -0
- data/lib/mongo/{binary.rb → types/binary.rb} +0 -0
- data/lib/mongo/{dbref.rb → types/dbref.rb} +0 -0
- data/lib/mongo/{objectid.rb → types/objectid.rb} +0 -0
- data/lib/mongo/types/regexp_of_holding.rb +44 -0
- data/lib/mongo/{undefined.rb → types/undefined.rb} +0 -0
- data/lib/mongo/util/bson.rb +34 -22
- data/lib/mongo/util/ordered_hash.rb +8 -0
- data/tests/test_bson.rb +20 -1
- data/tests/test_cursor.rb +63 -0
- data/tests/test_db_api.rb +4 -0
- metadata +8 -6
data/README.rdoc
CHANGED
@@ -49,6 +49,21 @@ be running, of course.
|
|
49
49
|
See also the test code, especially tests/test_db_api.rb.
|
50
50
|
|
51
51
|
|
52
|
+
= Notes
|
53
|
+
|
54
|
+
== String Encoding
|
55
|
+
|
56
|
+
The BSON ("Binary JSON") format used to communicate with Mongo requires that
|
57
|
+
strings be UTF-8 (http://en.wikipedia.org/wiki/UTF-8).
|
58
|
+
|
59
|
+
Ruby 1.9 has built-in character encoding support. All strings sent to Mongo
|
60
|
+
and received from Mongo are converted to UTF-8 when necessary, and strings
|
61
|
+
read from Mongo will have their character encodings set to UTF-8.
|
62
|
+
|
63
|
+
When used with Ruby 1.8, the bytes in each string are written to and read from
|
64
|
+
Mongo as-is. If the string is ASCII all is well, because ASCII is a subset of
|
65
|
+
UTF-8. If the string is not ASCII then it may not be a well-formed UTF-8 string.
|
66
|
+
|
52
67
|
= Testing
|
53
68
|
|
54
69
|
If you have the source code, you can run the tests.
|
data/Rakefile
CHANGED
@@ -7,7 +7,7 @@ require 'rake/gempackagetask'
|
|
7
7
|
require 'rake/contrib/rubyforgepublisher'
|
8
8
|
|
9
9
|
GEM = "mongo"
|
10
|
-
GEM_VERSION = '0.0.
|
10
|
+
GEM_VERSION = '0.0.4'
|
11
11
|
SUMMARY = 'Simple pure-Ruby driver for the 10gen Mongo DB'
|
12
12
|
DESCRIPTION = 'This is a simple pure-Ruby driver for the 10gen Mongo DB. For more information about Mongo, see http://www.mongodb.org.'
|
13
13
|
AUTHOR = 'Jim Menard'
|
data/lib/mongo.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
+
require 'mongo/types/binary'
|
2
|
+
require 'mongo/types/dbref'
|
3
|
+
require 'mongo/types/objectid'
|
4
|
+
require 'mongo/types/regexp_of_holding'
|
5
|
+
require 'mongo/types/undefined'
|
6
|
+
|
1
7
|
require 'mongo/mongo'
|
2
|
-
require 'mongo/objectid'
|
3
|
-
require 'mongo/dbref'
|
4
|
-
require 'mongo/binary'
|
5
|
-
require 'mongo/undefined'
|
6
8
|
require 'mongo/message'
|
7
9
|
require 'mongo/db'
|
8
10
|
require 'mongo/cursor'
|
data/lib/mongo/admin.rb
CHANGED
@@ -66,7 +66,7 @@ module XGen
|
|
66
66
|
# Return an array contining current profiling information from the
|
67
67
|
# database.
|
68
68
|
def profiling_info
|
69
|
-
@db.query(DB::SYSTEM_PROFILE_COLLECTION, Query.new({})).to_a
|
69
|
+
@db.query(Collection.new(@db, DB::SYSTEM_PROFILE_COLLECTION), Query.new({})).to_a
|
70
70
|
end
|
71
71
|
|
72
72
|
# Validate a named collection by raising an exception if there is a
|
data/lib/mongo/collection.rb
CHANGED
@@ -23,13 +23,22 @@ module XGen
|
|
23
23
|
# A named collection of records in a database.
|
24
24
|
class Collection
|
25
25
|
|
26
|
-
attr_reader :db, :name
|
26
|
+
attr_reader :db, :name, :hint_fields
|
27
27
|
|
28
28
|
def initialize(db, name)
|
29
29
|
@db = db
|
30
30
|
@name = name
|
31
31
|
end
|
32
32
|
|
33
|
+
# Set hint fields to use and return +self+. hint_fields may be a
|
34
|
+
# single field name or array of field names. May be +nil+. If no hint
|
35
|
+
# fields are specified, the ones in the collection are used if they
|
36
|
+
# exist.
|
37
|
+
def hint(hint_fields)
|
38
|
+
@hint_fileds = hint_fileds
|
39
|
+
self
|
40
|
+
end
|
41
|
+
|
33
42
|
# Return records that match a +selector+ hash. See Mongo docs for
|
34
43
|
# details.
|
35
44
|
#
|
@@ -47,7 +56,7 @@ module XGen
|
|
47
56
|
limit = options.delete(:limit) || 0
|
48
57
|
sort = options.delete(:sort)
|
49
58
|
raise RuntimeError, "Unknown options [#{options.inspect}]" unless options.empty?
|
50
|
-
@db.query(
|
59
|
+
@db.query(self, Query.new(selector, fields, offset, limit, sort))
|
51
60
|
end
|
52
61
|
|
53
62
|
# Insert +objects+, which are hashes. "<<" is aliased to this method.
|
data/lib/mongo/cursor.rb
CHANGED
@@ -29,12 +29,26 @@ module XGen
|
|
29
29
|
|
30
30
|
RESPONSE_HEADER_SIZE = 20
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
attr_reader :db, :collection, :query
|
33
|
+
|
34
|
+
def initialize(db, collection, query)
|
35
|
+
@db, @collection, @query = db, collection, query
|
36
|
+
@num_to_return = @query.number_to_return || 0
|
34
37
|
@cache = []
|
35
38
|
@closed = false
|
36
39
|
@can_call_to_a = true
|
37
|
-
|
40
|
+
@query_run = false
|
41
|
+
end
|
42
|
+
|
43
|
+
def closed?; @closed; end
|
44
|
+
|
45
|
+
# Set hint fields to use and return +self+. hint_fields may be a
|
46
|
+
# single field name or array of field names. May be +nil+. If no hint
|
47
|
+
# fields are specified, the ones in the collection are used if they
|
48
|
+
# exist.
|
49
|
+
def hint(hint_fields)
|
50
|
+
@hint_fields = hint_fields
|
51
|
+
self
|
38
52
|
end
|
39
53
|
|
40
54
|
# Return +true+ if there are more records to retrieve. We do not check
|
@@ -100,9 +114,20 @@ module XGen
|
|
100
114
|
@rows
|
101
115
|
end
|
102
116
|
|
117
|
+
# Returns an explain plan record.
|
118
|
+
def explain
|
119
|
+
sel = OrderedHash.new
|
120
|
+
sel['query'] = @query.selector
|
121
|
+
sel['$explain'] = true
|
122
|
+
c = Cursor.new(@db, @collection, Query.new(sel))
|
123
|
+
e = c.next_object
|
124
|
+
c.close
|
125
|
+
e
|
126
|
+
end
|
127
|
+
|
103
128
|
# Close the cursor.
|
104
129
|
def close
|
105
|
-
@db.send_to_db(
|
130
|
+
@db.send_to_db(KillCursorsMessage.new(@cursor_id)) if @cursor_id
|
106
131
|
@cache = []
|
107
132
|
@cursor_id = 0
|
108
133
|
@closed = true
|
@@ -146,6 +171,7 @@ module XGen
|
|
146
171
|
private
|
147
172
|
|
148
173
|
def next_object_on_wire
|
174
|
+
send_query_if_needed
|
149
175
|
# if @n_remaining is 0 but we have a non-zero cursor, there are more
|
150
176
|
# to fetch, so do a GetMore operation, but don't do it here - do it
|
151
177
|
# when someone pulls an object out of the cache and it's empty
|
@@ -154,8 +180,9 @@ module XGen
|
|
154
180
|
end
|
155
181
|
|
156
182
|
def refill_via_get_more
|
183
|
+
send_query_if_needed
|
157
184
|
return if @cursor_id == 0
|
158
|
-
@db.send_to_db(GetMoreMessage.new(@db.name, @collection, @cursor_id))
|
185
|
+
@db.send_to_db(GetMoreMessage.new(@db.name, @collection.name, @cursor_id))
|
159
186
|
read_all
|
160
187
|
end
|
161
188
|
|
@@ -170,6 +197,27 @@ module XGen
|
|
170
197
|
BSON.new(@db).deserialize(buf)
|
171
198
|
end
|
172
199
|
|
200
|
+
def send_query_if_needed
|
201
|
+
# Run query first time we request an object from the wire
|
202
|
+
unless @query_run
|
203
|
+
hints = @hint_fields || @collection.hint_fields
|
204
|
+
hints = [hints] if hints.kind_of?(String)
|
205
|
+
query = if hints
|
206
|
+
h = {}
|
207
|
+
hints.each { |field| h[field] = 1 }
|
208
|
+
sel = OrderedHash.new
|
209
|
+
sel['query'] = @query.selector
|
210
|
+
sel['$hint'] = h
|
211
|
+
Query.new(sel)
|
212
|
+
else
|
213
|
+
@query
|
214
|
+
end
|
215
|
+
@db.send_query_message(QueryMessage.new(@db.name, @collection.name, query))
|
216
|
+
@query_run = true
|
217
|
+
read_all
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
173
221
|
def to_s
|
174
222
|
"DBResponse(flags=#@result_flags, cursor_id=#@cursor_id, start=#@starting_from, n_returned=#@n_returned)"
|
175
223
|
end
|
@@ -177,4 +225,3 @@ module XGen
|
|
177
225
|
end
|
178
226
|
end
|
179
227
|
end
|
180
|
-
|
data/lib/mongo/db.rb
CHANGED
@@ -49,6 +49,8 @@ module XGen
|
|
49
49
|
# The name of the database.
|
50
50
|
attr_reader :name
|
51
51
|
|
52
|
+
attr_reader :host, :port
|
53
|
+
|
52
54
|
# The database's socket. For internal use only.
|
53
55
|
attr_reader :socket
|
54
56
|
|
@@ -82,7 +84,7 @@ module XGen
|
|
82
84
|
def collections_info(coll_name=nil)
|
83
85
|
selector = {}
|
84
86
|
selector[:name] = full_coll_name(coll_name) if coll_name
|
85
|
-
query(SYSTEM_NAMESPACE_COLLECTION, Query.new(selector))
|
87
|
+
query(Collection.new(self, SYSTEM_NAMESPACE_COLLECTION), Query.new(selector))
|
86
88
|
end
|
87
89
|
|
88
90
|
# Create a collection. If +strict+ is false, will return existing or
|
@@ -151,6 +153,20 @@ module XGen
|
|
151
153
|
ok?(doc) && is_master.kind_of?(Numeric) && is_master.to_i == 1
|
152
154
|
end
|
153
155
|
|
156
|
+
# Returns a string of the form "host:port" that points to the master
|
157
|
+
# database. Works even if this is the master database.
|
158
|
+
def master
|
159
|
+
doc = db_command(:ismaster => 1)
|
160
|
+
is_master = doc['ismaster']
|
161
|
+
raise "Error retrieving master database" unless ok?(doc) && is_master.kind_of?(Numeric)
|
162
|
+
case is_master.to_i
|
163
|
+
when 1
|
164
|
+
"#@host:#@port"
|
165
|
+
else
|
166
|
+
doc['remote']
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
154
170
|
# Close the connection to the database.
|
155
171
|
def close
|
156
172
|
@socket.close
|
@@ -161,12 +177,19 @@ module XGen
|
|
161
177
|
send_to_db(MsgMessage.new(msg))
|
162
178
|
end
|
163
179
|
|
164
|
-
#
|
165
|
-
#
|
166
|
-
|
180
|
+
# Returns a Cursor over the query results.
|
181
|
+
#
|
182
|
+
# Note that the query gets sent lazily; the cursor calls
|
183
|
+
# #send_query_message when needed. If the caller never requests an
|
184
|
+
# object from the cursor, the query never gets sent.
|
185
|
+
def query(collection, query)
|
186
|
+
Cursor.new(self, collection, query)
|
187
|
+
end
|
188
|
+
|
189
|
+
# Used by a Cursor to lazily send the query to the database.
|
190
|
+
def send_query_message(query_message)
|
167
191
|
@semaphore.synchronize {
|
168
|
-
send_to_db(
|
169
|
-
Cursor.new(self, collection_name, query.number_to_return)
|
192
|
+
send_to_db(query_message)
|
170
193
|
}
|
171
194
|
end
|
172
195
|
|
@@ -233,7 +256,7 @@ module XGen
|
|
233
256
|
# :ns :: Namespace; same as +collection_name+.
|
234
257
|
def index_information(collection_name)
|
235
258
|
sel = {:ns => full_coll_name(collection_name)}
|
236
|
-
query(SYSTEM_INDEX_COLLECTION, Query.new(sel)).collect { |row|
|
259
|
+
query(Collection.new(self, SYSTEM_INDEX_COLLECTION), Query.new(sel)).collect { |row|
|
237
260
|
h = {:name => row['name']}
|
238
261
|
raise "Name of index on return from db was nil. Coll = #{full_coll_name(collection_name)}" unless h[:name]
|
239
262
|
|
@@ -298,7 +321,7 @@ module XGen
|
|
298
321
|
|
299
322
|
q = Query.new(selector)
|
300
323
|
q.number_to_return = 1
|
301
|
-
query(SYSTEM_COMMAND_COLLECTION, q).next_object
|
324
|
+
query(Collection.new(self, SYSTEM_COMMAND_COLLECTION), q).next_object
|
302
325
|
end
|
303
326
|
|
304
327
|
end
|
@@ -8,8 +8,11 @@ module XGen
|
|
8
8
|
|
9
9
|
class QueryMessage < Message
|
10
10
|
|
11
|
+
attr_reader :query
|
12
|
+
|
11
13
|
def initialize(db_name, collection_name, query)
|
12
14
|
super(OP_QUERY)
|
15
|
+
@query = query
|
13
16
|
write_int(0)
|
14
17
|
write_string("#{db_name}.#{collection_name}")
|
15
18
|
write_int(query.number_to_skip)
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
3
|
+
#
|
4
|
+
# This program is free software: you can redistribute it and/or modify it
|
5
|
+
# under the terms of the GNU Affero General Public License, version 3, as
|
6
|
+
# published by the Free Software Foundation.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
9
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
10
|
+
# FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License
|
11
|
+
# for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU Affero General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
module XGen
|
18
|
+
module Mongo
|
19
|
+
module Driver
|
20
|
+
|
21
|
+
# A Regexp that can hold on to extra options and ignore them. Mongo
|
22
|
+
# regexes may contain option characters beyond 'i', 'm', and 'x'. (Note
|
23
|
+
# that Mongo only uses those three, but that regexes coming from other
|
24
|
+
# languages may store different option characters.)
|
25
|
+
#
|
26
|
+
# Note that you do not have to use this class at all if you wish to
|
27
|
+
# store regular expressions in Mongo. The Mongo and Ruby regex option
|
28
|
+
# flags are the same. Storing regexes is discouraged, in any case.
|
29
|
+
class RegexpOfHolding < Regexp
|
30
|
+
|
31
|
+
attr_accessor :extra_options_str
|
32
|
+
|
33
|
+
# +str+ and +options+ are the same as Regexp. +extra_options_str+
|
34
|
+
# contains all the other flags that were in Mongo but we do not use or
|
35
|
+
# understand.
|
36
|
+
def initialize(str, options, extra_options_str)
|
37
|
+
super(str, options)
|
38
|
+
@extra_options_str = extra_options_str
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
File without changes
|
data/lib/mongo/util/bson.rb
CHANGED
@@ -17,8 +17,11 @@
|
|
17
17
|
require 'base64'
|
18
18
|
require 'mongo/util/byte_buffer'
|
19
19
|
require 'mongo/util/ordered_hash'
|
20
|
-
require 'mongo/
|
21
|
-
require 'mongo/dbref'
|
20
|
+
require 'mongo/types/binary'
|
21
|
+
require 'mongo/types/dbref'
|
22
|
+
require 'mongo/types/objectid'
|
23
|
+
require 'mongo/types/regexp_of_holding'
|
24
|
+
require 'mongo/types/undefined'
|
22
25
|
|
23
26
|
# A BSON seralizer/deserializer.
|
24
27
|
class BSON
|
@@ -43,8 +46,18 @@ class BSON
|
|
43
46
|
NUMBER_INT = 16
|
44
47
|
MAXKEY = 127
|
45
48
|
|
49
|
+
if RUBY_VERSION >= '1.9'
|
50
|
+
def self.to_utf8(str)
|
51
|
+
str.encode("utf-8")
|
52
|
+
end
|
53
|
+
else
|
54
|
+
def self.to_utf8(str)
|
55
|
+
str # TODO punt for now
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
46
59
|
def self.serialize_cstr(buf, val)
|
47
|
-
buf.put_array(val.to_s.unpack("C*") + [0])
|
60
|
+
buf.put_array(to_utf8(val.to_s).unpack("C*") + [0])
|
48
61
|
end
|
49
62
|
|
50
63
|
def initialize(db=nil)
|
@@ -67,7 +80,7 @@ class BSON
|
|
67
80
|
obj.each {|k, v|
|
68
81
|
type = bson_type(v, k)
|
69
82
|
case type
|
70
|
-
when STRING, CODE
|
83
|
+
when STRING, CODE, SYMBOL
|
71
84
|
serialize_string_element(@buf, k, v, type)
|
72
85
|
when NUMBER, NUMBER_INT
|
73
86
|
serialize_number_element(@buf, k, v, type)
|
@@ -87,8 +100,6 @@ class BSON
|
|
87
100
|
serialize_null_element(@buf, k)
|
88
101
|
when REF
|
89
102
|
serialize_dbref_element(@buf, k, v)
|
90
|
-
when SYMBOL
|
91
|
-
serialize_symbol_element(@buf, k, v)
|
92
103
|
when BINARY
|
93
104
|
serialize_binary_element(@buf, k, v)
|
94
105
|
when UNDEFINED
|
@@ -118,6 +129,9 @@ class BSON
|
|
118
129
|
when STRING, CODE
|
119
130
|
key = deserialize_cstr(@buf)
|
120
131
|
doc[key] = deserialize_string_data(@buf)
|
132
|
+
when SYMBOL
|
133
|
+
key = deserialize_cstr(@buf)
|
134
|
+
doc[key] = deserialize_string_data(@buf).intern
|
121
135
|
when NUMBER
|
122
136
|
key = deserialize_cstr(@buf)
|
123
137
|
doc[key] = deserialize_number_data(@buf)
|
@@ -151,9 +165,6 @@ class BSON
|
|
151
165
|
when REF
|
152
166
|
key = deserialize_cstr(@buf)
|
153
167
|
doc[key] = deserialize_dbref_data(@buf, key, parent)
|
154
|
-
when SYMBOL
|
155
|
-
key = deserialize_cstr(@buf)
|
156
|
-
doc[key] = deserialize_symbol_data(@buf)
|
157
168
|
when BINARY
|
158
169
|
key = deserialize_cstr(@buf)
|
159
170
|
doc[key] = deserialize_binary_data(@buf)
|
@@ -170,6 +181,7 @@ class BSON
|
|
170
181
|
doc
|
171
182
|
end
|
172
183
|
|
184
|
+
# For debugging.
|
173
185
|
def hex_dump
|
174
186
|
str = ''
|
175
187
|
@buf.to_a.each_with_index { |b,i|
|
@@ -221,13 +233,18 @@ class BSON
|
|
221
233
|
options |= Regexp::IGNORECASE if options_str.include?('i')
|
222
234
|
options |= Regexp::MULTILINE if options_str.include?('m')
|
223
235
|
options |= Regexp::EXTENDED if options_str.include?('x')
|
224
|
-
|
236
|
+
options_str.gsub!(/[imx]/, '') # Now remove the three we understand
|
237
|
+
XGen::Mongo::Driver::RegexpOfHolding.new(str, options, options_str)
|
225
238
|
end
|
226
239
|
|
227
240
|
def deserialize_string_data(buf)
|
228
241
|
len = buf.get_int
|
229
242
|
bytes = buf.get(len)
|
230
|
-
bytes[0..-2].pack("C*")
|
243
|
+
str = bytes[0..-2].pack("C*")
|
244
|
+
if RUBY_VERSION >= '1.9'
|
245
|
+
str.force_encoding("utf-8")
|
246
|
+
end
|
247
|
+
str
|
231
248
|
end
|
232
249
|
|
233
250
|
def deserialize_oid_data(buf)
|
@@ -240,10 +257,6 @@ class BSON
|
|
240
257
|
XGen::Mongo::Driver::DBRef.new(parent, key, @db, ns, oid)
|
241
258
|
end
|
242
259
|
|
243
|
-
def deserialize_symbol_data(buf)
|
244
|
-
deserialize_cstr(buf).intern
|
245
|
-
end
|
246
|
-
|
247
260
|
def deserialize_binary_data(buf)
|
248
261
|
len = buf.get_int
|
249
262
|
bytes = buf.get(len)
|
@@ -268,12 +281,6 @@ class BSON
|
|
268
281
|
buf.put_array(val.object_id.to_a)
|
269
282
|
end
|
270
283
|
|
271
|
-
def serialize_symbol_element(buf, key, val)
|
272
|
-
buf.put(SYMBOL)
|
273
|
-
self.class.serialize_cstr(buf, key)
|
274
|
-
self.class.serialize_cstr(buf, val)
|
275
|
-
end
|
276
|
-
|
277
284
|
def serialize_binary_element(buf, key, val)
|
278
285
|
buf.put(BINARY)
|
279
286
|
self.class.serialize_cstr(buf, key)
|
@@ -342,7 +349,9 @@ class BSON
|
|
342
349
|
options_str << 'i' if ((options & Regexp::IGNORECASE) != 0)
|
343
350
|
options_str << 'm' if ((options & Regexp::MULTILINE) != 0)
|
344
351
|
options_str << 'x' if ((options & Regexp::EXTENDED) != 0)
|
345
|
-
|
352
|
+
options_str << val.extra_options_str if val.respond_to?(:extra_options_str)
|
353
|
+
# Must store option chars in alphabetical order
|
354
|
+
self.class.serialize_cstr(buf, options_str.split(//).sort.uniq.join)
|
346
355
|
end
|
347
356
|
|
348
357
|
def serialize_oid_element(buf, key, val)
|
@@ -379,6 +388,9 @@ class BSON
|
|
379
388
|
break if b == 0
|
380
389
|
chars << b.chr
|
381
390
|
end
|
391
|
+
if RUBY_VERSION >= '1.9'
|
392
|
+
chars.force_encoding("utf-8") # Mongo stores UTF-8
|
393
|
+
end
|
382
394
|
chars
|
383
395
|
end
|
384
396
|
|
@@ -15,8 +15,14 @@
|
|
15
15
|
# ++
|
16
16
|
|
17
17
|
# A hash in which the order of keys are preserved.
|
18
|
+
#
|
19
|
+
# Under Ruby 1.9 and greater, this class has no added methods because Ruby's
|
20
|
+
# Hash already keeps its keys ordered by order of insertion.
|
18
21
|
class OrderedHash < Hash
|
19
22
|
|
23
|
+
# We only need the body of this class if the RUBY_VERSION is before 1.9
|
24
|
+
if RUBY_VERSION < '1.9'
|
25
|
+
|
20
26
|
attr_accessor :ordered_keys
|
21
27
|
|
22
28
|
def keys
|
@@ -57,4 +63,6 @@ class OrderedHash < Hash
|
|
57
63
|
str << '}'
|
58
64
|
end
|
59
65
|
|
66
|
+
end # Ruby before 1.9
|
67
|
+
|
60
68
|
end
|
data/tests/test_bson.rb
CHANGED
@@ -59,7 +59,26 @@ class BSONTest < Test::Unit::TestCase
|
|
59
59
|
def test_regex
|
60
60
|
doc = {'doc' => /foobar/i}
|
61
61
|
@b.serialize(doc)
|
62
|
-
|
62
|
+
doc2 = @b.deserialize
|
63
|
+
assert_equal doc, doc2
|
64
|
+
|
65
|
+
r = doc2['doc']
|
66
|
+
assert_kind_of XGen::Mongo::Driver::RegexpOfHolding, r
|
67
|
+
assert_equal '', r.extra_options_str
|
68
|
+
|
69
|
+
r.extra_options_str << 'zywcab'
|
70
|
+
assert_equal 'zywcab', r.extra_options_str
|
71
|
+
|
72
|
+
b = BSON.new
|
73
|
+
doc = {'doc' => r}
|
74
|
+
b.serialize(doc)
|
75
|
+
doc2 = nil
|
76
|
+
doc2 = b.deserialize
|
77
|
+
assert_equal doc, doc2
|
78
|
+
|
79
|
+
r = doc2['doc']
|
80
|
+
assert_kind_of XGen::Mongo::Driver::RegexpOfHolding, r
|
81
|
+
assert_equal 'abcwyz', r.extra_options_str # must be sorted
|
63
82
|
end
|
64
83
|
|
65
84
|
def test_boolean
|
@@ -0,0 +1,63 @@
|
|
1
|
+
$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
require 'mongo'
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
# NOTE: assumes Mongo is running
|
6
|
+
class CursorTest < Test::Unit::TestCase
|
7
|
+
|
8
|
+
include XGen::Mongo::Driver
|
9
|
+
|
10
|
+
def setup
|
11
|
+
host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
|
12
|
+
port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT
|
13
|
+
@db = Mongo.new(host, port).db('ruby-mongo-test')
|
14
|
+
@coll = @db.collection('test')
|
15
|
+
@coll.clear
|
16
|
+
@r1 = @coll.insert('a' => 1) # collection not created until it's used
|
17
|
+
@coll_full_name = 'ruby-mongo-test.test'
|
18
|
+
end
|
19
|
+
|
20
|
+
def teardown
|
21
|
+
@coll.clear unless @coll == nil || @db.socket.closed?
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_explain
|
25
|
+
cursor = @coll.find('a' => 1)
|
26
|
+
explaination = cursor.explain
|
27
|
+
assert_not_nil explaination['cursor']
|
28
|
+
assert_kind_of Numeric, explaination['n']
|
29
|
+
assert_kind_of Numeric, explaination['millis']
|
30
|
+
assert_kind_of Numeric, explaination['nscanned']
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_close_no_query_sent
|
34
|
+
begin
|
35
|
+
cursor = @coll.find('a' => 1)
|
36
|
+
cursor.close
|
37
|
+
assert cursor.closed?
|
38
|
+
rescue => ex
|
39
|
+
fail ex.to_s
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_close_after_query_sent
|
44
|
+
begin
|
45
|
+
cursor = @coll.find('a' => 1)
|
46
|
+
cursor.next_object
|
47
|
+
cursor.close
|
48
|
+
assert cursor.closed?
|
49
|
+
rescue => ex
|
50
|
+
fail ex.to_s
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_hint
|
55
|
+
begin
|
56
|
+
cursor = @coll.find('a' => 1).hint('a')
|
57
|
+
assert_equal 1, cursor.to_a.size
|
58
|
+
rescue => ex
|
59
|
+
fail ex.to_s
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
data/tests/test_db_api.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jim Menard
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-01-
|
12
|
+
date: 2009-01-14 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -25,11 +25,9 @@ files:
|
|
25
25
|
- bin/mongo_console
|
26
26
|
- bin/validate
|
27
27
|
- lib/mongo/admin.rb
|
28
|
-
- lib/mongo/binary.rb
|
29
28
|
- lib/mongo/collection.rb
|
30
29
|
- lib/mongo/cursor.rb
|
31
30
|
- lib/mongo/db.rb
|
32
|
-
- lib/mongo/dbref.rb
|
33
31
|
- lib/mongo/message/get_more_message.rb
|
34
32
|
- lib/mongo/message/insert_message.rb
|
35
33
|
- lib/mongo/message/kill_cursors_message.rb
|
@@ -42,9 +40,12 @@ files:
|
|
42
40
|
- lib/mongo/message/update_message.rb
|
43
41
|
- lib/mongo/message.rb
|
44
42
|
- lib/mongo/mongo.rb
|
45
|
-
- lib/mongo/objectid.rb
|
46
43
|
- lib/mongo/query.rb
|
47
|
-
- lib/mongo/
|
44
|
+
- lib/mongo/types/binary.rb
|
45
|
+
- lib/mongo/types/dbref.rb
|
46
|
+
- lib/mongo/types/objectid.rb
|
47
|
+
- lib/mongo/types/regexp_of_holding.rb
|
48
|
+
- lib/mongo/types/undefined.rb
|
48
49
|
- lib/mongo/util/bson.rb
|
49
50
|
- lib/mongo/util/byte_buffer.rb
|
50
51
|
- lib/mongo/util/ordered_hash.rb
|
@@ -53,6 +54,7 @@ files:
|
|
53
54
|
- tests/test_admin.rb
|
54
55
|
- tests/test_bson.rb
|
55
56
|
- tests/test_byte_buffer.rb
|
57
|
+
- tests/test_cursor.rb
|
56
58
|
- tests/test_db_api.rb
|
57
59
|
- tests/test_db_connection.rb
|
58
60
|
- tests/test_message.rb
|