mongoid 7.5.4 → 8.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (298) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data/README.md +3 -3
  4. data/Rakefile +0 -25
  5. data/lib/config/locales/en.yml +46 -30
  6. data/lib/mongoid/association/accessors.rb +32 -3
  7. data/lib/mongoid/association/bindable.rb +48 -0
  8. data/lib/mongoid/association/builders.rb +4 -2
  9. data/lib/mongoid/association/eager_loadable.rb +29 -7
  10. data/lib/mongoid/association/embedded/batchable.rb +28 -5
  11. data/lib/mongoid/association/embedded/embedded_in/binding.rb +24 -2
  12. data/lib/mongoid/association/embedded/embedded_in.rb +2 -1
  13. data/lib/mongoid/association/embedded/embeds_many/binding.rb +1 -0
  14. data/lib/mongoid/association/embedded/embeds_many/buildable.rb +1 -1
  15. data/lib/mongoid/association/embedded/embeds_many/proxy.rb +40 -18
  16. data/lib/mongoid/association/embedded/embeds_one/buildable.rb +18 -4
  17. data/lib/mongoid/association/embedded/embeds_one/proxy.rb +21 -2
  18. data/lib/mongoid/association/macros.rb +2 -1
  19. data/lib/mongoid/association/many.rb +5 -0
  20. data/lib/mongoid/association/nested/many.rb +2 -1
  21. data/lib/mongoid/association/proxy.rb +12 -0
  22. data/lib/mongoid/association/referenced/auto_save.rb +3 -2
  23. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -0
  24. data/lib/mongoid/association/referenced/belongs_to/buildable.rb +1 -1
  25. data/lib/mongoid/association/referenced/belongs_to.rb +1 -1
  26. data/lib/mongoid/association/referenced/counter_cache.rb +8 -8
  27. data/lib/mongoid/association/referenced/has_and_belongs_to_many/proxy.rb +64 -11
  28. data/lib/mongoid/association/referenced/has_and_belongs_to_many.rb +4 -1
  29. data/lib/mongoid/association/referenced/has_many/enumerable.rb +10 -18
  30. data/lib/mongoid/association/referenced/has_many/proxy.rb +12 -9
  31. data/lib/mongoid/association/referenced/has_one/buildable.rb +1 -1
  32. data/lib/mongoid/association/referenced/has_one/proxy.rb +8 -11
  33. data/lib/mongoid/association/referenced/syncable.rb +2 -2
  34. data/lib/mongoid/association/relatable.rb +38 -4
  35. data/lib/mongoid/attributes/processing.rb +9 -2
  36. data/lib/mongoid/attributes.rb +30 -27
  37. data/lib/mongoid/cacheable.rb +2 -2
  38. data/lib/mongoid/changeable.rb +37 -2
  39. data/lib/mongoid/clients/options.rb +4 -0
  40. data/lib/mongoid/clients/sessions.rb +2 -14
  41. data/lib/mongoid/config.rb +15 -11
  42. data/lib/mongoid/contextual/aggregable/memory.rb +23 -15
  43. data/lib/mongoid/contextual/aggregable/mongo.rb +1 -1
  44. data/lib/mongoid/contextual/map_reduce.rb +2 -2
  45. data/lib/mongoid/contextual/memory.rb +55 -28
  46. data/lib/mongoid/contextual/mongo.rb +173 -262
  47. data/lib/mongoid/contextual/none.rb +33 -15
  48. data/lib/mongoid/copyable.rb +32 -8
  49. data/lib/mongoid/criteria/includable.rb +24 -20
  50. data/lib/mongoid/criteria/marshalable.rb +10 -2
  51. data/lib/mongoid/criteria/queryable/extensions/array.rb +2 -15
  52. data/lib/mongoid/criteria/queryable/extensions/big_decimal.rb +25 -4
  53. data/lib/mongoid/criteria/queryable/extensions/boolean.rb +1 -1
  54. data/lib/mongoid/criteria/queryable/extensions/date.rb +6 -1
  55. data/lib/mongoid/criteria/queryable/extensions/date_time.rb +6 -1
  56. data/lib/mongoid/criteria/queryable/extensions/hash.rb +0 -16
  57. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  58. data/lib/mongoid/criteria/queryable/extensions/object.rb +2 -1
  59. data/lib/mongoid/criteria/queryable/extensions/range.rb +13 -5
  60. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +1 -1
  61. data/lib/mongoid/criteria/queryable/extensions/symbol.rb +3 -1
  62. data/lib/mongoid/criteria/queryable/extensions/time.rb +6 -1
  63. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +6 -1
  64. data/lib/mongoid/criteria/queryable/optional.rb +3 -9
  65. data/lib/mongoid/criteria/queryable/options.rb +1 -1
  66. data/lib/mongoid/criteria/queryable/selectable.rb +2 -24
  67. data/lib/mongoid/criteria/queryable/selector.rb +90 -5
  68. data/lib/mongoid/criteria/queryable/smash.rb +39 -6
  69. data/lib/mongoid/criteria/queryable/storable.rb +1 -1
  70. data/lib/mongoid/criteria/queryable.rb +11 -6
  71. data/lib/mongoid/criteria.rb +1 -28
  72. data/lib/mongoid/deprecable.rb +36 -0
  73. data/lib/mongoid/deprecation.rb +25 -0
  74. data/lib/mongoid/document.rb +88 -33
  75. data/lib/mongoid/equality.rb +4 -4
  76. data/lib/mongoid/errors/document_not_found.rb +6 -2
  77. data/lib/mongoid/errors/invalid_dot_dollar_assignment.rb +23 -0
  78. data/lib/mongoid/errors/invalid_field.rb +5 -1
  79. data/lib/mongoid/errors/invalid_field_type.rb +26 -0
  80. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +1 -1
  81. data/lib/mongoid/errors.rb +2 -2
  82. data/lib/mongoid/extensions/array.rb +8 -6
  83. data/lib/mongoid/extensions/big_decimal.rb +29 -10
  84. data/lib/mongoid/extensions/binary.rb +42 -0
  85. data/lib/mongoid/extensions/boolean.rb +8 -2
  86. data/lib/mongoid/extensions/date.rb +26 -20
  87. data/lib/mongoid/extensions/date_time.rb +1 -1
  88. data/lib/mongoid/extensions/float.rb +4 -5
  89. data/lib/mongoid/extensions/hash.rb +12 -5
  90. data/lib/mongoid/extensions/integer.rb +4 -5
  91. data/lib/mongoid/extensions/object.rb +2 -0
  92. data/lib/mongoid/extensions/range.rb +41 -10
  93. data/lib/mongoid/extensions/regexp.rb +11 -4
  94. data/lib/mongoid/extensions/set.rb +11 -4
  95. data/lib/mongoid/extensions/string.rb +2 -13
  96. data/lib/mongoid/extensions/symbol.rb +3 -14
  97. data/lib/mongoid/extensions/time.rb +27 -16
  98. data/lib/mongoid/extensions/time_with_zone.rb +1 -2
  99. data/lib/mongoid/extensions.rb +1 -0
  100. data/lib/mongoid/factory.rb +42 -7
  101. data/lib/mongoid/fields/foreign_key.rb +7 -0
  102. data/lib/mongoid/fields/validators/macro.rb +3 -9
  103. data/lib/mongoid/fields.rb +49 -7
  104. data/lib/mongoid/findable.rb +21 -16
  105. data/lib/mongoid/indexable/specification.rb +1 -1
  106. data/lib/mongoid/indexable/validators/options.rb +4 -1
  107. data/lib/mongoid/interceptable.rb +69 -9
  108. data/lib/mongoid/persistable/creatable.rb +14 -5
  109. data/lib/mongoid/persistable/updatable.rb +12 -5
  110. data/lib/mongoid/persistence_context.rb +8 -42
  111. data/lib/mongoid/query_cache.rb +6 -258
  112. data/lib/mongoid/railties/controller_runtime.rb +1 -1
  113. data/lib/mongoid/reloadable.rb +7 -3
  114. data/lib/mongoid/scopable.rb +9 -11
  115. data/lib/mongoid/selectable.rb +1 -2
  116. data/lib/mongoid/shardable.rb +11 -35
  117. data/lib/mongoid/stateful.rb +27 -1
  118. data/lib/mongoid/timestamps/created.rb +1 -1
  119. data/lib/mongoid/timestamps/updated.rb +1 -1
  120. data/lib/mongoid/touchable.rb +2 -3
  121. data/lib/mongoid/traversable.rb +1 -0
  122. data/lib/mongoid/validatable/uniqueness.rb +2 -1
  123. data/lib/mongoid/version.rb +1 -1
  124. data/lib/mongoid/warnings.rb +3 -4
  125. data/lib/mongoid.rb +1 -0
  126. data/spec/config/mongoid.yml +16 -0
  127. data/spec/integration/app_spec.rb +8 -12
  128. data/spec/integration/associations/belongs_to_spec.rb +18 -0
  129. data/spec/integration/associations/embedded_spec.rb +15 -0
  130. data/spec/integration/associations/embeds_many_spec.rb +15 -2
  131. data/spec/integration/associations/embeds_one_spec.rb +18 -0
  132. data/spec/integration/associations/foreign_key_spec.rb +9 -0
  133. data/spec/integration/associations/has_and_belongs_to_many_spec.rb +21 -0
  134. data/spec/integration/associations/has_one_spec.rb +97 -1
  135. data/spec/integration/associations/scope_option_spec.rb +1 -1
  136. data/spec/integration/callbacks_models.rb +95 -1
  137. data/spec/integration/callbacks_spec.rb +226 -4
  138. data/spec/integration/criteria/range_spec.rb +95 -1
  139. data/spec/integration/discriminator_key_spec.rb +115 -76
  140. data/spec/integration/dots_and_dollars_spec.rb +277 -0
  141. data/spec/integration/i18n_fallbacks_spec.rb +1 -15
  142. data/spec/integration/matcher_examples_spec.rb +20 -13
  143. data/spec/integration/matcher_operator_data/type_decimal.yml +3 -2
  144. data/spec/integration/matcher_operator_spec.rb +3 -5
  145. data/spec/integration/persistence/range_field_spec.rb +350 -0
  146. data/spec/mongoid/association/counter_cache_spec.rb +1 -1
  147. data/spec/mongoid/association/depending_spec.rb +9 -9
  148. data/spec/mongoid/association/eager_spec.rb +2 -1
  149. data/spec/mongoid/association/embedded/embedded_in/binding_spec.rb +2 -1
  150. data/spec/mongoid/association/embedded/embedded_in/buildable_spec.rb +54 -0
  151. data/spec/mongoid/association/embedded/embedded_in/proxy_spec.rb +69 -9
  152. data/spec/mongoid/association/embedded/embeds_many/buildable_spec.rb +112 -0
  153. data/spec/mongoid/association/embedded/embeds_many/proxy_spec.rb +198 -8
  154. data/spec/mongoid/association/embedded/embeds_many_models.rb +36 -0
  155. data/spec/mongoid/association/embedded/embeds_many_query_spec.rb +12 -0
  156. data/spec/mongoid/association/embedded/embeds_many_spec.rb +68 -0
  157. data/spec/mongoid/association/embedded/embeds_one/buildable_spec.rb +25 -0
  158. data/spec/mongoid/association/embedded/embeds_one_models.rb +19 -0
  159. data/spec/mongoid/association/embedded/embeds_one_spec.rb +28 -0
  160. data/spec/mongoid/association/referenced/belongs_to/binding_spec.rb +2 -1
  161. data/spec/mongoid/association/referenced/belongs_to/buildable_spec.rb +54 -0
  162. data/spec/mongoid/association/referenced/belongs_to/proxy_spec.rb +15 -0
  163. data/spec/mongoid/association/referenced/belongs_to_models.rb +11 -0
  164. data/spec/mongoid/association/referenced/belongs_to_spec.rb +2 -2
  165. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +38 -5
  166. data/spec/mongoid/association/referenced/has_and_belongs_to_many_models.rb +25 -0
  167. data/spec/mongoid/association/referenced/has_and_belongs_to_many_spec.rb +35 -2
  168. data/spec/mongoid/association/referenced/has_many/buildable_spec.rb +109 -0
  169. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +2 -56
  170. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +62 -13
  171. data/spec/mongoid/association/referenced/has_many_models.rb +3 -1
  172. data/spec/mongoid/association/referenced/has_many_spec.rb +25 -0
  173. data/spec/mongoid/association/referenced/has_one/buildable_spec.rb +2 -2
  174. data/spec/mongoid/association/referenced/has_one/proxy_spec.rb +107 -1
  175. data/spec/mongoid/association/referenced/has_one_models.rb +16 -0
  176. data/spec/mongoid/association/syncable_spec.rb +14 -0
  177. data/spec/mongoid/atomic/paths_spec.rb +0 -14
  178. data/spec/mongoid/attributes/nested_spec.rb +80 -11
  179. data/spec/mongoid/attributes/nested_spec_models.rb +48 -0
  180. data/spec/mongoid/attributes/projector_spec.rb +1 -5
  181. data/spec/mongoid/attributes_spec.rb +480 -27
  182. data/spec/mongoid/cacheable_spec.rb +3 -3
  183. data/spec/mongoid/changeable_spec.rb +130 -13
  184. data/spec/mongoid/clients/factory_spec.rb +23 -30
  185. data/spec/mongoid/clients/sessions_spec.rb +0 -38
  186. data/spec/mongoid/clients_spec.rb +2 -2
  187. data/spec/mongoid/config_spec.rb +52 -14
  188. data/spec/mongoid/contextual/aggregable/memory_spec.rb +396 -158
  189. data/spec/mongoid/contextual/aggregable/memory_table.yml +88 -0
  190. data/spec/mongoid/contextual/aggregable/memory_table_spec.rb +62 -0
  191. data/spec/mongoid/contextual/map_reduce_spec.rb +2 -16
  192. data/spec/mongoid/contextual/memory_spec.rb +521 -14
  193. data/spec/mongoid/contextual/mongo_spec.rb +566 -416
  194. data/spec/mongoid/contextual/none_spec.rb +11 -19
  195. data/spec/mongoid/copyable_spec.rb +451 -1
  196. data/spec/mongoid/criteria/findable_spec.rb +86 -210
  197. data/spec/mongoid/criteria/includable_spec.rb +1492 -0
  198. data/spec/mongoid/criteria/includable_spec_models.rb +54 -0
  199. data/spec/mongoid/criteria/marshalable_spec.rb +18 -1
  200. data/spec/mongoid/criteria/queryable/extensions/array_spec.rb +7 -19
  201. data/spec/mongoid/criteria/queryable/extensions/big_decimal_spec.rb +134 -26
  202. data/spec/mongoid/criteria/queryable/extensions/date_spec.rb +11 -0
  203. data/spec/mongoid/criteria/queryable/extensions/date_time_spec.rb +11 -0
  204. data/spec/mongoid/criteria/queryable/extensions/hash_spec.rb +0 -15
  205. data/spec/mongoid/criteria/queryable/extensions/numeric_spec.rb +73 -7
  206. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +11 -0
  207. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +11 -0
  208. data/spec/mongoid/criteria/queryable/optional_spec.rb +0 -484
  209. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +50 -0
  210. data/spec/mongoid/criteria/queryable/selectable_spec.rb +77 -85
  211. data/spec/mongoid/criteria/queryable/selector_spec.rb +16 -77
  212. data/spec/mongoid/criteria/queryable/storable_spec.rb +0 -72
  213. data/spec/mongoid/criteria_spec.rb +469 -1201
  214. data/spec/mongoid/document_fields_spec.rb +173 -24
  215. data/spec/mongoid/document_spec.rb +32 -41
  216. data/spec/mongoid/equality_spec.rb +12 -12
  217. data/spec/mongoid/errors/document_not_found_spec.rb +29 -2
  218. data/spec/mongoid/errors/invalid_field_spec.rb +1 -1
  219. data/spec/mongoid/errors/invalid_field_type_spec.rb +55 -0
  220. data/spec/mongoid/errors/mongoid_error_spec.rb +3 -1
  221. data/spec/mongoid/errors/no_environment_spec.rb +3 -3
  222. data/spec/mongoid/errors/too_many_nested_attribute_records_spec.rb +1 -1
  223. data/spec/mongoid/extensions/array_spec.rb +16 -2
  224. data/spec/mongoid/extensions/big_decimal_spec.rb +697 -212
  225. data/spec/mongoid/extensions/binary_spec.rb +44 -9
  226. data/spec/mongoid/extensions/boolean_spec.rb +68 -82
  227. data/spec/mongoid/extensions/date_class_mongoize_spec.rb +7 -3
  228. data/spec/mongoid/extensions/date_spec.rb +71 -1
  229. data/spec/mongoid/extensions/date_time_spec.rb +15 -9
  230. data/spec/mongoid/extensions/float_spec.rb +48 -76
  231. data/spec/mongoid/extensions/hash_spec.rb +30 -0
  232. data/spec/mongoid/extensions/integer_spec.rb +45 -66
  233. data/spec/mongoid/extensions/range_spec.rb +255 -54
  234. data/spec/mongoid/extensions/regexp_spec.rb +58 -33
  235. data/spec/mongoid/extensions/set_spec.rb +106 -0
  236. data/spec/mongoid/extensions/string_spec.rb +53 -25
  237. data/spec/mongoid/extensions/symbol_spec.rb +18 -25
  238. data/spec/mongoid/extensions/time_spec.rb +634 -66
  239. data/spec/mongoid/extensions/time_with_zone_spec.rb +17 -31
  240. data/spec/mongoid/factory_spec.rb +61 -1
  241. data/spec/mongoid/fields_spec.rb +321 -50
  242. data/spec/mongoid/findable_spec.rb +64 -29
  243. data/spec/mongoid/indexable/specification_spec.rb +2 -2
  244. data/spec/mongoid/indexable_spec.rb +16 -19
  245. data/spec/mongoid/interceptable_spec.rb +584 -5
  246. data/spec/mongoid/interceptable_spec_models.rb +235 -4
  247. data/spec/mongoid/matcher/extract_attribute_spec.rb +1 -5
  248. data/spec/mongoid/mongoizable_spec.rb +285 -0
  249. data/spec/mongoid/persistable/creatable_spec.rb +2 -2
  250. data/spec/mongoid/persistable/deletable_spec.rb +2 -2
  251. data/spec/mongoid/persistable/destroyable_spec.rb +2 -2
  252. data/spec/mongoid/persistable/upsertable_spec.rb +14 -0
  253. data/spec/mongoid/persistence_context_spec.rb +24 -0
  254. data/spec/mongoid/query_cache_middleware_spec.rb +0 -18
  255. data/spec/mongoid/query_cache_spec.rb +0 -154
  256. data/spec/mongoid/reloadable_spec.rb +35 -2
  257. data/spec/mongoid/scopable_spec.rb +36 -34
  258. data/spec/mongoid/shardable_models.rb +0 -14
  259. data/spec/mongoid/shardable_spec.rb +61 -153
  260. data/spec/mongoid/stateful_spec.rb +28 -0
  261. data/spec/mongoid/timestamps_spec.rb +390 -0
  262. data/spec/mongoid/timestamps_spec_models.rb +67 -0
  263. data/spec/mongoid/touchable_spec.rb +116 -0
  264. data/spec/mongoid/touchable_spec_models.rb +12 -8
  265. data/spec/mongoid/traversable_spec.rb +4 -11
  266. data/spec/mongoid/validatable/presence_spec.rb +1 -1
  267. data/spec/mongoid/validatable/uniqueness_spec.rb +60 -31
  268. data/spec/mongoid/warnings_spec.rb +35 -0
  269. data/spec/mongoid_spec.rb +1 -7
  270. data/spec/rails/controller_extension/controller_runtime_spec.rb +2 -2
  271. data/spec/rails/mongoid_spec.rb +4 -16
  272. data/spec/shared/lib/mrss/event_subscriber.rb +5 -15
  273. data/spec/shared/lib/mrss/lite_constraints.rb +0 -8
  274. data/spec/shared/shlib/server.sh +5 -5
  275. data/spec/support/constraints.rb +24 -0
  276. data/spec/support/macros.rb +30 -0
  277. data/spec/support/models/augmentation.rb +12 -0
  278. data/spec/support/models/band.rb +3 -0
  279. data/spec/support/models/catalog.rb +24 -0
  280. data/spec/support/models/circus.rb +3 -0
  281. data/spec/support/models/fanatic.rb +8 -0
  282. data/spec/support/models/implant.rb +9 -0
  283. data/spec/support/models/label.rb +2 -0
  284. data/spec/support/models/passport.rb +9 -0
  285. data/spec/support/models/person.rb +1 -0
  286. data/spec/support/models/player.rb +2 -0
  287. data/spec/support/models/powerup.rb +12 -0
  288. data/spec/support/models/registry.rb +1 -0
  289. data/spec/support/models/school.rb +14 -0
  290. data/spec/support/models/shield.rb +18 -0
  291. data/spec/support/models/student.rb +14 -0
  292. data/spec/support/models/weapon.rb +12 -0
  293. data.tar.gz.sig +0 -0
  294. metadata +689 -657
  295. metadata.gz.sig +0 -0
  296. data/lib/mongoid/errors/eager_load.rb +0 -23
  297. data/lib/mongoid/errors/invalid_value.rb +0 -17
  298. data/spec/mongoid/errors/eager_load_spec.rb +0 -31
@@ -0,0 +1,1492 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "spec_helper"
4
+ require_relative "./includable_spec_models.rb"
5
+
6
+ describe Mongoid::Criteria::Includable do
7
+
8
+ describe "#includes" do
9
+
10
+ let!(:person) do
11
+ Person.create!(age: 1)
12
+ end
13
+
14
+ context "when providing a name that is not a relation" do
15
+
16
+ it "raises an error" do
17
+ expect {
18
+ Person.includes(:members)
19
+ }.to raise_error(Mongoid::Errors::InvalidIncludes)
20
+ end
21
+ end
22
+
23
+ context "when providing one association" do
24
+
25
+ let!(:user) do
26
+ User.create!(posts: [ post1 ])
27
+ end
28
+
29
+ let!(:post1) do
30
+ Post.create!
31
+ end
32
+
33
+ let(:result) do
34
+ User.includes(:posts).first
35
+ end
36
+
37
+ it "executes the query" do
38
+ expect(result).to eq(user)
39
+ end
40
+
41
+ it "includes the related objects" do
42
+ expect(result.posts).to eq([ post1 ])
43
+ end
44
+ end
45
+
46
+ context "when providing a list of associations" do
47
+
48
+ let!(:user) do
49
+ User.create!(posts: [ post1 ], descriptions: [ description1 ])
50
+ end
51
+
52
+ let!(:post1) do
53
+ Post.create!
54
+ end
55
+
56
+ let!(:description1) do
57
+ Description.create!(details: 1)
58
+ end
59
+
60
+ let(:result) do
61
+ User.includes(:posts, :descriptions).first
62
+ end
63
+
64
+ it "executes the query" do
65
+ expect(result).to eq(user)
66
+ end
67
+
68
+ it "includes the related objects" do
69
+ expect(result.posts).to eq([ post1 ])
70
+ expect(result.descriptions).to eq([ description1 ])
71
+ end
72
+ end
73
+
74
+ context "when providing a nested association" do
75
+
76
+ let!(:user) do
77
+ User.create!
78
+ end
79
+
80
+ before do
81
+ p = Post.create!(alerts: [ Alert.create! ])
82
+ user.posts = [ p ]
83
+ user.save!
84
+ end
85
+
86
+ let(:result) do
87
+ User.includes(:posts => [:alerts]).first
88
+ end
89
+
90
+ it "executes the query" do
91
+ expect(result).to eq(user)
92
+ end
93
+
94
+ it "includes the related objects" do
95
+ expect(result.posts.size).to eq(1)
96
+ expect(result.posts.first.alerts.size).to eq(1)
97
+ end
98
+ end
99
+
100
+ context "when providing a deeply nested association" do
101
+
102
+ let!(:user) do
103
+ User.create!
104
+ end
105
+
106
+ let(:results) do
107
+ User.includes(:posts => [{ :alerts => :items }]).to_a
108
+ end
109
+
110
+ it "executes the query" do
111
+ expect(results.first).to eq(user)
112
+ end
113
+ end
114
+
115
+ context "when the models are inherited" do
116
+
117
+ before(:all) do
118
+ class A
119
+ include Mongoid::Document
120
+ end
121
+
122
+ class B < A
123
+ belongs_to :c
124
+ end
125
+
126
+ class C
127
+ include Mongoid::Document
128
+ has_one :b
129
+ end
130
+ end
131
+
132
+ after(:all) do
133
+ Object.send(:remove_const, :A)
134
+ Object.send(:remove_const, :B)
135
+ Object.send(:remove_const, :C)
136
+ end
137
+
138
+ context "when the includes is on the subclass" do
139
+
140
+ let!(:c_one) do
141
+ C.create!
142
+ end
143
+
144
+ let!(:c_two) do
145
+ C.create!
146
+ end
147
+
148
+ let!(:b) do
149
+ B.create!(c: c_two)
150
+ end
151
+
152
+ let!(:results) do
153
+ C.includes(:b).to_a.detect do |c|
154
+ c.id == c_two.id
155
+ end
156
+ end
157
+
158
+ it "returns the correct documents" do
159
+ expect(results).to eq(c_two)
160
+ end
161
+
162
+ it "does not query the db" do
163
+ expect_query(0) do
164
+ results.b
165
+ end
166
+ end
167
+ end
168
+ end
169
+
170
+ context "when the models are inherited from another one model" do
171
+
172
+ context "when the relation is a has_one" do
173
+
174
+ before(:all) do
175
+ class A
176
+ include Mongoid::Document
177
+ end
178
+
179
+ class B < A
180
+ belongs_to :d
181
+ end
182
+
183
+ class C < A
184
+ belongs_to :d
185
+ end
186
+
187
+ class D
188
+ include Mongoid::Document
189
+ has_one :b
190
+ has_one :c
191
+ end
192
+ end
193
+
194
+ after(:all) do
195
+ Object.send(:remove_const, :A)
196
+ Object.send(:remove_const, :B)
197
+ Object.send(:remove_const, :C)
198
+ Object.send(:remove_const, :D)
199
+ end
200
+
201
+ context "when the includes is on the several relations" do
202
+
203
+ let!(:d_one) do
204
+ D.create!
205
+ end
206
+
207
+ let!(:d_two) do
208
+ D.create!
209
+ end
210
+
211
+ let!(:b) do
212
+ B.create!(d: d_two)
213
+ end
214
+
215
+ let!(:c) do
216
+ C.create!(d: d_two)
217
+ end
218
+
219
+ let!(:results) do
220
+ D.includes(:b, :c).entries.detect do |d|
221
+ d.id == d_two.id
222
+ end
223
+ end
224
+
225
+ it "returns the correct documents" do
226
+ expect(results).to eq(d_two)
227
+ end
228
+
229
+ it "does not query the db on b" do
230
+ expect_query(0) do
231
+ results.b
232
+ end
233
+ end
234
+
235
+ it "does not query the db on c" do
236
+ expect_query(0) do
237
+ results.b
238
+ end
239
+ end
240
+ end
241
+ end
242
+
243
+ context "when the relation is a has_many" do
244
+
245
+ before(:all) do
246
+ class A
247
+ include Mongoid::Document
248
+ end
249
+
250
+ class B < A
251
+ belongs_to :d
252
+ end
253
+
254
+ class C < A
255
+ belongs_to :d
256
+ end
257
+
258
+ class D
259
+ include Mongoid::Document
260
+ has_many :b
261
+ has_many :c
262
+ end
263
+ end
264
+
265
+ after(:all) do
266
+ Object.send(:remove_const, :A)
267
+ Object.send(:remove_const, :B)
268
+ Object.send(:remove_const, :C)
269
+ Object.send(:remove_const, :D)
270
+ end
271
+
272
+ context "when the includes is on the several relations" do
273
+
274
+ let!(:d_one) do
275
+ D.create!
276
+ end
277
+
278
+ let!(:d_two) do
279
+ D.create!
280
+ end
281
+
282
+ let!(:bs) do
283
+ 2.times.map { B.create!(d: d_two) }
284
+ end
285
+
286
+ let!(:cs) do
287
+ 2.times.map { C.create!(d: d_two) }
288
+ end
289
+
290
+ let!(:results) do
291
+ D.includes(:b, :c).entries.detect do |d|
292
+ d.id == d_two.id
293
+ end
294
+ end
295
+
296
+ it "returns the correct documents" do
297
+ expect(results).to eq(d_two)
298
+ end
299
+
300
+ it "does not query the db on b" do
301
+ expect_query(0) do
302
+ results.b
303
+ end
304
+ end
305
+
306
+ it "does not query the db on c" do
307
+ expect_query(0) do
308
+ results.b
309
+ end
310
+ end
311
+ end
312
+ end
313
+ end
314
+
315
+ context "when including the same association multiple times" do
316
+
317
+ let(:criteria) do
318
+ Person.all.includes(:posts, :posts).includes(:posts)
319
+ end
320
+
321
+ let(:association) do
322
+ Person.reflect_on_association(:posts)
323
+ end
324
+
325
+ it "does not duplicate the association in the inclusions" do
326
+ expect(criteria.inclusions).to eq([ association ])
327
+ end
328
+ end
329
+
330
+ context "when mapping the results more than once" do
331
+
332
+ let!(:post) do
333
+ person.posts.create!(title: "one")
334
+ end
335
+
336
+ let(:criteria) do
337
+ Post.includes(:person)
338
+ end
339
+
340
+ let!(:results) do
341
+ criteria.map { |doc| doc }
342
+ criteria.map { |doc| doc }
343
+ end
344
+
345
+ it "returns the proper results" do
346
+ expect(results.first.title).to eq("one")
347
+ end
348
+ end
349
+
350
+ context "when including a belongs to relation" do
351
+
352
+ context "when the criteria is from the root" do
353
+
354
+ let!(:person_two) do
355
+ Person.create!(age: 2)
356
+ end
357
+
358
+ let!(:post_one) do
359
+ person.posts.create!(title: "one")
360
+ end
361
+
362
+ let!(:post_two) do
363
+ person_two.posts.create!(title: "two")
364
+ end
365
+
366
+ context "when calling first" do
367
+
368
+ let(:criteria) do
369
+ Post.includes(:person)
370
+ end
371
+
372
+ let!(:document) do
373
+ criteria.first
374
+ end
375
+
376
+ it "eager loads the first document" do
377
+ expect_query(0) do
378
+ expect(document.person).to eq(person)
379
+ end
380
+ end
381
+
382
+ it "returns the first document" do
383
+ expect(document).to eq(post_one)
384
+ end
385
+ end
386
+
387
+ context "when calling last" do
388
+
389
+ let!(:criteria) do
390
+ Post.asc(:_id).includes(:person)
391
+ end
392
+
393
+ let!(:document) do
394
+ criteria.last
395
+ end
396
+
397
+ it "eager loads the last document" do
398
+ expect_query(0) do
399
+ expect(document.person).to eq(person_two)
400
+ end
401
+ end
402
+
403
+ it "returns the last document" do
404
+ expect(document).to eq(post_two)
405
+ end
406
+ end
407
+ end
408
+
409
+ context "when the criteria is from an embedded relation" do
410
+
411
+ let(:peep) do
412
+ Person.create!
413
+ end
414
+
415
+ let!(:address_one) do
416
+ peep.addresses.create!(street: "rosenthaler")
417
+ end
418
+
419
+ let!(:address_two) do
420
+ peep.addresses.create!(street: "weinmeister")
421
+ end
422
+
423
+ let!(:depeche) do
424
+ Band.create!(name: "Depeche Mode")
425
+ end
426
+
427
+ let!(:tool) do
428
+ Band.create!(name: "Tool")
429
+ end
430
+
431
+ before do
432
+ address_one.band = depeche
433
+ address_two.band = tool
434
+ address_one.save!
435
+ address_two.save!
436
+ end
437
+
438
+ context "when calling first" do
439
+
440
+ let(:criteria) do
441
+ peep.reload.addresses.includes(:band)
442
+ end
443
+
444
+ let(:context) do
445
+ criteria.context
446
+ end
447
+
448
+ let!(:document) do
449
+ criteria.first
450
+ end
451
+
452
+ it "eager loads the first document" do
453
+ expect_query(0) do
454
+ expect(document.band).to eq(depeche)
455
+ end
456
+ end
457
+
458
+ it "returns the document" do
459
+ expect(document).to eq(address_one)
460
+ end
461
+ end
462
+
463
+ context "when calling last" do
464
+
465
+ let(:criteria) do
466
+ peep.reload.addresses.includes(:band)
467
+ end
468
+
469
+ let(:context) do
470
+ criteria.context
471
+ end
472
+
473
+ let!(:document) do
474
+ criteria.last
475
+ end
476
+
477
+ it "eager loads the last document" do
478
+ expect_query(0) do
479
+ expect(document.band).to eq(tool)
480
+ end
481
+ end
482
+
483
+ it "returns the document" do
484
+ expect(document).to eq(address_two)
485
+ end
486
+ end
487
+
488
+ context "when iterating all documents" do
489
+
490
+ let(:criteria) do
491
+ peep.reload.addresses.includes(:band)
492
+ end
493
+
494
+ let(:context) do
495
+ criteria.context
496
+ end
497
+
498
+ let!(:documents) do
499
+ criteria.to_a
500
+ end
501
+
502
+ it "eager loads the first document" do
503
+ expect_query(0) do
504
+ expect(documents.first.band).to eq(depeche)
505
+ end
506
+ end
507
+
508
+ it "eager loads the last document" do
509
+ expect_query(0) do
510
+ expect(documents.last.band).to eq(tool)
511
+ end
512
+ end
513
+
514
+ it "returns the documents" do
515
+ expect(documents).to eq([ address_one, address_two ])
516
+ end
517
+ end
518
+ end
519
+ end
520
+
521
+ context "when providing inclusions to the default scope" do
522
+
523
+ before do
524
+ Person.default_scope(->{ Person.includes(:posts) })
525
+ end
526
+
527
+ after do
528
+ Person.default_scoping = nil
529
+ end
530
+
531
+ let!(:post_one) do
532
+ person.posts.create!(title: "one")
533
+ end
534
+
535
+ let!(:post_two) do
536
+ person.posts.create!(title: "two")
537
+ end
538
+
539
+ context "when the criteria has no options" do
540
+
541
+ let!(:criteria) do
542
+ Person.asc(:age).all
543
+ end
544
+
545
+ let!(:documents) do
546
+ criteria.entries
547
+ end
548
+
549
+ it "returns the correct documents" do
550
+ expect(documents).to eq([ person ])
551
+ end
552
+
553
+ it "eager loads the first document" do
554
+ expect_query(0) do
555
+ expect(documents.first.posts.first).to eq(post_one)
556
+ end
557
+ end
558
+
559
+ it "eager loads the last document" do
560
+ expect_query(0) do
561
+ expect(documents.first.posts.last).to eq(post_two)
562
+ end
563
+ end
564
+
565
+ context "when executing the query twice" do
566
+
567
+ let!(:new_criteria) do
568
+ Person.where(id: person.id)
569
+ end
570
+
571
+ let!(:new_context) do
572
+ new_criteria.context
573
+ end
574
+
575
+ before do
576
+ expect(new_context).to receive(:eager_load).with([person]).once.and_call_original
577
+ end
578
+
579
+ let!(:from_db) do
580
+ new_criteria.first
581
+ end
582
+
583
+ it "does not duplicate documents in the relation" do
584
+ expect(person.posts.size).to eq(2)
585
+ end
586
+ end
587
+ end
588
+
589
+ context "when calling first on the criteria" do
590
+
591
+ let(:criteria) do
592
+ Person.asc(:age).all
593
+ end
594
+
595
+ let!(:from_db) do
596
+ criteria.first
597
+ end
598
+
599
+ it "returns the correct documents" do
600
+ expect(from_db).to eq(person)
601
+ end
602
+
603
+ it "eager loads the first document" do
604
+ expect_query(0) do
605
+ expect(from_db.posts.first).to eq(post_one)
606
+ end
607
+ end
608
+
609
+ it "eager loads the last document" do
610
+ expect_query(0) do
611
+ expect(from_db.posts.last).to eq(post_two)
612
+ end
613
+ end
614
+ end
615
+
616
+ context "when calling last on the criteria" do
617
+
618
+ let(:criteria) do
619
+ Person.asc(:age).all
620
+ end
621
+
622
+ let!(:context) do
623
+ criteria.context
624
+ end
625
+
626
+ before do
627
+ expect(context).to receive(:eager_load).with([person]).once.and_call_original
628
+ end
629
+
630
+ let!(:from_db) do
631
+ criteria.last
632
+ end
633
+
634
+ it "returns the correct documents" do
635
+ expect(from_db).to eq(person)
636
+ end
637
+
638
+ it "eager loads the first document" do
639
+ expect_query(0) do
640
+ expect(from_db.posts.first).to eq(post_one)
641
+ end
642
+ end
643
+
644
+ it "eager loads the last document" do
645
+ expect_query(0) do
646
+ expect(from_db.posts.last).to eq(post_two)
647
+ end
648
+ end
649
+ end
650
+
651
+ context "when the criteria has limiting options" do
652
+
653
+ let!(:person_two) do
654
+ Person.create!
655
+ end
656
+
657
+ let!(:post_three) do
658
+ person_two.posts.create!(title: "three")
659
+ end
660
+
661
+ let!(:criteria) do
662
+ Person.asc(:age).limit(1)
663
+ end
664
+
665
+ let!(:documents) do
666
+ criteria.entries
667
+ end
668
+
669
+ it "returns the correct documents" do
670
+ expect(criteria).to eq([ person ])
671
+ end
672
+
673
+ it "eager loads the first document" do
674
+ expect_query(0) do
675
+ expect(documents.first.posts.first).to eq(post_one)
676
+ end
677
+ end
678
+
679
+ it "eager loads the second document" do
680
+ expect_query(0) do
681
+ expect(documents.first.posts.last).to eq(post_two)
682
+ end
683
+ end
684
+ end
685
+ end
686
+
687
+ context "when including a has and belongs to many" do
688
+
689
+ let!(:preference_one) do
690
+ person.preferences.create!(name: "one")
691
+ end
692
+
693
+ let!(:preference_two) do
694
+ person.preferences.create!(name: "two")
695
+ end
696
+
697
+ context "when one of the related items is deleted" do
698
+
699
+ before do
700
+ person.preferences = [ preference_one, preference_two ]
701
+ preference_two.delete
702
+ end
703
+
704
+ let(:criteria) do
705
+ Person.where(id: person.id).includes(:preferences)
706
+ end
707
+
708
+ it "only loads the existing related items" do
709
+ expect(criteria.entries.first.preferences).to eq([ preference_one ])
710
+ end
711
+ end
712
+
713
+ context "when the criteria has no options" do
714
+
715
+ let!(:criteria) do
716
+ Person.asc(:age).includes(:preferences)
717
+ end
718
+
719
+ let!(:documents) do
720
+ criteria.entries
721
+ end
722
+
723
+ it "returns the correct documents" do
724
+ expect(documents).to eq([ person ])
725
+ end
726
+
727
+ it "eager loads the first document" do
728
+ expect_query(0) do
729
+ expect(documents.first.preferences.first).to eq(preference_one)
730
+ end
731
+ end
732
+
733
+ it "eager loads the last document" do
734
+ expect_query(0) do
735
+ expect(documents.first.preferences.last).to eq(preference_two)
736
+ end
737
+ end
738
+ end
739
+
740
+ context "when calling first on the criteria" do
741
+
742
+ let!(:criteria) do
743
+ Person.asc(:age).includes(:preferences)
744
+ end
745
+
746
+ let!(:from_db) do
747
+ criteria.first
748
+ end
749
+
750
+ it "returns the correct documents" do
751
+ expect(from_db).to eq(person)
752
+ end
753
+
754
+ it "eager loads the first document" do
755
+ expect_query(0) do
756
+ expect(from_db.preferences.first).to eq(preference_one)
757
+ end
758
+ end
759
+
760
+ it "eager loads the last document" do
761
+ expect_query(0) do
762
+ expect(from_db.preferences.last).to eq(preference_two)
763
+ end
764
+ end
765
+ end
766
+
767
+ context "when calling last on the criteria" do
768
+
769
+ let!(:criteria) do
770
+ Person.asc(:age).includes(:preferences)
771
+ end
772
+
773
+ let!(:from_db) do
774
+ criteria.last
775
+ end
776
+
777
+ it "returns the correct documents" do
778
+ expect(from_db).to eq(person)
779
+ end
780
+
781
+ it "eager loads the first document" do
782
+ expect_query(0) do
783
+ expect(from_db.preferences.first).to eq(preference_one)
784
+ end
785
+ end
786
+
787
+ it "eager loads the last document" do
788
+ expect_query(0) do
789
+ expect(from_db.preferences.last).to eq(preference_two)
790
+ end
791
+ end
792
+ end
793
+ end
794
+
795
+ context "when including a has many" do
796
+
797
+ let!(:post_one) do
798
+ person.posts.create!(title: "one")
799
+ end
800
+
801
+ let!(:post_two) do
802
+ person.posts.create!(title: "two")
803
+ end
804
+
805
+ context "when the criteria has no options" do
806
+
807
+ let!(:criteria) do
808
+ Person.asc(:age).includes(:posts)
809
+ end
810
+
811
+ let!(:documents) do
812
+ criteria.entries
813
+ end
814
+
815
+ it "returns the correct documents" do
816
+ expect(documents).to eq([ person ])
817
+ end
818
+
819
+ it "eager loads the first document" do
820
+ expect_query(0) do
821
+ expect(documents.first.posts.first).to eq(post_one)
822
+ end
823
+ end
824
+
825
+ it "eager loads the last document" do
826
+ expect_query(0) do
827
+ expect(documents.first.posts.last).to eq(post_two)
828
+ end
829
+ end
830
+ end
831
+
832
+ context "when calling first on the criteria" do
833
+
834
+ let!(:criteria) do
835
+ Person.asc(:age).includes(:posts)
836
+ end
837
+
838
+ let!(:from_db) do
839
+ criteria.first
840
+ end
841
+
842
+ it "returns the correct documents" do
843
+ expect(from_db).to eq(person)
844
+ end
845
+
846
+ context "when subsequently getting all documents" do
847
+
848
+ let!(:documents) do
849
+ criteria.entries
850
+ end
851
+
852
+ it "returns the correct documents" do
853
+ expect(documents).to eq([ person ])
854
+ end
855
+ end
856
+ end
857
+
858
+ context "when calling last on the criteria" do
859
+
860
+ let!(:criteria) do
861
+ Person.asc(:age).includes(:posts)
862
+ end
863
+
864
+ let!(:from_db) do
865
+ criteria.last
866
+ end
867
+
868
+ it "returns the correct documents" do
869
+ expect(from_db).to eq(person)
870
+ end
871
+
872
+ context "when subsequently getting all documents" do
873
+
874
+ let!(:documents) do
875
+ criteria.entries
876
+ end
877
+
878
+ it "returns the correct documents" do
879
+ expect(documents).to eq([ person ])
880
+ end
881
+ end
882
+ end
883
+
884
+ context "when the criteria has limiting options" do
885
+
886
+ let!(:person_two) do
887
+ Person.create!
888
+ end
889
+
890
+ let!(:post_three) do
891
+ person_two.posts.create!(title: "three")
892
+ end
893
+
894
+ let!(:criteria) do
895
+ Person.includes(:posts).asc(:age).limit(1)
896
+ end
897
+
898
+ let(:context) do
899
+ criteria.context
900
+ end
901
+
902
+ before do
903
+ expect(context).to receive(:eager_load).with([ person ]).once.and_call_original
904
+ end
905
+
906
+ let!(:documents) do
907
+ criteria.entries
908
+ end
909
+
910
+ it "returns the correct documents" do
911
+ expect(documents).to eq([ person ])
912
+ end
913
+ end
914
+ end
915
+
916
+ context "when including a has one" do
917
+
918
+ let!(:game_one) do
919
+ person.create_game(name: "one")
920
+ end
921
+
922
+ let!(:game_two) do
923
+ person.create_game(name: "two")
924
+ end
925
+
926
+ context "when the criteria has no options" do
927
+
928
+ let!(:criteria) do
929
+ Person.asc(:age).includes(:game)
930
+ end
931
+
932
+ let(:context) do
933
+ criteria.context
934
+ end
935
+
936
+ before do
937
+ expect(context).to receive(:eager_load).with([ person ]).once.and_call_original
938
+ end
939
+
940
+ let!(:documents) do
941
+ criteria.entries
942
+ end
943
+
944
+ it "returns the correct documents" do
945
+ expect(documents).to eq([ person ])
946
+ end
947
+ end
948
+
949
+ context "when the criteria has limiting options" do
950
+
951
+ let!(:person_two) do
952
+ Person.create!(age: 2)
953
+ end
954
+
955
+ let!(:game_three) do
956
+ person_two.create_game(name: "Skyrim")
957
+ end
958
+
959
+ let!(:criteria) do
960
+ Person.where(id: person.id).includes(:game).asc(:age).limit(1)
961
+ end
962
+
963
+ let(:context) do
964
+ criteria.context
965
+ end
966
+
967
+ before do
968
+ expect(context).to receive(:eager_load).with([ person ]).once.and_call_original
969
+ end
970
+
971
+ let!(:documents) do
972
+ criteria.entries
973
+ end
974
+
975
+ it "returns the correct documents" do
976
+ expect(documents).to eq([ person ])
977
+ end
978
+ end
979
+ end
980
+
981
+ context "when including a belongs to" do
982
+
983
+ let(:person_two) do
984
+ Person.create!(age: 2)
985
+ end
986
+
987
+ let!(:game_one) do
988
+ person.create_game(name: "one")
989
+ end
990
+
991
+ let!(:game_two) do
992
+ person_two.create_game(name: "two")
993
+ end
994
+
995
+ context "when providing no options" do
996
+
997
+ let!(:criteria) do
998
+ Game.includes(:person)
999
+ end
1000
+
1001
+ let(:context) do
1002
+ criteria.context
1003
+ end
1004
+
1005
+ before do
1006
+ expect(context).to receive(:preload).twice.and_call_original
1007
+ end
1008
+
1009
+ let!(:documents) do
1010
+ criteria.entries
1011
+ end
1012
+
1013
+ it "returns the correct documents" do
1014
+ expect(criteria).to eq([ game_one, game_two ])
1015
+ end
1016
+ end
1017
+
1018
+ context "when the criteria has limiting options" do
1019
+
1020
+ let!(:criteria) do
1021
+ Game.where(id: game_one.id).includes(:person).asc(:_id).limit(1)
1022
+ end
1023
+
1024
+ let(:context) do
1025
+ criteria.context
1026
+ end
1027
+
1028
+ before do
1029
+ expect(context).to receive(:eager_load).with([ game_one ]).once.and_call_original
1030
+ end
1031
+
1032
+ let!(:documents) do
1033
+ criteria.entries
1034
+ end
1035
+
1036
+ it "returns the correct documents" do
1037
+ expect(documents).to eq([ game_one ])
1038
+ end
1039
+ end
1040
+ end
1041
+
1042
+ context "when including multiples in the same criteria" do
1043
+
1044
+ let!(:post_one) do
1045
+ person.posts.create!(title: "one")
1046
+ end
1047
+
1048
+ let!(:post_two) do
1049
+ person.posts.create!(title: "two")
1050
+ end
1051
+
1052
+ let!(:game_one) do
1053
+ person.create_game(name: "one")
1054
+ end
1055
+
1056
+ let!(:game_two) do
1057
+ person.create_game(name: "two")
1058
+ end
1059
+
1060
+ let!(:criteria) do
1061
+ Person.includes(:posts, :game).asc(:age)
1062
+ end
1063
+
1064
+ let(:context) do
1065
+ criteria.context
1066
+ end
1067
+
1068
+ before do
1069
+ expect(context).to receive(:preload).twice.and_call_original
1070
+ end
1071
+
1072
+ let!(:documents) do
1073
+ criteria.entries
1074
+ end
1075
+
1076
+ it "returns the correct documents" do
1077
+ expect(criteria).to eq([ person ])
1078
+ end
1079
+ end
1080
+
1081
+ context "when including nested referenced associations" do
1082
+
1083
+ context "when using a has_one association" do
1084
+ before(:all) do
1085
+ class A
1086
+ include Mongoid::Document
1087
+ has_one :b
1088
+ end
1089
+
1090
+ class B
1091
+ include Mongoid::Document
1092
+ belongs_to :a
1093
+ has_one :c
1094
+ end
1095
+
1096
+ class C
1097
+ include Mongoid::Document
1098
+ belongs_to :b
1099
+ has_one :d
1100
+ end
1101
+
1102
+ class D
1103
+ include Mongoid::Document
1104
+ belongs_to :c
1105
+ end
1106
+ end
1107
+
1108
+ after(:all) do
1109
+ Object.send(:remove_const, :A)
1110
+ Object.send(:remove_const, :B)
1111
+ Object.send(:remove_const, :C)
1112
+ Object.send(:remove_const, :D)
1113
+ end
1114
+
1115
+ let!(:a) do
1116
+ A.create!
1117
+ end
1118
+
1119
+ let!(:b) do
1120
+ B.create!
1121
+ end
1122
+
1123
+ let!(:c) do
1124
+ C.create!
1125
+ end
1126
+
1127
+ let!(:d) do
1128
+ D.create!
1129
+ end
1130
+
1131
+ before do
1132
+ c.d = d
1133
+ b.c = c
1134
+ a.b = b
1135
+ end
1136
+
1137
+ context "when including the belongs_to assocation" do
1138
+ let!(:result) do
1139
+ C.includes(b: :a).first
1140
+ end
1141
+
1142
+ it "finds the right document" do
1143
+ expect(result).to eq(c)
1144
+ expect(result.b).to eq(c.b)
1145
+ expect(result.b.a).to eq(c.b.a)
1146
+ end
1147
+
1148
+ it "does not execute a query" do
1149
+ expect_no_queries do
1150
+ result.b.a
1151
+ end
1152
+ end
1153
+ end
1154
+
1155
+ context "when including a doubly-nested belongs_to assocation" do
1156
+ let!(:result) do
1157
+ D.includes(c: { b: :a }).first
1158
+ end
1159
+
1160
+ it "finds the right document" do
1161
+ expect(result).to eq(d)
1162
+ expect(result.c).to eq(d.c)
1163
+ expect(result.c.b).to eq(d.c.b)
1164
+ expect(result.c.b.a).to eq(d.c.b.a)
1165
+ end
1166
+
1167
+ it "does not execute a query" do
1168
+ expect_no_queries do
1169
+ result.c.b.a
1170
+ end
1171
+ end
1172
+ end
1173
+
1174
+ context "when including the has_many assocation" do
1175
+ let!(:result) do
1176
+ A.includes(b: :c).first
1177
+ end
1178
+
1179
+ it "finds the right document" do
1180
+ expect(result).to eq(a)
1181
+ expect(result.b).to eq(a.b)
1182
+ expect(result.b.c).to eq(a.b.c)
1183
+ end
1184
+
1185
+ it "does not executes a query" do
1186
+ expect_no_queries do
1187
+ result.b.c
1188
+ end
1189
+ end
1190
+ end
1191
+
1192
+ context "when including a doubly-nested has_many assocation" do
1193
+ let!(:result) do
1194
+ A.includes(b: { c: :d }).first
1195
+ end
1196
+
1197
+ it "finds the right document" do
1198
+ expect(result).to eq(a)
1199
+ expect(result.b).to eq(a.b)
1200
+ expect(result.b.c).to eq(a.b.c)
1201
+ expect(result.b.c.d).to eq(a.b.c.d)
1202
+ end
1203
+
1204
+ it "does not execute a query" do
1205
+ expect_no_queries do
1206
+ result.b.c.d
1207
+ end
1208
+ end
1209
+ end
1210
+
1211
+ context "when there are multiple documents" do
1212
+ let!(:as) do
1213
+ res = 9.times.map do |i|
1214
+ A.create!.tap do |a|
1215
+ a.b = B.create!.tap do |b|
1216
+ b.c = C.create!
1217
+ end
1218
+ end
1219
+ end
1220
+ [a, *res]
1221
+ end
1222
+
1223
+ let!(:results) do
1224
+ A.includes(b: :c).entries.sort
1225
+ end
1226
+
1227
+ it "finds the right document" do
1228
+ as.length.times do |i|
1229
+ expect(as[i]).to eq(results[i])
1230
+ expect(as[i].b).to eq(results[i].b)
1231
+ expect(as[i].b.c).to eq(results[i].b.c)
1232
+ end
1233
+ end
1234
+
1235
+ it "does not execute a query" do
1236
+ expect_no_queries do
1237
+ results.each do |a|
1238
+ a.b.c
1239
+ end
1240
+ end
1241
+ end
1242
+ end
1243
+
1244
+ context "when there are multiple associations" do
1245
+ before(:all) do
1246
+ class A
1247
+ has_one :c
1248
+ end
1249
+
1250
+ class C
1251
+ belongs_to :a
1252
+ end
1253
+ end
1254
+
1255
+ let(:c2) { C.create! }
1256
+ let(:d2) { D.create! }
1257
+
1258
+ before do
1259
+ a.c = c2
1260
+ a.c.d = d2
1261
+ end
1262
+
1263
+ let!(:results) do
1264
+ A.includes(b: { c: :d }, c: :d).first
1265
+ end
1266
+
1267
+ it "finds the right document" do
1268
+ expect(results).to eq(a)
1269
+ expect(results.b).to eq(a.b)
1270
+ expect(results.b.c).to eq(a.b.c)
1271
+ expect(results.b.c.d).to eq(a.b.c.d)
1272
+ expect(results.c).to eq(a.c)
1273
+ expect(results.c.d).to eq(a.c.d)
1274
+ end
1275
+
1276
+ it "does not execute a query" do
1277
+ expect_no_queries do
1278
+ results.c.d
1279
+ results.b.c.d
1280
+ end
1281
+ end
1282
+ end
1283
+ end
1284
+ end
1285
+
1286
+ context "when using a has_many association" do
1287
+
1288
+ let!(:user) do
1289
+ IncUser.create!(posts: posts, comments: user_comments)
1290
+ end
1291
+
1292
+ let!(:posts) do
1293
+ [ IncPost.create!(comments: post_comments) ]
1294
+ end
1295
+
1296
+ let!(:user_comments) do
1297
+ 2.times.map{ IncComment.create! }
1298
+ end
1299
+
1300
+ let!(:post_comments) do
1301
+ 2.times.map{ IncComment.create! }
1302
+ end
1303
+
1304
+ context "when including the same class twice" do
1305
+ let!(:results) do
1306
+ IncPost.includes({ user: :comments }, :comments).entries.sort
1307
+ end
1308
+
1309
+ it "finds the right documents" do
1310
+ posts.length.times do |i|
1311
+ expect(posts[i]).to eq(results[i])
1312
+ expect(posts[i].user).to eq(results[i].user)
1313
+ expect(posts[i].user.comments).to eq(results[i].user.comments)
1314
+ expect(posts[i].comments).to eq(results[i].comments)
1315
+ end
1316
+ end
1317
+
1318
+ it "does not execute a query" do
1319
+ expect_no_queries do
1320
+ results.each do |res|
1321
+ res.user
1322
+ res.user.comments.to_a
1323
+ res.comments.to_a
1324
+ end
1325
+ end
1326
+ end
1327
+ end
1328
+
1329
+ context "when the association chain has a class name twice" do
1330
+ let!(:thread) { IncThread.create!(comments: user_comments) }
1331
+
1332
+ let!(:result) do
1333
+ IncThread.includes(comments: { user: { posts: :comments } }).first
1334
+ end
1335
+
1336
+ it "finds the right document" do
1337
+ expect(result).to eq(thread)
1338
+ result.comments.length.times do |i|
1339
+ c1 = result.comments[i]
1340
+ c2 = thread.comments[i]
1341
+ expect(c1).to eq(c2)
1342
+ expect(c1.user).to eq(c2.user)
1343
+ c1.user.posts.length.times do |i|
1344
+ p1 = c1.user.posts[i]
1345
+ p2 = c2.user.posts[i]
1346
+
1347
+ expect(p1).to eq(p2)
1348
+ expect(p1.comments).to eq(p2.comments)
1349
+ end
1350
+ end
1351
+ end
1352
+
1353
+ it "does not execute a query" do
1354
+ expect_no_queries do
1355
+ result.comments.each do |comment|
1356
+ comment.user.posts.each do |post|
1357
+ post.comments.to_a
1358
+ end
1359
+ end
1360
+ end
1361
+ end
1362
+ end
1363
+ end
1364
+ end
1365
+
1366
+ describe "#inclusions" do
1367
+
1368
+ let(:criteria) do
1369
+ Band.includes(:records)
1370
+ end
1371
+
1372
+ let(:association) do
1373
+ Band.relations["records"]
1374
+ end
1375
+
1376
+ it "returns the inclusions" do
1377
+ expect(criteria.inclusions).to eq([ association ])
1378
+ end
1379
+ end
1380
+
1381
+ describe "#inclusions=" do
1382
+
1383
+ let(:criteria) do
1384
+ Band.all
1385
+ end
1386
+
1387
+ let(:association) do
1388
+ Band.relations["records"]
1389
+ end
1390
+
1391
+ before do
1392
+ criteria.inclusions = [ association ]
1393
+ end
1394
+
1395
+ it "sets the inclusions" do
1396
+ expect(criteria.inclusions).to eq([ association ])
1397
+ end
1398
+ end
1399
+
1400
+ context "When multiple associations reference the same class" do
1401
+ before do
1402
+ IncBlog.create(
1403
+ posts: [
1404
+ IncBlogPost.create(author: IncAuthor.create),
1405
+ IncBlogPost.create(author: IncAuthor.create),
1406
+ IncBlogPost.create(author: IncAuthor.create),
1407
+ ],
1408
+ highlighted_post: IncBlogPost.create(author: IncAuthor.create)
1409
+ )
1410
+ end
1411
+
1412
+ let!(:result) do
1413
+ IncBlog.includes(:posts, highlighted_post: :author).first
1414
+ end
1415
+
1416
+ it "does not execute a query" do
1417
+ expect_no_queries do
1418
+ result.posts.to_a
1419
+ result.highlighted_post
1420
+ end
1421
+ end
1422
+
1423
+ it "executes a query for the non-retrieved elements" do
1424
+ expect_query(3) do
1425
+ result.posts.each do |post|
1426
+ post.author
1427
+ end
1428
+ end
1429
+ end
1430
+ end
1431
+
1432
+ context "When multiple parent_inclusions for the same association" do
1433
+ before do
1434
+ IncBlog.create(
1435
+ posts: [
1436
+ IncBlogPost.create(author: IncAuthor.create),
1437
+ IncBlogPost.create(author: IncAuthor.create),
1438
+ IncBlogPost.create(author: IncAuthor.create),
1439
+ ],
1440
+ highlighted_post: IncBlogPost.create(author: IncAuthor.create),
1441
+ pinned_post: IncBlogPost.create(author: IncAuthor.create)
1442
+ )
1443
+ end
1444
+
1445
+ let!(:result) do
1446
+ IncBlog.includes(:posts, highlighted_post: :author, pinned_post: :author).first
1447
+ end
1448
+
1449
+ it "does not execute a query" do
1450
+ expect_no_queries do
1451
+ result.posts.to_a
1452
+ result.highlighted_post
1453
+ result.pinned_post
1454
+ end
1455
+ end
1456
+
1457
+ it "executes a query for the non-retrieved elements" do
1458
+ expect_query(3) do
1459
+ result.posts.each do |post|
1460
+ post.author
1461
+ end
1462
+ end
1463
+ end
1464
+
1465
+ context "when including an association and using each twice on a criteria" do
1466
+
1467
+ let(:criteria) { IncPost.all.includes(:person) }
1468
+
1469
+ before do
1470
+ p = IncPerson.create!(name: "name")
1471
+ 4.times { IncPost.create!(person: p)}
1472
+ criteria
1473
+ expect_query(2) do
1474
+ criteria.each(&:person)
1475
+ end
1476
+ end
1477
+
1478
+ # The reason we are checking for two operations here is:
1479
+ # - The first operation gets all of the posts
1480
+ # - The second operation gets the person from the first post
1481
+ # Now, all subsequent posts should use the eager loaded person when
1482
+ # trying to retrieve their person.
1483
+ # MONGOID-3942 reported that after iterating the criteria a second time,
1484
+ # the posts would not get the eager loaded person.
1485
+ it "eager loads the criteria" do
1486
+ expect_query(2) do
1487
+ criteria.each(&:person)
1488
+ end
1489
+ end
1490
+ end
1491
+ end
1492
+ end