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.
- data/README.rdoc +41 -50
- data/Rakefile +14 -4
- data/examples/gridfs.rb +25 -70
- data/lib/mongo.rb +4 -2
- data/lib/mongo/collection.rb +70 -89
- data/lib/mongo/connection.rb +203 -43
- data/lib/mongo/cursor.rb +7 -7
- data/lib/mongo/db.rb +61 -18
- data/lib/mongo/exceptions.rb +7 -1
- data/lib/mongo/gridfs.rb +8 -1
- data/lib/mongo/gridfs/chunk.rb +2 -1
- data/lib/mongo/gridfs/grid.rb +90 -0
- data/lib/mongo/gridfs/grid_file_system.rb +113 -0
- data/lib/mongo/gridfs/grid_io.rb +339 -0
- data/lib/mongo/gridfs/grid_store.rb +43 -18
- data/lib/mongo/types/binary.rb +5 -1
- data/lib/mongo/types/code.rb +1 -1
- data/lib/mongo/types/dbref.rb +3 -1
- data/lib/mongo/types/min_max_keys.rb +1 -1
- data/lib/mongo/types/objectid.rb +16 -55
- data/lib/mongo/types/regexp_of_holding.rb +1 -1
- data/lib/mongo/util/bson_c.rb +2 -2
- data/lib/mongo/util/bson_ruby.rb +22 -11
- data/lib/mongo/util/byte_buffer.rb +1 -1
- data/lib/mongo/util/conversions.rb +1 -1
- data/lib/mongo/util/ordered_hash.rb +6 -1
- data/lib/mongo/util/server_version.rb +1 -1
- data/lib/mongo/util/support.rb +1 -1
- data/mongo-ruby-driver.gemspec +1 -1
- data/test/auxillary/authentication_test.rb +68 -0
- data/test/auxillary/autoreconnect_test.rb +41 -0
- data/test/binary_test.rb +15 -0
- data/test/{test_bson.rb → bson_test.rb} +63 -6
- data/test/{test_byte_buffer.rb → byte_buffer_test.rb} +0 -0
- data/test/{test_chunk.rb → chunk_test.rb} +0 -0
- data/test/{test_collection.rb → collection_test.rb} +35 -39
- data/test/{test_connection.rb → connection_test.rb} +33 -3
- data/test/{test_conversions.rb → conversions_test.rb} +0 -0
- data/test/{test_cursor.rb → cursor_test.rb} +0 -7
- data/test/{test_db_api.rb → db_api_test.rb} +3 -6
- data/test/{test_db_connection.rb → db_connection_test.rb} +0 -0
- data/test/{test_db.rb → db_test.rb} +33 -15
- data/test/grid_file_system_test.rb +210 -0
- data/test/grid_io_test.rb +78 -0
- data/test/{test_grid_store.rb → grid_store_test.rb} +33 -2
- data/test/grid_test.rb +87 -0
- data/test/{test_objectid.rb → objectid_test.rb} +2 -33
- data/test/{test_ordered_hash.rb → ordered_hash_test.rb} +4 -0
- data/test/{test_slave_connection.rb → slave_connection_test.rb} +0 -0
- data/test/test_helper.rb +2 -2
- data/test/{test_threading.rb → threading_test.rb} +0 -0
- data/test/unit/collection_test.rb +12 -3
- data/test/unit/connection_test.rb +85 -24
- data/test/unit/cursor_test.rb +1 -2
- data/test/unit/db_test.rb +70 -69
- metadata +27 -23
- data/bin/objectid_benchmark.rb +0 -23
- data/bin/perf.rb +0 -30
- data/lib/mongo/admin.rb +0 -95
- data/lib/mongo/util/xml_to_ruby.rb +0 -112
- data/test/test_admin.rb +0 -67
- data/test/test_round_trip.rb +0 -114
@@ -1,5 +1,5 @@
|
|
1
1
|
# --
|
2
|
-
# Copyright (C) 2008-
|
2
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -46,12 +46,16 @@ module GridFS
|
|
46
46
|
# GridStore.open(database, 'filename', 'r') do |f|
|
47
47
|
# puts f.read
|
48
48
|
# end
|
49
|
+
#
|
50
|
+
# @deprecated
|
49
51
|
class GridStore
|
52
|
+
include Enumerable
|
50
53
|
|
51
54
|
DEFAULT_ROOT_COLLECTION = 'fs'
|
55
|
+
|
52
56
|
DEFAULT_CONTENT_TYPE = 'text/plain'
|
53
57
|
|
54
|
-
|
58
|
+
DEPRECATION_WARNING = "GridFS::GridStore is deprecated. Use either Grid or GridFileSystem."
|
55
59
|
|
56
60
|
attr_accessor :filename
|
57
61
|
|
@@ -77,6 +81,14 @@ module GridFS
|
|
77
81
|
|
78
82
|
attr_reader :md5
|
79
83
|
|
84
|
+
def self.default_root_collection
|
85
|
+
@@default_root_collection ||= DEFAULT_ROOT_COLLECTION
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.default_root_collection=(name)
|
89
|
+
@@default_root_collection = name
|
90
|
+
end
|
91
|
+
|
80
92
|
# Determine whether a given file exists in the GridStore.
|
81
93
|
#
|
82
94
|
# @param [Mongo::DB] a MongoDB database.
|
@@ -84,7 +96,9 @@ module GridFS
|
|
84
96
|
# @param [String] root_collection the name of the gridfs root collection.
|
85
97
|
#
|
86
98
|
# @return [Boolean]
|
87
|
-
|
99
|
+
# @deprecated
|
100
|
+
def self.exist?(db, name, root_collection=GridStore.default_root_collection)
|
101
|
+
warn DEPRECATION_WARNING
|
88
102
|
db.collection("#{root_collection}.files").find({'filename' => name}).next_document != nil
|
89
103
|
end
|
90
104
|
|
@@ -93,13 +107,14 @@ module GridFS
|
|
93
107
|
#
|
94
108
|
# @param [Mongo::DB] a MongoDB database.
|
95
109
|
# @param [String] name the filename.
|
96
|
-
# @param [String] mode one of 'r', 'w', or 'w+' for reading, writing,
|
110
|
+
# @param [String] mode one of 'r', 'w', or 'w+' for reading, writing,
|
97
111
|
# and appending, respectively.
|
98
|
-
# @param [Hash] options any of the options available on
|
112
|
+
# @param [Hash] options any of the options available on
|
99
113
|
# GridStore initialization.
|
100
114
|
#
|
101
115
|
# @see GridStore#initialize.
|
102
116
|
# @see The various GridStore class methods, e.g., GridStore.open, GridStore.read etc.
|
117
|
+
# @deprecated
|
103
118
|
def self.open(db, name, mode, options={})
|
104
119
|
gs = self.new(db, name, mode, options)
|
105
120
|
result = nil
|
@@ -120,6 +135,7 @@ module GridFS
|
|
120
135
|
# beginning of the file to start reading.
|
121
136
|
#
|
122
137
|
# @return [String] the file data
|
138
|
+
# @deprecated
|
123
139
|
def self.read(db, name, length=nil, offset=nil)
|
124
140
|
GridStore.open(db, name, 'r') do |gs|
|
125
141
|
gs.seek(offset) if offset
|
@@ -134,13 +150,15 @@ module GridFS
|
|
134
150
|
# @param [String] root_collection the name of the root collection.
|
135
151
|
#
|
136
152
|
# @return [Array]
|
137
|
-
|
153
|
+
# @deprecated
|
154
|
+
def self.list(db, root_collection=GridStore.default_root_collection)
|
155
|
+
warn DEPRECATION_WARNING
|
138
156
|
db.collection("#{root_collection}.files").find().map do |f|
|
139
157
|
f['filename']
|
140
158
|
end
|
141
159
|
end
|
142
160
|
|
143
|
-
# Get each line of data from the specified file
|
161
|
+
# Get each line of data from the specified file
|
144
162
|
# as an array of strings.
|
145
163
|
#
|
146
164
|
# @param [Mongo::DB] db a MongoDB database.
|
@@ -148,6 +166,7 @@ module GridFS
|
|
148
166
|
# @param [String, Reg] separator
|
149
167
|
#
|
150
168
|
# @return [Array]
|
169
|
+
# @deprecated
|
151
170
|
def self.readlines(db, name, separator=$/)
|
152
171
|
GridStore.open(db, name, 'r') do |gs|
|
153
172
|
gs.readlines(separator)
|
@@ -160,10 +179,11 @@ module GridFS
|
|
160
179
|
# @param [Array<String>] names the filenames to remove
|
161
180
|
#
|
162
181
|
# @return [True]
|
182
|
+
# @deprecated
|
163
183
|
def self.unlink(db, *names)
|
164
184
|
names.each do |name|
|
165
185
|
gs = GridStore.new(db, name)
|
166
|
-
gs.
|
186
|
+
gs.delete_chunks
|
167
187
|
gs.collection.remove('_id' => gs.files_id)
|
168
188
|
end
|
169
189
|
end
|
@@ -172,13 +192,16 @@ module GridFS
|
|
172
192
|
end
|
173
193
|
|
174
194
|
# Rename a file in this collection. Note that this method uses
|
175
|
-
# Collection#update, which means that you will not be notified
|
195
|
+
# Collection#update, which means that you will not be notified of the
|
196
|
+
# success of the operation.
|
176
197
|
#
|
177
198
|
# @param [Mongo::DB] a MongoDB database.
|
178
199
|
# @param [String] src the name of the source file.
|
179
200
|
# @param [String] dest the name of the destination file.
|
180
201
|
# @param [String] root_collection the name of the default root collection.
|
181
|
-
|
202
|
+
# @deprecated
|
203
|
+
def self.mv(db, src, dest, root_collection=GridStore.default_root_collection)
|
204
|
+
warn DEPRECATION_WARNING
|
182
205
|
db.collection("#{root_collection}.files").update({ :filename => src }, { '$set' => { :filename => dest } })
|
183
206
|
end
|
184
207
|
|
@@ -197,11 +220,13 @@ module GridFS
|
|
197
220
|
# @option options [Integer] :chunk_size (Chunk::DEFAULT_CHUNK_SIZE) (w) Sets chunk size for files opened for writing.
|
198
221
|
# See also GridStore#chunk_size=.
|
199
222
|
#
|
200
|
-
# @option options [String] :content_type ('text/plain') Set the content type stored as the
|
223
|
+
# @option options [String] :content_type ('text/plain') Set the content type stored as the
|
201
224
|
# file's metadata. See also GridStore#content_type=.
|
225
|
+
# @deprecated
|
202
226
|
def initialize(db, name, mode='r', options={})
|
227
|
+
warn DEPRECATION_WARNING
|
203
228
|
@db, @filename, @mode = db, name, mode
|
204
|
-
@root = options[:root] ||
|
229
|
+
@root = options[:root] || GridStore.default_root_collection
|
205
230
|
|
206
231
|
doc = collection.find({'filename' => @filename}).next_document
|
207
232
|
if doc
|
@@ -488,6 +513,11 @@ module GridFS
|
|
488
513
|
@db == nil
|
489
514
|
end
|
490
515
|
|
516
|
+
def delete_chunks
|
517
|
+
chunk_collection.remove({'files_id' => @files_id}) if @files_id
|
518
|
+
@curr_chunk = nil
|
519
|
+
end
|
520
|
+
|
491
521
|
#---
|
492
522
|
# ================ protected ================
|
493
523
|
#+++
|
@@ -537,12 +567,7 @@ module GridFS
|
|
537
567
|
buf
|
538
568
|
end
|
539
569
|
|
540
|
-
|
541
|
-
chunk_collection.remove({'files_id' => @files_id}) if @files_id
|
542
|
-
@curr_chunk = nil
|
543
|
-
end
|
544
|
-
|
545
|
-
def nth_chunk(n)
|
570
|
+
def nth_chunk(n)
|
546
571
|
mongo_chunk = chunk_collection.find({'files_id' => @files_id, 'n' => n}).next_document
|
547
572
|
Chunk.new(self, mongo_chunk || {})
|
548
573
|
end
|
data/lib/mongo/types/binary.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# --
|
2
|
-
# Copyright (C) 2008-
|
2
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -44,5 +44,9 @@ module Mongo
|
|
44
44
|
@subtype = subtype
|
45
45
|
end
|
46
46
|
|
47
|
+
def inspect
|
48
|
+
"<Mongo::Binary:#{object_id}>"
|
49
|
+
end
|
50
|
+
|
47
51
|
end
|
48
52
|
end
|
data/lib/mongo/types/code.rb
CHANGED
data/lib/mongo/types/dbref.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# --
|
2
|
-
# Copyright (C) 2008-
|
2
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -25,6 +25,8 @@ module Mongo
|
|
25
25
|
#
|
26
26
|
# @param [String] a collection name
|
27
27
|
# @param [ObjectID] an object id
|
28
|
+
#
|
29
|
+
# @core dbrefs constructor_details
|
28
30
|
def initialize(namespace, object_id)
|
29
31
|
@namespace = namespace
|
30
32
|
@object_id = object_id
|
data/lib/mongo/types/objectid.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# --
|
2
|
-
# Copyright (C) 2008-
|
2
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -20,21 +20,15 @@ require 'digest/md5'
|
|
20
20
|
|
21
21
|
module Mongo
|
22
22
|
|
23
|
-
#
|
23
|
+
# Generates MongoDB object ids.
|
24
|
+
#
|
25
|
+
# @core objectids
|
24
26
|
class ObjectID
|
25
|
-
# This is the legacy byte ordering for Babble. Versions of the Ruby
|
26
|
-
# driver prior to 0.14 used this byte ordering when converting ObjectID
|
27
|
-
# instances to and from strings. If you have string representations of
|
28
|
-
# ObjectIDs using the legacy byte ordering make sure to use the
|
29
|
-
# to_s_legacy and from_string_legacy methods, or convert your strings
|
30
|
-
# with ObjectID#legacy_string_convert
|
31
|
-
BYTE_ORDER = [7, 6, 5, 4, 3, 2, 1, 0, 11, 10, 9, 8]
|
32
|
-
|
33
27
|
@@lock = Mutex.new
|
34
28
|
@@index = 0
|
35
29
|
|
36
30
|
# Create a new object id. If no parameter is given, an id corresponding
|
37
|
-
# to the ObjectID BSON data type will be created. This is a 12-byte value
|
31
|
+
# to the ObjectID BSON data type will be created. This is a 12-byte value
|
38
32
|
# consisting of a 4-byte timestamp, a 3-byte machine id, a 2-byte process id,
|
39
33
|
# and a 3-byte counter.
|
40
34
|
#
|
@@ -44,8 +38,14 @@ module Mongo
|
|
44
38
|
@data = data || generate
|
45
39
|
end
|
46
40
|
|
41
|
+
# Determine if the supplied string is legal. Legal strings will
|
42
|
+
# consist of 24 hexadecimal characters.
|
43
|
+
#
|
44
|
+
# @param [String] str
|
45
|
+
#
|
46
|
+
# @return [Boolean]
|
47
47
|
def self.legal?(str)
|
48
|
-
len =
|
48
|
+
len = 24
|
49
49
|
str =~ /([0-9a-f]+)/i
|
50
50
|
match = $1
|
51
51
|
str && str.length == len && match == str
|
@@ -115,21 +115,6 @@ module Mongo
|
|
115
115
|
self.new(data)
|
116
116
|
end
|
117
117
|
|
118
|
-
# @deprecated
|
119
|
-
# Create a new ObjectID given a string representation of an ObjectID
|
120
|
-
# using the legacy byte ordering. This method may eventually be
|
121
|
-
# removed. If you are not sure that you need this method you should be
|
122
|
-
# using the regular from_string.
|
123
|
-
def self.from_string_legacy(str)
|
124
|
-
warn "Support for legacy object ids has been DEPRECATED."
|
125
|
-
raise InvalidObjectID, "illegal ObjectID format" unless legal?(str)
|
126
|
-
data = []
|
127
|
-
BYTE_ORDER.each_with_index { |string_position, data_index|
|
128
|
-
data[data_index] = str[string_position * 2, 2].to_i(16)
|
129
|
-
}
|
130
|
-
self.new(data)
|
131
|
-
end
|
132
|
-
|
133
118
|
# Get a string representation of this object id.
|
134
119
|
#
|
135
120
|
# @return [String]
|
@@ -140,7 +125,10 @@ module Mongo
|
|
140
125
|
end
|
141
126
|
str
|
142
127
|
end
|
143
|
-
|
128
|
+
|
129
|
+
def inspect
|
130
|
+
"ObjectID('#{to_s}')"
|
131
|
+
end
|
144
132
|
|
145
133
|
# Convert to MongoDB extended JSON format. Since JSON includes type information,
|
146
134
|
# but lacks an ObjectID type, this JSON format encodes the type using an $id key.
|
@@ -150,33 +138,6 @@ module Mongo
|
|
150
138
|
"{\"$oid\": \"#{to_s}\"}"
|
151
139
|
end
|
152
140
|
|
153
|
-
# @deprecated
|
154
|
-
# Get a string representation of this ObjectID using the legacy byte
|
155
|
-
# ordering. This method may eventually be removed. If you are not sure
|
156
|
-
# that you need this method you should be using the regular to_s.
|
157
|
-
def to_s_legacy
|
158
|
-
warn "Support for legacy object ids has been DEPRECATED."
|
159
|
-
str = ' ' * 24
|
160
|
-
BYTE_ORDER.each_with_index { |string_position, data_index|
|
161
|
-
str[string_position * 2, 2] = '%02x' % @data[data_index]
|
162
|
-
}
|
163
|
-
str
|
164
|
-
end
|
165
|
-
|
166
|
-
# @deprecated
|
167
|
-
# Convert a string representation of an ObjectID using the legacy byte
|
168
|
-
# ordering to the proper byte ordering. This method may eventually be
|
169
|
-
# removed. If you are not sure that you need this method it is probably
|
170
|
-
# unnecessary.
|
171
|
-
def self.legacy_string_convert(str)
|
172
|
-
warn "Support for legacy object ids has been DEPRECATED."
|
173
|
-
legacy = ' ' * 24
|
174
|
-
BYTE_ORDER.each_with_index do |legacy_pos, pos|
|
175
|
-
legacy[legacy_pos * 2, 2] = str[pos * 2, 2]
|
176
|
-
end
|
177
|
-
legacy
|
178
|
-
end
|
179
|
-
|
180
141
|
# Return the UTC time at which this ObjectID was generated. This may
|
181
142
|
# be used in lieu of a created_at timestamp since this information
|
182
143
|
# is always encoded in the object id.
|
data/lib/mongo/util/bson_c.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# A thin wrapper for the CBson class
|
2
2
|
class BSON_C
|
3
3
|
|
4
|
-
def self.serialize(obj, check_keys=false)
|
5
|
-
ByteBuffer.new(CBson.serialize(obj, check_keys))
|
4
|
+
def self.serialize(obj, check_keys=false, move_id=false)
|
5
|
+
ByteBuffer.new(CBson.serialize(obj, check_keys, move_id))
|
6
6
|
end
|
7
7
|
|
8
8
|
def self.deserialize(buf=nil)
|
data/lib/mongo/util/bson_ruby.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# --
|
2
|
-
# Copyright (C) 2008-
|
2
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
3
3
|
#
|
4
4
|
# This program is free software: you can redistribute it and/or modify it
|
5
5
|
# under the terms of the GNU Affero General Public License, version 3, as
|
@@ -87,15 +87,15 @@ class BSON_RUBY
|
|
87
87
|
|
88
88
|
# Serializes an object.
|
89
89
|
# Implemented to ensure an API compatible with BSON extension.
|
90
|
-
def self.serialize(obj, check_keys=false)
|
91
|
-
new.serialize(obj, check_keys)
|
90
|
+
def self.serialize(obj, check_keys=false, move_id=false)
|
91
|
+
new.serialize(obj, check_keys, move_id)
|
92
92
|
end
|
93
93
|
|
94
94
|
def self.deserialize(buf=nil)
|
95
95
|
new.deserialize(buf)
|
96
96
|
end
|
97
97
|
|
98
|
-
def serialize(obj, check_keys=false)
|
98
|
+
def serialize(obj, check_keys=false, move_id=false)
|
99
99
|
raise "Document is null" unless obj
|
100
100
|
|
101
101
|
@buf.rewind
|
@@ -103,14 +103,20 @@ class BSON_RUBY
|
|
103
103
|
@buf.put_int(0)
|
104
104
|
|
105
105
|
# Write key/value pairs. Always write _id first if it exists.
|
106
|
-
if
|
107
|
-
|
108
|
-
|
109
|
-
|
106
|
+
if move_id
|
107
|
+
if obj.has_key? '_id'
|
108
|
+
serialize_key_value('_id', obj['_id'], false)
|
109
|
+
elsif obj.has_key? :_id
|
110
|
+
serialize_key_value('_id', obj[:_id], false)
|
111
|
+
end
|
112
|
+
obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
|
113
|
+
else
|
114
|
+
if obj.has_key?('_id') && obj.has_key?(:_id)
|
115
|
+
obj['_id'] = obj.delete(:_id)
|
116
|
+
end
|
117
|
+
obj.each {|k, v| serialize_key_value(k, v, check_keys) }
|
110
118
|
end
|
111
119
|
|
112
|
-
obj.each {|k, v| serialize_key_value(k, v, check_keys) unless k == '_id' || k == :_id }
|
113
|
-
|
114
120
|
serialize_eoo_element(@buf)
|
115
121
|
if @buf.size > 4 * 1024 * 1024
|
116
122
|
raise InvalidDocument, "Document is too large (#{@buf.size}). BSON documents are limited to 4MB (#{4 * 1024 * 1024})."
|
@@ -323,7 +329,12 @@ class BSON_RUBY
|
|
323
329
|
options |= Regexp::MULTILINE if options_str.include?('m')
|
324
330
|
options |= Regexp::EXTENDED if options_str.include?('x')
|
325
331
|
options_str.gsub!(/[imx]/, '') # Now remove the three we understand
|
326
|
-
|
332
|
+
if options_str == ''
|
333
|
+
Regexp.new(str, options)
|
334
|
+
else
|
335
|
+
warn("Using deprecated Regexp options #{options_str}; future versions of this MongoDB driver will support only i, m, and x. See deprecated class RegexpOfHolding for more info.")
|
336
|
+
RegexpOfHolding.new(str, options, options_str)
|
337
|
+
end
|
327
338
|
end
|
328
339
|
|
329
340
|
def deserialize_string_data(buf)
|
@@ -1,5 +1,5 @@
|
|
1
1
|
# --
|
2
|
-
# Copyright (C) 2008-
|
2
|
+
# Copyright (C) 2008-2010 10gen Inc.
|
3
3
|
#
|
4
4
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
5
|
# you may not use this file except in compliance with the License.
|
@@ -71,6 +71,11 @@ class OrderedHash < Hash
|
|
71
71
|
end
|
72
72
|
alias :each_pair :each
|
73
73
|
|
74
|
+
def to_a
|
75
|
+
@ordered_keys ||= []
|
76
|
+
@ordered_keys.map { |k| [k, self[k]] }
|
77
|
+
end
|
78
|
+
|
74
79
|
def values
|
75
80
|
collect { |k, v| v }
|
76
81
|
end
|