jmongo 1.0.3 → 1.1.0

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.
Files changed (96) hide show
  1. data/Gemfile +8 -0
  2. data/Gemfile.lock +43 -0
  3. data/Rakefile +72 -0
  4. data/jmongo.gemspec +84 -6
  5. data/lib/jmongo.rb +6 -14
  6. data/lib/jmongo/collection.rb +196 -114
  7. data/lib/jmongo/connection.rb +39 -13
  8. data/lib/jmongo/cursor.rb +161 -63
  9. data/lib/jmongo/db.rb +119 -30
  10. data/lib/jmongo/exceptions.rb +39 -0
  11. data/lib/jmongo/mongo-2.6.5.gb1.jar +0 -0
  12. data/lib/jmongo/mongo/bson.rb +130 -0
  13. data/lib/jmongo/mongo/collection.rb +185 -0
  14. data/lib/jmongo/mongo/connection.rb +45 -0
  15. data/lib/jmongo/mongo/db.rb +31 -0
  16. data/lib/jmongo/mongo/jmongo.rb +44 -0
  17. data/lib/jmongo/mongo/mongo.rb +98 -0
  18. data/lib/jmongo/mongo/ruby_ext.rb +38 -0
  19. data/lib/jmongo/mongo/utils.rb +136 -0
  20. data/lib/jmongo/version.rb +1 -1
  21. data/test-results.txt +98 -0
  22. data/test/auxillary/1.4_features.rb +166 -0
  23. data/test/auxillary/authentication_test.rb +68 -0
  24. data/test/auxillary/autoreconnect_test.rb +41 -0
  25. data/test/auxillary/fork_test.rb +30 -0
  26. data/test/auxillary/repl_set_auth_test.rb +58 -0
  27. data/test/auxillary/slave_connection_test.rb +36 -0
  28. data/test/auxillary/threaded_authentication_test.rb +101 -0
  29. data/test/bson/binary_test.rb +15 -0
  30. data/test/bson/bson_test.rb +657 -0
  31. data/test/bson/byte_buffer_test.rb +208 -0
  32. data/test/bson/hash_with_indifferent_access_test.rb +38 -0
  33. data/test/bson/json_test.rb +17 -0
  34. data/test/bson/object_id_test.rb +138 -0
  35. data/test/bson/ordered_hash_test.rb +245 -0
  36. data/test/bson/test_helper.rb +46 -0
  37. data/test/bson/timestamp_test.rb +46 -0
  38. data/test/collection_test.rb +933 -0
  39. data/test/connection_test.rb +325 -0
  40. data/test/conversions_test.rb +121 -0
  41. data/test/cursor_fail_test.rb +75 -0
  42. data/test/cursor_message_test.rb +43 -0
  43. data/test/cursor_test.rb +547 -0
  44. data/test/data/empty_data +0 -0
  45. data/test/data/sample_data +0 -0
  46. data/test/data/sample_file.pdf +0 -0
  47. data/test/data/small_data.txt +1 -0
  48. data/test/db_api_test.rb +739 -0
  49. data/test/db_connection_test.rb +15 -0
  50. data/test/db_test.rb +325 -0
  51. data/test/grid_file_system_test.rb +260 -0
  52. data/test/grid_io_test.rb +210 -0
  53. data/test/grid_test.rb +259 -0
  54. data/test/load/thin/config.ru +6 -0
  55. data/test/load/thin/config.yml.template +6 -0
  56. data/test/load/thin/load.rb +24 -0
  57. data/test/load/unicorn/config.ru +6 -0
  58. data/test/load/unicorn/load.rb +23 -0
  59. data/test/load/unicorn/unicorn.rb.template +29 -0
  60. data/test/replica_sets/connect_test.rb +111 -0
  61. data/test/replica_sets/connection_string_test.rb +29 -0
  62. data/test/replica_sets/count_test.rb +36 -0
  63. data/test/replica_sets/insert_test.rb +54 -0
  64. data/test/replica_sets/pooled_insert_test.rb +58 -0
  65. data/test/replica_sets/query_secondaries.rb +109 -0
  66. data/test/replica_sets/query_test.rb +52 -0
  67. data/test/replica_sets/read_preference_test.rb +43 -0
  68. data/test/replica_sets/refresh_test.rb +123 -0
  69. data/test/replica_sets/replication_ack_test.rb +71 -0
  70. data/test/replica_sets/rs_test_helper.rb +27 -0
  71. data/test/safe_test.rb +68 -0
  72. data/test/support/hash_with_indifferent_access.rb +186 -0
  73. data/test/support/keys.rb +45 -0
  74. data/test/support_test.rb +19 -0
  75. data/test/test_helper.rb +111 -0
  76. data/test/threading/threading_with_large_pool_test.rb +90 -0
  77. data/test/threading_test.rb +88 -0
  78. data/test/tools/auth_repl_set_manager.rb +14 -0
  79. data/test/tools/keyfile.txt +1 -0
  80. data/test/tools/repl_set_manager.rb +377 -0
  81. data/test/unit/collection_test.rb +128 -0
  82. data/test/unit/connection_test.rb +85 -0
  83. data/test/unit/cursor_test.rb +127 -0
  84. data/test/unit/db_test.rb +96 -0
  85. data/test/unit/grid_test.rb +51 -0
  86. data/test/unit/node_test.rb +73 -0
  87. data/test/unit/pool_manager_test.rb +47 -0
  88. data/test/unit/pool_test.rb +9 -0
  89. data/test/unit/read_test.rb +101 -0
  90. data/test/unit/safe_test.rb +125 -0
  91. data/test/uri_test.rb +92 -0
  92. metadata +170 -99
  93. data/lib/jmongo/ajrb.rb +0 -189
  94. data/lib/jmongo/jmongo_jext.rb +0 -302
  95. data/lib/jmongo/mongo-2.6.3.jar +0 -0
  96. data/lib/jmongo/utils.rb +0 -61
@@ -2,4 +2,43 @@ module Mongo
2
2
  MongoDBException = Java::ComMongodb::MongoException
3
3
  NetworkException = Java::ComMongodb::MongoException::Network
4
4
  DuplicateKeyException = Java::ComMongodb::MongoException::DuplicateKey
5
+
6
+ # Generic Mongo Ruby Driver exception class.
7
+ class MongoRubyError < StandardError; end
8
+
9
+ # Raised when MongoDB itself has returned an error.
10
+ class MongoDBError < RuntimeError; end
11
+
12
+ # Raised when invalid arguments are sent to Mongo Ruby methods.
13
+ class MongoArgumentError < MongoRubyError; end
14
+
15
+ # Raised on failures in connection to the database server.
16
+ class ConnectionError < MongoRubyError; end
17
+
18
+ # Raised on failures in connection to the database server.
19
+ class ReplicaSetConnectionError < ConnectionError; end
20
+
21
+ # Raised on failures in connection to the database server.
22
+ class ConnectionTimeoutError < MongoRubyError; end
23
+
24
+ # Raised when a connection operation fails.
25
+ class ConnectionFailure < MongoDBError; end
26
+
27
+ # Raised when authentication fails.
28
+ class AuthenticationError < MongoDBError; end
29
+
30
+ # Raised when a database operation fails.
31
+ class OperationFailure < MongoDBError; end
32
+
33
+ # Raised when a socket read operation times out.
34
+ class OperationTimeout < MongoDBError; end
35
+
36
+ # Raised when a client attempts to perform an invalid operation.
37
+ class InvalidOperation < MongoDBError; end
38
+
39
+ # Raised when an invalid collection or database name is used (invalid namespace name).
40
+ class InvalidNSName < RuntimeError; end
41
+
42
+ # Raised when the client supplies an invalid value to sort by.
43
+ class InvalidSortValueError < MongoRubyError; end
5
44
  end
@@ -0,0 +1,130 @@
1
+ module BSON
2
+ # add missing BSON::ObjectId ruby methods
3
+ java_import Java::OrgBsonTypes::ObjectId
4
+
5
+ java_import Java::ComMongodb::DBRef
6
+ java_import Java::OrgBsonTypes::MaxKey
7
+ java_import Java::OrgBsonTypes::MinKey
8
+ java_import Java::OrgBsonTypes::Symbol
9
+
10
+ OrderedHash = Java::ComMongodb::BasicDBObject
11
+ BsonCode = Java::OrgBsonTypes::CodeWScope
12
+
13
+ class ObjectId
14
+ def self.from_string(str)
15
+ v = is_valid?(str.to_s)
16
+ raise BSON::InvalidObjectId, "illegal ObjectID format" unless v
17
+ new(str.to_s)
18
+ end
19
+
20
+ def self.create_pk(doc)
21
+ doc.has_key?(:_id) || doc.has_key?('_id') ? doc : doc.merge!(:_id => self.new)
22
+ end
23
+
24
+ def self.from_time(time, opts={})
25
+ unique = opts.fetch(:unique, false)
26
+ if unique
27
+ self.new(time)
28
+ else
29
+ self.new([time.to_i,0,0].pack("NNN").to_java_bytes)
30
+ end
31
+ end
32
+
33
+ #"data=", "decode64", "encode64", "decode_b", "b64encode" - shout out if these methods are needed
34
+
35
+ def data
36
+ self.to_byte_array.to_a.map{|x| x & 0xFF}
37
+ end
38
+
39
+ def clone
40
+ self.class.new(self.to_byte_array)
41
+ end
42
+
43
+ def inspect
44
+ "BSON::ObjectID('#{self.to_s}')"
45
+ end
46
+
47
+ def generation_time
48
+ Time.at(self.get_time/1000).utc
49
+ end
50
+ end
51
+
52
+ class MaxKey
53
+ def ==(obj)
54
+ obj.class == self.class
55
+ end
56
+ end
57
+
58
+ class MinKey
59
+ def ==(obj)
60
+ obj.class == self.class
61
+ end
62
+ end
63
+
64
+ class Code
65
+ # Wrap code to be evaluated by MongoDB.
66
+ #
67
+ # @param [String] code the JavaScript code.
68
+ # @param [Hash] a document mapping identifiers to values, which
69
+ # represent the scope in which the code is to be executed.
70
+ def initialize(code, scope={})
71
+ unless code.is_a?(String)
72
+ raise ArgumentError, "BSON::Code must be in the form of a String; #{code.class} is not accepted."
73
+ end
74
+ @bson_code = BsonCode.new(code, scope.to_bson)
75
+ end
76
+
77
+ def code
78
+ @bson_code.code
79
+ end
80
+
81
+ def scope
82
+ @bson_code.scope
83
+ end
84
+
85
+ def length
86
+ code.length
87
+ end
88
+
89
+ def ==(other)
90
+ self.class == other.class &&
91
+ code == other.code && scope == other.scope
92
+ end
93
+
94
+ def inspect
95
+ "<BSON::Code:#{object_id} @code=\"#{code}\" @scope=\"#{scope.inspect}\">"
96
+ end
97
+
98
+ def to_s
99
+ code.to_s
100
+ end
101
+
102
+ def to_bson
103
+ @bson_code
104
+ end
105
+ alias :to_bson_code :to_bson
106
+ end
107
+
108
+ # Generic Mongo Ruby Driver exception class.
109
+ class MongoRubyError < StandardError; end
110
+
111
+ # Raised when MongoDB itself has returned an error.
112
+ class MongoDBError < RuntimeError; end
113
+
114
+ # This will replace MongoDBError.
115
+ class BSONError < MongoDBError; end
116
+
117
+ # Raised when given a string is not valid utf-8 (Ruby 1.8 only).
118
+ class InvalidStringEncoding < BSONError; end
119
+
120
+ # Raised when attempting to initialize an invalid ObjectId.
121
+ class InvalidObjectId < BSONError; end
122
+ class InvalidObjectID < BSONError; end
123
+
124
+ # Raised when trying to insert a document that exceeds the 4MB limit or
125
+ # when the document contains objects that can't be serialized as BSON.
126
+ class InvalidDocument < BSONError; end
127
+
128
+ # Raised when an invalid name is used.
129
+ class InvalidKeyName < BSONError; end
130
+ end
@@ -0,0 +1,185 @@
1
+ # Copyright (C) 2010 Guy Boertje
2
+
3
+ module Mongo
4
+ module JavaImpl
5
+ module Collection_
6
+
7
+ private
8
+
9
+ def name_from opts
10
+ return unless (opts[:name] || opts['name'])
11
+ name = opts.delete(:name) || opts.delete('name')
12
+ name ? name.to_s : nil
13
+ end
14
+
15
+ def _drop_index(spec)
16
+ name = generate_index_name(spec.is_a?(String) || spec.is_a?(Symbol) ? spec : parse_index_spec(spec))
17
+ idx = @db.index_information(@name).values.select do |entry|
18
+ entry['name'] == name || name == generate_index_name(entry['key'])
19
+ end
20
+ if idx.nil? || idx.empty?
21
+ raise MongoDBError, "Error with drop_index command for: #{name}"
22
+ end
23
+ @j_collection.dropIndexes(idx.first['name'].to_s)
24
+ end
25
+
26
+ def _create_indexes(obj,opts = {})
27
+ name = name_from(opts)
28
+ field_spec = parse_index_spec(obj)
29
+ opts[:dropDups] = opts[:drop_dups] if opts[:drop_dups]
30
+ if obj.is_a?(String) || obj.is_a?(Symbol)
31
+ name = obj.to_s unless name
32
+ end
33
+ name = generate_index_name(field_spec) unless name
34
+ opts['name'] = name
35
+ begin
36
+ @j_collection.ensureIndex(to_dbobject(field_spec),to_dbobject(opts))
37
+ rescue => e
38
+ if opts[:dropDups] && e.message =~ /E11000/
39
+ # NOP. If the user is intentionally dropping dups, we can ignore duplicate key errors.
40
+ else
41
+ msg = "Failed to create index #{field_spec.inspect} with the following error: #{e.message}"
42
+ raise Mongo::OperationFailure, msg
43
+ end
44
+ end
45
+ name
46
+ end
47
+
48
+ def generate_index_name(spec)
49
+ return spec.to_s if spec.is_a?(String) || spec.is_a?(Symbol)
50
+ indexes = []
51
+ spec.each_pair do |field, direction|
52
+ dir = sort_value(field,direction)
53
+ indexes.push("#{field}_#{dir}")
54
+ end
55
+ indexes.join("_")
56
+ end
57
+
58
+ def parse_index_spec(spec)
59
+ field_spec = Hash.new
60
+ if spec.is_a?(String) || spec.is_a?(Symbol)
61
+ field_spec[spec.to_s] = 1
62
+ elsif spec.is_a?(Array) && spec.all? {|field| field.is_a?(Array) }
63
+ spec.each do |f|
64
+ if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D].include?(f[1])
65
+ field_spec[f[0].to_s] = f[1]
66
+ else
67
+ raise MongoArgumentError, "Invalid index field #{f[1].inspect}; " +
68
+ "should be one of Mongo::ASCENDING (1), Mongo::DESCENDING (-1) or Mongo::GEO2D ('2d')."
69
+ end
70
+ end
71
+ else
72
+ raise MongoArgumentError, "Invalid index specification #{spec.inspect}; " +
73
+ "should be either a string, symbol, or an array of arrays."
74
+ end
75
+ field_spec
76
+ end
77
+
78
+ def remove_documents(obj, safe=nil)
79
+ concern = write_concern(safe)
80
+ wr = @j_collection.remove(to_dbobject(obj), concern)
81
+ return from_dbobject(wr.last_error(concern)) if concern.call_get_last_error
82
+ true
83
+ end
84
+
85
+ ## Note: refactor when java driver fully supports continue_on_error
86
+ def insert_documents(obj, safe=nil, continue_on_error=false)
87
+ to_do = [obj].flatten
88
+ concern = write_concern(safe)
89
+ if continue_on_error
90
+ out = []
91
+ to_do.each do |doc|
92
+ res = _insert_one(doc, concern)
93
+ out << res if res
94
+ end
95
+ if to_do.size != out.size
96
+ msg = "Failed to insert document #{obj.inspect}, duplicate key"
97
+ raise(Mongo::OperationFailure, msg)
98
+ end
99
+ else
100
+ begin
101
+ @j_collection.insert( to_dbobject(to_do), concern )
102
+ rescue => ex
103
+ if ex.message =~ /E11000/
104
+ msg = "Failed to insert document #{obj.inspect}, duplicate key, E11000"
105
+ raise(Mongo::OperationFailure, msg)
106
+ else
107
+ msg = "Failed to insert document #{obj.inspect} db error: #{ex.message}"
108
+ raise Mongo::MongoDBError, msg
109
+ end
110
+ end
111
+ end
112
+ to_do
113
+ end
114
+
115
+ def _insert_one(obj, concern)
116
+ one_obj = [obj].flatten.first
117
+ dbo = to_dbobject(one_obj)
118
+ begin
119
+ jres = @j_collection.insert( dbo, concern )
120
+ result = from_dbobject(jres.get_last_error(concern))
121
+ rescue => ex
122
+ if ex.message =~ /E11000/ #noop duplicate key
123
+ result = {'err'=>ex.message}
124
+ else
125
+ msg = "Failed to insert document #{obj.inspect} db error: #{ex.message}"
126
+ raise Mongo::MongoDBError, msg
127
+ end
128
+ end
129
+ doc = from_dbobject(dbo)
130
+ result["err"] =~ /E11000/ ? nil : doc
131
+ end
132
+
133
+ def find_and_modify_document(query,fields,sort,remove,update,new_,upsert)
134
+ from_dbobject @j_collection.find_and_modify(to_dbobject(query),to_dbobject(fields),to_dbobject(sort),remove,to_dbobject(update),new_,upsert)
135
+ end
136
+
137
+ def find_one_document(spec, opts = {})
138
+ opts[:skip] = 0
139
+ opts[:batch_size] = -1
140
+ opts[:limit] = 0
141
+ doc = nil
142
+ self.find(spec, opts) { |c| doc = c.next }
143
+ doc
144
+ end
145
+
146
+ def update_documents(selector, document, upsert=false, multi=false, safe=nil)
147
+ begin
148
+ @j_collection.update(to_dbobject(selector),to_dbobject(document), upsert, multi, write_concern(safe))
149
+ rescue => ex
150
+ if ex.message =~ /E11001/
151
+ msg = "Failed to update document #{document.inspect}, duplicate key"
152
+ raise(Mongo::OperationFailure, msg)
153
+ else
154
+ msg = "Failed to update document #{document.inspect} db error: #{ex.message}"
155
+ raise Mongo::MongoDBError, msg
156
+ end
157
+ end
158
+ end
159
+
160
+ def save_document(obj, safe=nil)
161
+ @pk_factory.create_pk(obj)
162
+ db_obj = to_dbobject(obj)
163
+ concern = write_concern(safe)
164
+ begin
165
+ @j_collection.save( db_obj, concern )
166
+ rescue => ex
167
+ if ex.message =~ /E11000/
168
+ msg = "Failed to insert document #{obj.inspect}, duplicate key"
169
+ raise(Mongo::OperationFailure, msg)
170
+ else
171
+ msg = "Failed to insert document #{obj.inspect} db error: #{ex.message}"
172
+ raise Mongo::MongoDBError, msg
173
+ end
174
+ end
175
+ db_obj['_id']
176
+ end
177
+
178
+ # def id_nil?(obj)
179
+ # return true if obj.has_key?(:_id) && obj[:_id].nil?
180
+ # return true if obj.has_key?('_id') && obj['_id'].nil?
181
+ # false
182
+ # end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,45 @@
1
+ # Copyright (C) 2010 Guy Boertje
2
+
3
+ module Mongo
4
+ module JavaImpl
5
+ module Connection_
6
+ module InstanceMethods
7
+
8
+ private
9
+
10
+ def get_db_names
11
+ @connection.get_database_names
12
+ end
13
+
14
+ def drop_a_db name
15
+ @connection.drop_database(name)
16
+ end
17
+ end
18
+
19
+ module ClassMethods
20
+ URI_RE = /^mongodb:\/\/(([-.\w]+):([^@]+)@)?([-.\w]+)(:([\w]+))?(\/([-\w]+))?/
21
+ OPTS_KEYS = %W[maxpoolsize waitqueuemultiple waitqueuetimeoutms connecttimeoutms sockettimeoutms
22
+ autoconnectretry slaveok safe w wtimeout fsync]
23
+
24
+ def _from_uri uri, opts={}
25
+ optarr = []
26
+ unless uri =~ URI_RE
27
+ raise MongoArgumentError, "MongoDB URI incorrect"
28
+ end
29
+ pieces = uri.split("//")
30
+ extra = pieces.last.count('/') == 0 ? "/" : ""
31
+ opts.each do|k,v|
32
+ if OPTS_KEYS.include?(k.to_s) && !v.nil?
33
+ (optarr << "#{k}=#{v}")
34
+ end
35
+ end
36
+ unless optarr.empty?
37
+ uri << "#{extra}?" << optarr.join("&")
38
+ end
39
+ opts[:new_from_uri] = Java::ComMongodb::MongoURI.new(uri)
40
+ new("",0,opts)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ # Copyright (C) 2010 Guy Boertje
2
+
3
+ module Mongo
4
+ module JavaImpl
5
+ module Db_
6
+ SYSTEM_NAMESPACE_COLLECTION = "system.namespaces"
7
+ SYSTEM_PROFILE_COLLECTION = "system.profile"
8
+
9
+ private
10
+
11
+ def exec_command(cmd)
12
+ cmd_hash = cmd.kind_of?(Hash) ? cmd : {cmd => 1}
13
+ cmd_res = @j_db.command(to_dbobject(cmd_hash))
14
+ from_dbobject(cmd_res)
15
+ end
16
+
17
+ def do_eval(string, *args)
18
+ @j_db.do_eval(string, *args)
19
+ end
20
+
21
+ def has_coll(name)
22
+ @j_db.collection_exists(name)
23
+ end
24
+
25
+ def get_last_error
26
+ from_dbobject @j_db.get_last_error
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,44 @@
1
+ module JMongo
2
+ java_import com.mongodb.BasicDBList
3
+ java_import com.mongodb.BasicDBObject
4
+ java_import com.mongodb.Bytes
5
+ java_import com.mongodb.DB
6
+ java_import com.mongodb.DBRef
7
+ java_import com.mongodb.DBCollection
8
+ java_import com.mongodb.DBCursor
9
+ java_import com.mongodb.DBObject
10
+ java_import com.mongodb.Mongo
11
+ java_import com.mongodb.MongoOptions
12
+ java_import com.mongodb.ServerAddress
13
+ java_import com.mongodb.WriteConcern
14
+ java_import com.mongodb.WriteResult
15
+ java_import com.mongodb.MongoException
16
+ java_import com.mongodb.MongoURI
17
+ java_import com.mongodb.MapReduceCommand
18
+ java_import com.mongodb.MapReduceOutput
19
+ end
20
+
21
+ class Java::ComMongodb::BasicDBObject
22
+ def self.[](*args)
23
+ Hash[*args]
24
+ end
25
+
26
+ if RUBY_PLATFORM == 'java' && JRUBY_VERSION =~ /(1\.[6-9]|[2-9]\.[0-9])..*/
27
+ def hashify
28
+ self.to_map.to_hash
29
+ end
30
+ else
31
+ def hashify
32
+ Hash[self.key_set.to_a.zip(self.values.to_a)]
33
+ end
34
+ end
35
+ def get(key)
36
+ self.java_send(:get,key.to_s)
37
+ end
38
+ end
39
+
40
+ class Java::ComMongodb::BasicDBList
41
+ def arrayify
42
+ self.to_array
43
+ end
44
+ end