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.

Files changed (50) hide show
  1. data/MIT_LICENSE +19 -0
  2. data/README.md +323 -0
  3. data/lib/moped.rb +19 -0
  4. data/lib/moped/bson.rb +25 -0
  5. data/lib/moped/bson/binary.rb +68 -0
  6. data/lib/moped/bson/code.rb +61 -0
  7. data/lib/moped/bson/document.rb +16 -0
  8. data/lib/moped/bson/extensions.rb +81 -0
  9. data/lib/moped/bson/extensions/array.rb +44 -0
  10. data/lib/moped/bson/extensions/boolean.rb +14 -0
  11. data/lib/moped/bson/extensions/false_class.rb +15 -0
  12. data/lib/moped/bson/extensions/float.rb +23 -0
  13. data/lib/moped/bson/extensions/hash.rb +49 -0
  14. data/lib/moped/bson/extensions/integer.rb +37 -0
  15. data/lib/moped/bson/extensions/nil_class.rb +20 -0
  16. data/lib/moped/bson/extensions/regexp.rb +40 -0
  17. data/lib/moped/bson/extensions/string.rb +35 -0
  18. data/lib/moped/bson/extensions/symbol.rb +25 -0
  19. data/lib/moped/bson/extensions/time.rb +21 -0
  20. data/lib/moped/bson/extensions/true_class.rb +15 -0
  21. data/lib/moped/bson/max_key.rb +21 -0
  22. data/lib/moped/bson/min_key.rb +21 -0
  23. data/lib/moped/bson/object_id.rb +123 -0
  24. data/lib/moped/bson/timestamp.rb +15 -0
  25. data/lib/moped/bson/types.rb +67 -0
  26. data/lib/moped/cluster.rb +193 -0
  27. data/lib/moped/collection.rb +67 -0
  28. data/lib/moped/cursor.rb +60 -0
  29. data/lib/moped/database.rb +76 -0
  30. data/lib/moped/errors.rb +61 -0
  31. data/lib/moped/indexes.rb +93 -0
  32. data/lib/moped/logging.rb +25 -0
  33. data/lib/moped/protocol.rb +20 -0
  34. data/lib/moped/protocol/command.rb +27 -0
  35. data/lib/moped/protocol/commands.rb +11 -0
  36. data/lib/moped/protocol/commands/authenticate.rb +54 -0
  37. data/lib/moped/protocol/delete.rb +92 -0
  38. data/lib/moped/protocol/get_more.rb +79 -0
  39. data/lib/moped/protocol/insert.rb +92 -0
  40. data/lib/moped/protocol/kill_cursors.rb +61 -0
  41. data/lib/moped/protocol/message.rb +320 -0
  42. data/lib/moped/protocol/query.rb +131 -0
  43. data/lib/moped/protocol/reply.rb +90 -0
  44. data/lib/moped/protocol/update.rb +107 -0
  45. data/lib/moped/query.rb +230 -0
  46. data/lib/moped/server.rb +73 -0
  47. data/lib/moped/session.rb +253 -0
  48. data/lib/moped/socket.rb +201 -0
  49. data/lib/moped/version.rb +4 -0
  50. 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
@@ -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
@@ -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"