mongo 0.0.1

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.
@@ -0,0 +1,4 @@
1
+ %w(get_more_message insert_message kill_cursors_message message_header
2
+ msg_message query_message remove_message update_message).each { |f|
3
+ require "mongo/message/#{f}"
4
+ }
@@ -0,0 +1,21 @@
1
+ require 'mongo/message/message'
2
+ require 'mongo/message/opcodes'
3
+
4
+ module XGen
5
+ module Mongo
6
+ module Driver
7
+
8
+ class GetMoreMessage < Message
9
+
10
+ def initialize(db_name, collection_name, cursor)
11
+ super(OP_GET_MORE)
12
+ write_int(0)
13
+ write_string("#{db_name}.#{collection_name}")
14
+ write_int(0) # num to return; leave it up to the db for now
15
+ write_long(cursor)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,19 @@
1
+ require 'mongo/message/message'
2
+ require 'mongo/message/opcodes'
3
+
4
+ module XGen
5
+ module Mongo
6
+ module Driver
7
+
8
+ class InsertMessage < Message
9
+
10
+ def initialize(db_name, collection_name, *objs)
11
+ super(OP_INSERT)
12
+ write_int(0)
13
+ write_string("#{db_name}.#{collection_name}")
14
+ objs.each { |o| write_doc(o) }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,20 @@
1
+ require 'mongo/message/message'
2
+ require 'mongo/message/opcodes'
3
+
4
+ module XGen
5
+ module Mongo
6
+ module Driver
7
+
8
+ class KillCursorsMessage < Message
9
+
10
+ def initialize(*cursors)
11
+ super(OP_KILL_CURSORS)
12
+ write_int(0)
13
+ write_int(cursors.length)
14
+ cursors.each { |c| write_long c }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,68 @@
1
+ require 'mongo/util/bson'
2
+ require 'mongo/util/byte_buffer'
3
+
4
+ module XGen
5
+ module Mongo
6
+ module Driver
7
+
8
+ class Message
9
+
10
+ HEADER_SIZE = 16 # size, id, response_to, opcode
11
+
12
+ @@class_req_id = 0
13
+
14
+ attr_reader :buf # for testing
15
+
16
+ def initialize(op)
17
+ @op = op
18
+ @message_length = HEADER_SIZE
19
+ @data_length = 0
20
+ @request_id = (@@class_req_id += 1)
21
+ @response_id = 0
22
+ @buf = ByteBuffer.new
23
+
24
+ @buf.put_int(16) # holder for length
25
+ @buf.put_int(@request_id)
26
+ @buf.put_int(0) # response_to
27
+ @buf.put_int(op)
28
+ end
29
+
30
+ def write_int(i)
31
+ @buf.put_int(i)
32
+ update_message_length
33
+ end
34
+
35
+ def write_long(i)
36
+ @buf.put_long(i)
37
+ update_message_length
38
+ end
39
+
40
+ def write_string(s)
41
+ BSON.serialize_cstr(@buf, s)
42
+ update_message_length
43
+ end
44
+
45
+ def write_doc(hash)
46
+ @buf.put_array(BSON.new.serialize(hash).to_a)
47
+ update_message_length
48
+ end
49
+
50
+ def to_a
51
+ @buf.to_a
52
+ end
53
+
54
+ def dump
55
+ @buf.dump
56
+ end
57
+
58
+ # Do not call. Private, but kept public for testing.
59
+ def update_message_length
60
+ pos = @buf.position
61
+ @buf.put_int(@buf.size, 0)
62
+ @buf.position = pos
63
+ end
64
+
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,34 @@
1
+ require 'mongo/util/byte_buffer'
2
+
3
+ module XGen
4
+ module Mongo
5
+ module Driver
6
+
7
+ class MessageHeader
8
+
9
+ HEADER_SIZE = 16
10
+
11
+ def initialize()
12
+ @buf = ByteBuffer.new
13
+ end
14
+
15
+ def read_header(socket)
16
+ @buf.rewind
17
+ @buf.put_array(socket.recv(HEADER_SIZE).unpack("C*"))
18
+ raise "Short read for DB response header: expected #{HEADER_SIZE} bytes, saw #{@buf.size}" unless @buf.size == HEADER_SIZE
19
+ @buf.rewind
20
+ @size = @buf.get_int
21
+ @request_id = @buf.get_int
22
+ @response_to = @buf.get_int
23
+ @op = @buf.get_int
24
+ self
25
+ end
26
+
27
+ def dump
28
+ @buf.dump
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,17 @@
1
+ require 'mongo/message/message'
2
+ require 'mongo/message/opcodes'
3
+
4
+ module XGen
5
+ module Mongo
6
+ module Driver
7
+
8
+ class MsgMessage < Message
9
+
10
+ def initialize(msg)
11
+ super(OP_MSG)
12
+ write_string(msg)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ module XGen
2
+ module Mongo
3
+ module Driver
4
+ OP_REPLY = 1 # reply. responseTo is set.
5
+ OP_MSG = 1000 # generic msg command followed by a string
6
+ OP_UPDATE = 2001 # update object
7
+ OP_INSERT = 2002
8
+ # GET_BY_OID = 2003
9
+ OP_QUERY = 2004
10
+ OP_GET_MORE = 2005
11
+ OP_DELETE = 2006
12
+ OP_KILL_CURSORS = 2007
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,45 @@
1
+ require 'mongo/message/message'
2
+ require 'mongo/message/opcodes'
3
+ require 'mongo/util/ordered_hash'
4
+
5
+ module XGen
6
+ module Mongo
7
+ module Driver
8
+
9
+ class QueryMessage < Message
10
+
11
+ def initialize(db_name, collection_name, query)
12
+ super(OP_QUERY)
13
+ write_int(0)
14
+ write_string("#{db_name}.#{collection_name}")
15
+ write_int(query.number_to_skip)
16
+ write_int(query.number_to_return)
17
+ sel = query.selector
18
+ if query.order_by && query.order_by.length > 0
19
+ sel = OrderedHash.new
20
+ sel['query'] = query.selector
21
+ sel['orderby'] = case query.order_by
22
+ when String
23
+ {query.order_by => 1}
24
+ when Array
25
+ h = OrderedHash.new
26
+ query.order_by.each { |ob| h[ob] = 1 }
27
+ h
28
+ when Hash # Should be an ordered hash, but this message doesn't care
29
+ query.order_by
30
+ else
31
+ raise "illegal order_by: is a #{query.order_by.class.name}, must be String, Array, Hash, or OrderedHash"
32
+ end
33
+
34
+ end
35
+ write_doc(sel)
36
+ write_doc(query.fields) if query.fields
37
+ end
38
+
39
+ def first_key(key)
40
+ @first_key = key
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,20 @@
1
+ require 'mongo/message/message'
2
+ require 'mongo/message/opcodes'
3
+
4
+ module XGen
5
+ module Mongo
6
+ module Driver
7
+
8
+ class RemoveMessage < Message
9
+
10
+ def initialize(db_name, collection_name, sel)
11
+ super(OP_DELETE)
12
+ write_int(0)
13
+ write_string("#{db_name}.#{collection_name}")
14
+ write_int(0) # flags?
15
+ write_doc(sel)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,21 @@
1
+ require 'mongo/message/message'
2
+ require 'mongo/message/opcodes'
3
+
4
+ module XGen
5
+ module Mongo
6
+ module Driver
7
+
8
+ class UpdateMessage < Message
9
+
10
+ def initialize(db_name, collection_name, sel, obj, repsert)
11
+ super(OP_UPDATE)
12
+ write_int(0)
13
+ write_string("#{db_name}.#{collection_name}")
14
+ write_int(repsert ? 1 : 0) # 1 if a repsert operation (upsert)
15
+ write_doc(sel)
16
+ write_doc(obj)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,52 @@
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
+ require 'mongo/db'
18
+
19
+ module XGen
20
+ module Mongo
21
+ module Driver
22
+
23
+ # Represents a Mongo database server.
24
+ class Mongo
25
+
26
+ DEFAULT_PORT = 27017
27
+
28
+ # Host default is 'localhost', port default is DEFAULT_PORT.
29
+ def initialize(host='localhost', port=DEFAULT_PORT)
30
+ @host, @port = host, port
31
+ end
32
+
33
+ # Return the XGen::Mongo::Driver::DB named +db_name+.
34
+ def db(db_name)
35
+ XGen::Mongo::Driver::DB.new(db_name, @host, @port)
36
+ end
37
+
38
+ # Not implemented.
39
+ def clone_database(from)
40
+ raise "not implemented"
41
+ end
42
+
43
+ # Not implemented.
44
+ def copy_database(from_host, from_db, to_db)
45
+ raise "not implemented"
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,107 @@
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
+ require 'mutex_m'
18
+ require 'mongo/util/byte_buffer'
19
+
20
+ module XGen
21
+ module Mongo
22
+ module Driver
23
+
24
+ # Implementation of the Babble OID. Object ids are not required by
25
+ # Mongo, but they make certain operations more efficient.
26
+ #
27
+ # The driver does not automatically assign ids to records that are
28
+ # inserted. (An upcoming feature will allow you to give an id "factory"
29
+ # to a database and/or a collection.)
30
+ #
31
+ # 12 bytes
32
+ # ---
33
+ # 0 time
34
+ # 1
35
+ # 2
36
+ # 3
37
+ # 4 machine
38
+ # 5
39
+ # 6
40
+ # 7 pid
41
+ # 8
42
+ # 9 inc
43
+ # 10
44
+ # 11
45
+ class ObjectID
46
+
47
+ MACHINE = ( val = rand(0x1000000); [val & 0xff, (val >> 8) & 0xff, (val >> 16) & 0xff] )
48
+ PID = ( val = rand(0x10000); [val & 0xff, (val >> 8) & 0xff]; )
49
+
50
+ LOCK = Object.new
51
+ LOCK.extend Mutex_m
52
+
53
+ @@index_time = Time.new.to_i
54
+ @@index = 0
55
+
56
+ # +data+ is an array of bytes. If nil, a new id will be generated.
57
+ # The time +t+ is only used for testing; leave it nil.
58
+ def initialize(data=nil, t=nil)
59
+ @data = data || generate_id(t)
60
+ end
61
+
62
+ def eql?(other)
63
+ @data == other.to_a
64
+ end
65
+ alias_method :==, :eql?
66
+
67
+ def to_a
68
+ @data.dup
69
+ end
70
+
71
+ def to_s
72
+ @data.collect { |b| '%02x' % b }.join
73
+ end
74
+
75
+ # (Would normally be private, but isn't so we can test it.)
76
+ def generate_id(t=nil)
77
+ t ||= Time.new.to_i
78
+ buf = ByteBuffer.new
79
+ buf.put_int(t & 0xffffffff)
80
+ buf.put_array(MACHINE)
81
+ buf.put_array(PID)
82
+ i = index_for_time(t)
83
+ buf.put(i & 0xff)
84
+ buf.put((i >> 8) & 0xff)
85
+ buf.put((i >> 16) & 0xff)
86
+
87
+ buf.rewind
88
+ buf.to_a.dup
89
+ end
90
+
91
+ # (Would normally be private, but isn't so we can test it.)
92
+ def index_for_time(t)
93
+ LOCK.mu_synchronize {
94
+ if t != @@index_time
95
+ @@index = 0
96
+ @@index_time = t
97
+ end
98
+ retval = @@index
99
+ @@index += 1
100
+ retval
101
+ }
102
+ end
103
+
104
+ end
105
+ end
106
+ end
107
+ end