mongo 1.5.2 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/Rakefile +24 -8
  2. data/docs/HISTORY.md +15 -0
  3. data/docs/READ_PREFERENCE.md +1 -1
  4. data/docs/RELEASES.md +18 -4
  5. data/docs/REPLICA_SETS.md +5 -5
  6. data/docs/WRITE_CONCERN.md +1 -1
  7. data/lib/mongo/collection.rb +49 -10
  8. data/lib/mongo/connection.rb +7 -24
  9. data/lib/mongo/cursor.rb +3 -1
  10. data/lib/mongo/db.rb +5 -1
  11. data/lib/mongo/exceptions.rb +0 -3
  12. data/lib/mongo/gridfs/grid.rb +1 -1
  13. data/lib/mongo/gridfs/grid_file_system.rb +11 -3
  14. data/lib/mongo/networking.rb +7 -3
  15. data/lib/mongo/repl_set_connection.rb +58 -82
  16. data/lib/mongo/util/logging.rb +26 -18
  17. data/lib/mongo/util/node.rb +11 -2
  18. data/lib/mongo/util/pool_manager.rb +7 -5
  19. data/lib/mongo/util/support.rb +2 -2
  20. data/lib/mongo/util/uri_parser.rb +74 -36
  21. data/lib/mongo/version.rb +1 -1
  22. data/mongo.gemspec +2 -2
  23. data/test/auxillary/authentication_test.rb +8 -0
  24. data/test/auxillary/repl_set_auth_test.rb +1 -2
  25. data/test/bson/ordered_hash_test.rb +1 -1
  26. data/test/bson/test_helper.rb +2 -1
  27. data/test/collection_test.rb +71 -0
  28. data/test/connection_test.rb +9 -0
  29. data/test/db_test.rb +7 -0
  30. data/test/grid_file_system_test.rb +12 -0
  31. data/test/load/thin/load.rb +1 -1
  32. data/test/replica_sets/basic_test.rb +36 -26
  33. data/test/replica_sets/complex_connect_test.rb +44 -0
  34. data/test/replica_sets/connect_test.rb +48 -22
  35. data/test/replica_sets/count_test.rb +4 -6
  36. data/test/replica_sets/insert_test.rb +13 -14
  37. data/test/replica_sets/pooled_insert_test.rb +9 -10
  38. data/test/replica_sets/query_test.rb +4 -4
  39. data/test/replica_sets/read_preference_test.rb +48 -14
  40. data/test/replica_sets/refresh_test.rb +43 -42
  41. data/test/replica_sets/refresh_with_threads_test.rb +10 -9
  42. data/test/replica_sets/replication_ack_test.rb +3 -3
  43. data/test/replica_sets/rs_test_helper.rb +17 -11
  44. data/test/test_helper.rb +2 -1
  45. data/test/tools/repl_set_manager.rb +63 -39
  46. data/test/unit/collection_test.rb +2 -1
  47. data/test/unit/connection_test.rb +22 -0
  48. data/test/unit/cursor_test.rb +6 -3
  49. data/test/unit/read_test.rb +1 -1
  50. data/test/uri_test.rb +17 -2
  51. metadata +151 -167
data/Rakefile CHANGED
@@ -12,6 +12,7 @@ begin
12
12
  rescue LoadError
13
13
  end
14
14
  include Config
15
+
15
16
  ENV['TEST_MODE'] = 'TRUE'
16
17
 
17
18
  task :java do
@@ -42,10 +43,14 @@ task :test do
42
43
  puts "To test the pure ruby driver: \nrake test:ruby\n\n"
43
44
  end
44
45
 
46
+ task :path do
47
+ $:.unshift(File.join(File.dirname(__FILE__), 'lib'))
48
+ end
49
+
45
50
  namespace :test do
46
51
 
47
52
  desc "Test the driver with the C extension enabled."
48
- task :c do
53
+ task :c => :path do
49
54
  ENV['C_EXT'] = 'TRUE'
50
55
  if ENV['TEST']
51
56
  Rake::Task['test:functional'].invoke
@@ -60,7 +65,7 @@ namespace :test do
60
65
  end
61
66
 
62
67
  desc "Test the driver using pure ruby (no C extension)"
63
- task :ruby do
68
+ task :ruby => :path do
64
69
  ENV['C_EXT'] = nil
65
70
  if ENV['TEST']
66
71
  Rake::Task['test:functional'].invoke
@@ -80,6 +85,13 @@ namespace :test do
80
85
  t.ruby_opts << '-w'
81
86
  end
82
87
 
88
+ desc "Run the replica set test suite"
89
+ Rake::TestTask.new(:rs_no_threads) do |t|
90
+ t.test_files = FileList['test/replica_sets/*_test.rb'] - ["test/replica_sets/refresh_with_threads_test.rb"]
91
+ t.verbose = true
92
+ t.ruby_opts << '-w'
93
+ end
94
+
83
95
  Rake::TestTask.new(:unit) do |t|
84
96
  t.test_files = FileList['test/unit/*_test.rb']
85
97
  t.verbose = true
@@ -122,9 +134,9 @@ namespace :test do
122
134
  t.ruby_opts << '-w'
123
135
  end
124
136
 
125
- task :drop_databases do |t|
137
+ task :drop_databases => :path do |t|
126
138
  puts "Dropping test databases..."
127
- require './lib/mongo'
139
+ require 'mongo'
128
140
  con = Mongo::Connection.new(ENV['MONGO_RUBY_DRIVER_HOST'] || 'localhost',
129
141
  ENV['MONGO_RUBY_DRIVER_PORT'] || Mongo::Connection::DEFAULT_PORT)
130
142
  con.database_names.each do |name|
@@ -143,13 +155,13 @@ end
143
155
 
144
156
  desc "Generate YARD documentation"
145
157
  task :ydoc do
146
- require File.join(File.dirname(__FILE__), 'lib', 'mongo')
158
+ require 'mongo'
147
159
  out = File.join('ydoc', Mongo::VERSION)
148
160
  FileUtils.rm_rf('ydoc')
149
161
  system "yardoc lib/**/*.rb lib/mongo/**/*.rb lib/bson/**/*.rb -e ./yard/yard_ext.rb -p yard/templates -o #{out} --title MongoRuby-#{Mongo::VERSION} --files docs/TUTORIAL.md,docs/GridFS.md,docs/FAQ.md,docs/REPLICA_SETS.md,docs/WRITE_CONCERN.md,docs/READ_PREFERENCE.md,docs/HISTORY.md,docs/CREDITS.md,docs/RELEASES.md,docs/CREDITS.md,docs/TAILABLE_CURSORS.md"
150
162
  end
151
163
 
152
- namespace :bamboo do
164
+ namespace :jenkins do
153
165
  task :ci_reporter do
154
166
  begin
155
167
  require 'ci/reporter/rake/test_unit'
@@ -183,10 +195,14 @@ namespace :gem do
183
195
  `rm mongo-*.gem`
184
196
  `rm bson-*.gem`
185
197
  end
198
+
199
+ desc "Uninstall the optional c extensions"
200
+ task :uninstall_extensions do
201
+ `gem uninstall bson_ext`
202
+ end
186
203
 
187
204
  desc "Install the optional c extensions"
188
205
  task :install_extensions do
189
- `gem uninstall bson_ext`
190
206
  `gem build bson_ext.gemspec`
191
207
  `gem install --no-rdoc --no-ri bson_ext-*.gem`
192
208
  `rm bson_ext-*.gem`
@@ -204,7 +220,7 @@ end
204
220
 
205
221
  namespace :ci do
206
222
  namespace :test do
207
- task :c do
223
+ task :c => :path do
208
224
  Rake::Task['gem:install'].invoke
209
225
  Rake::Task['gem:install_extensions'].invoke
210
226
  Rake::Task['test:c'].invoke
data/docs/HISTORY.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # MongoDB Ruby Driver History
2
2
 
3
+ ### 1.6.0
4
+ 2012-02-22
5
+
6
+ * Added Gemfile
7
+ * ReplSetConnection seed format is now array of 'host:port' strings
8
+ * Added read preference :secondary_only
9
+ * Added ability to log duration -- enabled by default (Cyril Mougel)
10
+ * Added read_only option for DB#add_user (Ariel Salomon)
11
+ * Added :collect_on_error option for bulk-insert (Masahiro Nakagawa)
12
+ * Added and updated URI options (now case insensitive)
13
+ * Bug fix for ReplSet refresh attempting to close a closed socket
14
+ * Default op_timeout for ReplSetConnection is now disabled (was 30 seconds)
15
+ * Support db output option for map reduce (John Ewart)
16
+ * Support for keeping limited versions of files using GridFS (VvanGemert)
17
+
3
18
  ### 1.5.2
4
19
  2011-12-13
5
20
 
@@ -17,7 +17,7 @@ The Ruby driver allows you to set read preference on each of four levels: the co
17
17
  Objects will inherit the default read preference from their parents. Thus, if you set a read preference of `{:read => :secondary}` when creating
18
18
  a new connection, then all databases and collections created from that connection will inherit the same setting. See this code example:
19
19
 
20
- @con = Mongo::ReplSetConnection.new([['localhost', 27017], ['localhost', 27018]], :read => :secondary)
20
+ @con = Mongo::ReplSetConnection.new(['localhost:27017','localhost:27018'], :read => :secondary)
21
21
  @db = @con['test']
22
22
  @collection = @db['foo']
23
23
  @collection.find({:name => 'foo'})
data/docs/RELEASES.md CHANGED
@@ -24,10 +24,24 @@ Before each relese to Rubygems.org, the following steps will be taken:
24
24
 
25
25
  1. All driver tests will be run on Linux, OS X, and Windows via continuous integration system.
26
26
 
27
- 2. HISTORY file will document all significant commits.
27
+ 2. Update the HISTORY file and document all significant commits.
28
28
 
29
- 3. Version number will be incremented per the semantic version spec described above.
29
+ 3. Update the version in lib/bson.rb, lib/mongo/version.rb, and ext/version.h.
30
30
 
31
- 4. Appropriate branches and tags will be created in Git repository, as necessary.
31
+ 4. Commit: "Release [VERSION]"
32
32
 
33
- 5. Docs will be updated to the latest version of the driver and posted [online](http://api.mongodb.org/ruby/current/index.html).
33
+ 5. git tag [version]
34
+
35
+ 6. Build gems. Ensure that they have the correct versions.
36
+
37
+ 7. Push tags and commit to GitHub (git push origin master, git push --tags).
38
+
39
+ 8. Build and push docs.
40
+
41
+ 9. Push gems to Rubygems.org.
42
+
43
+ 10. Test that the gem is downloadable from Rubygems.org.
44
+
45
+ 11. Close out release in JIRA.
46
+
47
+ 12. Annouce release on mongodb-user and mongodb-dev.
data/docs/REPLICA_SETS.md CHANGED
@@ -11,13 +11,13 @@ takes a list of seed nodes followed by any connection options. You'll want to sp
11
11
  the driver more chances to connect in the event that any one seed node is offline. Once the driver connects, it will
12
12
  cache the replica set topology as reported by the given seed node and use that information if a failover is later required.
13
13
 
14
- @connection = ReplSetConnection.new(['n1.mydb.net', 27017], ['n2.mydb.net', 27017], ['n3.mydb.net', 27017])
14
+ @connection = ReplSetConnection.new(['n1.mydb.net:27017', 'n2.mydb.net:27017', 'n3.mydb.net:27017'])
15
15
 
16
16
  ### Read slaves
17
17
 
18
18
  If you want to read from a secondary node, you can pass :read => :secondary to ReplSetConnection#new.
19
19
 
20
- @connection = ReplSetConnection.new(['n1.mydb.net', 27017], ['n2.mydb.net', 27017], ['n3.mydb.net', 27017],
20
+ @connection = ReplSetConnection.new(['n1.mydb.net:27017', 'n2.mydb.net:27017', 'n3.mydb.net:27017'],
21
21
  :read => :secondary)
22
22
 
23
23
  A random secondary will be chosen to be read from. In a typical multi-process Ruby application, you'll have a good distribution of reads across secondary nodes.
@@ -48,18 +48,18 @@ having to manually restart your app server, then you should enable it. You can e
48
48
  synchronously, which will refresh the replica set data in a synchronous fashion (which may
49
49
  ocassionally slow down your queries):
50
50
 
51
- @connection = ReplSetConnection.new(['n1.mydb.net', 27017], :refresh_mode => :sync)
51
+ @connection = ReplSetConnection.new(['n1.mydb.net:27017'], :refresh_mode => :sync)
52
52
 
53
53
  If you want to change the default refresh interval of 90 seconds, you can do so like this:
54
54
 
55
- @connection = ReplSetConnection.new(['n1.mydb.net', 27017], :refresh_mode => :sync,
55
+ @connection = ReplSetConnection.new(['n1.mydb.net:27017'], :refresh_mode => :sync,
56
56
  :refresh_interval => 60)
57
57
 
58
58
  Do not set this value to anything lower than 30, or you may start to experience performance issues.
59
59
 
60
60
  You can also disable refresh mode altogether:
61
61
 
62
- @connection = ReplSetConnection.new(['n1.mydb.net', 27017], :refresh_mode => false)
62
+ @connection = ReplSetConnection.new(['n1.mydb.net:27017'], :refresh_mode => false)
63
63
 
64
64
  And you can call `refresh` manually on any replica set connection:
65
65
 
@@ -10,7 +10,7 @@ Write concern is set using the `:safe` option. There are several possible option
10
10
  @collection.save({:doc => 'foo'}, :safe => {:w => 2, :wtimeout => 200, :j => true})
11
11
 
12
12
  The first, `true`, simply indicates that we should request a response from the server to ensure that to errors have occurred. The second, `{:w => 2}`, forces the server to wait until at least two servers have recorded the write. The third does the same but will time out if the replication can't be completed in 200 milliseconds.
13
- Setting a value for `wtimeout` is encouraed.
13
+ Setting a value for `wtimeout` is encouraged.
14
14
 
15
15
  Finally, the fourth example forces the journal to sync to disk if journaling is enabled.
16
16
 
@@ -264,8 +264,8 @@ module Mongo
264
264
  # @return [OrderedHash, Nil]
265
265
  # a single document or nil if no result is found.
266
266
  #
267
- # @param [Hash, ObjectId, Nil] spec_or_object_id a hash specifying elements
268
- # which must be present for a document to be included in the result set or an
267
+ # @param [Hash, ObjectId, Nil] spec_or_object_id a hash specifying elements
268
+ # which must be present for a document to be included in the result set or an
269
269
  # instance of ObjectId to be used as the value for an _id query.
270
270
  # If nil, an empty selector, {}, will be used.
271
271
  #
@@ -321,6 +321,10 @@ module Mongo
321
321
  #
322
322
  # @return [ObjectId, Array]
323
323
  # The _id of the inserted document or a list of _ids of all inserted documents.
324
+ # @return [[ObjectId, Array], [Hash, Array]]
325
+ # 1st, the _id of the inserted document or a list of _ids of all inserted documents.
326
+ # 2nd, a list of invalid documents.
327
+ # Return this result format only when :collect_on_error is true.
324
328
  #
325
329
  # @option opts [Boolean, Hash] :safe (+false+)
326
330
  # run the operation in safe mode, which run a getlasterror command on the
@@ -333,7 +337,13 @@ module Mongo
333
337
  # @option opts [Boolean] :continue_on_error (+false+) If true, then
334
338
  # continue a bulk insert even if one of the documents inserted
335
339
  # triggers a database assertion (as in a duplicate insert, for instance).
340
+ # If not using safe mode, the list of ids returned will
341
+ # include the object ids of all documents attempted on insert, even
342
+ # if some are rejected on error. When safe mode is
343
+ # enabled, any error will raise an OperationFailure exception.
336
344
  # MongoDB v2.0+.
345
+ # @option opts [Boolean] :collect_on_error (+false+) if true, then
346
+ # collects invalid documents as an array. Note that this option changes the result format.
337
347
  #
338
348
  # @core insert insert-instance_method
339
349
  def insert(doc_or_docs, opts={})
@@ -403,7 +413,7 @@ module Mongo
403
413
  # @option opts [Boolean] :upsert (+false+) if true, performs an upsert (update or insert)
404
414
  # @option opts [Boolean] :multi (+false+) update all documents matching the selector, as opposed to
405
415
  # just the first matching document. Note: only works in MongoDB 1.1.3 or later.
406
- # @option opts [Boolean] :safe (+false+)
416
+ # @option opts [Boolean] :safe (+false+)
407
417
  # If true, check that the save succeeded. OperationFailure
408
418
  # will be raised on an error. Note that a safe check requires an extra
409
419
  # round-trip to the database. Safe options provided here will override any safe
@@ -624,7 +634,12 @@ module Mongo
624
634
  if raw
625
635
  result
626
636
  elsif result["result"]
627
- @db[result["result"]]
637
+ if result['result'].is_a? BSON::OrderedHash and result['result'].has_key? 'db' and result['result'].has_key? 'collection'
638
+ otherdb = @db.connection[result['result']['db']]
639
+ otherdb[result['result']['collection']]
640
+ else
641
+ @db[result["result"]]
642
+ end
628
643
  else
629
644
  raise ArgumentError, "Could not instantiate collection from result. If you specified " +
630
645
  "{:out => {:inline => true}}, then you must also specify :raw => true to get the results."
@@ -891,12 +906,12 @@ module Mongo
891
906
  if [Mongo::ASCENDING, Mongo::DESCENDING, Mongo::GEO2D, Mongo::GEOHAYSTACK].include?(f[1])
892
907
  field_spec[f[0].to_s] = f[1]
893
908
  else
894
- raise MongoArgumentError, "Invalid index field #{f[1].inspect}; " +
909
+ raise MongoArgumentError, "Invalid index field #{f[1].inspect}; " +
895
910
  "should be one of Mongo::ASCENDING (1), Mongo::DESCENDING (-1) or Mongo::GEO2D ('2d')."
896
911
  end
897
912
  end
898
913
  else
899
- raise MongoArgumentError, "Invalid index specification #{spec.inspect}; " +
914
+ raise MongoArgumentError, "Invalid index specification #{spec.inspect}; " +
900
915
  "should be either a string, symbol, or an array of arrays."
901
916
  end
902
917
  field_spec
@@ -935,10 +950,28 @@ module Mongo
935
950
  else
936
951
  message = BSON::ByteBuffer.new("\0\0\0\0")
937
952
  end
953
+
954
+ collect_on_error = !!flags[:collect_on_error]
955
+ error_docs = [] if collect_on_error
956
+
938
957
  BSON::BSON_RUBY.serialize_cstr(message, "#{@db.name}.#{collection_name}")
939
- documents.each do |doc|
940
- message.put_binary(BSON::BSON_CODER.serialize(doc, check_keys, true, @connection.max_bson_size).to_s)
941
- end
958
+ documents =
959
+ if collect_on_error
960
+ documents.select do |doc|
961
+ begin
962
+ message.put_binary(BSON::BSON_CODER.serialize(doc, check_keys, true, @connection.max_bson_size).to_s)
963
+ true
964
+ rescue StandardError => e # StandardError will be replaced with BSONError
965
+ doc.delete(:_id)
966
+ error_docs << doc
967
+ false
968
+ end
969
+ end
970
+ else
971
+ documents.each do |doc|
972
+ message.put_binary(BSON::BSON_CODER.serialize(doc, check_keys, true, @connection.max_bson_size).to_s)
973
+ end
974
+ end
942
975
  raise InvalidOperation, "Exceded maximum insert size of 16,000,000 bytes" if message.size > 16_000_000
943
976
 
944
977
  instrument(:insert, :database => @db.name, :collection => collection_name, :documents => documents) do
@@ -948,7 +981,13 @@ module Mongo
948
981
  @connection.send_message(Mongo::Constants::OP_INSERT, message)
949
982
  end
950
983
  end
951
- documents.collect { |o| o[:_id] || o['_id'] }
984
+
985
+ doc_ids = documents.collect { |o| o[:_id] || o['_id'] }
986
+ if collect_on_error
987
+ return doc_ids, error_docs
988
+ else
989
+ doc_ids
990
+ end
952
991
  end
953
992
 
954
993
  def generate_index_name(spec)
@@ -38,7 +38,7 @@ module Mongo
38
38
 
39
39
  attr_reader :logger, :size, :auths, :primary, :safe, :host_to_try,
40
40
  :pool_size, :connect_timeout, :pool_timeout,
41
- :primary_pool, :socket_class
41
+ :primary_pool, :socket_class, :op_timeout
42
42
 
43
43
  # Create a connection to single MongoDB instance.
44
44
  #
@@ -52,7 +52,7 @@ module Mongo
52
52
  # Connection#arbiters. This is useful if your application needs to connect manually to nodes other
53
53
  # than the primary.
54
54
  #
55
- # @param [String, Hash] host.
55
+ # @param [String, Hash] host
56
56
  # @param [Integer] port specify a port number here if only one host is being specified.
57
57
  #
58
58
  # @option opts [Boolean, Hash] :safe (false) Set the default safe-mode options
@@ -348,7 +348,7 @@ module Mongo
348
348
  self["admin"].command(oh)
349
349
  end
350
350
 
351
- # Checks if a server is alive. This command will return immediately
351
+ # Checks if a server is alive. This command will return immediately
352
352
  # even if the server is in a lock.
353
353
  #
354
354
  # @return [Hash]
@@ -379,14 +379,6 @@ module Mongo
379
379
  @slave_ok
380
380
  end
381
381
 
382
- def get_socket_from_thread_local
383
- Thread.current[:socket_map] ||= {}
384
- Thread.current[:socket_map][self] ||= {}
385
- Thread.current[:socket_map][self][:writer] ||= checkout_writer
386
- Thread.current[:socket_map][self][:reader] =
387
- Thread.current[:socket_map][self][:writer]
388
- end
389
-
390
382
  # Create a new socket and attempt to connect to master.
391
383
  # If successful, sets host and port to master and returns the socket.
392
384
  #
@@ -415,10 +407,6 @@ module Mongo
415
407
  end
416
408
  alias :reconnect :connect
417
409
 
418
- def connecting?
419
- @nodes_to_try.length > 0
420
- end
421
-
422
410
  # It's possible that we defined connected as all nodes being connected???
423
411
  # NOTE: Do check if this needs to be more stringent.
424
412
  # Probably not since if any node raises a connection failure, all nodes will be closed.
@@ -550,21 +538,14 @@ module Mongo
550
538
  # Timeout on socket connect.
551
539
  @connect_timeout = opts[:connect_timeout] || nil
552
540
 
553
- # Mutex for synchronizing pool access
554
- # TODO: remove this.
555
- @connection_mutex = Mutex.new
556
-
557
541
  # Global safe option. This is false by default.
558
542
  @safe = opts[:safe] || false
559
-
560
- # Condition variable for signal and wait
561
- @queue = ConditionVariable.new
562
-
543
+
563
544
  # Connection pool for primay node
564
545
  @primary = nil
565
546
  @primary_pool = nil
566
547
 
567
- @logger = opts[:logger] || nil
548
+ @logger = opts.fetch(:logger, nil)
568
549
 
569
550
  if @logger
570
551
  write_logging_startup_message
@@ -604,6 +585,8 @@ module Mongo
604
585
  def check_is_master(node)
605
586
  begin
606
587
  host, port = *node
588
+ socket = nil
589
+ config = nil
607
590
 
608
591
  if @connect_timeout
609
592
  Mongo::TimeoutHandler.timeout(@connect_timeout, OperationTimeout) do
data/lib/mongo/cursor.rb CHANGED
@@ -199,7 +199,7 @@ module Mongo
199
199
  # This method overrides any sort order specified in the Collection#find
200
200
  # method, and only the last sort applied has an effect.
201
201
  #
202
- # @param [Symbol, Array] key_or_list either 1) a key to sort by or 2)
202
+ # @param [Symbol, Array] key_or_list either 1) a key to sort by or 2)
203
203
  # an array of [key, direction] pairs to sort by. Direction should
204
204
  # be specified as Mongo::ASCENDING (or :ascending / :asc) or Mongo::DESCENDING (or :descending / :desc)
205
205
  #
@@ -522,6 +522,8 @@ module Mongo
522
522
  @checkin_connection = true
523
523
  if @command || @read_preference == :primary
524
524
  socket = @connection.checkout_writer
525
+ elsif @read_preference == :secondary_only
526
+ socket = @connection.checkout_secondary
525
527
  else
526
528
  @read_pool = @connection.read_pool
527
529
  socket = @connection.checkout_reader
data/lib/mongo/db.rb CHANGED
@@ -173,12 +173,15 @@ module Mongo
173
173
  #
174
174
  # @param [String] username
175
175
  # @param [String] password
176
+ # @param [Boolean] read_only
177
+ # Create a read-only user.
176
178
  #
177
179
  # @return [Hash] an object representing the user.
178
- def add_user(username, password)
180
+ def add_user(username, password, read_only = false)
179
181
  users = self[SYSTEM_USER_COLLECTION]
180
182
  user = users.find_one({:user => username}) || {:user => username}
181
183
  user['pwd'] = Mongo::Support.hash_password(username, password)
184
+ user['readOnly'] = true if read_only;
182
185
  users.save(user)
183
186
  return user
184
187
  end
@@ -332,6 +335,7 @@ module Mongo
332
335
  # @option opts [Boolean] :fsync (false)
333
336
  # @option opts [Integer] :w (nil)
334
337
  # @option opts [Integer] :wtimeout (nil)
338
+ # @option opts [Boolean] :j (false)
335
339
  #
336
340
  # @return [Hash] the entire response to getlasterror.
337
341
  #