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.
- data/lib/mongoid.rb +10 -2
- data/lib/mongoid/associations.rb +82 -58
- data/lib/mongoid/associations/embeds_one.rb +6 -6
- data/lib/mongoid/associations/foreign_key.rb +35 -0
- data/lib/mongoid/associations/meta_data.rb +9 -0
- data/lib/mongoid/associations/options.rb +1 -1
- data/lib/mongoid/associations/proxy.rb +9 -0
- data/lib/mongoid/associations/{belongs_to_related.rb → referenced_in.rb} +6 -5
- data/lib/mongoid/associations/{has_many_related.rb → references_many.rb} +69 -26
- data/lib/mongoid/associations/references_many_as_array.rb +78 -0
- data/lib/mongoid/associations/{has_one_related.rb → references_one.rb} +16 -2
- data/lib/mongoid/atomicity.rb +42 -0
- data/lib/mongoid/attributes.rb +148 -146
- data/lib/mongoid/callbacks.rb +5 -1
- data/lib/mongoid/collections.rb +31 -1
- data/lib/mongoid/components.rb +4 -1
- data/lib/mongoid/config.rb +2 -1
- data/lib/mongoid/criteria.rb +9 -0
- data/lib/mongoid/dirty.rb +211 -212
- data/lib/mongoid/document.rb +126 -185
- data/lib/mongoid/extensions.rb +5 -0
- data/lib/mongoid/extensions/array/conversions.rb +3 -5
- data/lib/mongoid/extensions/hash/conversions.rb +19 -22
- data/lib/mongoid/extensions/object/conversions.rb +3 -5
- data/lib/mongoid/extensions/set/conversions.rb +20 -0
- data/lib/mongoid/field.rb +11 -0
- data/lib/mongoid/finders.rb +8 -0
- data/lib/mongoid/hierarchy.rb +76 -0
- data/lib/mongoid/identity.rb +37 -29
- data/lib/mongoid/paths.rb +46 -47
- data/lib/mongoid/persistence.rb +111 -113
- data/lib/mongoid/persistence/insert.rb +1 -1
- data/lib/mongoid/persistence/insert_embedded.rb +10 -5
- data/lib/mongoid/persistence/remove_all.rb +3 -2
- data/lib/mongoid/persistence/update.rb +8 -3
- data/lib/mongoid/railtie.rb +3 -0
- data/lib/mongoid/railties/database.rake +33 -18
- data/lib/mongoid/timestamps.rb +9 -12
- data/lib/mongoid/validations/uniqueness.rb +16 -4
- data/lib/mongoid/version.rb +1 -1
- data/lib/mongoid/versioning.rb +10 -11
- data/lib/rails/generators/mongoid/config/config_generator.rb +0 -16
- metadata +64 -24
data/lib/mongoid/document.rb
CHANGED
@@ -5,22 +5,20 @@ module Mongoid #:nodoc:
|
|
5
5
|
included do
|
6
6
|
include Mongoid::Components
|
7
7
|
|
8
|
-
cattr_accessor :primary_key
|
9
|
-
self.hereditary = false
|
8
|
+
cattr_accessor :primary_key
|
10
9
|
|
11
|
-
attr_accessor :association_name
|
10
|
+
attr_accessor :association_name
|
12
11
|
attr_reader :new_record
|
13
12
|
|
14
|
-
delegate :
|
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 ||=
|
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
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
end
|
77
|
+
# Delegates to ==
|
78
|
+
def eql?(comparison_object)
|
79
|
+
self == (comparison_object)
|
80
|
+
end
|
132
81
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
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
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
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
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
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
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
176
|
-
|
177
|
-
|
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
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
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
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
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
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
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
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
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
|
-
|
225
|
-
|
226
|
-
|
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
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
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
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
176
|
+
# Return an array with this +Document+ only in it.
|
177
|
+
def to_a
|
178
|
+
[ self ]
|
179
|
+
end
|
238
180
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
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
|
data/lib/mongoid/extensions.rb
CHANGED
@@ -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
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
if
|
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
|
-
|
9
|
-
|
10
|
-
|
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
|