mongo 0.18.3 → 0.19

Sign up to get free protection for your applications and to get access to all the features.
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