mongoid 0.8.7 → 0.8.8
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.
- data/VERSION +1 -1
- data/lib/mongoid/associations/has_many.rb +12 -3
- data/lib/mongoid/associations/has_one.rb +18 -2
- data/lib/mongoid/document.rb +110 -18
- data/mongoid.gemspec +1 -1
- data/spec/integration/mongoid/document_spec.rb +27 -0
- data/spec/unit/mongoid/associations/has_many_spec.rb +64 -39
- data/spec/unit/mongoid/associations/has_one_spec.rb +57 -15
- metadata +1 -1
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.8.
|
1
|
+
0.8.8
|
@@ -44,6 +44,17 @@ module Mongoid #:nodoc:
|
|
44
44
|
object
|
45
45
|
end
|
46
46
|
|
47
|
+
# Creates a new Document and adds it to the association collection. The
|
48
|
+
# document created will be of the same class as the others in the
|
49
|
+
# association, and the attributes will be passed into the constructor and
|
50
|
+
# the new object will then be saved.
|
51
|
+
#
|
52
|
+
# Returns the newly created object.
|
53
|
+
def create(attributes)
|
54
|
+
object = build(attributes)
|
55
|
+
object.save
|
56
|
+
end
|
57
|
+
|
47
58
|
# Finds a document in this association.
|
48
59
|
# If :all is passed, returns all the documents
|
49
60
|
# If an id is passed, will return the document for that id.
|
@@ -60,9 +71,7 @@ module Mongoid #:nodoc:
|
|
60
71
|
# This then delegated all methods to the array class since this is
|
61
72
|
# essentially a proxy to an array itself.
|
62
73
|
def initialize(document, options)
|
63
|
-
@parent = document
|
64
|
-
@association_name = options.name
|
65
|
-
@klass = options.klass
|
74
|
+
@parent, @association_name, @klass = document, options.name, options.klass
|
66
75
|
attributes = document.attributes[@association_name]
|
67
76
|
@documents = attributes ? attributes.collect do |attribute|
|
68
77
|
child = @klass.instantiate(attribute)
|
@@ -5,7 +5,23 @@ module Mongoid #:nodoc:
|
|
5
5
|
|
6
6
|
delegate :valid?, :to => :document
|
7
7
|
|
8
|
-
attr_accessor :klass
|
8
|
+
attr_accessor :klass, :parent, :association_name
|
9
|
+
|
10
|
+
# Build a new object for the association.
|
11
|
+
def build(attributes)
|
12
|
+
@document = @klass.instantiate(attributes)
|
13
|
+
@document.parentize(@parent, @association_name)
|
14
|
+
@document.notify
|
15
|
+
decorate!
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
# Create a new object for the association and save it.
|
20
|
+
def create(attributes)
|
21
|
+
build(attributes)
|
22
|
+
@document.save
|
23
|
+
self
|
24
|
+
end
|
9
25
|
|
10
26
|
# Creates the new association by finding the attributes in
|
11
27
|
# the parent document with its name, and instantiating a
|
@@ -14,7 +30,7 @@ module Mongoid #:nodoc:
|
|
14
30
|
# All method calls on this object will then be delegated
|
15
31
|
# to the internal document itself.
|
16
32
|
def initialize(document, options)
|
17
|
-
@klass = options.klass
|
33
|
+
@klass, @parent, @association_name = options.klass, document, options.name
|
18
34
|
attributes = document.attributes[options.name]
|
19
35
|
@document = klass.instantiate(attributes || {})
|
20
36
|
@document.parentize(document, options.name)
|
data/lib/mongoid/document.rb
CHANGED
@@ -113,7 +113,8 @@ module Mongoid #:nodoc:
|
|
113
113
|
|
114
114
|
end
|
115
115
|
|
116
|
-
# Performs equality checking on the attributes.
|
116
|
+
# Performs equality checking on the attributes. For now we chack against
|
117
|
+
# all attributes excluding timestamps on the object.
|
117
118
|
def ==(other)
|
118
119
|
return false unless other.is_a?(Document)
|
119
120
|
@attributes.except(:modified_at).except(:created_at) ==
|
@@ -146,8 +147,8 @@ module Mongoid #:nodoc:
|
|
146
147
|
self.class.fields
|
147
148
|
end
|
148
149
|
|
149
|
-
# Get the id associated with this object.
|
150
|
-
#
|
150
|
+
# Get the id associated with this object. This will pull the _id value out
|
151
|
+
# of the attributes +Hash+.
|
151
152
|
def id
|
152
153
|
@attributes[:_id]
|
153
154
|
end
|
@@ -160,8 +161,20 @@ module Mongoid #:nodoc:
|
|
160
161
|
alias :_id :id
|
161
162
|
alias :_id= :id=
|
162
163
|
|
163
|
-
# Instantiate a new Document
|
164
|
-
# If no attributes are provided, they will be initialized with
|
164
|
+
# Instantiate a new +Document+, setting the Document's attributes if
|
165
|
+
# given. If no attributes are provided, they will be initialized with
|
166
|
+
# an empty +Hash+.
|
167
|
+
#
|
168
|
+
# If a primary key is defined, the document's id will be set to that key,
|
169
|
+
# otherwise it will be set to a fresh +Mongo::ObjectID+ string.
|
170
|
+
#
|
171
|
+
# Options:
|
172
|
+
#
|
173
|
+
# attrs: The attributes +Hash+ to set up the document with.
|
174
|
+
#
|
175
|
+
# Example:
|
176
|
+
#
|
177
|
+
# <tt>Person.new(:title => "Mr", :age => 30)</tt>
|
165
178
|
def initialize(attrs = {})
|
166
179
|
@attributes = {}.with_indifferent_access
|
167
180
|
process(defaults.merge(attrs))
|
@@ -169,34 +182,72 @@ module Mongoid #:nodoc:
|
|
169
182
|
generate_key
|
170
183
|
end
|
171
184
|
|
185
|
+
# Returns the class name plus its attributes.
|
172
186
|
def inspect
|
173
187
|
"#{self.class.name} : #{@attributes.inspect}"
|
174
188
|
end
|
175
189
|
|
176
|
-
# Return the +Document+ primary key.
|
190
|
+
# Return the +Document+ primary key. This will only exist if a key has been
|
191
|
+
# set up on the +Document+ and will return an array of fields.
|
192
|
+
#
|
193
|
+
# Example:
|
194
|
+
#
|
195
|
+
# class Person < Mongoid::Document
|
196
|
+
# field :first_name
|
197
|
+
# field :last_name
|
198
|
+
# key :first_name, :last_name
|
199
|
+
# end
|
200
|
+
#
|
201
|
+
# <tt>person.primary_key #[:first_name, :last_name]</tt>
|
177
202
|
def primary_key
|
178
203
|
self.class.primary_key
|
179
204
|
end
|
180
205
|
|
181
|
-
# Returns true is the Document has not been persisted to the database,
|
206
|
+
# Returns true is the +Document+ has not been persisted to the database,
|
207
|
+
# false if it has. This is determined by the instance variable @new_record
|
208
|
+
# and NOT if the object has an id.
|
182
209
|
def new_record?
|
183
210
|
@new_record == true
|
184
211
|
end
|
185
212
|
|
186
|
-
#
|
213
|
+
# Set the changed state of the +Document+ then notify observers that it has changed.
|
214
|
+
#
|
215
|
+
# Example:
|
216
|
+
#
|
217
|
+
# <tt>person.notify</tt>
|
187
218
|
def notify
|
188
219
|
changed(true)
|
189
220
|
notify_observers(self)
|
190
221
|
end
|
191
222
|
|
192
|
-
# Sets
|
223
|
+
# Sets up a child/parent association. This is used for newly created
|
224
|
+
# objects so they can be properly added to the graph and have the parent
|
225
|
+
# observers set up properly.
|
226
|
+
#
|
227
|
+
# Options:
|
228
|
+
#
|
229
|
+
# abject: The parent object that needs to be set for the child.
|
230
|
+
# association_name: The name of the association for the child.
|
231
|
+
#
|
232
|
+
# Example:
|
233
|
+
#
|
234
|
+
# <tt>address.parentize(person, :addresses)</tt>
|
193
235
|
def parentize(object, association_name)
|
194
236
|
self.parent = object
|
195
237
|
self.association_name = association_name
|
196
238
|
add_observer(object)
|
197
239
|
end
|
198
240
|
|
199
|
-
# Read from the attributes
|
241
|
+
# Read a value from the +Document+ attributes. If the value does not exist
|
242
|
+
# it will return nil.
|
243
|
+
#
|
244
|
+
# Options:
|
245
|
+
#
|
246
|
+
# name: The name of the attribute to get.
|
247
|
+
#
|
248
|
+
# Example:
|
249
|
+
#
|
250
|
+
# <tt>person.read_attribute(:title)</tt>
|
200
251
|
def read_attribute(name)
|
201
252
|
fields[name].get(@attributes[name])
|
202
253
|
end
|
@@ -206,26 +257,55 @@ module Mongoid #:nodoc:
|
|
206
257
|
@attributes = collection.find_one(:_id => id).with_indifferent_access
|
207
258
|
end
|
208
259
|
|
209
|
-
# Return the root +Document+ in the object graph.
|
260
|
+
# Return the root +Document+ in the object graph. If the current +Document+
|
261
|
+
# is the root object in the graph it will return self.
|
210
262
|
def root
|
211
263
|
object = self
|
212
264
|
while (object.parent) do object = object.parent; end
|
213
265
|
object || self
|
214
266
|
end
|
215
267
|
|
216
|
-
# Returns the id of the Document
|
268
|
+
# Returns the id of the Document, used in Rails compatibility.
|
217
269
|
def to_param
|
218
|
-
id
|
270
|
+
id
|
219
271
|
end
|
220
272
|
|
221
|
-
#
|
273
|
+
# Observe a notify call from a child +Document+. This will either update
|
274
|
+
# existing attributes on the +Document+ or clear them out for the child if
|
275
|
+
# the clear boolean is provided.
|
276
|
+
#
|
277
|
+
# Options:
|
278
|
+
#
|
279
|
+
# child: The child +Document+ that sent the notification.
|
280
|
+
# clear: Will clear out the child's attributes if set to true.
|
281
|
+
#
|
282
|
+
# Example:
|
283
|
+
#
|
284
|
+
# <tt>person.notify_observers(self)</tt> will cause this method to execute.
|
285
|
+
#
|
286
|
+
# This will also cause the observing +Document+ to notify it's parent if
|
287
|
+
# there is any.
|
222
288
|
def update(child, clear = false)
|
223
289
|
@attributes.insert(child.association_name, child.attributes) unless clear
|
224
290
|
@attributes.delete(child.association_name) if clear
|
225
291
|
notify
|
226
292
|
end
|
227
293
|
|
228
|
-
# Write to the
|
294
|
+
# Write a single attribute to the +Document+ attribute +Hash+. This will
|
295
|
+
# also fire the before and after update callbacks, and perform any
|
296
|
+
# necessary typecasting.
|
297
|
+
#
|
298
|
+
# Options:
|
299
|
+
#
|
300
|
+
# name: The name of the attribute to update.
|
301
|
+
# value: The value to set for the attribute.
|
302
|
+
#
|
303
|
+
# Example:
|
304
|
+
#
|
305
|
+
# <tt>person.write_attribute(:title, "Mr.")</tt>
|
306
|
+
#
|
307
|
+
# This will also cause the observing +Document+ to notify it's parent if
|
308
|
+
# there is any.
|
229
309
|
def write_attribute(name, value)
|
230
310
|
run_callbacks(:before_update)
|
231
311
|
@attributes[name] = fields[name].set(value)
|
@@ -233,14 +313,26 @@ module Mongoid #:nodoc:
|
|
233
313
|
notify
|
234
314
|
end
|
235
315
|
|
236
|
-
# Writes
|
237
|
-
# the
|
316
|
+
# Writes the supplied attributes +Hash+ to the +Document+. This will only
|
317
|
+
# overwrite existing attributes if they are present in the new +Hash+, all
|
318
|
+
# others will be preserved.
|
319
|
+
#
|
320
|
+
# Options:
|
321
|
+
#
|
322
|
+
# attrs: The +Hash+ of new attributes to set on the +Document+
|
323
|
+
#
|
324
|
+
# Example:
|
325
|
+
#
|
326
|
+
# <tt>person.write_attributes(:title => "Mr.")</tt>
|
327
|
+
#
|
328
|
+
# This will also cause the observing +Document+ to notify it's parent if
|
329
|
+
# there is any.
|
238
330
|
def write_attributes(attrs)
|
239
331
|
process(attrs)
|
240
332
|
notify
|
241
333
|
end
|
242
334
|
|
243
|
-
|
335
|
+
protected
|
244
336
|
def generate_key
|
245
337
|
if primary_key
|
246
338
|
values = primary_key.collect { |key| @attributes[key] }
|
data/mongoid.gemspec
CHANGED
@@ -35,6 +35,33 @@ describe Mongoid::Document do
|
|
35
35
|
person.attributes[:title].should == "Test"
|
36
36
|
end
|
37
37
|
|
38
|
+
context "when creating a has many" do
|
39
|
+
|
40
|
+
before do
|
41
|
+
@person = Person.new(:title => "Esquire")
|
42
|
+
@person.addresses.create(:street => "Nan Jing Dong Lu", :city => "Shanghai")
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should create and save the entire graph" do
|
46
|
+
person = Person.find(@person.id)
|
47
|
+
person.addresses.first.street.should == "Nan Jing Dong Lu"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
context "when creating a has one" do
|
53
|
+
|
54
|
+
before do
|
55
|
+
@person = Person.new(:title => "Esquire")
|
56
|
+
@person.name.create(:first_name => "Jorge")
|
57
|
+
end
|
58
|
+
|
59
|
+
it "should create and save the entire graph" do
|
60
|
+
person = Person.find(@person.id)
|
61
|
+
person.name.first_name.should == "Jorge"
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
38
65
|
end
|
39
66
|
|
40
67
|
describe "#find" do
|
@@ -9,25 +9,6 @@ describe Mongoid::Associations::HasMany do
|
|
9
9
|
@document = stub(:attributes => @attributes, :add_observer => true, :update => true)
|
10
10
|
end
|
11
11
|
|
12
|
-
describe "#update" do
|
13
|
-
|
14
|
-
before do
|
15
|
-
@address = Address.new(:street => "Madison Ave")
|
16
|
-
@person = Person.new(:title => "Sir")
|
17
|
-
Mongoid::Associations::HasMany.update([@address], @person, Mongoid::Associations::Options.new(:name => :addresses))
|
18
|
-
end
|
19
|
-
|
20
|
-
it "parentizes the child document" do
|
21
|
-
@address.parent.should == @person
|
22
|
-
end
|
23
|
-
|
24
|
-
it "sets the attributes of the child on the parent" do
|
25
|
-
@person.attributes[:addresses].should ==
|
26
|
-
[{ "_id" => "madison-ave", "street" => "Madison Ave" }]
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
|
31
12
|
describe "#[]" do
|
32
13
|
|
33
14
|
before do
|
@@ -56,7 +37,10 @@ describe Mongoid::Associations::HasMany do
|
|
56
37
|
describe "#<<" do
|
57
38
|
|
58
39
|
before do
|
59
|
-
@association = Mongoid::Associations::HasMany.new(
|
40
|
+
@association = Mongoid::Associations::HasMany.new(
|
41
|
+
@document,
|
42
|
+
Mongoid::Associations::Options.new(:name => :addresses)
|
43
|
+
)
|
60
44
|
@address = Address.new
|
61
45
|
end
|
62
46
|
|
@@ -74,22 +58,50 @@ describe Mongoid::Associations::HasMany do
|
|
74
58
|
|
75
59
|
end
|
76
60
|
|
77
|
-
describe "#
|
61
|
+
describe "#build" do
|
78
62
|
|
79
63
|
before do
|
80
|
-
@association = Mongoid::Associations::HasMany.new(
|
81
|
-
|
64
|
+
@association = Mongoid::Associations::HasMany.new(
|
65
|
+
@document,
|
66
|
+
Mongoid::Associations::Options.new(:name => :addresses)
|
67
|
+
)
|
82
68
|
end
|
83
69
|
|
84
|
-
it "adds
|
85
|
-
@association.
|
70
|
+
it "adds a new document to the array with the suppied parameters" do
|
71
|
+
@association.build({ :street => "Street 1" })
|
86
72
|
@association.length.should == 3
|
87
|
-
@
|
73
|
+
@association[2].should be_a_kind_of(Address)
|
74
|
+
@association[2].street.should == "Street 1"
|
75
|
+
end
|
76
|
+
|
77
|
+
it "returns the newly built object in the association" do
|
78
|
+
address = @association.build({ :street => "Yet Another" })
|
79
|
+
address.should be_a_kind_of(Address)
|
80
|
+
address.street.should == "Yet Another"
|
88
81
|
end
|
89
82
|
|
90
83
|
end
|
91
84
|
|
92
|
-
describe "#
|
85
|
+
describe "#create" do
|
86
|
+
|
87
|
+
before do
|
88
|
+
@association = Mongoid::Associations::HasMany.new(
|
89
|
+
@document,
|
90
|
+
Mongoid::Associations::Options.new(:name => :addresses)
|
91
|
+
)
|
92
|
+
@address = Address.new(:street => "Yet Another")
|
93
|
+
end
|
94
|
+
|
95
|
+
it "builds and saves a new object" do
|
96
|
+
Mongoid::Commands::Create.expects(:execute).returns(@address)
|
97
|
+
address = @association.create({ :street => "Yet Another" })
|
98
|
+
address.should be_a_kind_of(Address)
|
99
|
+
address.street.should == "Yet Another"
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
describe "#concat" do
|
93
105
|
|
94
106
|
before do
|
95
107
|
@association = Mongoid::Associations::HasMany.new(@document, Mongoid::Associations::Options.new(:name => :addresses))
|
@@ -97,30 +109,24 @@ describe Mongoid::Associations::HasMany do
|
|
97
109
|
end
|
98
110
|
|
99
111
|
it "adds the parent document before appending to the array" do
|
100
|
-
@association.
|
112
|
+
@association.concat [@address]
|
101
113
|
@association.length.should == 3
|
102
114
|
@address.parent.should == @document
|
103
115
|
end
|
104
116
|
|
105
117
|
end
|
106
118
|
|
107
|
-
describe "#
|
119
|
+
describe "#push" do
|
108
120
|
|
109
121
|
before do
|
110
122
|
@association = Mongoid::Associations::HasMany.new(@document, Mongoid::Associations::Options.new(:name => :addresses))
|
123
|
+
@address = Address.new
|
111
124
|
end
|
112
125
|
|
113
|
-
it "adds
|
114
|
-
@association.
|
126
|
+
it "adds the parent document before appending to the array" do
|
127
|
+
@association.push @address
|
115
128
|
@association.length.should == 3
|
116
|
-
@
|
117
|
-
@association[2].street.should == "Street 1"
|
118
|
-
end
|
119
|
-
|
120
|
-
it "returns the newly built object in the association" do
|
121
|
-
address = @association.build({ :street => "Yet Another" })
|
122
|
-
address.should be_a_kind_of(Address)
|
123
|
-
address.street.should == "Yet Another"
|
129
|
+
@address.parent.should == @document
|
124
130
|
end
|
125
131
|
|
126
132
|
end
|
@@ -217,4 +223,23 @@ describe Mongoid::Associations::HasMany do
|
|
217
223
|
|
218
224
|
end
|
219
225
|
|
226
|
+
describe "#update" do
|
227
|
+
|
228
|
+
before do
|
229
|
+
@address = Address.new(:street => "Madison Ave")
|
230
|
+
@person = Person.new(:title => "Sir")
|
231
|
+
Mongoid::Associations::HasMany.update([@address], @person, Mongoid::Associations::Options.new(:name => :addresses))
|
232
|
+
end
|
233
|
+
|
234
|
+
it "parentizes the child document" do
|
235
|
+
@address.parent.should == @person
|
236
|
+
end
|
237
|
+
|
238
|
+
it "sets the attributes of the child on the parent" do
|
239
|
+
@person.attributes[:addresses].should ==
|
240
|
+
[{ "_id" => "madison-ave", "street" => "Madison Ave" }]
|
241
|
+
end
|
242
|
+
|
243
|
+
end
|
244
|
+
|
220
245
|
end
|
@@ -7,25 +7,44 @@ describe Mongoid::Associations::HasOne do
|
|
7
7
|
@document = stub(:attributes => @attributes, :update => true)
|
8
8
|
end
|
9
9
|
|
10
|
-
describe "#
|
10
|
+
describe "#build" do
|
11
11
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
12
|
+
context "when attributes provided" do
|
13
|
+
|
14
|
+
before do
|
15
|
+
@association = Mongoid::Associations::HasOne.new(
|
16
|
+
@document,
|
17
|
+
Mongoid::Associations::Options.new(:name => :mixed_drink)
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "replaces the existing has_one" do
|
22
|
+
drink = @association.build({ :name => "Sapphire and Tonic" })
|
23
|
+
drink.name.should == "Sapphire and Tonic"
|
24
|
+
end
|
21
25
|
|
22
|
-
it "parentizes the child document" do
|
23
|
-
@name.parent.should == @person
|
24
26
|
end
|
25
27
|
|
26
|
-
|
27
|
-
|
28
|
-
|
28
|
+
end
|
29
|
+
|
30
|
+
describe "#create" do
|
31
|
+
|
32
|
+
context "when attributes provided" do
|
33
|
+
|
34
|
+
before do
|
35
|
+
@association = Mongoid::Associations::HasOne.new(
|
36
|
+
@document,
|
37
|
+
Mongoid::Associations::Options.new(:name => :mixed_drink)
|
38
|
+
)
|
39
|
+
@drink = MixedDrink.new(:name => "Sapphire and Tonic")
|
40
|
+
end
|
41
|
+
|
42
|
+
it "replaces and saves the existing has_one" do
|
43
|
+
Mongoid::Commands::Create.expects(:execute).returns(@drink)
|
44
|
+
drink = @association.create({ :name => "Sapphire and Tonic" })
|
45
|
+
drink.name.should == "Sapphire and Tonic"
|
46
|
+
end
|
47
|
+
|
29
48
|
end
|
30
49
|
|
31
50
|
end
|
@@ -58,4 +77,27 @@ describe Mongoid::Associations::HasOne do
|
|
58
77
|
|
59
78
|
end
|
60
79
|
|
80
|
+
describe "#update" do
|
81
|
+
|
82
|
+
before do
|
83
|
+
@name = Name.new(:first_name => "Donald")
|
84
|
+
@person = Person.new(:title => "Sir")
|
85
|
+
Mongoid::Associations::HasOne.update(
|
86
|
+
@name,
|
87
|
+
@person,
|
88
|
+
Mongoid::Associations::Options.new(:name => :name)
|
89
|
+
)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "parentizes the child document" do
|
93
|
+
@name.parent.should == @person
|
94
|
+
end
|
95
|
+
|
96
|
+
it "sets the attributes of the child on the parent" do
|
97
|
+
@person.attributes[:name].should ==
|
98
|
+
{ "_id" => "donald", "first_name" => "Donald" }
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
61
103
|
end
|