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
@@ -17,7 +17,11 @@ module Mongoid # :nodoc:
17
17
  # @return [ Document ] A single document.
18
18
  def build(type = nil)
19
19
  return object unless object.is_a?(Hash)
20
- Mongoid::Factory.from_db(metadata.klass, object)
20
+ if loading
21
+ Mongoid::Factory.from_db(metadata.klass, object)
22
+ else
23
+ Mongoid::Factory.build(metadata.klass, object)
24
+ end
21
25
  end
22
26
  end
23
27
  end
@@ -114,9 +114,9 @@ module Mongoid # :nodoc:
114
114
  return if reject?(attrs)
115
115
  if id = attrs[:id] || attrs["id"] || attrs["_id"]
116
116
  document = existing.find(convert_id(id))
117
- destroyable?(attrs) ? document.destroy : document.update_attributes(attrs)
117
+ destroyable?(attrs) ? existing.delete(document) : document.update_attributes(attrs)
118
118
  else
119
- existing.push(metadata.klass.new(attrs)) unless destroyable?(attrs)
119
+ existing.push(Mongoid::Factory.build(metadata.klass, attrs)) unless destroyable?(attrs)
120
120
  end
121
121
  end
122
122
  end
@@ -27,7 +27,7 @@ module Mongoid # :nodoc:
27
27
  if update?
28
28
  existing.attributes = attributes
29
29
  elsif replace?
30
- parent.send(metadata.setter, metadata.klass.new(attributes))
30
+ parent.send(metadata.setter, Mongoid::Factory.build(metadata.klass, attributes))
31
31
  elsif delete?
32
32
  parent.send(metadata.setter, nil)
33
33
  end
@@ -16,11 +16,8 @@ module Mongoid # :nodoc:
16
16
  # @return [ Document ] A single document.
17
17
  def build(type = nil)
18
18
  return object unless query?
19
- begin
20
- (type ? type.constantize : metadata.klass).find(object)
21
- rescue Errors::DocumentNotFound
22
- return nil
23
- end
19
+ model = type ? type.constantize : metadata.klass
20
+ IdentityMap.get(model, object) || metadata.criteria(object, model).first
24
21
  end
25
22
  end
26
23
  end
@@ -17,8 +17,7 @@ module Mongoid # :nodoc:
17
17
  def build(type = nil)
18
18
  return object unless query?
19
19
  return [] if object.is_a?(Array)
20
- key = metadata.foreign_key
21
- metadata.klass.where(key => convertable(metadata, object))
20
+ metadata.criteria(convertable(metadata, object))
22
21
  end
23
22
 
24
23
  private
@@ -37,7 +36,7 @@ module Mongoid # :nodoc:
37
36
  if inverse.using_object_ids? || object.is_a?(BSON::ObjectId)
38
37
  object
39
38
  else
40
- Mongoid::Criterion::Unconvertable.new(object)
39
+ Criterion::Unconvertable.new(object)
41
40
  end
42
41
  end
43
42
  end
@@ -16,11 +16,20 @@ module Mongoid # :nodoc:
16
16
  # @return [ Array<Document> ] The documents.
17
17
  def build(type = nil)
18
18
  return object.try(:dup) unless query?
19
- begin
20
- metadata.klass.find(object)
21
- rescue Errors::DocumentNotFound
22
- return []
23
- end
19
+ metadata.criteria(object)
20
+ end
21
+
22
+ # Do we need to perform a database query? It will be so if the object we
23
+ # have is not a document.
24
+ #
25
+ # @example Should we query the database?
26
+ # builder.query?
27
+ #
28
+ # @return [ true, false ] Whether a database query should happen.
29
+ #
30
+ # @since 2.0.0.rc.1
31
+ def query?
32
+ object.nil? || !object.first.is_a?(Mongoid::Document)
24
33
  end
25
34
  end
26
35
  end
@@ -16,9 +16,8 @@ module Mongoid # :nodoc:
16
16
  # @return [ Document ] A single document.
17
17
  def build(type = nil)
18
18
  return object unless query?
19
- metadata.klass.first(
20
- :conditions => { metadata.foreign_key => object }
21
- )
19
+ criteria = metadata.criteria(object)
20
+ IdentityMap.match(criteria) || criteria.first
22
21
  end
23
22
  end
24
23
  end
@@ -53,12 +53,12 @@ module Mongoid #:nodoc:
53
53
  #
54
54
  # @since 2.0.0
55
55
  def atomically(modifier, &block)
56
- updater = Thread.current[:mongoid_atomic_update] ||= MODIFIERS[modifier].new
56
+ updater = Threaded.update ||= MODIFIERS[modifier].new
57
57
  count_executions do
58
58
  block.call if block
59
59
  end.tap do
60
60
  if @executions.zero?
61
- Thread.current[:mongoid_atomic_update] = nil
61
+ Threaded.update = nil
62
62
  updater.execute(collection)
63
63
  end
64
64
  end
@@ -8,28 +8,6 @@ module Mongoid # :nodoc:
8
8
  # multiple documents.
9
9
  class In < Relations::One
10
10
 
11
- # Binds the base object to the inverse of the relation. This is so we
12
- # are referenced to the actual objects themselves and dont hit the
13
- # database twice when setting the relations up.
14
- #
15
- # This is called after first creating the relation, or if a new object
16
- # is set on the relation.
17
- #
18
- # @example Bind the relation.
19
- # name.person.bind(:continue => true)
20
- #
21
- # @param [ Hash ] options The options to bind with.
22
- #
23
- # @option options [ true, false ] :binding Are we in build mode?
24
- # @option options [ true, false ] :continue Continue binding the
25
- # inverse?
26
- #
27
- # @since 2.0.0.rc.1
28
- def bind(options = {})
29
- binding.bind(options)
30
- base.save if target.persisted? && !options[:binding]
31
- end
32
-
33
11
  # Instantiate a new embedded_in relation.
34
12
  #
35
13
  # @example Create the new relation.
@@ -43,29 +21,32 @@ module Mongoid # :nodoc:
43
21
  def initialize(base, target, metadata)
44
22
  init(base, target, metadata) do
45
23
  characterize_one(target)
46
- base.parentize(target)
24
+ bind_one
25
+ base.save if persistable?
47
26
  end
48
27
  end
49
28
 
50
- # Unbinds the base object to the inverse of the relation. This occurs
51
- # when setting a side of the relation to nil.
29
+ # Substitutes the supplied target documents for the existing document
30
+ # in the relation.
52
31
  #
53
- # Will delete the object if necessary.
32
+ # @example Substitute the new document.
33
+ # person.name.substitute(new_name)
54
34
  #
55
- # @example Unbind the relation.
56
- # name.person.unbind(:continue => false)
35
+ # @param [ Document ] other A document to replace the target.
57
36
  #
58
- # @param [ Proxy ] old_target The previous target of the relation.
59
- # @param [ Hash ] options The options to bind with.
60
- #
61
- # @option options [ true, false ] :binding Are we in build mode?
62
- # @option options [ true, false ] :continue Continue binding the
63
- # inverse?
37
+ # @return [ Document, nil ] The relation or nil.
64
38
  #
65
39
  # @since 2.0.0.rc.1
66
- def unbind(old_target, options = {})
67
- binding(old_target).unbind(options)
68
- base.delete if old_target.persisted? && !base.destroyed?
40
+ def substitute(replacement)
41
+ tap do |proxy|
42
+ proxy.unbind_one
43
+ unless replacement
44
+ base.delete unless binding?
45
+ return nil
46
+ end
47
+ proxy.target = replacement
48
+ proxy.bind_one
49
+ end
69
50
  end
70
51
 
71
52
  private
@@ -80,8 +61,32 @@ module Mongoid # :nodoc:
80
61
  # @return [ Binding ] A binding object.
81
62
  #
82
63
  # @since 2.0.0.rc.1
83
- def binding(new_target = nil)
84
- Bindings::Embedded::In.new(base, new_target || target, metadata)
64
+ def binding
65
+ Bindings::Embedded::In.new(base, target, metadata)
66
+ end
67
+
68
+ # Characterize the document.
69
+ #
70
+ # @example Set the base metadata.
71
+ # relation.characterize_one(document)
72
+ #
73
+ # @param [ Document ] document The document to set the metadata on.
74
+ #
75
+ # @since 2.1.0
76
+ def characterize_one(document)
77
+ base.metadata = metadata.inverse_metadata(document)
78
+ end
79
+
80
+ # Are we able to persist this relation?
81
+ #
82
+ # @example Can we persist the relation?
83
+ # relation.persistable?
84
+ #
85
+ # @return [ true, false ] If the relation is persistable.
86
+ #
87
+ # @since 2.1.0
88
+ def persistable?
89
+ target.persisted? && !binding? && !building?
85
90
  end
86
91
 
87
92
  class << self
@@ -98,8 +103,8 @@ module Mongoid # :nodoc:
98
103
  # @return [ Builder ] A newly instantiated builder object.
99
104
  #
100
105
  # @since 2.0.0.rc.1
101
- def builder(meta, object)
102
- Builders::Embedded::In.new(meta, object)
106
+ def builder(meta, object, loading = false)
107
+ Builders::Embedded::In.new(meta, object, loading)
103
108
  end
104
109
 
105
110
  # Returns true if the relation is an embedded one. In this case
@@ -154,6 +159,20 @@ module Mongoid # :nodoc:
154
159
  Builders::NestedAttributes::One.new(metadata, attributes, options)
155
160
  end
156
161
 
162
+ # Get the path calculator for the supplied document.
163
+ #
164
+ # @example Get the path calculator.
165
+ # Proxy.path(document)
166
+ #
167
+ # @param [ Document ] document The document to calculate on.
168
+ #
169
+ # @return [ Root ] The root atomic path calculator.
170
+ #
171
+ # @since 2.1.0
172
+ def path(document)
173
+ Mongoid::Atomic::Paths::Root.new(document)
174
+ end
175
+
157
176
  # Tells the caller if this relation is one that stores the foreign
158
177
  # key on its own objects.
159
178
  #
@@ -166,6 +185,18 @@ module Mongoid # :nodoc:
166
185
  def stores_foreign_key?
167
186
  false
168
187
  end
188
+
189
+ # Get the valid options allowed with this relation.
190
+ #
191
+ # @example Get the valid options.
192
+ # Relation.valid_options
193
+ #
194
+ # @return [ Array<Symbol> ] The valid options.
195
+ #
196
+ # @since 2.1.0
197
+ def valid_options
198
+ [ :cyclic, :polymorphic ]
199
+ end
169
200
  end
170
201
  end
171
202
  end
@@ -22,55 +22,35 @@ module Mongoid # :nodoc:
22
22
  #
23
23
  # @param [ Document, Array<Document> ] *args Any number of documents.
24
24
  def <<(*args)
25
- options = default_options(args)
26
25
  atomically(:$pushAll) do
27
26
  args.flatten.each do |doc|
28
- return doc unless doc
29
- append(doc, options)
30
- doc.save if base.persisted? && !options[:binding]
27
+ next unless doc
28
+ append(doc)
29
+ doc.save if persistable?
31
30
  end
32
31
  end
33
32
  end
33
+ alias :concat :<<
34
+ alias :push :<<
34
35
 
35
- # Binds the base object to the inverse of the relation. This is so we
36
- # are referenced to the actual objects themselves and dont hit the
37
- # database twice when setting the relations up.
36
+ # Builds a new document in the relation and appends it to the target.
37
+ # Takes an optional type if you want to specify a subclass.
38
38
  #
39
- # This is called after first creating the relation, or if a new object
40
- # is set on the relation.
39
+ # @example Build a new document on the relation.
40
+ # person.people.build(:name => "Bozo")
41
41
  #
42
- # @example Bind the relation.
43
- # person.addresses.bind(:continue => true)
44
- #
45
- # @param [ Hash ] options The options to bind with.
46
- #
47
- # @option options [ true, false ] :binding Are we in build mode?
48
- # @option options [ true, false ] :continue Continue binding the
49
- # inverse?
50
- #
51
- # @since 2.0.0.rc.1
52
- def bind(options = {})
53
- binding.bind(options)
54
- if base.persisted? && !options[:binding]
55
- atomically(:$set) { target.each(&:save) }
42
+ # @param [ Hash ] attributes The attributes to build the document with.
43
+ # @param [ Class ] type Optional class to build the document with.
44
+ #
45
+ # @return [ Document ] The new document.
46
+ def build(attributes = {}, type = nil)
47
+ Factory.build(type || metadata.klass, attributes).tap do |doc|
48
+ doc.identify
49
+ append(doc)
50
+ yield(doc) if block_given?
56
51
  end
57
52
  end
58
-
59
- # Bind the inverse relation between a single document in this proxy
60
- # instead of the entire target.
61
- #
62
- # Used when appending to the target instead of setting the entire
63
- # thing.
64
- #
65
- # @example Bind a single document.
66
- # person.addressses.bind_one(address)
67
- #
68
- # @param [ Document ] document The document to bind.
69
- #
70
- # @since 2.0.0.rc.1
71
- def bind_one(document, options = {})
72
- binding.bind_one(document, options)
73
- end
53
+ alias :new :build
74
54
 
75
55
  # Clear the relation. Will delete the documents from the db if they are
76
56
  # already persisted.
@@ -80,7 +60,9 @@ module Mongoid # :nodoc:
80
60
  #
81
61
  # @return [ Many ] The empty relation.
82
62
  def clear
83
- load! and substitute(nil)
63
+ tap do |proxy|
64
+ atomically(:$unset) { proxy.delete_all }
65
+ end
84
66
  end
85
67
 
86
68
  # Returns a count of the number of documents in the association that have
@@ -94,7 +76,7 @@ module Mongoid # :nodoc:
94
76
  # @return [ Integer ] The total number of persisted embedded docs, as
95
77
  # flagged by the #persisted? method.
96
78
  def count
97
- target.select(&:persisted?).size
79
+ target.select { |doc| doc.persisted? }.size
98
80
  end
99
81
 
100
82
  # Create a new document in the relation. This is essentially the same
@@ -108,7 +90,7 @@ module Mongoid # :nodoc:
108
90
  #
109
91
  # @return [ Document ] The newly created document.
110
92
  def create(attributes = {}, type = nil, &block)
111
- build(attributes, type, &block).tap(&:save)
93
+ build(attributes, type, &block).tap { |doc| doc.save }
112
94
  end
113
95
 
114
96
  # Create a new document in the relation. This is essentially the same
@@ -125,7 +107,7 @@ module Mongoid # :nodoc:
125
107
  #
126
108
  # @return [ Document ] The newly created document.
127
109
  def create!(attributes = {}, type = nil, &block)
128
- build(attributes, type, &block).tap(&:save!)
110
+ build(attributes, type, &block).tap { |doc| doc.save! }
129
111
  end
130
112
 
131
113
  # Delete the supplied document from the target. This method is proxied
@@ -140,7 +122,10 @@ module Mongoid # :nodoc:
140
122
  #
141
123
  # @since 2.0.0.rc.1
142
124
  def delete(document)
143
- target.delete(document).tap { reindex }
125
+ target.delete_one(document).tap do |doc|
126
+ unbind_one(doc) if doc && !binding?
127
+ reindex
128
+ end
144
129
  end
145
130
 
146
131
  # Delete all the documents in the association without running callbacks.
@@ -207,29 +192,22 @@ module Mongoid # :nodoc:
207
192
  def initialize(base, target, metadata)
208
193
  init(base, target, metadata) do
209
194
  target.each_with_index do |doc, index|
210
- characterize_one(doc)
211
- doc.parentize(base)
195
+ integrate(doc)
212
196
  doc._index = index
213
197
  end
214
198
  end
215
199
  end
216
200
 
217
- # Will load the target into an array if the target had not already been
218
- # loaded.
201
+ # Get all the documents in the relation that are loaded into memory.
219
202
  #
220
- # @example Load the relation into memory.
221
- # relation.load!
203
+ # @example Get the in memory documents.
204
+ # relation.in_memory
222
205
  #
223
- # @return [ Many ] The relation.
206
+ # @return [ Array<Document> ] The documents in memory.
224
207
  #
225
- # @since 2.0.0.rc.5
226
- def load!(options = {})
227
- tap do |relation|
228
- unless relation.loaded?
229
- relation.bind(options)
230
- relation.loaded = true
231
- end
232
- end
208
+ # @since 2.1.0
209
+ def in_memory
210
+ target
233
211
  end
234
212
 
235
213
  # Substitutes the supplied target documents for the existing documents
@@ -244,14 +222,19 @@ module Mongoid # :nodoc:
244
222
  # @return [ Many ] The proxied relation.
245
223
  #
246
224
  # @since 2.0.0.rc.1
247
- def substitute(new_target, options = {})
248
- old_target = target
249
- tap do |relation|
250
- relation.target = new_target || []
251
- if !new_target.blank?
252
- atomically(:$set) { rebind(old_target, options) }
225
+ def substitute(replacement)
226
+ tap do |proxy|
227
+ if replacement.blank?
228
+ proxy.clear
253
229
  else
254
- atomically(:$unset) { unbind(old_target, options) }
230
+ atomically(:$set) do
231
+ proxy.target = replacement
232
+ proxy.target.each_with_index do |doc, index|
233
+ integrate(doc)
234
+ doc._index = index
235
+ doc.save if base.persisted?
236
+ end
237
+ end
255
238
  end
256
239
  end
257
240
  end
@@ -265,32 +248,9 @@ module Mongoid # :nodoc:
265
248
  #
266
249
  # @since 2.0.0.rc.1
267
250
  def as_document
268
- target.inject([]) do |attributes, doc|
269
- attributes.tap do |attr|
270
- attr << doc.as_document
271
- end
272
- end
273
- end
274
-
275
- # Unbind the inverse relation from this set of documents. Used when the
276
- # entire proxy has been cleared, set to nil or empty, or replaced.
277
- #
278
- # @example Unbind the relation.
279
- # person.addresses.unbind(target, :continue => false)
280
- #
281
- # @param [ Array<Document> ] old_target The relations previous target.
282
- # @param [ Hash ] options The options to bind with.
283
- #
284
- # @option options [ true, false ] :binding Are we in build mode?
285
- # @option options [ true, false ] :continue Continue binding the
286
- # inverse?
287
- #
288
- # @since 2.0.0.rc.1
289
- def unbind(old_target, options = {})
290
- binding(old_target).unbind(options)
291
- if base.persisted?
292
- old_target.each do |doc|
293
- doc.delete unless doc.destroyed?
251
+ [].tap do |attributes|
252
+ target.each do |doc|
253
+ attributes << doc.as_document
294
254
  end
295
255
  end
296
256
  end
@@ -306,10 +266,9 @@ module Mongoid # :nodoc:
306
266
  # @param [ Document ] document The document to append to the target.
307
267
  #
308
268
  # @since 2.0.0.rc.1
309
- def append(document, options = {})
310
- load! and target.push(document)
311
- characterize_one(document)
312
- bind_one(document, options)
269
+ def append(document)
270
+ target.push(document)
271
+ integrate(document)
313
272
  document._index = target.size - 1
314
273
  end
315
274
 
@@ -323,8 +282,8 @@ module Mongoid # :nodoc:
323
282
  # @return [ Binding ] The many binding.
324
283
  #
325
284
  # @since 2.0.0.rc.1
326
- def binding(new_target = nil)
327
- Bindings::Embedded::Many.new(base, new_target || target, metadata)
285
+ def binding
286
+ Bindings::Embedded::Many.new(base, target, metadata)
328
287
  end
329
288
 
330
289
  # Returns the criteria object for the target class with its documents set
@@ -335,11 +294,25 @@ module Mongoid # :nodoc:
335
294
  #
336
295
  # @return [ Criteria ] A new criteria.
337
296
  def criteria
338
- metadata.klass.criteria(true).tap do |criterion|
297
+ klass.criteria(true).tap do |criterion|
339
298
  criterion.documents = target
340
299
  end
341
300
  end
342
301
 
302
+ # Integrate the document into the relation. will set its metadata and
303
+ # attempt to bind the inverse.
304
+ #
305
+ # @example Integrate the document.
306
+ # relation.integrate(document)
307
+ #
308
+ # @param [ Document ] document The document to integrate.
309
+ #
310
+ # @since 2.1.0
311
+ def integrate(document)
312
+ characterize_one(document)
313
+ bind_one(document)
314
+ end
315
+
343
316
  # If the target array does not respond to the supplied method then try to
344
317
  # find a named scope or criteria on the class and send the call there.
345
318
  #
@@ -351,13 +324,24 @@ module Mongoid # :nodoc:
351
324
  #
352
325
  # @return [ Criteria, Object ] A Criteria or return value from the target.
353
326
  def method_missing(name, *args, &block)
354
- load!(:binding => true) and return super if target.respond_to?(name)
355
- klass = metadata.klass
327
+ return super if target.respond_to?(name)
356
328
  klass.send(:with_scope, criteria) do
357
329
  criteria.send(name, *args, &block)
358
330
  end
359
331
  end
360
332
 
333
+ # Are we able to persist this relation?
334
+ #
335
+ # @example Can we persist the relation?
336
+ # relation.persistable?
337
+ #
338
+ # @return [ true, false ] If the relation is persistable.
339
+ #
340
+ # @since 2.1.0
341
+ def persistable?
342
+ base.persisted? && !binding?
343
+ end
344
+
361
345
  # Reindex all the target elements. This is useful when performing
362
346
  # operations on the proxied target directly and the indices need to
363
347
  # match that on the database side.
@@ -386,29 +370,14 @@ module Mongoid # :nodoc:
386
370
  criteria = find(:all, conditions || {})
387
371
  criteria.size.tap do
388
372
  criteria.each do |doc|
389
- target.delete(doc)
373
+ target.delete_one(doc)
374
+ unbind_one(doc)
390
375
  doc.send(method, :suppress => true)
391
376
  end
392
377
  reindex
393
378
  end
394
379
  end
395
380
 
396
- # Convenience method to clean up the substitute code. Unbinds the old
397
- # target and reindexes.
398
- #
399
- # @example Rebind the relation.
400
- # relation.rebind([])
401
- #
402
- # @param [ Array<Document> ] old_target The old target.
403
- # @param [ Hash ] options The options passed to substitute.
404
- #
405
- # @since 2.0.0
406
- def rebind(old_target, options)
407
- reindex
408
- unbind(old_target, options)
409
- bind(options)
410
- end
411
-
412
381
  class << self
413
382
 
414
383
  # Return the builder that is responsible for generating the documents
@@ -424,8 +393,8 @@ module Mongoid # :nodoc:
424
393
  # @return [ Builder ] A newly instantiated builder object.
425
394
  #
426
395
  # @since 2.0.0.rc.1
427
- def builder(meta, object)
428
- Builders::Embedded::Many.new(meta, object)
396
+ def builder(meta, object, loading = false)
397
+ Builders::Embedded::Many.new(meta, object, loading)
429
398
  end
430
399
 
431
400
  # Returns true if the relation is an embedded one. In this case
@@ -480,6 +449,21 @@ module Mongoid # :nodoc:
480
449
  Builders::NestedAttributes::Many.new(metadata, attributes, options)
481
450
  end
482
451
 
452
+ # Get the path calculator for the supplied document.
453
+ #
454
+ # @example Get the path calculator.
455
+ # Proxy.path(document)
456
+ #
457
+ # @param [ Document ] document The document to calculate on.
458
+ #
459
+ # @return [ Mongoid::Atomic::Paths::Embedded::Many ]
460
+ # The embedded many atomic path calculator.
461
+ #
462
+ # @since 2.1.0
463
+ def path(document)
464
+ Mongoid::Atomic::Paths::Embedded::Many.new(document)
465
+ end
466
+
483
467
  # Tells the caller if this relation is one that stores the foreign
484
468
  # key on its own objects.
485
469
  #
@@ -492,6 +476,18 @@ module Mongoid # :nodoc:
492
476
  def stores_foreign_key?
493
477
  false
494
478
  end
479
+
480
+ # Get the valid options allowed with this relation.
481
+ #
482
+ # @example Get the valid options.
483
+ # Relation.valid_options
484
+ #
485
+ # @return [ Array<Symbol> ] The valid options.
486
+ #
487
+ # @since 2.1.0
488
+ def valid_options
489
+ [ :as, :cyclic, :order, :versioned ]
490
+ end
495
491
  end
496
492
  end
497
493
  end