mongo 0.1.0 → 0.15

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 (89) hide show
  1. data/README.rdoc +268 -71
  2. data/Rakefile +27 -62
  3. data/bin/bson_benchmark.rb +59 -0
  4. data/bin/mongo_console +3 -3
  5. data/bin/run_test_script +19 -0
  6. data/bin/standard_benchmark +109 -0
  7. data/examples/admin.rb +41 -0
  8. data/examples/benchmarks.rb +42 -0
  9. data/examples/blog.rb +76 -0
  10. data/examples/capped.rb +23 -0
  11. data/examples/cursor.rb +47 -0
  12. data/examples/gridfs.rb +87 -0
  13. data/examples/index_test.rb +125 -0
  14. data/examples/info.rb +30 -0
  15. data/examples/queries.rb +69 -0
  16. data/examples/simple.rb +23 -0
  17. data/examples/strict.rb +34 -0
  18. data/examples/types.rb +35 -0
  19. data/lib/mongo.rb +9 -2
  20. data/lib/mongo/admin.rb +65 -68
  21. data/lib/mongo/collection.rb +379 -117
  22. data/lib/mongo/connection.rb +151 -0
  23. data/lib/mongo/cursor.rb +271 -216
  24. data/lib/mongo/db.rb +500 -315
  25. data/lib/mongo/errors.rb +26 -0
  26. data/lib/mongo/gridfs.rb +16 -0
  27. data/lib/mongo/gridfs/chunk.rb +92 -0
  28. data/lib/mongo/gridfs/grid_store.rb +464 -0
  29. data/lib/mongo/message.rb +16 -0
  30. data/lib/mongo/message/get_more_message.rb +24 -13
  31. data/lib/mongo/message/insert_message.rb +29 -11
  32. data/lib/mongo/message/kill_cursors_message.rb +23 -12
  33. data/lib/mongo/message/message.rb +74 -62
  34. data/lib/mongo/message/message_header.rb +35 -24
  35. data/lib/mongo/message/msg_message.rb +21 -9
  36. data/lib/mongo/message/opcodes.rb +26 -15
  37. data/lib/mongo/message/query_message.rb +63 -43
  38. data/lib/mongo/message/remove_message.rb +29 -12
  39. data/lib/mongo/message/update_message.rb +30 -13
  40. data/lib/mongo/query.rb +97 -89
  41. data/lib/mongo/types/binary.rb +25 -21
  42. data/lib/mongo/types/code.rb +30 -0
  43. data/lib/mongo/types/dbref.rb +19 -23
  44. data/lib/mongo/types/objectid.rb +130 -116
  45. data/lib/mongo/types/regexp_of_holding.rb +27 -31
  46. data/lib/mongo/util/bson.rb +273 -160
  47. data/lib/mongo/util/byte_buffer.rb +32 -28
  48. data/lib/mongo/util/ordered_hash.rb +88 -42
  49. data/lib/mongo/util/xml_to_ruby.rb +18 -15
  50. data/mongo-ruby-driver.gemspec +103 -0
  51. data/test/mongo-qa/_common.rb +8 -0
  52. data/test/mongo-qa/admin +26 -0
  53. data/test/mongo-qa/capped +22 -0
  54. data/test/mongo-qa/count1 +18 -0
  55. data/test/mongo-qa/dbs +22 -0
  56. data/test/mongo-qa/find +10 -0
  57. data/test/mongo-qa/find1 +15 -0
  58. data/test/mongo-qa/gridfs_in +16 -0
  59. data/test/mongo-qa/gridfs_out +17 -0
  60. data/test/mongo-qa/indices +49 -0
  61. data/test/mongo-qa/remove +25 -0
  62. data/test/mongo-qa/stress1 +35 -0
  63. data/test/mongo-qa/test1 +11 -0
  64. data/test/mongo-qa/update +18 -0
  65. data/{tests → test}/test_admin.rb +25 -16
  66. data/test/test_bson.rb +268 -0
  67. data/{tests → test}/test_byte_buffer.rb +0 -0
  68. data/test/test_chunk.rb +84 -0
  69. data/test/test_collection.rb +282 -0
  70. data/test/test_connection.rb +101 -0
  71. data/test/test_cursor.rb +321 -0
  72. data/test/test_db.rb +196 -0
  73. data/test/test_db_api.rb +798 -0
  74. data/{tests → test}/test_db_connection.rb +4 -3
  75. data/test/test_grid_store.rb +284 -0
  76. data/{tests → test}/test_message.rb +1 -1
  77. data/test/test_objectid.rb +105 -0
  78. data/{tests → test}/test_ordered_hash.rb +55 -0
  79. data/{tests → test}/test_round_trip.rb +13 -9
  80. data/test/test_threading.rb +37 -0
  81. metadata +74 -32
  82. data/bin/validate +0 -51
  83. data/lib/mongo/mongo.rb +0 -74
  84. data/lib/mongo/types/undefined.rb +0 -31
  85. data/tests/test_bson.rb +0 -135
  86. data/tests/test_cursor.rb +0 -66
  87. data/tests/test_db.rb +0 -51
  88. data/tests/test_db_api.rb +0 -349
  89. data/tests/test_objectid.rb +0 -88
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
5
+
6
+ if $DEBUG
7
+ db.drop_collection('capped1')
8
+ db.drop_collection('capped2')
9
+ end
10
+
11
+ db.create_collection('capped1', :capped => true, :size => 500)
12
+ coll = db.collection('capped1')
13
+ coll.insert('x' => 1)
14
+ coll.insert('x' => 2)
15
+
16
+ db.create_collection('capped2', :capped => true, :size => 1000, :max => 11)
17
+ coll = db.collection('capped2')
18
+ str = ''
19
+ 100.times {
20
+ coll.insert('dashes' => str)
21
+ str << '-'
22
+ }
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
5
+
6
+ if $DEBUG
7
+ 3.times { |i| db.drop_collection("test#{i+1}") }
8
+ db.create_collection('test1')
9
+ db.collection('test2').insert({:name => 'a'})
10
+ c = db.collection('test3')
11
+ 100.times { |i| c.insert(:i => i, :foo => 'bar') }
12
+ end
13
+
14
+ puts db.collection('test1').count
15
+ puts db.collection('test2').count
16
+ puts db.collection('test3').find('i' => 'a').count
17
+ puts db.collection('test3').find('i' => 3).count
18
+ puts db.collection('test3').find({'i' => {'$gte' => 67}}).count
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
5
+
6
+ if $DEBUG
7
+ 3.times { |i| db.drop_collection("dbs_#{i+1}") }
8
+ end
9
+
10
+ def print_dbs_names(db)
11
+ db.collection_names.select{ |n| n =~ /\.dbs/ }.sort.each { |name|
12
+ puts name.sub(/^#{db.name}\./, '')
13
+ }
14
+ end
15
+
16
+ db.collection('dbs_1').insert('foo' => 'bar')
17
+ db.collection('dbs_2').insert('psi' => 'phi')
18
+ puts db.name
19
+ print_dbs_names(db)
20
+ db.drop_collection('dbs_1')
21
+ db.create_collection('dbs_3')
22
+ print_dbs_names(db)
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
5
+
6
+ if $DEBUG
7
+ db.drop_collection('test')
8
+ end
9
+
10
+ db.collection('test').insert('a' => 2)
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
5
+
6
+ if $DEBUG
7
+ db.drop_collection('c')
8
+ c = db.collection('c')
9
+ (5..15).each { |i| c.insert(:x => 0, :y => i, :z => (i+64).chr) }
10
+ (1..50).each { |i| c.insert(:x => 1, :y => i, :z => (i+64).chr) }
11
+ (5..15).each { |i| c.insert(:x => 2, :y => i, :z => (i+64).chr) }
12
+ end
13
+
14
+ cursor = db.collection('c').find({'x' => 1}, :sort => 'y', :skip => 20, :limit => 10)
15
+ cursor.each { |row| puts row['z'] }
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+
5
+ require 'mongo/gridfs'
6
+ include GridFS
7
+
8
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
9
+
10
+ input_file = ARGV[0]
11
+
12
+ File.open(input_file, "r") { |f|
13
+ GridStore.open(db, input_file, "w") { |g|
14
+ g.write(f.read)
15
+ }
16
+ }
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+
5
+ require 'mongo/gridfs'
6
+ include GridFS
7
+
8
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
9
+
10
+ input_file = ARGV[0]
11
+ output_file = ARGV[1]
12
+
13
+ File.open(output_file, "w") { |f|
14
+ GridStore.open(db, input_file, "r") { |g|
15
+ f.write(g.read)
16
+ }
17
+ }
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+
5
+ include Mongo
6
+
7
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
8
+ x = db.collection('x')
9
+ y = db.collection('y')
10
+
11
+ def sorted_index_info(c)
12
+ c.index_information.sort { |a,b| a[:name] <=> b[:name] }
13
+ end
14
+
15
+ def sorted_info_keys(info)
16
+ info[:keys].keys.sort.collect { |key| "#{key}_1" }.join("_")
17
+ end
18
+
19
+ def check_keys(c, expected)
20
+ keys = sorted_index_info(c).collect {|info| sorted_info_keys(info)}
21
+ if keys == expected
22
+ ''
23
+ else
24
+ "#{c.name} indices should start out #{expected.inspect} but is #{keys.inspect}"
25
+ end
26
+ end
27
+
28
+ if $DEBUG
29
+ x.drop # also drops indexes
30
+ x.insert('field1' => 'f1', 'field2' => 'f2')
31
+ x.create_index('field1_1', 'field1')
32
+ x.create_index('field2_1', 'field2')
33
+ y.drop
34
+ end
35
+
36
+ # There should only be two indices on x, and none on y. We raise an error if
37
+ # the preconditions are not met. (They were not, on our testing harness, for a
38
+ # while due to Mongo behavior.)
39
+ err = []
40
+ err << check_keys(x, ['field1_1', 'field2_1'])
41
+ err << check_keys(y, [])
42
+ raise "\n#{err.join("\n")}" unless err == ['', '']
43
+
44
+ x.drop_index('field1_1')
45
+ sorted_index_info(x).each { |info| puts sorted_info_keys(info) }
46
+
47
+ y.create_index([['a', ASCENDING], ['b', ASCENDING], ['c', ASCENDING]])
48
+ y.create_index('d')
49
+ sorted_index_info(y).each { |info| puts sorted_info_keys(info) }
@@ -0,0 +1,25 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
5
+
6
+ if $DEBUG
7
+ c = db.collection('remove1')
8
+ c.clear
9
+ 50.times { |i| c.insert(:a => i) }
10
+ c = db.collection('remove2')
11
+ c.clear
12
+ c.insert(:a => 3, :b => 1)
13
+ c.insert(:a => 3, :b => 3)
14
+ c.insert(:a => 2, :b => 3)
15
+ c.insert(:b => 3)
16
+ end
17
+
18
+ db.collection('remove1').clear
19
+ db.collection('remove2').remove('a' => 3)
20
+
21
+ if $DEBUG
22
+ puts "remove1 count = #{db.collection('remove1').count}"
23
+ puts "remove2 count = #{db.collection('remove2').count}"
24
+ db.collection('remove2').find.each { |row| puts row.inspect }
25
+ end
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ LONG_STRING = "lksjhasoh1298alshasoidiohaskjasiouashoasasiugoas" * 6
4
+
5
+ require File.join(File.dirname(__FILE__), '_common.rb')
6
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
7
+ c = db.collection('stress1')
8
+
9
+ n1 = 50_000
10
+ n2 = 10_000
11
+
12
+ if $DEBUG
13
+ n1 = 5
14
+ n2 = 1
15
+ c.drop
16
+ end
17
+
18
+ n1.times { |i|
19
+ c.insert(:name => "asdf#{i}", :date => Time.now, :id => i,
20
+ :blah => LONG_STRING, :subarray => [])
21
+ }
22
+ puts
23
+
24
+ n2.times { |i|
25
+ x = c.find_one({:id => i})
26
+ x['subarray'] = "foo#{i}"
27
+ p x
28
+ c.modify({:id => i}, x)
29
+ }
30
+ puts
31
+
32
+ if $DEBUG
33
+ puts "stress1 has #{c.count} records"
34
+ c.find.each { |row| puts "#{row['id']}: #{row['subarray'].inspect}" }
35
+ end
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
5
+ coll = db.collection('part1')
6
+
7
+ if $DEBUG
8
+ coll.drop
9
+ end
10
+
11
+ 100.times { |i| coll.insert('x' => i) }
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.join(File.dirname(__FILE__), '_common.rb')
4
+ db = Connection.new(DEFAULT_HOST, DEFAULT_PORT).db(DEFAULT_DB)
5
+ foo = db.collection('foo')
6
+
7
+ if $DEBUG
8
+ foo.drop
9
+ foo.insert(:x => 1)
10
+ end
11
+
12
+ foo.modify({:x => 1}, {:x => 1, :y => 2})
13
+ foo.modify({:x => 2}, {:x => 1, :y => 7})
14
+ foo.repsert({:x => 3}, {:x => 4, :y => 1})
15
+
16
+ if $DEBUG
17
+ foo.find.each { |row| puts row.inspect }
18
+ end
@@ -5,26 +5,24 @@ require 'test/unit'
5
5
  # NOTE: assumes Mongo is running
6
6
  class AdminTest < Test::Unit::TestCase
7
7
 
8
- include XGen::Mongo::Driver
8
+ include Mongo
9
+
10
+ @@db = Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
11
+ ENV['MONGO_RUBY_DRIVER_PORT'] || Connection::DEFAULT_PORT).db('ruby-mongo-test')
12
+ @@coll = @@db.collection('test')
9
13
 
10
14
  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
15
  # 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
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
20
  end
21
21
 
22
22
  def teardown
23
- if @db.connected?
24
- @admin.profiling_level = :off
25
- @coll.clear if @coll
26
- @db.close
27
- end
23
+ @admin.profiling_level = :off
24
+ @@coll.clear if @@coll
25
+ @@db.error
28
26
  end
29
27
 
30
28
  def test_default_profiling_level
@@ -36,12 +34,19 @@ class AdminTest < Test::Unit::TestCase
36
34
  assert_equal :slow_only, @admin.profiling_level
37
35
  @admin.profiling_level = :off
38
36
  assert_equal :off, @admin.profiling_level
37
+ @admin.profiling_level = :all
38
+ assert_equal :all, @admin.profiling_level
39
+ begin
40
+ @admin.profiling_level = :medium
41
+ fail "shouldn't be able to do this"
42
+ rescue
43
+ end
39
44
  end
40
45
 
41
46
  def test_profiling_info
42
47
  # Perform at least one query while profiling so we have something to see.
43
48
  @admin.profiling_level = :all
44
- @coll.find()
49
+ @@coll.find()
45
50
  @admin.profiling_level = :off
46
51
 
47
52
  info = @admin.profiling_info
@@ -54,7 +59,11 @@ class AdminTest < Test::Unit::TestCase
54
59
  end
55
60
 
56
61
  def test_validate_collection
57
- assert @admin.validate_collection(@coll.name)
62
+ doc = @admin.validate_collection(@@coll.name)
63
+ assert_not_nil doc
64
+ result = doc['result']
65
+ assert_not_nil result
66
+ assert_match /firstExtent/, result
58
67
  end
59
68
 
60
69
  end
@@ -0,0 +1,268 @@
1
+ $LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib')
2
+ require 'mongo'
3
+ require 'mongo/util/ordered_hash'
4
+ require 'test/unit'
5
+
6
+ class BSONTest < Test::Unit::TestCase
7
+
8
+ include Mongo
9
+
10
+ def setup
11
+ # We don't pass a DB to the constructor, even though we are about to test
12
+ # deserialization. This means that when we deserialize, any DBRefs will
13
+ # have nil @db ivars. That's fine for now.
14
+ @b = BSON.new
15
+ end
16
+
17
+ def test_string
18
+ doc = {'doc' => 'hello, world'}
19
+ @b.serialize(doc)
20
+ assert_equal doc, @b.deserialize
21
+ end
22
+
23
+ def test_code
24
+ doc = {'$where' => Code.new('this.a.b < this.b')}
25
+ @b.serialize(doc)
26
+ assert_equal doc, @b.deserialize
27
+ end
28
+
29
+ def test_number
30
+ doc = {'doc' => 41.99}
31
+ @b.serialize(doc)
32
+ assert_equal doc, @b.deserialize
33
+ end
34
+
35
+ def test_int
36
+ doc = {'doc' => 42}
37
+ @b.serialize(doc)
38
+ assert_equal doc, @b.deserialize
39
+
40
+ doc = {"doc" => -5600}
41
+ @b.serialize(doc)
42
+ assert_equal doc, @b.deserialize
43
+
44
+ doc = {"doc" => 2147483647}
45
+ @b.serialize(doc)
46
+ assert_equal doc, @b.deserialize
47
+
48
+ doc = {"doc" => -2147483648}
49
+ @b.serialize(doc)
50
+ assert_equal doc, @b.deserialize
51
+ end
52
+
53
+ def test_ordered_hash
54
+ doc = OrderedHash.new
55
+ doc["b"] = 1
56
+ doc["a"] = 2
57
+ doc["c"] = 3
58
+ doc["d"] = 4
59
+ @b.serialize(doc)
60
+ assert_equal doc, @b.deserialize
61
+ end
62
+
63
+ def test_object
64
+ doc = {'doc' => {'age' => 42, 'name' => 'Spongebob', 'shoe_size' => 9.5}}
65
+ @b.serialize(doc)
66
+ assert_equal doc, @b.deserialize
67
+ end
68
+
69
+ def test_oid
70
+ doc = {'doc' => ObjectID.new}
71
+ @b.serialize(doc)
72
+ assert_equal doc, @b.deserialize
73
+ end
74
+
75
+ def test_array
76
+ doc = {'doc' => [1, 2, 'a', 'b']}
77
+ @b.serialize(doc)
78
+ assert_equal doc, @b.deserialize
79
+ end
80
+
81
+ def test_regex
82
+ doc = {'doc' => /foobar/i}
83
+ @b.serialize(doc)
84
+ doc2 = @b.deserialize
85
+ assert_equal doc, doc2
86
+
87
+ r = doc2['doc']
88
+ assert_kind_of RegexpOfHolding, r
89
+ assert_equal '', r.extra_options_str
90
+
91
+ r.extra_options_str << 'zywcab'
92
+ assert_equal 'zywcab', r.extra_options_str
93
+
94
+ b = BSON.new
95
+ doc = {'doc' => r}
96
+ b.serialize(doc)
97
+ doc2 = nil
98
+ doc2 = b.deserialize
99
+ assert_equal doc, doc2
100
+
101
+ r = doc2['doc']
102
+ assert_kind_of RegexpOfHolding, r
103
+ assert_equal 'abcwyz', r.extra_options_str # must be sorted
104
+ end
105
+
106
+ def test_boolean
107
+ doc = {'doc' => true}
108
+ @b.serialize(doc)
109
+ assert_equal doc, @b.deserialize
110
+ end
111
+
112
+ def test_date
113
+ doc = {'date' => Time.now}
114
+ @b.serialize(doc)
115
+ doc2 = @b.deserialize
116
+ # Mongo only stores up to the millisecond
117
+ assert_in_delta doc['date'], doc2['date'], 0.001
118
+ end
119
+
120
+ def test_date_returns_as_utc
121
+ doc = {'date' => Time.now}
122
+ @b.serialize(doc)
123
+ doc2 = @b.deserialize
124
+ assert doc2['date'].utc?
125
+ end
126
+
127
+ def test_date_before_epoch
128
+ begin
129
+ doc = {'date' => Time.utc(1600)}
130
+ @b.serialize(doc)
131
+ doc2 = @b.deserialize
132
+ # Mongo only stores up to the millisecond
133
+ assert_in_delta doc['date'], doc2['date'], 0.001
134
+ rescue ArgumentError
135
+ # some versions of Ruby won't let you create pre-epoch Time instances
136
+ #
137
+ # TODO figure out how that will work if somebady has saved data
138
+ # w/ early dates already and is just querying for it.
139
+ end
140
+ end
141
+
142
+ def test_dbref
143
+ oid = ObjectID.new
144
+ doc = {}
145
+ doc['dbref'] = DBRef.new('namespace', oid)
146
+ @b.serialize(doc)
147
+ doc2 = @b.deserialize
148
+ assert_equal 'namespace', doc2['dbref'].namespace
149
+ assert_equal oid, doc2['dbref'].object_id
150
+ end
151
+
152
+ def test_symbol
153
+ doc = {'sym' => :foo}
154
+ @b.serialize(doc)
155
+ doc2 = @b.deserialize
156
+ assert_equal :foo, doc2['sym']
157
+ end
158
+
159
+ def test_binary
160
+ bin = Binary.new
161
+ 'binstring'.each_byte { |b| bin.put(b) }
162
+
163
+ doc = {'bin' => bin}
164
+ @b.serialize(doc)
165
+ doc2 = @b.deserialize
166
+ bin2 = doc2['bin']
167
+ assert_kind_of Binary, bin2
168
+ assert_equal 'binstring', bin2.to_s
169
+ assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
170
+ end
171
+
172
+ def test_binary_type
173
+ bin = Binary.new([1, 2, 3, 4, 5], Binary::SUBTYPE_USER_DEFINED)
174
+
175
+ doc = {'bin' => bin}
176
+ @b.serialize(doc)
177
+ doc2 = @b.deserialize
178
+ bin2 = doc2['bin']
179
+ assert_kind_of Binary, bin2
180
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
181
+ assert_equal Binary::SUBTYPE_USER_DEFINED, bin2.subtype
182
+ end
183
+
184
+ def test_binary_byte_buffer
185
+ bb = ByteBuffer.new
186
+ 5.times { |i| bb.put(i + 1) }
187
+
188
+ doc = {'bin' => bb}
189
+ @b.serialize(doc)
190
+ doc2 = @b.deserialize
191
+ bin2 = doc2['bin']
192
+ assert_kind_of Binary, bin2
193
+ assert_equal [1, 2, 3, 4, 5], bin2.to_a
194
+ assert_equal Binary::SUBTYPE_BYTES, bin2.subtype
195
+ end
196
+
197
+ def test_put_id_first
198
+ val = OrderedHash.new
199
+ val['not_id'] = 1
200
+ val['_id'] = 2
201
+ roundtrip = @b.deserialize(@b.serialize(val).to_a)
202
+ assert_kind_of OrderedHash, roundtrip
203
+ assert_equal '_id', roundtrip.keys.first
204
+
205
+ val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
206
+ roundtrip = @b.deserialize(@b.serialize(val).to_a)
207
+ assert_kind_of OrderedHash, roundtrip
208
+ assert_equal '_id', roundtrip.keys.first
209
+ end
210
+
211
+ def test_nil_id
212
+ doc = {"_id" => nil}
213
+ assert_equal doc, @b.deserialize(@b.serialize(doc).to_a)
214
+ end
215
+
216
+ def test_timestamp
217
+ val = {"test" => [4, 20]}
218
+ assert_equal val, @b.deserialize([0x13, 0x00, 0x00, 0x00,
219
+ 0x11, 0x74, 0x65, 0x73,
220
+ 0x74, 0x00, 0x04, 0x00,
221
+ 0x00, 0x00, 0x14, 0x00,
222
+ 0x00, 0x00, 0x00])
223
+ end
224
+
225
+ def test_overflow
226
+ doc = {"x" => 2**75}
227
+ assert_raise RangeError do
228
+ @b.serialize(doc)
229
+ end
230
+
231
+ doc = {"x" => 9223372036854775}
232
+ assert_equal doc, @b.deserialize(@b.serialize(doc).to_a)
233
+
234
+ doc = {"x" => 9223372036854775807}
235
+ assert_equal doc, @b.deserialize(@b.serialize(doc).to_a)
236
+
237
+ doc["x"] = doc["x"] + 1
238
+ assert_raise RangeError do
239
+ @b.serialize(doc)
240
+ end
241
+
242
+ doc = {"x" => -9223372036854775}
243
+ assert_equal doc, @b.deserialize(@b.serialize(doc).to_a)
244
+
245
+ doc = {"x" => -9223372036854775808}
246
+ assert_equal doc, @b.deserialize(@b.serialize(doc).to_a)
247
+
248
+ doc["x"] = doc["x"] - 1
249
+ assert_raise RangeError do
250
+ @b.serialize(doc)
251
+ end
252
+ end
253
+
254
+ def test_do_not_change_original_object
255
+ val = OrderedHash.new
256
+ val['not_id'] = 1
257
+ val['_id'] = 2
258
+ assert val.keys.include?('_id')
259
+ @b.serialize(val)
260
+ assert val.keys.include?('_id')
261
+
262
+ val = {'a' => 'foo', 'b' => 'bar', :_id => 42, 'z' => 'hello'}
263
+ assert val.keys.include?(:_id)
264
+ @b.serialize(val)
265
+ assert val.keys.include?(:_id)
266
+ end
267
+
268
+ end