mongo 2.1.0.beta → 2.1.0.rc0

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 (253) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/Rakefile +2 -2
  5. data/lib/mongo.rb +2 -3
  6. data/lib/mongo/address.rb +7 -5
  7. data/lib/mongo/address/unix.rb +2 -2
  8. data/lib/mongo/auth/ldap/conversation.rb +6 -2
  9. data/lib/mongo/auth/scram/conversation.rb +8 -2
  10. data/lib/mongo/auth/user/view.rb +21 -0
  11. data/lib/mongo/bulk_write.rb +155 -23
  12. data/lib/mongo/bulk_write/combineable.rb +51 -0
  13. data/lib/mongo/bulk_write/ordered_combiner.rb +55 -0
  14. data/lib/mongo/bulk_write/result.rb +61 -8
  15. data/lib/mongo/bulk_write/result_combiner.rb +117 -0
  16. data/lib/mongo/bulk_write/transformable.rb +117 -0
  17. data/lib/mongo/bulk_write/unordered_combiner.rb +52 -0
  18. data/lib/mongo/bulk_write/validatable.rb +62 -0
  19. data/lib/mongo/client.rb +7 -3
  20. data/lib/mongo/cluster.rb +3 -3
  21. data/lib/mongo/cluster/topology/replica_set.rb +8 -6
  22. data/lib/mongo/cluster/topology/unknown.rb +5 -2
  23. data/lib/mongo/collection.rb +75 -4
  24. data/lib/mongo/collection/view.rb +1 -2
  25. data/lib/mongo/collection/view/aggregation.rb +13 -8
  26. data/lib/mongo/collection/view/immutable.rb +6 -6
  27. data/lib/mongo/collection/view/iterable.rb +13 -4
  28. data/lib/mongo/collection/view/map_reduce.rb +22 -17
  29. data/lib/mongo/collection/view/readable.rb +121 -70
  30. data/lib/mongo/cursor.rb +5 -1
  31. data/lib/mongo/database.rb +3 -3
  32. data/lib/mongo/database/view.rb +1 -1
  33. data/lib/mongo/error.rb +7 -0
  34. data/lib/mongo/{bulk_write/unordered_bulk_write.rb → error/closed_stream.rb} +12 -21
  35. data/lib/mongo/{bulk_write/ordered_bulk_write.rb → error/extra_file_chunk.rb} +13 -27
  36. data/lib/mongo/error/file_not_found.rb +37 -0
  37. data/lib/mongo/error/invalid_file.rb +2 -2
  38. data/lib/mongo/error/invalid_file_revision.rb +37 -0
  39. data/lib/mongo/error/invalid_uri.rb +5 -4
  40. data/lib/mongo/error/missing_file_chunk.rb +38 -0
  41. data/lib/mongo/error/operation_failure.rb +1 -1
  42. data/lib/mongo/error/unchangeable_collection_option.rb +38 -0
  43. data/lib/mongo/error/unexpected_chunk_length.rb +39 -0
  44. data/lib/mongo/grid.rb +2 -1
  45. data/lib/mongo/grid/file.rb +12 -9
  46. data/lib/mongo/grid/file/chunk.rb +6 -6
  47. data/lib/mongo/grid/file/{metadata.rb → info.rb} +41 -39
  48. data/lib/mongo/grid/fs_bucket.rb +441 -0
  49. data/lib/mongo/grid/stream.rb +64 -0
  50. data/lib/mongo/grid/stream/read.rb +208 -0
  51. data/lib/mongo/grid/stream/write.rb +187 -0
  52. data/lib/mongo/index/view.rb +1 -1
  53. data/lib/mongo/loggable.rb +34 -57
  54. data/lib/mongo/logger.rb +16 -78
  55. data/lib/mongo/monitoring.rb +1 -5
  56. data/lib/mongo/monitoring/command_log_subscriber.rb +35 -17
  57. data/lib/mongo/monitoring/event/command_succeeded.rb +20 -1
  58. data/lib/mongo/monitoring/publishable.rb +22 -12
  59. data/lib/mongo/operation.rb +3 -6
  60. data/lib/mongo/operation/commands.rb +24 -0
  61. data/lib/mongo/operation/{aggregate.rb → commands/aggregate.rb} +3 -41
  62. data/lib/mongo/operation/{aggregate → commands/aggregate}/result.rb +0 -0
  63. data/lib/mongo/operation/commands/collections_info.rb +66 -0
  64. data/lib/mongo/operation/{command.rb → commands/command.rb} +2 -18
  65. data/lib/mongo/operation/commands/indexes.rb +70 -0
  66. data/lib/mongo/operation/commands/list_collections.rb +54 -0
  67. data/lib/mongo/operation/commands/list_collections/result.rb +112 -0
  68. data/lib/mongo/operation/commands/list_indexes.rb +56 -0
  69. data/lib/mongo/operation/commands/list_indexes/result.rb +115 -0
  70. data/lib/mongo/operation/{map_reduce.rb → commands/map_reduce.rb} +3 -41
  71. data/lib/mongo/operation/{map_reduce → commands/map_reduce}/result.rb +0 -0
  72. data/lib/mongo/operation/{parallel_scan.rb → commands/parallel_scan.rb} +3 -23
  73. data/lib/mongo/operation/{parallel_scan → commands/parallel_scan}/result.rb +0 -0
  74. data/lib/mongo/operation/commands/user_query.rb +69 -0
  75. data/lib/mongo/operation/commands/users_info.rb +53 -0
  76. data/lib/mongo/operation/commands/users_info/result.rb +36 -0
  77. data/lib/mongo/operation/executable.rb +4 -68
  78. data/lib/mongo/operation/kill_cursors.rb +3 -3
  79. data/lib/mongo/operation/read.rb +0 -4
  80. data/lib/mongo/operation/read/get_more.rb +2 -22
  81. data/lib/mongo/operation/read/query.rb +2 -21
  82. data/lib/mongo/operation/{read_preferrable.rb → read_preference.rb} +3 -2
  83. data/lib/mongo/operation/specifiable.rb +24 -0
  84. data/lib/mongo/operation/write.rb +2 -0
  85. data/lib/mongo/operation/write/bulk.rb +6 -3
  86. data/lib/mongo/operation/write/bulk/bulkable.rb +82 -0
  87. data/lib/mongo/operation/write/bulk/delete.rb +71 -0
  88. data/lib/mongo/operation/write/bulk/delete/result.rb +74 -0
  89. data/lib/mongo/operation/write/bulk/insert.rb +96 -0
  90. data/lib/mongo/operation/write/bulk/insert/result.rb +129 -0
  91. data/lib/mongo/operation/write/bulk/legacy_mergable.rb +87 -0
  92. data/lib/mongo/operation/write/bulk/mergable.rb +71 -0
  93. data/lib/mongo/operation/write/bulk/update.rb +81 -0
  94. data/lib/mongo/operation/write/bulk/update/result.rb +174 -0
  95. data/lib/mongo/operation/write/command/create_index.rb +0 -1
  96. data/lib/mongo/operation/write/command/create_user.rb +0 -1
  97. data/lib/mongo/operation/write/command/delete.rb +0 -1
  98. data/lib/mongo/operation/write/command/drop_index.rb +0 -1
  99. data/lib/mongo/operation/write/command/insert.rb +0 -1
  100. data/lib/mongo/operation/write/command/remove_user.rb +0 -1
  101. data/lib/mongo/operation/write/command/update.rb +0 -1
  102. data/lib/mongo/operation/write/command/update_user.rb +0 -1
  103. data/lib/mongo/operation/write/command/writable.rb +13 -18
  104. data/lib/mongo/operation/write/create_index.rb +4 -27
  105. data/lib/mongo/operation/write/create_user.rb +4 -30
  106. data/lib/mongo/operation/write/delete.rb +5 -28
  107. data/lib/mongo/operation/write/drop_index.rb +3 -3
  108. data/lib/mongo/operation/write/gle.rb +48 -0
  109. data/lib/mongo/operation/write/idable.rb +5 -0
  110. data/lib/mongo/operation/write/insert.rb +2 -24
  111. data/lib/mongo/operation/write/remove_user.rb +4 -27
  112. data/lib/mongo/operation/write/update.rb +4 -32
  113. data/lib/mongo/operation/write/update_user.rb +4 -30
  114. data/lib/mongo/operation/write/write_command_enabled.rb +53 -0
  115. data/lib/mongo/options/mapper.rb +4 -2
  116. data/lib/mongo/protocol/delete.rb +68 -3
  117. data/lib/mongo/protocol/get_more.rb +54 -2
  118. data/lib/mongo/protocol/insert.rb +59 -1
  119. data/lib/mongo/protocol/kill_cursors.rb +53 -4
  120. data/lib/mongo/protocol/message.rb +12 -12
  121. data/lib/mongo/protocol/query.rb +139 -65
  122. data/lib/mongo/protocol/reply.rb +69 -1
  123. data/lib/mongo/protocol/update.rb +70 -1
  124. data/lib/mongo/server/connection.rb +11 -3
  125. data/lib/mongo/server/description.rb +29 -0
  126. data/lib/mongo/server/description/features.rb +2 -1
  127. data/lib/mongo/server/monitor.rb +2 -2
  128. data/lib/mongo/server_selector.rb +14 -10
  129. data/lib/mongo/server_selector/selectable.rb +24 -22
  130. data/lib/mongo/socket.rb +6 -3
  131. data/lib/mongo/socket/tcp.rb +2 -2
  132. data/lib/mongo/socket/unix.rb +5 -8
  133. data/lib/mongo/uri.rb +243 -139
  134. data/lib/mongo/version.rb +1 -1
  135. data/spec/mongo/address/unix_spec.rb +1 -1
  136. data/spec/mongo/address_spec.rb +25 -0
  137. data/spec/mongo/auth/ldap/conversation_spec.rb +43 -0
  138. data/spec/mongo/auth/user/view_spec.rb +26 -1
  139. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +271 -0
  140. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +239 -0
  141. data/spec/mongo/bulk_write_spec.rb +332 -166
  142. data/spec/mongo/client_spec.rb +25 -0
  143. data/spec/mongo/cluster/topology/replica_set_spec.rb +2 -0
  144. data/spec/mongo/collection/view/aggregation_spec.rb +65 -0
  145. data/spec/mongo/collection/view/immutable_spec.rb +103 -0
  146. data/spec/mongo/collection/view/map_reduce_spec.rb +98 -3
  147. data/spec/mongo/collection/view/readable_spec.rb +17 -30
  148. data/spec/mongo/collection/view_spec.rb +233 -7
  149. data/spec/mongo/collection_spec.rb +360 -18
  150. data/spec/mongo/command_monitoring_spec.rb +51 -0
  151. data/spec/mongo/connection_string_spec.rb +137 -0
  152. data/spec/mongo/database_spec.rb +27 -11
  153. data/spec/mongo/grid/file/chunk_spec.rb +5 -5
  154. data/spec/mongo/grid/file/{metadata_spec.rb → info_spec.rb} +29 -17
  155. data/spec/mongo/grid/file_spec.rb +8 -8
  156. data/spec/mongo/grid/fs_bucket_spec.rb +1020 -0
  157. data/spec/mongo/grid/stream/read_spec.rb +275 -0
  158. data/spec/mongo/grid/stream/write_spec.rb +440 -0
  159. data/spec/mongo/grid/stream_spec.rb +48 -0
  160. data/spec/mongo/gridfs_spec.rb +50 -0
  161. data/spec/mongo/logger_spec.rb +0 -40
  162. data/spec/mongo/monitoring/command_log_subscriber_spec.rb +76 -0
  163. data/spec/mongo/operation/{aggregate_spec.rb → commands/aggregate_spec.rb} +0 -42
  164. data/spec/mongo/operation/{read → commands}/collections_info_spec.rb +1 -1
  165. data/spec/mongo/operation/{command_spec.rb → commands/command_spec.rb} +0 -0
  166. data/spec/mongo/operation/{read → commands}/indexes_spec.rb +1 -1
  167. data/spec/mongo/operation/{map_reduce_spec.rb → commands/map_reduce_spec.rb} +0 -18
  168. data/spec/mongo/operation/kill_cursors_spec.rb +1 -1
  169. data/spec/mongo/operation/{read_preferrable_spec.rb → read_preference_spec.rb} +11 -11
  170. data/spec/mongo/operation/write/bulk/{bulk_delete_spec.rb → delete_spec.rb} +1 -12
  171. data/spec/mongo/operation/write/bulk/{bulk_insert_spec.rb → insert_spec.rb} +1 -12
  172. data/spec/mongo/operation/write/bulk/{bulk_update_spec.rb → update_spec.rb} +1 -12
  173. data/spec/mongo/operation/write/insert_spec.rb +0 -11
  174. data/spec/mongo/protocol/kill_cursors_spec.rb +5 -3
  175. data/spec/mongo/server/description_spec.rb +42 -0
  176. data/spec/mongo/server/monitor_spec.rb +21 -0
  177. data/spec/mongo/server_discovery_and_monitoring_spec.rb +1 -0
  178. data/spec/mongo/server_selection_spec.rb +3 -3
  179. data/spec/mongo/server_selector/nearest_spec.rb +34 -27
  180. data/spec/mongo/server_selector/primary_preferred_spec.rb +31 -30
  181. data/spec/mongo/server_selector/primary_spec.rb +14 -13
  182. data/spec/mongo/server_selector/secondary_preferred_spec.rb +27 -26
  183. data/spec/mongo/server_selector/secondary_spec.rb +23 -22
  184. data/spec/mongo/server_selector_spec.rb +87 -24
  185. data/spec/mongo/socket/unix_spec.rb +52 -0
  186. data/spec/mongo/uri_spec.rb +251 -39
  187. data/spec/spec_helper.rb +11 -4
  188. data/spec/support/authorization.rb +4 -5
  189. data/spec/support/command_monitoring.rb +365 -0
  190. data/spec/support/command_monitoring/bulkWrite.yml +73 -0
  191. data/spec/support/command_monitoring/command.yml +42 -0
  192. data/spec/support/command_monitoring/deleteMany.yml +55 -0
  193. data/spec/support/command_monitoring/deleteOne.yml +55 -0
  194. data/spec/support/command_monitoring/find.yml +219 -0
  195. data/spec/support/command_monitoring/insertMany.yml +81 -0
  196. data/spec/support/command_monitoring/insertOne.yml +51 -0
  197. data/spec/support/command_monitoring/updateMany.yml +67 -0
  198. data/spec/support/command_monitoring/updateOne.yml +95 -0
  199. data/spec/support/connection_string.rb +228 -0
  200. data/spec/support/connection_string_tests/invalid-uris.yml +193 -0
  201. data/spec/support/connection_string_tests/valid-auth.yml +256 -0
  202. data/spec/support/connection_string_tests/valid-host_identifiers.yml +121 -0
  203. data/spec/support/connection_string_tests/valid-options.yml +30 -0
  204. data/spec/support/connection_string_tests/valid-unix_socket-absolute.yml +197 -0
  205. data/spec/support/connection_string_tests/valid-unix_socket-relative.yml +213 -0
  206. data/spec/support/connection_string_tests/valid-warnings.yml +55 -0
  207. data/spec/support/crud.rb +3 -1
  208. data/spec/support/crud/read.rb +14 -10
  209. data/spec/support/crud/write.rb +36 -9
  210. data/spec/support/gridfs.rb +637 -0
  211. data/spec/support/gridfs_tests/delete.yml +157 -0
  212. data/spec/support/gridfs_tests/download.yml +210 -0
  213. data/spec/support/gridfs_tests/download_by_name.yml +113 -0
  214. data/spec/support/gridfs_tests/upload.yml +158 -0
  215. data/spec/support/sdam/rs/equal_electionids.yml +1 -2
  216. data/spec/support/sdam/rs/new_primary_new_electionid.yml +0 -3
  217. data/spec/support/sdam/rs/primary_mismatched_me.yml +37 -0
  218. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +75 -0
  219. data/spec/support/sdam/rs/secondary_mismatched_me.yml +37 -0
  220. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
  221. data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
  222. data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
  223. data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
  224. data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
  225. data/spec/support/sdam/single/not_ok_response.yml +0 -1
  226. data/spec/support/server_discovery_and_monitoring.rb +3 -1
  227. data/spec/support/server_selection.rb +3 -1
  228. data/spec/support/shared/bulk_write.rb +192 -0
  229. data/spec/support/shared/server_selector.rb +21 -12
  230. metadata +147 -57
  231. metadata.gz.sig +0 -0
  232. data/lib/mongo/bulk_write/bulk_writable.rb +0 -252
  233. data/lib/mongo/bulk_write/deletable.rb +0 -57
  234. data/lib/mongo/bulk_write/insertable.rb +0 -49
  235. data/lib/mongo/bulk_write/replacable.rb +0 -58
  236. data/lib/mongo/bulk_write/updatable.rb +0 -69
  237. data/lib/mongo/grid/fs.rb +0 -146
  238. data/lib/mongo/operation/list_collections/result.rb +0 -114
  239. data/lib/mongo/operation/list_indexes/result.rb +0 -118
  240. data/lib/mongo/operation/read/collections_info.rb +0 -68
  241. data/lib/mongo/operation/read/indexes.rb +0 -69
  242. data/lib/mongo/operation/read/list_collections.rb +0 -76
  243. data/lib/mongo/operation/read/list_indexes.rb +0 -78
  244. data/lib/mongo/operation/write/bulk/bulk_delete.rb +0 -145
  245. data/lib/mongo/operation/write/bulk/bulk_delete/result.rb +0 -75
  246. data/lib/mongo/operation/write/bulk/bulk_insert.rb +0 -132
  247. data/lib/mongo/operation/write/bulk/bulk_insert/result.rb +0 -130
  248. data/lib/mongo/operation/write/bulk/bulk_mergable.rb +0 -67
  249. data/lib/mongo/operation/write/bulk/bulk_update.rb +0 -154
  250. data/lib/mongo/operation/write/bulk/bulk_update/result.rb +0 -174
  251. data/lib/mongo/operation/write/bulk/legacy_bulk_mergable.rb +0 -83
  252. data/spec/mongo/grid/fs_spec.rb +0 -160
  253. data/spec/mongo/loggable_spec.rb +0 -63
@@ -105,9 +105,12 @@ module Mongo
105
105
  #
106
106
  # @since 2.0.0
107
107
  def dispatch(messages, operation_id = nil)
108
- publish_command(messages, operation_id || Monitoring.next_operation_id) do |msgs|
109
- write(msgs)
110
- msgs.last.replyable? ? read : nil
108
+ if monitoring.subscribers?(Monitoring::COMMAND)
109
+ publish_command(messages, operation_id || Monitoring.next_operation_id) do |msgs|
110
+ deliver(msgs)
111
+ end
112
+ else
113
+ deliver(messages)
111
114
  end
112
115
  end
113
116
 
@@ -138,6 +141,11 @@ module Mongo
138
141
 
139
142
  private
140
143
 
144
+ def deliver(messages)
145
+ write(messages)
146
+ messages.last.replyable? ? read : nil
147
+ end
148
+
141
149
  def setup_authentication!
142
150
  if options[:user]
143
151
  default_mechanism = @server.features.scram_sha_1_enabled? ? :scram : :mongodb_cr
@@ -84,6 +84,11 @@ module Mongo
84
84
  # @since 2.0.0
85
85
  MAX_WRITE_BATCH_SIZE = 'maxWriteBatchSize'.freeze
86
86
 
87
+ # Constant for reading the me field.
88
+ #
89
+ # @since 2.1.0
90
+ ME = 'me'.freeze
91
+
87
92
  # Default max write batch size.
88
93
  #
89
94
  # @since 2.0.0
@@ -302,6 +307,18 @@ module Mongo
302
307
  config[MIN_WIRE_VERSION] || LEGACY_WIRE_VERSION
303
308
  end
304
309
 
310
+ # Get the me field value.
311
+ #
312
+ # @example Get the me field value.
313
+ # description.me
314
+ #
315
+ # @return [ String ] The me field.
316
+ #
317
+ # @since 2.1.0
318
+ def me
319
+ config[ME]
320
+ end
321
+
305
322
  # Get the tags configured for the server.
306
323
  #
307
324
  # @example Get the tags.
@@ -511,6 +528,18 @@ module Mongo
511
528
  !(standalone? || mongos?)
512
529
  end
513
530
 
531
+ # Check if there is a mismatch between the address host and the me field.
532
+ #
533
+ # @example Check if there is a mismatch.
534
+ # description.me_mismatch?
535
+ #
536
+ # @return [ true, false ] If there is a mismatch between the me field and the address host.
537
+ #
538
+ # @since 2.0.6
539
+ def me_mismatch?
540
+ !!(address.to_s != me if me)
541
+ end
542
+
514
543
  # Check equality of two descriptions.
515
544
  #
516
545
  # @example Check description equality.
@@ -28,7 +28,8 @@ module Mongo
28
28
  :list_collections => 3,
29
29
  :list_indexes => 3,
30
30
  :scram_sha_1 => 3,
31
- :write_command => 2
31
+ :write_command => 2,
32
+ :users_info => 2
32
33
  }.freeze
33
34
 
34
35
  # The wire protocol versions that this version of the driver supports.
@@ -140,7 +140,7 @@ module Mongo
140
140
  #
141
141
  # @since 2.0.0
142
142
  def stop!
143
- @thread.kill && @thread.stop?
143
+ connection.disconnect! && @thread.kill && @thread.stop?
144
144
  end
145
145
 
146
146
  # Restarts the server monitor unless the current thread is alive.
@@ -173,7 +173,7 @@ module Mongo
173
173
  result = connection.dispatch([ ISMASTER ]).documents[0]
174
174
  return result, calculate_average_round_trip_time(start)
175
175
  rescue Exception => e
176
- log_debug([ e.message ])
176
+ log_debug(e.message)
177
177
  return {}, calculate_average_round_trip_time(start)
178
178
  end
179
179
  end
@@ -27,6 +27,17 @@ module Mongo
27
27
  module ServerSelector
28
28
  extend self
29
29
 
30
+ # The max latency in seconds between the closest server and other servers
31
+ # considered for selection.
32
+ #
33
+ # @since 2.0.0
34
+ LOCAL_THRESHOLD = 0.015.freeze
35
+
36
+ # How long to block for server selection before throwing an exception.
37
+ #
38
+ # @since 2.0.0
39
+ SERVER_SELECTION_TIMEOUT = 30.freeze
40
+
30
41
  # Hash lookup for the selector classes based off the symbols
31
42
  # provided in configuration.
32
43
  #
@@ -43,20 +54,13 @@ module Mongo
43
54
  #
44
55
  # @example Get a server selector object for selecting a secondary with
45
56
  # specific tag sets.
46
- # Mongo::ServerSelector.get({ :mode => :secondary, :tag_sets => [{'dc' => 'nyc'}] })
57
+ # Mongo::ServerSelector.get(:mode => :secondary, :tag_sets => [{'dc' => 'nyc'}])
47
58
  #
48
59
  # @param [ Hash ] preference The server preference.
49
- # @param [ Hash ] options The preference options.
50
- #
51
- # @option preference :mode [ Symbol ] The preference mode.
52
- # @option preference :tag_sets [ Array<Hash> ] The tag sets.
53
60
  #
54
61
  # @since 2.0.0
55
- def get(preference = {}, options = {})
56
- PREFERENCES.fetch(preference[:mode] || :primary).new(
57
- preference[:tag_sets] || [],
58
- options
59
- )
62
+ def get(preference = {})
63
+ PREFERENCES.fetch(preference[:mode] || :primary).new(preference)
60
64
  end
61
65
  end
62
66
  end
@@ -20,17 +20,6 @@ module Mongo
20
20
  # @since 2.0.0
21
21
  module Selectable
22
22
 
23
- # The max latency in seconds between the closest server and other servers
24
- # considered for selection.
25
- #
26
- # @since 2.0.0
27
- LOCAL_THRESHOLD = 0.015.freeze
28
-
29
- # How long to block for server selection before throwing an exception.
30
- #
31
- # @since 2.0.0
32
- SERVER_SELECTION_TIMEOUT = 30.freeze
33
-
34
23
  # @return [ Hash ] options The options.
35
24
  attr_reader :options
36
25
 
@@ -53,22 +42,27 @@ module Mongo
53
42
 
54
43
  # Initialize the server selector.
55
44
  #
56
- # @example Initialize the preference with tag sets.
57
- # Mongo::ServerSelector::Secondary.new([{ 'tag' => 'set' }])
45
+ # @example Initialize the selector.
46
+ # Mongo::ServerSelector::Secondary.new(:tag_sets => [{'dc' => 'nyc'}])
58
47
  #
59
48
  # @example Initialize the preference with no options.
60
49
  # Mongo::ServerSelector::Secondary.new
61
50
  #
62
- # @param [ Array ] tag_sets The tag sets used to select servers.
51
+ # @param [ Hash ] options The server preference options.
52
+ #
53
+ # @option options [ Integer ] :server_selection_timeout The timeout in seconds
54
+ # for selecting a server.
63
55
  #
64
- # @todo: document specific error
65
- # @raise [ Exception ] If tag sets are specified but not allowed.
56
+ # @option options [ Integer ] :local_threshold The local threshold boundary for
57
+ # nearest selection in seconds.
58
+ #
59
+ # @raise [ Error::InvalidServerPreference ] If tag sets are specified
60
+ # but not allowed.
66
61
  #
67
62
  # @since 2.0.0
68
- def initialize(tag_sets = [], options = {})
69
- if !tag_sets.all? { |set| set.empty? } && !tags_allowed?
70
- raise Error::InvalidServerPreference.new(name)
71
- end
63
+ def initialize(options = {})
64
+ tag_sets = options[:tag_sets] || []
65
+ validate_tag_sets!(tag_sets)
72
66
  @tag_sets = tag_sets
73
67
  @options = options
74
68
  end
@@ -109,7 +103,7 @@ module Mongo
109
103
  # @since 2.0.0
110
104
  def server_selection_timeout
111
105
  @server_selection_timeout ||=
112
- (options[:server_selection_timeout] || SERVER_SELECTION_TIMEOUT)
106
+ (options[:server_selection_timeout] || ServerSelector::SERVER_SELECTION_TIMEOUT)
113
107
  end
114
108
 
115
109
  # Get the local threshold boundary for nearest selection in seconds.
@@ -121,7 +115,7 @@ module Mongo
121
115
  #
122
116
  # @since 2.0.0
123
117
  def local_threshold
124
- @local_threshold ||= (options[:local_threshold] || LOCAL_THRESHOLD)
118
+ @local_threshold ||= (options[:local_threshold] || ServerSelector::LOCAL_THRESHOLD)
125
119
  end
126
120
 
127
121
  private
@@ -186,6 +180,14 @@ module Mongo
186
180
  end
187
181
  matches || []
188
182
  end
183
+
184
+ private
185
+
186
+ def validate_tag_sets!(tag_sets)
187
+ if !tag_sets.all? { |set| set.empty? } && !tags_allowed?
188
+ raise Error::InvalidServerPreference.new(name)
189
+ end
190
+ end
189
191
  end
190
192
  end
191
193
  end
data/lib/mongo/socket.rb CHANGED
@@ -173,10 +173,13 @@ module Mongo
173
173
  end
174
174
 
175
175
  def set_socket_options(sock)
176
- encoded_timeout = [ timeout, 0 ].pack(TIMEOUT_PACK)
177
176
  sock.set_encoding(BSON::BINARY)
178
- sock.setsockopt(SOL_SOCKET, SO_RCVTIMEO, encoded_timeout)
179
- sock.setsockopt(SOL_SOCKET, SO_SNDTIMEO, encoded_timeout)
177
+
178
+ unless sock.is_a?(UNIXSocket) && BSON::Environment.jruby?
179
+ encoded_timeout = [ timeout, 0 ].pack(TIMEOUT_PACK)
180
+ sock.setsockopt(SOL_SOCKET, SO_RCVTIMEO, encoded_timeout)
181
+ sock.setsockopt(SOL_SOCKET, SO_SNDTIMEO, encoded_timeout)
182
+ end
180
183
  end
181
184
 
182
185
  def handle_errors
@@ -51,8 +51,8 @@ module Mongo
51
51
  # Initializes a new TCP socket.
52
52
  #
53
53
  # @example Create the TCP socket.
54
- # TCP.new('::1', 27017, 30)
55
- # TCP.new('127.0.0.1', 27017, 30)
54
+ # TCP.new('::1', 27017, 30, Socket::PF_INET)
55
+ # TCP.new('127.0.0.1', 27017, 30, Socket::PF_INET)
56
56
  #
57
57
  # @param [ String ] host The hostname or IP address.
58
58
  # @param [ Integer ] port The port number.
@@ -38,25 +38,22 @@ module Mongo
38
38
  #
39
39
  # @since 2.0.0
40
40
  def connect!
41
- Timeout.timeout(timeout, Error::SocketTimeoutError) do
42
- handle_errors { socket.connect(path) }
43
- self
44
- end
41
+ self
45
42
  end
46
43
 
47
44
  # Initializes a new Unix socket.
48
45
  #
49
46
  # @example Create the Unix socket.
50
- # Unix.new('/path/to.sock', 27017, 30)
47
+ # Unix.new('/path/to.sock', 5)
51
48
  #
52
49
  # @param [ String ] path The path.
53
50
  # @param [ Float ] timeout The socket timeout value.
54
- # @param [ Integer ] family The socket family.
55
51
  #
56
52
  # @since 2.0.0
57
- def initialize(path, timeout, family)
53
+ def initialize(path, timeout)
58
54
  @path, @timeout = path, timeout
59
- super(family)
55
+ @socket = ::UNIXSocket.new(path)
56
+ set_socket_options(@socket)
60
57
  end
61
58
  end
62
59
  end
data/lib/mongo/uri.rb CHANGED
@@ -29,58 +29,116 @@ module Mongo
29
29
  class URI
30
30
  include Loggable
31
31
 
32
- # Scheme Regex: non-capturing, matches scheme.
32
+ # The uri parser object options.
33
33
  #
34
34
  # @since 2.0.0
35
- SCHEME = %r{(?:mongodb://)}.freeze
35
+ attr_reader :options
36
36
 
37
- # User Regex: capturing, group 1, matches anything but ':'
37
+ # The options specified in the uri.
38
38
  #
39
- # @since 2.0.0
40
- USER = /([^:]+)/.freeze
39
+ # @since 2.1.0
40
+ attr_reader :uri_options
41
41
 
42
- # Password Regex: capturing, group 2, matches anything but '@'
42
+ # The servers specified in the uri.
43
43
  #
44
44
  # @since 2.0.0
45
- PASSWORD = /([^@]+)/.freeze
45
+ attr_reader :servers
46
46
 
47
- # Credentials Regex: non capturing, matches 'user:password@'
47
+ # Unsafe characters that must be urlencoded.
48
48
  #
49
- # @since 2.0.0
50
- CREDENTIALS = /(?:#{USER}:#{PASSWORD}?@)?/.freeze
49
+ # @since 2.1.0
50
+ UNSAFE = /[\:\/\+\@]/
51
51
 
52
- # Host and port server Regex: matches anything but a forward slash
52
+ # Unix socket suffix.
53
53
  #
54
- # @since 2.0.0
55
- HOSTPORT = /[^\/]+/.freeze
54
+ # @since 2.1.0
55
+ UNIX_SOCKET = /.sock/
56
56
 
57
- # Unix socket server Regex: matches unix socket server
57
+ # The mongodb connection string scheme.
58
58
  #
59
59
  # @since 2.0.0
60
- UNIX = /\/.+.sock?/.freeze
60
+ SCHEME = 'mongodb://'.freeze
61
61
 
62
- # server Regex: capturing, matches host and port server or unix server
62
+ # The character delimiting hosts.
63
63
  #
64
- # @since 2.0.0
65
- SERVERS = /((?:(?:#{HOSTPORT}|#{UNIX}),?)+)/.freeze
64
+ # @since 2.1.0
65
+ HOST_DELIM = ','.freeze
66
66
 
67
- # Database Regex: matches anything but the characters that cannot
68
- # be part of any MongoDB database name.
67
+ # The character separating a host and port.
69
68
  #
70
- # @since 2.0.0
71
- DATABASE = %r{(?:/([^/\.\ "*<>:\|\?]*))?}.freeze
69
+ # @since 2.1.0
70
+ HOST_PORT_DELIM = ':'.freeze
72
71
 
73
- # Option Regex: only matches the ampersand separator and does
74
- # not allow for semicolon to be used to separate options.
72
+ # The character delimiting a database.
75
73
  #
76
- # @since 2.0.0
77
- OPTIONS = /(?:\?(?:(.+=.+)&?)+)*/.freeze
74
+ # @since 2.1.0
75
+ DATABASE_DELIM = '/'.freeze
78
76
 
79
- # Complete URI Regex: matches all of the combined components
77
+ # The character delimiting options.
80
78
  #
81
- # @since 2.0.0
82
- URI = /#{SCHEME}#{CREDENTIALS}#{SERVERS}#{DATABASE}#{OPTIONS}/.freeze
79
+ # @since 2.1.0
80
+ URI_OPTS_DELIM = '?'.freeze
81
+
82
+ # The character delimiting multiple options.
83
+ #
84
+ # @since 2.1.0
85
+ INDIV_URI_OPTS_DELIM = '&'.freeze
86
+
87
+ # The character delimiting an option and its value.
88
+ #
89
+ # @since 2.1.0
90
+ URI_OPTS_VALUE_DELIM = '='.freeze
83
91
 
92
+ # The character separating a username from the password.
93
+ #
94
+ # @since 2.1.0
95
+ AUTH_USER_PWD_DELIM = ':'.freeze
96
+
97
+ # The character delimiting auth credentials.
98
+ #
99
+ # @since 2.1.0
100
+ AUTH_DELIM = '@'.freeze
101
+
102
+ # Error details for an invalid scheme.
103
+ #
104
+ # @since 2.1.0
105
+ INVALID_SCHEME = "Invalid scheme. Scheme must be '#{SCHEME}'".freeze
106
+
107
+ # Error details for an invalid options format.
108
+ #
109
+ # @since 2.1.0
110
+ INVALID_OPTS_VALUE_DELIM = "Options and their values must be delimited" +
111
+ " by '#{URI_OPTS_VALUE_DELIM}'".freeze
112
+
113
+ # Error details for an non-urlencoded user name or password.
114
+ #
115
+ # @since 2.1.0
116
+ UNESCAPED_USER_PWD = "User name and password must be urlencoded.".freeze
117
+
118
+ # Error details for a non-urlencoded unix socket path.
119
+ #
120
+ # @since 2.1.0
121
+ UNESCAPED_UNIX_SOCKET = "UNIX domain sockets must be urlencoded.".freeze
122
+
123
+ # Error details for a non-urlencoded auth databsae name.
124
+ #
125
+ # @since 2.1.0
126
+ UNESCAPED_DATABASE = "Auth database must be urlencoded.".freeze
127
+
128
+ # Error details for providing options without a database delimiter.
129
+ #
130
+ # @since 2.1.0
131
+ INVALID_OPTS_DELIM = "Database delimiter '#{DATABASE_DELIM}' must be present if options are specified.".freeze
132
+
133
+ # Error details for a missing host.
134
+ #
135
+ # @since 2.1.0
136
+ INVALID_HOST = "Missing host; at least one must be provided.".freeze
137
+
138
+ # Error details for an invalid port.
139
+ #
140
+ # @since 2.1.0
141
+ INVALID_PORT = "Invalid port. Port must be an integer greater than 0 and less than 65536".freeze
84
142
 
85
143
  # MongoDB URI format specification.
86
144
  #
@@ -113,32 +171,28 @@ module Mongo
113
171
  'GSSAPI' => :gssapi
114
172
  }.freeze
115
173
 
174
+ # Options that are allowed to appear more than once in the uri.
175
+ #
176
+ # @since 2.1.0
177
+ REPEATABLE_OPTIONS = [ :tag_sets ]
178
+
116
179
  # Create the new uri from the provided string.
117
180
  #
118
181
  # @example Create the new URI.
119
182
  # URI.new('mongodb://localhost:27017')
120
183
  #
121
184
  # @param [ String ] string The uri string.
185
+ # @param [ Hash ] options The options.
122
186
  #
123
- # @raise [ BadURI ] If the uri does not match the spec.
187
+ # @raise [ Error::InvalidURI ] If the uri does not match the spec.
124
188
  #
125
189
  # @since 2.0.0
126
- def initialize(string)
190
+ def initialize(string, options = {})
127
191
  @string = string
128
- @match = @string.match(URI)
129
- raise Error::InvalidURI.new(string) unless @match
130
- end
131
-
132
- # Get the servers provided in the URI.
133
- #
134
- # @example Get the servers.
135
- # uri.servers
136
- #
137
- # @return [ Array<String> ] The servers.
138
- #
139
- # @since 2.0.0
140
- def servers
141
- @match[3].split(',')
192
+ @options = options
193
+ empty, scheme, remaining = @string.partition(SCHEME)
194
+ raise_invalid_error!(INVALID_SCHEME) unless scheme == SCHEME
195
+ setup!(remaining)
142
196
  end
143
197
 
144
198
  # Gets the options hash that needs to be passed to a Mongo::Client on
@@ -152,8 +206,8 @@ module Mongo
152
206
  #
153
207
  # @since 2.0.0
154
208
  def client_options
155
- opts = options.merge(:database => database)
156
- user ? opts.merge(credentials) : opts
209
+ opts = uri_options.merge(:database => database)
210
+ @user ? opts.merge(credentials) : opts
157
211
  end
158
212
 
159
213
  # Get the credentials provided in the URI.
@@ -167,7 +221,7 @@ module Mongo
167
221
  #
168
222
  # @since 2.0.0
169
223
  def credentials
170
- { :user => user, :password => password }
224
+ { :user => @user, :password => @password }
171
225
  end
172
226
 
173
227
  # Get the database provided in the URI.
@@ -179,115 +233,161 @@ module Mongo
179
233
  #
180
234
  # @since 2.0.0
181
235
  def database
182
- @match[4].nil? ? Database::ADMIN : @match[4]
236
+ @database ? @database : Database::ADMIN
183
237
  end
184
238
 
185
- # Get the options provided in the URI.
186
- #
187
- # @example Get The options.
188
- # uri.options
189
- #
190
- # @return [Hash] The options.
191
- #
192
- # Generic Options
193
- # * :replica_set [String] replica set name
194
- # * :connect_timeout [Fixnum] connect timeout
195
- # * :socket_timeout [Fixnum] socket timeout
196
- # * :ssl [true, false] ssl enabled?
197
- #
198
- # Write Options (returned in a hash under the :write key)
199
- # * :w [String, Fixnum] write concern value
200
- # * :j [true, false] journal
201
- # * :fsync [true, false] fsync
202
- # * :timeout [Fixnum] timeout for write operation
203
- #
204
- # Read Options (returned in a hash under the :read key)
205
- # * :mode [Symbol] read mode
206
- # * :tag_sets [Array<Hash>] read tag sets
207
- #
208
- # @since 2.0.0
209
- def options
210
- parsed_options = @match[5]
211
- return {} unless parsed_options
212
- parsed_options.split('&').reduce({}) do |options, option|
213
- key, value = option.split('=')
214
- strategy = OPTION_MAP[key]
239
+ private
240
+
241
+ def setup!(remaining)
242
+ creds_hosts, db_opts = extract_db_opts!(remaining)
243
+ parse_creds_hosts!(creds_hosts)
244
+ parse_db_opts!(db_opts)
245
+ end
246
+
247
+ def extract_db_opts!(string)
248
+ db_opts, d, creds_hosts = string.reverse.partition(DATABASE_DELIM)
249
+ db_opts, creds_hosts = creds_hosts, db_opts if creds_hosts.empty?
250
+ if db_opts.empty? && creds_hosts.include?(URI_OPTS_DELIM)
251
+ raise_invalid_error!(INVALID_OPTS_DELIM)
252
+ end
253
+ [ creds_hosts, db_opts ].map { |s| s.reverse }
254
+ end
255
+
256
+ def parse_creds_hosts!(string)
257
+ hosts, creds = split_creds_hosts(string)
258
+ @servers = parse_servers!(hosts)
259
+ @user = parse_user!(creds)
260
+ @password = parse_password!(creds)
261
+ end
262
+
263
+ def split_creds_hosts(string)
264
+ hosts, d, creds = string.reverse.partition(AUTH_DELIM)
265
+ hosts, creds = creds, hosts if hosts.empty?
266
+ [ hosts, creds ].map { |s| s.reverse }
267
+ end
268
+
269
+ def parse_db_opts!(string)
270
+ auth_db, d, uri_opts = string.partition(URI_OPTS_DELIM)
271
+ @uri_options = parse_uri_options!(uri_opts)
272
+ @database = parse_database!(auth_db)
273
+ end
274
+
275
+ def parse_uri_options!(string)
276
+ return {} unless string
277
+ string.split(INDIV_URI_OPTS_DELIM).reduce({}) do |uri_options, opt|
278
+ raise_invalid_error!(INVALID_OPTS_VALUE_DELIM) unless opt.index(URI_OPTS_VALUE_DELIM)
279
+ key, value = opt.split(URI_OPTS_VALUE_DELIM)
280
+ strategy = URI_OPTION_MAP[key.downcase]
215
281
  if strategy.nil?
216
- log_warn([
217
- "Unsupported URI option '#{key}' on URI '#{@string}'. It will be ignored."
218
- ])
282
+ log_warn("Unsupported URI option '#{key}' on URI '#{@string}'. It will be ignored.")
219
283
  else
220
- add_option(strategy, value, options)
284
+ add_uri_option(strategy, value, uri_options)
221
285
  end
222
- options
286
+ uri_options
223
287
  end
224
288
  end
225
289
 
226
- private
290
+ def parse_user!(string)
291
+ if (string && user = string.partition(AUTH_USER_PWD_DELIM)[0])
292
+ raise_invalid_error!(UNESCAPED_USER_PWD) if user =~ UNSAFE
293
+ decode(user) if user.length > 0
294
+ end
295
+ end
296
+
297
+ def parse_password!(string)
298
+ if (string && pwd = string.partition(AUTH_USER_PWD_DELIM)[2])
299
+ raise_invalid_error!(UNESCAPED_USER_PWD) if pwd =~ UNSAFE
300
+ decode(pwd) if pwd.length > 0
301
+ end
302
+ end
303
+
304
+ def parse_database!(string)
305
+ raise_invalid_error!(UNESCAPED_DATABASE) if string =~ UNSAFE
306
+ decode(string) if string.length > 0
307
+ end
308
+
309
+ def validate_port_string!(port)
310
+ unless port.nil? || (port.length > 0 && port.to_i > 0 && port.to_i <= 65535)
311
+ raise_invalid_error!(INVALID_PORT)
312
+ end
313
+ end
314
+
315
+ def parse_servers!(string)
316
+ raise_invalid_error!(INVALID_HOST) unless string.size > 0
317
+ string.split(HOST_DELIM).reduce([]) do |servers, host|
318
+ if host[0] == '['
319
+ if host.index(']:')
320
+ h, p = host.split(']:')
321
+ validate_port_string!(p)
322
+ end
323
+ elsif host.index(HOST_PORT_DELIM)
324
+ h, d, p = host.partition(HOST_PORT_DELIM)
325
+ raise_invalid_error!(INVALID_HOST) unless h.size > 0
326
+ validate_port_string!(p)
327
+ elsif host =~ UNIX_SOCKET
328
+ raise_invalid_error!(UNESCAPED_UNIX_SOCKET) if host =~ UNSAFE
329
+ end
330
+ servers << host
331
+ end
332
+ end
333
+
334
+ def raise_invalid_error!(details)
335
+ raise Error::InvalidURI.new(@string, details)
336
+ end
337
+
338
+ def decode(value)
339
+ ::URI.decode(value)
340
+ end
227
341
 
228
342
  # Hash for storing map of URI option parameters to conversion strategies
229
- OPTION_MAP = {}
343
+ URI_OPTION_MAP = {}
230
344
 
231
- # Simple internal dsl to register a MongoDB URI option in the OPTION_MAP.
345
+ # Simple internal dsl to register a MongoDB URI option in the URI_OPTION_MAP.
232
346
  #
233
347
  # @param uri_key [String] The MongoDB URI option to register.
234
348
  # @param name [Symbol] The name of the option in the driver.
235
349
  # @param extra [Hash] Extra options.
236
350
  # * :group [Symbol] Nested hash where option will go.
237
351
  # * :type [Symbol] Name of function to transform value.
238
- def self.option(uri_key, name, extra = {})
239
- OPTION_MAP[uri_key] = { :name => name }.merge(extra)
352
+ def self.uri_option(uri_key, name, extra = {})
353
+ URI_OPTION_MAP[uri_key] = { :name => name }.merge(extra)
240
354
  end
241
355
 
242
356
  # Replica Set Options
243
- option 'replicaSet', :replica_set, :type => :replica_set
357
+ uri_option 'replicaset', :replica_set, :type => :replica_set
244
358
 
245
359
  # Timeout Options
246
- option 'connectTimeoutMS', :connect_timeout, :type => :ms_convert
247
- option 'socketTimeoutMS', :socket_timeout, :type => :ms_convert
248
- option 'serverSelectionTimeoutMS', :server_selection_timeout, :type => :ms_convert
249
- option 'localThresholdMS', :local_threshold, :type => :ms_convert
360
+ uri_option 'connecttimeoutms', :connect_timeout, :type => :ms_convert
361
+ uri_option 'sockettimeoutms', :socket_timeout, :type => :ms_convert
362
+ uri_option 'serverselectiontimeoutms', :server_selection_timeout, :type => :ms_convert
363
+ uri_option 'localthresholdms', :local_threshold, :type => :ms_convert
250
364
 
251
365
  # Write Options
252
- option 'w', :w, :group => :write
253
- option 'journal', :j, :group => :write
254
- option 'fsync', :fsync, :group => :write
255
- option 'wtimeoutMS', :timeout, :group => :write
366
+ uri_option 'w', :w, :group => :write
367
+ uri_option 'journal', :j, :group => :write
368
+ uri_option 'fsync', :fsync, :group => :write
369
+ uri_option 'wtimeoutms', :timeout, :group => :write
256
370
 
257
371
  # Read Options
258
- option 'readPreference', :mode, :group => :read, :type => :read_mode
259
- option 'readPreferenceTags', :tag_sets, :group => :read, :type => :read_tags
372
+ uri_option 'readpreference', :mode, :group => :read, :type => :read_mode
373
+ uri_option 'readpreferencetags', :tag_sets, :group => :read, :type => :read_tags
260
374
 
261
375
  # Pool options
262
- option 'minPoolSize', :min_pool_size
263
- option 'maxPoolSize', :max_pool_size
264
- option 'waitQueueTimeoutMS', :wait_queue_timeout, :type => :ms_convert
376
+ uri_option 'minpoolsize', :min_pool_size
377
+ uri_option 'maxpoolsize', :max_pool_size
378
+ uri_option 'waitqueuetimeoutms', :wait_queue_timeout, :type => :ms_convert
265
379
 
266
380
  # Security Options
267
- option 'ssl', :ssl
381
+ uri_option 'ssl', :ssl
268
382
 
269
383
  # Topology options
270
- option 'connect', :connect
384
+ uri_option 'connect', :connect
271
385
 
272
386
  # Auth Options
273
- option 'authSource', :source, :group => :auth, :type => :auth_source
274
- option 'authMechanism', :mechanism, :group => :auth, :type => :auth_mech
275
- option 'authMechanismProperties', :auth_mech_properties, :group => :auth,
276
- :type => :auth_mech_props
277
-
278
- # Gets the user provided in the URI
279
- #
280
- # @return [String] The user.
281
- def user
282
- @match[1]
283
- end
284
-
285
- # Gets the password provided in the URI
286
- #
287
- # @return [String] The password.
288
- def password
289
- @match[2]
290
- end
387
+ uri_option 'authsource', :source, :group => :auth, :type => :auth_source
388
+ uri_option 'authmechanism', :auth_mech, :type => :auth_mech
389
+ uri_option 'authmechanismproperties', :auth_mech_properties, :group => :auth,
390
+ :type => :auth_mech_props
291
391
 
292
392
  # Casts option values that do not have a specifically provided
293
393
  # transofrmation to the appropriate type.
@@ -303,7 +403,7 @@ module Mongo
303
403
  elsif value =~ /[\d]/
304
404
  value.to_i
305
405
  else
306
- value.to_sym
406
+ decode(value).to_sym
307
407
  end
308
408
  end
309
409
 
@@ -322,15 +422,15 @@ module Mongo
322
422
 
323
423
  # Selects the output destination for an option.
324
424
  #
325
- # @param options [Hash] The base target.
326
- # @param group [Symbol] Group subtarget.
425
+ # @param [Hash] uri_options The base target.
426
+ # @param [Symbol] group Group subtarget.
327
427
  #
328
428
  # @return [Hash] The target for the option.
329
- def select_target(options, group = nil)
429
+ def select_target(uri_options, group = nil)
330
430
  if group
331
- options[group] ||= {}
431
+ uri_options[group] ||= {}
332
432
  else
333
- options
433
+ uri_options
334
434
  end
335
435
  end
336
436
 
@@ -345,15 +445,19 @@ module Mongo
345
445
  # @param target [Hash] The destination.
346
446
  # @param value [Object] The value to be merged.
347
447
  # @param name [Symbol] The name of the option.
348
- def merge_option(target, value, name)
448
+ def merge_uri_option(target, value, name)
349
449
  if target.key?(name)
350
- target[name] += value
450
+ if REPEATABLE_OPTIONS.include?(name)
451
+ target[name] += value
452
+ else
453
+ log_warn("Repeated option key: #{name}.")
454
+ end
351
455
  else
352
456
  target.merge!(name => value)
353
457
  end
354
458
  end
355
459
 
356
- # Adds an option to the options hash via the supplied strategy.
460
+ # Adds an option to the uri options hash via the supplied strategy.
357
461
  #
358
462
  # Acquires a target for the option based on group.
359
463
  # Transforms the value.
@@ -361,11 +465,11 @@ module Mongo
361
465
  #
362
466
  # @param strategy [Symbol] The strategy for this option.
363
467
  # @param value [String] The value of the option.
364
- # @param options [Hash] The base option target.
365
- def add_option(strategy, value, options)
366
- target = select_target(options, strategy[:group])
468
+ # @param uri_options [Hash] The base option target.
469
+ def add_uri_option(strategy, value, uri_options)
470
+ target = select_target(uri_options, strategy[:group])
367
471
  value = apply_transform(value, strategy[:type])
368
- merge_option(target, value, strategy[:name])
472
+ merge_uri_option(target, value, strategy[:name])
369
473
  end
370
474
 
371
475
  # Replica set transformation, avoid converting to Symbol.
@@ -374,7 +478,7 @@ module Mongo
374
478
  #
375
479
  # @return [String] Same value to avoid cast to Symbol.
376
480
  def replica_set(value)
377
- value
481
+ decode(value)
378
482
  end
379
483
 
380
484
  # Auth source transformation, either db string or :external.
@@ -384,7 +488,7 @@ module Mongo
384
488
  # @return [String] If auth source is database name.
385
489
  # @return [:external] If auth source is external authentication.
386
490
  def auth_source(value)
387
- value == '$external' ? :external : value
491
+ value == '$external' ? :external : decode(value)
388
492
  end
389
493
 
390
494
  # Authentication mechanism transformation.
@@ -458,7 +562,7 @@ module Mongo
458
562
  def hash_extractor(value)
459
563
  value.split(',').reduce({}) do |set, tag|
460
564
  k, v = tag.split(':')
461
- set.merge(k.downcase.to_sym => v)
565
+ set.merge(decode(k).downcase.to_sym => decode(v))
462
566
  end
463
567
  end
464
568
  end