mongoid 2.0.0.beta.5 → 2.0.0.beta.7

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 (43) hide show
  1. data/lib/mongoid.rb +10 -2
  2. data/lib/mongoid/associations.rb +82 -58
  3. data/lib/mongoid/associations/embeds_one.rb +6 -6
  4. data/lib/mongoid/associations/foreign_key.rb +35 -0
  5. data/lib/mongoid/associations/meta_data.rb +9 -0
  6. data/lib/mongoid/associations/options.rb +1 -1
  7. data/lib/mongoid/associations/proxy.rb +9 -0
  8. data/lib/mongoid/associations/{belongs_to_related.rb → referenced_in.rb} +6 -5
  9. data/lib/mongoid/associations/{has_many_related.rb → references_many.rb} +69 -26
  10. data/lib/mongoid/associations/references_many_as_array.rb +78 -0
  11. data/lib/mongoid/associations/{has_one_related.rb → references_one.rb} +16 -2
  12. data/lib/mongoid/atomicity.rb +42 -0
  13. data/lib/mongoid/attributes.rb +148 -146
  14. data/lib/mongoid/callbacks.rb +5 -1
  15. data/lib/mongoid/collections.rb +31 -1
  16. data/lib/mongoid/components.rb +4 -1
  17. data/lib/mongoid/config.rb +2 -1
  18. data/lib/mongoid/criteria.rb +9 -0
  19. data/lib/mongoid/dirty.rb +211 -212
  20. data/lib/mongoid/document.rb +126 -185
  21. data/lib/mongoid/extensions.rb +5 -0
  22. data/lib/mongoid/extensions/array/conversions.rb +3 -5
  23. data/lib/mongoid/extensions/hash/conversions.rb +19 -22
  24. data/lib/mongoid/extensions/object/conversions.rb +3 -5
  25. data/lib/mongoid/extensions/set/conversions.rb +20 -0
  26. data/lib/mongoid/field.rb +11 -0
  27. data/lib/mongoid/finders.rb +8 -0
  28. data/lib/mongoid/hierarchy.rb +76 -0
  29. data/lib/mongoid/identity.rb +37 -29
  30. data/lib/mongoid/paths.rb +46 -47
  31. data/lib/mongoid/persistence.rb +111 -113
  32. data/lib/mongoid/persistence/insert.rb +1 -1
  33. data/lib/mongoid/persistence/insert_embedded.rb +10 -5
  34. data/lib/mongoid/persistence/remove_all.rb +3 -2
  35. data/lib/mongoid/persistence/update.rb +8 -3
  36. data/lib/mongoid/railtie.rb +3 -0
  37. data/lib/mongoid/railties/database.rake +33 -18
  38. data/lib/mongoid/timestamps.rb +9 -12
  39. data/lib/mongoid/validations/uniqueness.rb +16 -4
  40. data/lib/mongoid/version.rb +1 -1
  41. data/lib/mongoid/versioning.rb +10 -11
  42. data/lib/rails/generators/mongoid/config/config_generator.rb +0 -16
  43. metadata +64 -24
@@ -5,22 +5,20 @@ module Mongoid #:nodoc:
5
5
  included do
6
6
  include Mongoid::Components
7
7
 
8
- cattr_accessor :primary_key, :hereditary
9
- self.hereditary = false
8
+ cattr_accessor :primary_key
10
9
 
11
- attr_accessor :association_name, :_parent
10
+ attr_accessor :association_name
12
11
  attr_reader :new_record
13
12
 
14
- delegate :db, :primary_key, :to => "self.class"
13
+ delegate :primary_key, :to => "self.class"
15
14
  end
16
15
 
17
- module ClassMethods
18
- # Return the database associated with this class.
19
- def db
20
- collection.db
21
- end
16
+ module ClassMethods #:nodoc:
22
17
 
23
18
  # Perform default behavior but mark the hierarchy as being hereditary.
19
+ #
20
+ # This method must remain in the +Document+ module, even though its
21
+ # behavior affects items in the Hierarchy module.
24
22
  def inherited(subclass)
25
23
  super(subclass)
26
24
  self.hereditary = true
@@ -61,202 +59,145 @@ module Mongoid #:nodoc:
61
59
  end
62
60
 
63
61
  # Returns all types to query for when using this class as the base.
62
+ # *subclasses* is from activesupport. Note that a bug in *subclasses*
63
+ # causes the first call to only return direct children, hence
64
+ # the double call and unique.
64
65
  def _types
65
- @_type ||= (subclasses_of(self).map { |o| o.to_s } + [ self.name ])
66
- end
67
-
68
- # return the list of subclassses for an object
69
- def subclasses_of(*superclasses) #:nodoc:
70
- subclasses = []
71
- superclasses.each do |sup|
72
- ObjectSpace.each_object(class << sup; self; end) do |k|
73
- if k != sup && (k.name.blank? || eval("defined?(::#{k}) && ::#{k}.object_id == k.object_id"))
74
- subclasses << k
75
- end
76
- end
77
- end
78
- subclasses
66
+ @_type ||= [subclasses + subclasses + [self.name]].flatten.uniq
79
67
  end
80
68
  end
81
69
 
82
- module InstanceMethods
83
- # Performs equality checking on the document ids. For more robust
84
- # equality checking please override this method.
85
- def ==(other)
86
- return false unless other.is_a?(Document)
87
- id == other.id
88
- end
89
-
90
- # Delegates to ==
91
- def eql?(comparison_object)
92
- self == (comparison_object)
93
- end
94
-
95
- # Delegates to id in order to allow two records of the same type and id to work with something like:
96
- # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
97
- def hash
98
- id.hash
99
- end
100
-
101
- # Is inheritance in play here?
102
- #
103
- # Returns:
104
- #
105
- # <tt>true</tt> if inheritance used, <tt>false</tt> if not.
106
- def hereditary?
107
- !!self.hereditary
108
- end
109
-
110
- # Introduces a child object into the +Document+ object graph. This will
111
- # set up the relationships between the parent and child and update the
112
- # attributes of the parent +Document+.
113
- #
114
- # Options:
115
- #
116
- # parent: The +Document+ to assimilate with.
117
- # options: The association +Options+ for the child.
118
- def assimilate(parent, options)
119
- parentize(parent, options.name); notify; self
120
- end
121
-
122
- # Return the attributes hash with indifferent access.
123
- def attributes
124
- @attributes.with_indifferent_access
125
- end
70
+ # Performs equality checking on the document ids. For more robust
71
+ # equality checking please override this method.
72
+ def ==(other)
73
+ return false unless other.is_a?(Document)
74
+ id == other.id
75
+ end
126
76
 
127
- # Clone the current +Document+. This will return all attributes with the
128
- # exception of the document's id and versions.
129
- def clone
130
- self.class.instantiate(@attributes.except("_id").except("versions").dup, true)
131
- end
77
+ # Delegates to ==
78
+ def eql?(comparison_object)
79
+ self == (comparison_object)
80
+ end
132
81
 
133
- # Generate an id for this +Document+.
134
- def identify
135
- Identity.create(self)
136
- end
82
+ # Delegates to id in order to allow two records of the same type and id to work with something like:
83
+ # [ Person.find(1), Person.find(2), Person.find(3) ] & [ Person.find(1), Person.find(4) ] # => [ Person.find(1) ]
84
+ def hash
85
+ id.hash
86
+ end
137
87
 
138
- # Instantiate a new +Document+, setting the Document's attributes if
139
- # given. If no attributes are provided, they will be initialized with
140
- # an empty +Hash+.
141
- #
142
- # If a primary key is defined, the document's id will be set to that key,
143
- # otherwise it will be set to a fresh +BSON::ObjectID+ string.
144
- #
145
- # Options:
146
- #
147
- # attrs: The attributes +Hash+ to set up the document with.
148
- def initialize(attrs = nil)
149
- @attributes = default_attributes
150
- process(attrs)
151
- @new_record = true
152
- document = yield self if block_given?
153
- identify
154
- end
88
+ # Introduces a child object into the +Document+ object graph. This will
89
+ # set up the relationships between the parent and child and update the
90
+ # attributes of the parent +Document+.
91
+ #
92
+ # Options:
93
+ #
94
+ # parent: The +Document+ to assimilate with.
95
+ # options: The association +Options+ for the child.
96
+ def assimilate(parent, options)
97
+ parentize(parent, options.name); notify; self
98
+ end
155
99
 
156
- # Returns the class name plus its attributes.
157
- def inspect
158
- attrs = fields.map { |name, field| "#{name}: #{@attributes[name].inspect}" }
159
- if Mongoid.allow_dynamic_fields
160
- dynamic_keys = @attributes.keys - fields.keys - associations.keys - ["_id", "_type"]
161
- attrs += dynamic_keys.map { |name| "#{name}: #{@attributes[name].inspect}" }
162
- end
163
- "#<#{self.class.name} _id: #{id}, #{attrs * ', '}>"
164
- end
100
+ # Return the attributes hash with indifferent access.
101
+ def attributes
102
+ @attributes.with_indifferent_access
103
+ end
165
104
 
166
- # Notify observers of an update.
167
- #
168
- # Example:
169
- #
170
- # <tt>person.notify</tt>
171
- def notify
172
- notify_observers(self)
173
- end
105
+ # Clone the current +Document+. This will return all attributes with the
106
+ # exception of the document's id and versions.
107
+ def clone
108
+ self.class.instantiate(@attributes.except("_id").except("versions").dup, true)
109
+ end
174
110
 
175
- # Sets up a child/parent association. This is used for newly created
176
- # objects so they can be properly added to the graph and have the parent
177
- # observers set up properly.
178
- #
179
- # Options:
180
- #
181
- # abject: The parent object that needs to be set for the child.
182
- # association_name: The name of the association for the child.
183
- #
184
- # Example:
185
- #
186
- # <tt>address.parentize(person, :addresses)</tt>
187
- def parentize(object, association_name)
188
- self._parent = object
189
- self.association_name = association_name.to_s
190
- add_observer(object)
191
- end
111
+ # Generate an id for this +Document+.
112
+ def identify
113
+ Identity.new(self).create
114
+ end
192
115
 
193
- # Return the attributes hash.
194
- def raw_attributes
195
- @attributes
196
- end
116
+ # Instantiate a new +Document+, setting the Document's attributes if
117
+ # given. If no attributes are provided, they will be initialized with
118
+ # an empty +Hash+.
119
+ #
120
+ # If a primary key is defined, the document's id will be set to that key,
121
+ # otherwise it will be set to a fresh +BSON::ObjectID+ string.
122
+ #
123
+ # Options:
124
+ #
125
+ # attrs: The attributes +Hash+ to set up the document with.
126
+ def initialize(attrs = nil)
127
+ @attributes = default_attributes
128
+ process(attrs)
129
+ @new_record = true
130
+ document = yield self if block_given?
131
+ identify
132
+ end
197
133
 
198
- # Reloads the +Document+ attributes from the database.
199
- def reload
200
- reloaded = collection.find_one(:_id => id)
201
- if Mongoid.raise_not_found_error
202
- raise Errors::DocumentNotFound.new(self.class, id) if reloaded.nil?
203
- end
204
- @attributes = {}.merge(reloaded || {})
205
- self.associations.keys.each { |association_name| unmemoize(association_name) }; self
134
+ # Returns the class name plus its attributes.
135
+ def inspect
136
+ attrs = fields.map { |name, field| "#{name}: #{@attributes[name].inspect}" }
137
+ if Mongoid.allow_dynamic_fields
138
+ dynamic_keys = @attributes.keys - fields.keys - associations.keys - ["_id", "_type"]
139
+ attrs += dynamic_keys.map { |name| "#{name}: #{@attributes[name].inspect}" }
206
140
  end
141
+ "#<#{self.class.name} _id: #{id}, #{attrs * ', '}>"
142
+ end
207
143
 
208
- # Remove a child document from this parent +Document+. Will reset the
209
- # memoized association and notify the parent of the change.
210
- def remove(child)
211
- name = child.association_name
212
- reset(name) { @attributes.remove(name, child.raw_attributes) }
213
- notify
214
- end
144
+ # Notify observers of an update.
145
+ #
146
+ # Example:
147
+ #
148
+ # <tt>person.notify</tt>
149
+ def notify
150
+ notify_observers(self)
151
+ end
215
152
 
216
- # Return the root +Document+ in the object graph. If the current +Document+
217
- # is the root object in the graph it will return self.
218
- def _root
219
- object = self
220
- while (object._parent) do object = object._parent; end
221
- object || self
222
- end
153
+ # Return the attributes hash.
154
+ def raw_attributes
155
+ @attributes
156
+ end
223
157
 
224
- # Return an array with this +Document+ only in it.
225
- def to_a
226
- [ self ]
158
+ # Reloads the +Document+ attributes from the database.
159
+ def reload
160
+ reloaded = collection.find_one(:_id => id)
161
+ if Mongoid.raise_not_found_error
162
+ raise Errors::DocumentNotFound.new(self.class, id) if reloaded.nil?
227
163
  end
164
+ @attributes = {}.merge(reloaded || {})
165
+ self.associations.keys.each { |association_name| unmemoize(association_name) }; self
166
+ end
228
167
 
229
- # Returns nil if document is new, or an array of primary keys if not.
230
- def to_key
231
- new_record? ? nil : [ id ]
232
- end
168
+ # Remove a child document from this parent +Document+. Will reset the
169
+ # memoized association and notify the parent of the change.
170
+ def remove(child)
171
+ name = child.association_name
172
+ reset(name) { @attributes.remove(name, child.raw_attributes) }
173
+ notify
174
+ end
233
175
 
234
- # Returns the id of the Document, used in Rails compatibility.
235
- def to_param
236
- id
237
- end
176
+ # Return an array with this +Document+ only in it.
177
+ def to_a
178
+ [ self ]
179
+ end
238
180
 
239
- # Observe a notify call from a child +Document+. This will either update
240
- # existing attributes on the +Document+ or clear them out for the child if
241
- # the clear boolean is provided.
242
- #
243
- # Options:
244
- #
245
- # child: The child +Document+ that sent the notification.
246
- # clear: Will clear out the child's attributes if set to true.
247
- #
248
- # This will also cause the observing +Document+ to notify it's parent if
249
- # there is any.
250
- def observe(child, clear = false)
251
- name = child.association_name
252
- attrs = child.instance_variable_get(:@attributes)
253
- if clear
254
- @attributes.delete(name)
255
- else
256
- @attributes.insert(name, attrs) unless @attributes[name] && @attributes[name].include?(attrs)
257
- end
258
- notify
259
- end
181
+ # Observe a notify call from a child +Document+. This will either update
182
+ # existing attributes on the +Document+ or clear them out for the child if
183
+ # the clear boolean is provided.
184
+ #
185
+ # Options:
186
+ #
187
+ # child: The child +Document+ that sent the notification.
188
+ # clear: Will clear out the child's attributes if set to true.
189
+ #
190
+ # This will also cause the observing +Document+ to notify it's parent if
191
+ # there is any.
192
+ def observe(child, clear = false)
193
+ name = child.association_name
194
+ attrs = child.instance_variable_get(:@attributes)
195
+ if clear
196
+ @attributes.delete(name)
197
+ else
198
+ @attributes.insert(name, attrs) unless @attributes[name] && @attributes[name].include?(attrs)
199
+ end
200
+ notify
260
201
  end
261
202
  end
262
203
  end
@@ -5,6 +5,7 @@ require "mongoid/extensions/array/aliasing"
5
5
  require "mongoid/extensions/array/assimilation"
6
6
  require "mongoid/extensions/array/conversions"
7
7
  require "mongoid/extensions/array/parentization"
8
+ require "mongoid/extensions/set/conversions"
8
9
  require "mongoid/extensions/big_decimal/conversions"
9
10
  require "mongoid/extensions/binary/conversions"
10
11
  require "mongoid/extensions/boolean/conversions"
@@ -32,6 +33,10 @@ class Array #:nodoc
32
33
  include Mongoid::Extensions::Array::Parentization
33
34
  end
34
35
 
36
+ class Set #:nodoc
37
+ include Mongoid::Extensions::Set::Conversions
38
+ end
39
+
35
40
  class BigDecimal #:nodoc
36
41
  extend Mongoid::Extensions::BigDecimal::Conversions
37
42
  end
@@ -6,11 +6,9 @@ module Mongoid #:nodoc:
6
6
  module Conversions #:nodoc:
7
7
  extend ActiveSupport::Concern
8
8
 
9
- module InstanceMethods #:nodoc:
10
- # Converts this array into an array of hashes.
11
- def mongoidize
12
- collect { |obj| obj.attributes }
13
- end
9
+ # Converts this array into an array of hashes.
10
+ def mongoidize
11
+ collect { |obj| obj.attributes }
14
12
  end
15
13
 
16
14
  module ClassMethods #:nodoc:
@@ -5,30 +5,27 @@ module Mongoid #:nodoc:
5
5
  module Conversions #:nodoc:
6
6
  extend ActiveSupport::Concern
7
7
 
8
- module InstanceMethods #:nodoc:
9
-
10
- # Get the difference between 2 hashes. This will give back a new hash
11
- # with the keys and pairs of [ old, new ] values.
12
- #
13
- # Example:
14
- #
15
- # first = { :field => "value" }
16
- # second = { :field => "new" }
17
- # first.difference(second) # => { :field => [ "value", "new" ] }
18
- #
19
- # Returns:
20
- #
21
- # A +Hash+ of modifications.
22
- def difference(other)
23
- changes = {}
24
- each_pair do |key, value|
25
- if other.has_key?(key)
26
- new_value = other[key]
27
- changes[key] = [ value, new_value ] if new_value != value
28
- end
8
+ # Get the difference between 2 hashes. This will give back a new hash
9
+ # with the keys and pairs of [ old, new ] values.
10
+ #
11
+ # Example:
12
+ #
13
+ # first = { :field => "value" }
14
+ # second = { :field => "new" }
15
+ # first.difference(second) # => { :field => [ "value", "new" ] }
16
+ #
17
+ # Returns:
18
+ #
19
+ # A +Hash+ of modifications.
20
+ def difference(other)
21
+ changes = {}
22
+ each_pair do |key, value|
23
+ if other.has_key?(key)
24
+ new_value = other[key]
25
+ changes[key] = [ value, new_value ] if new_value != value
29
26
  end
30
- changes
31
27
  end
28
+ changes
32
29
  end
33
30
 
34
31
  module ClassMethods #:nodoc:
@@ -5,11 +5,9 @@ module Mongoid #:nodoc:
5
5
  # This module converts objects into mongoid related objects.
6
6
  module Conversions #:nodoc:
7
7
  extend ActiveSupport::Concern
8
- module InstanceMethods
9
- # Converts this object to a hash of attributes
10
- def mongoidize
11
- self.raw_attributes
12
- end
8
+ # Converts this object to a hash of attributes
9
+ def mongoidize
10
+ self.raw_attributes
13
11
  end
14
12
 
15
13
  module ClassMethods