mongo 0.18.3 → 0.19

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 (62) hide show
  1. data/README.rdoc +41 -50
  2. data/Rakefile +14 -4
  3. data/examples/gridfs.rb +25 -70
  4. data/lib/mongo.rb +4 -2
  5. data/lib/mongo/collection.rb +70 -89
  6. data/lib/mongo/connection.rb +203 -43
  7. data/lib/mongo/cursor.rb +7 -7
  8. data/lib/mongo/db.rb +61 -18
  9. data/lib/mongo/exceptions.rb +7 -1
  10. data/lib/mongo/gridfs.rb +8 -1
  11. data/lib/mongo/gridfs/chunk.rb +2 -1
  12. data/lib/mongo/gridfs/grid.rb +90 -0
  13. data/lib/mongo/gridfs/grid_file_system.rb +113 -0
  14. data/lib/mongo/gridfs/grid_io.rb +339 -0
  15. data/lib/mongo/gridfs/grid_store.rb +43 -18
  16. data/lib/mongo/types/binary.rb +5 -1
  17. data/lib/mongo/types/code.rb +1 -1
  18. data/lib/mongo/types/dbref.rb +3 -1
  19. data/lib/mongo/types/min_max_keys.rb +1 -1
  20. data/lib/mongo/types/objectid.rb +16 -55
  21. data/lib/mongo/types/regexp_of_holding.rb +1 -1
  22. data/lib/mongo/util/bson_c.rb +2 -2
  23. data/lib/mongo/util/bson_ruby.rb +22 -11
  24. data/lib/mongo/util/byte_buffer.rb +1 -1
  25. data/lib/mongo/util/conversions.rb +1 -1
  26. data/lib/mongo/util/ordered_hash.rb +6 -1
  27. data/lib/mongo/util/server_version.rb +1 -1
  28. data/lib/mongo/util/support.rb +1 -1
  29. data/mongo-ruby-driver.gemspec +1 -1
  30. data/test/auxillary/authentication_test.rb +68 -0
  31. data/test/auxillary/autoreconnect_test.rb +41 -0
  32. data/test/binary_test.rb +15 -0
  33. data/test/{test_bson.rb → bson_test.rb} +63 -6
  34. data/test/{test_byte_buffer.rb → byte_buffer_test.rb} +0 -0
  35. data/test/{test_chunk.rb → chunk_test.rb} +0 -0
  36. data/test/{test_collection.rb → collection_test.rb} +35 -39
  37. data/test/{test_connection.rb → connection_test.rb} +33 -3
  38. data/test/{test_conversions.rb → conversions_test.rb} +0 -0
  39. data/test/{test_cursor.rb → cursor_test.rb} +0 -7
  40. data/test/{test_db_api.rb → db_api_test.rb} +3 -6
  41. data/test/{test_db_connection.rb → db_connection_test.rb} +0 -0
  42. data/test/{test_db.rb → db_test.rb} +33 -15
  43. data/test/grid_file_system_test.rb +210 -0
  44. data/test/grid_io_test.rb +78 -0
  45. data/test/{test_grid_store.rb → grid_store_test.rb} +33 -2
  46. data/test/grid_test.rb +87 -0
  47. data/test/{test_objectid.rb → objectid_test.rb} +2 -33
  48. data/test/{test_ordered_hash.rb → ordered_hash_test.rb} +4 -0
  49. data/test/{test_slave_connection.rb → slave_connection_test.rb} +0 -0
  50. data/test/test_helper.rb +2 -2
  51. data/test/{test_threading.rb → threading_test.rb} +0 -0
  52. data/test/unit/collection_test.rb +12 -3
  53. data/test/unit/connection_test.rb +85 -24
  54. data/test/unit/cursor_test.rb +1 -2
  55. data/test/unit/db_test.rb +70 -69
  56. metadata +27 -23
  57. data/bin/objectid_benchmark.rb +0 -23
  58. data/bin/perf.rb +0 -30
  59. data/lib/mongo/admin.rb +0 -95
  60. data/lib/mongo/util/xml_to_ruby.rb +0 -112
  61. data/test/test_admin.rb +0 -67
  62. data/test/test_round_trip.rb +0 -114
@@ -539,22 +539,19 @@ class DBAPITest < Test::Unit::TestCase
539
539
  test = @@db.collection("test")
540
540
 
541
541
  assert_equal [], test.group([], {}, {"count" => 0}, "function (obj, prev) { prev.count++; }")
542
- assert_equal [], test.group([], {}, {"count" => 0}, "function (obj, prev) { prev.count++; }", true)
542
+ assert_equal [], test.group([], {}, {"count" => 0}, "function (obj, prev) { prev.count++; }")
543
543
 
544
544
  test.insert("a" => 2)
545
545
  test.insert("b" => 5)
546
546
  test.insert("a" => 1)
547
547
 
548
548
  assert_equal 3, test.group([], {}, {"count" => 0}, "function (obj, prev) { prev.count++; }")[0]["count"]
549
- assert_equal 3, test.group([], {}, {"count" => 0}, "function (obj, prev) { prev.count++; }", true)[0]["count"]
549
+ assert_equal 3, test.group([], {}, {"count" => 0}, "function (obj, prev) { prev.count++; }")[0]["count"]
550
+ assert_equal 1, test.group([], {"a" => {"$gt" => 1}}, {"count" => 0}, "function (obj, prev) { prev.count++; }")[0]["count"]
550
551
  assert_equal 1, test.group([], {"a" => {"$gt" => 1}}, {"count" => 0}, "function (obj, prev) { prev.count++; }")[0]["count"]
551
- assert_equal 1, test.group([], {"a" => {"$gt" => 1}}, {"count" => 0}, "function (obj, prev) { prev.count++; }", true)[0]["count"]
552
552
 
553
553
  finalize = "function (obj) { obj.f = obj.count - 1; }"
554
554
  assert_equal 2, test.group([], {}, {"count" => 0}, "function (obj, prev) { prev.count++; }", true, finalize)[0]["f"]
555
- assert_raise OperationFailure do
556
- test.group([], {}, {"count" => 0}, "function (obj, prev) { prev.count++; }", false, finalize)[0]["f"]
557
- end
558
555
 
559
556
  test.insert("a" => 2, "b" => 3)
560
557
  expected = [{"a" => 2, "count" => 2},
@@ -21,18 +21,6 @@ class DBTest < Test::Unit::TestCase
21
21
  @@db = @@conn.db('ruby-mongo-test')
22
22
  @@users = @@db.collection('system.users')
23
23
 
24
- def setup
25
- @spongebob = 'spongebob'
26
- @spongebob_password = 'squarepants'
27
- @@users.remove
28
- @@users.insert(:user => @spongebob, :pwd => @@db.send(:hash_password, @spongebob, @spongebob_password))
29
- end
30
-
31
- def teardown
32
- @@users.remove if @@users
33
- @@db.error
34
- end
35
-
36
24
  def test_close
37
25
  @@conn.close
38
26
  assert !@@conn.connected?
@@ -139,9 +127,25 @@ class DBTest < Test::Unit::TestCase
139
127
  end
140
128
 
141
129
  def test_authenticate
142
- assert !@@db.authenticate('nobody', 'nopassword')
143
- assert !@@db.authenticate(@spongebob, 'squareliederhosen')
144
- assert @@db.authenticate(@spongebob, @spongebob_password)
130
+ @@db.add_user('spongebob', 'squarepants')
131
+ assert_raise Mongo::AuthenticationError do
132
+ assert !@@db.authenticate('nobody', 'nopassword')
133
+ end
134
+ assert_raise Mongo::AuthenticationError do
135
+ assert !@@db.authenticate('spongebob' , 'squareliederhosen')
136
+ end
137
+ assert @@db.authenticate('spongebob', 'squarepants')
138
+ @@db.logout
139
+ @@db.remove_user('spongebob')
140
+ end
141
+
142
+ def test_authenticate_with_connection_uri
143
+ @@db.add_user('spongebob', 'squarepants')
144
+ assert Mongo::Connection.from_uri("mongodb://spongebob:squarepants@localhost/#{@@db.name}")
145
+
146
+ assert_raise Mongo::AuthenticationError do
147
+ Mongo::Connection.from_uri("mongodb://wrong:info@localhost/#{@@db.name}")
148
+ end
145
149
  end
146
150
 
147
151
  def test_logout
@@ -202,6 +206,20 @@ class DBTest < Test::Unit::TestCase
202
206
  assert db.collection('users').remove
203
207
  end
204
208
 
209
+ def test_user_management
210
+ @@db.add_user("bob", "secret")
211
+ assert @@db.authenticate("bob", "secret")
212
+ @@db.logout
213
+ assert @@db.remove_user("bob")
214
+ assert_raise Mongo::AuthenticationError do
215
+ @@db.authenticate("bob", "secret")
216
+ end
217
+ end
218
+
219
+ def test_remove_non_existant_user
220
+ assert !@@db.remove_user("joe")
221
+ end
222
+
205
223
  context "database profiling" do
206
224
  setup do
207
225
  @db = @@conn['ruby-mongo-test-admin-functions']
@@ -0,0 +1,210 @@
1
+ require 'test/test_helper'
2
+ include Mongo
3
+
4
+ class GridFileSystemTest < Test::Unit::TestCase
5
+ context "GridFileSystem:" do
6
+ setup do
7
+ @con = Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
8
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT)
9
+ @db = @con.db('mongo-ruby-test')
10
+ end
11
+
12
+ teardown do
13
+ @db['fs.files'].remove
14
+ @db['fs.chunks'].remove
15
+ end
16
+
17
+ context "When reading:" do
18
+ setup do
19
+ @chunks_data = "CHUNKS" * 50000
20
+ @grid = GridFileSystem.new(@db)
21
+ @grid.open('sample.file', 'w') do |f|
22
+ f.write @chunks_data
23
+ end
24
+
25
+ @grid = GridFileSystem.new(@db)
26
+ end
27
+
28
+ should "read sample data" do
29
+ data = @grid.open('sample.file', 'r') { |f| f.read }
30
+ assert_equal data.length, @chunks_data.length
31
+ end
32
+
33
+ should "return an empty string if length is zero" do
34
+ data = @grid.open('sample.file', 'r') { |f| f.read(0) }
35
+ assert_equal '', data
36
+ end
37
+
38
+ should "return the first n bytes" do
39
+ data = @grid.open('sample.file', 'r') {|f| f.read(288888) }
40
+ assert_equal 288888, data.length
41
+ assert_equal @chunks_data[0...288888], data
42
+ end
43
+
44
+ should "return the first n bytes even with an offset" do
45
+ data = @grid.open('sample.file', 'r') do |f|
46
+ f.seek(1000)
47
+ f.read(288888)
48
+ end
49
+ assert_equal 288888, data.length
50
+ assert_equal @chunks_data[1000...289888], data
51
+ end
52
+ end
53
+
54
+ context "When writing:" do
55
+ setup do
56
+ @data = "BYTES" * 50
57
+ @grid = GridFileSystem.new(@db)
58
+ @grid.open('sample', 'w') do |f|
59
+ f.write @data
60
+ end
61
+ end
62
+
63
+ should "read sample data" do
64
+ data = @grid.open('sample', 'r') { |f| f.read }
65
+ assert_equal data.length, @data.length
66
+ end
67
+
68
+ should "return the total number of bytes written" do
69
+ data = 'a' * 300000
70
+ assert_equal 300000, @grid.open('sample', 'w') {|f| f.write(data) }
71
+ end
72
+
73
+ should "more read sample data" do
74
+ data = @grid.open('sample', 'r') { |f| f.read }
75
+ assert_equal data.length, @data.length
76
+ end
77
+
78
+ should "raise exception if not opened for write" do
79
+ assert_raise GridError do
80
+ @grid.open('io', 'r') { |f| f.write('hello') }
81
+ end
82
+ end
83
+
84
+ context "and when overwriting the file" do
85
+ setup do
86
+ @old = @grid.open('sample', 'r')
87
+
88
+ @new_data = "DATA" * 10
89
+ sleep(2)
90
+ @grid.open('sample', 'w') do |f|
91
+ f.write @new_data
92
+ end
93
+
94
+ @new = @grid.open('sample', 'r')
95
+ end
96
+
97
+ should "have a newer upload date" do
98
+ assert @new.upload_date > @old.upload_date, "New data is not greater than old date."
99
+ end
100
+
101
+ should "have a different files_id" do
102
+ assert_not_equal @new.files_id, @old.files_id
103
+ end
104
+
105
+ should "contain the new data" do
106
+ assert_equal @new_data, @new.read, "Expected DATA"
107
+ end
108
+
109
+ context "and on a second overwrite" do
110
+ setup do
111
+ sleep(2)
112
+ new_data = "NEW" * 1000
113
+ @grid.open('sample', 'w') do |f|
114
+ f.write new_data
115
+ end
116
+
117
+ @ids = @db['fs.files'].find({'filename' => 'sample'}).map {|file| file['_id']}
118
+ end
119
+
120
+ should "write a third version of the file" do
121
+ assert_equal 3, @db['fs.files'].find({'filename' => 'sample'}).count
122
+ assert_equal 3, @db['fs.chunks'].find({'files_id' => {'$in' => @ids}}).count
123
+ end
124
+
125
+ should "remove all versions and their data on delete" do
126
+ @grid.delete('sample')
127
+ assert_equal 0, @db['fs.files'].find({'filename' => 'sample'}).count
128
+ assert_equal 0, @db['fs.chunks'].find({'files_id' => {'$in' => @ids}}).count
129
+ end
130
+ end
131
+ end
132
+ end
133
+
134
+ context "When writing chunks:" do
135
+ setup do
136
+ data = "B" * 50000
137
+ @grid = GridFileSystem.new(@db)
138
+ @grid.open('sample', 'w', :chunk_size => 1000) do |f|
139
+ f.write data
140
+ end
141
+ end
142
+
143
+ should "write the correct number of chunks" do
144
+ file = @db['fs.files'].find_one({:filename => 'sample'})
145
+ chunks = @db['fs.chunks'].find({'files_id' => file['_id']}).to_a
146
+ assert_equal 50, chunks.length
147
+ end
148
+ end
149
+
150
+ context "Positioning:" do
151
+ setup do
152
+ data = 'hello, world' + '1' * 5000 + 'goodbye!' + '2' * 1000 + '!'
153
+ @grid = GridFileSystem.new(@db)
154
+ @grid.open('hello', 'w', :chunk_size => 1000) do |f|
155
+ f.write data
156
+ end
157
+ end
158
+
159
+ should "seek within chunks" do
160
+ @grid.open('hello', 'r') do |f|
161
+ f.seek(0)
162
+ assert_equal 'h', f.read(1)
163
+ f.seek(7)
164
+ assert_equal 'w', f.read(1)
165
+ f.seek(4)
166
+ assert_equal 'o', f.read(1)
167
+ f.seek(0)
168
+ f.seek(7, IO::SEEK_CUR)
169
+ assert_equal 'w', f.read(1)
170
+ f.seek(-2, IO::SEEK_CUR)
171
+ assert_equal ' ', f.read(1)
172
+ f.seek(-4, IO::SEEK_CUR)
173
+ assert_equal 'l', f.read(1)
174
+ f.seek(3, IO::SEEK_CUR)
175
+ assert_equal 'w', f.read(1)
176
+ end
177
+ end
178
+
179
+ should "seek between chunks" do
180
+ @grid.open('hello', 'r') do |f|
181
+ f.seek(1000)
182
+ assert_equal '11111', f.read(5)
183
+
184
+ f.seek(5009)
185
+ assert_equal '111goodbye!222', f.read(14)
186
+
187
+ f.seek(-1, IO::SEEK_END)
188
+ assert_equal '!', f.read(1)
189
+ f.seek(-6, IO::SEEK_END)
190
+ assert_equal '2', f.read(1)
191
+ end
192
+ end
193
+
194
+ should "tell the current position" do
195
+ @grid.open('hello', 'r') do |f|
196
+ assert_equal 0, f.tell
197
+
198
+ f.seek(999)
199
+ assert_equal 999, f.tell
200
+ end
201
+ end
202
+
203
+ should "seek only in read mode" do
204
+ assert_raise GridError do
205
+ @grid.open('hello', 'w') {|f| f.seek(0) }
206
+ end
207
+ end
208
+ end
209
+ end
210
+ end
@@ -0,0 +1,78 @@
1
+ require 'test/test_helper'
2
+ include Mongo
3
+
4
+ class GridIOTest < Test::Unit::TestCase
5
+
6
+ context "GridIO" do
7
+ setup do
8
+ @db ||= Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
9
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db('ruby-mongo-test')
10
+ @files = @db.collection('fs.files')
11
+ @chunks = @db.collection('fs.chunks')
12
+ @chunks.create_index([['files_id', Mongo::ASCENDING], ['n', Mongo::ASCENDING]])
13
+ end
14
+
15
+ teardown do
16
+ @files.remove
17
+ @chunks.remove
18
+ end
19
+
20
+ context "Options" do
21
+ setup do
22
+ @filename = 'test'
23
+ @mode = 'w'
24
+ end
25
+
26
+ should "set default 256k chunk size" do
27
+ file = GridIO.new(@files, @chunks, @filename, @mode)
28
+ assert_equal 256 * 1024, file.chunk_size
29
+ end
30
+
31
+ should "set chunk size" do
32
+ file = GridIO.new(@files, @chunks, @filename, @mode, :chunk_size => 1000)
33
+ assert_equal 1000, file.chunk_size
34
+ end
35
+ end
36
+
37
+ context "Grid MD5 check" do
38
+ should "run in safe mode" do
39
+ file = GridIO.new(@files, @chunks, 'smallfile', 'w', :safe => true)
40
+ file.write("DATA" * 100)
41
+ assert file.close
42
+ assert_equal file.server_md5, file.client_md5
43
+ end
44
+
45
+ should "validate with a large file" do
46
+ io = File.open(File.join(File.dirname(__FILE__), 'data', 'sample_file.pdf'), 'r')
47
+ file = GridIO.new(@files, @chunks, 'bigfile', 'w', :safe => true)
48
+ file.write(io)
49
+ assert file.close
50
+ assert_equal file.server_md5, file.client_md5
51
+ end
52
+ end
53
+
54
+ context "Content types" do
55
+
56
+ if defined?(MIME)
57
+ should "determine common content types from the extension" do
58
+ file = GridIO.new(@files, @chunks, 'sample.pdf', 'w')
59
+ assert_equal 'application/pdf', file.content_type
60
+
61
+ file = GridIO.new(@files, @chunks, 'sample.txt', 'w')
62
+ assert_equal 'text/plain', file.content_type
63
+ end
64
+ end
65
+
66
+ should "default to binary/octet-stream when type is unknown" do
67
+ file = GridIO.new(@files, @chunks, 'sample.l33t', 'w')
68
+ assert_equal 'binary/octet-stream', file.content_type
69
+ end
70
+
71
+ should "use any provided content type by default" do
72
+ file = GridIO.new(@files, @chunks, 'sample.l33t', 'w', :content_type => 'image/jpg')
73
+ assert_equal 'image/jpg', file.content_type
74
+ end
75
+ end
76
+ end
77
+
78
+ end
@@ -130,7 +130,22 @@ class GridStoreTest < Test::Unit::TestCase
130
130
  }
131
131
 
132
132
  assert_equal 3, @@chunks.count
133
- #assert_equal ('x' * size) + ('y' * size) + ('z' * size), GridStore.read(@@db, 'biggie')
133
+ end
134
+
135
+ def test_binary
136
+ file = File.open(File.join(File.dirname(__FILE__), 'data', 'data.tar.gz'), 'r')
137
+ GridStore.open(@@db, 'zip', 'w') do |f|
138
+ f.write(file.read)
139
+ end
140
+
141
+ file.rewind
142
+ data = file.read
143
+ if data.respond_to?(:force_encoding)
144
+ data.force_encoding(:binary)
145
+ end
146
+ GridStore.open(@@db, 'zip', 'r') do |f|
147
+ assert_equal data.length, f.read.length
148
+ end
134
149
  end
135
150
 
136
151
  def test_puts_and_readlines
@@ -151,7 +166,23 @@ class GridStoreTest < Test::Unit::TestCase
151
166
  assert_equal 0, @@files.count
152
167
  assert_equal 0, @@chunks.count
153
168
  end
154
-
169
+
170
+ def test_unlink_alternate_root_collection
171
+ GridStore.default_root_collection = 'gridfs'
172
+ GridStore.open(@@db, 'foobar', 'w') do |f|
173
+ f.puts "Hello"
174
+ end
175
+ assert GridStore.exist?(@@db, 'foobar')
176
+
177
+ GridStore.default_root_collection = 'fs'
178
+ GridStore.unlink(@@db, 'foobar')
179
+ assert !GridStore.exist?(@@db, 'foobar')
180
+
181
+ GridStore.default_root_collection = 'gridfs'
182
+ GridStore.unlink(@@db, 'foobar')
183
+ assert !GridStore.exist?(@@db, 'foobar')
184
+ end
185
+
155
186
  def test_mv
156
187
  assert_equal 1, @@files.count
157
188
  assert_equal 1, @@chunks.count
data/test/grid_test.rb ADDED
@@ -0,0 +1,87 @@
1
+ require 'test/test_helper'
2
+ include Mongo
3
+
4
+ class GridTest < Test::Unit::TestCase
5
+ context "Tests:" do
6
+ setup do
7
+ @db ||= Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
8
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db('ruby-mongo-test')
9
+ @files = @db.collection('test-fs.files')
10
+ @chunks = @db.collection('test-fs.chunks')
11
+ end
12
+
13
+ teardown do
14
+ @files.remove
15
+ @chunks.remove
16
+ end
17
+
18
+ context "A basic grid-stored file" do
19
+ setup do
20
+ @data = "GRIDDATA" * 50000
21
+ @grid = Grid.new(@db, 'test-fs')
22
+ @id = @grid.put(@data, 'sample', :metadata => {'app' => 'photos'})
23
+ end
24
+
25
+ should "retrieve the stored data" do
26
+ data = @grid.get(@id).data
27
+ assert_equal @data, data
28
+ end
29
+
30
+ should "store the filename" do
31
+ file = @grid.get(@id)
32
+ assert_equal 'sample', file.filename
33
+ end
34
+
35
+ should "store any relevant metadata" do
36
+ file = @grid.get(@id)
37
+ assert_equal 'photos', file.metadata['app']
38
+ end
39
+
40
+ should "delete the file and any chunks" do
41
+ @grid.delete(@id)
42
+ assert_raise GridError do
43
+ @grid.get(@id)
44
+ end
45
+ end
46
+ end
47
+
48
+ context "Streaming: " do || {}
49
+ setup do
50
+ def read_and_write_stream(filename, read_length, opts={})
51
+ io = File.open(File.join(File.dirname(__FILE__), 'data', filename), 'r')
52
+ id = @grid.put(io, filename + read_length.to_s, opts)
53
+ file = @grid.get(id)
54
+ io.rewind
55
+ data = io.read
56
+ if data.respond_to?(:force_encoding)
57
+ data.force_encoding(:binary)
58
+ end
59
+ read_data = ""
60
+ while(chunk = file.read(read_length))
61
+ read_data << chunk
62
+ end
63
+ assert_equal data.length, read_data.length
64
+ assert_equal data, read_data, "Unequal!"
65
+ end
66
+
67
+ @grid = Grid.new(@db, 'test-fs')
68
+ end
69
+
70
+ should "put and get a small io object with a small chunk size" do
71
+ read_and_write_stream('small_data.txt', 1, :chunk_size => 2)
72
+ end
73
+
74
+ should "put and get a small io object" do
75
+ read_and_write_stream('small_data.txt', 1)
76
+ end
77
+
78
+ should "put and get a large io object when reading smaller than the chunk size" do
79
+ read_and_write_stream('sample_file.pdf', 256 * 1024)
80
+ end
81
+
82
+ should "put and get a large io object when reading larger than the chunk size" do
83
+ read_and_write_stream('sample_file.pdf', 300 * 1024)
84
+ end
85
+ end
86
+ end
87
+ end