moped 1.0.0.beta → 1.0.0.rc
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.
Potentially problematic release.
This version of moped might be problematic. Click here for more details.
- data/LICENSE +20 -0
- data/README.md +11 -8
- data/lib/moped/bson.rb +17 -0
- data/lib/moped/bson/extensions/hash.rb +2 -2
- data/lib/moped/bson/extensions/regexp.rb +2 -2
- data/lib/moped/bson/extensions/symbol.rb +13 -3
- data/lib/moped/cluster.rb +96 -39
- data/lib/moped/collection.rb +54 -31
- data/lib/moped/connection.rb +152 -47
- data/lib/moped/cursor.rb +91 -34
- data/lib/moped/database.rb +52 -20
- data/lib/moped/errors.rb +52 -22
- data/lib/moped/indexes.rb +53 -27
- data/lib/moped/logging.rb +35 -2
- data/lib/moped/node.rb +316 -126
- data/lib/moped/protocol/delete.rb +1 -0
- data/lib/moped/protocol/get_more.rb +1 -0
- data/lib/moped/protocol/insert.rb +1 -0
- data/lib/moped/protocol/kill_cursors.rb +1 -0
- data/lib/moped/protocol/message.rb +0 -2
- data/lib/moped/protocol/query.rb +1 -0
- data/lib/moped/protocol/update.rb +1 -0
- data/lib/moped/query.rb +183 -113
- data/lib/moped/session.rb +148 -96
- data/lib/moped/threaded.rb +41 -5
- data/lib/moped/version.rb +1 -1
- metadata +5 -5
- data/MIT_LICENSE +0 -19
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2011-2012 Bernerd Schaefer
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,6 +1,17 @@
|
|
1
|
+
Moped [](http://travis-ci.org/mongoid/moped)
|
2
|
+
========
|
3
|
+
|
1
4
|
Moped is a MongoDB driver for Ruby, which exposes a simple, elegant, and fast
|
2
5
|
API.
|
3
6
|
|
7
|
+
Compatibility
|
8
|
+
-------------
|
9
|
+
|
10
|
+
Moped is tested against MRI 1.9.2, 1.9.3, 2.0.0, and JRuby (1.9).
|
11
|
+
|
12
|
+
Usage
|
13
|
+
-----
|
14
|
+
|
4
15
|
```ruby
|
5
16
|
session = Moped::Session.new %w[127.0.0.1:27017]
|
6
17
|
session.use "echo_test"
|
@@ -473,11 +484,3 @@ failover. We've decided that, for now, it's not worth making the replica set
|
|
473
484
|
code thread-safe.
|
474
485
|
|
475
486
|
**TL;DR**: use one `Moped::Session` instance per thread.
|
476
|
-
|
477
|
-
# Compatibility
|
478
|
-
|
479
|
-
Moped is tested against MRI 1.9.2, 1.9.3, 2.0.0, and JRuby (1.9).
|
480
|
-
|
481
|
-
<img src="https://secure.travis-ci.org/mongoid/moped.png?branch=master&.png"/>
|
482
|
-
|
483
|
-
[Build History](http://travis-ci.org/mongoid/moped)
|
data/lib/moped/bson.rb
CHANGED
@@ -21,5 +21,22 @@ module Moped
|
|
21
21
|
FLOAT_PACK = 'E'.freeze
|
22
22
|
|
23
23
|
START_LENGTH = [0].pack(INT32_PACK).freeze
|
24
|
+
|
25
|
+
class << self
|
26
|
+
|
27
|
+
# Create a new object id from the provided string.
|
28
|
+
#
|
29
|
+
# @example Create a new object id.
|
30
|
+
# Moped::BSON::ObjectId("4faf83c7dbf89b7b29000001")
|
31
|
+
#
|
32
|
+
# @param [ String ] string The string to use.
|
33
|
+
#
|
34
|
+
# @return [ ObjectId ] The object id.
|
35
|
+
#
|
36
|
+
# @since 1.0.0
|
37
|
+
def ObjectId(string)
|
38
|
+
ObjectId.from_string(string)
|
39
|
+
end
|
40
|
+
end
|
24
41
|
end
|
25
42
|
end
|
@@ -10,7 +10,7 @@ module Moped
|
|
10
10
|
io.read 4
|
11
11
|
|
12
12
|
while (buf = io.readbyte) != 0
|
13
|
-
key = io.gets(NULL_BYTE).chop
|
13
|
+
key = io.gets(NULL_BYTE).chop!
|
14
14
|
|
15
15
|
if native_class = Types::MAP[buf]
|
16
16
|
doc[key] = native_class.__bson_load__(io)
|
@@ -34,7 +34,7 @@ module Moped
|
|
34
34
|
io << START_LENGTH
|
35
35
|
|
36
36
|
each do |k, v|
|
37
|
-
v.__bson_dump__(io, k.to_s
|
37
|
+
v.__bson_dump__(io, k.to_s)
|
38
38
|
end
|
39
39
|
io << EOD
|
40
40
|
|
@@ -5,7 +5,7 @@ module Moped
|
|
5
5
|
module Regexp
|
6
6
|
module ClassMethods
|
7
7
|
def __bson_load__(io)
|
8
|
-
source = io.gets(NULL_BYTE).chop
|
8
|
+
source = io.gets(NULL_BYTE).chop!
|
9
9
|
options = 0
|
10
10
|
while (option = io.getbyte) != 0
|
11
11
|
case option
|
@@ -26,7 +26,7 @@ module Moped
|
|
26
26
|
io << Types::REGEX
|
27
27
|
io << key
|
28
28
|
io << NULL_BYTE
|
29
|
-
io << source
|
29
|
+
io << source
|
30
30
|
io << NULL_BYTE
|
31
31
|
|
32
32
|
io << 'i' if (options & ::Regexp::IGNORECASE) != 0
|
@@ -14,9 +14,19 @@ module Moped
|
|
14
14
|
io << key
|
15
15
|
io << NULL_BYTE
|
16
16
|
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
begin
|
18
|
+
data = to_s.encode('utf-8')
|
19
|
+
rescue EncodingError
|
20
|
+
data = to_s.dup
|
21
|
+
data.force_encoding('utf-8')
|
22
|
+
|
23
|
+
raise unless data.valid_encoding?
|
24
|
+
end
|
25
|
+
|
26
|
+
data.force_encoding('binary')
|
27
|
+
|
28
|
+
io << [data.bytesize+1].pack(INT32_PACK)
|
29
|
+
io << data
|
20
30
|
io << NULL_BYTE
|
21
31
|
end
|
22
32
|
end
|
data/lib/moped/cluster.rb
CHANGED
@@ -1,15 +1,37 @@
|
|
1
1
|
module Moped
|
2
2
|
|
3
|
+
# The cluster represents a cluster of MongoDB server nodes, either a single
|
4
|
+
# node, a replica set, or a mongos server.
|
3
5
|
class Cluster
|
4
6
|
|
5
|
-
# @
|
7
|
+
# @attribute [r] seeds The seeds the cluster was initialized with.
|
6
8
|
attr_reader :seeds
|
7
9
|
|
8
|
-
#
|
9
|
-
#
|
10
|
+
# Get the authentication details for the cluster.
|
11
|
+
#
|
12
|
+
# @example Get the authentication details.
|
13
|
+
# cluster.auth
|
14
|
+
#
|
15
|
+
# @return [ Hash ] the cached authentication credentials for this cluster.
|
16
|
+
#
|
17
|
+
# @since 1.0.0
|
18
|
+
def auth
|
19
|
+
@auth ||= {}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Initialize the new cluster.
|
23
|
+
#
|
24
|
+
# @example Initialize the cluster.
|
25
|
+
# Cluster.new([ "localhost:27017" ], down_interval: 20)
|
10
26
|
#
|
27
|
+
# @param [ Hash ] options The cluster options.
|
28
|
+
#
|
29
|
+
# @option options :down_interval number of seconds to wait before attempting
|
30
|
+
# to reconnect to a down node. (30)
|
11
31
|
# @option options :refresh_interval number of seconds to cache information
|
12
|
-
#
|
32
|
+
# about a node. (300)
|
33
|
+
#
|
34
|
+
# @since 1.0.0
|
13
35
|
def initialize(hosts, options)
|
14
36
|
@options = {
|
15
37
|
down_interval: 30,
|
@@ -20,13 +42,45 @@ module Moped
|
|
20
42
|
@nodes = hosts.map { |host| Node.new(host) }
|
21
43
|
end
|
22
44
|
|
45
|
+
# Returns the list of available nodes, refreshing 1) any nodes which were
|
46
|
+
# down and ready to be checked again and 2) any nodes whose information is
|
47
|
+
# out of date.
|
48
|
+
#
|
49
|
+
# @example Get the available nodes.
|
50
|
+
# cluster.nodes
|
51
|
+
#
|
52
|
+
# @return [ Array<Node> ] the list of available nodes.
|
53
|
+
#
|
54
|
+
# @since 1.0.0
|
55
|
+
def nodes
|
56
|
+
# Find the nodes that were down but are ready to be refreshed, or those
|
57
|
+
# with stale connection information.
|
58
|
+
needs_refresh, available = @nodes.partition do |node|
|
59
|
+
(node.down? && node.down_at < (Time.new - @options[:down_interval])) ||
|
60
|
+
node.needs_refresh?(Time.new - @options[:refresh_interval])
|
61
|
+
end
|
62
|
+
|
63
|
+
# Refresh those nodes.
|
64
|
+
available.concat refresh(needs_refresh)
|
65
|
+
|
66
|
+
# Now return all the nodes that are available.
|
67
|
+
available.reject(&:down?)
|
68
|
+
end
|
69
|
+
|
23
70
|
# Refreshes information for each of the nodes provided. The node list
|
24
71
|
# defaults to the list of all known nodes.
|
25
72
|
#
|
26
73
|
# If a node is successfully refreshed, any newly discovered peers will also
|
27
74
|
# be refreshed.
|
28
75
|
#
|
29
|
-
# @
|
76
|
+
# @example Refresh the nodes.
|
77
|
+
# cluster.refresh
|
78
|
+
#
|
79
|
+
# @param [ Array<Node> ] nodes_to_refresh The nodes to refresh.
|
80
|
+
#
|
81
|
+
# @return [ Array<Node> ] the available nodes
|
82
|
+
#
|
83
|
+
# @since 1.0.0
|
30
84
|
def refresh(nodes_to_refresh = @nodes)
|
31
85
|
refreshed_nodes = []
|
32
86
|
seen = {}
|
@@ -46,43 +100,34 @@ module Moped
|
|
46
100
|
refreshed_nodes << node unless refreshed_nodes.include?(node)
|
47
101
|
|
48
102
|
# Now refresh any newly discovered peer nodes.
|
49
|
-
(node.peers - @nodes).each
|
103
|
+
(node.peers - @nodes).each(&refresh_node)
|
50
104
|
rescue Errors::ConnectionFailure
|
51
105
|
# We couldn't connect to the node, so don't do anything with it.
|
52
106
|
end
|
53
107
|
end
|
54
108
|
end
|
55
109
|
|
56
|
-
nodes_to_refresh.each
|
57
|
-
|
110
|
+
nodes_to_refresh.each(&refresh_node)
|
58
111
|
refreshed_nodes.to_a
|
59
112
|
end
|
60
113
|
|
61
|
-
# Returns the list of available nodes, refreshing 1) any nodes which were
|
62
|
-
# down and ready to be checked again and 2) any nodes whose information is
|
63
|
-
# out of date.
|
64
|
-
#
|
65
|
-
# @return [Array<Node>] the list of available nodes.
|
66
|
-
def nodes
|
67
|
-
# Find the nodes that were down but are ready to be refreshed, or those
|
68
|
-
# with stale connection information.
|
69
|
-
needs_refresh, available = @nodes.partition do |node|
|
70
|
-
(node.down? && node.down_at < (Time.new - @options[:down_interval])) ||
|
71
|
-
node.needs_refresh?(Time.new - @options[:refresh_interval])
|
72
|
-
end
|
73
|
-
|
74
|
-
# Refresh those nodes.
|
75
|
-
available.concat refresh(needs_refresh)
|
76
|
-
|
77
|
-
# Now return all the nodes that are available.
|
78
|
-
available.reject &:down?
|
79
|
-
end
|
80
|
-
|
81
114
|
# Yields the replica set's primary node to the provided block. This method
|
82
115
|
# will retry the block in case of connection errors or replica set
|
83
116
|
# reconfiguration.
|
84
117
|
#
|
85
|
-
# @
|
118
|
+
# @example Yield the primary to the block.
|
119
|
+
# cluster.with_primary do |node|
|
120
|
+
# # ...
|
121
|
+
# end
|
122
|
+
#
|
123
|
+
# @param [ true, false ] retry_on_failure Whether to retry if an error was
|
124
|
+
# raised.
|
125
|
+
#
|
126
|
+
# @raises [ ConnectionFailure ] When no primary node can be found
|
127
|
+
#
|
128
|
+
# @return [ Object ] The result of the yield.
|
129
|
+
#
|
130
|
+
# @since 1.0.0
|
86
131
|
def with_primary(retry_on_failure = true, &block)
|
87
132
|
if node = nodes.find(&:primary?)
|
88
133
|
begin
|
@@ -100,14 +145,29 @@ module Moped
|
|
100
145
|
refresh
|
101
146
|
with_primary(false, &block)
|
102
147
|
else
|
103
|
-
raise
|
148
|
+
raise(
|
149
|
+
Errors::ConnectionFailure,
|
150
|
+
"Could not connect to a primary node for replica set #{inspect}"
|
151
|
+
)
|
104
152
|
end
|
105
153
|
end
|
106
154
|
|
107
155
|
# Yields a secondary node if available, otherwise the primary node. This
|
108
156
|
# method will retry the block in case of connection errors.
|
109
157
|
#
|
110
|
-
# @
|
158
|
+
# @example Yield the secondary to the block.
|
159
|
+
# cluster.with_secondary do |node|
|
160
|
+
# # ...
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# @param [ true, false ] retry_on_failure Whether to retry if an error was
|
164
|
+
# raised.
|
165
|
+
#
|
166
|
+
# @raises [ ConnectionFailure ] When no primary node can be found
|
167
|
+
#
|
168
|
+
# @return [ Object ] The result of the yield.
|
169
|
+
#
|
170
|
+
# @since 1.0.0
|
111
171
|
def with_secondary(retry_on_failure = true, &block)
|
112
172
|
available_nodes = nodes.shuffle!.partition(&:secondary?).flatten
|
113
173
|
|
@@ -126,20 +186,17 @@ module Moped
|
|
126
186
|
refresh
|
127
187
|
with_secondary(false, &block)
|
128
188
|
else
|
129
|
-
raise
|
189
|
+
raise(
|
190
|
+
Errors::ConnectionFailure,
|
191
|
+
"Could not connect to any secondary or primary nodes for replica set #{inspect}"
|
192
|
+
)
|
130
193
|
end
|
131
194
|
end
|
132
195
|
|
133
|
-
# @return [Hash] the cached authentication credentials for this cluster.
|
134
|
-
def auth
|
135
|
-
@auth ||= {}
|
136
|
-
end
|
137
|
-
|
138
196
|
private
|
139
197
|
|
140
198
|
def initialize_copy(_)
|
141
|
-
@nodes = @nodes.map
|
199
|
+
@nodes = @nodes.map(&:dup)
|
142
200
|
end
|
143
|
-
|
144
201
|
end
|
145
202
|
end
|
data/lib/moped/collection.rb
CHANGED
@@ -9,54 +9,77 @@ module Moped
|
|
9
9
|
# users.find.to_a # => [{ name: "John" }]
|
10
10
|
class Collection
|
11
11
|
|
12
|
-
# @
|
13
|
-
|
12
|
+
# @attribute [r] database The collection's database.
|
13
|
+
# @attribute [r] name The collection name.
|
14
|
+
attr_reader :database, :name
|
14
15
|
|
15
|
-
#
|
16
|
-
|
16
|
+
# Drop the collection.
|
17
|
+
#
|
18
|
+
# @example Drop the collection.
|
19
|
+
# collection.drop
|
20
|
+
#
|
21
|
+
# @return [ Hash ] The command information.
|
22
|
+
#
|
23
|
+
# @since 1.0.0
|
24
|
+
def drop
|
25
|
+
database.command(drop: name)
|
26
|
+
end
|
17
27
|
|
18
|
-
#
|
19
|
-
#
|
20
|
-
|
21
|
-
|
22
|
-
|
28
|
+
# Build a query for this collection.
|
29
|
+
#
|
30
|
+
# @example Build a query based on the provided selector.
|
31
|
+
# collection.find(name: "Placebo")
|
32
|
+
#
|
33
|
+
# @param [ Hash ] selector The query selector.
|
34
|
+
#
|
35
|
+
# @return [ Query ] The generated query.
|
36
|
+
#
|
37
|
+
# @since 1.0.0
|
38
|
+
def find(selector = {})
|
39
|
+
Query.new(self, selector)
|
23
40
|
end
|
41
|
+
alias :where :find
|
24
42
|
|
25
43
|
# Access information about this collection's indexes.
|
26
44
|
#
|
27
|
-
# @
|
45
|
+
# @example Get the index information.
|
46
|
+
# collection.indexes
|
47
|
+
#
|
48
|
+
# @return [ Indexes ] The index information.
|
49
|
+
#
|
50
|
+
# @since 1.0.0
|
28
51
|
def indexes
|
29
52
|
Indexes.new(database, name)
|
30
53
|
end
|
31
54
|
|
32
|
-
#
|
33
|
-
def drop
|
34
|
-
database.command drop: name
|
35
|
-
end
|
36
|
-
|
37
|
-
# Build a query for this collection.
|
55
|
+
# Initialize the new collection.
|
38
56
|
#
|
39
|
-
# @
|
40
|
-
#
|
41
|
-
|
42
|
-
|
57
|
+
# @example Initialize the collection.
|
58
|
+
# Collection.new(database, :artists)
|
59
|
+
#
|
60
|
+
# @param [ Database ] database The collection's database.
|
61
|
+
# @param [ String, Symbol] name The collection name.
|
62
|
+
#
|
63
|
+
# @since 1.0.0
|
64
|
+
def initialize(database, name)
|
65
|
+
@database, @name = database, name
|
43
66
|
end
|
44
|
-
alias where find
|
45
67
|
|
46
68
|
# Insert one or more documents into the collection.
|
47
69
|
#
|
48
|
-
# @
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
70
|
+
# @example Insert a single document.
|
71
|
+
# db[:people].insert(name: "John")
|
72
|
+
#
|
73
|
+
# @example Insert multiple documents in batch.
|
74
|
+
# db[:people].insert([{name: "John"}, {name: "Joe"}])
|
52
75
|
#
|
53
|
-
# @
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
76
|
+
# @param [ Hash, Array<Hash> ] documents The document(s) to insert.
|
77
|
+
#
|
78
|
+
# @return [ nil ] nil.
|
79
|
+
#
|
80
|
+
# @since 1.0.0
|
57
81
|
def insert(documents)
|
58
|
-
documents = [documents] unless documents.is_a?
|
59
|
-
|
82
|
+
documents = [documents] unless documents.is_a?(Array)
|
60
83
|
database.session.with(consistency: :strong) do |session|
|
61
84
|
session.context.insert(database.name, name, documents)
|
62
85
|
end
|