mongoid 2.0.2 → 2.1.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 (240) hide show
  1. data/README.rdoc +3 -1
  2. data/Rakefile +3 -2
  3. data/lib/config/locales/bg.yml +6 -0
  4. data/lib/config/locales/de.yml +6 -0
  5. data/lib/config/locales/en-GB.yml +48 -0
  6. data/lib/config/locales/en.yml +6 -3
  7. data/lib/config/locales/es.yml +6 -0
  8. data/lib/config/locales/fr.yml +6 -0
  9. data/lib/config/locales/hi.yml +39 -0
  10. data/lib/config/locales/hu.yml +13 -7
  11. data/lib/config/locales/id.yml +3 -0
  12. data/lib/config/locales/it.yml +7 -1
  13. data/lib/config/locales/ja.yml +4 -1
  14. data/lib/config/locales/kr.yml +9 -34
  15. data/lib/config/locales/nl.yml +6 -0
  16. data/lib/config/locales/pl.yml +6 -0
  17. data/lib/config/locales/pt-BR.yml +6 -0
  18. data/lib/config/locales/pt.yml +6 -0
  19. data/lib/config/locales/ro.yml +6 -0
  20. data/lib/config/locales/ru.yml +6 -0
  21. data/lib/config/locales/sv.yml +6 -0
  22. data/lib/config/locales/vi.yml +3 -0
  23. data/lib/config/locales/zh-CN.yml +6 -0
  24. data/lib/mongoid.rb +51 -45
  25. data/lib/mongoid/atomic.rb +145 -0
  26. data/lib/mongoid/atomic/modifiers.rb +109 -0
  27. data/lib/mongoid/atomic/paths.rb +3 -0
  28. data/lib/mongoid/atomic/paths/embedded.rb +43 -0
  29. data/lib/mongoid/atomic/paths/embedded/many.rb +44 -0
  30. data/lib/mongoid/atomic/paths/embedded/one.rb +43 -0
  31. data/lib/mongoid/atomic/paths/root.rb +40 -0
  32. data/lib/mongoid/attributes.rb +12 -23
  33. data/lib/mongoid/attributes/processing.rb +5 -5
  34. data/lib/mongoid/callbacks.rb +2 -0
  35. data/lib/mongoid/collection.rb +12 -59
  36. data/lib/mongoid/collections.rb +23 -20
  37. data/lib/mongoid/collections/master.rb +6 -4
  38. data/lib/mongoid/collections/operations.rb +1 -0
  39. data/lib/mongoid/collections/retry.rb +7 -0
  40. data/lib/mongoid/components.rb +2 -2
  41. data/lib/mongoid/config.rb +42 -55
  42. data/lib/mongoid/config/database.rb +6 -2
  43. data/lib/mongoid/config/replset_database.rb +7 -3
  44. data/lib/mongoid/contexts.rb +9 -3
  45. data/lib/mongoid/contexts/enumerable.rb +7 -3
  46. data/lib/mongoid/contexts/mongo.rb +139 -101
  47. data/lib/mongoid/criteria.rb +86 -69
  48. data/lib/mongoid/criterion/complex.rb +32 -5
  49. data/lib/mongoid/criterion/inclusion.rb +4 -2
  50. data/lib/mongoid/criterion/optional.rb +111 -86
  51. data/lib/mongoid/criterion/selector.rb +8 -4
  52. data/lib/mongoid/cursor.rb +27 -27
  53. data/lib/mongoid/dirty.rb +54 -214
  54. data/lib/mongoid/document.rb +37 -39
  55. data/lib/mongoid/errors/document_not_found.rb +3 -4
  56. data/lib/mongoid/errors/invalid_collection.rb +2 -3
  57. data/lib/mongoid/errors/invalid_database.rb +2 -3
  58. data/lib/mongoid/errors/invalid_field.rb +2 -3
  59. data/lib/mongoid/errors/invalid_options.rb +19 -7
  60. data/lib/mongoid/errors/invalid_type.rb +2 -3
  61. data/lib/mongoid/errors/mongoid_error.rb +5 -6
  62. data/lib/mongoid/errors/too_many_nested_attribute_records.rb +2 -3
  63. data/lib/mongoid/errors/unsupported_version.rb +2 -3
  64. data/lib/mongoid/errors/validations.rb +2 -3
  65. data/lib/mongoid/extensions.rb +8 -62
  66. data/lib/mongoid/extensions/array/deletion.rb +29 -0
  67. data/lib/mongoid/extensions/false_class/equality.rb +14 -1
  68. data/lib/mongoid/extensions/hash/criteria_helpers.rb +21 -10
  69. data/lib/mongoid/extensions/hash/scoping.rb +14 -1
  70. data/lib/mongoid/extensions/nil/collectionization.rb +12 -1
  71. data/lib/mongoid/extensions/object/reflections.rb +33 -2
  72. data/lib/mongoid/extensions/object_id/conversions.rb +2 -36
  73. data/lib/mongoid/extensions/proc/scoping.rb +14 -1
  74. data/lib/mongoid/extensions/string/conversions.rb +4 -16
  75. data/lib/mongoid/extensions/string/inflections.rb +35 -14
  76. data/lib/mongoid/extensions/symbol/inflections.rb +38 -12
  77. data/lib/mongoid/extensions/true_class/equality.rb +14 -1
  78. data/lib/mongoid/extras.rb +11 -30
  79. data/lib/mongoid/factory.rb +1 -1
  80. data/lib/mongoid/fields.rb +121 -29
  81. data/lib/mongoid/fields/mappings.rb +36 -0
  82. data/lib/mongoid/fields/serializable.rb +131 -0
  83. data/lib/mongoid/fields/serializable/array.rb +64 -0
  84. data/lib/mongoid/fields/serializable/big_decimal.rb +42 -0
  85. data/lib/mongoid/fields/serializable/bignum.rb +10 -0
  86. data/lib/mongoid/fields/serializable/binary.rb +11 -0
  87. data/lib/mongoid/fields/serializable/boolean.rb +44 -0
  88. data/lib/mongoid/fields/serializable/date.rb +51 -0
  89. data/lib/mongoid/fields/serializable/date_time.rb +28 -0
  90. data/lib/mongoid/fields/serializable/fixnum.rb +10 -0
  91. data/lib/mongoid/fields/serializable/float.rb +33 -0
  92. data/lib/mongoid/fields/serializable/foreign_keys/array.rb +56 -0
  93. data/lib/mongoid/fields/serializable/foreign_keys/object.rb +43 -0
  94. data/lib/mongoid/fields/serializable/hash.rb +25 -0
  95. data/lib/mongoid/fields/serializable/integer.rb +33 -0
  96. data/lib/mongoid/fields/serializable/object.rb +11 -0
  97. data/lib/mongoid/fields/serializable/object_id.rb +32 -0
  98. data/lib/mongoid/fields/serializable/range.rb +42 -0
  99. data/lib/mongoid/fields/serializable/set.rb +42 -0
  100. data/lib/mongoid/fields/serializable/string.rb +28 -0
  101. data/lib/mongoid/fields/serializable/symbol.rb +28 -0
  102. data/lib/mongoid/fields/serializable/time.rb +12 -0
  103. data/lib/mongoid/fields/serializable/time_with_zone.rb +12 -0
  104. data/lib/mongoid/fields/serializable/timekeeping.rb +102 -0
  105. data/lib/mongoid/finders.rb +61 -37
  106. data/lib/mongoid/hierarchy.rb +43 -8
  107. data/lib/mongoid/identity_map.rb +106 -0
  108. data/lib/mongoid/indexes.rb +17 -1
  109. data/lib/mongoid/javascript.rb +2 -3
  110. data/lib/mongoid/keys.rb +10 -21
  111. data/lib/mongoid/logger.rb +22 -1
  112. data/lib/mongoid/matchers/all.rb +10 -0
  113. data/lib/mongoid/matchers/default.rb +1 -1
  114. data/lib/mongoid/matchers/exists.rb +10 -0
  115. data/lib/mongoid/matchers/gt.rb +10 -0
  116. data/lib/mongoid/matchers/gte.rb +10 -0
  117. data/lib/mongoid/matchers/in.rb +10 -0
  118. data/lib/mongoid/matchers/lt.rb +10 -0
  119. data/lib/mongoid/matchers/lte.rb +10 -0
  120. data/lib/mongoid/matchers/ne.rb +10 -0
  121. data/lib/mongoid/matchers/nin.rb +10 -0
  122. data/lib/mongoid/matchers/or.rb +7 -4
  123. data/lib/mongoid/matchers/size.rb +10 -0
  124. data/lib/mongoid/multi_database.rb +26 -6
  125. data/lib/mongoid/multi_parameter_attributes.rb +40 -17
  126. data/lib/mongoid/named_scope.rb +1 -2
  127. data/lib/mongoid/nested_attributes.rb +4 -1
  128. data/lib/mongoid/observer.rb +108 -5
  129. data/lib/mongoid/paranoia.rb +26 -26
  130. data/lib/mongoid/persistence.rb +15 -21
  131. data/lib/mongoid/persistence/atomic.rb +135 -0
  132. data/lib/mongoid/persistence/atomic/add_to_set.rb +11 -8
  133. data/lib/mongoid/persistence/atomic/bit.rb +37 -0
  134. data/lib/mongoid/persistence/atomic/inc.rb +9 -6
  135. data/lib/mongoid/persistence/atomic/operation.rb +48 -7
  136. data/lib/mongoid/persistence/atomic/pop.rb +34 -0
  137. data/lib/mongoid/persistence/atomic/pull.rb +34 -0
  138. data/lib/mongoid/persistence/atomic/pull_all.rb +10 -9
  139. data/lib/mongoid/persistence/atomic/push.rb +8 -5
  140. data/lib/mongoid/persistence/atomic/push_all.rb +31 -0
  141. data/lib/mongoid/persistence/atomic/rename.rb +31 -0
  142. data/lib/mongoid/persistence/atomic/set.rb +30 -0
  143. data/lib/mongoid/persistence/atomic/unset.rb +28 -0
  144. data/lib/mongoid/persistence/deletion.rb +32 -0
  145. data/lib/mongoid/persistence/insertion.rb +41 -0
  146. data/lib/mongoid/persistence/modification.rb +37 -0
  147. data/lib/mongoid/persistence/operations.rb +214 -0
  148. data/lib/mongoid/persistence/operations/embedded/insert.rb +42 -0
  149. data/lib/mongoid/persistence/operations/embedded/remove.rb +40 -0
  150. data/lib/mongoid/persistence/operations/insert.rb +34 -0
  151. data/lib/mongoid/persistence/operations/remove.rb +33 -0
  152. data/lib/mongoid/persistence/operations/update.rb +53 -0
  153. data/lib/mongoid/railtie.rb +21 -33
  154. data/lib/mongoid/railties/database.rake +12 -12
  155. data/lib/mongoid/relations.rb +9 -5
  156. data/lib/mongoid/relations/accessors.rb +15 -36
  157. data/lib/mongoid/relations/auto_save.rb +2 -2
  158. data/lib/mongoid/relations/binding.rb +28 -1
  159. data/lib/mongoid/relations/bindings/embedded/in.rb +17 -30
  160. data/lib/mongoid/relations/bindings/embedded/many.rb +16 -21
  161. data/lib/mongoid/relations/bindings/embedded/one.rb +11 -16
  162. data/lib/mongoid/relations/bindings/referenced/in.rb +31 -32
  163. data/lib/mongoid/relations/bindings/referenced/many.rb +19 -61
  164. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +15 -63
  165. data/lib/mongoid/relations/bindings/referenced/one.rb +18 -26
  166. data/lib/mongoid/relations/builder.rb +4 -2
  167. data/lib/mongoid/relations/builders.rb +21 -2
  168. data/lib/mongoid/relations/builders/embedded/in.rb +5 -1
  169. data/lib/mongoid/relations/builders/embedded/many.rb +12 -4
  170. data/lib/mongoid/relations/builders/embedded/one.rb +5 -1
  171. data/lib/mongoid/relations/builders/nested_attributes/many.rb +2 -2
  172. data/lib/mongoid/relations/builders/nested_attributes/one.rb +1 -1
  173. data/lib/mongoid/relations/builders/referenced/in.rb +2 -5
  174. data/lib/mongoid/relations/builders/referenced/many.rb +2 -3
  175. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +14 -5
  176. data/lib/mongoid/relations/builders/referenced/one.rb +2 -3
  177. data/lib/mongoid/relations/embedded/atomic.rb +2 -2
  178. data/lib/mongoid/relations/embedded/in.rb +72 -41
  179. data/lib/mongoid/relations/embedded/many.rb +116 -120
  180. data/lib/mongoid/relations/embedded/one.rb +59 -41
  181. data/lib/mongoid/relations/embedded/sort.rb +31 -0
  182. data/lib/mongoid/relations/macros.rb +28 -24
  183. data/lib/mongoid/relations/many.rb +10 -103
  184. data/lib/mongoid/relations/metadata.rb +335 -38
  185. data/lib/mongoid/relations/one.rb +7 -32
  186. data/lib/mongoid/relations/options.rb +47 -0
  187. data/lib/mongoid/relations/proxy.rb +29 -28
  188. data/lib/mongoid/relations/referenced/batch.rb +2 -3
  189. data/lib/mongoid/relations/referenced/in.rb +66 -53
  190. data/lib/mongoid/relations/referenced/many.rb +216 -143
  191. data/lib/mongoid/relations/referenced/many_to_many.rb +132 -163
  192. data/lib/mongoid/relations/referenced/one.rb +76 -58
  193. data/lib/mongoid/relations/synchronization.rb +113 -0
  194. data/lib/mongoid/relations/targets.rb +2 -0
  195. data/lib/mongoid/relations/targets/enumerable.rb +329 -0
  196. data/lib/mongoid/safety.rb +24 -156
  197. data/lib/mongoid/serialization.rb +21 -0
  198. data/lib/mongoid/state.rb +34 -0
  199. data/lib/mongoid/threaded.rb +175 -0
  200. data/lib/mongoid/timestamps/updated.rb +1 -1
  201. data/lib/mongoid/validations.rb +3 -7
  202. data/lib/mongoid/version.rb +1 -1
  203. data/lib/mongoid/versioning.rb +61 -7
  204. data/lib/rack/mongoid.rb +2 -0
  205. data/lib/rack/mongoid/middleware/identity_map.rb +38 -0
  206. data/lib/rails/generators/mongoid/model/model_generator.rb +1 -1
  207. data/lib/rails/generators/mongoid/model/templates/{model.rb → model.rb.tt} +0 -0
  208. data/lib/rails/generators/mongoid/observer/observer_generator.rb +1 -1
  209. data/lib/rails/generators/mongoid/observer/templates/{observer.rb → observer.rb.tt} +0 -0
  210. data/lib/rails/mongoid.rb +17 -17
  211. metadata +136 -102
  212. data/lib/mongoid/atomicity.rb +0 -111
  213. data/lib/mongoid/collections/cyclic_iterator.rb +0 -34
  214. data/lib/mongoid/collections/slaves.rb +0 -61
  215. data/lib/mongoid/extensions/array/conversions.rb +0 -23
  216. data/lib/mongoid/extensions/array/parentization.rb +0 -13
  217. data/lib/mongoid/extensions/big_decimal/conversions.rb +0 -19
  218. data/lib/mongoid/extensions/binary/conversions.rb +0 -17
  219. data/lib/mongoid/extensions/boolean/conversions.rb +0 -27
  220. data/lib/mongoid/extensions/date/conversions.rb +0 -25
  221. data/lib/mongoid/extensions/datetime/conversions.rb +0 -12
  222. data/lib/mongoid/extensions/float/conversions.rb +0 -20
  223. data/lib/mongoid/extensions/hash/conversions.rb +0 -19
  224. data/lib/mongoid/extensions/integer/conversions.rb +0 -20
  225. data/lib/mongoid/extensions/object/conversions.rb +0 -25
  226. data/lib/mongoid/extensions/range/conversions.rb +0 -25
  227. data/lib/mongoid/extensions/set/conversions.rb +0 -20
  228. data/lib/mongoid/extensions/symbol/conversions.rb +0 -21
  229. data/lib/mongoid/extensions/time_conversions.rb +0 -38
  230. data/lib/mongoid/field.rb +0 -162
  231. data/lib/mongoid/paths.rb +0 -61
  232. data/lib/mongoid/persistence/command.rb +0 -71
  233. data/lib/mongoid/persistence/insert.rb +0 -53
  234. data/lib/mongoid/persistence/insert_embedded.rb +0 -43
  235. data/lib/mongoid/persistence/remove.rb +0 -44
  236. data/lib/mongoid/persistence/remove_all.rb +0 -40
  237. data/lib/mongoid/persistence/remove_embedded.rb +0 -48
  238. data/lib/mongoid/persistence/update.rb +0 -77
  239. data/lib/mongoid/safe.rb +0 -23
  240. data/lib/mongoid/validations/referenced.rb +0 -58
@@ -5,46 +5,65 @@ module Mongoid # :nodoc:
5
5
 
6
6
  # This class defines the behaviour for all relations that are a
7
7
  # many-to-many between documents in different collections.
8
- class ManyToMany < Referenced::Many
8
+ class ManyToMany < Many
9
9
 
10
10
  # Appends a document or array of documents to the relation. Will set
11
11
  # the parent and update the index in the process.
12
12
  #
13
13
  # @example Append a document.
14
- # person.addresses << address
14
+ # person.posts << post
15
15
  #
16
16
  # @example Push a document.
17
- # person.addresses.push(address)
17
+ # person.posts.push(post)
18
18
  #
19
19
  # @example Concat with other documents.
20
- # person.addresses.concat([ address_one, address_two ])
20
+ # person.posts.concat([ post_one, post_two ])
21
21
  #
22
22
  # @param [ Document, Array<Document> ] *args Any number of documents.
23
+ #
24
+ # @return [ Array<Document> ] The loaded docs.
25
+ #
26
+ # @since 2.0.0.beta.1
23
27
  def <<(*args)
24
- options = default_options(args)
25
- args.flatten.each do |doc|
26
- return doc unless doc
27
- append(doc, options)
28
- base.add_to_set(metadata.foreign_key, doc.id)
29
- doc.save if base.persisted? && !options[:binding]
28
+ batched do
29
+ [].tap do |ids|
30
+ args.flatten.each do |doc|
31
+ next unless doc
32
+ append(doc)
33
+ if persistable?
34
+ ids.push(doc.id)
35
+ doc.save
36
+ else
37
+ base.send(metadata.foreign_key).push(doc.id)
38
+ end
39
+ end
40
+ if persistable?
41
+ base.push_all(metadata.foreign_key, ids)
42
+ base.synced[metadata.foreign_key] = false
43
+ end
44
+ end
30
45
  end
31
46
  end
32
47
  alias :concat :<<
33
48
  alias :push :<<
34
49
 
35
- # Builds a new document in the relation and appends it to the target.
36
- # Takes an optional type if you want to specify a subclass.
50
+ # Build a new document from the attributes and append it to this
51
+ # relation without saving.
37
52
  #
38
53
  # @example Build a new document on the relation.
39
- # person.people.build(:name => "Bozo")
54
+ # person.posts.build(:title => "A new post")
40
55
  #
41
- # @param [ Hash ] attributes The attributes to build the document with.
42
- # @param [ Class ] type Optional class to build the document with.
56
+ # @param [ Hash ] attributes The attributes of the new document.
57
+ # @param [ Class ] type The optional subclass to build.
43
58
  #
44
59
  # @return [ Document ] The new document.
45
- def build(attributes = {}, type = nil, &block)
46
- super.tap do |doc|
60
+ #
61
+ # @since 2.0.0.beta.1
62
+ def build(attributes = {}, type = nil)
63
+ Factory.build(type || klass, attributes).tap do |doc|
47
64
  base.send(metadata.foreign_key).push(doc.id)
65
+ append(doc)
66
+ yield(doc) if block_given?
48
67
  end
49
68
  end
50
69
  alias :new :build
@@ -53,16 +72,19 @@ module Mongoid # :nodoc:
53
72
  # save the document if the parent has been persisted.
54
73
  #
55
74
  # @example Create and save the new document.
56
- # person.preferences.create(:text => "Testing")
75
+ # person.posts.create(:text => "Testing")
57
76
  #
58
77
  # @param [ Hash ] attributes The attributes to create with.
59
78
  # @param [ Class ] type The optional type of document to create.
60
79
  #
61
80
  # @return [ Document ] The newly created document.
81
+ #
82
+ # @since 2.0.0.beta.1
62
83
  def create(attributes = nil, type = nil, &block)
63
- build(attributes, type, &block).tap do |doc|
64
- base.add_to_set(metadata.foreign_key, doc.id)
65
- doc.save if base.persisted?
84
+ super.tap do |doc|
85
+ base.send(metadata.foreign_key).delete_one(doc.id)
86
+ base.push(metadata.foreign_key, doc.id)
87
+ base.synced[metadata.foreign_key] = false
66
88
  end
67
89
  end
68
90
 
@@ -71,7 +93,7 @@ module Mongoid # :nodoc:
71
93
  # error if validation fails.
72
94
  #
73
95
  # @example Create and save the new document.
74
- # person.preferences.create!(:text => "Testing")
96
+ # person.posts.create!(:text => "Testing")
75
97
  #
76
98
  # @param [ Hash ] attributes The attributes to create with.
77
99
  # @param [ Class ] type The optional type of document to create.
@@ -79,72 +101,63 @@ module Mongoid # :nodoc:
79
101
  # @raise [ Errors::Validations ] If validation failed.
80
102
  #
81
103
  # @return [ Document ] The newly created document.
82
- def create!(attributes = nil, type = nil, &block)
83
- build(attributes, type, &block).tap do |doc|
84
- base.add_to_set(metadata.foreign_key, doc.id)
85
- doc.save! if base.persisted?
86
- end
87
- end
88
-
89
- # Delete a single document from the relation.
90
- #
91
- # @example Delete a document.
92
- # person.preferences.delete(preference)
93
- #
94
- # @param [ Document ] document The document to delete.
95
104
  #
96
- # @since 2.0.0.rc.1
97
- def delete(document, options = {})
98
- target.delete(document).tap do |doc|
99
- binding.unbind_one(doc, default_options.merge!(options)) if doc
105
+ # @since 2.0.0.beta.1
106
+ def create!(attributes = nil, type = nil, &block)
107
+ super.tap do |doc|
108
+ base.send(metadata.foreign_key).delete_one(doc.id)
109
+ base.push(metadata.foreign_key, doc.id)
110
+ base.synced[metadata.foreign_key] = false
100
111
  end
101
112
  end
102
113
 
103
- # Deletes all related documents from the database given the supplied
104
- # conditions.
114
+ # Delete the document from the relation. This will set the foreign key
115
+ # on the document to nil. If the dependent options on the relation are
116
+ # :delete or :destroy the appropriate removal will occur.
105
117
  #
106
- # @example Delete all documents in the relation.
107
- # person.preferences.delete_all
118
+ # @example Delete the document.
119
+ # person.posts.delete(post)
108
120
  #
109
- # @example Conditonally delete all documents in the relation.
110
- # person.preferences.delete_all(:conditions => { :title => "Testing" })
121
+ # @param [ Document ] document The document to remove.
111
122
  #
112
- # @param [ Hash ] conditions Optional conditions to delete with.
123
+ # @return [ Document ] The matching document.
113
124
  #
114
- # @return [ Integer ] The number of documents deleted.
115
- def delete_all(conditions = nil)
116
- remove_all(conditions, :delete_all)
117
- end
118
-
119
- # Destroys all related documents from the database given the supplied
120
- # conditions.
121
- #
122
- # @example Destroy all documents in the relation.
123
- # person.preferences.destroy_all
124
- #
125
- # @example Conditonally destroy all documents in the relation.
126
- # person.preferences.destroy_all(:conditions => { :title => "Testing" })
127
- #
128
- # @param [ Hash ] conditions Optional conditions to destroy with.
129
- #
130
- # @return [ Integer ] The number of documents destroyd.
131
- def destroy_all(conditions = nil)
132
- remove_all(conditions, :destroy_all)
125
+ # @since 2.1.0
126
+ def delete(document)
127
+ super.tap do |doc|
128
+ if doc && persistable?
129
+ base.pull(metadata.foreign_key, doc.id)
130
+ base.synced[metadata.foreign_key] = false
131
+ end
132
+ end
133
133
  end
134
134
 
135
135
  # Instantiate a new references_many relation. Will set the foreign key
136
136
  # and the base on the inverse object.
137
137
  #
138
138
  # @example Create the new relation.
139
- # Referenced::ManyToMany.new(base, target, metadata)
139
+ # Referenced::Many.new(base, target, metadata)
140
140
  #
141
141
  # @param [ Document ] base The document this relation hangs off of.
142
142
  # @param [ Array<Document> ] target The target of the relation.
143
143
  # @param [ Metadata ] metadata The relation's metadata.
144
+ #
145
+ # @since 2.0.0.beta.1
144
146
  def initialize(base, target, metadata)
145
- init(base, target, metadata) do
146
- unless base.frozen?
147
- base.send(metadata.foreign_key_setter, target.map(&:id))
147
+ init(base, Targets::Enumerable.new(target), metadata) do |proxy|
148
+ raise_mixed if klass.embedded?
149
+ batched do
150
+ proxy.in_memory do |doc|
151
+ characterize_one(doc)
152
+ bind_one(doc)
153
+ if persistable?
154
+ base.push(metadata.foreign_key, doc.id)
155
+ base.synced[metadata.foreign_key] = false
156
+ doc.save
157
+ else
158
+ base.send(metadata.foreign_key).push(doc.id)
159
+ end
160
+ end
148
161
  end
149
162
  end
150
163
  end
@@ -158,79 +171,42 @@ module Mongoid # :nodoc:
158
171
  #
159
172
  # @since 2.0.0.rc.1
160
173
  def nullify
161
- load! and target.each do |doc|
162
- base.send(metadata.foreign_key).delete(doc.id)
163
- dereference(doc)
174
+ # @todo: Durran: This is wrong.
175
+ criteria.update(metadata.inverse_foreign_key => [])
176
+ # We need to update the inverse as well.
177
+ target.clear do |doc|
178
+ unbind_one(doc)
164
179
  end
165
- target.clear
166
180
  end
167
181
  alias :nullify_all :nullify
168
182
 
169
- # Substitutes the supplied target documents for the existing documents
170
- # in the relation. If the new target is nil, perform the necessary
171
- # deletion.
172
- #
173
- # @example Replace the relation.
174
- # person.posts.substitute(new_name)
175
- #
176
- # @param [ Array<Document> ] target The replacement target.
177
- # @param [ Hash ] options The options to bind with.
178
- #
179
- # @option options [ true, false ] :binding Are we in build mode?
180
- # @option options [ true, false ] :continue Continue binding the
181
- # inverse?
182
- #
183
- # @return [ Many ] The relation.
184
- #
185
- # @since 2.0.0.rc.1
186
- def substitute(new_target, options = {})
187
- tap do |relation|
188
- if new_target
189
- binding.unbind(options)
190
- relation.target = new_target.to_a
191
- base.send(metadata.foreign_key_setter, new_target.map(&:id))
192
- bind(options)
193
- else
194
- relation.target = unbind(options)
195
- end
196
- end
197
- end
183
+ private
198
184
 
199
- # Unbinds the base object to the inverse of the relation. This occurs
200
- # when setting a side of the relation to nil.
201
- #
202
- # Will delete the object if necessary.
203
- #
204
- # @example Unbind the target.
205
- # person.posts.unbind
185
+ # Appends the document to the target array, updating the index on the
186
+ # document at the same time.
206
187
  #
207
- # @param [ Hash ] options The options to bind with.
188
+ # @example Append the document to the relation.
189
+ # relation.append(document)
208
190
  #
209
- # @option options [ true, false ] :binding Are we in build mode?
210
- # @option options [ true, false ] :continue Continue binding the
211
- # inverse?
191
+ # @param [ Document ] document The document to append to the target.
212
192
  #
213
193
  # @since 2.0.0.rc.1
214
- def unbind(options = {})
215
- target.each(&:delete) if base.persisted?
216
- binding.unbind(options)
217
- []
194
+ def append(document)
195
+ target.push(document)
196
+ characterize_one(document)
197
+ bind_one(document)
218
198
  end
219
199
 
220
- private
221
-
222
200
  # Instantiate the binding associated with this relation.
223
201
  #
224
202
  # @example Get the binding.
225
203
  # relation.binding([ address ])
226
204
  #
227
- # @param [ Array<Document> ] new_target The new documents to bind with.
228
- #
229
205
  # @return [ Binding ] The binding.
230
206
  #
231
207
  # @since 2.0.0.rc.1
232
- def binding(new_target = nil)
233
- Bindings::Referenced::ManyToMany.new(base, new_target || target, metadata)
208
+ def binding
209
+ Bindings::Referenced::ManyToMany.new(base, target, metadata)
234
210
  end
235
211
 
236
212
  # Returns the criteria object for the target class with its documents set
@@ -241,44 +217,7 @@ module Mongoid # :nodoc:
241
217
  #
242
218
  # @return [ Criteria ] A new criteria.
243
219
  def criteria
244
- if metadata.inverse
245
- metadata.klass.any_in(metadata.inverse_foreign_key => [ base.id ])
246
- else
247
- metadata.klass.where(:_id => { "$in" => base.send(metadata.foreign_key) })
248
- end
249
- end
250
-
251
- # Dereferences the supplied document from the base of the relation.
252
- #
253
- # @example Dereference the document.
254
- # person.preferences.dereference(preference)
255
- #
256
- # @param [ Document ] document The document to dereference.
257
- def dereference(document)
258
- document.send(metadata.inverse_foreign_key).delete(base.id)
259
- document.send(metadata.inverse(document)).target.delete(base)
260
- document.save
261
- end
262
-
263
- # Remove all documents from the relation, either with a delete or a
264
- # destroy depending on what this was called through.
265
- #
266
- # @example Destroy documents from the relation.
267
- # relation.remove_all(:conditions => { :num => 1 }, true)
268
- #
269
- # @param [ Hash ] conditions Conditions to filter by.
270
- # @param [ true, false ] destroy If true then destroy, else delete.
271
- #
272
- # @return [ Integer ] The number of documents removed.
273
- def remove_all(conditions = {}, method = :destroy)
274
- cond = conditions || {}
275
- target.delete_if do |doc|
276
- doc.matches?(cond[:conditions] || {})
277
- end
278
- ids = criteria.merge(cond).only(:_id).map(&:_id)
279
- criteria.merge(cond).send(method).tap do
280
- base.pull_all(metadata.foreign_key, ids)
281
- end
220
+ ManyToMany.criteria(metadata, base.send(metadata.foreign_key))
282
221
  end
283
222
 
284
223
  class << self
@@ -296,8 +235,12 @@ module Mongoid # :nodoc:
296
235
  # @return [ Builder ] A new builder object.
297
236
  #
298
237
  # @since 2.0.0.rc.1
299
- def builder(meta, object)
300
- Builders::Referenced::ManyToMany.new(meta, object)
238
+ def builder(meta, object, loading = false)
239
+ Builders::Referenced::ManyToMany.new(meta, object, loading)
240
+ end
241
+
242
+ def criteria(metadata, object, type = nil)
243
+ metadata.klass.any_in(:_id => object)
301
244
  end
302
245
 
303
246
  # Returns true if the relation is an embedded one. In this case
@@ -374,6 +317,20 @@ module Mongoid # :nodoc:
374
317
  Builders::NestedAttributes::Many.new(metadata, attributes, options)
375
318
  end
376
319
 
320
+ # Get the path calculator for the supplied document.
321
+ #
322
+ # @example Get the path calculator.
323
+ # Proxy.path(document)
324
+ #
325
+ # @param [ Document ] document The document to calculate on.
326
+ #
327
+ # @return [ Root ] The root atomic path calculator.
328
+ #
329
+ # @since 2.1.0
330
+ def path(document)
331
+ Mongoid::Atomic::Paths::Root.new(document)
332
+ end
333
+
377
334
  # Tells the caller if this relation is one that stores the foreign
378
335
  # key on its own objects.
379
336
  #
@@ -386,6 +343,18 @@ module Mongoid # :nodoc:
386
343
  def stores_foreign_key?
387
344
  true
388
345
  end
346
+
347
+ # Get the valid options allowed with this relation.
348
+ #
349
+ # @example Get the valid options.
350
+ # Relation.valid_options
351
+ #
352
+ # @return [ Array<Symbol> ] The valid options.
353
+ #
354
+ # @since 2.1.0
355
+ def valid_options
356
+ [ :autosave, :dependent, :foreign_key, :index, :order ]
357
+ end
389
358
  end
390
359
  end
391
360
  end
@@ -7,28 +7,6 @@ module Mongoid # :nodoc:
7
7
  # one-to-one between documents in different collections.
8
8
  class One < Relations::One
9
9
 
10
- # Binds the base object to the inverse of the relation. This is so we
11
- # are referenced to the actual objects themselves and dont hit the
12
- # database twice when setting the relations up.
13
- #
14
- # This is called after first creating the relation, or if a new object
15
- # is set on the relation.
16
- #
17
- # @example Bind the relation.
18
- # person.game.bind(:continue => false)
19
- #
20
- # @param [ Hash ] options The options to bind with.
21
- #
22
- # @option options [ true, false ] :binding Are we in build mode?
23
- # @option options [ true, false ] :continue Continue binding the
24
- # inverse?
25
- #
26
- # @since 2.0.0.rc.1
27
- def bind(options = {})
28
- binding.bind(options)
29
- target.save if base.persisted? && !options[:binding]
30
- end
31
-
32
10
  # Instantiate a new references_one relation. Will set the foreign key
33
11
  # and the base on the inverse object.
34
12
  #
@@ -40,24 +18,13 @@ module Mongoid # :nodoc:
40
18
  # @param [ Metadata ] metadata The relation's metadata.
41
19
  def initialize(base, target, metadata)
42
20
  init(base, target, metadata) do
21
+ raise_mixed if klass.embedded?
43
22
  characterize_one(target)
23
+ bind_one
24
+ target.save if persistable?
44
25
  end
45
26
  end
46
27
 
47
- # Will load the target into an array if the target had not already been
48
- # loaded.
49
- #
50
- # @example Load the relation into memory.
51
- # relation.load!
52
- #
53
- # @return [ One ] The relation.
54
- #
55
- # @since 2.0.0.rc.5
56
- def load!(options = {})
57
- raise_mixed if klass.embedded?
58
- super(options)
59
- end
60
-
61
28
  # Removes the association between the base document and the target
62
29
  # document by deleting the foreign key and the reference, orphaning
63
30
  # the target document in the process.
@@ -67,32 +34,29 @@ module Mongoid # :nodoc:
67
34
  #
68
35
  # @since 2.0.0.rc.1
69
36
  def nullify
70
- target.send(metadata.foreign_key_setter, nil)
71
- target.send(:remove_instance_variable, "@#{metadata.inverse(target)}")
72
- base.send(:remove_instance_variable, "@#{metadata.name}")
37
+ unbind_one
73
38
  target.save
74
39
  end
75
40
 
76
- # Unbinds the base object to the inverse of the relation. This occurs
77
- # when setting a side of the relation to nil.
78
- #
79
- # Will delete the object if necessary.
41
+ # Substitutes the supplied target document for the existing document
42
+ # in the relation. If the new target is nil, perform the necessary
43
+ # deletion.
80
44
  #
81
- # @example Unbind the relation.
82
- # person.game.unbind(name, :continue => true)
45
+ # @example Replace the relation.
46
+ # person.game.substitute(new_game)
83
47
  #
84
- # @param [ Document ] old_target The previous target of the relation.
85
- # @param [ Hash ] options The options to bind with.
48
+ # @param [ Array<Document> ] replacement The replacement target.
86
49
  #
87
- # @option options [ true, false ] :binding Are we in build mode?
88
- # @option options [ true, false ] :continue Continue binding the
89
- # inverse?
50
+ # @return [ One ] The relation.
90
51
  #
91
52
  # @since 2.0.0.rc.1
92
- def unbind(old_target, options = {})
93
- binding(old_target).unbind(options)
94
- if base.persisted? && !old_target.destroyed? && !options[:binding]
95
- old_target.delete
53
+ def substitute(replacement)
54
+ tap do |proxy|
55
+ proxy.unbind_one
56
+ proxy.target.delete if persistable?
57
+ return nil unless replacement
58
+ proxy.target = replacement
59
+ proxy.bind_one
96
60
  end
97
61
  end
98
62
 
@@ -106,8 +70,20 @@ module Mongoid # :nodoc:
106
70
  # @param [ Document ] new_target The new target of the relation.
107
71
  #
108
72
  # @return [ Binding ] The binding object.
109
- def binding(new_target = nil)
110
- Bindings::Referenced::One.new(base, new_target || target, metadata)
73
+ def binding
74
+ Bindings::Referenced::One.new(base, target, metadata)
75
+ end
76
+
77
+ # Are we able to persist this relation?
78
+ #
79
+ # @example Can we persist the relation?
80
+ # relation.persistable?
81
+ #
82
+ # @return [ true, false ] If the relation is persistable.
83
+ #
84
+ # @since 2.1.0
85
+ def persistable?
86
+ base.persisted? && !binding? && !building?
111
87
  end
112
88
 
113
89
  class << self
@@ -125,8 +101,24 @@ module Mongoid # :nodoc:
125
101
  # @return [ Builder ] A new builder object.
126
102
  #
127
103
  # @since 2.0.0.rc.1
128
- def builder(meta, object)
129
- Builders::Referenced::One.new(meta, object)
104
+ def builder(meta, object, loading = false)
105
+ Builders::Referenced::One.new(meta, object, loading)
106
+ end
107
+
108
+ # Get the standard criteria used for querying this relation.
109
+ #
110
+ # @example Get the criteria.
111
+ # Proxy.criteria(meta, id, Model)
112
+ #
113
+ # @param [ Metadata ] metadata The metadata.
114
+ # @param [ Object ] object The value of the foreign key.
115
+ # @param [ Class ] type The optional type.
116
+ #
117
+ # @return [ Criteria ] The criteria.
118
+ #
119
+ # @since 2.1.0
120
+ def criteria(metadata, object, type = nil)
121
+ metadata.klass.where(metadata.foreign_key => object)
130
122
  end
131
123
 
132
124
  # Returns true if the relation is an embedded one. In this case
@@ -203,6 +195,20 @@ module Mongoid # :nodoc:
203
195
  Builders::NestedAttributes::One.new(metadata, attributes, options)
204
196
  end
205
197
 
198
+ # Get the path calculator for the supplied document.
199
+ #
200
+ # @example Get the path calculator.
201
+ # Proxy.path(document)
202
+ #
203
+ # @param [ Document ] document The document to calculate on.
204
+ #
205
+ # @return [ Root ] The root atomic path calculator.
206
+ #
207
+ # @since 2.1.0
208
+ def path(document)
209
+ Mongoid::Atomic::Paths::Root.new(document)
210
+ end
211
+
206
212
  # Tells the caller if this relation is one that stores the foreign
207
213
  # key on its own objects.
208
214
  #
@@ -215,6 +221,18 @@ module Mongoid # :nodoc:
215
221
  def stores_foreign_key?
216
222
  false
217
223
  end
224
+
225
+ # Get the valid options allowed with this relation.
226
+ #
227
+ # @example Get the valid options.
228
+ # Relation.valid_options
229
+ #
230
+ # @return [ Array<Symbol> ] The valid options.
231
+ #
232
+ # @since 2.1.0
233
+ def valid_options
234
+ [ :as, :autosave, :dependent, :foreign_key ]
235
+ end
218
236
  end
219
237
  end
220
238
  end