mongodb-mongo 0.2.4 → 0.2.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.
- data/README.rdoc +5 -0
- data/lib/mongo/cursor.rb +13 -1
- data/lib/mongo/db.rb +64 -22
- data/lib/mongo/mongo.rb +78 -22
- data/mongo-ruby-driver.gemspec +1 -1
- data/tests/test_db.rb +27 -10
- data/tests/test_db_api.rb +11 -0
- data/tests/test_mongo.rb +20 -0
- metadata +1 -1
data/README.rdoc
CHANGED
@@ -32,6 +32,11 @@ Install the "mongo" gem by typing
|
|
32
32
|
$ gem sources -a http://gems.github.com
|
33
33
|
$ sudo gem install mongodb-mongo-ruby-driver
|
34
34
|
|
35
|
+
The first line tells RubyGems to add the GitHub gem repository. You only need
|
36
|
+
to run this command once.
|
37
|
+
|
38
|
+
=== From the GitHub source
|
39
|
+
|
35
40
|
The source code is available at http://github.com/mongodb/mongo-ruby-driver.
|
36
41
|
You can either clone the git repository or download a tarball or zip file.
|
37
42
|
Once you have the source, you can use it from wherever you downloaded it or
|
data/lib/mongo/cursor.rb
CHANGED
@@ -70,7 +70,19 @@ module XGen
|
|
70
70
|
def next_object
|
71
71
|
refill_via_get_more if num_remaining == 0
|
72
72
|
o = @cache.shift
|
73
|
-
|
73
|
+
|
74
|
+
if o && o['$err']
|
75
|
+
err = o['$err']
|
76
|
+
|
77
|
+
# If the server has stopped being the master (e.g., it's one of a
|
78
|
+
# pair but it has died or something like that) then we close that
|
79
|
+
# connection. If the db has auto connect option and a pair of
|
80
|
+
# servers, next request will re-open on master server.
|
81
|
+
@db.close if err == "not master"
|
82
|
+
|
83
|
+
raise err
|
84
|
+
end
|
85
|
+
|
74
86
|
o
|
75
87
|
end
|
76
88
|
|
data/lib/mongo/db.rb
CHANGED
@@ -61,6 +61,9 @@ module XGen
|
|
61
61
|
# The database's socket. For internal (and Cursor) use only.
|
62
62
|
attr_reader :socket
|
63
63
|
|
64
|
+
def slave_ok?; @slave_ok; end
|
65
|
+
def auto_reconnect?; @auto_reconnect; end
|
66
|
+
|
64
67
|
# A primary key factory object (or +nil+). See the README.doc file or
|
65
68
|
# DB#new for details.
|
66
69
|
attr_reader :pk_factory
|
@@ -70,9 +73,12 @@ module XGen
|
|
70
73
|
@pk_factory = pk_factory
|
71
74
|
end
|
72
75
|
|
76
|
+
# Instances of DB are normally obtained by calling Mongo#db.
|
77
|
+
#
|
73
78
|
# db_name :: The database name
|
74
79
|
#
|
75
|
-
# nodes :: An array of [host, port] pairs.
|
80
|
+
# nodes :: An array of [host, port] pairs. See Mongo#new, which offers
|
81
|
+
# a more flexible way of defining nodes.
|
76
82
|
#
|
77
83
|
# options :: A hash of options.
|
78
84
|
#
|
@@ -92,27 +98,49 @@ module XGen
|
|
92
98
|
# object's +create_pk+ method will be called and the new hash
|
93
99
|
# returned will be inserted.
|
94
100
|
#
|
95
|
-
#
|
96
|
-
#
|
97
|
-
#
|
101
|
+
# :slave_ok :: Only used if +nodes+ contains only one host/port. If
|
102
|
+
# false, when connecting to that host/port we check to
|
103
|
+
# see if the server is the master. If it is not, an error
|
104
|
+
# is thrown.
|
105
|
+
#
|
106
|
+
# :auto_reconnect :: If the connection gets closed (for example, we
|
107
|
+
# have a server pair and saw the "not master"
|
108
|
+
# error, which closes the connection), then
|
109
|
+
# automatically try to reconnect to the master or
|
110
|
+
# to the single server we have been given. Defaults
|
111
|
+
# to +false+.
|
112
|
+
#
|
113
|
+
# When a DB object first connects to a pair, it will find the master
|
114
|
+
# instance and connect to that one. On socket error or if we recieve a
|
115
|
+
# "not master" error, we again find the master of the pair.
|
98
116
|
def initialize(db_name, nodes, options={})
|
99
117
|
raise "Invalid DB name \"#{db_name}\" (must be non-nil, non-zero-length, and can not contain \".\")" if !db_name || (db_name && db_name.length > 0 && db_name.include?("."))
|
100
118
|
@name, @nodes = db_name, nodes
|
101
119
|
@strict = options[:strict]
|
102
120
|
@pk_factory = options[:pk]
|
121
|
+
@slave_ok = options[:slave_ok] && @nodes.length == 1 # only OK if one node
|
122
|
+
@auto_reconnect = options[:auto_reconnect]
|
103
123
|
@semaphore = Object.new
|
104
124
|
@semaphore.extend Mutex_m
|
105
|
-
|
125
|
+
connect_to_master
|
106
126
|
end
|
107
127
|
|
108
|
-
def
|
128
|
+
def connect_to_master
|
109
129
|
close if @socket
|
110
130
|
@host = @port = nil
|
111
131
|
@nodes.detect { |hp|
|
112
132
|
@host, @port = *hp
|
113
133
|
begin
|
114
134
|
@socket = TCPSocket.new(@host, @port)
|
115
|
-
|
135
|
+
|
136
|
+
# Check for master. Can't call master? because it uses mutex,
|
137
|
+
# which may already be in use during this call.
|
138
|
+
semaphore_is_locked = @semaphore.locked?
|
139
|
+
@semaphore.unlock if semaphore_is_locked
|
140
|
+
is_master = master?
|
141
|
+
@semaphore.lock if semaphore_is_locked
|
142
|
+
|
143
|
+
break if @slave_ok || is_master
|
116
144
|
rescue => ex
|
117
145
|
close if @socket
|
118
146
|
end
|
@@ -214,6 +242,28 @@ module XGen
|
|
214
242
|
ok?(db_command(:drop => name))
|
215
243
|
end
|
216
244
|
|
245
|
+
# Returns the error message from the most recently executed database
|
246
|
+
# operation for this connection, or +nil+ if there was no error.
|
247
|
+
#
|
248
|
+
# Note: as of this writing, errors are only detected on the db server
|
249
|
+
# for certain kinds of operations (writes). The plan is to change this
|
250
|
+
# so that all operations will set the error if needed.
|
251
|
+
def error
|
252
|
+
doc = db_command(:getlasterror => 1)
|
253
|
+
raise "error retrieving last error: #{doc}" unless ok?(doc)
|
254
|
+
doc['err']
|
255
|
+
end
|
256
|
+
|
257
|
+
# Returns +true+ if there is was an error caused by the most recently
|
258
|
+
# executed database operation.
|
259
|
+
#
|
260
|
+
# Note: as of this writing, errors are only detected on the db server
|
261
|
+
# for certain kinds of operations (writes). The plan is to change this
|
262
|
+
# so that all operations will set the error if needed.
|
263
|
+
def error?
|
264
|
+
error != nil
|
265
|
+
end
|
266
|
+
|
217
267
|
# Returns true if this database is a master (or is not paired with any
|
218
268
|
# other database), false if it is a slave.
|
219
269
|
def master?
|
@@ -236,20 +286,6 @@ module XGen
|
|
236
286
|
end
|
237
287
|
end
|
238
288
|
|
239
|
-
# Switches our socket to the master database. If we are already the
|
240
|
-
# master, no change is made.
|
241
|
-
def switch_to_master
|
242
|
-
master_str = master()
|
243
|
-
unless master_str == "#@host:#@port"
|
244
|
-
@semaphore.synchronize {
|
245
|
-
master_str =~ /(.+):(\d+)/
|
246
|
-
@host, @port = $1, $2
|
247
|
-
close()
|
248
|
-
@socket = TCPSocket.new(@host, @port)
|
249
|
-
}
|
250
|
-
end
|
251
|
-
end
|
252
|
-
|
253
289
|
# Close the connection to the database.
|
254
290
|
def close
|
255
291
|
@socket.close if @socket
|
@@ -387,7 +423,13 @@ module XGen
|
|
387
423
|
end
|
388
424
|
|
389
425
|
def send_to_db(message)
|
390
|
-
@
|
426
|
+
connect_to_master if !connected? && @auto_reconnect
|
427
|
+
begin
|
428
|
+
@socket.print(message.buf.to_s)
|
429
|
+
rescue => ex
|
430
|
+
close
|
431
|
+
raise ex
|
432
|
+
end
|
391
433
|
end
|
392
434
|
|
393
435
|
def full_coll_name(collection_name)
|
data/lib/mongo/mongo.rb
CHANGED
@@ -25,37 +25,73 @@ module XGen
|
|
25
25
|
|
26
26
|
DEFAULT_PORT = 27017
|
27
27
|
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
# port is DEFAULT_PORT. Since that's so confusing, here are a few
|
33
|
-
# examples:
|
28
|
+
# Create a Mongo database server instance. You specify either one or a
|
29
|
+
# pair of servers. If one, you also say if connecting to a slave is
|
30
|
+
# OK. In either case, the host default is "localhost" and port default
|
31
|
+
# is DEFAULT_PORT.
|
34
32
|
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# Mongo.new([["db1.example.com", 3000], ["db2.example.com", 3000]]])
|
33
|
+
# If you specify a pair, pair_or_host is a hash with two keys :left
|
34
|
+
# and :right. Each key maps to either
|
35
|
+
# * a server name, in which case port is DEFAULT_PORT
|
36
|
+
# * a port number, in which case server is "localhost"
|
37
|
+
# * an array containing a server name and a port number in either order
|
41
38
|
#
|
42
|
-
#
|
43
|
-
#
|
44
|
-
|
45
|
-
|
39
|
+
# +options+ are passed on to each DB instance:
|
40
|
+
#
|
41
|
+
# :slave_ok :: Only used if one host is specified. If false, when
|
42
|
+
# connecting to that host/port a DB object will check to
|
43
|
+
# see if the server is the master. If it is not, an error
|
44
|
+
# is thrown.
|
45
|
+
#
|
46
|
+
# :auto_reconnect :: If a DB connection gets closed (for example, we
|
47
|
+
# have a server pair and saw the "not master"
|
48
|
+
# error, which closes the connection), then
|
49
|
+
# automatically try to reconnect to the master or
|
50
|
+
# to the single server we have been given. Defaults
|
51
|
+
# to +false+.
|
52
|
+
#
|
53
|
+
# Since that's so confusing, here are a few examples:
|
54
|
+
#
|
55
|
+
# Mongo.new # localhost, DEFAULT_PORT, !slave
|
56
|
+
# Mongo.new("localhost") # localhost, DEFAULT_PORT, !slave
|
57
|
+
# Mongo.new("localhost", 3000) # localhost, 3000, slave not ok
|
58
|
+
# # localhost, 3000, slave ok
|
59
|
+
# Mongo.new("localhost", 3000, :slave_ok => true)
|
60
|
+
# # localhost, DEFAULT_PORT, auto reconnect
|
61
|
+
# Mongo.new(nil, nil, :auto_reconnect => true)
|
62
|
+
#
|
63
|
+
# # A pair of servers. DB will always talk to the master. On socket
|
64
|
+
# # error or "not master" error, we will auto-reconnect to the
|
65
|
+
# # current master.
|
66
|
+
# Mongo.new({:left => ["db1.example.com", 3000],
|
67
|
+
# :right => "db2.example.com"}, # DEFAULT_PORT
|
68
|
+
# nil, :auto_reconnect => true)
|
69
|
+
#
|
70
|
+
# # Here, :right is localhost/DEFAULT_PORT. No auto-reconnect.
|
71
|
+
# Mongo.new({:left => ["db1.example.com", 3000]})
|
72
|
+
#
|
73
|
+
# When a DB object first connects to a pair, it will find the master
|
74
|
+
# instance and connect to that one.
|
75
|
+
def initialize(pair_or_host=nil, port=nil, options={})
|
76
|
+
@pair = case pair_or_host
|
46
77
|
when String
|
47
|
-
[[
|
48
|
-
when
|
49
|
-
|
78
|
+
[[pair_or_host, port || DEFAULT_PORT]]
|
79
|
+
when Hash
|
80
|
+
connections = []
|
81
|
+
connections << pair_val_to_connection(pair_or_host[:left])
|
82
|
+
connections << pair_val_to_connection(pair_or_host[:right])
|
83
|
+
connections
|
50
84
|
when nil
|
51
85
|
[['localhost', DEFAULT_PORT]]
|
52
86
|
end
|
87
|
+
@options = options
|
53
88
|
end
|
54
89
|
|
55
|
-
# Return the XGen::Mongo::Driver::DB named +db_name+.
|
56
|
-
#
|
90
|
+
# Return the XGen::Mongo::Driver::DB named +db_name+. The slave_ok and
|
91
|
+
# auto_reconnect options passed in via #new may be overridden here.
|
92
|
+
# See DB#new for other options you can pass in.
|
57
93
|
def db(db_name, options={})
|
58
|
-
XGen::Mongo::Driver::DB.new(db_name, @
|
94
|
+
XGen::Mongo::Driver::DB.new(db_name, @pair, @options.merge(options))
|
59
95
|
end
|
60
96
|
|
61
97
|
# Returns a hash containing database names as keys and disk space for
|
@@ -91,6 +127,26 @@ module XGen
|
|
91
127
|
|
92
128
|
protected
|
93
129
|
|
130
|
+
# Turns an array containing an optional host name string and an
|
131
|
+
# optional port number integer into a [host, port] pair array.
|
132
|
+
def pair_val_to_connection(a)
|
133
|
+
case a
|
134
|
+
when nil
|
135
|
+
['localhost', DEFAULT_PORT]
|
136
|
+
when String
|
137
|
+
[a, DEFAULT_PORT]
|
138
|
+
when Integer
|
139
|
+
['localhost', a]
|
140
|
+
when Array
|
141
|
+
connection = ['localhost', DEFAULT_PORT]
|
142
|
+
connection[0] = a[0] if a[0].kind_of?(String)
|
143
|
+
connection[0] = a[1] if a[1].kind_of?(String)
|
144
|
+
connection[1] = a[0] if a[0].kind_of?(Integer)
|
145
|
+
connection[1] = a[1] if a[1].kind_of?(Integer)
|
146
|
+
connection
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
94
150
|
# Send cmd (a hash, possibly ordered) to the admin database and return
|
95
151
|
# the answer. Raises an error unless the return is "ok" (DB#ok?
|
96
152
|
# returns +true+).
|
data/mongo-ruby-driver.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'mongo'
|
3
|
-
s.version = '0.2.
|
3
|
+
s.version = '0.2.5'
|
4
4
|
s.platform = Gem::Platform::RUBY
|
5
5
|
s.summary = 'Simple pure-Ruby driver for the 10gen Mongo DB'
|
6
6
|
s.description = 'A pure-Ruby driver for the 10gen Mongo DB. For more information about Mongo, see http://www.mongodb.org.'
|
data/tests/test_db.rb
CHANGED
@@ -28,7 +28,7 @@ class DBTest < Test::Unit::TestCase
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def teardown
|
31
|
-
if @db.connected?
|
31
|
+
if @db && @db.connected?
|
32
32
|
@users.clear if @users
|
33
33
|
@db.close
|
34
34
|
end
|
@@ -50,17 +50,10 @@ class DBTest < Test::Unit::TestCase
|
|
50
50
|
assert_equal 'ruby-mongo-test.test', @db.full_coll_name(coll.name)
|
51
51
|
end
|
52
52
|
|
53
|
-
def
|
54
|
-
# Doesn't really test anything since we probably only have one database
|
55
|
-
# during this test.
|
56
|
-
@db.switch_to_master
|
57
|
-
assert @db.connected?
|
58
|
-
end
|
59
|
-
|
60
|
-
def test_array
|
53
|
+
def test_pair
|
61
54
|
@db.close
|
62
55
|
@users = nil
|
63
|
-
@db = Mongo.new(
|
56
|
+
@db = Mongo.new({:left => "nosuch.example.com", :right => [@host, @port]}).db('ruby-mongo-test')
|
64
57
|
assert @db.connected?
|
65
58
|
end
|
66
59
|
|
@@ -101,4 +94,28 @@ class DBTest < Test::Unit::TestCase
|
|
101
94
|
@db.logout # only testing that we don't throw exception
|
102
95
|
end
|
103
96
|
|
97
|
+
def test_auto_connect
|
98
|
+
@db.close
|
99
|
+
db = Mongo.new(@host, @port, :auto_reconnect => true).db('ruby-mongo-test')
|
100
|
+
assert db.connected?
|
101
|
+
assert db.auto_reconnect?
|
102
|
+
db.close
|
103
|
+
assert !db.connected?
|
104
|
+
assert db.auto_reconnect?
|
105
|
+
db.collection('test').insert('a' => 1)
|
106
|
+
assert db.connected?
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_error
|
110
|
+
doc = @db.send(:db_command, :forceerror => 1)
|
111
|
+
assert @db.error?
|
112
|
+
err = @db.error
|
113
|
+
assert_match /forced error/, err
|
114
|
+
|
115
|
+
# ask again
|
116
|
+
assert @db.error?
|
117
|
+
err2 = @db.error
|
118
|
+
assert_equal err, err2
|
119
|
+
end
|
120
|
+
|
104
121
|
end
|
data/tests/test_db_api.rb
CHANGED
@@ -47,6 +47,17 @@ class DBAPITest < Test::Unit::TestCase
|
|
47
47
|
assert docs.detect { |row| row['b'] == 4 }
|
48
48
|
end
|
49
49
|
|
50
|
+
def test_insert_multiple
|
51
|
+
@coll.insert({'a' => 2}, {'b' => 3})
|
52
|
+
|
53
|
+
assert_equal 3, @coll.count
|
54
|
+
docs = @coll.find().to_a
|
55
|
+
assert_equal 3, docs.length
|
56
|
+
assert docs.detect { |row| row['a'] == 1 }
|
57
|
+
assert docs.detect { |row| row['a'] == 2 }
|
58
|
+
assert docs.detect { |row| row['b'] == 3 }
|
59
|
+
end
|
60
|
+
|
50
61
|
def test_find_simple
|
51
62
|
@r2 = @coll.insert('a' => 2)
|
52
63
|
@r3 = @coll.insert('b' => 3)
|
data/tests/test_mongo.rb
CHANGED
@@ -41,4 +41,24 @@ class MongoTest < Test::Unit::TestCase
|
|
41
41
|
assert !@mongo.database_names.include?('will-be-deleted')
|
42
42
|
end
|
43
43
|
|
44
|
+
def test_pair
|
45
|
+
db = Mongo.new({:left => ['foo', 123]})
|
46
|
+
pair = db.instance_variable_get('@pair')
|
47
|
+
assert_equal 2, pair.length
|
48
|
+
assert_equal ['foo', 123], pair[0]
|
49
|
+
assert_equal ['localhost', Mongo::DEFAULT_PORT], pair[1]
|
50
|
+
|
51
|
+
db = Mongo.new({:right => 'bar'})
|
52
|
+
pair = db.instance_variable_get('@pair')
|
53
|
+
assert_equal 2, pair.length
|
54
|
+
assert_equal ['localhost', Mongo::DEFAULT_PORT], pair[0]
|
55
|
+
assert_equal ['bar', Mongo::DEFAULT_PORT], pair[1]
|
56
|
+
|
57
|
+
db = Mongo.new({:right => [123, 'foo'], :left => 'bar'})
|
58
|
+
pair = db.instance_variable_get('@pair')
|
59
|
+
assert_equal 2, pair.length
|
60
|
+
assert_equal ['bar', Mongo::DEFAULT_PORT], pair[0]
|
61
|
+
assert_equal ['foo', 123], pair[1]
|
62
|
+
end
|
63
|
+
|
44
64
|
end
|