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

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