mongo 1.12.5 → 2.0.0.beta

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 (437) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CONTRIBUTING.md +64 -0
  5. data/LICENSE +1 -1
  6. data/README.md +23 -125
  7. data/Rakefile +26 -21
  8. data/bin/mongo_console +6 -38
  9. data/lib/mongo.rb +23 -82
  10. data/lib/mongo/address.rb +111 -0
  11. data/lib/mongo/address/ipv4.rb +85 -0
  12. data/lib/mongo/address/ipv6.rb +85 -0
  13. data/lib/mongo/address/unix.rb +76 -0
  14. data/lib/mongo/auth.rb +108 -0
  15. data/lib/mongo/auth/cr.rb +44 -0
  16. data/lib/mongo/auth/cr/conversation.rb +119 -0
  17. data/lib/mongo/auth/executable.rb +52 -0
  18. data/lib/mongo/auth/ldap.rb +48 -0
  19. data/lib/mongo/auth/ldap/conversation.rb +92 -0
  20. data/lib/mongo/auth/roles.rb +104 -0
  21. data/lib/mongo/auth/scram.rb +53 -0
  22. data/lib/mongo/auth/scram/conversation.rb +450 -0
  23. data/lib/mongo/auth/user.rb +159 -0
  24. data/lib/mongo/auth/user/view.rb +102 -0
  25. data/lib/mongo/auth/x509.rb +48 -0
  26. data/lib/mongo/auth/x509/conversation.rb +92 -0
  27. data/lib/mongo/{gridfs.rb → bulk.rb} +2 -5
  28. data/lib/mongo/bulk/bulk_write.rb +307 -0
  29. data/lib/mongo/client.rb +233 -0
  30. data/lib/mongo/cluster.rb +203 -0
  31. data/lib/mongo/cluster/topology.rb +60 -0
  32. data/lib/mongo/cluster/topology/replica_set.rb +160 -0
  33. data/lib/mongo/cluster/topology/sharded.rb +132 -0
  34. data/lib/mongo/cluster/topology/standalone.rb +132 -0
  35. data/lib/mongo/cluster/topology/unknown.rb +155 -0
  36. data/lib/mongo/collection.rb +130 -1101
  37. data/lib/mongo/collection/view.rb +169 -0
  38. data/lib/mongo/collection/view/aggregation.rb +108 -0
  39. data/lib/mongo/collection/view/explainable.rb +49 -0
  40. data/lib/mongo/collection/view/immutable.rb +43 -0
  41. data/lib/mongo/collection/view/iterable.rb +48 -0
  42. data/lib/mongo/collection/view/map_reduce.rb +191 -0
  43. data/lib/mongo/collection/view/readable.rb +363 -0
  44. data/lib/mongo/collection/view/writable.rb +169 -0
  45. data/lib/mongo/cursor.rb +79 -680
  46. data/lib/mongo/database.rb +224 -0
  47. data/lib/mongo/database/view.rb +101 -0
  48. data/lib/mongo/error.rb +81 -0
  49. data/lib/mongo/error/bulk_write_failure.rb +41 -0
  50. data/lib/mongo/{utils/thread_local_variable_manager.rb → error/empty_batch.rb} +22 -8
  51. data/{test/functional/db_connection_test.rb → lib/mongo/error/invalid_bulk_operation.rb} +19 -8
  52. data/lib/mongo/error/invalid_collection_name.rb +39 -0
  53. data/lib/mongo/error/invalid_database_name.rb +39 -0
  54. data/{test/replica_set/ssl_test.rb → lib/mongo/error/invalid_document.rb} +21 -14
  55. data/lib/mongo/error/invalid_file.rb +38 -0
  56. data/lib/mongo/error/invalid_nonce.rb +46 -0
  57. data/lib/mongo/error/invalid_replacement_document.rb +39 -0
  58. data/lib/mongo/error/invalid_signature.rb +47 -0
  59. data/{test/functional/ssl_test.rb → lib/mongo/error/invalid_update_document.rb} +22 -12
  60. data/lib/mongo/error/max_bson_size.rb +40 -0
  61. data/lib/mongo/error/max_message_size.rb +42 -0
  62. data/lib/mongo/{utils.rb → error/need_primary_server.rb} +10 -6
  63. data/lib/mongo/{connection.rb → error/operation_failure.rb} +10 -6
  64. data/lib/mongo/error/parser.rb +77 -0
  65. data/lib/mongo/{connection/socket.rb → error/socket_error.rb} +10 -5
  66. data/lib/mongo/error/socket_timeout_error.rb +23 -0
  67. data/lib/mongo/error/unsupported_features.rb +43 -0
  68. data/lib/mongo/event.rb +40 -0
  69. data/lib/mongo/event/listeners.rb +63 -0
  70. data/lib/mongo/event/primary_elected.rb +53 -0
  71. data/lib/mongo/event/publisher.rb +42 -0
  72. data/lib/mongo/event/server_added.rb +53 -0
  73. data/lib/mongo/event/server_removed.rb +53 -0
  74. data/lib/mongo/event/subscriber.rb +41 -0
  75. data/lib/mongo/grid.rb +16 -0
  76. data/lib/mongo/grid/file.rb +94 -0
  77. data/lib/mongo/grid/file/chunk.rb +184 -0
  78. data/lib/mongo/grid/file/metadata.rb +223 -0
  79. data/lib/mongo/grid/fs.rb +149 -0
  80. data/lib/mongo/index.rb +64 -0
  81. data/lib/mongo/index/view.rb +205 -0
  82. data/lib/mongo/loggable.rb +126 -0
  83. data/lib/mongo/logger.rb +132 -0
  84. data/lib/mongo/operation.rb +26 -0
  85. data/lib/mongo/operation/aggregate.rb +100 -0
  86. data/lib/mongo/operation/aggregate/result.rb +84 -0
  87. data/lib/mongo/operation/batchable.rb +103 -0
  88. data/lib/mongo/operation/bulk_delete/result.rb +197 -0
  89. data/lib/mongo/operation/bulk_insert/result.rb +195 -0
  90. data/lib/mongo/operation/bulk_update/result.rb +295 -0
  91. data/lib/mongo/operation/command.rb +62 -0
  92. data/lib/mongo/operation/executable.rb +105 -0
  93. data/lib/mongo/operation/kill_cursors.rb +39 -0
  94. data/lib/mongo/operation/limited.rb +37 -0
  95. data/lib/mongo/operation/list_collections/result.rb +116 -0
  96. data/lib/mongo/operation/list_indexes/result.rb +118 -0
  97. data/lib/mongo/operation/map_reduce.rb +96 -0
  98. data/lib/mongo/operation/map_reduce/result.rb +122 -0
  99. data/lib/mongo/{functional.rb → operation/read.rb} +7 -7
  100. data/lib/mongo/operation/read/collections_info.rb +67 -0
  101. data/lib/mongo/operation/read/get_more.rb +71 -0
  102. data/lib/mongo/operation/read/indexes.rb +68 -0
  103. data/lib/mongo/operation/read/list_collections.rb +75 -0
  104. data/lib/mongo/operation/read/list_indexes.rb +77 -0
  105. data/lib/mongo/operation/read/query.rb +71 -0
  106. data/lib/mongo/operation/read_preferrable.rb +34 -0
  107. data/lib/mongo/operation/result.rb +259 -0
  108. data/lib/mongo/operation/specifiable.rb +380 -0
  109. data/lib/mongo/operation/write.rb +25 -0
  110. data/lib/mongo/operation/write/bulk_delete.rb +158 -0
  111. data/lib/mongo/operation/write/bulk_insert.rb +160 -0
  112. data/lib/mongo/operation/write/bulk_update.rb +167 -0
  113. data/lib/mongo/{connection/socket/socket_util.rb → operation/write/command.rb} +9 -24
  114. data/lib/mongo/operation/write/command/create_user.rb +43 -0
  115. data/lib/mongo/operation/write/command/delete.rb +56 -0
  116. data/lib/mongo/operation/write/command/drop_index.rb +51 -0
  117. data/lib/mongo/operation/write/command/ensure_index.rb +55 -0
  118. data/lib/mongo/operation/write/command/insert.rb +55 -0
  119. data/lib/mongo/operation/write/command/remove_user.rb +42 -0
  120. data/lib/mongo/operation/write/command/update.rb +60 -0
  121. data/lib/mongo/operation/write/command/writable.rb +61 -0
  122. data/lib/mongo/operation/write/create_index.rb +84 -0
  123. data/lib/mongo/operation/write/create_user.rb +75 -0
  124. data/lib/mongo/operation/write/delete.rb +91 -0
  125. data/lib/mongo/operation/write/drop_index.rb +62 -0
  126. data/lib/mongo/operation/write/insert.rb +88 -0
  127. data/lib/mongo/operation/write/remove_user.rb +70 -0
  128. data/lib/mongo/operation/write/update.rb +98 -0
  129. data/lib/mongo/protocol.rb +15 -0
  130. data/lib/mongo/protocol/bit_vector.rb +61 -0
  131. data/lib/mongo/protocol/delete.rb +94 -0
  132. data/lib/mongo/protocol/get_more.rb +99 -0
  133. data/lib/mongo/protocol/insert.rb +99 -0
  134. data/lib/mongo/protocol/kill_cursors.rb +74 -0
  135. data/lib/mongo/protocol/message.rb +252 -0
  136. data/lib/mongo/protocol/query.rb +147 -0
  137. data/lib/mongo/protocol/reply.rb +72 -0
  138. data/lib/mongo/protocol/serializers.rb +180 -0
  139. data/lib/mongo/protocol/update.rb +111 -0
  140. data/lib/mongo/server.rb +163 -0
  141. data/lib/mongo/server/connectable.rb +99 -0
  142. data/lib/mongo/server/connection.rb +133 -0
  143. data/lib/mongo/server/connection_pool.rb +141 -0
  144. data/lib/mongo/server/connection_pool/queue.rb +182 -0
  145. data/lib/mongo/server/context.rb +66 -0
  146. data/lib/mongo/server/description.rb +450 -0
  147. data/lib/mongo/server/description/features.rb +85 -0
  148. data/lib/mongo/server/description/inspector.rb +79 -0
  149. data/lib/mongo/server/description/inspector/primary_elected.rb +58 -0
  150. data/lib/mongo/server/description/inspector/server_added.rb +59 -0
  151. data/lib/mongo/server/description/inspector/server_removed.rb +59 -0
  152. data/lib/mongo/server/monitor.rb +160 -0
  153. data/lib/mongo/server/monitor/connection.rb +88 -0
  154. data/lib/mongo/server_selector.rb +81 -0
  155. data/lib/mongo/server_selector/nearest.rb +94 -0
  156. data/lib/mongo/server_selector/primary.rb +88 -0
  157. data/lib/mongo/server_selector/primary_preferred.rb +94 -0
  158. data/lib/mongo/server_selector/secondary.rb +91 -0
  159. data/lib/mongo/server_selector/secondary_preferred.rb +96 -0
  160. data/lib/mongo/server_selector/selectable.rb +209 -0
  161. data/lib/mongo/socket.rb +179 -0
  162. data/lib/mongo/socket/ssl.rb +108 -0
  163. data/lib/mongo/socket/tcp.rb +69 -0
  164. data/lib/mongo/socket/unix.rb +66 -0
  165. data/lib/mongo/uri.rb +504 -0
  166. data/lib/mongo/version.rb +21 -0
  167. data/lib/mongo/write_concern.rb +99 -0
  168. data/lib/mongo/write_concern/acknowledged.rb +38 -0
  169. data/lib/mongo/write_concern/normalizable.rb +73 -0
  170. data/lib/mongo/write_concern/unacknowledged.rb +43 -0
  171. data/mongo.gemspec +17 -14
  172. data/spec/mongo/address/ipv4_spec.rb +74 -0
  173. data/spec/mongo/address/ipv6_spec.rb +74 -0
  174. data/spec/mongo/address/unix_spec.rb +30 -0
  175. data/spec/mongo/address_spec.rb +206 -0
  176. data/spec/mongo/auth/cr_spec.rb +59 -0
  177. data/spec/mongo/auth/ldap_spec.rb +40 -0
  178. data/spec/mongo/auth/scram/conversation_spec.rb +197 -0
  179. data/spec/mongo/auth/scram_spec.rb +55 -0
  180. data/spec/mongo/auth/user/view_spec.rb +76 -0
  181. data/spec/mongo/auth/user_spec.rb +190 -0
  182. data/spec/mongo/auth/x509_spec.rb +40 -0
  183. data/spec/mongo/auth_spec.rb +65 -0
  184. data/spec/mongo/bulk/bulk_write_spec.rb +175 -0
  185. data/spec/mongo/client_spec.rb +564 -0
  186. data/spec/mongo/cluster/topology/replica_set_spec.rb +101 -0
  187. data/spec/mongo/cluster/topology/sharded_spec.rb +74 -0
  188. data/spec/mongo/cluster/topology/standalone_spec.rb +79 -0
  189. data/spec/mongo/cluster/topology_spec.rb +65 -0
  190. data/spec/mongo/cluster_spec.rb +129 -0
  191. data/spec/mongo/collection/view/aggregation_spec.rb +135 -0
  192. data/spec/mongo/collection/view/explainable_spec.rb +32 -0
  193. data/spec/mongo/collection/view/map_reduce_spec.rb +242 -0
  194. data/spec/mongo/collection/view/readable_spec.rb +603 -0
  195. data/spec/mongo/collection/view/writable_spec.rb +504 -0
  196. data/spec/mongo/collection/view_spec.rb +521 -0
  197. data/spec/mongo/collection_spec.rb +362 -0
  198. data/spec/mongo/cursor_spec.rb +295 -0
  199. data/spec/mongo/database_spec.rb +306 -0
  200. data/spec/mongo/error/parser_spec.rb +119 -0
  201. data/spec/mongo/event/publisher_spec.rb +50 -0
  202. data/spec/mongo/event/subscriber_spec.rb +34 -0
  203. data/spec/mongo/grid/file/chunk_spec.rb +226 -0
  204. data/spec/mongo/grid/file/metadata_spec.rb +69 -0
  205. data/spec/mongo/grid/file_spec.rb +138 -0
  206. data/spec/mongo/grid/fs_spec.rb +129 -0
  207. data/spec/mongo/index/view_spec.rb +226 -0
  208. data/spec/mongo/loggable_spec.rb +62 -0
  209. data/spec/mongo/logger_spec.rb +97 -0
  210. data/spec/mongo/operation/aggregate/result_spec.rb +80 -0
  211. data/spec/mongo/operation/aggregate_spec.rb +135 -0
  212. data/spec/mongo/operation/command_spec.rb +106 -0
  213. data/spec/mongo/operation/kill_cursors_spec.rb +66 -0
  214. data/spec/mongo/operation/limited_spec.rb +50 -0
  215. data/spec/mongo/operation/map_reduce_spec.rb +143 -0
  216. data/spec/mongo/operation/read/collections_info_spec.rb +40 -0
  217. data/spec/mongo/operation/read/get_more_spec.rb +81 -0
  218. data/spec/mongo/operation/read/indexes_spec.rb +31 -0
  219. data/spec/mongo/operation/read/query_spec.rb +84 -0
  220. data/spec/mongo/operation/result_spec.rb +275 -0
  221. data/spec/mongo/operation/specifiable_spec.rb +53 -0
  222. data/spec/mongo/operation/write/bulk_delete_spec.rb +473 -0
  223. data/spec/mongo/operation/write/bulk_insert_spec.rb +466 -0
  224. data/spec/mongo/operation/write/bulk_update_spec.rb +524 -0
  225. data/spec/mongo/operation/write/command/delete_spec.rb +116 -0
  226. data/spec/mongo/operation/write/command/insert_spec.rb +117 -0
  227. data/spec/mongo/operation/write/command/update_spec.rb +123 -0
  228. data/spec/mongo/operation/write/create_user_spec.rb +44 -0
  229. data/spec/mongo/operation/write/delete_spec.rb +178 -0
  230. data/spec/mongo/operation/write/drop_index_spec.rb +51 -0
  231. data/spec/mongo/operation/write/ensure_index_spec.rb +81 -0
  232. data/spec/mongo/operation/write/insert_spec.rb +231 -0
  233. data/spec/mongo/operation/write/remove_user_spec.rb +46 -0
  234. data/spec/mongo/operation/write/response_spec.rb +85 -0
  235. data/spec/mongo/operation/write/update_spec.rb +177 -0
  236. data/spec/mongo/protocol/delete_spec.rb +167 -0
  237. data/spec/mongo/protocol/get_more_spec.rb +146 -0
  238. data/spec/mongo/protocol/insert_spec.rb +161 -0
  239. data/spec/mongo/protocol/kill_cursors_spec.rb +101 -0
  240. data/spec/mongo/protocol/query_spec.rb +285 -0
  241. data/spec/mongo/protocol/reply_spec.rb +157 -0
  242. data/spec/mongo/protocol/update_spec.rb +186 -0
  243. data/spec/mongo/server/connection_pool/queue_spec.rb +170 -0
  244. data/spec/mongo/server/connection_pool_spec.rb +120 -0
  245. data/spec/mongo/server/connection_spec.rb +289 -0
  246. data/spec/mongo/server/description/features_spec.rb +138 -0
  247. data/spec/mongo/server/description/inspector/primary_elected_spec.rb +94 -0
  248. data/spec/mongo/server/description/inspector/server_added_spec.rb +92 -0
  249. data/spec/mongo/server/description/inspector/server_removed_spec.rb +95 -0
  250. data/spec/mongo/server/description_spec.rb +510 -0
  251. data/spec/mongo/server/monitor_spec.rb +130 -0
  252. data/spec/mongo/server_discovery_and_monitoring_spec.rb +103 -0
  253. data/spec/mongo/server_selection_rtt_spec.rb +104 -0
  254. data/spec/mongo/server_selection_spec.rb +89 -0
  255. data/spec/mongo/server_selector/nearest_spec.rb +250 -0
  256. data/spec/mongo/server_selector/primary_preferred_spec.rb +290 -0
  257. data/spec/mongo/server_selector/primary_spec.rb +114 -0
  258. data/spec/mongo/server_selector/secondary_preferred_spec.rb +252 -0
  259. data/spec/mongo/server_selector/secondary_spec.rb +196 -0
  260. data/spec/mongo/server_selector_spec.rb +101 -0
  261. data/spec/mongo/server_spec.rb +131 -0
  262. data/spec/mongo/uri_spec.rb +517 -0
  263. data/spec/mongo/write_concern/acknowledged_spec.rb +44 -0
  264. data/spec/mongo/write_concern/unacknowledged_spec.rb +15 -0
  265. data/spec/mongo_orchestration_spec.rb +70 -0
  266. data/spec/spec_helper.rb +148 -0
  267. data/spec/support/authorization.rb +245 -0
  268. data/spec/support/helpers.rb +140 -0
  269. data/spec/support/matchers.rb +37 -0
  270. data/spec/support/mongo_orchestration.rb +61 -0
  271. data/spec/support/mongo_orchestration/requestable.rb +109 -0
  272. data/spec/support/mongo_orchestration/standalone.rb +57 -0
  273. data/spec/support/sdam/rs/discover_arbiters.yml +41 -0
  274. data/spec/support/sdam/rs/discover_passives.yml +41 -0
  275. data/spec/support/sdam/rs/discover_primary.yml +40 -0
  276. data/spec/support/sdam/rs/discover_secondary.yml +41 -0
  277. data/spec/support/sdam/rs/discovery.yml +195 -0
  278. data/spec/support/sdam/rs/ghost_discovered.yml +39 -0
  279. data/spec/support/sdam/rs/hosts_differ_from_seeds.yml +34 -0
  280. data/spec/support/sdam/rs/member_reconfig.yml +68 -0
  281. data/spec/support/sdam/rs/member_standalone.yml +60 -0
  282. data/spec/support/sdam/rs/new_primary.yml +74 -0
  283. data/spec/support/sdam/rs/new_primary_wrong_set_name.yml +71 -0
  284. data/spec/support/sdam/rs/non_rs_member.yml +31 -0
  285. data/spec/support/sdam/rs/normalize_case.yml +49 -0
  286. data/spec/support/sdam/rs/primary_becomes_standalone.yml +52 -0
  287. data/spec/support/sdam/rs/primary_changes_set_name.yml +57 -0
  288. data/spec/support/sdam/rs/primary_disconnect.yml +56 -0
  289. data/spec/support/sdam/rs/primary_wrong_set_name.yml +27 -0
  290. data/spec/support/sdam/rs/response_from_removed.yml +63 -0
  291. data/spec/support/sdam/rs/rsother_discovered.yml +41 -0
  292. data/spec/support/sdam/rs/sec_not_auth.yml +49 -0
  293. data/spec/support/sdam/rs/secondary_wrong_set_name.yml +28 -0
  294. data/spec/support/sdam/rs/secondary_wrong_set_name_with_primary.yml +69 -0
  295. data/spec/support/sdam/rs/unexpected_mongos.yml +26 -0
  296. data/spec/support/sdam/rs/wrong_set_name.yml +35 -0
  297. data/spec/support/sdam/sharded/multiple_mongoses.yml +46 -0
  298. data/spec/support/sdam/sharded/non_mongos_removed.yml +41 -0
  299. data/spec/support/sdam/sharded/normalize_uri_case.yml +32 -0
  300. data/spec/support/sdam/single/direct_connection_external_ip.yml +34 -0
  301. data/spec/support/sdam/single/direct_connection_mongos.yml +33 -0
  302. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +35 -0
  303. data/spec/support/sdam/single/direct_connection_rsprimary.yml +34 -0
  304. data/spec/support/sdam/single/direct_connection_rssecondary.yml +35 -0
  305. data/spec/support/sdam/single/direct_connection_slave.yml +32 -0
  306. data/spec/support/sdam/single/direct_connection_standalone.yml +32 -0
  307. data/spec/support/sdam/single/not_ok_response.yml +39 -0
  308. data/spec/support/sdam/single/standalone_removed.yml +32 -0
  309. data/spec/support/sdam/single/unavailable_seed.yml +28 -0
  310. data/spec/support/server_discovery_and_monitoring.rb +167 -0
  311. data/spec/support/server_selection.rb +140 -0
  312. data/spec/support/server_selection/rtt/first_value.yml +4 -0
  313. data/spec/support/server_selection/rtt/first_value_zero.yml +4 -0
  314. data/spec/support/server_selection/rtt/value_test_1.yml +4 -0
  315. data/spec/support/server_selection/rtt/value_test_2.yml +4 -0
  316. data/spec/support/server_selection/rtt/value_test_3.yml +4 -0
  317. data/spec/support/server_selection/rtt/value_test_4.yml +4 -0
  318. data/spec/support/server_selection/rtt/value_test_5.yml +4 -0
  319. data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/Nearest.yml +32 -0
  320. data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/Nearest_non_matching.yml +27 -0
  321. data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/Primary.yml +23 -0
  322. data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/PrimaryPreferred.yml +32 -0
  323. data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/PrimaryPreferred_non_matching.yml +27 -0
  324. data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/Secondary.yml +32 -0
  325. data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/SecondaryPreferred.yml +32 -0
  326. data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/SecondaryPreferred_non_matching.yml +27 -0
  327. data/spec/support/server_selection/selection/ReplicaSetNoPrimary/read/Secondary_non_matching.yml +27 -0
  328. data/spec/support/server_selection/selection/ReplicaSetWithPrimary/read/Nearest.yml +41 -0
  329. data/spec/support/server_selection/selection/ReplicaSetWithPrimary/read/Nearest_non_matching.yml +34 -0
  330. data/spec/support/server_selection/selection/ReplicaSetWithPrimary/read/Primary.yml +33 -0
  331. data/spec/support/server_selection/selection/ReplicaSetWithPrimary/read/PrimaryPreferred.yml +39 -0
  332. data/spec/support/server_selection/selection/ReplicaSetWithPrimary/read/PrimaryPreferred_non_matching.yml +36 -0
  333. data/spec/support/server_selection/selection/Sharded/read/SecondaryPreferred.yml +32 -0
  334. data/spec/support/server_selection/selection/Single/read/SecondaryPreferred.yml +23 -0
  335. data/spec/support/server_selection/selection/Unknown/read/SecondaryPreferred.yml +13 -0
  336. data/spec/support/server_selection_rtt.rb +41 -0
  337. data/spec/support/shared/bulk_write.rb +498 -0
  338. data/spec/support/shared/cursor.rb +38 -0
  339. data/spec/support/shared/operation.rb +77 -0
  340. data/spec/support/shared/protocol.rb +31 -0
  341. data/spec/support/shared/server_selector.rb +111 -0
  342. data/spec/support/shared/socket.rb +82 -0
  343. data/spec/support/travis.rb +14 -0
  344. metadata +523 -189
  345. metadata.gz.sig +0 -0
  346. data/VERSION +0 -1
  347. data/lib/mongo/bulk_write_collection_view.rb +0 -387
  348. data/lib/mongo/collection_writer.rb +0 -364
  349. data/lib/mongo/connection/node.rb +0 -249
  350. data/lib/mongo/connection/pool.rb +0 -340
  351. data/lib/mongo/connection/pool_manager.rb +0 -320
  352. data/lib/mongo/connection/sharding_pool_manager.rb +0 -67
  353. data/lib/mongo/connection/socket/ssl_socket.rb +0 -95
  354. data/lib/mongo/connection/socket/tcp_socket.rb +0 -87
  355. data/lib/mongo/connection/socket/unix_socket.rb +0 -39
  356. data/lib/mongo/db.rb +0 -808
  357. data/lib/mongo/exception.rb +0 -145
  358. data/lib/mongo/functional/authentication.rb +0 -455
  359. data/lib/mongo/functional/logging.rb +0 -85
  360. data/lib/mongo/functional/read_preference.rb +0 -183
  361. data/lib/mongo/functional/scram.rb +0 -556
  362. data/lib/mongo/functional/uri_parser.rb +0 -409
  363. data/lib/mongo/functional/write_concern.rb +0 -66
  364. data/lib/mongo/gridfs/grid.rb +0 -112
  365. data/lib/mongo/gridfs/grid_ext.rb +0 -53
  366. data/lib/mongo/gridfs/grid_file_system.rb +0 -163
  367. data/lib/mongo/gridfs/grid_io.rb +0 -484
  368. data/lib/mongo/legacy.rb +0 -140
  369. data/lib/mongo/mongo_client.rb +0 -697
  370. data/lib/mongo/mongo_replica_set_client.rb +0 -535
  371. data/lib/mongo/mongo_sharded_client.rb +0 -159
  372. data/lib/mongo/networking.rb +0 -372
  373. data/lib/mongo/utils/conversions.rb +0 -110
  374. data/lib/mongo/utils/core_ext.rb +0 -70
  375. data/lib/mongo/utils/server_version.rb +0 -69
  376. data/lib/mongo/utils/support.rb +0 -80
  377. data/test/functional/authentication_test.rb +0 -39
  378. data/test/functional/bulk_api_stress_test.rb +0 -133
  379. data/test/functional/bulk_write_collection_view_test.rb +0 -1198
  380. data/test/functional/client_test.rb +0 -627
  381. data/test/functional/collection_test.rb +0 -2175
  382. data/test/functional/collection_writer_test.rb +0 -83
  383. data/test/functional/conversions_test.rb +0 -163
  384. data/test/functional/cursor_fail_test.rb +0 -57
  385. data/test/functional/cursor_message_test.rb +0 -56
  386. data/test/functional/cursor_test.rb +0 -683
  387. data/test/functional/db_api_test.rb +0 -835
  388. data/test/functional/db_test.rb +0 -348
  389. data/test/functional/grid_file_system_test.rb +0 -285
  390. data/test/functional/grid_io_test.rb +0 -252
  391. data/test/functional/grid_test.rb +0 -273
  392. data/test/functional/pool_test.rb +0 -136
  393. data/test/functional/safe_test.rb +0 -98
  394. data/test/functional/support_test.rb +0 -62
  395. data/test/functional/timeout_test.rb +0 -60
  396. data/test/functional/uri_test.rb +0 -446
  397. data/test/functional/write_concern_test.rb +0 -118
  398. data/test/helpers/general.rb +0 -50
  399. data/test/helpers/test_unit.rb +0 -476
  400. data/test/replica_set/authentication_test.rb +0 -37
  401. data/test/replica_set/basic_test.rb +0 -189
  402. data/test/replica_set/client_test.rb +0 -393
  403. data/test/replica_set/connection_test.rb +0 -138
  404. data/test/replica_set/count_test.rb +0 -66
  405. data/test/replica_set/cursor_test.rb +0 -220
  406. data/test/replica_set/insert_test.rb +0 -157
  407. data/test/replica_set/max_values_test.rb +0 -151
  408. data/test/replica_set/pinning_test.rb +0 -105
  409. data/test/replica_set/query_test.rb +0 -73
  410. data/test/replica_set/read_preference_test.rb +0 -219
  411. data/test/replica_set/refresh_test.rb +0 -211
  412. data/test/replica_set/replication_ack_test.rb +0 -95
  413. data/test/sharded_cluster/basic_test.rb +0 -203
  414. data/test/shared/authentication/basic_auth_shared.rb +0 -260
  415. data/test/shared/authentication/bulk_api_auth_shared.rb +0 -249
  416. data/test/shared/authentication/gssapi_shared.rb +0 -176
  417. data/test/shared/authentication/sasl_plain_shared.rb +0 -96
  418. data/test/shared/authentication/scram_shared.rb +0 -92
  419. data/test/shared/ssl_shared.rb +0 -235
  420. data/test/test_helper.rb +0 -61
  421. data/test/threading/basic_test.rb +0 -120
  422. data/test/tools/mongo_config.rb +0 -708
  423. data/test/tools/mongo_config_test.rb +0 -160
  424. data/test/unit/client_test.rb +0 -381
  425. data/test/unit/collection_test.rb +0 -166
  426. data/test/unit/connection_test.rb +0 -335
  427. data/test/unit/cursor_test.rb +0 -307
  428. data/test/unit/db_test.rb +0 -136
  429. data/test/unit/grid_test.rb +0 -76
  430. data/test/unit/mongo_sharded_client_test.rb +0 -48
  431. data/test/unit/node_test.rb +0 -93
  432. data/test/unit/pool_manager_test.rb +0 -111
  433. data/test/unit/read_pref_test.rb +0 -406
  434. data/test/unit/read_test.rb +0 -159
  435. data/test/unit/safe_test.rb +0 -158
  436. data/test/unit/sharding_pool_manager_test.rb +0 -84
  437. data/test/unit/write_concern_test.rb +0 -175
@@ -0,0 +1,159 @@
1
+ # Copyright (C) 2014-2015 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'mongo/auth/user/view'
16
+
17
+ module Mongo
18
+ module Auth
19
+
20
+ # Represents a user in MongoDB.
21
+ #
22
+ # @since 2.0.0
23
+ class User
24
+
25
+ # The users collection for the database.
26
+ #
27
+ # @since 2.0.0
28
+ COLLECTION = 'system.users'.freeze
29
+
30
+ # @return [ String ] The authorization source, either a database or
31
+ # external name.
32
+ attr_reader :auth_source
33
+
34
+ # @return [ String ] The database the user is created in.
35
+ attr_reader :database
36
+
37
+ # @return [ Hash ] The authentication mechanism properties.
38
+ attr_reader :auth_mech_properties
39
+
40
+ # @return [ Symbol ] The authorization mechanism.
41
+ attr_reader :mechanism
42
+
43
+ # @return [ String ] The username.
44
+ attr_reader :name
45
+
46
+ # @return [ String ] The cleartext password.
47
+ attr_reader :password
48
+
49
+ # @return [ Array<String> ] roles The user roles.
50
+ attr_reader :roles
51
+
52
+ # Determine if this user is equal to another.
53
+ #
54
+ # @example Check user equality.
55
+ # user == other
56
+ #
57
+ # @param [ Object ] other The object to compare against.
58
+ #
59
+ # @return [ true, false ] If the objects are equal.
60
+ #
61
+ # @since 2.0.0
62
+ def ==(other)
63
+ return false unless other.is_a?(User)
64
+ name == other.name && database == other.database && password == other.password
65
+ end
66
+
67
+ # Get an authentication key for the user based on a nonce from the
68
+ # server.
69
+ #
70
+ # @example Get the authentication key.
71
+ # user.auth_key(nonce)
72
+ #
73
+ # @param [ String ] nonce The response from the server.
74
+ #
75
+ # @return [ String ] The authentication key.
76
+ #
77
+ # @since 2.0.0
78
+ def auth_key(nonce)
79
+ Digest::MD5.hexdigest("#{nonce}#{name}#{hashed_password}")
80
+ end
81
+
82
+ # Get the UTF-8 encoded name with escaped special characters for use with
83
+ # SCRAM authorization.
84
+ #
85
+ # @example Get the encoded name.
86
+ # user.encoded_name
87
+ #
88
+ # @return [ String ] The encoded user name.
89
+ #
90
+ # @since 2.0.0
91
+ def encoded_name
92
+ name.encode(BSON::UTF8).gsub('=','=3D').gsub(',','=2C')
93
+ end
94
+
95
+ # Get the hash key for the user.
96
+ #
97
+ # @example Get the hash key.
98
+ # user.hash
99
+ #
100
+ # @return [ String ] The user hash key.
101
+ #
102
+ # @since 2.0.0
103
+ def hash
104
+ [ name, database, password ].hash
105
+ end
106
+
107
+ # Get the user's hashed password.
108
+ #
109
+ # @example Get the user's hashed password.
110
+ # user.hashed_password
111
+ #
112
+ # @return [ String ] The hashed password.
113
+ #
114
+ # @since 2.0.0
115
+ def hashed_password
116
+ @hashed_password ||= Digest::MD5.hexdigest("#{name}:mongo:#{password}").encode(BSON::UTF8)
117
+ end
118
+
119
+ # Create the new user.
120
+ #
121
+ # @example Create a new user.
122
+ # Mongo::Auth::User.new(options)
123
+ #
124
+ # @param [ Hash ] options The options to create the user from.
125
+ #
126
+ # @option options [ String ] :auth_source The authorization database or
127
+ # external source.
128
+ # @option options [ String ] :database The database the user is
129
+ # authorized for.
130
+ # @option options [ String ] :user The user name.
131
+ # @option options [ String ] :password The user's password.
132
+ # @option options [ Symbol ] :auth_mech The authorization mechanism.
133
+ # @option options [ Array<String>, Array<Hash> ] roles The user roles.
134
+ #
135
+ # @since 2.0.0
136
+ def initialize(options)
137
+ @auth_source = options[:auth_source] || options[:database] || Database::ADMIN
138
+ @database = options[:database] || Database::ADMIN
139
+ @name = options[:user]
140
+ @password = options[:password] || options[:pwd]
141
+ @mechanism = options[:auth_mech] || :mongodb_cr
142
+ @auth_mech_properties = options[:auth_mech_properties] || {}
143
+ @roles = options[:roles] || []
144
+ end
145
+
146
+ # Get the specification for the user, used in creation.
147
+ #
148
+ # @example Get the user's specification.
149
+ # user.spec
150
+ #
151
+ # @return [ Hash ] The user spec.
152
+ #
153
+ # @since 2.0.0
154
+ def spec
155
+ { pwd: hashed_password, roles: roles }
156
+ end
157
+ end
158
+ end
159
+ end
@@ -0,0 +1,102 @@
1
+ # Copyright (C) 2014-2015 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Auth
17
+ class User
18
+
19
+ # Defines behaviour for user related operation on databases.
20
+ #
21
+ # @since 2.0.0
22
+ class View
23
+ extend Forwardable
24
+
25
+ # @return [ Database ] database The view's database.
26
+ attr_reader :database
27
+
28
+ def_delegators :database, :cluster, :read_preference
29
+ def_delegators :cluster, :next_primary
30
+
31
+ # Create a new user in the database.
32
+ #
33
+ # @example Create a new read/write user.
34
+ # view.create('user', password: 'password', roles: [ 'readWrite' ])
35
+ #
36
+ # @param [ Auth::User, String ] user_or_name The user object or user name.
37
+ # @param [ Hash ] options The user options.
38
+ #
39
+ # @return [ Result ] The command response.
40
+ #
41
+ # @since 2.0.0
42
+ def create(user_or_name, options = {})
43
+ user = generate(user_or_name, options)
44
+ Operation::Write::CreateUser.new(
45
+ user: user,
46
+ db_name: database.name
47
+ ).execute(next_primary.context)
48
+ end
49
+
50
+ # Initialize the new user view.
51
+ #
52
+ # @example Initialize the user view.
53
+ # View::User.new(database)
54
+ #
55
+ # @param [ Mongo::Database ] database The database the view is for.
56
+ #
57
+ # @since 2.0.0
58
+ def initialize(database)
59
+ @database = database
60
+ end
61
+
62
+ # Remove a user from the database.
63
+ #
64
+ # @example Remove the user from the database.
65
+ # view.remove('user')
66
+ #
67
+ # @param [ String ] name The user name.
68
+ #
69
+ # @return [ Result ] The command response.
70
+ #
71
+ # @since 2.0.0
72
+ def remove(name)
73
+ Operation::Write::RemoveUser.new(
74
+ user_name: name,
75
+ db_name: database.name
76
+ ).execute(next_primary.context)
77
+ end
78
+
79
+ # Update a user in the database.
80
+ #
81
+ # @example Update a user.
82
+ # view.update('name', password: 'testpwd')
83
+ #
84
+ # @param [ Auth::User, String ] user_or_name The user object or user name.
85
+ # @param [ Hash ] options The user options.
86
+ #
87
+ # @return [ Result ] The response.
88
+ #
89
+ # @since 2.0.0
90
+ def update(user_or_name, options = {})
91
+
92
+ end
93
+
94
+ private
95
+
96
+ def generate(user, options)
97
+ user.is_a?(String) ? Auth::User.new({ user: user }.merge(options)) : user
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,48 @@
1
+ # Copyright (C) 2014-2015 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ require 'mongo/auth/x509/conversation'
16
+
17
+ module Mongo
18
+ module Auth
19
+
20
+ # Defines behaviour for x.509 authentication.
21
+ #
22
+ # @since 2.0.0
23
+ class X509
24
+ include Executable
25
+
26
+ # The authentication mechinism string.
27
+ #
28
+ # @since 2.0.0
29
+ MECHANISM = 'MONGODB-X509'.freeze
30
+
31
+ # Log the user in on the given connection.
32
+ #
33
+ # @example Log the user in.
34
+ # user.login(connection)
35
+ #
36
+ # @param [ Mongo::Connection ] connection The connection to log into.
37
+ # on.
38
+ #
39
+ # @return [ Protocol::Reply ] The authentication response.
40
+ #
41
+ # @since 2.0.0
42
+ def login(connection)
43
+ conversation = Conversation.new(user)
44
+ conversation.finalize(connection.dispatch([ conversation.start ]))
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,92 @@
1
+ # Copyright (C) 2014 MongoDB Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module Auth
17
+ class X509
18
+
19
+ # Defines behaviour around a single x.509 conversation between the
20
+ # client and server.
21
+ #
22
+ # @since 2.0.0
23
+ class Conversation
24
+
25
+ # The login message.
26
+ #
27
+ # @since 2.0.0
28
+ LOGIN = { authenticate: 1 }.freeze
29
+
30
+ # @return [ Protocol::Reply ] reply The current reply in the
31
+ # conversation.
32
+ attr_reader :reply
33
+
34
+ # @return [ User ] user The user for the conversation.
35
+ attr_reader :user
36
+
37
+ # Finalize the x.509 conversation. This is meant to be iterated until
38
+ # the provided reply indicates the conversation is finished.
39
+ #
40
+ # @example Finalize the conversation.
41
+ # conversation.finalize(reply)
42
+ #
43
+ # @param [ Protocol::Reply ] reply The reply of the previous
44
+ # message.
45
+ #
46
+ # @return [ Protocol::Query ] The next message to send.
47
+ #
48
+ # @since 2.0.0
49
+ def finalize(reply)
50
+ validate!(reply)
51
+ end
52
+
53
+ # Start the x.509 conversation. This returns the first message that
54
+ # needs to be send to the server.
55
+ #
56
+ # @example Start the conversation.
57
+ # conversation.start
58
+ #
59
+ # @return [ Protocol::Query ] The first x.509 conversation message.
60
+ #
61
+ # @since 2.0.0
62
+ def start
63
+ Protocol::Query.new(
64
+ Auth::EXTERNAL,
65
+ Database::COMMAND,
66
+ LOGIN.merge(user: user.name, mechanism: X509::MECHANISM),
67
+ limit: -1
68
+ )
69
+ end
70
+
71
+ # Create the new conversation.
72
+ #
73
+ # @example Create the new coversation.
74
+ # Conversation.new(user, "admin")
75
+ #
76
+ # @param [ Auth::User ] user The user to converse about.
77
+ #
78
+ # @since 2.0.0
79
+ def initialize(user)
80
+ @user = user
81
+ end
82
+
83
+ private
84
+
85
+ def validate!(reply)
86
+ raise Unauthorized.new(user) if reply.documents[0]['ok'] != 1
87
+ @reply = reply
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -1,4 +1,4 @@
1
- # Copyright (C) 2009-2013 MongoDB, Inc.
1
+ # Copyright (C) 2014-2015 MongoDB, Inc.
2
2
  #
3
3
  # Licensed under the Apache License, Version 2.0 (the "License");
4
4
  # you may not use this file except in compliance with the License.
@@ -12,7 +12,4 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- require 'mongo/gridfs/grid_ext'
16
- require 'mongo/gridfs/grid'
17
- require 'mongo/gridfs/grid_file_system'
18
- require 'mongo/gridfs/grid_io'
15
+ require 'mongo/bulk/bulk_write'
@@ -0,0 +1,307 @@
1
+ # Copyright (C) 2014-2015 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+
17
+ # This semi-public class handles the logic for executing a batch of
18
+ # operations.
19
+ #
20
+ # @since 2.0.0
21
+ class BulkWrite
22
+
23
+ # @return [ Mongo::Collection ] The collection on which this bulk write
24
+ # operation will be executed.
25
+ attr_reader :collection
26
+
27
+ # @return [ Array<Hash> ] operations The list of operations.
28
+ attr_reader :operations
29
+
30
+ # @return [ Hash ] options The options.
31
+ attr_reader :options
32
+
33
+ # @return [ Acknowledged, Unacknowledged ] write_concern The write concern.
34
+ attr_reader :write_concern
35
+
36
+ # Initialize the BulkWrite object.
37
+ #
38
+ # @example Create an ordered bulk write object.
39
+ # BulkWrite.new([
40
+ # { insert_one: { x: 1 }},
41
+ # { update_one: [{ x: 1 }, { '$set' => { x: 2 }}]}
42
+ # ],
43
+ # { ordered: true },
44
+ # collection
45
+ # )
46
+ #
47
+ # @example Create an unordered bulk write object.
48
+ # BulkWrite.new([
49
+ # { insert_one: { x: 1 }},
50
+ # { update_one: [{ x: 1 }, { '$set' => { x: 2 }}]}
51
+ # ],
52
+ # { ordered: false },
53
+ # collection
54
+ # )
55
+ #
56
+ # @param [ Array<Hash> ] operations The operations to execute.
57
+ # @param [ Hash ] options The options for executing the operations.
58
+ # @param [ Collection ] collection The collection on which the
59
+ # operations will be executed.
60
+ #
61
+ # @option options [ String ] :ordered Whether the operations should
62
+ # be executed in order.
63
+ # @option options [ Hash ] :write_concern The write concern to use when
64
+ # executing the operations.
65
+ #
66
+ # @since 2.0.0
67
+ def initialize(operations, options, collection)
68
+ @operations = operations
69
+ @options = options
70
+ @collection = collection
71
+ if options[:write_concern]
72
+ @write_concern = WriteConcern.get(options[:write_concern])
73
+ else
74
+ @write_concern = collection.write_concern
75
+ end
76
+ end
77
+
78
+ # Is the bulk write operation ordered?
79
+ #
80
+ # @example Is the bulk write operation ordered?
81
+ # bulk_write.ordered?
82
+ #
83
+ # @return [ true, false ] If the bulk write is ordered.
84
+ #
85
+ # @since 2.0.0
86
+ def ordered?
87
+ @ordered ||= !!options[:ordered]
88
+ end
89
+
90
+ # Execute the bulk write.
91
+ #
92
+ # @example execute the bulk write operations.
93
+ # bulk.execute
94
+ #
95
+ # @return [ Hash ] The result of doing the bulk write.
96
+ def execute
97
+ raise Error::EmptyBatch.new if operations.empty?
98
+
99
+ @index = -1
100
+ @ops = []
101
+
102
+ operations.each do |operation|
103
+ operation.each do |name, document|
104
+ if respond_to?(name, true)
105
+ send(name, document)
106
+ else
107
+ raise Error::InvalidBulkOperation.new(name)
108
+ end
109
+ end
110
+ end
111
+
112
+ ops = merge_ops
113
+
114
+ replies = []
115
+ until ops.empty?
116
+ op = ops.shift
117
+
118
+ until op.valid_batch_size?(collection.next_primary.context.max_write_batch_size)
119
+ ops = op.batch(2) + ops
120
+ op = ops.shift
121
+ end
122
+
123
+ begin
124
+ replies << op.execute(collection.next_primary.context)
125
+ # @todo: No test for max message size.
126
+ rescue Error::MaxBSONSize, Error::MaxMessageSize => ex
127
+ raise ex unless op.batchable?
128
+ ops = op.batch(2) + ops
129
+ end
130
+ return make_response!(replies) if stop_executing?(replies.last)
131
+ end
132
+ make_response!(replies) if op.write_concern.get_last_error
133
+ end
134
+
135
+ private
136
+
137
+ def increment_index
138
+ @index += 1
139
+ end
140
+
141
+ def valid_doc?(doc)
142
+ doc.respond_to?(:keys)
143
+ end
144
+
145
+ def update_doc?(doc)
146
+ !doc.empty? &&
147
+ doc.respond_to?(:keys) &&
148
+ doc.keys.first.to_s =~ /^\$/
149
+ end
150
+
151
+ def replacement_doc?(doc)
152
+ doc.respond_to?(:keys) && doc.keys.all?{|key| key !~ /^\$/}
153
+ end
154
+
155
+ def insert_one(doc)
156
+ raise Error::InvalidDocument.new unless valid_doc?(doc)
157
+ spec = { documents: [ doc ],
158
+ db_name: db_name,
159
+ coll_name: collection.name,
160
+ ordered: ordered?,
161
+ write_concern: write_concern }
162
+
163
+ push_op(Mongo::Operation::Write::BulkInsert, spec)
164
+ end
165
+
166
+ def delete_one(selector)
167
+ raise Error::InvalidDocument.new unless valid_doc?(selector)
168
+ spec = { deletes: [{ q: selector, limit: 1 }],
169
+ db_name: db_name,
170
+ coll_name: collection.name,
171
+ ordered: ordered?,
172
+ write_concern: write_concern }
173
+
174
+ push_op(Mongo::Operation::Write::BulkDelete, spec)
175
+ end
176
+
177
+ def delete_many(selector)
178
+ raise Error::InvalidDocument.new unless valid_doc?(selector)
179
+ spec = { deletes: [{ q: selector, limit: 0 }],
180
+ db_name: db_name,
181
+ coll_name: collection.name,
182
+ ordered: ordered?,
183
+ write_concern: write_concern }
184
+
185
+ push_op(Mongo::Operation::Write::BulkDelete, spec)
186
+ end
187
+
188
+ def replace_one(docs)
189
+ selector = docs[0]
190
+ replacement = docs[1]
191
+ upsert = (docs[2] || {})[:upsert]
192
+ raise ArgumentError unless selector && replacement
193
+ raise Error::InvalidReplacementDocument.new unless replacement_doc?(replacement)
194
+ upsert = !!upsert
195
+ spec = { updates: [{ q: selector,
196
+ u: replacement,
197
+ multi: false,
198
+ upsert: upsert }],
199
+ db_name: db_name,
200
+ coll_name: collection.name,
201
+ ordered: ordered?,
202
+ write_concern: write_concern }
203
+
204
+ push_op(Mongo::Operation::Write::BulkUpdate, spec)
205
+ end
206
+
207
+ def update_one(docs)
208
+ upsert = (docs[2] || {})[:upsert]
209
+ update_one_or_many(docs[0], docs[1], upsert, false)
210
+ end
211
+
212
+ def update_many(docs)
213
+ upsert = (docs[2] || {})[:upsert]
214
+ update_one_or_many(docs[0], docs[1], upsert, true)
215
+ end
216
+
217
+ def update_one_or_many(selector, update, upsert, multi)
218
+ raise ArgumentError unless selector && update
219
+ raise Error::InvalidUpdateDocument.new unless update_doc?(update)
220
+ upsert = !!upsert
221
+ spec = { updates: [{ q: selector,
222
+ u: update,
223
+ multi: multi,
224
+ upsert: upsert }],
225
+ db_name: db_name,
226
+ coll_name: collection.name,
227
+ ordered: ordered?,
228
+ write_concern: write_concern }
229
+
230
+ push_op(Mongo::Operation::Write::BulkUpdate, spec)
231
+ end
232
+
233
+ def push_op(op_class, spec)
234
+ spec.merge!(indexes: [ increment_index ])
235
+ @ops << op_class.send(:new, spec)
236
+ end
237
+
238
+ def merge_ops
239
+ if ordered?
240
+ merge_consecutive_ops(@ops)
241
+ else
242
+ merge_ops_by_type(@ops)
243
+ end
244
+ end
245
+
246
+ def merge_consecutive_ops(operations)
247
+ operations.inject([]) do |merged_ops, op|
248
+ previous_op = merged_ops.last
249
+ if previous_op.class == op.class
250
+ merged_ops.tap do |m|
251
+ m[m.size - 1] = previous_op.merge!(op)
252
+ end
253
+ else
254
+ merged_ops << op
255
+ end
256
+ end
257
+ end
258
+
259
+ def merge_ops_by_type(operations)
260
+ ops_by_type = operations.inject({}) do |merged_ops, op|
261
+ if merged_ops[op.class]
262
+ merged_ops.tap do |m|
263
+ m[op.class] << op
264
+ end
265
+ else
266
+ merged_ops.tap do |m|
267
+ m[op.class] = [ op ]
268
+ end
269
+ end
270
+ end
271
+
272
+ ops_by_type.keys.inject([]) do |merged_ops, type|
273
+ merged_ops << merge_consecutive_ops( ops_by_type[ type ] )
274
+ end.flatten
275
+ end
276
+
277
+ def stop_executing?(result)
278
+ result && ordered? && !result.successful?
279
+ end
280
+
281
+ def make_response!(results)
282
+ response = results.reduce({}) do |response, result|
283
+ write_errors = result.aggregate_write_errors
284
+ write_concern_errors = result.aggregate_write_concern_errors
285
+ response.tap do |r|
286
+ r['nInserted'] = ( r['nInserted'] || 0 ) + result.n_inserted if result.respond_to?(:n_inserted)
287
+ r['nMatched'] = ( r['nMatched'] || 0 ) + result.n_matched if result.respond_to?(:n_matched)
288
+ r['nModified'] = ( r['nModified'] || 0 ) + result.n_modified if result.respond_to?(:n_modified) && result.n_modified
289
+ r['nUpserted'] = ( r['nUpserted'] || 0 ) + result.n_upserted if result.respond_to?(:n_upserted)
290
+ r['nRemoved'] = ( r['nRemoved'] || 0 ) + result.n_removed if result.respond_to?(:n_removed)
291
+ r['writeErrors'] = ( r['writeErrors'] || [] ) + write_errors if write_errors
292
+ r['writeConcernErrors'] = ( r['writeConcernErrors'] || [] ) + write_concern_errors if write_concern_errors
293
+ end
294
+ end
295
+
296
+ if response['writeErrors'] || response['writeConcernErrors']
297
+ response.merge!('errmsg' => 'batch item errors occurred')
298
+ raise Error::BulkWriteFailure.new(response)
299
+ end
300
+ response
301
+ end
302
+
303
+ def db_name
304
+ collection.database.name
305
+ end
306
+ end
307
+ end