moped 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of moped might be problematic. Click here for more details.
- data/CHANGELOG.md +39 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/lib/moped/bson.rb +4 -4
- data/lib/moped/bson/binary.rb +96 -30
- data/lib/moped/bson/code.rb +81 -32
- data/lib/moped/bson/document.rb +25 -0
- data/lib/moped/bson/extensions/array.rb +14 -24
- data/lib/moped/bson/extensions/boolean.rb +3 -1
- data/lib/moped/bson/extensions/false_class.rb +2 -1
- data/lib/moped/bson/extensions/float.rb +7 -7
- data/lib/moped/bson/extensions/hash.rb +16 -25
- data/lib/moped/bson/extensions/integer.rb +8 -7
- data/lib/moped/bson/extensions/nil_class.rb +6 -6
- data/lib/moped/bson/extensions/object.rb +2 -4
- data/lib/moped/bson/extensions/regexp.rb +13 -14
- data/lib/moped/bson/extensions/string.rb +8 -9
- data/lib/moped/bson/extensions/symbol.rb +8 -8
- data/lib/moped/bson/extensions/time.rb +9 -7
- data/lib/moped/bson/extensions/true_class.rb +2 -1
- data/lib/moped/bson/max_key.rb +34 -3
- data/lib/moped/bson/min_key.rb +34 -3
- data/lib/moped/bson/object_id.rb +223 -77
- data/lib/moped/bson/timestamp.rb +27 -4
- data/lib/moped/bson/types.rb +5 -5
- data/lib/moped/cluster.rb +21 -4
- data/lib/moped/collection.rb +15 -5
- data/lib/moped/database.rb +1 -1
- data/lib/moped/errors.rb +19 -5
- data/lib/moped/node.rb +23 -31
- data/lib/moped/protocol/query.rb +15 -0
- data/lib/moped/query.rb +31 -9
- data/lib/moped/session.rb +22 -8
- data/lib/moped/sockets/connectable.rb +58 -7
- data/lib/moped/version.rb +1 -1
- metadata +4 -4
data/lib/moped/bson/object_id.rb
CHANGED
@@ -6,83 +6,262 @@ module Moped
|
|
6
6
|
class ObjectId
|
7
7
|
include Comparable
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
def from_data(data)
|
24
|
-
id = allocate
|
25
|
-
id.send(:data=, data)
|
26
|
-
id
|
27
|
-
end
|
9
|
+
# Serialize the object id to its raw bytes.
|
10
|
+
#
|
11
|
+
# @example Serialize the object id.
|
12
|
+
# object_id.__bson_dump__("", "_id")
|
13
|
+
#
|
14
|
+
# @param [ String ] io The raw bytes to write to.
|
15
|
+
# @param [ String ] key The field name.
|
16
|
+
#
|
17
|
+
# @since 1.0.0
|
18
|
+
def __bson_dump__(io, key)
|
19
|
+
io << Types::OBJECT_ID
|
20
|
+
io << key.to_bson_cstring
|
21
|
+
io << data
|
28
22
|
end
|
29
23
|
|
24
|
+
# Check equality on the object.
|
25
|
+
#
|
26
|
+
# @example Check equality.
|
27
|
+
# object === other
|
28
|
+
#
|
29
|
+
# @param [ Object ] other The object to check against.
|
30
|
+
#
|
31
|
+
# @return [ true, false ] If the objects are equal.
|
32
|
+
#
|
33
|
+
# @since 1.0.0
|
30
34
|
def ===(other)
|
31
35
|
return to_str === other.to_str if other.respond_to?(:to_str)
|
32
36
|
super
|
33
37
|
end
|
34
38
|
|
39
|
+
# Check equality on the object.
|
40
|
+
#
|
41
|
+
# @example Check equality.
|
42
|
+
# object == other
|
43
|
+
#
|
44
|
+
# @param [ Object ] other The object to check against.
|
45
|
+
#
|
46
|
+
# @return [ true, false ] If the objects are equal.
|
47
|
+
#
|
48
|
+
# @since 1.0.0
|
49
|
+
def ==(other)
|
50
|
+
BSON::ObjectId === other && data == other.data
|
51
|
+
end
|
52
|
+
alias :eql? :==
|
53
|
+
|
54
|
+
# Compare this object with another object, used in sorting.
|
55
|
+
#
|
56
|
+
# @example Compare the two objects.
|
57
|
+
# object <=> other
|
58
|
+
#
|
59
|
+
# @param [ Object ] other The object to compare to.
|
60
|
+
#
|
61
|
+
# @return [ Integer ] The result of the comparison.
|
62
|
+
#
|
63
|
+
# @since 1.0.0
|
64
|
+
def <=>(other)
|
65
|
+
data <=> other.data
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get the raw data (bytes) for the object id.
|
69
|
+
#
|
70
|
+
# @example Get the raw data.
|
71
|
+
# object_id.data
|
72
|
+
#
|
73
|
+
# @return [ String ] The raw bytes.
|
74
|
+
#
|
75
|
+
# @since 1.0.0
|
35
76
|
def data
|
36
77
|
# If @data is defined, then we know we've been loaded in some
|
37
78
|
# non-standard way, so we attempt to repair the data.
|
38
79
|
repair! @data if defined? @data
|
39
|
-
|
40
80
|
@raw_data ||= @@generator.next
|
41
81
|
end
|
42
82
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
83
|
+
# Return the UTC time at which this ObjectId was generated. This may
|
84
|
+
# be used instread of a created_at timestamp since this information
|
85
|
+
# is always encoded in the object id.
|
86
|
+
#
|
87
|
+
# @example Get the generation time.
|
88
|
+
# object_id.generation_time
|
89
|
+
#
|
90
|
+
# @return [ Time ] The time the id was generated.
|
91
|
+
#
|
92
|
+
# @since 1.0.0
|
93
|
+
def generation_time
|
94
|
+
Time.at(data.unpack("N")[0]).utc
|
50
95
|
end
|
51
96
|
|
97
|
+
# Gets the hash code for the object.
|
98
|
+
#
|
99
|
+
# @example Get the hash code.
|
100
|
+
# object.hash
|
101
|
+
#
|
102
|
+
# @return [ Fixnum ] The hash code.
|
103
|
+
#
|
104
|
+
# @since 1.0.0
|
52
105
|
def hash
|
53
106
|
data.hash
|
54
107
|
end
|
55
108
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
109
|
+
# Gets the string inspection for the object.
|
110
|
+
#
|
111
|
+
# @example Get the string inspection.
|
112
|
+
# object.inspect
|
113
|
+
#
|
114
|
+
# @return [ String ] The inspection.
|
115
|
+
#
|
116
|
+
# @since 1.0.0
|
61
117
|
def inspect
|
62
118
|
to_s.inspect
|
63
119
|
end
|
64
120
|
|
121
|
+
# Dump the object for use in a marshal dump.
|
122
|
+
#
|
123
|
+
# @example Dump the object.
|
124
|
+
# object.marshal_dump
|
125
|
+
#
|
126
|
+
# @return [ String ] The dumped object.
|
127
|
+
#
|
128
|
+
# @since 1.0.0
|
129
|
+
def marshal_dump
|
130
|
+
data
|
131
|
+
end
|
132
|
+
|
133
|
+
# Load the object from the marshal dump.
|
134
|
+
#
|
135
|
+
# @example Load the object.
|
136
|
+
# object.marshal_load("")
|
137
|
+
#
|
138
|
+
# @param [ String ] data The raw data.
|
139
|
+
#
|
140
|
+
# @since 1.0.0
|
141
|
+
def marshal_load(data)
|
142
|
+
self.data = data
|
143
|
+
end
|
144
|
+
|
145
|
+
# Convert the object to a JSON string.
|
146
|
+
#
|
147
|
+
# @example Convert to a JSON string.
|
148
|
+
# obejct.to_json
|
149
|
+
#
|
150
|
+
# @return [ String ] The object as JSON.
|
151
|
+
#
|
152
|
+
# @since 1.0.0
|
65
153
|
def to_json(*args)
|
66
154
|
"{\"$oid\": \"#{to_s}\"}"
|
67
155
|
end
|
68
156
|
|
69
|
-
#
|
70
|
-
#
|
71
|
-
#
|
72
|
-
|
73
|
-
|
157
|
+
# Get the string representation of the object.
|
158
|
+
#
|
159
|
+
# @example Get the string representation.
|
160
|
+
# object.to_s
|
161
|
+
#
|
162
|
+
# @return [ String ] The string representation.
|
163
|
+
#
|
164
|
+
# @since 1.0.0
|
165
|
+
def to_s
|
166
|
+
data.unpack("H*")[0].force_encoding(UTF8_ENCODING)
|
167
|
+
end
|
168
|
+
alias :to_str :to_s
|
169
|
+
|
170
|
+
private
|
171
|
+
|
172
|
+
# Private interface for setting the internal data for an object id.
|
173
|
+
def data=(data)
|
174
|
+
@raw_data = data
|
175
|
+
end
|
176
|
+
|
177
|
+
# Attempts to repair ObjectId data marshalled in previous formats.
|
178
|
+
#
|
179
|
+
# The first check covers an ObjectId generated by the mongo-ruby-driver.
|
180
|
+
#
|
181
|
+
# The second check covers an ObjectId generated by moped before a custom
|
182
|
+
# marshal strategy was added.
|
183
|
+
def repair!(data)
|
184
|
+
if data.is_a?(Array) && data.size == 12
|
185
|
+
self.data = data.pack("C*")
|
186
|
+
elsif data.is_a?(String) && data.size == 12
|
187
|
+
self.data = data
|
188
|
+
else
|
189
|
+
raise TypeError, "Could not convert #{data.inspect} into an ObjectId"
|
190
|
+
end
|
74
191
|
end
|
75
192
|
|
76
193
|
class << self
|
194
|
+
|
77
195
|
def __bson_load__(io)
|
78
196
|
from_data(io.read(12))
|
79
197
|
end
|
80
|
-
end
|
81
198
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
199
|
+
# Create a new object id from a string.
|
200
|
+
#
|
201
|
+
# @example Create an object id from the string.
|
202
|
+
# Moped::BSON::ObjectId.from_string(id)
|
203
|
+
#
|
204
|
+
# @param [ String ] string The string to create from.
|
205
|
+
#
|
206
|
+
# @return [ ObjectId ] The new object id.
|
207
|
+
#
|
208
|
+
# @since 1.0.0
|
209
|
+
def from_string(string)
|
210
|
+
raise Errors::InvalidObjectId.new(string) unless legal?(string)
|
211
|
+
from_data [string].pack("H*")
|
212
|
+
end
|
213
|
+
|
214
|
+
# Create a new object id from a time.
|
215
|
+
#
|
216
|
+
# @example Create an object id from a time.
|
217
|
+
# Moped::BSON::ObjectId.from_id(time)
|
218
|
+
#
|
219
|
+
# @example Create an object id from a time, ensuring uniqueness.
|
220
|
+
# Moped::BSON::ObjectId.from_id(time, unique: true)
|
221
|
+
#
|
222
|
+
# @param [ Time ] time The time to generate from.
|
223
|
+
# @param [ Hash ] options The options.
|
224
|
+
#
|
225
|
+
# @option options [ true, false ] :unique Whether the id should be
|
226
|
+
# unique.
|
227
|
+
#
|
228
|
+
# @return [ ObjectId ] The new object id.
|
229
|
+
#
|
230
|
+
# @since 1.0.0
|
231
|
+
def from_time(time, options = nil)
|
232
|
+
unique = (options || {})[:unique]
|
233
|
+
from_data(unique ? @@generator.next(time.to_i) : [ time.to_i ].pack("Nx8"))
|
234
|
+
end
|
235
|
+
|
236
|
+
# Determine if the string is a legal object id.
|
237
|
+
#
|
238
|
+
# @example Is the string a legal object id?
|
239
|
+
# Moped::BSON::ObjectId.legal?(string)
|
240
|
+
#
|
241
|
+
# @param [ String ] The string to test.
|
242
|
+
#
|
243
|
+
# @return [ true, false ] If the string is legal.
|
244
|
+
#
|
245
|
+
# @since 1.0.0
|
246
|
+
def legal?(string)
|
247
|
+
/\A\h{24}\Z/ === string.to_s
|
248
|
+
end
|
249
|
+
|
250
|
+
# Create a new object id from some raw data.
|
251
|
+
#
|
252
|
+
# @example Create an object id from raw data.
|
253
|
+
# Moped::BSON::ObjectId.from_data(data)
|
254
|
+
#
|
255
|
+
# @param [ String ] data The raw bytes.
|
256
|
+
#
|
257
|
+
# @return [ ObjectId ] The new object id.
|
258
|
+
#
|
259
|
+
# @since 1.0.0
|
260
|
+
def from_data(data)
|
261
|
+
id = allocate
|
262
|
+
id.send(:data=, data)
|
263
|
+
id
|
264
|
+
end
|
86
265
|
end
|
87
266
|
|
88
267
|
# @api private
|
@@ -98,7 +277,7 @@ module Moped
|
|
98
277
|
|
99
278
|
# Return object id data based on the current time, incrementing the
|
100
279
|
# object id counter.
|
101
|
-
def next
|
280
|
+
def next(time = nil)
|
102
281
|
@mutex.lock
|
103
282
|
begin
|
104
283
|
counter = @counter = (@counter + 1) % 0xFFFFFF
|
@@ -106,7 +285,7 @@ module Moped
|
|
106
285
|
@mutex.unlock rescue nil
|
107
286
|
end
|
108
287
|
|
109
|
-
generate(Time.new.to_i, counter)
|
288
|
+
generate(time || Time.new.to_i, counter)
|
110
289
|
end
|
111
290
|
|
112
291
|
# Generate object id data for a given time using the provided +counter+.
|
@@ -117,39 +296,6 @@ module Moped
|
|
117
296
|
end
|
118
297
|
|
119
298
|
@@generator = Generator.new
|
120
|
-
|
121
|
-
# @private
|
122
|
-
def marshal_dump
|
123
|
-
data
|
124
|
-
end
|
125
|
-
|
126
|
-
# @private
|
127
|
-
def marshal_load(data)
|
128
|
-
self.data = data
|
129
|
-
end
|
130
|
-
|
131
|
-
private
|
132
|
-
|
133
|
-
# Attempts to repair ObjectId data marshalled in previous formats.
|
134
|
-
#
|
135
|
-
# The first check covers an ObjectId generated by the mongo-ruby-driver.
|
136
|
-
#
|
137
|
-
# The second check covers an ObjectId generated by moped before a custom
|
138
|
-
# marshal strategy was added.
|
139
|
-
def repair!(data)
|
140
|
-
if data.is_a?(Array) && data.size == 12
|
141
|
-
self.data = data.pack("C*")
|
142
|
-
elsif data.is_a?(String) && data.size == 12
|
143
|
-
self.data = data
|
144
|
-
else
|
145
|
-
raise TypeError, "Could not convert #{data.inspect} into an ObjectId"
|
146
|
-
end
|
147
|
-
end
|
148
|
-
|
149
|
-
# Private interface for setting the internal data for an object id.
|
150
|
-
def data=(data)
|
151
|
-
@raw_data = data
|
152
|
-
end
|
153
299
|
end
|
154
300
|
end
|
155
301
|
end
|
data/lib/moped/bson/timestamp.rb
CHANGED
@@ -1,15 +1,38 @@
|
|
1
1
|
module Moped
|
2
2
|
module BSON
|
3
|
+
|
4
|
+
# A time representation in BSON.
|
3
5
|
class Timestamp < Struct.new(:seconds, :increment)
|
6
|
+
|
7
|
+
# Serialize the time to the stream.
|
8
|
+
#
|
9
|
+
# @example Serialize the time.
|
10
|
+
# time.__bson_dump__("", "created_at")
|
11
|
+
#
|
12
|
+
# @param [ String ] io The raw bytes.
|
13
|
+
# @param [ String ] key The field name.
|
14
|
+
#
|
15
|
+
# @since 1.0.0
|
16
|
+
def __bson_dump__(io, key)
|
17
|
+
io << [17, key, increment, seconds].pack('cZ*l2')
|
18
|
+
end
|
19
|
+
|
4
20
|
class << self
|
21
|
+
|
22
|
+
# Deserialize the timestamp to an object.
|
23
|
+
#
|
24
|
+
# @example Deserialize the time.
|
25
|
+
# Moped::BSON::Timestamp.__bson_load__(string)
|
26
|
+
#
|
27
|
+
# @param [ String ] io The raw bytes.
|
28
|
+
#
|
29
|
+
# @return [ Timestamp ] The time.
|
30
|
+
#
|
31
|
+
# @since 1.0.0
|
5
32
|
def __bson_load__(io)
|
6
33
|
new(*io.read(8).unpack('l2').reverse)
|
7
34
|
end
|
8
35
|
end
|
9
|
-
|
10
|
-
def __bson_dump__(io, key)
|
11
|
-
io << [17, key, increment, seconds].pack('cZ*l2')
|
12
|
-
end
|
13
36
|
end
|
14
37
|
end
|
15
38
|
end
|
data/lib/moped/bson/types.rb
CHANGED
@@ -1,20 +1,21 @@
|
|
1
1
|
module Moped
|
2
2
|
module BSON
|
3
3
|
|
4
|
-
#
|
4
|
+
# Various BSON type behaviour.
|
5
5
|
module Types
|
6
|
+
|
6
7
|
class CodeWithScope
|
8
|
+
|
7
9
|
def self.__bson_load__(io)
|
8
10
|
io.read 4 # swallow the length
|
9
|
-
|
10
11
|
code = io.read(*io.read(4).unpack(INT32_PACK)).from_utf8_binary.chop!
|
11
12
|
scope = BSON::Document.deserialize(io)
|
12
|
-
|
13
|
-
Code.new code, scope
|
13
|
+
Code.new(code, scope)
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
17
|
class Integer64
|
18
|
+
|
18
19
|
def self.__bson_load__(io)
|
19
20
|
io.read(8).unpack(INT64_PACK)[0]
|
20
21
|
end
|
@@ -61,7 +62,6 @@ module Moped
|
|
61
62
|
MIN_KEY = 255.chr.freeze
|
62
63
|
|
63
64
|
TRUE = 1.chr.freeze
|
64
|
-
|
65
65
|
end
|
66
66
|
end
|
67
67
|
end
|
data/lib/moped/cluster.rb
CHANGED
@@ -100,6 +100,7 @@ module Moped
|
|
100
100
|
def initialize(hosts, options)
|
101
101
|
@seeds = hosts
|
102
102
|
@nodes = hosts.map { |host| Node.new(host, options) }
|
103
|
+
@peers = []
|
103
104
|
|
104
105
|
@options = {
|
105
106
|
down_interval: 30,
|
@@ -135,7 +136,9 @@ module Moped
|
|
135
136
|
|
136
137
|
# Now return all the nodes that are available and participating in the
|
137
138
|
# replica set.
|
138
|
-
available.reject
|
139
|
+
available.reject do |node|
|
140
|
+
node.down? || !member?(node) || (!opts[:include_arbiters] && node.arbiter?)
|
141
|
+
end
|
139
142
|
end
|
140
143
|
|
141
144
|
# Refreshes information for each of the nodes provided. The node list
|
@@ -170,10 +173,11 @@ module Moped
|
|
170
173
|
# This node is good, so add it to the list of nodes to return.
|
171
174
|
refreshed_nodes << node unless refreshed_nodes.include?(node)
|
172
175
|
|
173
|
-
# Now refresh any newly discovered peer nodes
|
174
|
-
|
176
|
+
# Now refresh any newly discovered peer nodes - this will also
|
177
|
+
# remove nodes that are not included in the peer list.
|
178
|
+
refresh_peers(node, &refresh_node)
|
175
179
|
rescue Errors::ConnectionFailure
|
176
|
-
# We couldn't connect to the node
|
180
|
+
# We couldn't connect to the node.
|
177
181
|
end
|
178
182
|
end
|
179
183
|
end
|
@@ -273,5 +277,18 @@ module Moped
|
|
273
277
|
def initialize_copy(_)
|
274
278
|
@nodes = @nodes.map(&:dup)
|
275
279
|
end
|
280
|
+
|
281
|
+
def member?(node)
|
282
|
+
@peers.empty? || @peers.include?(node)
|
283
|
+
end
|
284
|
+
|
285
|
+
def refresh_peers(node, &block)
|
286
|
+
peers = node.peers
|
287
|
+
return if peers.empty?
|
288
|
+
peers.each do |node|
|
289
|
+
block.call(node) unless @nodes.include?(node)
|
290
|
+
@peers.push(node) unless peers.include?(node)
|
291
|
+
end
|
292
|
+
end
|
276
293
|
end
|
277
294
|
end
|