moped 0.0.0.beta → 1.0.0.alpha
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/MIT_LICENSE +19 -0
- data/README.md +323 -0
- data/lib/moped.rb +19 -0
- data/lib/moped/bson.rb +25 -0
- data/lib/moped/bson/binary.rb +68 -0
- data/lib/moped/bson/code.rb +61 -0
- data/lib/moped/bson/document.rb +16 -0
- data/lib/moped/bson/extensions.rb +81 -0
- data/lib/moped/bson/extensions/array.rb +44 -0
- data/lib/moped/bson/extensions/boolean.rb +14 -0
- data/lib/moped/bson/extensions/false_class.rb +15 -0
- data/lib/moped/bson/extensions/float.rb +23 -0
- data/lib/moped/bson/extensions/hash.rb +49 -0
- data/lib/moped/bson/extensions/integer.rb +37 -0
- data/lib/moped/bson/extensions/nil_class.rb +20 -0
- data/lib/moped/bson/extensions/regexp.rb +40 -0
- data/lib/moped/bson/extensions/string.rb +35 -0
- data/lib/moped/bson/extensions/symbol.rb +25 -0
- data/lib/moped/bson/extensions/time.rb +21 -0
- data/lib/moped/bson/extensions/true_class.rb +15 -0
- data/lib/moped/bson/max_key.rb +21 -0
- data/lib/moped/bson/min_key.rb +21 -0
- data/lib/moped/bson/object_id.rb +123 -0
- data/lib/moped/bson/timestamp.rb +15 -0
- data/lib/moped/bson/types.rb +67 -0
- data/lib/moped/cluster.rb +193 -0
- data/lib/moped/collection.rb +67 -0
- data/lib/moped/cursor.rb +60 -0
- data/lib/moped/database.rb +76 -0
- data/lib/moped/errors.rb +61 -0
- data/lib/moped/indexes.rb +93 -0
- data/lib/moped/logging.rb +25 -0
- data/lib/moped/protocol.rb +20 -0
- data/lib/moped/protocol/command.rb +27 -0
- data/lib/moped/protocol/commands.rb +11 -0
- data/lib/moped/protocol/commands/authenticate.rb +54 -0
- data/lib/moped/protocol/delete.rb +92 -0
- data/lib/moped/protocol/get_more.rb +79 -0
- data/lib/moped/protocol/insert.rb +92 -0
- data/lib/moped/protocol/kill_cursors.rb +61 -0
- data/lib/moped/protocol/message.rb +320 -0
- data/lib/moped/protocol/query.rb +131 -0
- data/lib/moped/protocol/reply.rb +90 -0
- data/lib/moped/protocol/update.rb +107 -0
- data/lib/moped/query.rb +230 -0
- data/lib/moped/server.rb +73 -0
- data/lib/moped/session.rb +253 -0
- data/lib/moped/socket.rb +201 -0
- data/lib/moped/version.rb +4 -0
- metadata +108 -46
@@ -0,0 +1,67 @@
|
|
1
|
+
module Moped
|
2
|
+
|
3
|
+
# The class for interacting with a MongoDB collection.
|
4
|
+
#
|
5
|
+
# @example
|
6
|
+
# users = session[:users] # => <Moped::Collection ...>
|
7
|
+
# users.drop
|
8
|
+
# users.insert(name: "John")
|
9
|
+
# users.find.to_a # => [{ name: "John" }]
|
10
|
+
class Collection
|
11
|
+
|
12
|
+
# @return [Database] the database this collection belongs to
|
13
|
+
attr_reader :database
|
14
|
+
|
15
|
+
# @return [String, Symbol] the collection's name
|
16
|
+
attr_reader :name
|
17
|
+
|
18
|
+
# @param [Database] database the database this collection belongs to
|
19
|
+
# @param [String, Symbol] name the collection's name
|
20
|
+
def initialize(database, name)
|
21
|
+
@database = database
|
22
|
+
@name = name
|
23
|
+
end
|
24
|
+
|
25
|
+
# Access information about this collection's indexes.
|
26
|
+
#
|
27
|
+
# @return [Indexes]
|
28
|
+
def indexes
|
29
|
+
Indexes.new(database, name)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Drop the collection.
|
33
|
+
def drop
|
34
|
+
database.command drop: name
|
35
|
+
end
|
36
|
+
|
37
|
+
# Build a query for this collection.
|
38
|
+
#
|
39
|
+
# @param [Hash] selector the selector
|
40
|
+
# @return [Moped::Query]
|
41
|
+
def find(selector = {})
|
42
|
+
Query.new self, selector
|
43
|
+
end
|
44
|
+
alias where find
|
45
|
+
|
46
|
+
# Insert one or more documents into the collection.
|
47
|
+
#
|
48
|
+
# @overload insert(document)
|
49
|
+
# @example
|
50
|
+
# db[:people].insert(name: "John")
|
51
|
+
# @param [Hash] document the document to insert
|
52
|
+
#
|
53
|
+
# @overload insert(documents)
|
54
|
+
# @example
|
55
|
+
# db[:people].insert([{name: "John"}, {name: "Joe"}])
|
56
|
+
# @param [Array<Hash>] documents the documents to insert
|
57
|
+
def insert(documents)
|
58
|
+
documents = [documents] unless documents.is_a? Array
|
59
|
+
insert = Protocol::Insert.new(database.name, name, documents)
|
60
|
+
|
61
|
+
database.session.with(consistency: :strong) do |session|
|
62
|
+
session.execute insert
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
data/lib/moped/cursor.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
module Moped
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
class Cursor
|
5
|
+
attr_reader :session
|
6
|
+
|
7
|
+
attr_reader :query_op
|
8
|
+
attr_reader :get_more_op
|
9
|
+
attr_reader :kill_cursor_op
|
10
|
+
|
11
|
+
def initialize(session, query_operation)
|
12
|
+
@session = session
|
13
|
+
@query_op = query_operation.dup
|
14
|
+
|
15
|
+
@get_more_op = Protocol::GetMore.new(
|
16
|
+
@query_op.database,
|
17
|
+
@query_op.collection,
|
18
|
+
0,
|
19
|
+
@query_op.limit
|
20
|
+
)
|
21
|
+
|
22
|
+
@kill_cursor_op = Protocol::KillCursors.new([0])
|
23
|
+
end
|
24
|
+
|
25
|
+
def each
|
26
|
+
documents = query @query_op
|
27
|
+
documents.each { |doc| yield doc }
|
28
|
+
|
29
|
+
while more?
|
30
|
+
return kill if limited? && @get_more_op.limit <= 0
|
31
|
+
|
32
|
+
documents = query @get_more_op
|
33
|
+
documents.each { |doc| yield doc }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def query(operation)
|
38
|
+
reply = session.query operation
|
39
|
+
|
40
|
+
@get_more_op.limit -= reply.count if limited?
|
41
|
+
@get_more_op.cursor_id = reply.cursor_id
|
42
|
+
@kill_cursor_op.cursor_ids = [reply.cursor_id]
|
43
|
+
|
44
|
+
reply.documents
|
45
|
+
end
|
46
|
+
|
47
|
+
def limited?
|
48
|
+
@query_op.limit > 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def more?
|
52
|
+
@get_more_op.cursor_id != 0
|
53
|
+
end
|
54
|
+
|
55
|
+
def kill
|
56
|
+
session.execute kill_cursor_op
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Moped
|
2
|
+
|
3
|
+
# The class for interacting with a MongoDB database. One only interacts with
|
4
|
+
# this class indirectly through a session.
|
5
|
+
#
|
6
|
+
# @example
|
7
|
+
# session.use :moped
|
8
|
+
# session.drop
|
9
|
+
# session[:users].insert(name: "John")
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# session.with(database: :moped) do |moped|
|
13
|
+
# moped[:users].drop
|
14
|
+
# end
|
15
|
+
class Database
|
16
|
+
|
17
|
+
# @return [Session] the database's session
|
18
|
+
attr_reader :session
|
19
|
+
|
20
|
+
# @return [String, Symbol] the database's name
|
21
|
+
attr_reader :name
|
22
|
+
|
23
|
+
# @param [Session] session the session
|
24
|
+
# @param [String, Symbol] name the database's name
|
25
|
+
def initialize(session, name)
|
26
|
+
@session = session
|
27
|
+
@name = name
|
28
|
+
end
|
29
|
+
|
30
|
+
# Drop the database.
|
31
|
+
def drop
|
32
|
+
command dropDatabase: 1
|
33
|
+
end
|
34
|
+
|
35
|
+
# Log in with +username+ and +password+ on the current database.
|
36
|
+
#
|
37
|
+
# @param [String] username the username
|
38
|
+
# @param [String] password the password
|
39
|
+
def login(username, password)
|
40
|
+
session.cluster.login(name, username, password)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Log out from the current database.
|
44
|
+
def logout
|
45
|
+
session.cluster.logout(name)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Run +command+ on the database.
|
49
|
+
#
|
50
|
+
# @example
|
51
|
+
# db.command(ismaster: 1)
|
52
|
+
# # => { "master" => true, hosts: [] }
|
53
|
+
#
|
54
|
+
# @param [Hash] command the command to run
|
55
|
+
# @return [Hash] the result of the command
|
56
|
+
def command(command)
|
57
|
+
operation = Protocol::Command.new(name, command)
|
58
|
+
|
59
|
+
result = session.with(consistency: :strong) do |session|
|
60
|
+
session.simple_query(operation)
|
61
|
+
end
|
62
|
+
|
63
|
+
raise Errors::OperationFailure.new(
|
64
|
+
operation, result
|
65
|
+
) unless result["ok"] == 1.0
|
66
|
+
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
# @param [Symbol, String] collection the collection name
|
71
|
+
# @return [Moped::Collection] an instance of +collection+
|
72
|
+
def [](collection)
|
73
|
+
Collection.new(self, collection)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
data/lib/moped/errors.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Moped
|
2
|
+
|
3
|
+
# The namespace for all errors generated by Moped.
|
4
|
+
module Errors
|
5
|
+
|
6
|
+
# Mongo's exceptions are sparsely documented, but this is the most accurate
|
7
|
+
# source of information on error codes.
|
8
|
+
ERROR_REFERENCE = "https://github.com/mongodb/mongo/blob/master/docs/errors.md"
|
9
|
+
|
10
|
+
# Generic error class for exceptions related to connection failures.
|
11
|
+
class ConnectionFailure < StandardError; end
|
12
|
+
|
13
|
+
# Generic error class for exceptions generated on the remote MongoDB
|
14
|
+
# server.
|
15
|
+
class MongoError < StandardError; end
|
16
|
+
|
17
|
+
# Exception class for exceptions generated as a direct result of an
|
18
|
+
# operation, such as a failed insert or an invalid command.
|
19
|
+
class OperationFailure < MongoError
|
20
|
+
|
21
|
+
# @return the command that generated the error
|
22
|
+
attr_reader :command
|
23
|
+
|
24
|
+
# @return [Hash] the details about the error
|
25
|
+
attr_reader :details
|
26
|
+
|
27
|
+
# Create a new operation failure exception.
|
28
|
+
#
|
29
|
+
# @param command the command that generated the error
|
30
|
+
# @param [Hash] details the details about the error
|
31
|
+
def initialize(command, details)
|
32
|
+
@command = command
|
33
|
+
@details = details
|
34
|
+
|
35
|
+
super build_message
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def build_message
|
41
|
+
"The operation: #{command.inspect}\n#{error_message}"
|
42
|
+
end
|
43
|
+
|
44
|
+
def error_message
|
45
|
+
err = details["err"] || details["errmsg"] || details["$err"]
|
46
|
+
|
47
|
+
if code = details["code"]
|
48
|
+
"failed with error #{code}: #{err.inspect}\n\n" <<
|
49
|
+
"See #{ERROR_REFERENCE}\nfor details about this error."
|
50
|
+
else
|
51
|
+
"failed with error #{err.inspect}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# A special kind of OperationFailure, raised when Mongo sets the
|
57
|
+
# :query_failure flag on a query response.
|
58
|
+
class QueryFailure < OperationFailure; end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
module Moped
|
2
|
+
class Indexes
|
3
|
+
include Enumerable
|
4
|
+
|
5
|
+
private
|
6
|
+
|
7
|
+
attr_reader :database
|
8
|
+
attr_reader :collection_name
|
9
|
+
attr_reader :namespace
|
10
|
+
|
11
|
+
def initialize(database, collection_name)
|
12
|
+
@database = database
|
13
|
+
@collection_name = collection_name
|
14
|
+
@namespace = "#{database.name}.#{collection_name}"
|
15
|
+
end
|
16
|
+
|
17
|
+
public
|
18
|
+
|
19
|
+
# Retrive an index by its definition.
|
20
|
+
#
|
21
|
+
# @param [Hash] key an index key definition
|
22
|
+
# @return [Hash, nil] the index with the provided key, or nil.
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# session[:users].indexes[id: 1]
|
26
|
+
# # => {"v"=>1, "key"=>{"_id"=>1}, "ns"=>"moped_test.users", "name"=>"_id_" }
|
27
|
+
def [](key)
|
28
|
+
database[:"system.indexes"].find(ns: namespace, key: key).one
|
29
|
+
end
|
30
|
+
|
31
|
+
# Create an index unless it already exists.
|
32
|
+
#
|
33
|
+
# @param [Hash] key the index spec
|
34
|
+
# @param [Hash] options the options for the index.
|
35
|
+
# @see http://www.mongodb.org/display/DOCS/Indexes#Indexes-CreationOptions
|
36
|
+
#
|
37
|
+
# @example Without options
|
38
|
+
# session[:users].indexes.create(name: 1)
|
39
|
+
# session[:users].indexes[name: 1]
|
40
|
+
# # => {"v"=>1, "key"=>{"name"=>1}, "ns"=>"moped_test.users", "name"=>"name_1" }
|
41
|
+
#
|
42
|
+
# @example With options
|
43
|
+
# session[:users].indexes.create(
|
44
|
+
# { location: "2d", name: 1 },
|
45
|
+
# { unique: true, dropDups: true }
|
46
|
+
# )
|
47
|
+
# session[:users][location: "2d", name: 1]
|
48
|
+
# {"v"=>1,
|
49
|
+
# "key"=>{"location"=>"2d", "name"=>1},
|
50
|
+
# "unique"=>true,
|
51
|
+
# "ns"=>"moped_test.users",
|
52
|
+
# "dropDups"=>true,
|
53
|
+
# "name"=>"location_2d_name_1"}
|
54
|
+
def create(key, options = {})
|
55
|
+
spec = options.merge(ns: namespace, key: key)
|
56
|
+
spec[:name] ||= key.to_a.join("_")
|
57
|
+
|
58
|
+
database[:"system.indexes"].insert(spec)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Drop an index, or all indexes.
|
62
|
+
#
|
63
|
+
# @param [Hash] key the index's key
|
64
|
+
# @return [Boolean] whether the indexes were dropped.
|
65
|
+
#
|
66
|
+
# @example Drop all indexes
|
67
|
+
# session[:users].indexes.count # => 3
|
68
|
+
# # Does not drop the _id index
|
69
|
+
# session[:users].indexes.drop
|
70
|
+
# session[:users].indexes.count # => 1
|
71
|
+
#
|
72
|
+
# @example Drop a particular index
|
73
|
+
# session[:users].indexes.drop(name: 1)
|
74
|
+
# session[:users].indexes[name: 1] # => nil
|
75
|
+
def drop(key = nil)
|
76
|
+
if key
|
77
|
+
index = self[key] or return false
|
78
|
+
name = index["name"]
|
79
|
+
else
|
80
|
+
name = "*"
|
81
|
+
end
|
82
|
+
|
83
|
+
result = database.command deleteIndexes: collection_name, index: name
|
84
|
+
result["ok"] == 1
|
85
|
+
end
|
86
|
+
|
87
|
+
# @yield [Hash] each index for the collection.
|
88
|
+
def each(&block)
|
89
|
+
database[:"system.indexes"].find(ns: namespace).each(&block)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Moped
|
2
|
+
module Logging
|
3
|
+
def logger
|
4
|
+
return @logger if defined?(@logger)
|
5
|
+
|
6
|
+
@logger = rails_logger || default_logger
|
7
|
+
end
|
8
|
+
|
9
|
+
def rails_logger
|
10
|
+
defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger
|
11
|
+
end
|
12
|
+
|
13
|
+
def default_logger
|
14
|
+
Logger.new(STDOUT).tap do |logger|
|
15
|
+
logger.level = Logger::INFO
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def logger=(logger)
|
20
|
+
@logger = logger
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
extend Logging
|
25
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Moped #:nodoc:
|
2
|
+
|
3
|
+
# The +Moped::Protocol+ namespace contains convenience classes for
|
4
|
+
# building all of the possible messages defined in the Mongo Wire Protocol.
|
5
|
+
module Protocol
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
require "moped/protocol/message"
|
10
|
+
|
11
|
+
require "moped/protocol/delete"
|
12
|
+
require "moped/protocol/get_more"
|
13
|
+
require "moped/protocol/insert"
|
14
|
+
require "moped/protocol/kill_cursors"
|
15
|
+
require "moped/protocol/query"
|
16
|
+
require "moped/protocol/reply"
|
17
|
+
require "moped/protocol/update"
|
18
|
+
|
19
|
+
require "moped/protocol/command"
|
20
|
+
require "moped/protocol/commands"
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Moped
|
2
|
+
module Protocol
|
3
|
+
|
4
|
+
# This is a convenience class on top of +Query+ for quickly creating a
|
5
|
+
# command.
|
6
|
+
#
|
7
|
+
# @example
|
8
|
+
# command = Moped::Protocol::Command.new :moped, ismaster: 1
|
9
|
+
# socket.write command.serialize
|
10
|
+
class Command < Query
|
11
|
+
|
12
|
+
# @param [String, Symbol] database the database to run this command on
|
13
|
+
# @param [Hash] command the command to run
|
14
|
+
def initialize(database, command)
|
15
|
+
super database, :$cmd, command, limit: -1
|
16
|
+
end
|
17
|
+
|
18
|
+
def log_inspect
|
19
|
+
type = "COMMAND"
|
20
|
+
|
21
|
+
"%-12s database=%s command=%s" % [type, database, selector.inspect]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Moped
|
2
|
+
module Protocol
|
3
|
+
|
4
|
+
# The +Moped::Protocol::Commands+ namespace contains classes for
|
5
|
+
# specific commands, such as authentication, to execute on a database.
|
6
|
+
module Commands
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
require "moped/protocol/commands/authenticate"
|