mongo 0.18.2 → 0.18.3
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.
- data/README.rdoc +37 -28
- data/Rakefile +19 -0
- data/bin/objectid_benchmark.rb +23 -0
- data/examples/admin.rb +7 -6
- data/examples/capped.rb +3 -4
- data/examples/cursor.rb +4 -3
- data/examples/gridfs.rb +3 -2
- data/examples/index_test.rb +10 -9
- data/examples/info.rb +3 -2
- data/examples/queries.rb +3 -2
- data/examples/simple.rb +3 -2
- data/examples/strict.rb +2 -1
- data/examples/types.rb +4 -3
- data/lib/mongo.rb +29 -12
- data/lib/mongo/admin.rb +17 -2
- data/lib/mongo/collection.rb +230 -169
- data/lib/mongo/connection.rb +136 -91
- data/lib/mongo/cursor.rb +68 -40
- data/lib/mongo/db.rb +247 -123
- data/lib/mongo/{errors.rb → exceptions.rb} +6 -5
- data/lib/mongo/gridfs.rb +6 -0
- data/lib/mongo/gridfs/grid_store.rb +142 -94
- data/lib/mongo/types/binary.rb +11 -1
- data/lib/mongo/types/code.rb +6 -1
- data/lib/mongo/types/dbref.rb +7 -2
- data/lib/mongo/types/min_max_keys.rb +58 -0
- data/lib/mongo/types/objectid.rb +76 -20
- data/lib/mongo/types/regexp_of_holding.rb +5 -0
- data/lib/mongo/util/bson_ruby.rb +36 -2
- data/lib/mongo/util/byte_buffer.rb +18 -2
- data/lib/mongo/util/conversions.rb +6 -5
- data/lib/mongo/util/ordered_hash.rb +3 -1
- data/lib/mongo/util/support.rb +3 -0
- data/lib/mongo/util/xml_to_ruby.rb +7 -0
- data/test/test_bson.rb +48 -0
- data/test/test_collection.rb +13 -0
- data/test/test_connection.rb +35 -0
- data/test/test_conversions.rb +1 -1
- data/test/test_cursor.rb +37 -5
- data/test/test_db.rb +51 -2
- data/test/test_db_api.rb +4 -7
- data/test/test_grid_store.rb +10 -0
- data/test/test_objectid.rb +16 -2
- data/test/test_ordered_hash.rb +14 -0
- data/test/threading/test_threading_large_pool.rb +4 -4
- data/test/unit/db_test.rb +43 -0
- metadata +5 -7
- data/examples/benchmarks.rb +0 -42
- data/examples/blog.rb +0 -76
- data/lib/mongo/constants.rb +0 -15
- data/test/mongo-qa/_common.rb +0 -8
@@ -1,18 +1,18 @@
|
|
1
|
-
#
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
2
3
|
#
|
3
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
5
|
# you may not use this file except in compliance with the License.
|
5
6
|
# You may obtain a copy of the License at
|
6
7
|
#
|
7
|
-
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
9
|
#
|
9
10
|
# Unless required by applicable law or agreed to in writing, software
|
10
11
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
12
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
13
|
# See the License for the specific language governing permissions and
|
13
14
|
# limitations under the License.
|
14
|
-
|
15
|
-
# Exceptions raised by the MongoDB driver.
|
15
|
+
# ++
|
16
16
|
|
17
17
|
module Mongo
|
18
18
|
# Generic Mongo Ruby Driver exception class.
|
@@ -39,7 +39,8 @@ module Mongo
|
|
39
39
|
# Raised on failures in connection to the database server.
|
40
40
|
class ConnectionTimeoutError < MongoRubyError; end
|
41
41
|
|
42
|
-
# Raised when trying to insert a document that exceeds the 4MB limit
|
42
|
+
# Raised when trying to insert a document that exceeds the 4MB limit or
|
43
|
+
# when the document contains objects that can't be serialized as BSON.
|
43
44
|
class InvalidDocument < MongoDBError; end
|
44
45
|
|
45
46
|
# Raised when a database operation fails.
|
data/lib/mongo/gridfs.rb
CHANGED
@@ -20,25 +20,32 @@ require 'mongo/gridfs/chunk'
|
|
20
20
|
|
21
21
|
module GridFS
|
22
22
|
|
23
|
-
# GridStore is an IO-like
|
24
|
-
# streams of data to
|
25
|
-
# storage implementation details.
|
23
|
+
# GridStore is an IO-like class that provides input and output for
|
24
|
+
# streams of data to MongoDB.
|
26
25
|
#
|
27
|
-
#
|
26
|
+
# @example
|
28
27
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
#
|
41
|
-
#
|
28
|
+
# include GridFS
|
29
|
+
#
|
30
|
+
# #Store the text "Hello, world!" in the grid store.
|
31
|
+
# GridStore.open(database, 'filename', 'w') do |f|
|
32
|
+
# f.puts "Hello, world!"
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # Output "Hello, world!"
|
36
|
+
# GridStore.open(database, 'filename', 'r') do |f|
|
37
|
+
# puts f.read
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# # Add text to the grid store.
|
41
|
+
# GridStore.open(database, 'filename', 'w+') do |f|
|
42
|
+
# f.puts "But wait, there's more!"
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# # Retrieve everything, outputting "Hello, world!\nBut wait, there's more!\n"
|
46
|
+
# GridStore.open(database, 'filename', 'r') do |f|
|
47
|
+
# puts f.read
|
48
|
+
# end
|
42
49
|
class GridStore
|
43
50
|
|
44
51
|
DEFAULT_ROOT_COLLECTION = 'fs'
|
@@ -70,80 +77,128 @@ module GridFS
|
|
70
77
|
|
71
78
|
attr_reader :md5
|
72
79
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
80
|
+
# Determine whether a given file exists in the GridStore.
|
81
|
+
#
|
82
|
+
# @param [Mongo::DB] a MongoDB database.
|
83
|
+
# @param [String] name the filename.
|
84
|
+
# @param [String] root_collection the name of the gridfs root collection.
|
85
|
+
#
|
86
|
+
# @return [Boolean]
|
87
|
+
def self.exist?(db, name, root_collection=DEFAULT_ROOT_COLLECTION)
|
88
|
+
db.collection("#{root_collection}.files").find({'filename' => name}).next_document != nil
|
89
|
+
end
|
78
90
|
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
91
|
+
# Open a GridFS file for reading, writing, or appending. Note that
|
92
|
+
# this method must be used with a block.
|
93
|
+
#
|
94
|
+
# @param [Mongo::DB] a MongoDB database.
|
95
|
+
# @param [String] name the filename.
|
96
|
+
# @param [String] mode one of 'r', 'w', or 'w+' for reading, writing,
|
97
|
+
# and appending, respectively.
|
98
|
+
# @param [Hash] options any of the options available on
|
99
|
+
# GridStore initialization.
|
100
|
+
#
|
101
|
+
# @see GridStore#initialize.
|
102
|
+
# @see The various GridStore class methods, e.g., GridStore.open, GridStore.read etc.
|
103
|
+
def self.open(db, name, mode, options={})
|
104
|
+
gs = self.new(db, name, mode, options)
|
105
|
+
result = nil
|
106
|
+
begin
|
107
|
+
result = yield gs if block_given?
|
108
|
+
ensure
|
109
|
+
gs.close
|
88
110
|
end
|
111
|
+
result
|
112
|
+
end
|
89
113
|
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
114
|
+
# Read a file stored in GridFS.
|
115
|
+
#
|
116
|
+
# @param [Mongo::DB] db a MongoDB database.
|
117
|
+
# @param [String] name the name of the file.
|
118
|
+
# @param [Integer] length the number of bytes to read.
|
119
|
+
# @param [Integer] offset the number of bytes beyond the
|
120
|
+
# beginning of the file to start reading.
|
121
|
+
#
|
122
|
+
# @return [String] the file data
|
123
|
+
def self.read(db, name, length=nil, offset=nil)
|
124
|
+
GridStore.open(db, name, 'r') do |gs|
|
125
|
+
gs.seek(offset) if offset
|
126
|
+
gs.read(length)
|
95
127
|
end
|
128
|
+
end
|
96
129
|
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
130
|
+
# List the contents of all GridFS files stored in the given db and
|
131
|
+
# root collection.
|
132
|
+
#
|
133
|
+
# @param [Mongo::DB] db a MongoDB database.
|
134
|
+
# @param [String] root_collection the name of the root collection.
|
135
|
+
#
|
136
|
+
# @return [Array]
|
137
|
+
def self.list(db, root_collection=DEFAULT_ROOT_COLLECTION)
|
138
|
+
db.collection("#{root_collection}.files").find().map do |f|
|
139
|
+
f['filename']
|
107
140
|
end
|
141
|
+
end
|
108
142
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
143
|
+
# Get each line of data from the specified file
|
144
|
+
# as an array of strings.
|
145
|
+
#
|
146
|
+
# @param [Mongo::DB] db a MongoDB database.
|
147
|
+
# @param [String] name the filename.
|
148
|
+
# @param [String, Reg] separator
|
149
|
+
#
|
150
|
+
# @return [Array]
|
151
|
+
def self.readlines(db, name, separator=$/)
|
152
|
+
GridStore.open(db, name, 'r') do |gs|
|
153
|
+
gs.readlines(separator)
|
113
154
|
end
|
155
|
+
end
|
114
156
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
157
|
+
# Remove one for more files from the given db.
|
158
|
+
#
|
159
|
+
# @param [Mongo::Database] db a MongoDB database.
|
160
|
+
# @param [Array<String>] names the filenames to remove
|
161
|
+
#
|
162
|
+
# @return [True]
|
163
|
+
def self.unlink(db, *names)
|
164
|
+
names.each do |name|
|
165
|
+
gs = GridStore.new(db, name)
|
166
|
+
gs.send(:delete_chunks)
|
167
|
+
gs.collection.remove('_id' => gs.files_id)
|
121
168
|
end
|
169
|
+
end
|
170
|
+
class << self
|
122
171
|
alias_method :delete, :unlink
|
123
|
-
|
124
172
|
end
|
125
173
|
|
126
|
-
|
127
|
-
#
|
128
|
-
|
174
|
+
# Rename a file in this collection. Note that this method uses
|
175
|
+
# Collection#update, which means that you will not be notified
|
176
|
+
#
|
177
|
+
# @param [Mongo::DB] a MongoDB database.
|
178
|
+
# @param [String] src the name of the source file.
|
179
|
+
# @param [String] dest the name of the destination file.
|
180
|
+
# @param [String] root_collection the name of the default root collection.
|
181
|
+
def self.mv(db, src, dest, root_collection=DEFAULT_ROOT_COLLECTION)
|
182
|
+
db.collection("#{root_collection}.files").update({ :filename => src }, { '$set' => { :filename => dest } })
|
183
|
+
end
|
129
184
|
|
130
|
-
#
|
185
|
+
# Initialize a GridStore instance for reading, writing, or modifying a given file.
|
186
|
+
# Note that it's often easier to work with the various GridStore class methods (open, read, etc.).
|
131
187
|
#
|
132
|
-
#
|
133
|
-
#
|
188
|
+
# @param [Mongo::DB] db a MongoDB database.
|
189
|
+
# @param [String] name a filename.
|
190
|
+
# @param [String] mode either 'r', 'w', or 'w+' for reading, writing, or appending, respectively.
|
134
191
|
#
|
135
|
-
# :root
|
136
|
-
# DEFAULT_ROOT_COLLECTION.
|
192
|
+
# @option options [String] :root DEFAULT_ROOT_COLLECTION ('r', 'w', 'w+') the name of the root collection to use.
|
137
193
|
#
|
138
|
-
# :metadata
|
139
|
-
#
|
194
|
+
# @option options [String] :metadata ({}) (w, w+) A hash containing any data you want persisted as
|
195
|
+
# this file's metadata.
|
140
196
|
#
|
141
|
-
# :chunk_size :: (w) Sets chunk size for files opened for writing
|
142
|
-
#
|
143
|
-
# any data is written.
|
197
|
+
# @option options [Integer] :chunk_size (Chunk::DEFAULT_CHUNK_SIZE) (w) Sets chunk size for files opened for writing.
|
198
|
+
# See also GridStore#chunk_size=.
|
144
199
|
#
|
145
|
-
# :content_type
|
146
|
-
#
|
200
|
+
# @option options [String] :content_type ('text/plain') Set the content type stored as the
|
201
|
+
# file's metadata. See also GridStore#content_type=.
|
147
202
|
def initialize(db, name, mode='r', options={})
|
148
203
|
@db, @filename, @mode = db, name, mode
|
149
204
|
@root = options[:root] || DEFAULT_ROOT_COLLECTION
|
@@ -191,18 +246,26 @@ module GridFS
|
|
191
246
|
@pushback_byte = nil
|
192
247
|
end
|
193
248
|
|
249
|
+
# Get the files collection referenced by this GridStore instance.
|
250
|
+
#
|
251
|
+
# @return [Mongo::Collection]
|
194
252
|
def collection
|
195
253
|
@db.collection("#{@root}.files")
|
196
254
|
end
|
197
255
|
|
198
|
-
#
|
199
|
-
#
|
256
|
+
# Get the chunk collection referenced by this GridStore.
|
257
|
+
#
|
258
|
+
# @return [Mongo::Collection]
|
200
259
|
def chunk_collection
|
201
260
|
@db.collection("#{@root}.chunks")
|
202
261
|
end
|
203
262
|
|
204
|
-
# Change chunk size.
|
263
|
+
# Change the chunk size. This is permitted only when the file is opened for write
|
205
264
|
# and no data has yet been written.
|
265
|
+
#
|
266
|
+
# @param [Integer] size the new chunk size, in bytes.
|
267
|
+
#
|
268
|
+
# @return [Integer] the new chunk size.
|
206
269
|
def chunk_size=(size)
|
207
270
|
unless @mode[0] == ?w && @position == 0 && @upload_date == nil
|
208
271
|
raise "error: can only change chunk size if open for write and no data written."
|
@@ -210,9 +273,7 @@ module GridFS
|
|
210
273
|
@chunk_size = size
|
211
274
|
end
|
212
275
|
|
213
|
-
#---
|
214
276
|
# ================ reading ================
|
215
|
-
#+++
|
216
277
|
|
217
278
|
def getc
|
218
279
|
if @pushback_byte
|
@@ -245,17 +306,6 @@ module GridFS
|
|
245
306
|
str
|
246
307
|
end
|
247
308
|
|
248
|
-
def old_read(len=nil, buf=nil)
|
249
|
-
buf ||= ''
|
250
|
-
byte = self.getc
|
251
|
-
while byte != nil && (len == nil || len > 0)
|
252
|
-
buf << byte.chr
|
253
|
-
len -= 1 if len
|
254
|
-
byte = self.getc if (len == nil || len > 0)
|
255
|
-
end
|
256
|
-
buf
|
257
|
-
end
|
258
|
-
|
259
309
|
def read(len=nil, buf=nil)
|
260
310
|
if len
|
261
311
|
read_partial(len, buf)
|
@@ -302,9 +352,7 @@ module GridFS
|
|
302
352
|
@position -= 1
|
303
353
|
end
|
304
354
|
|
305
|
-
#---
|
306
355
|
# ================ writing ================
|
307
|
-
#+++
|
308
356
|
|
309
357
|
def putc(byte)
|
310
358
|
if @curr_chunk.pos == @chunk_size
|
@@ -344,6 +392,10 @@ module GridFS
|
|
344
392
|
|
345
393
|
def write(string)
|
346
394
|
raise "#@filename not opened for write" unless @mode[0] == ?w
|
395
|
+
# Since Ruby 1.9.1 doesn't necessarily store one character per byte.
|
396
|
+
if string.respond_to?(:force_encoding)
|
397
|
+
string.force_encoding("binary")
|
398
|
+
end
|
347
399
|
to_write = string.length
|
348
400
|
while (to_write > 0) do
|
349
401
|
if @curr_chunk && @curr_chunk.data.position == @chunk_size
|
@@ -363,9 +415,7 @@ module GridFS
|
|
363
415
|
def flush
|
364
416
|
end
|
365
417
|
|
366
|
-
#---
|
367
418
|
# ================ status ================
|
368
|
-
#+++
|
369
419
|
|
370
420
|
def eof
|
371
421
|
raise IOError.new("stream not open for reading") unless @mode[0] == ?r
|
@@ -373,9 +423,7 @@ module GridFS
|
|
373
423
|
end
|
374
424
|
alias_method :eof?, :eof
|
375
425
|
|
376
|
-
#---
|
377
426
|
# ================ positioning ================
|
378
|
-
#+++
|
379
427
|
|
380
428
|
def rewind
|
381
429
|
if @curr_chunk.chunk_number != 0
|
data/lib/mongo/types/binary.rb
CHANGED
@@ -18,7 +18,10 @@ require 'mongo/util/byte_buffer'
|
|
18
18
|
|
19
19
|
module Mongo
|
20
20
|
|
21
|
-
# An array of binary bytes with a
|
21
|
+
# An array of binary bytes with a MongoDB subtype. See the subtype
|
22
|
+
# constants for reference.
|
23
|
+
#
|
24
|
+
# Use this class when storing binary data in documents.
|
22
25
|
class Binary < ByteBuffer
|
23
26
|
|
24
27
|
SUBTYPE_BYTES = 0x02
|
@@ -29,6 +32,13 @@ module Mongo
|
|
29
32
|
# One of the SUBTYPE_* constants. Default is SUBTYPE_BYTES.
|
30
33
|
attr_accessor :subtype
|
31
34
|
|
35
|
+
# Create a buffer for storing binary data in MongoDB.
|
36
|
+
#
|
37
|
+
# @param [Array] initia_data
|
38
|
+
# @param [Fixnum] one of four values specifying a BSON binary subtype. Possible values are
|
39
|
+
# SUBTYPE_BYTES, SUBTYPE_UUID, SUBTYPE_MD5, and SUBTYPE_USER_DEFINED.
|
40
|
+
#
|
41
|
+
# @see http://www.mongodb.org/display/DOCS/BSON#BSON-noteondatabinary BSON binary subtypes.
|
32
42
|
def initialize(initial_data=[], subtype=SUBTYPE_BYTES)
|
33
43
|
super(initial_data)
|
34
44
|
@subtype = subtype
|
data/lib/mongo/types/code.rb
CHANGED
@@ -16,12 +16,17 @@
|
|
16
16
|
|
17
17
|
module Mongo
|
18
18
|
|
19
|
-
# JavaScript code to be evaluated by MongoDB
|
19
|
+
# JavaScript code to be evaluated by MongoDB.
|
20
20
|
class Code < String
|
21
21
|
|
22
22
|
# Hash mapping identifiers to their values
|
23
23
|
attr_accessor :scope
|
24
24
|
|
25
|
+
# Wrap code to be evaluated by MongoDB.
|
26
|
+
#
|
27
|
+
# @param [String] code the JavaScript code.
|
28
|
+
# @param [Hash] a document mapping identifiers to values, which
|
29
|
+
# represent the scope in which the code is to be executed.
|
25
30
|
def initialize(code, scope={})
|
26
31
|
super(code)
|
27
32
|
@scope = scope
|
data/lib/mongo/types/dbref.rb
CHANGED
@@ -16,13 +16,18 @@
|
|
16
16
|
|
17
17
|
module Mongo
|
18
18
|
|
19
|
+
# A reference to another object in a MongoDB database.
|
19
20
|
class DBRef
|
20
21
|
|
21
22
|
attr_reader :namespace, :object_id
|
22
23
|
|
24
|
+
# Create a DBRef. Use this class in conjunction with DB#dereference.
|
25
|
+
#
|
26
|
+
# @param [String] a collection name
|
27
|
+
# @param [ObjectID] an object id
|
23
28
|
def initialize(namespace, object_id)
|
24
|
-
@namespace
|
25
|
-
|
29
|
+
@namespace = namespace
|
30
|
+
@object_id = object_id
|
26
31
|
end
|
27
32
|
|
28
33
|
def to_s
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# --
|
2
|
+
# Copyright (C) 2008-2009 10gen Inc.
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
# ++
|
16
|
+
|
17
|
+
module Mongo
|
18
|
+
|
19
|
+
# A class representing the BSON MaxKey type. MaxKey will always compare greater than
|
20
|
+
# all other BSON types and values.
|
21
|
+
#
|
22
|
+
# @example Sorting (assume @numbers is a collection):
|
23
|
+
#
|
24
|
+
# >> @numbers.save({"n" => Mongo::MaxKey.new})
|
25
|
+
# >> @numbers.save({"n" => 0})
|
26
|
+
# >> @numbers.save({"n" => 5_000_000})
|
27
|
+
# >> @numbers.find.sort("n").to_a
|
28
|
+
# => [{"_id"=>4b5a050c238d3bace2000004, "n"=>0},
|
29
|
+
# {"_id"=>4b5a04e6238d3bace2000002, "n"=>5_000_000},
|
30
|
+
# {"_id"=>4b5a04ea238d3bace2000003, "n"=>#<Mongo::MaxKey:0x1014ef410>},
|
31
|
+
# ]
|
32
|
+
class MaxKey
|
33
|
+
|
34
|
+
def ==(obj)
|
35
|
+
obj.class == MaxKey
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# A class representing the BSON MinKey type. MinKey will always compare less than
|
40
|
+
# all other BSON types and values.
|
41
|
+
#
|
42
|
+
# @example Sorting (assume @numbers is a collection):
|
43
|
+
#
|
44
|
+
# >> @numbers.save({"n" => Mongo::MinKey.new})
|
45
|
+
# >> @numbers.save({"n" => -1_000_000})
|
46
|
+
# >> @numbers.save({"n" => 1_000_000})
|
47
|
+
# >> @numbers.find.sort("n").to_a
|
48
|
+
# => [{"_id"=>4b5a050c238d3bace2000004, "n"=>#<Mongo::MinKey:0x1014ef410>},
|
49
|
+
# {"_id"=>4b5a04e6238d3bace2000002, "n"=>-1_000_000},
|
50
|
+
# {"_id"=>4b5a04ea238d3bace2000003, "n"=>1_000_000},
|
51
|
+
# ]
|
52
|
+
class MinKey
|
53
|
+
|
54
|
+
def ==(obj)
|
55
|
+
obj.class == MinKey
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|