mongodb-mongo 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/README.rdoc +216 -0
  2. data/Rakefile +54 -0
  3. data/bin/mongo_console +21 -0
  4. data/bin/validate +51 -0
  5. data/examples/benchmarks.rb +38 -0
  6. data/examples/blog.rb +76 -0
  7. data/examples/index_test.rb +128 -0
  8. data/examples/simple.rb +17 -0
  9. data/lib/mongo/admin.rb +86 -0
  10. data/lib/mongo/collection.rb +161 -0
  11. data/lib/mongo/cursor.rb +230 -0
  12. data/lib/mongo/db.rb +399 -0
  13. data/lib/mongo/message/get_more_message.rb +21 -0
  14. data/lib/mongo/message/insert_message.rb +19 -0
  15. data/lib/mongo/message/kill_cursors_message.rb +20 -0
  16. data/lib/mongo/message/message.rb +68 -0
  17. data/lib/mongo/message/message_header.rb +34 -0
  18. data/lib/mongo/message/msg_message.rb +17 -0
  19. data/lib/mongo/message/opcodes.rb +16 -0
  20. data/lib/mongo/message/query_message.rb +67 -0
  21. data/lib/mongo/message/remove_message.rb +20 -0
  22. data/lib/mongo/message/update_message.rb +21 -0
  23. data/lib/mongo/message.rb +4 -0
  24. data/lib/mongo/mongo.rb +98 -0
  25. data/lib/mongo/query.rb +110 -0
  26. data/lib/mongo/types/binary.rb +34 -0
  27. data/lib/mongo/types/dbref.rb +37 -0
  28. data/lib/mongo/types/objectid.rb +137 -0
  29. data/lib/mongo/types/regexp_of_holding.rb +44 -0
  30. data/lib/mongo/types/undefined.rb +31 -0
  31. data/lib/mongo/util/bson.rb +431 -0
  32. data/lib/mongo/util/byte_buffer.rb +163 -0
  33. data/lib/mongo/util/ordered_hash.rb +68 -0
  34. data/lib/mongo/util/xml_to_ruby.rb +102 -0
  35. data/lib/mongo.rb +12 -0
  36. data/mongo-ruby-driver.gemspec +62 -0
  37. data/tests/test_admin.rb +60 -0
  38. data/tests/test_bson.rb +135 -0
  39. data/tests/test_byte_buffer.rb +69 -0
  40. data/tests/test_cursor.rb +66 -0
  41. data/tests/test_db.rb +85 -0
  42. data/tests/test_db_api.rb +354 -0
  43. data/tests/test_db_connection.rb +17 -0
  44. data/tests/test_message.rb +35 -0
  45. data/tests/test_objectid.rb +98 -0
  46. data/tests/test_ordered_hash.rb +85 -0
  47. data/tests/test_round_trip.rb +116 -0
  48. metadata +100 -0
@@ -0,0 +1,102 @@
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 'rexml/document'
18
+ require 'mongo'
19
+
20
+ # Converts a .xson file (an XML file that describes a Mongo-type document) to
21
+ # an OrderedHash.
22
+ class XMLToRuby
23
+
24
+ include XGen::Mongo::Driver
25
+
26
+ def xml_to_ruby(io)
27
+ doc = REXML::Document.new(io)
28
+ doc_to_ruby(doc.root.elements['doc'])
29
+ end
30
+
31
+ protected
32
+
33
+ def element_to_ruby(e)
34
+ type = e.name
35
+ child = e.elements[1]
36
+ case type
37
+ when 'oid'
38
+ ObjectID.from_string(e.text)
39
+ when 'ref'
40
+ dbref_to_ruby(e.elements)
41
+ when 'int'
42
+ e.text.to_i
43
+ when 'number'
44
+ e.text.to_f
45
+ when 'string', 'code'
46
+ e.text.to_s
47
+ when 'binary'
48
+ Base64.decode64(e.text.to_s).to_mongo_binary
49
+ when 'symbol'
50
+ e.text.to_s.intern
51
+ when 'boolean'
52
+ e.text.to_s == 'true'
53
+ when 'array'
54
+ array_to_ruby(e.elements)
55
+ when 'date'
56
+ Time.at(e.text.to_f / 1000.0)
57
+ when 'regex'
58
+ regex_to_ruby(e.elements)
59
+ when 'null'
60
+ nil
61
+ when 'undefined'
62
+ Undefined.new
63
+ when 'doc'
64
+ doc_to_ruby(e)
65
+ else
66
+ raise "Unknown type #{type} in element with name #{e.attributes['name']}"
67
+ end
68
+ end
69
+
70
+ def doc_to_ruby(element)
71
+ oh = OrderedHash.new
72
+ element.elements.each { |e| oh[e.attributes['name']] = element_to_ruby(e) }
73
+ oh
74
+ end
75
+
76
+ def array_to_ruby(elements)
77
+ a = []
78
+ elements.each { |e|
79
+ index_str = e.attributes['name']
80
+ a[index_str.to_i] = element_to_ruby(e)
81
+ }
82
+ a
83
+ end
84
+
85
+ def regex_to_ruby(elements)
86
+ pattern = elements['pattern'].text
87
+ options_str = elements['options'].text || ''
88
+
89
+ options = 0
90
+ options |= Regexp::IGNORECASE if options_str.include?('i')
91
+ options |= Regexp::MULTILINE if options_str.include?('m')
92
+ options |= Regexp::EXTENDED if options_str.include?('x')
93
+ Regexp.new(pattern, options)
94
+ end
95
+
96
+ def dbref_to_ruby(elements)
97
+ ns = elements['ns'].text
98
+ oid_str = elements['oid'].text
99
+ DBRef.new(nil, nil, nil, ns, ObjectID.from_string(oid_str))
100
+ end
101
+
102
+ end
data/lib/mongo.rb ADDED
@@ -0,0 +1,12 @@
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
+
7
+ require 'mongo/mongo'
8
+ require 'mongo/message'
9
+ require 'mongo/db'
10
+ require 'mongo/cursor'
11
+ require 'mongo/collection'
12
+ require 'mongo/admin'
@@ -0,0 +1,62 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'mongo'
3
+ s.version = '0.1.3'
4
+ s.platform = Gem::Platform::RUBY
5
+ s.summary = 'Simple pure-Ruby driver for the 10gen Mongo DB'
6
+ s.description = 'A pure-Ruby driver for the 10gen Mongo DB. For more information about Mongo, see http://www.mongodb.org.'
7
+
8
+ s.require_paths = ['lib']
9
+
10
+ s.files = ['bin/mongo_console', 'bin/validate',
11
+ 'examples/benchmarks.rb',
12
+ 'examples/blog.rb',
13
+ 'examples/index_test.rb',
14
+ 'examples/simple.rb',
15
+ 'lib/mongo.rb',
16
+ 'lib/mongo/admin.rb',
17
+ 'lib/mongo/collection.rb',
18
+ 'lib/mongo/cursor.rb',
19
+ 'lib/mongo/db.rb',
20
+ 'lib/mongo/message/get_more_message.rb',
21
+ 'lib/mongo/message/insert_message.rb',
22
+ 'lib/mongo/message/kill_cursors_message.rb',
23
+ 'lib/mongo/message/message.rb',
24
+ 'lib/mongo/message/message_header.rb',
25
+ 'lib/mongo/message/msg_message.rb',
26
+ 'lib/mongo/message/opcodes.rb',
27
+ 'lib/mongo/message/query_message.rb',
28
+ 'lib/mongo/message/remove_message.rb',
29
+ 'lib/mongo/message/update_message.rb',
30
+ 'lib/mongo/message.rb',
31
+ 'lib/mongo/mongo.rb',
32
+ 'lib/mongo/query.rb',
33
+ 'lib/mongo/types/binary.rb',
34
+ 'lib/mongo/types/dbref.rb',
35
+ 'lib/mongo/types/objectid.rb',
36
+ 'lib/mongo/types/regexp_of_holding.rb',
37
+ 'lib/mongo/types/undefined.rb',
38
+ 'lib/mongo/util/bson.rb',
39
+ 'lib/mongo/util/byte_buffer.rb',
40
+ 'lib/mongo/util/ordered_hash.rb',
41
+ 'lib/mongo/util/xml_to_ruby.rb',
42
+ 'README.rdoc', 'Rakefile', 'mongo-ruby-driver.gemspec']
43
+ s.test_files = ['tests/test_admin.rb',
44
+ 'tests/test_bson.rb',
45
+ 'tests/test_byte_buffer.rb',
46
+ 'tests/test_cursor.rb',
47
+ 'tests/test_db.rb',
48
+ 'tests/test_db_api.rb',
49
+ 'tests/test_db_connection.rb',
50
+ 'tests/test_message.rb',
51
+ 'tests/test_objectid.rb',
52
+ 'tests/test_ordered_hash.rb',
53
+ 'tests/test_round_trip.rb']
54
+
55
+ s.has_rdoc = true
56
+ s.rdoc_options = ['--main', 'README.rdoc', '--inline-source']
57
+ s.extra_rdoc_files = ['README.rdoc']
58
+
59
+ s.author = 'Jim Menard'
60
+ s.email = 'jim@10gen.com'
61
+ s.homepage = 'http://www.mongodb.org'
62
+ end
@@ -0,0 +1,60 @@
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 AdminTest < 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
+ # Insert some data to make sure the database itself exists.
15
+ @coll = @db.collection('test')
16
+ @coll.clear
17
+ @r1 = @coll.insert('a' => 1) # collection not created until it's used
18
+ @coll_full_name = 'ruby-mongo-test.test'
19
+ @admin = @db.admin
20
+ end
21
+
22
+ def teardown
23
+ if @db.connected?
24
+ @admin.profiling_level = :off
25
+ @coll.clear if @coll
26
+ @db.close
27
+ end
28
+ end
29
+
30
+ def test_default_profiling_level
31
+ assert_equal :off, @admin.profiling_level
32
+ end
33
+
34
+ def test_change_profiling_level
35
+ @admin.profiling_level = :slow_only
36
+ assert_equal :slow_only, @admin.profiling_level
37
+ @admin.profiling_level = :off
38
+ assert_equal :off, @admin.profiling_level
39
+ end
40
+
41
+ def test_profiling_info
42
+ # Perform at least one query while profiling so we have something to see.
43
+ @admin.profiling_level = :all
44
+ @coll.find()
45
+ @admin.profiling_level = :off
46
+
47
+ info = @admin.profiling_info
48
+ assert_kind_of Array, info
49
+ assert info.length >= 1
50
+ first = info.first
51
+ assert_kind_of String, first['info']
52
+ assert_kind_of Time, first['ts']
53
+ assert_kind_of Numeric, first['millis']
54
+ end
55
+
56
+ def test_validate_collection
57
+ assert @admin.validate_collection(@coll.name)
58
+ end
59
+
60
+ end
@@ -0,0 +1,135 @@
1
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'mongo'
3
+ require 'test/unit'
4
+
5
+ class BSONTest < Test::Unit::TestCase
6
+
7
+ include XGen::Mongo::Driver
8
+
9
+ def setup
10
+ # We don't pass a DB to the constructor, even though we are about to test
11
+ # deserialization. This means that when we deserialize, any DBRefs will
12
+ # have nil @db ivars. That's fine for now.
13
+ @b = BSON.new
14
+ end
15
+
16
+ def test_string
17
+ doc = {'doc' => 'hello, world'}
18
+ @b.serialize(doc)
19
+ assert_equal doc, @b.deserialize
20
+ end
21
+
22
+ def test_code
23
+ doc = {'$where' => 'this.a.b < this.b'}
24
+ @b.serialize(doc)
25
+ assert_equal doc, @b.deserialize
26
+ end
27
+
28
+ def test_number
29
+ doc = {'doc' => 41.99}
30
+ @b.serialize(doc)
31
+ assert_equal doc, @b.deserialize
32
+ end
33
+
34
+ def test_int
35
+ doc = {'doc' => 42}
36
+ @b.serialize(doc)
37
+ assert_equal doc, @b.deserialize
38
+ end
39
+
40
+ def test_object
41
+ doc = {'doc' => {'age' => 42, 'name' => 'Spongebob', 'shoe_size' => 9.5}}
42
+ @b.serialize(doc)
43
+ assert_equal doc, @b.deserialize
44
+ end
45
+
46
+ def test_oid
47
+ doc = {'doc' => ObjectID.new}
48
+ @b.serialize(doc)
49
+ assert_equal doc, @b.deserialize
50
+ end
51
+
52
+ def test_array
53
+ doc = {'doc' => [1, 2, 'a', 'b']}
54
+ @b.serialize(doc)
55
+ assert_equal doc, @b.deserialize
56
+ end
57
+
58
+ def test_regex
59
+ doc = {'doc' => /foobar/i}
60
+ @b.serialize(doc)
61
+ doc2 = @b.deserialize
62
+ assert_equal doc, doc2
63
+
64
+ r = doc2['doc']
65
+ assert_kind_of XGen::Mongo::Driver::RegexpOfHolding, r
66
+ assert_equal '', r.extra_options_str
67
+
68
+ r.extra_options_str << 'zywcab'
69
+ assert_equal 'zywcab', r.extra_options_str
70
+
71
+ b = BSON.new
72
+ doc = {'doc' => r}
73
+ b.serialize(doc)
74
+ doc2 = nil
75
+ doc2 = b.deserialize
76
+ assert_equal doc, doc2
77
+
78
+ r = doc2['doc']
79
+ assert_kind_of XGen::Mongo::Driver::RegexpOfHolding, r
80
+ assert_equal 'abcwyz', r.extra_options_str # must be sorted
81
+ end
82
+
83
+ def test_boolean
84
+ doc = {'doc' => true}
85
+ @b.serialize(doc)
86
+ assert_equal doc, @b.deserialize
87
+ end
88
+
89
+ def test_date
90
+ doc = {'date' => Time.now}
91
+ @b.serialize(doc)
92
+ doc2 = @b.deserialize
93
+ # Mongo only stores seconds, so comparing raw Time objects will fail
94
+ # because the fractional seconds will be different.
95
+ assert_equal doc['date'].to_i, doc2['date'].to_i
96
+ end
97
+
98
+ def test_null
99
+ end
100
+
101
+ def test_dbref
102
+ oid = ObjectID.new
103
+ doc = {}
104
+ doc['dbref'] = DBRef.new(doc, 'dbref', nil, 'namespace', oid)
105
+ @b.serialize(doc)
106
+ doc2 = @b.deserialize
107
+ assert_equal 'namespace', doc2['dbref'].namespace
108
+ assert_equal oid, doc2['dbref'].object_id
109
+ end
110
+
111
+ def test_symbol
112
+ doc = {'sym' => :foo}
113
+ @b.serialize(doc)
114
+ doc2 = @b.deserialize
115
+ assert_equal :foo, doc2['sym']
116
+ end
117
+
118
+ def test_binary
119
+ bin = 'binstring'.to_mongo_binary
120
+ assert_kind_of Binary, bin
121
+
122
+ doc = {'bin' => bin}
123
+ @b.serialize(doc)
124
+ doc2 = @b.deserialize
125
+ assert_equal 'binstring', doc2['bin']
126
+ end
127
+
128
+ def test_undefined
129
+ doc = {'undef' => Undefined.new}
130
+ @b.serialize(doc)
131
+ doc2 = @b.deserialize
132
+ assert_kind_of Undefined, doc2['undef']
133
+ end
134
+
135
+ end
@@ -0,0 +1,69 @@
1
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'mongo'
3
+ require 'test/unit'
4
+
5
+ class ByteBufferTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ @buf = ByteBuffer.new
9
+ end
10
+
11
+ def test_nil_get_returns_one_byte
12
+ @buf.put_array([1, 2, 3, 4])
13
+ @buf.rewind
14
+ assert_equal 1, @buf.get
15
+ end
16
+
17
+ def test_one_get_returns_array_length_one
18
+ @buf.put_array([1, 2, 3, 4])
19
+ @buf.rewind
20
+ assert_equal [1], @buf.get(1)
21
+ end
22
+
23
+ def test_zero_get_returns_empty_array
24
+ @buf.put_array([1, 2, 3, 4])
25
+ @buf.rewind
26
+ assert_equal [], @buf.get(0)
27
+ end
28
+
29
+ def test_empty
30
+ assert_equal 0, @buf.length
31
+ end
32
+
33
+ def test_length
34
+ @buf.put_int 3
35
+ assert_equal 4, @buf.length
36
+ end
37
+
38
+ def test_default_order
39
+ assert_equal :little_endian, @buf.order
40
+ end
41
+
42
+ def test_long_length
43
+ @buf.put_long 1027
44
+ assert_equal 8, @buf.length
45
+ end
46
+
47
+ def test_get_long
48
+ @buf.put_long 1027
49
+ @buf.rewind
50
+ assert_equal 1027, @buf.get_long
51
+ end
52
+
53
+ def test_get_double
54
+ @buf.put_double 41.2
55
+ @buf.rewind
56
+ assert_equal 41.2, @buf.get_double
57
+ end
58
+
59
+ def test_rewrite
60
+ @buf.put_int(0)
61
+ @buf.rewind
62
+ @buf.put_int(1027)
63
+ assert_equal 4, @buf.length
64
+ @buf.rewind
65
+ assert_equal 1027, @buf.get_int
66
+ assert_equal 4, @buf.position
67
+ end
68
+
69
+ end
@@ -0,0 +1,66 @@
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
+ if @db.connected?
22
+ @coll.clear if @coll
23
+ @db.close
24
+ end
25
+ end
26
+
27
+ def test_explain
28
+ cursor = @coll.find('a' => 1)
29
+ explaination = cursor.explain
30
+ assert_not_nil explaination['cursor']
31
+ assert_kind_of Numeric, explaination['n']
32
+ assert_kind_of Numeric, explaination['millis']
33
+ assert_kind_of Numeric, explaination['nscanned']
34
+ end
35
+
36
+ def test_close_no_query_sent
37
+ begin
38
+ cursor = @coll.find('a' => 1)
39
+ cursor.close
40
+ assert cursor.closed?
41
+ rescue => ex
42
+ fail ex.to_s
43
+ end
44
+ end
45
+
46
+ def test_close_after_query_sent
47
+ begin
48
+ cursor = @coll.find('a' => 1)
49
+ cursor.next_object
50
+ cursor.close
51
+ assert cursor.closed?
52
+ rescue => ex
53
+ fail ex.to_s
54
+ end
55
+ end
56
+
57
+ def test_hint
58
+ begin
59
+ cursor = @coll.find('a' => 1).hint('a')
60
+ assert_equal 1, cursor.to_a.size
61
+ rescue => ex
62
+ fail ex.to_s
63
+ end
64
+ end
65
+
66
+ end
data/tests/test_db.rb ADDED
@@ -0,0 +1,85 @@
1
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'mongo'
3
+ require 'test/unit'
4
+
5
+ class TestPKFactory
6
+ def create_pk(row)
7
+ row['_id'] ||= XGen::Mongo::Driver::ObjectID.new
8
+ row
9
+ end
10
+ end
11
+
12
+ # NOTE: assumes Mongo is running
13
+ class DBTest < Test::Unit::TestCase
14
+
15
+ include XGen::Mongo::Driver
16
+
17
+ def setup
18
+ @host = ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost'
19
+ @port = ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::DEFAULT_PORT
20
+ @db = Mongo.new(@host, @port).db('ruby-mongo-test')
21
+ end
22
+
23
+ def teardown
24
+ if @db.connected?
25
+ @db.close
26
+ end
27
+ end
28
+
29
+ def test_close
30
+ @db.close
31
+ assert !@db.connected?
32
+ begin
33
+ @db.collection('test').insert('a' => 1)
34
+ fail "expected 'NilClass' exception"
35
+ rescue => ex
36
+ assert_match /NilClass/, ex.to_s
37
+ end
38
+ end
39
+
40
+ def test_full_coll_name
41
+ coll = @db.collection('test')
42
+ assert_equal 'ruby-mongo-test.test', @db.full_coll_name(coll.name)
43
+ end
44
+
45
+ def test_master
46
+ # Doesn't really test anything since we probably only have one database
47
+ # during this test.
48
+ @db.switch_to_master
49
+ assert @db.connected?
50
+ end
51
+
52
+ def test_array
53
+ @db.close
54
+ @db = Mongo.new([["nosuch.example.com"], [@host, @port]]).db('ruby-mongo-test')
55
+ assert @db.connected?
56
+ end
57
+
58
+ def test_pk_factory
59
+ db = Mongo.new(@host, @port).db('ruby-mongo-test', :pk => TestPKFactory.new)
60
+ coll = db.collection('test')
61
+ coll.clear
62
+
63
+ obj = coll.insert('name' => 'Fred', 'age' => 42)
64
+ row = coll.find({'name' => 'Fred'}, :limit => 1).next_object
65
+ assert_equal obj, row
66
+
67
+ oid = XGen::Mongo::Driver::ObjectID.new
68
+ obj = coll.insert('_id' => oid, 'name' => 'Barney', 'age' => 41)
69
+ row = coll.find({'name' => 'Barney'}, :limit => 1).next_object
70
+ assert_equal obj, row
71
+
72
+ coll.clear
73
+ end
74
+
75
+ def test_pk_factory_reset
76
+ @db.pk_factory = Object.new # first time
77
+ begin
78
+ @db.pk_factory = Object.new
79
+ fail "error: expected exception"
80
+ rescue => ex
81
+ assert_match /can not change PK factory/, ex.to_s
82
+ end
83
+ end
84
+
85
+ end