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
@@ -6,41 +6,16 @@ module Mongoid # :nodoc:
6
6
  # behaviour or those proxies.
7
7
  class One < Proxy
8
8
 
9
- # Will load the target into an array if the target had not already been
10
- # loaded.
9
+ # Get all the documents in the relation that are loaded into memory.
11
10
  #
12
- # @example Load the relation into memory.
13
- # relation.load!
11
+ # @example Get the in memory documents.
12
+ # relation.in_memory
14
13
  #
15
- # @return [ One ] The relation.
14
+ # @return [ Array<Document> ] The documents in memory.
16
15
  #
17
- # @since 2.0.0.rc.5
18
- def load!(options = {})
19
- tap do |relation|
20
- unless relation.loaded?
21
- relation.bind(options)
22
- relation.loaded = true
23
- end
24
- end
25
- end
26
-
27
- # Substitutes the supplied target documents for the existing document
28
- # in the relation.
29
- #
30
- # @example Substitute the new document.
31
- # person.name.substitute(new_name)
32
- #
33
- # @param [ Document ] other A document to replace the target.
34
- #
35
- # @return [ Document, nil ] The relation or nil.
36
- #
37
- # @since 2.0.0.rc.1
38
- def substitute(new_target, options = {})
39
- old_target = target
40
- tap do |relation|
41
- relation.target = new_target
42
- new_target ? bind(options) : (unbind(old_target, options) and return nil)
43
- end
16
+ # @since 2.1.0
17
+ def in_memory
18
+ [ target ]
44
19
  end
45
20
  end
46
21
  end
@@ -0,0 +1,47 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # This module contains the validating logic for options passed to relation
6
+ # macros.
7
+ module Options
8
+ extend self
9
+
10
+ # These options are available to all relations.
11
+ COMMON = [
12
+ :class_name,
13
+ :extend,
14
+ :inverse_class_name,
15
+ :inverse_of,
16
+ :name,
17
+ :relation,
18
+ :validate
19
+ ]
20
+
21
+ # Determine if the provided options are valid for the relation.
22
+ #
23
+ # @example Check the options.
24
+ # Options.validate!(:name => :comments)
25
+ #
26
+ # @param [ Hash ] options The options to check.
27
+ #
28
+ # @raise [ ArgumentError ] If the options are invalid.
29
+ #
30
+ # @return [ true, false ] If the options are valid.
31
+ #
32
+ # @since 2.1.0
33
+ def validate!(options)
34
+ valid_options = options[:relation].valid_options.concat(COMMON)
35
+ options.keys.each do |key|
36
+ if !valid_options.include?(key)
37
+ raise Errors::InvalidOptions.new(
38
+ options[:name],
39
+ key,
40
+ valid_options
41
+ )
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -17,6 +17,8 @@ module Mongoid # :nodoc:
17
17
  # Backwards compatibility with Mongoid beta releases.
18
18
  delegate :klass, :to => :metadata
19
19
 
20
+ delegate :bind_one, :unbind_one, :to => :binding
21
+
20
22
  # Convenience for setting the target and the metadata properties since
21
23
  # all proxies will need to do this.
22
24
  #
@@ -28,14 +30,38 @@ module Mongoid # :nodoc:
28
30
  # @param [ Metadata ] metadata The relation's metadata.
29
31
  #
30
32
  # @since 2.0.0.rc.1
31
- def init(base, target, metadata, &block)
33
+ def init(base, target, metadata)
32
34
  @base, @target, @metadata = base, target, metadata
33
- block.call if block
34
- extend Module.new(&metadata.extension) if metadata.extension?
35
+ yield(self) if block_given?
36
+ extend metadata.extension if metadata.extension?
35
37
  end
36
38
 
37
39
  protected
38
40
 
41
+ # Is the current thread in binding mode?
42
+ #
43
+ # @example Is the current thread in binding mode?
44
+ # proxy.binding?
45
+ #
46
+ # @return [ true, false ] If the thread is binding.
47
+ #
48
+ # @since 2.1.0
49
+ def binding?
50
+ Threaded.binding?
51
+ end
52
+
53
+ # Is the current thread in building mode?
54
+ #
55
+ # @example Is the current thread in building mode?
56
+ # proxy.building?
57
+ #
58
+ # @return [ true, false ] If the thread is building.
59
+ #
60
+ # @since 2.1.0
61
+ def building?
62
+ Threaded.building?
63
+ end
64
+
39
65
  # Get the collection from the root of the hierarchy.
40
66
  #
41
67
  # @example Get the collection.
@@ -65,31 +91,6 @@ module Mongoid # :nodoc:
65
91
  type ? type.new : metadata.klass.new
66
92
  end
67
93
 
68
- # Determines if the target been loaded into memory or not.
69
- #
70
- # @example Is the proxy loaded?
71
- # proxy.loaded?
72
- #
73
- # @return [ true, false ] True if loaded, false if not.
74
- #
75
- # @since 2.0.0.rc.1
76
- def loaded?
77
- !!@loaded
78
- end
79
-
80
- # Takes the supplied documents and sets the metadata on them. Used when
81
- # creating new documents and adding them to the relation.
82
- #
83
- # @example Set the metadata.
84
- # proxy.characterize(addresses)
85
- #
86
- # @param [ Array<Document> ] documents The documents to set metadata on.
87
- #
88
- # @since 2.0.0.rc.4
89
- def characterize(documents)
90
- documents.each { |doc| characterize_one(doc) }
91
- end
92
-
93
94
  # Takes the supplied document and sets the metadata on it.
94
95
  #
95
96
  # @example Set the metadata.
@@ -36,12 +36,11 @@ module Mongoid #:nodoc:
36
36
  #
37
37
  # @since 2.0.2, batch-relational-insert
38
38
  def batched(&block)
39
- inserter = Thread.current[:mongoid_batch_insert] ||= Insert.new
39
+ inserter = Threaded.insert ||= Insert.new
40
40
  count_executions(&block)
41
41
  ensure
42
42
  if @executions.zero?
43
- Thread.current[:mongoid_batch_insert] = nil
44
- raise_mixed if embedded?
43
+ Threaded.insert = nil
45
44
  inserter.execute(collection)
46
45
  end
47
46
  end
@@ -9,27 +9,6 @@ module Mongoid # :nodoc:
9
9
  # collection.
10
10
  class In < Relations::One
11
11
 
12
- # Binds the base object to the inverse of the relation. This is so we
13
- # are referenced to the actual objects themselves and dont hit the
14
- # database twice when setting the relations up.
15
- #
16
- # This is called after first creating the relation, or if a new object
17
- # is set on the relation.
18
- #
19
- # @example Bind the relation.
20
- # game.person.bind
21
- #
22
- # @param [ Hash ] options The options to bind with.
23
- #
24
- # @option options [ true, false ] :binding Are we in build mode?
25
- # @option options [ true, false ] :continue Continue binding the
26
- # inverse?
27
- #
28
- # @since 2.0.0.rc.1
29
- def bind(options = {})
30
- binding.bind(options)
31
- end
32
-
33
12
  # Instantiate a new referenced_in relation.
34
13
  #
35
14
  # @example Create the new relation.
@@ -42,6 +21,7 @@ module Mongoid # :nodoc:
42
21
  def initialize(base, target, metadata)
43
22
  init(base, target, metadata) do
44
23
  characterize_one(target)
24
+ bind_one
45
25
  end
46
26
  end
47
27
 
@@ -57,37 +37,16 @@ module Mongoid # :nodoc:
57
37
  # @return [ In, nil ] The relation or nil.
58
38
  #
59
39
  # @since 2.0.0.rc.1
60
- def substitute(new_target, options = {})
61
- old_target = target
62
- tap do |relation|
63
- relation.target = new_target
64
- if new_target
65
- bind(options)
66
- else
67
- unbind(old_target, options)
68
- nil
69
- end
40
+ def substitute(replacement)
41
+ tap do |proxy|
42
+ proxy.unbind_one
43
+ proxy.target.delete if persistable?
44
+ return nil unless replacement
45
+ proxy.target = replacement
46
+ proxy.bind_one
70
47
  end
71
48
  end
72
49
 
73
- # Unbinds the base object to the inverse of the relation. This occurs
74
- # when setting a side of the relation to nil.
75
- #
76
- # @example Unbind the relation.
77
- # game.person.unbind
78
- #
79
- # @param [ Document, Array<Document> ] old_target The previous target.
80
- # @param [ Hash ] options The options to bind with.
81
- #
82
- # @option options [ true, false ] :binding Are we in build mode?
83
- # @option options [ true, false ] :continue Continue binding the
84
- # inverse?
85
- #
86
- # @since 2.0.0.rc.1
87
- def unbind(old_target, options = {})
88
- binding(old_target).unbind(options)
89
- end
90
-
91
50
  private
92
51
 
93
52
  # Instantiate the binding associated with this relation.
@@ -100,8 +59,20 @@ module Mongoid # :nodoc:
100
59
  # @return [ Binding ] The binding object.
101
60
  #
102
61
  # @since 2.0.0.rc.1
103
- def binding(new_target = nil)
104
- Bindings::Referenced::In.new(base, new_target || target, metadata)
62
+ def binding
63
+ Bindings::Referenced::In.new(base, target, metadata)
64
+ end
65
+
66
+ # Are we able to persist this relation?
67
+ #
68
+ # @example Can we persist the relation?
69
+ # relation.persistable?
70
+ #
71
+ # @return [ true, false ] If the relation is persistable.
72
+ #
73
+ # @since 2.1.0
74
+ def persistable?
75
+ target.persisted? && !binding? && !building?
105
76
  end
106
77
 
107
78
  class << self
@@ -119,8 +90,24 @@ module Mongoid # :nodoc:
119
90
  # @return [ Builder ] A new builder object.
120
91
  #
121
92
  # @since 2.0.0.rc.1
122
- def builder(meta, object)
123
- Builders::Referenced::In.new(meta, object)
93
+ def builder(meta, object, loading = false)
94
+ Builders::Referenced::In.new(meta, object, loading)
95
+ end
96
+
97
+ # Get the standard criteria used for querying this relation.
98
+ #
99
+ # @example Get the criteria.
100
+ # Proxy.criteria(meta, id, Model)
101
+ #
102
+ # @param [ Metadata ] metadata The metadata.
103
+ # @param [ Object ] object The value of the foreign key.
104
+ # @param [ Class ] type The optional type.
105
+ #
106
+ # @return [ Criteria ] The criteria.
107
+ #
108
+ # @since 2.1.0
109
+ def criteria(metadata, object, type = nil)
110
+ type.where(:_id => object)
124
111
  end
125
112
 
126
113
  # Returns true if the relation is an embedded one. In this case
@@ -197,6 +184,20 @@ module Mongoid # :nodoc:
197
184
  Builders::NestedAttributes::One.new(metadata, attributes, options)
198
185
  end
199
186
 
187
+ # Get the path calculator for the supplied document.
188
+ #
189
+ # @example Get the path calculator.
190
+ # Proxy.path(document)
191
+ #
192
+ # @param [ Document ] document The document to calculate on.
193
+ #
194
+ # @return [ Root ] The root atomic path calculator.
195
+ #
196
+ # @since 2.1.0
197
+ def path(document)
198
+ Mongoid::Atomic::Paths::Root.new(document)
199
+ end
200
+
200
201
  # Tells the caller if this relation is one that stores the foreign
201
202
  # key on its own objects.
202
203
  #
@@ -209,6 +210,18 @@ module Mongoid # :nodoc:
209
210
  def stores_foreign_key?
210
211
  true
211
212
  end
213
+
214
+ # Get the valid options allowed with this relation.
215
+ #
216
+ # @example Get the valid options.
217
+ # Relation.valid_options
218
+ #
219
+ # @return [ Array<Symbol> ] The valid options.
220
+ #
221
+ # @since 2.1.0
222
+ def valid_options
223
+ [ :autosave, :foreign_key, :index, :polymorphic ]
224
+ end
212
225
  end
213
226
  end
214
227
  end
@@ -8,6 +8,9 @@ module Mongoid #:nodoc:
8
8
  class Many < Relations::Many
9
9
  include Batch
10
10
 
11
+ delegate :count, :to => :criteria
12
+ delegate :first, :in_memory, :last, :reset, :uniq, :to => :target
13
+
11
14
  # Appends a document or array of documents to the relation. Will set
12
15
  # the parent and update the index in the process.
13
16
  #
@@ -21,43 +24,41 @@ module Mongoid #:nodoc:
21
24
  # person.posts.concat([ post_one, post_two ])
22
25
  #
23
26
  # @param [ Document, Array<Document> ] *args Any number of documents.
27
+ #
28
+ # @return [ Array<Document> ] The loaded docs.
29
+ #
30
+ # @since 2.0.0.beta.1
24
31
  def <<(*args)
25
- options = default_options(args)
26
32
  batched do
27
33
  args.flatten.each do |doc|
28
- return doc unless doc
29
- append(doc, options)
30
- doc.save if base.persisted? && !options[:binding]
34
+ next unless doc
35
+ append(doc)
36
+ doc.save if persistable?
31
37
  end
32
38
  end
33
39
  end
34
40
  alias :concat :<<
35
41
  alias :push :<<
36
42
 
37
- # Binds the base object to the inverse of the relation. This is so we
38
- # are referenced to the actual objects themselves and dont hit the
39
- # database twice when setting the relations up.
40
- #
41
- # This is called after first creating the relation, or if a new object
42
- # is set on the relation.
43
+ # Build a new document from the attributes and append it to this
44
+ # relation without saving.
43
45
  #
44
- # @example Bind the relation.
45
- # person.posts.bind
46
+ # @example Build a new document on the relation.
47
+ # person.posts.build(:title => "A new post")
46
48
  #
47
- # @param [ Hash ] options The options to bind with.
49
+ # @param [ Hash ] attributes The attributes of the new document.
50
+ # @param [ Class ] type The optional subclass to build.
48
51
  #
49
- # @option options [ true, false ] :binding Are we in build mode?
50
- # @option options [ true, false ] :continue Continue binding the
51
- # inverse?
52
+ # @return [ Document ] The new document.
52
53
  #
53
- # @since 2.0.0.rc.1
54
- def bind(options = {})
55
- binding.bind(options)
56
- target.map(&:save) if base.persisted? && !options[:binding]
54
+ # @since 2.0.0.beta.1
55
+ def build(attributes = {}, type = nil)
56
+ Factory.build(type || klass, attributes).tap do |doc|
57
+ append(doc)
58
+ yield(doc) if block_given?
59
+ end
57
60
  end
58
-
59
- alias :concat :<<
60
- alias :push :<<
61
+ alias :new :build
61
62
 
62
63
  # Clear the relation. Will delete the documents from the db if they are
63
64
  # already persisted.
@@ -66,24 +67,14 @@ module Mongoid #:nodoc:
66
67
  # person.posts.clear
67
68
  #
68
69
  # @return [ Many ] The relation emptied.
69
- def clear(options = {})
70
- load! and tap do |relation|
71
- relation.unbind(default_options(options))
72
- target.clear
73
- end
74
- end
75
-
76
- # Returns a count of the number of documents in the association that have
77
- # actually been persisted to the database.
78
- #
79
- # Use #size if you want the total number of documents in memory.
80
- #
81
- # @example Get the count of persisted documents.
82
- # person.posts.count
83
70
  #
84
- # @return [ Integer ] The total number of persisted documents.
85
- def count
86
- criteria.count
71
+ # @since 2.0.0.beta.1
72
+ def clear
73
+ criteria.delete_all
74
+ target.clear do |doc|
75
+ unbind_one(doc)
76
+ doc.destroyed = true
77
+ end
87
78
  end
88
79
 
89
80
  # Creates a new document on the references many relation. This will
@@ -96,6 +87,8 @@ module Mongoid #:nodoc:
96
87
  # @param [ Class ] type The optional type of document to create.
97
88
  #
98
89
  # @return [ Document ] The newly created document.
90
+ #
91
+ # @since 2.0.0.beta.1
99
92
  def create(attributes = nil, type = nil, &block)
100
93
  build(attributes, type, &block).tap do |doc|
101
94
  base.persisted? ? doc.save : raise_unsaved(doc)
@@ -115,12 +108,35 @@ module Mongoid #:nodoc:
115
108
  # @raise [ Errors::Validations ] If validation failed.
116
109
  #
117
110
  # @return [ Document ] The newly created document.
111
+ #
112
+ # @since 2.0.0.beta.1
118
113
  def create!(attributes = nil, type = nil, &block)
119
114
  build(attributes, type, &block).tap do |doc|
120
115
  base.persisted? ? doc.save! : raise_unsaved(doc)
121
116
  end
122
117
  end
123
118
 
119
+ # Delete the document from the relation. This will set the foreign key
120
+ # on the document to nil. If the dependent options on the relation are
121
+ # :delete or :destroy the appropriate removal will occur.
122
+ #
123
+ # @example Delete the document.
124
+ # person.posts.delete(post)
125
+ #
126
+ # @param [ Document ] document The document to remove.
127
+ #
128
+ # @return [ Document ] The matching document.
129
+ #
130
+ # @since 2.1.0
131
+ def delete(document)
132
+ target.delete(document) do |doc|
133
+ if doc
134
+ unbind_one(doc)
135
+ cascade!(doc)
136
+ end
137
+ end
138
+ end
139
+
124
140
  # Deletes all related documents from the database given the supplied
125
141
  # conditions.
126
142
  #
@@ -133,13 +149,10 @@ module Mongoid #:nodoc:
133
149
  # @param [ Hash ] conditions Optional conditions to delete with.
134
150
  #
135
151
  # @return [ Integer ] The number of documents deleted.
152
+ #
153
+ # @since 2.0.0.beta.1
136
154
  def delete_all(conditions = nil)
137
- raise_mixed if klass.embedded?
138
- selector = (conditions || {})[:conditions] || {}
139
- target.delete_if { |doc| doc.matches?(selector) }
140
- metadata.klass.delete_all(
141
- :conditions => criteria.selector.merge(selector)
142
- )
155
+ remove_all(conditions, :delete_all)
143
156
  end
144
157
 
145
158
  # Destroys all related documents from the database given the supplied
@@ -154,13 +167,27 @@ module Mongoid #:nodoc:
154
167
  # @param [ Hash ] conditions Optional conditions to destroy with.
155
168
  #
156
169
  # @return [ Integer ] The number of documents destroyd.
170
+ #
171
+ # @since 2.0.0.beta.1
157
172
  def destroy_all(conditions = nil)
158
- raise_mixed if klass.embedded?
159
- selector = (conditions || {})[:conditions] || {}
160
- target.delete_if { |doc| doc.matches?(selector) }
161
- metadata.klass.destroy_all(
162
- :conditions => criteria.selector.merge(selector)
163
- )
173
+ remove_all(conditions, :destroy_all)
174
+ end
175
+
176
+ # Iterate over each document in the relation and yield to the provided
177
+ # block.
178
+ #
179
+ # @note This will load the entire relation into memory.
180
+ #
181
+ # @example Iterate over the documents.
182
+ # person.posts.each do |post|
183
+ # post.save
184
+ # end
185
+ #
186
+ # @return [ Array<Document> ] The loaded docs.
187
+ #
188
+ # @since 2.1.0
189
+ def each
190
+ target.each { |doc| yield(doc) if block_given? }
164
191
  end
165
192
 
166
193
  # Find the matchind document on the association, either based on id or
@@ -186,6 +213,8 @@ module Mongoid #:nodoc:
186
213
  # @param [ Hash ] options The options to search with.
187
214
  #
188
215
  # @return [ Document, Criteria ] The matching document(s).
216
+ #
217
+ # @since 2.0.0.beta.1
189
218
  def find(*args)
190
219
  criteria.find(*args)
191
220
  end
@@ -199,26 +228,17 @@ module Mongoid #:nodoc:
199
228
  # @param [ Document ] base The document this relation hangs off of.
200
229
  # @param [ Array<Document> ] target The target of the relation.
201
230
  # @param [ Metadata ] metadata The relation's metadata.
202
- def initialize(base, target, metadata)
203
- init(base, target, metadata)
204
- end
205
-
206
- # Will load the target into an array if the target had not already been
207
- # loaded.
208
- #
209
- # @example Load the relation into memory.
210
- # relation.load!
211
231
  #
212
- # @return [ Many ] The relation.
213
- #
214
- # @since 2.0.0.rc.5
215
- def load!(options = {})
216
- raise_mixed if klass.embedded?
217
- tap do |relation|
218
- unless relation.loaded?
219
- relation.target = target.entries
220
- relation.bind(options)
221
- relation.loaded = true
232
+ # @since 2.0.0.beta.1
233
+ def initialize(base, target, metadata)
234
+ init(base, Targets::Enumerable.new(target), metadata) do |proxy|
235
+ raise_mixed if klass.embedded?
236
+ batched do
237
+ proxy.in_memory do |doc|
238
+ characterize_one(doc)
239
+ bind_one(doc)
240
+ doc.save if persistable?
241
+ end
222
242
  end
223
243
  end
224
244
  end
@@ -232,7 +252,10 @@ module Mongoid #:nodoc:
232
252
  #
233
253
  # @since 2.0.0.rc.1
234
254
  def nullify
235
- clear(:binding => true, :continue => true, :nullify => true)
255
+ criteria.update(metadata.foreign_key => nil)
256
+ target.clear do |doc|
257
+ unbind_one(doc)
258
+ end
236
259
  end
237
260
  alias :nullify_all :nullify
238
261
 
@@ -241,44 +264,18 @@ module Mongoid #:nodoc:
241
264
  # deletion.
242
265
  #
243
266
  # @example Replace the relation.
244
- # person.posts.substitute(new_name)
267
+ # person.posts.substitute([ new_post ])
245
268
  #
246
- # @param [ Array<Document> ] target The replacement target.
247
- # @param [ Hash ] options The options to bind with.
248
- #
249
- # @option options [ true, false ] :binding Are we in build mode?
250
- # @option options [ true, false ] :continue Continue binding the
251
- # inverse?
269
+ # @param [ Array<Document> ] replacement The replacement target.
252
270
  #
253
271
  # @return [ Many ] The relation.
254
272
  #
255
273
  # @since 2.0.0.rc.1
256
- def substitute(target, options = {})
257
- tap { target ? (@target = target.to_a; bind(options)) : (@target = unbind(options)) }
258
- end
259
-
260
- # Unbinds the base object to the inverse of the relation. This occurs
261
- # when setting a side of the relation to nil.
262
- #
263
- # Will delete the object if necessary.
264
- #
265
- # @example Unbind the target.
266
- # person.posts.unbind
267
- #
268
- # @param [ Hash ] options The options to bind with.
269
- #
270
- # @option options [ true, false ] :binding Are we in build mode?
271
- # @option options [ true, false ] :continue Continue binding the
272
- # inverse?
273
- #
274
- # @since 2.0.0.rc.1
275
- def unbind(options = {})
276
- binding.unbind(options)
277
- unless base.new_record?
278
- target.each(&:delete) unless options[:binding]
279
- target.each(&:save) if options[:nullify]
274
+ def substitute(replacement)
275
+ tap do |proxy|
276
+ proxy.clear
277
+ proxy.push(replacement) if replacement
280
278
  end
281
- []
282
279
  end
283
280
 
284
281
  private
@@ -292,11 +289,10 @@ module Mongoid #:nodoc:
292
289
  # @param [ Document ] document The document to append to the target.
293
290
  #
294
291
  # @since 2.0.0.rc.1
295
- def append(document, options = {})
296
- init_target if !initialized? && !loaded?
292
+ def append(document)
297
293
  target.push(document)
298
294
  characterize_one(document)
299
- binding.bind_one(document, options)
295
+ bind_one(document)
300
296
  end
301
297
 
302
298
  # Instantiate the binding associated with this relation.
@@ -309,8 +305,8 @@ module Mongoid #:nodoc:
309
305
  # @return [ Binding ] The binding.
310
306
  #
311
307
  # @since 2.0.0.rc.1
312
- def binding(new_target = nil)
313
- Bindings::Referenced::Many.new(base, new_target || target, metadata)
308
+ def binding
309
+ Bindings::Referenced::Many.new(base, target, metadata)
314
310
  end
315
311
 
316
312
  # Get the collection of the relation in question.
@@ -320,9 +316,9 @@ module Mongoid #:nodoc:
320
316
  #
321
317
  # @return [ Collection ] The collection of the relation.
322
318
  #
323
- # @since 2.0.2, batch-relational-insert
319
+ # @since 2.0.2
324
320
  def collection
325
- metadata.klass.collection
321
+ klass.collection
326
322
  end
327
323
 
328
324
  # Get the value for the foreign key in convertable or unconvertable
@@ -341,7 +337,7 @@ module Mongoid #:nodoc:
341
337
  if inverse.using_object_ids? || base.id.is_a?(BSON::ObjectId)
342
338
  base.id
343
339
  else
344
- Mongoid::Criterion::Unconvertable.new(base.id)
340
+ Criterion::Unconvertable.new(base.id)
345
341
  end
346
342
  end
347
343
 
@@ -352,38 +348,31 @@ module Mongoid #:nodoc:
352
348
  # relation.criteria
353
349
  #
354
350
  # @return [ Criteria ] A new criteria.
355
- def criteria
356
- raise_mixed if klass.embedded?
357
- metadata.klass.where(metadata.foreign_key => convertable)
358
- end
359
-
360
- # Tells if the target array been initialized.
361
351
  #
362
- # @example Is the target initialized?
363
- # relation.initialized?
364
- #
365
- # @return [ true, false ] If the target is an array.
366
- #
367
- # @since 2.0.0
368
- def initialized?
369
- !!@initialized
352
+ # @since 2.0.0.beta.1
353
+ def criteria
354
+ Many.criteria(metadata, convertable)
370
355
  end
371
356
 
372
- # Initializes the target of the proxy as an empty array instead of
373
- # hitting the database.
357
+ # Perform the necessary cascade operations for documents that just got
358
+ # deleted or nullified.
374
359
  #
375
- # @example Initialize the target.
376
- # relation.init_target
360
+ # @example Cascade the change.
361
+ # relation.cascade!(document)
377
362
  #
378
- # @raise [ Errors::MixedRelations ] If the class is embedded.
363
+ # @param [ Document ] document The document to cascade on.
379
364
  #
380
- # @return [ true ] Always true.
365
+ # @return [ true, false ] If the metadata is destructive.
381
366
  #
382
- # @since 2.0.0
383
- def init_target
384
- raise_mixed if klass.embedded?
385
- @target = []
386
- @initialized = true
367
+ # @since 2.1.0
368
+ def cascade!(document)
369
+ if persistable?
370
+ if metadata.destructive?
371
+ document.send(metadata.dependent)
372
+ else
373
+ document.save
374
+ end
375
+ end
387
376
  end
388
377
 
389
378
  # If the target array does not respond to the supplied method then try to
@@ -396,11 +385,53 @@ module Mongoid #:nodoc:
396
385
  # @param [ Proc ] block Optional block to pass.
397
386
  #
398
387
  # @return [ Criteria, Object ] A Criteria or return value from the target.
388
+ #
389
+ # @since 2.0.0.beta.1
399
390
  def method_missing(name, *args, &block)
400
- load!(:binding => true) and return super if [].respond_to?(name)
401
- klass = metadata.klass
402
- klass.send(:with_scope, criteria) do
403
- criteria.send(name, *args, &block)
391
+ if target.respond_to?(name)
392
+ target.send(name, *args, &block)
393
+ else
394
+ klass.send(:with_scope, criteria) do
395
+ criteria.send(name, *args, &block)
396
+ end
397
+ end
398
+ end
399
+
400
+ # Are we able to persist this relation?
401
+ #
402
+ # @example Can we persist the relation?
403
+ # relation.persistable?
404
+ #
405
+ # @return [ true, false ] If the relation is persistable.
406
+ #
407
+ # @since 2.1.0
408
+ def persistable?
409
+ base.persisted? && !binding?
410
+ end
411
+
412
+ # Deletes all related documents from the database given the supplied
413
+ # conditions.
414
+ #
415
+ # @example Delete all documents in the relation.
416
+ # person.posts.delete_all
417
+ #
418
+ # @example Conditonally delete all documents in the relation.
419
+ # person.posts.delete_all(:conditions => { :title => "Testing" })
420
+ #
421
+ # @param [ Hash ] conditions Optional conditions to delete with.
422
+ # @param [ Symbol ] The deletion method to call.
423
+ #
424
+ # @return [ Integer ] The number of documents deleted.
425
+ #
426
+ # @since 2.1.0
427
+ def remove_all(conditions = nil, method = :delete_all)
428
+ selector = (conditions || {})[:conditions] || {}
429
+ klass.send(method, :conditions => selector.merge!(criteria.selector)).tap do
430
+ target.delete_if do |doc|
431
+ if doc.matches?(selector)
432
+ unbind_one(doc) and true
433
+ end
434
+ end
404
435
  end
405
436
  end
406
437
 
@@ -419,8 +450,24 @@ module Mongoid #:nodoc:
419
450
  # @return [ Builder ] A new builder object.
420
451
  #
421
452
  # @since 2.0.0.rc.1
422
- def builder(meta, object)
423
- Builders::Referenced::Many.new(meta, object || [])
453
+ def builder(meta, object, loading = false)
454
+ Builders::Referenced::Many.new(meta, object || [], loading)
455
+ end
456
+
457
+ # Get the standard criteria used for querying this relation.
458
+ #
459
+ # @example Get the criteria.
460
+ # Proxy.criteria(meta, id, Model)
461
+ #
462
+ # @param [ Metadata ] metadata The metadata.
463
+ # @param [ Object ] object The value of the foreign key.
464
+ # @param [ Class ] type The optional type.
465
+ #
466
+ # @return [ Criteria ] The criteria.
467
+ #
468
+ # @since 2.1.0
469
+ def criteria(metadata, object, type = nil)
470
+ metadata.klass.where(metadata.foreign_key => object)
424
471
  end
425
472
 
426
473
  # Returns true if the relation is an embedded one. In this case
@@ -497,6 +544,20 @@ module Mongoid #:nodoc:
497
544
  Builders::NestedAttributes::Many.new(metadata, attributes, options)
498
545
  end
499
546
 
547
+ # Get the path calculator for the supplied document.
548
+ #
549
+ # @example Get the path calculator.
550
+ # Proxy.path(document)
551
+ #
552
+ # @param [ Document ] document The document to calculate on.
553
+ #
554
+ # @return [ Root ] The root atomic path calculator.
555
+ #
556
+ # @since 2.1.0
557
+ def path(document)
558
+ Mongoid::Atomic::Paths::Root.new(document)
559
+ end
560
+
500
561
  # Tells the caller if this relation is one that stores the foreign
501
562
  # key on its own objects.
502
563
  #
@@ -509,6 +570,18 @@ module Mongoid #:nodoc:
509
570
  def stores_foreign_key?
510
571
  false
511
572
  end
573
+
574
+ # Get the valid options allowed with this relation.
575
+ #
576
+ # @example Get the valid options.
577
+ # Relation.valid_options
578
+ #
579
+ # @return [ Array<Symbol> ] The valid options.
580
+ #
581
+ # @since 2.1.0
582
+ def valid_options
583
+ [ :as, :autosave, :dependent, :foreign_key, :order ]
584
+ end
512
585
  end
513
586
  end
514
587
  end