mongoid 2.0.2 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
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