mongo 2.1.0.beta → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (342) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +10 -3
  4. data/Rakefile +1 -7
  5. data/lib/mongo/address/ipv4.rb +6 -1
  6. data/lib/mongo/address/unix.rb +2 -2
  7. data/lib/mongo/address.rb +32 -10
  8. data/lib/mongo/auth/cr/conversation.rb +1 -1
  9. data/lib/mongo/auth/ldap/conversation.rb +7 -3
  10. data/lib/mongo/auth/scram/conversation.rb +9 -3
  11. data/lib/mongo/auth/user/view.rb +23 -2
  12. data/lib/mongo/auth/x509/conversation.rb +1 -1
  13. data/lib/mongo/bulk_write/combineable.rb +51 -0
  14. data/lib/mongo/bulk_write/ordered_combiner.rb +55 -0
  15. data/lib/mongo/bulk_write/result.rb +61 -8
  16. data/lib/mongo/bulk_write/result_combiner.rb +117 -0
  17. data/lib/mongo/bulk_write/transformable.rb +132 -0
  18. data/lib/mongo/bulk_write/unordered_combiner.rb +52 -0
  19. data/lib/mongo/bulk_write/validatable.rb +62 -0
  20. data/lib/mongo/bulk_write.rb +164 -23
  21. data/lib/mongo/client.rb +75 -18
  22. data/lib/mongo/cluster/topology/replica_set.rb +8 -6
  23. data/lib/mongo/cluster/topology/unknown.rb +5 -2
  24. data/lib/mongo/cluster.rb +85 -5
  25. data/lib/mongo/collection/view/aggregation.rb +19 -45
  26. data/lib/mongo/collection/view/builder/aggregation.rb +98 -0
  27. data/lib/mongo/collection/view/builder/find_command.rb +111 -0
  28. data/lib/mongo/collection/view/builder/flags.rb +62 -0
  29. data/lib/mongo/collection/view/builder/map_reduce.rb +134 -0
  30. data/lib/mongo/collection/view/builder/modifiers.rb +80 -0
  31. data/lib/mongo/collection/view/builder/op_query.rb +83 -0
  32. data/lib/mongo/collection/view/builder.rb +20 -0
  33. data/lib/mongo/collection/view/explainable.rb +15 -0
  34. data/lib/mongo/collection/view/immutable.rb +4 -11
  35. data/lib/mongo/collection/view/iterable.rb +40 -5
  36. data/lib/mongo/collection/view/map_reduce.rb +67 -37
  37. data/lib/mongo/collection/view/readable.rb +114 -100
  38. data/lib/mongo/collection/view/writable.rb +46 -22
  39. data/lib/mongo/collection/view.rb +25 -22
  40. data/lib/mongo/collection.rb +130 -12
  41. data/lib/mongo/cursor/builder/get_more_command.rb +71 -0
  42. data/lib/mongo/cursor/builder/kill_cursors_command.rb +62 -0
  43. data/lib/mongo/cursor/builder/op_get_more.rb +61 -0
  44. data/lib/mongo/cursor/builder/op_kill_cursors.rb +56 -0
  45. data/lib/mongo/cursor/builder.rb +18 -0
  46. data/lib/mongo/cursor.rb +76 -21
  47. data/lib/mongo/database/view.rb +11 -6
  48. data/lib/mongo/database.rb +16 -6
  49. data/lib/mongo/dbref.rb +9 -9
  50. data/lib/mongo/{bulk_write/unordered_bulk_write.rb → error/closed_stream.rb} +12 -21
  51. data/lib/mongo/{bulk_write/ordered_bulk_write.rb → error/extra_file_chunk.rb} +13 -27
  52. data/lib/mongo/error/file_not_found.rb +37 -0
  53. data/lib/mongo/error/invalid_file.rb +2 -2
  54. data/lib/mongo/error/invalid_file_revision.rb +37 -0
  55. data/lib/mongo/error/invalid_uri.rb +5 -4
  56. data/lib/mongo/error/invalid_write_concern.rb +35 -0
  57. data/lib/mongo/error/missing_file_chunk.rb +38 -0
  58. data/lib/mongo/error/operation_failure.rb +33 -2
  59. data/lib/mongo/error/unchangeable_collection_option.rb +38 -0
  60. data/lib/mongo/error/unexpected_chunk_length.rb +39 -0
  61. data/lib/mongo/error.rb +8 -0
  62. data/lib/mongo/grid/file/chunk.rb +9 -9
  63. data/lib/mongo/grid/file/{metadata.rb → info.rb} +41 -39
  64. data/lib/mongo/grid/file.rb +12 -9
  65. data/lib/mongo/grid/fs_bucket.rb +448 -0
  66. data/lib/mongo/grid/stream/read.rb +208 -0
  67. data/lib/mongo/grid/stream/write.rb +187 -0
  68. data/lib/mongo/grid/stream.rb +64 -0
  69. data/lib/mongo/grid.rb +2 -1
  70. data/lib/mongo/index/view.rb +7 -4
  71. data/lib/mongo/index.rb +5 -0
  72. data/lib/mongo/loggable.rb +34 -57
  73. data/lib/mongo/logger.rb +16 -78
  74. data/lib/mongo/monitoring/command_log_subscriber.rb +38 -14
  75. data/lib/mongo/monitoring/event/command_started.rb +2 -1
  76. data/lib/mongo/monitoring/event/command_succeeded.rb +24 -2
  77. data/lib/mongo/monitoring/event/secure.rb +58 -0
  78. data/lib/mongo/monitoring/event.rb +1 -0
  79. data/lib/mongo/monitoring/publishable.rb +22 -12
  80. data/lib/mongo/monitoring.rb +1 -5
  81. data/lib/mongo/operation/commands/aggregate/result.rb +89 -0
  82. data/lib/mongo/operation/commands/aggregate.rb +64 -0
  83. data/lib/mongo/operation/commands/collections_info/result.rb +41 -0
  84. data/lib/mongo/operation/{read → commands}/collections_info.rb +5 -3
  85. data/lib/mongo/operation/commands/command.rb +47 -0
  86. data/lib/mongo/operation/commands/find/result.rb +62 -0
  87. data/lib/mongo/operation/commands/find.rb +27 -0
  88. data/lib/mongo/operation/commands/get_more/result.rb +62 -0
  89. data/lib/mongo/operation/commands/get_more.rb +27 -0
  90. data/lib/mongo/operation/{read → commands}/indexes.rb +9 -6
  91. data/lib/mongo/operation/{list_collections → commands/list_collections}/result.rb +1 -21
  92. data/lib/mongo/operation/{read → commands}/list_collections.rb +4 -32
  93. data/lib/mongo/operation/{list_indexes → commands/list_indexes}/result.rb +1 -21
  94. data/lib/mongo/operation/{read → commands}/list_indexes.rb +3 -33
  95. data/lib/mongo/operation/commands/map_reduce/result.rb +119 -0
  96. data/lib/mongo/operation/commands/map_reduce.rb +49 -0
  97. data/lib/mongo/operation/commands/parallel_scan/result.rb +64 -0
  98. data/lib/mongo/operation/commands/parallel_scan.rb +52 -0
  99. data/lib/mongo/operation/commands/user_query.rb +71 -0
  100. data/lib/mongo/operation/commands/users_info/result.rb +38 -0
  101. data/lib/mongo/operation/commands/users_info.rb +48 -0
  102. data/lib/mongo/operation/commands.rb +26 -0
  103. data/lib/mongo/operation/executable.rb +4 -68
  104. data/lib/mongo/operation/kill_cursors.rb +3 -3
  105. data/lib/mongo/operation/object_id_generator.rb +36 -0
  106. data/lib/mongo/operation/read/get_more.rb +2 -22
  107. data/lib/mongo/operation/read/query/result.rb +40 -0
  108. data/lib/mongo/operation/read/query.rb +4 -21
  109. data/lib/mongo/operation/read.rb +0 -4
  110. data/lib/mongo/operation/{read_preferrable.rb → read_preference.rb} +3 -2
  111. data/lib/mongo/operation/result.rb +43 -1
  112. data/lib/mongo/operation/specifiable.rb +59 -1
  113. data/lib/mongo/operation/write/bulk/bulkable.rb +83 -0
  114. data/lib/mongo/operation/write/bulk/delete/result.rb +67 -0
  115. data/lib/mongo/operation/write/bulk/delete.rb +71 -0
  116. data/lib/mongo/operation/write/bulk/insert/result.rb +129 -0
  117. data/lib/mongo/operation/write/bulk/insert.rb +96 -0
  118. data/lib/mongo/operation/write/bulk/legacy_mergable.rb +87 -0
  119. data/lib/mongo/operation/write/bulk/mergable.rb +71 -0
  120. data/lib/mongo/operation/write/bulk/update/result.rb +174 -0
  121. data/lib/mongo/operation/write/bulk/update.rb +81 -0
  122. data/lib/mongo/operation/write/bulk.rb +6 -3
  123. data/lib/mongo/operation/write/command/create_index.rb +0 -1
  124. data/lib/mongo/operation/write/command/create_user.rb +0 -1
  125. data/lib/mongo/operation/write/command/delete.rb +3 -3
  126. data/lib/mongo/operation/write/command/drop_index.rb +0 -1
  127. data/lib/mongo/operation/write/command/insert.rb +4 -3
  128. data/lib/mongo/operation/write/command/remove_user.rb +0 -1
  129. data/lib/mongo/operation/write/command/update.rb +6 -4
  130. data/lib/mongo/operation/write/command/update_user.rb +0 -1
  131. data/lib/mongo/operation/write/command/writable.rb +13 -18
  132. data/lib/mongo/operation/write/create_index.rb +4 -27
  133. data/lib/mongo/operation/write/create_user.rb +4 -30
  134. data/lib/mongo/operation/write/delete.rb +6 -29
  135. data/lib/mongo/operation/write/drop_index.rb +3 -3
  136. data/lib/mongo/operation/write/gle.rb +49 -0
  137. data/lib/mongo/operation/write/idable.rb +24 -2
  138. data/lib/mongo/operation/write/insert.rb +2 -24
  139. data/lib/mongo/operation/write/remove_user.rb +4 -27
  140. data/lib/mongo/operation/write/update.rb +13 -36
  141. data/lib/mongo/operation/write/update_user.rb +4 -30
  142. data/lib/mongo/operation/write/write_command_enabled.rb +53 -0
  143. data/lib/mongo/operation/write.rb +2 -0
  144. data/lib/mongo/operation.rb +33 -5
  145. data/lib/mongo/options/mapper.rb +26 -2
  146. data/lib/mongo/options/redacted.rb +156 -0
  147. data/lib/mongo/options.rb +1 -0
  148. data/lib/mongo/protocol/bit_vector.rb +11 -9
  149. data/lib/mongo/protocol/delete.rb +78 -3
  150. data/lib/mongo/protocol/get_more.rb +59 -2
  151. data/lib/mongo/protocol/insert.rb +73 -1
  152. data/lib/mongo/protocol/kill_cursors.rb +66 -4
  153. data/lib/mongo/protocol/message.rb +44 -20
  154. data/lib/mongo/protocol/query.rb +153 -65
  155. data/lib/mongo/protocol/reply.rb +92 -1
  156. data/lib/mongo/protocol/serializers.rb +49 -40
  157. data/lib/mongo/protocol/update.rb +93 -1
  158. data/lib/mongo/retryable.rb +101 -0
  159. data/lib/mongo/server/connectable.rb +28 -8
  160. data/lib/mongo/server/connection.rb +52 -10
  161. data/lib/mongo/server/connection_pool/queue.rb +15 -0
  162. data/lib/mongo/server/connection_pool.rb +12 -15
  163. data/lib/mongo/server/description/features.rb +4 -2
  164. data/lib/mongo/server/description.rb +39 -3
  165. data/lib/mongo/server/monitor/connection.rb +49 -28
  166. data/lib/mongo/server/monitor.rb +3 -14
  167. data/lib/mongo/server.rb +31 -4
  168. data/lib/mongo/server_selector/selectable.rb +58 -32
  169. data/lib/mongo/server_selector.rb +19 -10
  170. data/lib/mongo/socket/ssl.rb +4 -1
  171. data/lib/mongo/socket/tcp.rb +2 -2
  172. data/lib/mongo/socket/unix.rb +5 -8
  173. data/lib/mongo/socket.rb +11 -4
  174. data/lib/mongo/uri.rb +245 -139
  175. data/lib/mongo/version.rb +1 -1
  176. data/lib/mongo/write_concern.rb +21 -6
  177. data/lib/mongo.rb +4 -4
  178. data/mongo.gemspec +1 -2
  179. data/spec/mongo/address/unix_spec.rb +1 -1
  180. data/spec/mongo/address_spec.rb +25 -0
  181. data/spec/mongo/auth/ldap/conversation_spec.rb +43 -0
  182. data/spec/mongo/auth/user/view_spec.rb +26 -1
  183. data/spec/mongo/bulk_write/ordered_combiner_spec.rb +284 -0
  184. data/spec/mongo/bulk_write/unordered_combiner_spec.rb +239 -0
  185. data/spec/mongo/bulk_write_spec.rb +385 -161
  186. data/spec/mongo/client_spec.rb +193 -23
  187. data/spec/mongo/cluster/topology/replica_set_spec.rb +2 -0
  188. data/spec/mongo/collection/view/aggregation_spec.rb +65 -0
  189. data/spec/mongo/collection/view/builder/find_command_spec.rb +167 -0
  190. data/spec/mongo/collection/view/builder/flags_spec.rb +106 -0
  191. data/spec/mongo/collection/view/builder/modifiers_spec.rb +210 -0
  192. data/spec/mongo/collection/view/builder/op_query_spec.rb +154 -0
  193. data/spec/mongo/collection/view/explainable_spec.rb +1 -2
  194. data/spec/mongo/collection/view/immutable_spec.rb +54 -0
  195. data/spec/mongo/collection/view/map_reduce_spec.rb +104 -9
  196. data/spec/mongo/collection/view/readable_spec.rb +109 -112
  197. data/spec/mongo/collection/view_spec.rb +119 -487
  198. data/spec/mongo/collection_spec.rb +1002 -33
  199. data/spec/mongo/command_monitoring_spec.rb +64 -0
  200. data/spec/mongo/connection_string_spec.rb +115 -0
  201. data/spec/mongo/cursor/builder/get_more_command_spec.rb +160 -0
  202. data/spec/mongo/cursor/builder/op_get_more_spec.rb +52 -0
  203. data/spec/mongo/cursor_spec.rb +10 -60
  204. data/spec/mongo/database_spec.rb +81 -12
  205. data/spec/mongo/dbref_spec.rb +4 -4
  206. data/spec/mongo/grid/file/chunk_spec.rb +6 -6
  207. data/spec/mongo/grid/file/{metadata_spec.rb → info_spec.rb} +29 -17
  208. data/spec/mongo/grid/file_spec.rb +8 -8
  209. data/spec/mongo/grid/fs_bucket_spec.rb +1020 -0
  210. data/spec/mongo/grid/stream/read_spec.rb +275 -0
  211. data/spec/mongo/grid/stream/write_spec.rb +440 -0
  212. data/spec/mongo/grid/stream_spec.rb +48 -0
  213. data/spec/mongo/gridfs_spec.rb +50 -0
  214. data/spec/mongo/index/view_spec.rb +41 -0
  215. data/spec/mongo/logger_spec.rb +0 -40
  216. data/spec/mongo/monitoring/command_log_subscriber_spec.rb +76 -0
  217. data/spec/mongo/monitoring/event/command_started_spec.rb +26 -0
  218. data/spec/mongo/monitoring/event/command_succeeded_spec.rb +26 -0
  219. data/spec/mongo/monitoring/event/secure_spec.rb +57 -0
  220. data/spec/mongo/operation/{aggregate → commands/aggregate}/result_spec.rb +1 -1
  221. data/spec/mongo/operation/commands/aggregate_spec.rb +69 -0
  222. data/spec/mongo/operation/{read → commands}/collections_info_spec.rb +1 -1
  223. data/spec/mongo/operation/{command_spec.rb → commands/command_spec.rb} +1 -19
  224. data/spec/mongo/operation/{read → commands}/indexes_spec.rb +1 -1
  225. data/spec/mongo/operation/{map_reduce_spec.rb → commands/map_reduce_spec.rb} +1 -19
  226. data/spec/mongo/operation/kill_cursors_spec.rb +1 -17
  227. data/spec/mongo/operation/read/get_more_spec.rb +0 -16
  228. data/spec/mongo/operation/read/query_spec.rb +19 -16
  229. data/spec/mongo/operation/{read_preferrable_spec.rb → read_preference_spec.rb} +11 -11
  230. data/spec/mongo/operation/result_spec.rb +19 -0
  231. data/spec/mongo/operation/write/bulk/{bulk_delete_spec.rb → delete_spec.rb} +17 -28
  232. data/spec/mongo/operation/write/bulk/{bulk_insert_spec.rb → insert_spec.rb} +1 -12
  233. data/spec/mongo/operation/write/bulk/{bulk_update_spec.rb → update_spec.rb} +7 -18
  234. data/spec/mongo/operation/write/command/delete_spec.rb +18 -9
  235. data/spec/mongo/operation/write/command/insert_spec.rb +18 -9
  236. data/spec/mongo/operation/write/command/update_spec.rb +18 -9
  237. data/spec/mongo/operation/write/delete_spec.rb +3 -3
  238. data/spec/mongo/operation/write/insert_spec.rb +0 -11
  239. data/spec/mongo/operation/write/update_spec.rb +6 -6
  240. data/spec/mongo/options/redacted_spec.rb +350 -0
  241. data/spec/mongo/protocol/delete_spec.rb +4 -4
  242. data/spec/mongo/protocol/get_more_spec.rb +4 -4
  243. data/spec/mongo/protocol/insert_spec.rb +3 -3
  244. data/spec/mongo/protocol/kill_cursors_spec.rb +8 -6
  245. data/spec/mongo/protocol/query_spec.rb +21 -7
  246. data/spec/mongo/protocol/update_spec.rb +5 -5
  247. data/spec/mongo/retryable_spec.rb +221 -0
  248. data/spec/mongo/server/connection_pool/queue_spec.rb +16 -0
  249. data/spec/mongo/server/connection_pool_spec.rb +42 -6
  250. data/spec/mongo/server/connection_spec.rb +86 -1
  251. data/spec/mongo/server/description/features_spec.rb +25 -0
  252. data/spec/mongo/server/description_spec.rb +42 -0
  253. data/spec/mongo/server/monitor_spec.rb +44 -0
  254. data/spec/mongo/server_discovery_and_monitoring_spec.rb +25 -59
  255. data/spec/mongo/server_selection_rtt_spec.rb +37 -57
  256. data/spec/mongo/server_selection_spec.rb +5 -3
  257. data/spec/mongo/server_selector/nearest_spec.rb +35 -27
  258. data/spec/mongo/server_selector/primary_preferred_spec.rb +32 -30
  259. data/spec/mongo/server_selector/primary_spec.rb +21 -14
  260. data/spec/mongo/server_selector/secondary_preferred_spec.rb +28 -26
  261. data/spec/mongo/server_selector/secondary_spec.rb +24 -22
  262. data/spec/mongo/server_selector_spec.rb +87 -24
  263. data/spec/mongo/server_spec.rb +78 -15
  264. data/spec/mongo/socket/ssl_spec.rb +101 -57
  265. data/spec/mongo/socket/unix_spec.rb +52 -0
  266. data/spec/mongo/uri_spec.rb +271 -59
  267. data/spec/mongo/write_concern_spec.rb +126 -0
  268. data/spec/spec_helper.rb +29 -23
  269. data/spec/support/authorization.rb +4 -5
  270. data/spec/support/command_monitoring/bulkWrite.yml +73 -0
  271. data/spec/support/command_monitoring/command.yml +42 -0
  272. data/spec/support/command_monitoring/deleteMany.yml +55 -0
  273. data/spec/support/command_monitoring/deleteOne.yml +55 -0
  274. data/spec/support/command_monitoring/find.yml +268 -0
  275. data/spec/support/command_monitoring/insertMany.yml +81 -0
  276. data/spec/support/command_monitoring/insertOne.yml +51 -0
  277. data/spec/support/command_monitoring/updateMany.yml +67 -0
  278. data/spec/support/command_monitoring/updateOne.yml +95 -0
  279. data/spec/support/command_monitoring.rb +373 -0
  280. data/spec/support/connection_string.rb +228 -0
  281. data/spec/support/connection_string_tests/invalid-uris.yml +193 -0
  282. data/spec/support/connection_string_tests/valid-auth.yml +256 -0
  283. data/spec/support/connection_string_tests/valid-host_identifiers.yml +121 -0
  284. data/spec/support/connection_string_tests/valid-options.yml +30 -0
  285. data/spec/support/connection_string_tests/valid-unix_socket-absolute.yml +197 -0
  286. data/spec/support/connection_string_tests/valid-unix_socket-relative.yml +213 -0
  287. data/spec/support/connection_string_tests/valid-warnings.yml +55 -0
  288. data/spec/support/crud/read.rb +14 -10
  289. data/spec/support/crud/write.rb +36 -9
  290. data/spec/support/crud.rb +10 -2
  291. data/spec/support/gridfs.rb +637 -0
  292. data/spec/support/gridfs_tests/delete.yml +157 -0
  293. data/spec/support/gridfs_tests/download.yml +210 -0
  294. data/spec/support/gridfs_tests/download_by_name.yml +113 -0
  295. data/spec/support/gridfs_tests/upload.yml +158 -0
  296. data/spec/support/matchers.rb +2 -2
  297. data/spec/support/sdam/rs/equal_electionids.yml +1 -2
  298. data/spec/support/sdam/rs/new_primary_new_electionid.yml +0 -3
  299. data/spec/support/sdam/rs/primary_mismatched_me.yml +37 -0
  300. data/spec/support/sdam/rs/primary_to_no_primary_mismatched_me.yml +75 -0
  301. data/spec/support/sdam/rs/rsother_discovered.yml +24 -3
  302. data/spec/support/sdam/rs/secondary_mismatched_me.yml +37 -0
  303. data/spec/support/sdam/rs/stepdown_change_set_name.yml +59 -0
  304. data/spec/support/sdam/single/direct_connection_rsarbiter.yml +1 -1
  305. data/spec/support/sdam/single/direct_connection_rsprimary.yml +1 -1
  306. data/spec/support/sdam/single/direct_connection_rssecondary.yml +1 -1
  307. data/spec/support/sdam/single/direct_connection_slave.yml +1 -1
  308. data/spec/support/sdam/single/direct_connection_standalone.yml +1 -1
  309. data/spec/support/sdam/single/not_ok_response.yml +0 -1
  310. data/spec/support/server_discovery_and_monitoring.rb +3 -1
  311. data/spec/support/server_selection.rb +3 -1
  312. data/spec/support/shared/bulk_write.rb +192 -0
  313. data/spec/support/shared/protocol.rb +5 -5
  314. data/spec/support/shared/server_selector.rb +78 -13
  315. data/spec/support/travis.rb +1 -1
  316. data.tar.gz.sig +0 -0
  317. metadata +211 -72
  318. metadata.gz.sig +0 -0
  319. data/lib/mongo/bulk_write/bulk_writable.rb +0 -252
  320. data/lib/mongo/bulk_write/deletable.rb +0 -57
  321. data/lib/mongo/bulk_write/insertable.rb +0 -49
  322. data/lib/mongo/bulk_write/replacable.rb +0 -58
  323. data/lib/mongo/bulk_write/updatable.rb +0 -69
  324. data/lib/mongo/grid/fs.rb +0 -146
  325. data/lib/mongo/operation/aggregate/result.rb +0 -103
  326. data/lib/mongo/operation/aggregate.rb +0 -108
  327. data/lib/mongo/operation/command.rb +0 -61
  328. data/lib/mongo/operation/map_reduce/result.rb +0 -122
  329. data/lib/mongo/operation/map_reduce.rb +0 -95
  330. data/lib/mongo/operation/parallel_scan/result.rb +0 -72
  331. data/lib/mongo/operation/parallel_scan.rb +0 -76
  332. data/lib/mongo/operation/write/bulk/bulk_delete/result.rb +0 -75
  333. data/lib/mongo/operation/write/bulk/bulk_delete.rb +0 -145
  334. data/lib/mongo/operation/write/bulk/bulk_insert/result.rb +0 -130
  335. data/lib/mongo/operation/write/bulk/bulk_insert.rb +0 -132
  336. data/lib/mongo/operation/write/bulk/bulk_mergable.rb +0 -67
  337. data/lib/mongo/operation/write/bulk/bulk_update/result.rb +0 -174
  338. data/lib/mongo/operation/write/bulk/bulk_update.rb +0 -154
  339. data/lib/mongo/operation/write/bulk/legacy_bulk_mergable.rb +0 -83
  340. data/spec/mongo/grid/fs_spec.rb +0 -160
  341. data/spec/mongo/loggable_spec.rb +0 -63
  342. data/spec/mongo/operation/aggregate_spec.rb +0 -127
@@ -0,0 +1,1020 @@
1
+ require 'spec_helper'
2
+
3
+ describe Mongo::Grid::FSBucket do
4
+
5
+ let(:fs) do
6
+ described_class.new(authorized_client.database, options)
7
+ end
8
+
9
+ let(:options) do
10
+ { }
11
+ end
12
+
13
+ let(:filename) do
14
+ 'specs.rb'
15
+ end
16
+
17
+ let(:file) do
18
+ File.open(__FILE__)
19
+ end
20
+
21
+ describe '#initialize' do
22
+
23
+ it 'sets the files collection' do
24
+ expect(fs.files_collection.name).to eq('fs.files')
25
+ end
26
+
27
+ it 'sets the chunks collection' do
28
+ expect(fs.chunks_collection.name).to eq('fs.chunks')
29
+ end
30
+
31
+ context 'when options are provided' do
32
+
33
+ let(:fs) do
34
+ described_class.new(authorized_client.database, options)
35
+ end
36
+
37
+ context 'when a write concern is set' do
38
+
39
+ context 'when the option :write is provided' do
40
+
41
+ let(:options) do
42
+ { write: { w: 2 } }
43
+ end
44
+
45
+ it 'sets the write concern' do
46
+ expect(fs.send(:write_concern).options).to eq(Mongo::WriteConcern.get(w: 2).options)
47
+ end
48
+ end
49
+ end
50
+
51
+ context 'when a read preference is set' do
52
+
53
+ let(:options) do
54
+ { read: { mode: :secondary, server_selection_timeout: 0.1 } }
55
+ end
56
+
57
+ let(:read_pref) do
58
+ Mongo::ServerSelector.get(Mongo::Options::Redacted.new(options[:read].merge(authorized_client.options)))
59
+ end
60
+
61
+ it 'sets the read preference' do
62
+ expect(fs.send(:read_preference)).to eq(read_pref)
63
+ end
64
+ end
65
+
66
+ context 'when a write stream is opened' do
67
+
68
+ let(:stream) do
69
+ fs.open_upload_stream('test.txt')
70
+ end
71
+
72
+ let(:fs) do
73
+ described_class.new(authorized_client.database, options)
74
+ end
75
+
76
+ context 'when a write option is specified' do
77
+
78
+ let(:options) do
79
+ { write: { w: 2 } }
80
+ end
81
+
82
+ it 'passes the write concern to the write stream' do
83
+ expect(stream.write_concern.options).to eq(Mongo::WriteConcern.get(options[:write]).options)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ describe '#find' do
91
+
92
+ let(:fs) do
93
+ described_class.new(authorized_client.database)
94
+ end
95
+
96
+ context 'when there is no selector provided' do
97
+
98
+ let(:files) do
99
+ [
100
+ Mongo::Grid::File.new('hello world!', :filename => 'test.txt'),
101
+ Mongo::Grid::File.new('goodbye world!', :filename => 'test1.txt')
102
+ ]
103
+ end
104
+
105
+ before do
106
+ files.each do |file|
107
+ fs.insert_one(file)
108
+ end
109
+ end
110
+
111
+ after do
112
+ fs.files_collection.delete_many
113
+ fs.chunks_collection.delete_many
114
+ end
115
+
116
+ it 'returns a collection view' do
117
+ expect(fs.find).to be_a(Mongo::Collection::View)
118
+ end
119
+
120
+ it 'iterates over the documents in the result' do
121
+ fs.find.each do |document|
122
+ expect(document).to_not be_nil
123
+ end
124
+ end
125
+ end
126
+
127
+ context 'when provided a filter' do
128
+
129
+ let(:view) do
130
+ fs.find(filename: 'test.txt')
131
+ end
132
+
133
+ it 'returns a collection view for the filter' do
134
+ expect(view.filter).to eq('filename' => 'test.txt')
135
+ end
136
+ end
137
+
138
+ context 'when options are provided' do
139
+
140
+ let(:view) do
141
+ fs.find({filename: 'test.txt'}, options)
142
+ end
143
+
144
+ context 'when provided batch_size' do
145
+
146
+ let(:options) do
147
+ { batch_size: 5 }
148
+ end
149
+
150
+ it 'sets the batch_size on the view' do
151
+ expect(view.batch_size).to eq(options[:batch_size])
152
+ end
153
+ end
154
+
155
+ context 'when provided limit' do
156
+
157
+ let(:options) do
158
+ { limit: 5 }
159
+ end
160
+
161
+ it 'sets the limit on the view' do
162
+ expect(view.limit).to eq(options[:limit])
163
+ end
164
+ end
165
+
166
+ context 'when provided no_cursor_timeout' do
167
+
168
+ let(:options) do
169
+ { no_cursor_timeout: true }
170
+ end
171
+
172
+ it 'sets the no_cursor_timeout on the view' do
173
+ expect(view.options[:no_cursor_timeout]).to eq(options[:no_cursor_timeout])
174
+ end
175
+ end
176
+
177
+ context 'when provided skip' do
178
+
179
+ let(:options) do
180
+ { skip: 5 }
181
+ end
182
+
183
+ it 'sets the skip on the view' do
184
+ expect(view.skip).to eq(options[:skip])
185
+ end
186
+ end
187
+
188
+ context 'when provided sort' do
189
+
190
+ let(:options) do
191
+ { sort: { 'x' => Mongo::Index::ASCENDING } }
192
+ end
193
+
194
+ it 'sets the sort on the view' do
195
+ expect(view.sort).to eq(options[:sort])
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ describe '#find_one' do
202
+
203
+ let(:fs) do
204
+ described_class.new(authorized_client.database)
205
+ end
206
+
207
+ let(:file) do
208
+ Mongo::Grid::File.new('hello world!', :filename => 'test.txt')
209
+ end
210
+
211
+ before do
212
+ fs.insert_one(file)
213
+ end
214
+
215
+ after do
216
+ fs.files_collection.delete_many
217
+ fs.chunks_collection.delete_many
218
+ end
219
+
220
+ let(:from_db) do
221
+ fs.find_one(:filename => 'test.txt')
222
+ end
223
+
224
+ it 'returns the assembled file from the db' do
225
+ expect(from_db.filename).to eq(file.info.filename)
226
+ end
227
+ end
228
+
229
+ describe '#insert_one' do
230
+
231
+ let(:fs) do
232
+ described_class.new(authorized_client.database)
233
+ end
234
+
235
+ let(:file) do
236
+ Mongo::Grid::File.new('Hello!', :filename => 'test.txt')
237
+ end
238
+
239
+ context 'when inserting the file once' do
240
+
241
+ let!(:result) do
242
+ fs.insert_one(file)
243
+ end
244
+
245
+ after do
246
+ fs.files_collection.delete_many
247
+ fs.chunks_collection.delete_many
248
+ end
249
+
250
+ let(:from_db) do
251
+ fs.find_one(:filename => 'test.txt')
252
+ end
253
+
254
+ it 'inserts the file into the database' do
255
+ expect(from_db.filename).to eq(file.info.filename)
256
+ end
257
+
258
+ it 'includes the chunks and data with the file' do
259
+ expect(from_db.data).to eq('Hello!')
260
+ end
261
+
262
+ it 'returns the file id' do
263
+ expect(result).to eq(file.id)
264
+ end
265
+ end
266
+
267
+ context 'when the files collection is empty' do
268
+
269
+ before do
270
+ fs.files_collection.delete_many
271
+ fs.chunks_collection.delete_many
272
+ expect(fs.files_collection).to receive(:indexes).and_call_original
273
+ expect(fs.chunks_collection).to receive(:indexes).and_call_original
274
+ fs.insert_one(file)
275
+ end
276
+
277
+ after do
278
+ fs.files_collection.delete_many
279
+ fs.chunks_collection.delete_many
280
+ end
281
+
282
+ let(:chunks_index) do
283
+ fs.database[fs.chunks_collection.name].indexes.get(:files_id => 1, :n => 1)
284
+ end
285
+
286
+ let(:files_index) do
287
+ fs.database[fs.files_collection.name].indexes.get(:filename => 1, :uploadDate => 1)
288
+ end
289
+
290
+ it 'creates an index on the files collection' do
291
+ expect(files_index[:name]).to eq('filename_1_uploadDate_1')
292
+ end
293
+
294
+ it 'creates an index on the chunks collection' do
295
+ expect(chunks_index[:name]).to eq('files_id_1_n_1')
296
+ end
297
+
298
+ context 'when a write operation is called more than once' do
299
+
300
+ before do
301
+ expect(fs).not_to receive(:ensure_indexes!)
302
+ end
303
+
304
+ let(:file2) do
305
+ Mongo::Grid::File.new('Goodbye!', :filename => 'test2.txt')
306
+ end
307
+
308
+ it 'only creates the indexes the first time' do
309
+ expect(fs.insert_one(file2)).to be_a(BSON::ObjectId)
310
+ end
311
+ end
312
+ end
313
+
314
+ context 'when the index creation encounters an error', if: write_command_enabled? do
315
+
316
+ before do
317
+ fs.chunks_collection.drop
318
+ fs.chunks_collection.indexes.create_one(Mongo::Grid::FSBucket::CHUNKS_INDEX, :unique => false)
319
+ expect(fs.chunks_collection).to receive(:indexes).and_call_original
320
+ expect(fs.files_collection).not_to receive(:indexes)
321
+ end
322
+
323
+ after do
324
+ fs.database[fs.chunks_collection.name].indexes.drop_one('files_id_1_n_1')
325
+ end
326
+
327
+ it 'raises the error to the user' do
328
+ expect {
329
+ fs.insert_one(file)
330
+ }.to raise_error(Mongo::Error::OperationFailure)
331
+ end
332
+ end
333
+
334
+ context 'when the files collection is not empty' do
335
+
336
+ before do
337
+ fs.files_collection.insert_one(a: 1)
338
+ expect(fs.files_collection).not_to receive(:indexes)
339
+ expect(fs.chunks_collection).not_to receive(:indexes)
340
+ fs.insert_one(file)
341
+ end
342
+
343
+ after do
344
+ fs.files_collection.delete_many
345
+ fs.chunks_collection.delete_many
346
+ end
347
+
348
+ let(:files_index) do
349
+ fs.database[fs.files_collection.name].indexes.get(:filename => 1, :uploadDate => 1)
350
+ end
351
+
352
+ it 'assumes indexes already exist' do
353
+ expect(files_index[:name]).to eq('filename_1_uploadDate_1')
354
+ end
355
+ end
356
+
357
+ context 'when inserting the file more than once' do
358
+
359
+ after do
360
+ fs.files_collection.delete_many
361
+ fs.chunks_collection.delete_many
362
+ end
363
+
364
+ it 'raises an error' do
365
+ expect {
366
+ fs.insert_one(file)
367
+ fs.insert_one(file)
368
+ }.to raise_error(Mongo::Error::BulkWriteError)
369
+ end
370
+ end
371
+
372
+ context 'when the file exceeds the max bson size' do
373
+
374
+ let(:fs) do
375
+ described_class.new(authorized_client.database)
376
+ end
377
+
378
+ let(:file) do
379
+ str = 'y' * 16777216
380
+ Mongo::Grid::File.new(str, :filename => 'large-file.txt')
381
+ end
382
+
383
+ before do
384
+ fs.insert_one(file)
385
+ end
386
+
387
+ after do
388
+ fs.files_collection.delete_many
389
+ fs.chunks_collection.delete_many
390
+ end
391
+
392
+ it 'successfully inserts the file' do
393
+ expect(
394
+ fs.find_one(:filename => 'large-file.txt').chunks
395
+ ).to eq(file.chunks)
396
+ end
397
+ end
398
+ end
399
+
400
+ describe '#delete_one' do
401
+
402
+ let(:file) do
403
+ Mongo::Grid::File.new('Hello!', :filename => 'test.txt')
404
+ end
405
+
406
+ before do
407
+ fs.insert_one(file)
408
+ fs.delete_one(file)
409
+ end
410
+
411
+ let(:from_db) do
412
+ fs.find_one(:filename => 'test.txt')
413
+ end
414
+
415
+ it 'removes the file from the db' do
416
+ expect(from_db).to be_nil
417
+ end
418
+ end
419
+
420
+ describe '#delete' do
421
+
422
+ let(:file_id) do
423
+ fs.upload_from_stream(filename, file)
424
+ end
425
+
426
+ before do
427
+ fs.delete(file_id)
428
+ end
429
+
430
+ let(:from_db) do
431
+ fs.find_one(:filename => filename)
432
+ end
433
+
434
+ it 'removes the file from the db' do
435
+ expect(from_db).to be_nil
436
+ end
437
+ end
438
+
439
+ context 'when a read stream is opened' do
440
+
441
+ let(:fs) do
442
+ described_class.new(authorized_client.database)
443
+ end
444
+
445
+ let(:io) do
446
+ StringIO.new
447
+ end
448
+
449
+ after do
450
+ fs.files_collection.delete_many
451
+ fs.chunks_collection.delete_many
452
+ end
453
+
454
+ describe '#open_download_stream' do
455
+
456
+ let!(:file_id) do
457
+ fs.open_upload_stream(filename) do |stream|
458
+ stream.write(file)
459
+ end.file_id
460
+ end
461
+
462
+ context 'when a block is provided' do
463
+
464
+ let!(:stream) do
465
+ fs.open_download_stream(file_id) do |stream|
466
+ io.write(stream.read)
467
+ end
468
+ end
469
+
470
+ it 'returns a Stream::Read object' do
471
+ expect(stream).to be_a(Mongo::Grid::FSBucket::Stream::Read)
472
+ end
473
+
474
+ it 'closes the stream after the block completes' do
475
+ expect(stream.closed?).to be(true)
476
+ end
477
+
478
+ it 'yields the stream to the block' do
479
+ expect(io.size).to eq(file.size)
480
+ end
481
+ end
482
+
483
+ context 'when a block is not provided' do
484
+
485
+ let!(:stream) do
486
+ fs.open_download_stream(file_id)
487
+ end
488
+
489
+ it 'returns a Stream::Read object' do
490
+ expect(stream).to be_a(Mongo::Grid::FSBucket::Stream::Read)
491
+ end
492
+
493
+ it 'does not close the stream' do
494
+ expect(stream.closed?).to be(false)
495
+ end
496
+
497
+ it 'does not yield the stream to the block' do
498
+ expect(io.size).to eq(0)
499
+ end
500
+ end
501
+ end
502
+
503
+ describe '#download_to_stream' do
504
+
505
+ context 'when the file is found' do
506
+
507
+ let!(:file_id) do
508
+ fs.open_upload_stream(filename) do |stream|
509
+ stream.write(file)
510
+ end.file_id
511
+ end
512
+
513
+ before do
514
+ fs.download_to_stream(file_id, io)
515
+ end
516
+
517
+ it 'writes to the provided stream' do
518
+ expect(io.size).to eq(file.size)
519
+ end
520
+
521
+ it 'does not close the stream' do
522
+ expect(io.closed?).to be(false)
523
+ end
524
+
525
+ context 'when the file has length 0' do
526
+
527
+ let(:file) do
528
+ StringIO.new('')
529
+ end
530
+
531
+ let(:from_db) do
532
+ fs.open_upload_stream(filename) { |s| s.write(file) }
533
+ fs.find_one(:filename => filename)
534
+ end
535
+
536
+ it 'can read the file back' do
537
+ expect(from_db.data.size).to eq(file.size)
538
+ end
539
+ end
540
+ end
541
+
542
+ context 'when there is no files collection document found' do
543
+
544
+ it 'raises an exception' do
545
+ expect{
546
+ fs.download_to_stream(BSON::ObjectId.new, io)
547
+ }.to raise_exception(Mongo::Error::FileNotFound)
548
+ end
549
+ end
550
+
551
+ context 'when a file has an id that is not an ObjectId' do
552
+
553
+ before do
554
+ fs.insert_one(file)
555
+ fs.download_to_stream(file_id, io)
556
+ end
557
+
558
+ let(:file_id) do
559
+ 'non-object-id'
560
+ end
561
+
562
+ let(:file) do
563
+ Mongo::Grid::File.new(File.open(__FILE__).read,
564
+ :filename => filename,
565
+ :_id => file_id)
566
+ end
567
+
568
+ it 'reads the file successfully' do
569
+ expect(io.size).to eq(file.data.size)
570
+ end
571
+ end
572
+ end
573
+
574
+ context 'when a read preference is specified' do
575
+
576
+ let(:fs) do
577
+ described_class.new(authorized_client.database, options)
578
+ end
579
+
580
+ let(:options) do
581
+ { read: { mode: :secondary } }
582
+ end
583
+
584
+ let(:stream) do
585
+ fs.open_download_stream(BSON::ObjectId)
586
+ end
587
+
588
+ it 'sets the read preference on the Stream::Read object' do
589
+ expect(stream.read_preference).to eq(Mongo::ServerSelector.get(options[:read]))
590
+ end
591
+ end
592
+
593
+ describe '#download_to_stream_by_name' do
594
+
595
+ let(:files) do
596
+ [
597
+ StringIO.new('hello 1'),
598
+ StringIO.new('hello 2'),
599
+ StringIO.new('hello 3'),
600
+ StringIO.new('hello 4')
601
+ ]
602
+ end
603
+
604
+ before do
605
+ files.each do |file|
606
+ fs.upload_from_stream('test.txt', file)
607
+ end
608
+ end
609
+
610
+ let(:io) do
611
+ StringIO.new
612
+ end
613
+
614
+ context 'when revision is not specified' do
615
+
616
+ let!(:result) do
617
+ fs.download_to_stream_by_name('test.txt', io)
618
+ end
619
+
620
+ it 'returns the most recent version' do
621
+ expect(io.string).to eq('hello 4')
622
+ end
623
+ end
624
+
625
+ context 'when revision is 0' do
626
+
627
+ let!(:result) do
628
+ fs.download_to_stream_by_name('test.txt', io, revision: 0)
629
+ end
630
+
631
+ it 'returns the original stored file' do
632
+ expect(io.string).to eq('hello 1')
633
+ end
634
+ end
635
+
636
+ context 'when revision is negative' do
637
+
638
+ let!(:result) do
639
+ fs.download_to_stream_by_name('test.txt', io, revision: -2)
640
+ end
641
+
642
+ it 'returns that number of versions from the most recent' do
643
+ expect(io.string).to eq('hello 3')
644
+ end
645
+ end
646
+
647
+ context 'when revision is positive' do
648
+
649
+ let!(:result) do
650
+ fs.download_to_stream_by_name('test.txt', io, revision: 1)
651
+ end
652
+
653
+ it 'returns that number revision' do
654
+ expect(io.string).to eq('hello 2')
655
+ end
656
+ end
657
+
658
+ context 'when the file revision is not found' do
659
+
660
+ it 'raises a FileNotFound error' do
661
+ expect {
662
+ fs.download_to_stream_by_name('test.txt', io, revision: 100)
663
+ }.to raise_exception(Mongo::Error::InvalidFileRevision)
664
+ end
665
+ end
666
+
667
+ context 'when the file is not found' do
668
+
669
+ it 'raises a FileNotFound error' do
670
+ expect {
671
+ fs.download_to_stream_by_name('non-existent.txt', io)
672
+ }.to raise_exception(Mongo::Error::FileNotFound)
673
+ end
674
+ end
675
+ end
676
+
677
+ describe '#open_download_stream_by_name' do
678
+
679
+ let(:files) do
680
+ [
681
+ StringIO.new('hello 1'),
682
+ StringIO.new('hello 2'),
683
+ StringIO.new('hello 3'),
684
+ StringIO.new('hello 4')
685
+ ]
686
+ end
687
+
688
+ before do
689
+ files.each do |file|
690
+ fs.upload_from_stream('test.txt', file)
691
+ end
692
+ end
693
+
694
+ let(:io) do
695
+ StringIO.new
696
+ end
697
+
698
+ context 'when a block is provided' do
699
+
700
+ let(:stream) do
701
+ fs.open_download_stream_by_name('test.txt') do |stream|
702
+ io.write(stream.read)
703
+ end
704
+ end
705
+
706
+ it 'returns a Stream::Read object' do
707
+ expect(stream).to be_a(Mongo::Grid::FSBucket::Stream::Read)
708
+ end
709
+
710
+ it 'closes the stream after the block completes' do
711
+ expect(stream.closed?).to be(true)
712
+ end
713
+
714
+ it 'yields the stream to the block' do
715
+ stream
716
+ expect(io.size).to eq(files[0].size)
717
+ end
718
+
719
+ context 'when revision is not specified' do
720
+
721
+ let!(:result) do
722
+ fs.open_download_stream_by_name('test.txt') do |stream|
723
+ io.write(stream.read)
724
+ end
725
+ end
726
+
727
+ it 'returns the most recent version' do
728
+ expect(io.string).to eq('hello 4')
729
+ end
730
+ end
731
+
732
+ context 'when revision is 0' do
733
+
734
+ let!(:result) do
735
+ fs.open_download_stream_by_name('test.txt', revision: 0) do |stream|
736
+ io.write(stream.read)
737
+ end
738
+ end
739
+
740
+ it 'returns the original stored file' do
741
+ expect(io.string).to eq('hello 1')
742
+ end
743
+ end
744
+
745
+ context 'when revision is negative' do
746
+
747
+ let!(:result) do
748
+ fs.open_download_stream_by_name('test.txt', revision: -2) do |stream|
749
+ io.write(stream.read)
750
+ end
751
+ end
752
+
753
+ it 'returns that number of versions from the most recent' do
754
+ expect(io.string).to eq('hello 3')
755
+ end
756
+ end
757
+
758
+ context 'when revision is positive' do
759
+
760
+ let!(:result) do
761
+ fs.open_download_stream_by_name('test.txt', revision: 1) do |stream|
762
+ io.write(stream.read)
763
+ end
764
+ end
765
+
766
+ it 'returns that number revision' do
767
+ expect(io.string).to eq('hello 2')
768
+ end
769
+ end
770
+
771
+ context 'when the file revision is not found' do
772
+
773
+ it 'raises a FileNotFound error' do
774
+ expect {
775
+ fs.open_download_stream_by_name('test.txt', revision: 100)
776
+ }.to raise_exception(Mongo::Error::InvalidFileRevision)
777
+ end
778
+ end
779
+
780
+ context 'when the file is not found' do
781
+
782
+ it 'raises a FileNotFound error' do
783
+ expect {
784
+ fs.open_download_stream_by_name('non-existent.txt')
785
+ }.to raise_exception(Mongo::Error::FileNotFound)
786
+ end
787
+ end
788
+ end
789
+
790
+ context 'when a block is not provided' do
791
+
792
+ let!(:stream) do
793
+ fs.open_download_stream_by_name('test.txt')
794
+ end
795
+
796
+ it 'returns a Stream::Read object' do
797
+ expect(stream).to be_a(Mongo::Grid::FSBucket::Stream::Read)
798
+ end
799
+
800
+ it 'does not close the stream' do
801
+ expect(stream.closed?).to be(false)
802
+ end
803
+
804
+ it 'does not yield the stream to the block' do
805
+ expect(io.size).to eq(0)
806
+ end
807
+ end
808
+ end
809
+ end
810
+
811
+ context 'when a write stream is opened' do
812
+
813
+ let(:stream) do
814
+ fs.open_upload_stream(filename)
815
+ end
816
+
817
+ after do
818
+ fs.files_collection.delete_many
819
+ fs.chunks_collection.delete_many
820
+ end
821
+
822
+ describe '#open_upload_stream' do
823
+
824
+ context 'when a block is not provided' do
825
+
826
+ it 'returns a Stream::Write object' do
827
+ expect(stream).to be_a(Mongo::Grid::FSBucket::Stream::Write)
828
+ end
829
+
830
+ it 'creates an ObjectId for the file' do
831
+ expect(stream.file_id).to be_a(BSON::ObjectId)
832
+ end
833
+ end
834
+
835
+ context 'when a block is provided' do
836
+
837
+ let!(:stream) do
838
+ fs.open_upload_stream(filename) do |stream|
839
+ stream.write(file)
840
+ end
841
+ end
842
+
843
+ let(:result) do
844
+ fs.find_one(filename: filename)
845
+ end
846
+
847
+ it 'returns the stream' do
848
+ expect(stream).to be_a(Mongo::Grid::FSBucket::Stream::Write)
849
+ end
850
+
851
+ it 'creates an ObjectId for the file' do
852
+ expect(stream.file_id).to be_a(BSON::ObjectId)
853
+ end
854
+
855
+ it 'yields the stream to the block' do
856
+ expect(result.data.size).to eq(file.size)
857
+ end
858
+
859
+ it 'closes the stream when the block completes' do
860
+ expect(stream.closed?).to be(true)
861
+ end
862
+ end
863
+ end
864
+
865
+ describe '#upload_from_stream' do
866
+
867
+ let!(:result) do
868
+ fs.upload_from_stream(filename, file)
869
+ end
870
+
871
+ let(:file_from_db) do
872
+ fs.find_one(:filename => filename)
873
+ end
874
+
875
+ it 'writes to the provided stream' do
876
+ expect(file_from_db.data.length).to eq(file.size)
877
+ end
878
+
879
+ it 'does not close the stream' do
880
+ expect(file.closed?).to be(false)
881
+ end
882
+
883
+ it 'returns the id of the file' do
884
+ expect(result).to be_a(BSON::ObjectId)
885
+ end
886
+
887
+ context 'when the io stream raises an error' do
888
+
889
+ let(:stream) do
890
+ fs.open_upload_stream(filename)
891
+ end
892
+
893
+ before do
894
+ allow(fs).to receive(:open_upload_stream).and_yield(stream)
895
+ end
896
+
897
+ context 'when stream#abort does not raise an OperationFailure' do
898
+
899
+ before do
900
+ expect(stream).to receive(:abort).and_call_original
901
+ file.close
902
+ end
903
+
904
+ it 'raises the original IOError' do
905
+ expect {
906
+ fs.upload_from_stream(filename, file)
907
+ }.to raise_exception(IOError)
908
+ end
909
+ end
910
+
911
+ context 'when stream#abort raises an OperationFailure' do
912
+
913
+ before do
914
+ allow(stream).to receive(:abort).and_raise(Mongo::Error::OperationFailure)
915
+ file.close
916
+ end
917
+
918
+ it 'raises the original IOError' do
919
+ expect {
920
+ fs.upload_from_stream(filename, file)
921
+ }.to raise_exception(IOError)
922
+ end
923
+ end
924
+ end
925
+ end
926
+
927
+ context 'when options are provided when opening the write stream' do
928
+
929
+ let(:stream) do
930
+ fs.open_upload_stream(filename, stream_options)
931
+ end
932
+
933
+ context 'when a write option is specified' do
934
+
935
+ let(:stream_options) do
936
+ { write: { w: 2 } }
937
+ end
938
+
939
+ it 'sets the write concern on the write stream' do
940
+ expect(stream.write_concern.options).to eq(Mongo::WriteConcern.get(stream_options[:write]).options)
941
+ end
942
+ end
943
+
944
+ context 'when there is a chunk size set on the FSBucket' do
945
+
946
+ let(:stream_options) do
947
+ { }
948
+ end
949
+
950
+ let(:options) do
951
+ { chunk_size: 100 }
952
+ end
953
+
954
+ it 'sets the chunk size as the default on the write stream' do
955
+ expect(stream.options[:chunk_size]).to eq(options[:chunk_size])
956
+ end
957
+ end
958
+
959
+ context 'when a chunk size option is specified' do
960
+
961
+ let(:stream_options) do
962
+ { chunk_size: 50 }
963
+ end
964
+
965
+ it 'sets the chunk size on the write stream' do
966
+ expect(stream.options[:chunk_size]).to eq(stream_options[:chunk_size])
967
+ end
968
+
969
+ context 'when there is a chunk size set on the FSBucket' do
970
+
971
+ let(:options) do
972
+ { chunk_size: 100 }
973
+ end
974
+
975
+ let(:fs) do
976
+ described_class.new(authorized_client.database, options)
977
+ end
978
+
979
+ it 'uses the chunk size set on the write stream' do
980
+ expect(stream.options[:chunk_size]).to eq(stream_options[:chunk_size])
981
+ end
982
+
983
+ end
984
+ end
985
+
986
+ context 'when a file metadata option is specified' do
987
+
988
+ let(:stream_options) do
989
+ { metadata: { some_field: 1 } }
990
+ end
991
+
992
+ it 'sets the file metadata option on the write stream' do
993
+ expect(stream.options[:metadata]).to eq(stream_options[:metadata])
994
+ end
995
+ end
996
+
997
+ context 'when a content type option is specified' do
998
+
999
+ let(:stream_options) do
1000
+ { content_type: 'text/plain' }
1001
+ end
1002
+
1003
+ it 'sets the content type on the write stream' do
1004
+ expect(stream.options[:content_type]).to eq(stream_options[:content_type])
1005
+ end
1006
+ end
1007
+
1008
+ context 'when a aliases option is specified' do
1009
+
1010
+ let(:stream_options) do
1011
+ { aliases: [ 'another-name.txt' ] }
1012
+ end
1013
+
1014
+ it 'sets the alias option on the write stream' do
1015
+ expect(stream.options[:aliases]).to eq(stream_options[:aliases])
1016
+ end
1017
+ end
1018
+ end
1019
+ end
1020
+ end