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