mongoid 2.0.0.beta.20 → 2.0.0.rc.1

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 (134) hide show
  1. data/README.rdoc +8 -0
  2. data/Rakefile +51 -0
  3. data/lib/config/locales/nl.yml +39 -0
  4. data/lib/config/locales/ro.yml +1 -1
  5. data/lib/mongoid.rb +17 -17
  6. data/lib/mongoid/atomicity.rb +54 -22
  7. data/lib/mongoid/attributes.rb +145 -125
  8. data/lib/mongoid/callbacks.rb +7 -2
  9. data/lib/mongoid/collection.rb +49 -32
  10. data/lib/mongoid/collections.rb +0 -1
  11. data/lib/mongoid/components.rb +34 -29
  12. data/lib/mongoid/config.rb +207 -193
  13. data/lib/mongoid/config/database.rb +167 -0
  14. data/lib/mongoid/contexts.rb +2 -5
  15. data/lib/mongoid/contexts/enumerable.rb +30 -4
  16. data/lib/mongoid/contexts/ids.rb +2 -2
  17. data/lib/mongoid/contexts/mongo.rb +30 -5
  18. data/lib/mongoid/copyable.rb +44 -0
  19. data/lib/mongoid/criteria.rb +110 -56
  20. data/lib/mongoid/criterion/creational.rb +34 -0
  21. data/lib/mongoid/criterion/destructive.rb +37 -0
  22. data/lib/mongoid/criterion/exclusion.rb +3 -1
  23. data/lib/mongoid/criterion/inclusion.rb +59 -64
  24. data/lib/mongoid/criterion/inspection.rb +22 -0
  25. data/lib/mongoid/criterion/optional.rb +42 -54
  26. data/lib/mongoid/criterion/selector.rb +9 -0
  27. data/lib/mongoid/default_scope.rb +28 -0
  28. data/lib/mongoid/deprecation.rb +5 -5
  29. data/lib/mongoid/dirty.rb +4 -5
  30. data/lib/mongoid/document.rb +161 -114
  31. data/lib/mongoid/extensions.rb +7 -11
  32. data/lib/mongoid/extensions/array/parentization.rb +2 -2
  33. data/lib/mongoid/extensions/date/conversions.rb +1 -1
  34. data/lib/mongoid/extensions/hash/conversions.rb +0 -23
  35. data/lib/mongoid/extensions/nil/collectionization.rb +12 -0
  36. data/lib/mongoid/extensions/object/reflections.rb +17 -0
  37. data/lib/mongoid/extensions/object/yoda.rb +27 -0
  38. data/lib/mongoid/extensions/string/conversions.rb +23 -4
  39. data/lib/mongoid/extensions/time_conversions.rb +4 -4
  40. data/lib/mongoid/field.rb +30 -19
  41. data/lib/mongoid/fields.rb +15 -5
  42. data/lib/mongoid/finders.rb +19 -11
  43. data/lib/mongoid/hierarchy.rb +34 -28
  44. data/lib/mongoid/identity.rb +62 -20
  45. data/lib/mongoid/inspection.rb +58 -0
  46. data/lib/mongoid/matchers.rb +20 -0
  47. data/lib/mongoid/multi_database.rb +11 -0
  48. data/lib/mongoid/nested_attributes.rb +41 -0
  49. data/lib/mongoid/paranoia.rb +3 -4
  50. data/lib/mongoid/paths.rb +1 -1
  51. data/lib/mongoid/persistence.rb +89 -90
  52. data/lib/mongoid/persistence/command.rb +20 -4
  53. data/lib/mongoid/persistence/insert.rb +13 -11
  54. data/lib/mongoid/persistence/insert_embedded.rb +8 -6
  55. data/lib/mongoid/persistence/remove.rb +6 -4
  56. data/lib/mongoid/persistence/remove_all.rb +6 -4
  57. data/lib/mongoid/persistence/remove_embedded.rb +8 -6
  58. data/lib/mongoid/persistence/update.rb +12 -10
  59. data/lib/mongoid/railtie.rb +2 -2
  60. data/lib/mongoid/railties/database.rake +10 -9
  61. data/lib/mongoid/relations.rb +104 -0
  62. data/lib/mongoid/relations/accessors.rb +154 -0
  63. data/lib/mongoid/relations/auto_save.rb +34 -0
  64. data/lib/mongoid/relations/binding.rb +24 -0
  65. data/lib/mongoid/relations/bindings.rb +9 -0
  66. data/lib/mongoid/relations/bindings/embedded/in.rb +77 -0
  67. data/lib/mongoid/relations/bindings/embedded/many.rb +93 -0
  68. data/lib/mongoid/relations/bindings/embedded/one.rb +65 -0
  69. data/lib/mongoid/relations/bindings/referenced/in.rb +78 -0
  70. data/lib/mongoid/relations/bindings/referenced/many.rb +93 -0
  71. data/lib/mongoid/relations/bindings/referenced/many_to_many.rb +94 -0
  72. data/lib/mongoid/relations/bindings/referenced/one.rb +63 -0
  73. data/lib/mongoid/relations/builder.rb +41 -0
  74. data/lib/mongoid/relations/builders.rb +79 -0
  75. data/lib/mongoid/relations/builders/embedded/in.rb +25 -0
  76. data/lib/mongoid/relations/builders/embedded/many.rb +32 -0
  77. data/lib/mongoid/relations/builders/embedded/one.rb +26 -0
  78. data/lib/mongoid/relations/builders/nested_attributes/many.rb +116 -0
  79. data/lib/mongoid/relations/builders/nested_attributes/one.rb +135 -0
  80. data/lib/mongoid/relations/builders/referenced/in.rb +32 -0
  81. data/lib/mongoid/relations/builders/referenced/many.rb +26 -0
  82. data/lib/mongoid/relations/builders/referenced/many_to_many.rb +29 -0
  83. data/lib/mongoid/relations/builders/referenced/one.rb +30 -0
  84. data/lib/mongoid/relations/cascading.rb +55 -0
  85. data/lib/mongoid/relations/cascading/delete.rb +19 -0
  86. data/lib/mongoid/relations/cascading/destroy.rb +19 -0
  87. data/lib/mongoid/relations/cascading/nullify.rb +18 -0
  88. data/lib/mongoid/relations/cascading/strategy.rb +26 -0
  89. data/lib/mongoid/relations/cyclic.rb +97 -0
  90. data/lib/mongoid/relations/embedded/in.rb +172 -0
  91. data/lib/mongoid/relations/embedded/many.rb +450 -0
  92. data/lib/mongoid/relations/embedded/one.rb +169 -0
  93. data/lib/mongoid/relations/macros.rb +302 -0
  94. data/lib/mongoid/relations/many.rb +185 -0
  95. data/lib/mongoid/relations/metadata.rb +529 -0
  96. data/lib/mongoid/relations/nested_builder.rb +52 -0
  97. data/lib/mongoid/relations/one.rb +29 -0
  98. data/lib/mongoid/relations/polymorphic.rb +54 -0
  99. data/lib/mongoid/relations/proxy.rb +122 -0
  100. data/lib/mongoid/relations/referenced/in.rb +214 -0
  101. data/lib/mongoid/relations/referenced/many.rb +358 -0
  102. data/lib/mongoid/relations/referenced/many_to_many.rb +379 -0
  103. data/lib/mongoid/relations/referenced/one.rb +204 -0
  104. data/lib/mongoid/relations/reflections.rb +45 -0
  105. data/lib/mongoid/safe.rb +11 -1
  106. data/lib/mongoid/safety.rb +122 -97
  107. data/lib/mongoid/scope.rb +14 -9
  108. data/lib/mongoid/state.rb +37 -3
  109. data/lib/mongoid/timestamps.rb +11 -0
  110. data/lib/mongoid/validations.rb +42 -3
  111. data/lib/mongoid/validations/associated.rb +8 -5
  112. data/lib/mongoid/validations/uniqueness.rb +23 -2
  113. data/lib/mongoid/version.rb +1 -1
  114. data/lib/mongoid/versioning.rb +25 -16
  115. data/lib/rails/generators/mongoid/model/templates/model.rb +3 -1
  116. metadata +95 -80
  117. data/lib/mongoid/associations.rb +0 -364
  118. data/lib/mongoid/associations/embedded_in.rb +0 -74
  119. data/lib/mongoid/associations/embeds_many.rb +0 -299
  120. data/lib/mongoid/associations/embeds_one.rb +0 -111
  121. data/lib/mongoid/associations/foreign_key.rb +0 -35
  122. data/lib/mongoid/associations/meta_data.rb +0 -38
  123. data/lib/mongoid/associations/options.rb +0 -78
  124. data/lib/mongoid/associations/proxy.rb +0 -60
  125. data/lib/mongoid/associations/referenced_in.rb +0 -70
  126. data/lib/mongoid/associations/references_many.rb +0 -254
  127. data/lib/mongoid/associations/references_many_as_array.rb +0 -128
  128. data/lib/mongoid/associations/references_one.rb +0 -104
  129. data/lib/mongoid/extensions/array/accessors.rb +0 -17
  130. data/lib/mongoid/extensions/array/assimilation.rb +0 -26
  131. data/lib/mongoid/extensions/hash/accessors.rb +0 -42
  132. data/lib/mongoid/extensions/hash/assimilation.rb +0 -40
  133. data/lib/mongoid/extensions/nil/assimilation.rb +0 -17
  134. data/lib/mongoid/memoization.rb +0 -33
@@ -0,0 +1,55 @@
1
+ # encoding: utf-8
2
+ require "mongoid/relations/cascading/strategy"
3
+ require "mongoid/relations/cascading/delete"
4
+ require "mongoid/relations/cascading/destroy"
5
+ require "mongoid/relations/cascading/nullify"
6
+
7
+ module Mongoid # :nodoc:
8
+ module Relations #:nodoc:
9
+
10
+ # This module defines the behaviour for setting up cascading deletes and
11
+ # nullifies for relations, and how to delegate to the approriate strategy.
12
+ module Cascading
13
+ extend ActiveSupport::Concern
14
+
15
+ included do
16
+ class_inheritable_accessor :cascades
17
+ self.cascades = []
18
+ delegate :cascades, :to => "self.class"
19
+ end
20
+
21
+ # Perform all cascading deletes, destroys, or nullifies. Will delegate to
22
+ # the appropriate strategy to perform the operation.
23
+ #
24
+ # @example Execute cascades.
25
+ # document.cascade!
26
+ #
27
+ # @since 2.0.0.rc.1
28
+ def cascade!
29
+ cascades.each do |name|
30
+ metadata = relations[name]
31
+ strategy = metadata.cascade_strategy
32
+ strategy.new(self, metadata).cascade
33
+ end
34
+ end
35
+
36
+ module ClassMethods #:nodoc:
37
+
38
+ # Attempt to add the cascading information for the document to know how
39
+ # to handle associated documents on a removal.
40
+ #
41
+ # @example Set up cascading information
42
+ # Movie.cascade(metadata)
43
+ #
44
+ # @param [ Metadata ] metadata The metadata for the relation.
45
+ #
46
+ # @return [ Class ] The class of the document.
47
+ #
48
+ # @since 2.0.0.rc.1
49
+ def cascade(metadata)
50
+ tap { cascades << metadata.name.to_s if metadata.dependent? }
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+ module Cascading #:nodoc:
5
+ class Delete < Strategy
6
+
7
+ # Execute the cascading deletion for the relation if it already exists.
8
+ # This should be optimized in the future potentially not to load all
9
+ # objects from the db.
10
+ #
11
+ # @example Perform the cascading delete.
12
+ # strategy.cascade
13
+ def cascade
14
+ relation.to_a.each { |doc| doc.delete } if relation
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+ module Cascading #:nodoc:
5
+ class Destroy < Strategy
6
+
7
+ # Execute the cascading deletion for the relation if it already exists.
8
+ # This should be optimized in the future potentially not to load all
9
+ # objects from the db.
10
+ #
11
+ # @example Perform the cascading destroy.
12
+ # strategy.cascade
13
+ def cascade
14
+ relation.to_a.each { |doc| doc.destroy } if relation
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+ module Cascading #:nodoc:
5
+ class Nullify < Strategy
6
+
7
+ # This cascade does not delete the referenced relations, but instead
8
+ # sets the foreign key values to nil.
9
+ #
10
+ # @example Nullify the reference.
11
+ # strategy.cascade
12
+ def cascade
13
+ relation.nullify
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+ module Cascading #:nodoc:
5
+ class Strategy
6
+
7
+ attr_accessor :document, :relation, :metadata
8
+
9
+ # Initialize the new cascade strategy, which will set up the relation
10
+ # and the metadata.
11
+ #
12
+ # @example Instantiate the strategy
13
+ # Strategy.new(document, metadata)
14
+ #
15
+ # @param [ Document ] document The document to cascade from.
16
+ # @param [ Metadata ] metadata The relation's metadata.
17
+ #
18
+ # @return [ Strategy ] The new strategy.
19
+ def initialize(document, metadata)
20
+ @document, @metadata = document, metadata
21
+ @relation = document.send(metadata.name)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,97 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+
5
+ # This module provides convenience macros for using cyclic embedded
6
+ # relations.
7
+ module Cyclic
8
+ extend ActiveSupport::Concern
9
+
10
+ module ClassMethods #:nodoc:
11
+
12
+ # Create a cyclic embedded relation that creates a tree hierarchy for
13
+ # the document and many embedded child documents.
14
+ #
15
+ # @example Set up a recursive embeds many.
16
+ #
17
+ # class Role
18
+ # include Mongoid::Document
19
+ # recursively_embeds_many
20
+ # end
21
+ #
22
+ # @example The previous example is a shorcut for this.
23
+ #
24
+ # class Role
25
+ # include Mongoid::Document
26
+ # embeds_many :child_roles, :class_name => "Role", :cyclic => true
27
+ # embedded_in :parent_role, :class_name => "Role", :cyclic => true
28
+ # end
29
+ #
30
+ # This provides the default nomenclature for accessing a parent document
31
+ # or its children.
32
+ #
33
+ # @since 2.0.0.rc.1
34
+ def recursively_embeds_many
35
+ embeds_many cyclic_child_name, :class_name => self.name, :cyclic => true
36
+ embedded_in cyclic_parent_name, :class_name => self.name, :cyclic => true
37
+ end
38
+
39
+ # Create a cyclic embedded relation that creates a single self
40
+ # referencing relationship for a parent and a single child.
41
+ #
42
+ # @example Set up a recursive embeds one.
43
+ #
44
+ # class Role
45
+ # include Mongoid::Document
46
+ # recursively_embeds_one
47
+ # end
48
+ #
49
+ # @example The previous example is a shorcut for this.
50
+ #
51
+ # class Role
52
+ # include Mongoid::Document
53
+ # embeds_one :child_role, :class_name => "Role", :cyclic => true
54
+ # embedded_in :parent_role, :class_name => "Role", :cyclic => true
55
+ # end
56
+ #
57
+ # This provides the default nomenclature for accessing a parent document
58
+ # or its children.
59
+ #
60
+ # @since 2.0.0.rc.1
61
+ def recursively_embeds_one
62
+ embeds_one cyclic_child_name(false), :class_name => self.name, :cyclic => true
63
+ embedded_in cyclic_parent_name, :class_name => self.name, :cyclic => true
64
+ end
65
+
66
+ private
67
+
68
+ # Determines the parent name given the class.
69
+ #
70
+ # @example Determine the parent name.
71
+ # Role.cyclic_parent_name
72
+ #
73
+ # @return [ String ] "parent_" plus the class name underscored.
74
+ #
75
+ # @since 2.0.0.rc.1
76
+ def cyclic_parent_name
77
+ ("parent_" << self.name.underscore.singularize).to_sym
78
+ end
79
+
80
+ # Determines the child name given the class.
81
+ #
82
+ # @example Determine the child name.
83
+ # Role.cyclic_child_name
84
+ #
85
+ # @param [ true, false ] many Is the a many relation?
86
+ #
87
+ # @return [ String ] "child_" plus the class name underscored in
88
+ # singular or plural form.
89
+ #
90
+ # @since 2.0.0.rc.1
91
+ def cyclic_child_name(many = true)
92
+ ("child_" << self.name.underscore.send(many ? :pluralize : :singularize)).to_sym
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,172 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+ module Embedded
5
+
6
+ # This class defines the behaviour necessary to handle relations that are
7
+ # embedded within another relation, either as a single document or
8
+ # multiple documents.
9
+ class In < Relations::One
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 ] :building 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[:building]
31
+ end
32
+
33
+ # Instantiate a new embedded_in relation.
34
+ #
35
+ # @example Create the new relation.
36
+ # Embedded::In.new(name, person, metadata)
37
+ #
38
+ # @param [ Document ] base The document the relation hangs off of.
39
+ # @param [ Document ] target The target (parent) of the relation.
40
+ # @param [ Metadata ] metadata The relation metadata.
41
+ #
42
+ # @return [ In ] The proxy.
43
+ def initialize(base, target, metadata)
44
+ init(base, target, metadata) do
45
+ base.parentize(target)
46
+ end
47
+ end
48
+
49
+ # Unbinds the base object to the inverse of the relation. This occurs
50
+ # when setting a side of the relation to nil.
51
+ #
52
+ # Will delete the object if necessary.
53
+ #
54
+ # @example Unbind the relation.
55
+ # name.person.unbind(:continue => false)
56
+ #
57
+ # @param [ Proxy ] old_target The previous target of the relation.
58
+ # @param [ Hash ] options The options to bind with.
59
+ #
60
+ # @option options [ true, false ] :building Are we in build mode?
61
+ # @option options [ true, false ] :continue Continue binding the
62
+ # inverse?
63
+ #
64
+ # @since 2.0.0.rc.1
65
+ def unbind(old_target, options = {})
66
+ binding(old_target).unbind(options)
67
+ base.delete if old_target.persisted? && !base.destroyed?
68
+ end
69
+
70
+ private
71
+
72
+ # Instantiate the binding associated with this relation.
73
+ #
74
+ # @example Get the binding.
75
+ # binding([ address ])
76
+ #
77
+ # @param [ Proxy ] new_target The new documents to bind with.
78
+ #
79
+ # @return [ Binding ] A binding object.
80
+ #
81
+ # @since 2.0.0.rc.1
82
+ def binding(new_target = nil)
83
+ Bindings::Embedded::In.new(base, new_target || target, metadata)
84
+ end
85
+
86
+ class << self
87
+
88
+ # Return the builder that is responsible for generating the documents
89
+ # that will be used by this relation.
90
+ #
91
+ # @example Get the builder.
92
+ # Embedded::In.builder(meta, object, person)
93
+ #
94
+ # @param [ Metadata ] meta The metadata of the relation.
95
+ # @param [ Document, Hash ] object A document or attributes to build with.
96
+ #
97
+ # @return [ Builder ] A newly instantiated builder object.
98
+ #
99
+ # @since 2.0.0.rc.1
100
+ def builder(meta, object)
101
+ Builders::Embedded::In.new(meta, object)
102
+ end
103
+
104
+ # Returns true if the relation is an embedded one. In this case
105
+ # always true.
106
+ #
107
+ # @example Is this relation embedded?
108
+ # Embedded::In.embedded?
109
+ #
110
+ # @return [ true ] true.
111
+ #
112
+ # @since 2.0.0.rc.1
113
+ def embedded?
114
+ true
115
+ end
116
+
117
+ # Returns the macro for this relation. Used mostly as a helper in
118
+ # reflection.
119
+ #
120
+ # @example Get the macro.
121
+ # Mongoid::Relations::Embedded::In.macro
122
+ #
123
+ # @return [ Symbol ] :embedded_in.
124
+ #
125
+ # @since 2.0.0.rc.1
126
+ def macro
127
+ :embedded_in
128
+ end
129
+
130
+ # Return the nested builder that is responsible for generating
131
+ # the documents that will be used by this relation.
132
+ #
133
+ # @example Get the builder.
134
+ # NestedAttributes::One.builder(attributes, options)
135
+ #
136
+ # @param [ Metadata ] metadata The relation metadata.
137
+ # @param [ Hash ] attributes The attributes to build with.
138
+ # @param [ Hash ] options The options for the builder.
139
+ #
140
+ # @option options [ true, false ] :allow_destroy Can documents be
141
+ # deleted?
142
+ # @option options [ Integer ] :limit Max number of documents to
143
+ # create at once.
144
+ # @option options [ Proc, Symbol ] :reject_if If documents match this
145
+ # option then they are ignored.
146
+ # @option options [ true, false ] :update_only Only existing documents
147
+ # can be modified.
148
+ #
149
+ # @return [ Builder ] A newly instantiated nested builder object.
150
+ #
151
+ # @since 2.0.0.rc.1
152
+ def nested_builder(metadata, attributes, options)
153
+ Builders::NestedAttributes::One.new(metadata, attributes, options)
154
+ end
155
+
156
+ # Tells the caller if this relation is one that stores the foreign
157
+ # key on its own objects.
158
+ #
159
+ # @example Does this relation store a foreign key?
160
+ # Embedded::In.stores_foreign_key?
161
+ #
162
+ # @return [ false ] false.
163
+ #
164
+ # @since 2.0.0.rc.1
165
+ def stores_foreign_key?
166
+ false
167
+ end
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,450 @@
1
+ # encoding: utf-8
2
+ module Mongoid # :nodoc:
3
+ module Relations #:nodoc:
4
+ module Embedded #:nodoc:
5
+
6
+ # This class handles the behaviour for a document that embeds many other
7
+ # documents within in it as an array.
8
+ class Many < Relations::Many
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.addresses.bind(:continue => true)
19
+ #
20
+ # @param [ Hash ] options The options to bind with.
21
+ #
22
+ # @option options [ true, false ] :building 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.each(&:save) if base.persisted? && !options[:building]
30
+ end
31
+
32
+ # Bind the inverse relation between a single document in this proxy
33
+ # instead of the entire target.
34
+ #
35
+ # Used when appending to the target instead of setting the entire
36
+ # thing.
37
+ #
38
+ # @example Bind a single document.
39
+ # person.addressses.bind_one(address)
40
+ #
41
+ # @param [ Document ] document The document to bind.
42
+ #
43
+ # @since 2.0.0.rc.1
44
+ def bind_one(document, options = {})
45
+ binding.bind_one(document, options)
46
+ end
47
+
48
+ # Clear the relation. Will delete the documents from the db if they are
49
+ # already persisted.
50
+ #
51
+ # @example Clear the relation.
52
+ # person.addresses.clear
53
+ #
54
+ # @return [ Many ] The empty relation.
55
+ def clear
56
+ substitute(nil)
57
+ end
58
+
59
+ # Create a new document in the relation. This is essentially the same
60
+ # as doing a #build then #save on the new document.
61
+ #
62
+ # @example Create a new document in the relation.
63
+ # person.movies.create(:name => "Bozo")
64
+ #
65
+ # @param [ Hash ] attributes The attributes to build the document with.
66
+ # @param [ Class ] type Optional class to create the document with.
67
+ #
68
+ # @return [ Document ] The newly created document.
69
+ def create(attributes = {}, type = nil)
70
+ build(attributes, type).tap(&:save)
71
+ end
72
+
73
+ # Create a new document in the relation. This is essentially the same
74
+ # as doing a #build then #save on the new document. If validation
75
+ # failed on the document an error will get raised.
76
+ #
77
+ # @example Create the document.
78
+ # person.addresses.create!(:street => "Unter der Linden")</tt>
79
+ #
80
+ # @param [ Hash ] attributes The attributes to build the document with.
81
+ # @param [ Class ] type Optional class to create the document with.
82
+ #
83
+ # @raise [ Errors::Validations ] If a validation error occured.
84
+ #
85
+ # @return [ Document ] The newly created document.
86
+ def create!(attributes = {}, type = nil)
87
+ build(attributes, type).tap(&:save!)
88
+ end
89
+
90
+ # Delete the supplied document from the target. This method is proxied
91
+ # in order to reindex the array after the operation occurs.
92
+ #
93
+ # @example Delete the document from the relation.
94
+ # perosn.addresses.delete(address)
95
+ #
96
+ # @param [ Document ] document The document to be deleted.
97
+ #
98
+ # @return [ Document, nil ] The deleted document or nil if nothing deleted.
99
+ #
100
+ # @since 2.0.0.rc.1
101
+ def delete(document)
102
+ target.delete(document).tap { reindex }
103
+ end
104
+
105
+ # Delete all the documents in the association without running callbacks.
106
+ #
107
+ # @example Delete all documents from the relation.
108
+ # person.addresses.delete_all
109
+ #
110
+ # @example Conditionally delete documents from the relation.
111
+ # person.addresses.delete_all(:conditions => { :street => "Bond" })
112
+ #
113
+ # @param [ Hash ] conditions Conditions on which documents to delete.
114
+ #
115
+ # @return [ Integer ] The number of documents deleted.
116
+ def delete_all(conditions = {})
117
+ remove_all(conditions, false)
118
+ end
119
+
120
+ # Destroy all the documents in the association whilst running callbacks.
121
+ #
122
+ # @example Destroy all documents from the relation.
123
+ # person.addresses.destroy_all
124
+ #
125
+ # @example Conditionally destroy documents from the relation.
126
+ # person.addresses.destroy_all(:conditions => { :street => "Bond" })
127
+ #
128
+ # @param [ Hash ] conditions Conditions on which documents to destroy.
129
+ #
130
+ # @return [ Integer ] The number of documents destroyed.
131
+ def destroy_all(conditions = {})
132
+ remove_all(conditions, true)
133
+ end
134
+
135
+ # Finds a document in this association through several different
136
+ # methods.
137
+ #
138
+ # @example Find a document by its id.
139
+ # person.addresses.find(BSON::ObjectId.new)
140
+ #
141
+ # @example Find documents for multiple ids.
142
+ # person.addresses.find([ BSON::ObjectId.new, BSON::ObjectId.new ])
143
+ #
144
+ # @example Find documents based on conditions.
145
+ # person.addresses.find(:all, :conditions => { :number => 10 })
146
+ # person.addresses.find(:first, :conditions => { :number => 10 })
147
+ # person.addresses.find(:last, :conditions => { :number => 10 })
148
+ #
149
+ # @param [ Array<Object> ] args Various arguments.
150
+ #
151
+ # @return [ Array<Document>, Document ] A single or multiple documents.
152
+ def find(*args)
153
+ type, criteria = Criteria.parse!(self, true, *args)
154
+ case type
155
+ when :first then return criteria.one
156
+ when :last then return criteria.last
157
+ else
158
+ criteria.tap do |crit|
159
+ crit.documents = target if crit.is_a?(Criteria)
160
+ end
161
+ end
162
+ end
163
+
164
+ # Instantiate a new embeds_many relation.
165
+ #
166
+ # @example Create the new relation.
167
+ # Many.new(person, addresses, metadata)
168
+ #
169
+ # @param [ Document ] base The document this relation hangs off of.
170
+ # @param [ Array<Document> ] target The child documents of the relation.
171
+ # @param [ Metadata ] metadata The relation's metadata
172
+ #
173
+ # @return [ Many ] The proxy.
174
+ def initialize(base, target, metadata)
175
+ init(base, target, metadata) do
176
+ target.each_with_index do |doc, index|
177
+ doc.parentize(base)
178
+ doc._index = index
179
+ end
180
+ end
181
+ end
182
+
183
+ # Paginate the association. Will create a new criteria, set the documents
184
+ # on it and execute in an enumerable context.
185
+ #
186
+ # @example Paginate the relation.
187
+ # person.addresses.paginate(:page => 1, :per_page => 20)
188
+ #
189
+ # @param [ Hash ] options The pagination options.
190
+ #
191
+ # @option options [ Integer ] :page The page number.
192
+ # @option options [ Integer ] :per_page The number on each page.
193
+ #
194
+ # @return [ WillPaginate::Collection ] The paginated documents.
195
+ def paginate(options)
196
+ criteria = Mongoid::Criteria.translate(metadata.klass, true, options)
197
+ criteria.documents = target
198
+ criteria.paginate(options)
199
+ end
200
+
201
+ # Substitutes the supplied target documents for the existing documents
202
+ # in the relation.
203
+ #
204
+ # @example Substitute the relation's target.
205
+ # person.addresses.substitute([ address ])
206
+ #
207
+ # @param [ Array<Document> ] new_target The replacement array.
208
+ # @param [ true, false ] building Are we in build mode?
209
+ #
210
+ # @return [ Many ] The proxied relation.
211
+ #
212
+ # @since 2.0.0.rc.1
213
+ def substitute(new_target, options = {})
214
+ old_target = target
215
+ tap do |relation|
216
+ relation.target = new_target || []
217
+ if !new_target.blank?
218
+ unbind(old_target, options)
219
+ bind(options)
220
+ else
221
+ unbind(old_target, options)
222
+ end
223
+ end
224
+ end
225
+
226
+ # Get this relation as as its representation in the database.
227
+ #
228
+ # @example Convert the relation to an attributes hash.
229
+ # person.addresses.to_hash
230
+ #
231
+ # @return [ Array<Hash> ] The relation as stored in the db.
232
+ #
233
+ # @since 2.0.0.rc.1
234
+ def to_hash
235
+ target.inject([]) do |attributes, doc|
236
+ attributes.tap do |attr|
237
+ attr << doc.to_hash
238
+ end
239
+ end
240
+ end
241
+
242
+ # Unbind the inverse relation from this set of documents. Used when the
243
+ # entire proxy has been cleared, set to nil or empty, or replaced.
244
+ #
245
+ # @example Unbind the relation.
246
+ # person.addresses.unbind(target, :continue => false)
247
+ #
248
+ # @param [ Array<Document> ] old_target The relations previous target.
249
+ # @param [ Hash ] options The options to bind with.
250
+ #
251
+ # @option options [ true, false ] :building Are we in build mode?
252
+ # @option options [ true, false ] :continue Continue binding the
253
+ # inverse?
254
+ #
255
+ # @since 2.0.0.rc.1
256
+ def unbind(old_target, options = {})
257
+ binding(old_target).unbind(options)
258
+ if base.persisted?
259
+ old_target.each do |doc|
260
+ doc.delete unless doc.destroyed?
261
+ end
262
+ end
263
+ end
264
+
265
+ private
266
+
267
+ # Appends the document to the target array, updating the index on the
268
+ # document at the same time.
269
+ #
270
+ # @example Append to the document.
271
+ # relation.append(document)
272
+ #
273
+ # @param [ Document ] document The document to append to the target.
274
+ #
275
+ # @since 2.0.0.rc.1
276
+ def append(document, options = {})
277
+ target << document
278
+ metadatafy(document)
279
+ bind_one(document, options)
280
+ document._index = target.size - 1
281
+ end
282
+
283
+ # Instantiate the binding associated with this relation.
284
+ #
285
+ # @example Create the binding.
286
+ # relation.binding([ address ])
287
+ #
288
+ # @param [ Array<Document> ] new_target The new documents to bind with.
289
+ #
290
+ # @return [ Binding ] The many binding.
291
+ #
292
+ # @since 2.0.0.rc.1
293
+ def binding(new_target = nil)
294
+ Bindings::Embedded::Many.new(base, new_target || target, metadata)
295
+ end
296
+
297
+ # Returns the criteria object for the target class with its documents set
298
+ # to target.
299
+ #
300
+ # @example Get a criteria for the relation.
301
+ # relation.criteria
302
+ #
303
+ # @return [ Criteria ] A new criteria.
304
+ def criteria
305
+ metadata.klass.criteria(true).tap do |criterion|
306
+ criterion.documents = target
307
+ end
308
+ end
309
+
310
+ # If the target array does not respond to the supplied method then try to
311
+ # find a named scope or criteria on the class and send the call there.
312
+ #
313
+ # If the method exists on the array, use the default proxy behavior.
314
+ #
315
+ # @param [ Symbol, String ] name The name of the method.
316
+ # @param [ Array ] args The method args
317
+ # @param [ Proc ] block Optional block to pass.
318
+ #
319
+ # @return [ Criteria, Object ] A Criteria or return value from the target.
320
+ def method_missing(name, *args, &block)
321
+ return super if target.respond_to?(name)
322
+ klass = metadata.klass
323
+ klass.send(:with_scope, criteria) do
324
+ klass.send(name, *args)
325
+ end
326
+ end
327
+
328
+ # Reindex all the target elements. This is useful when performing
329
+ # operations on the proxied target directly and the indices need to
330
+ # match that on the database side.
331
+ #
332
+ # @example Reindex the relation.
333
+ # person.addresses.reindex
334
+ #
335
+ # @since 2.0.0.rc.1
336
+ def reindex
337
+ target.each_with_index do |doc, index|
338
+ doc._index = index
339
+ end
340
+ end
341
+
342
+ # Remove all documents from the relation, either with a delete or a
343
+ # destroy depending on what this was called through.
344
+ #
345
+ # @example Destroy documents from the relation.
346
+ # relation.remove_all(:conditions => { :num => 1 }, true)
347
+ #
348
+ # @param [ Hash ] conditions Conditions to filter by.
349
+ # @param [ true, false ] destroy If true then destroy, else delete.
350
+ #
351
+ # @return [ Integer ] The number of documents removed.
352
+ def remove_all(conditions = {}, destroy = false)
353
+ criteria = find(conditions || {})
354
+ criteria.size.tap do
355
+ criteria.each do |doc|
356
+ target.delete(doc)
357
+ destroy ? doc.destroy : doc.delete
358
+ end
359
+ reindex
360
+ end
361
+ end
362
+
363
+ class << self
364
+
365
+ # Return the builder that is responsible for generating the documents
366
+ # that will be used by this relation.
367
+ #
368
+ # @example Get the builder.
369
+ # Embedded::Many.builder(meta, object)
370
+ #
371
+ # @param [ Metadata ] meta The metadata of the relation.
372
+ # @param [ Document, Hash ] object A document or attributes to build
373
+ # with.
374
+ #
375
+ # @return [ Builder ] A newly instantiated builder object.
376
+ #
377
+ # @since 2.0.0.rc.1
378
+ def builder(meta, object)
379
+ Builders::Embedded::Many.new(meta, object)
380
+ end
381
+
382
+ # Returns true if the relation is an embedded one. In this case
383
+ # always true.
384
+ #
385
+ # @example Is the relation embedded?
386
+ # Embedded::Many.embedded?
387
+ #
388
+ # @return [ true ] true.
389
+ #
390
+ # @since 2.0.0.rc.1
391
+ def embedded?
392
+ true
393
+ end
394
+
395
+ # Returns the macro for this relation. Used mostly as a helper in
396
+ # reflection.
397
+ #
398
+ # @example Get the relation macro.
399
+ # Mongoid::Relations::Embedded::Many.macro
400
+ #
401
+ # @return [ Symbol ] :embeds_many
402
+ #
403
+ # @since 2.0.0.rc.1
404
+ def macro
405
+ :embeds_many
406
+ end
407
+
408
+ # Return the nested builder that is responsible for generating the
409
+ # documents that will be used by this relation.
410
+ #
411
+ # @example Get the nested builder.
412
+ # NestedAttributes::Many.builder(attributes, options)
413
+ #
414
+ # @param [ Metadata ] metadata The relation metadata.
415
+ # @param [ Hash ] attributes The attributes to build with.
416
+ # @param [ Hash ] options The builder options.
417
+ #
418
+ # @option options [ true, false ] :allow_destroy Can documents be
419
+ # deleted?
420
+ # @option options [ Integer ] :limit Max number of documents to
421
+ # create at once.
422
+ # @option options [ Proc, Symbol ] :reject_if If documents match this
423
+ # option then they are ignored.
424
+ # @option options [ true, false ] :update_only Only existing documents
425
+ # can be modified.
426
+ #
427
+ # @return [ NestedBuilder ] The nested attributes builder.
428
+ #
429
+ # @since 2.0.0.rc.1
430
+ def nested_builder(metadata, attributes, options)
431
+ Builders::NestedAttributes::Many.new(metadata, attributes, options)
432
+ end
433
+
434
+ # Tells the caller if this relation is one that stores the foreign
435
+ # key on its own objects.
436
+ #
437
+ # @example Does this relation store a foreign key?
438
+ # Embedded::Many.stores_foreign_key?
439
+ #
440
+ # @return [ false ] false.
441
+ #
442
+ # @since 2.0.0.rc.1
443
+ def stores_foreign_key?
444
+ false
445
+ end
446
+ end
447
+ end
448
+ end
449
+ end
450
+ end