hashrocket-mongomapper 0.3.3
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/.gitignore +7 -0
- data/History +70 -0
- data/LICENSE +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +73 -0
- data/VERSION +1 -0
- data/bin/mmconsole +56 -0
- data/lib/mongomapper.rb +70 -0
- data/lib/mongomapper/associations.rb +84 -0
- data/lib/mongomapper/associations/base.rb +69 -0
- data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
- data/lib/mongomapper/associations/belongs_to_proxy.rb +22 -0
- data/lib/mongomapper/associations/many_documents_proxy.rb +103 -0
- data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
- data/lib/mongomapper/associations/many_embedded_proxy.rb +17 -0
- data/lib/mongomapper/associations/many_polymorphic_proxy.rb +11 -0
- data/lib/mongomapper/associations/many_proxy.rb +6 -0
- data/lib/mongomapper/associations/proxy.rb +63 -0
- data/lib/mongomapper/callbacks.rb +106 -0
- data/lib/mongomapper/document.rb +337 -0
- data/lib/mongomapper/dynamic_finder.rb +38 -0
- data/lib/mongomapper/embedded_document.rb +267 -0
- data/lib/mongomapper/finder_options.rb +85 -0
- data/lib/mongomapper/key.rb +76 -0
- data/lib/mongomapper/observing.rb +50 -0
- data/lib/mongomapper/pagination.rb +52 -0
- data/lib/mongomapper/rails_compatibility/document.rb +15 -0
- data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
- data/lib/mongomapper/save_with_validation.rb +19 -0
- data/lib/mongomapper/serialization.rb +55 -0
- data/lib/mongomapper/serializers/json_serializer.rb +92 -0
- data/lib/mongomapper/support.rb +30 -0
- data/lib/mongomapper/validations.rb +61 -0
- data/mongomapper.gemspec +142 -0
- data/test/NOTE_ON_TESTING +1 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +53 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +45 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +131 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +106 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +261 -0
- data/test/functional/associations/test_many_proxy.rb +295 -0
- data/test/functional/test_associations.rb +47 -0
- data/test/functional/test_callbacks.rb +85 -0
- data/test/functional/test_document.rb +952 -0
- data/test/functional/test_pagination.rb +81 -0
- data/test/functional/test_rails_compatibility.rb +30 -0
- data/test/functional/test_validations.rb +172 -0
- data/test/models.rb +139 -0
- data/test/test_helper.rb +67 -0
- data/test/unit/serializers/test_json_serializer.rb +157 -0
- data/test/unit/test_association_base.rb +144 -0
- data/test/unit/test_document.rb +123 -0
- data/test/unit/test_embedded_document.rb +526 -0
- data/test/unit/test_finder_options.rb +183 -0
- data/test/unit/test_key.rb +247 -0
- data/test/unit/test_mongomapper.rb +28 -0
- data/test/unit/test_observing.rb +101 -0
- data/test/unit/test_pagination.rb +113 -0
- data/test/unit/test_rails_compatibility.rb +34 -0
- data/test/unit/test_serializations.rb +52 -0
- data/test/unit/test_validations.rb +500 -0
- metadata +189 -0
@@ -0,0 +1,295 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'models'
|
3
|
+
|
4
|
+
class ManyProxyTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
clear_all_collections
|
7
|
+
end
|
8
|
+
|
9
|
+
should "default reader to empty array" do
|
10
|
+
project = Project.new
|
11
|
+
project.statuses.should == []
|
12
|
+
end
|
13
|
+
|
14
|
+
should "allow adding to association like it was an array" do
|
15
|
+
project = Project.new
|
16
|
+
project.statuses << Status.new
|
17
|
+
project.statuses.push Status.new
|
18
|
+
project.statuses.concat Status.new
|
19
|
+
project.statuses.size.should == 3
|
20
|
+
end
|
21
|
+
|
22
|
+
should "be able to replace the association" do
|
23
|
+
project = Project.new
|
24
|
+
project.statuses = [Status.new("name" => "ready")]
|
25
|
+
project.save.should be_true
|
26
|
+
|
27
|
+
from_db = Project.find(project.id)
|
28
|
+
from_db.statuses.size.should == 1
|
29
|
+
from_db.statuses[0].name.should == "ready"
|
30
|
+
end
|
31
|
+
|
32
|
+
should "correctly assign foreign key when using <<, push and concat" do
|
33
|
+
project = Project.new
|
34
|
+
project.statuses << Status.new(:name => '<<')
|
35
|
+
project.statuses.push Status.new(:name => 'push')
|
36
|
+
project.statuses.concat Status.new(:name => 'concat')
|
37
|
+
|
38
|
+
from_db = Project.find(project.id)
|
39
|
+
from_db.statuses[0].project_id.should == project.id
|
40
|
+
from_db.statuses[1].project_id.should == project.id
|
41
|
+
from_db.statuses[2].project_id.should == project.id
|
42
|
+
end
|
43
|
+
|
44
|
+
context "build" do
|
45
|
+
should "assign foreign key" do
|
46
|
+
project = Project.create
|
47
|
+
status = project.statuses.build
|
48
|
+
status.project_id.should == project.id
|
49
|
+
end
|
50
|
+
|
51
|
+
should "allow assigning attributes" do
|
52
|
+
project = Project.create
|
53
|
+
status = project.statuses.build(:name => 'Foo')
|
54
|
+
status.name.should == 'Foo'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
context "create" do
|
59
|
+
should "assign foreign key" do
|
60
|
+
project = Project.create
|
61
|
+
status = project.statuses.create
|
62
|
+
status.project_id.should == project.id
|
63
|
+
end
|
64
|
+
|
65
|
+
should "save record" do
|
66
|
+
project = Project.create
|
67
|
+
lambda {
|
68
|
+
project.statuses.create
|
69
|
+
}.should change { Status.count }
|
70
|
+
end
|
71
|
+
|
72
|
+
should "allow passing attributes" do
|
73
|
+
project = Project.create
|
74
|
+
status = project.statuses.create(:name => 'Foo!')
|
75
|
+
status.name.should == 'Foo!'
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "count" do
|
80
|
+
should "work scoped to association" do
|
81
|
+
project = Project.create
|
82
|
+
3.times { project.statuses.create }
|
83
|
+
|
84
|
+
other_project = Project.create
|
85
|
+
2.times { other_project.statuses.create }
|
86
|
+
|
87
|
+
project.statuses.count.should == 3
|
88
|
+
other_project.statuses.count.should == 2
|
89
|
+
end
|
90
|
+
|
91
|
+
should "work with conditions" do
|
92
|
+
project = Project.create
|
93
|
+
project.statuses.create(:name => 'Foo')
|
94
|
+
project.statuses.create(:name => 'Other 1')
|
95
|
+
project.statuses.create(:name => 'Other 2')
|
96
|
+
|
97
|
+
project.statuses.count(:name => 'Foo').should == 1
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "Unassociating documents" do
|
102
|
+
setup do
|
103
|
+
@project = Project.create
|
104
|
+
@project.statuses << Status.create(:name => '1')
|
105
|
+
@project.statuses << Status.create(:name => '2')
|
106
|
+
|
107
|
+
@project2 = Project.create
|
108
|
+
@project2.statuses << Status.create(:name => '1')
|
109
|
+
@project2.statuses << Status.create(:name => '2')
|
110
|
+
end
|
111
|
+
|
112
|
+
should "work with destroy all" do
|
113
|
+
@project.statuses.count.should == 2
|
114
|
+
@project.statuses.destroy_all
|
115
|
+
@project.statuses.count.should == 0
|
116
|
+
|
117
|
+
@project2.statuses.count.should == 2
|
118
|
+
Status.count.should == 2
|
119
|
+
end
|
120
|
+
|
121
|
+
should "work with destroy all and conditions" do
|
122
|
+
@project.statuses.count.should == 2
|
123
|
+
@project.statuses.destroy_all(:name => '1')
|
124
|
+
@project.statuses.count.should == 1
|
125
|
+
|
126
|
+
@project2.statuses.count.should == 2
|
127
|
+
Status.count.should == 3
|
128
|
+
end
|
129
|
+
|
130
|
+
should "work with delete all" do
|
131
|
+
@project.statuses.count.should == 2
|
132
|
+
@project.statuses.delete_all
|
133
|
+
@project.statuses.count.should == 0
|
134
|
+
|
135
|
+
@project2.statuses.count.should == 2
|
136
|
+
Status.count.should == 2
|
137
|
+
end
|
138
|
+
|
139
|
+
should "work with delete all and conditions" do
|
140
|
+
@project.statuses.count.should == 2
|
141
|
+
@project.statuses.delete_all(:name => '1')
|
142
|
+
@project.statuses.count.should == 1
|
143
|
+
|
144
|
+
@project2.statuses.count.should == 2
|
145
|
+
Status.count.should == 3
|
146
|
+
end
|
147
|
+
|
148
|
+
should "work with nullify" do
|
149
|
+
@project.statuses.count.should == 2
|
150
|
+
@project.statuses.nullify
|
151
|
+
@project.statuses.count.should == 0
|
152
|
+
|
153
|
+
@project2.statuses.count.should == 2
|
154
|
+
Status.count.should == 4
|
155
|
+
Status.count(:name => '1').should == 2
|
156
|
+
Status.count(:name => '2').should == 2
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
context "Finding scoped to association" do
|
161
|
+
setup do
|
162
|
+
@project1 = Project.new(:name => 'Project 1')
|
163
|
+
@brand_new = Status.create(:name => 'New', :position => 1 )
|
164
|
+
@complete = Status.create(:name => 'Complete', :position => 2)
|
165
|
+
@project1.statuses = [@brand_new, @complete]
|
166
|
+
@project1.save
|
167
|
+
|
168
|
+
@project2 = Project.create(:name => 'Project 2')
|
169
|
+
@in_progress = Status.create(:name => 'In Progress')
|
170
|
+
@archived = Status.create(:name => 'Archived')
|
171
|
+
@another_complete = Status.create(:name => 'Complete')
|
172
|
+
@project2.statuses = [@in_progress, @archived, @another_complete]
|
173
|
+
@project2.save
|
174
|
+
end
|
175
|
+
|
176
|
+
context "with :all" do
|
177
|
+
should "work" do
|
178
|
+
@project1.statuses.find(:all, :order => "position asc").should == [@brand_new, @complete]
|
179
|
+
end
|
180
|
+
|
181
|
+
should "work with conditions" do
|
182
|
+
statuses = @project1.statuses.find(:all, :conditions => {'name' => 'Complete'})
|
183
|
+
statuses.should == [@complete]
|
184
|
+
end
|
185
|
+
|
186
|
+
should "work with order" do
|
187
|
+
statuses = @project1.statuses.find(:all, :order => 'name asc')
|
188
|
+
statuses.should == [@complete, @brand_new]
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
context "with #all" do
|
193
|
+
should "work" do
|
194
|
+
@project1.statuses.all(:order => "position asc").should == [@brand_new, @complete]
|
195
|
+
end
|
196
|
+
|
197
|
+
should "work with conditions" do
|
198
|
+
statuses = @project1.statuses.all(:conditions => {'name' => 'Complete'})
|
199
|
+
statuses.should == [@complete]
|
200
|
+
end
|
201
|
+
|
202
|
+
should "work with order" do
|
203
|
+
statuses = @project1.statuses.all(:order => 'name asc')
|
204
|
+
statuses.should == [@complete, @brand_new]
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
context "with :first" do
|
209
|
+
should "work" do
|
210
|
+
@project1.statuses.find(:first, :order => 'name').should == @complete
|
211
|
+
end
|
212
|
+
|
213
|
+
should "work with conditions" do
|
214
|
+
status = @project1.statuses.find(:first, :conditions => {:name => 'Complete'})
|
215
|
+
status.should == @complete
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
context "with #first" do
|
220
|
+
should "work" do
|
221
|
+
@project1.statuses.first(:order => 'name').should == @complete
|
222
|
+
end
|
223
|
+
|
224
|
+
should "work with conditions" do
|
225
|
+
status = @project1.statuses.first(:conditions => {:name => 'Complete'})
|
226
|
+
status.should == @complete
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
context "with :last" do
|
231
|
+
should "work" do
|
232
|
+
@project1.statuses.find(:last, :order => "position asc").should == @complete
|
233
|
+
end
|
234
|
+
|
235
|
+
should "work with conditions" do
|
236
|
+
status = @project1.statuses.find(:last, :conditions => {:name => 'New'})
|
237
|
+
status.should == @brand_new
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
context "with #last" do
|
242
|
+
should "work" do
|
243
|
+
@project1.statuses.last(:order => "position asc").should == @complete
|
244
|
+
end
|
245
|
+
|
246
|
+
should "work with conditions" do
|
247
|
+
status = @project1.statuses.last(:conditions => {:name => 'New'})
|
248
|
+
status.should == @brand_new
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
context "with one id" do
|
253
|
+
should "work for id in association" do
|
254
|
+
@project1.statuses.find(@complete.id).should == @complete
|
255
|
+
end
|
256
|
+
|
257
|
+
should "not work for id not in association" do
|
258
|
+
lambda {
|
259
|
+
@project1.statuses.find(@archived.id)
|
260
|
+
}.should raise_error(MongoMapper::DocumentNotFound)
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
context "with multiple ids" do
|
265
|
+
should "work for ids in association" do
|
266
|
+
statuses = @project1.statuses.find(@brand_new.id, @complete.id)
|
267
|
+
statuses.should == [@brand_new, @complete]
|
268
|
+
end
|
269
|
+
|
270
|
+
should "not work for ids not in association" do
|
271
|
+
lambda {
|
272
|
+
@project1.statuses.find(@brand_new.id, @complete.id, @archived.id)
|
273
|
+
}.should raise_error(MongoMapper::DocumentNotFound)
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
context "with #paginate" do
|
278
|
+
setup do
|
279
|
+
@statuses = @project2.statuses.paginate(:per_page => 2, :page => 1, :order => 'name asc')
|
280
|
+
end
|
281
|
+
|
282
|
+
should "return total pages" do
|
283
|
+
@statuses.total_pages.should == 2
|
284
|
+
end
|
285
|
+
|
286
|
+
should "return total entries" do
|
287
|
+
@statuses.total_entries.should == 3
|
288
|
+
end
|
289
|
+
|
290
|
+
should "return the subject" do
|
291
|
+
@statuses.collect(&:name).should == %w(Archived Complete)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|
295
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'models'
|
3
|
+
|
4
|
+
class AssociationsTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
clear_all_collections
|
7
|
+
end
|
8
|
+
|
9
|
+
should "allow changing class names" do
|
10
|
+
class AwesomeUser
|
11
|
+
include MongoMapper::Document
|
12
|
+
|
13
|
+
many :posts, :class_name => 'AssociationsTest::AwesomePost', :foreign_key => :creator_id
|
14
|
+
end
|
15
|
+
|
16
|
+
class AwesomeTag
|
17
|
+
include MongoMapper::EmbeddedDocument
|
18
|
+
|
19
|
+
key :name, String
|
20
|
+
key :post_id, String
|
21
|
+
|
22
|
+
belongs_to :post, :class_name => 'AssociationsTest::AwesomeUser'
|
23
|
+
end
|
24
|
+
|
25
|
+
class AwesomePost
|
26
|
+
include MongoMapper::Document
|
27
|
+
|
28
|
+
key :creator_id, String
|
29
|
+
|
30
|
+
belongs_to :creator, :class_name => 'AssociationsTest::AwesomeUser'
|
31
|
+
many :tags, :class_name => 'AssociationsTest::AwesomeTag', :foreign_key => :post_id
|
32
|
+
end
|
33
|
+
|
34
|
+
AwesomeUser.collection.clear
|
35
|
+
AwesomePost.collection.clear
|
36
|
+
|
37
|
+
user = AwesomeUser.create
|
38
|
+
tag1 = AwesomeTag.new(:name => 'awesome')
|
39
|
+
tag2 = AwesomeTag.new(:name => 'grand')
|
40
|
+
post1 = AwesomePost.create(:creator => user, :tags => [tag1])
|
41
|
+
post2 = AwesomePost.create(:creator => user, :tags => [tag2])
|
42
|
+
user.posts.should == [post1, post2]
|
43
|
+
|
44
|
+
post1_from_db = AwesomePost.find(post1.id)
|
45
|
+
post1_from_db.tags.should == [tag1]
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class CallbacksTest < Test::Unit::TestCase
|
4
|
+
context "Defining and running callbacks" do
|
5
|
+
setup do
|
6
|
+
@document = Class.new do
|
7
|
+
include MongoMapper::Document
|
8
|
+
|
9
|
+
key :name, String
|
10
|
+
|
11
|
+
[ :before_validation_on_create, :before_validation_on_update,
|
12
|
+
:before_validation, :after_validation,
|
13
|
+
:before_create, :after_create,
|
14
|
+
:before_update, :after_update,
|
15
|
+
:before_save, :after_save,
|
16
|
+
:before_destroy, :after_destroy].each do |callback|
|
17
|
+
callback_method = "#{callback}_callback"
|
18
|
+
send(callback, callback_method)
|
19
|
+
define_method(callback_method) do
|
20
|
+
history << callback.to_sym
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def history
|
25
|
+
@history ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def clear_history
|
29
|
+
@history = nil
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
clear_all_collections
|
34
|
+
end
|
35
|
+
|
36
|
+
should "get the order right for creating documents" do
|
37
|
+
doc = @document.create(:name => 'John Nunemaker')
|
38
|
+
doc.history.should == [:before_validation, :before_validation_on_create, :after_validation, :before_save, :before_create, :after_create, :after_save]
|
39
|
+
end
|
40
|
+
|
41
|
+
should "get the order right for updating documents" do
|
42
|
+
doc = @document.create(:name => 'John Nunemaker')
|
43
|
+
doc.clear_history
|
44
|
+
doc.name = 'John'
|
45
|
+
doc.save
|
46
|
+
doc.history.should == [:before_validation, :before_validation_on_update, :after_validation, :before_save, :before_update, :after_update, :after_save]
|
47
|
+
end
|
48
|
+
|
49
|
+
should "work for before and after validation" do
|
50
|
+
doc = @document.new(:name => 'John Nunemaker')
|
51
|
+
doc.valid?
|
52
|
+
doc.history.should include(:before_validation)
|
53
|
+
doc.history.should include(:after_validation)
|
54
|
+
end
|
55
|
+
|
56
|
+
should "work for before and after create" do
|
57
|
+
doc = @document.create(:name => 'John Nunemaker')
|
58
|
+
doc.history.should include(:before_create)
|
59
|
+
doc.history.should include(:after_create)
|
60
|
+
end
|
61
|
+
|
62
|
+
should "work for before and after update" do
|
63
|
+
doc = @document.create(:name => 'John Nunemaker')
|
64
|
+
doc.name = 'John Doe'
|
65
|
+
doc.save
|
66
|
+
doc.history.should include(:before_update)
|
67
|
+
doc.history.should include(:after_update)
|
68
|
+
end
|
69
|
+
|
70
|
+
should "work for before and after save" do
|
71
|
+
doc = @document.new
|
72
|
+
doc.name = 'John Doe'
|
73
|
+
doc.save
|
74
|
+
doc.history.should include(:before_save)
|
75
|
+
doc.history.should include(:after_save)
|
76
|
+
end
|
77
|
+
|
78
|
+
should "work for before and after destroy" do
|
79
|
+
doc = @document.create(:name => 'John Nunemaker')
|
80
|
+
doc.destroy
|
81
|
+
doc.history.should include(:before_destroy)
|
82
|
+
doc.history.should include(:after_destroy)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,952 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'models'
|
3
|
+
|
4
|
+
class DocumentTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@document = Class.new do
|
7
|
+
include MongoMapper::Document
|
8
|
+
collection 'users'
|
9
|
+
|
10
|
+
key :first_name, String
|
11
|
+
key :last_name, String
|
12
|
+
key :age, Integer
|
13
|
+
end
|
14
|
+
|
15
|
+
@document.collection.clear
|
16
|
+
end
|
17
|
+
|
18
|
+
context "Saving a document with a custom id" do
|
19
|
+
should "clear custom id flag when saved" do
|
20
|
+
doc = @document.new(:id => '1234')
|
21
|
+
doc.using_custom_id?.should be_true
|
22
|
+
doc.save.should be_true
|
23
|
+
doc.using_custom_id?.should be_false
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context "Loading a document from the database with keys that are not defined" do
|
28
|
+
setup do
|
29
|
+
@id = XGen::Mongo::Driver::ObjectID.new.to_s
|
30
|
+
@document.collection.insert({
|
31
|
+
:_id => @id,
|
32
|
+
:first_name => 'John',
|
33
|
+
:last_name => 'Nunemaker',
|
34
|
+
:age => 27,
|
35
|
+
:favorite_color => 'red',
|
36
|
+
:skills => ['ruby', 'rails', 'javascript', 'xhtml', 'css']
|
37
|
+
})
|
38
|
+
end
|
39
|
+
|
40
|
+
should "assign all keys from database" do
|
41
|
+
doc = @document.find(@id)
|
42
|
+
doc.first_name.should == 'John'
|
43
|
+
doc.last_name.should == 'Nunemaker'
|
44
|
+
doc.age.should == 27
|
45
|
+
doc.favorite_color.should == 'red'
|
46
|
+
doc.skills.should == ['ruby', 'rails', 'javascript', 'xhtml', 'css']
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
context "Document Class Methods" do
|
51
|
+
context "Using key with type Array" do
|
52
|
+
setup do
|
53
|
+
@document.key :tags, Array
|
54
|
+
end
|
55
|
+
|
56
|
+
should "give correct default" do
|
57
|
+
doc = @document.new
|
58
|
+
doc.tags.should == []
|
59
|
+
end
|
60
|
+
|
61
|
+
should "work with assignment" do
|
62
|
+
doc = @document.new
|
63
|
+
doc.tags = %w(foo bar)
|
64
|
+
doc.tags.should == %w(foo bar)
|
65
|
+
end
|
66
|
+
|
67
|
+
should "work with assignment after saving" do
|
68
|
+
doc = @document.new
|
69
|
+
doc.tags = %w(foo bar)
|
70
|
+
doc.save
|
71
|
+
doc.tags.should == %w(foo bar)
|
72
|
+
@document.find(doc.id).tags.should == %w(foo bar)
|
73
|
+
end
|
74
|
+
|
75
|
+
should "work with assignment then <<" do
|
76
|
+
doc = @document.new
|
77
|
+
doc.tags = []
|
78
|
+
doc.tags << "foo"
|
79
|
+
doc.tags.should == ["foo"]
|
80
|
+
end
|
81
|
+
|
82
|
+
should "work with <<" do
|
83
|
+
doc = @document.new
|
84
|
+
doc.tags << "foo"
|
85
|
+
doc.tags.should == ["foo"]
|
86
|
+
end
|
87
|
+
|
88
|
+
should "work with << then save" do
|
89
|
+
doc = @document.new
|
90
|
+
doc.tags << "foo"
|
91
|
+
doc.tags << "bar"
|
92
|
+
doc.save
|
93
|
+
doc.tags.should == %w(foo bar)
|
94
|
+
@document.find(doc.id).tags.should == %w(foo bar)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "Using key with type Hash" do
|
99
|
+
setup do
|
100
|
+
@document.key :foo, Hash
|
101
|
+
end
|
102
|
+
|
103
|
+
should "give correct default" do
|
104
|
+
doc = @document.new
|
105
|
+
doc.foo.should == {}
|
106
|
+
end
|
107
|
+
|
108
|
+
should "work with []=" do
|
109
|
+
doc = @document.new
|
110
|
+
doc.foo["quux"] = "bar"
|
111
|
+
doc.foo["quux"].should == "bar"
|
112
|
+
doc.foo.should == { "quux" => "bar" }
|
113
|
+
end
|
114
|
+
|
115
|
+
should "work with indifferent access" do
|
116
|
+
doc = @document.new
|
117
|
+
doc.foo = {:baz => 'bar'}
|
118
|
+
doc.foo[:baz].should == 'bar'
|
119
|
+
doc.foo['baz'].should == 'bar'
|
120
|
+
end
|
121
|
+
|
122
|
+
should "work with indifferent access after save" do
|
123
|
+
doc = @document.new
|
124
|
+
doc.foo = {:baz => 'bar'}
|
125
|
+
doc.save
|
126
|
+
|
127
|
+
doc = @document.find(doc.id)
|
128
|
+
doc.foo[:baz].should == 'bar'
|
129
|
+
doc.foo['baz'].should == 'bar'
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "Saving a document with an embedded document" do
|
134
|
+
setup do
|
135
|
+
@document.class_eval do
|
136
|
+
key :foo, Address
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
should "embed embedded document" do
|
141
|
+
address = Address.new(:city => 'South Bend', :state => 'IN')
|
142
|
+
doc = @document.new(:foo => address)
|
143
|
+
doc.save
|
144
|
+
doc.foo.city.should == 'South Bend'
|
145
|
+
doc.foo.state.should == 'IN'
|
146
|
+
|
147
|
+
from_db = @document.find(doc.id)
|
148
|
+
from_db.foo.city.should == 'South Bend'
|
149
|
+
from_db.foo.state.should == 'IN'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
context "Creating a single document" do
|
154
|
+
setup do
|
155
|
+
@doc_instance = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
156
|
+
end
|
157
|
+
|
158
|
+
should "create a document in correct collection" do
|
159
|
+
@document.count.should == 1
|
160
|
+
end
|
161
|
+
|
162
|
+
should "automatically set id" do
|
163
|
+
@doc_instance.id.should_not be_nil
|
164
|
+
@doc_instance.id.size.should == 24
|
165
|
+
end
|
166
|
+
|
167
|
+
should "no longer be new?" do
|
168
|
+
@doc_instance.new?.should be_false
|
169
|
+
end
|
170
|
+
|
171
|
+
should "return instance of document" do
|
172
|
+
@doc_instance.should be_instance_of(@document)
|
173
|
+
@doc_instance.first_name.should == 'John'
|
174
|
+
@doc_instance.last_name.should == 'Nunemaker'
|
175
|
+
@doc_instance.age.should == 27
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
context "Creating a document with no attributes provided" do
|
180
|
+
setup do
|
181
|
+
@document = Class.new do
|
182
|
+
include MongoMapper::Document
|
183
|
+
end
|
184
|
+
@document.collection.clear
|
185
|
+
end
|
186
|
+
|
187
|
+
should "create the document" do
|
188
|
+
lambda {
|
189
|
+
@document.create
|
190
|
+
}.should change { @document.count }.by(1)
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
context "Creating multiple documents" do
|
195
|
+
setup do
|
196
|
+
@doc_instances = @document.create([
|
197
|
+
{:first_name => 'John', :last_name => 'Nunemaker', :age => '27'},
|
198
|
+
{:first_name => 'Steve', :last_name => 'Smith', :age => '28'},
|
199
|
+
])
|
200
|
+
end
|
201
|
+
|
202
|
+
should "create multiple documents" do
|
203
|
+
@document.count.should == 2
|
204
|
+
end
|
205
|
+
|
206
|
+
should "return an array of doc instances" do
|
207
|
+
@doc_instances.map do |doc_instance|
|
208
|
+
doc_instance.should be_instance_of(@document)
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|
212
|
+
|
213
|
+
context "Updating a document" do
|
214
|
+
setup do
|
215
|
+
doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
216
|
+
@doc_instance = @document.update(doc.id, {:age => 40})
|
217
|
+
end
|
218
|
+
|
219
|
+
should "update attributes provided" do
|
220
|
+
@doc_instance.age.should == 40
|
221
|
+
end
|
222
|
+
|
223
|
+
should "not update existing attributes that were not set to update" do
|
224
|
+
@doc_instance.first_name.should == 'John'
|
225
|
+
@doc_instance.last_name.should == 'Nunemaker'
|
226
|
+
end
|
227
|
+
|
228
|
+
should "not create new document" do
|
229
|
+
@document.count.should == 1
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
should "raise error when updating single doc if not provided id and attributes" do
|
234
|
+
doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
235
|
+
lambda { @document.update }.should raise_error(ArgumentError)
|
236
|
+
lambda { @document.update(doc.id) }.should raise_error(ArgumentError)
|
237
|
+
lambda { @document.update(doc.id, [1]) }.should raise_error(ArgumentError)
|
238
|
+
end
|
239
|
+
|
240
|
+
context "Updating multiple documents" do
|
241
|
+
setup do
|
242
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
243
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
244
|
+
|
245
|
+
@doc_instances = @document.update({
|
246
|
+
@doc1.id => {:age => 30},
|
247
|
+
@doc2.id => {:age => 30},
|
248
|
+
})
|
249
|
+
end
|
250
|
+
|
251
|
+
should "not create any new documents" do
|
252
|
+
@document.count.should == 2
|
253
|
+
end
|
254
|
+
|
255
|
+
should "should return an array of doc instances" do
|
256
|
+
@doc_instances.map do |doc_instance|
|
257
|
+
doc_instance.should be_instance_of(@document)
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
should "update the documents" do
|
262
|
+
@document.find(@doc1.id).age.should == 30
|
263
|
+
@document.find(@doc2.id).age.should == 30
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
should "raise error when updating multiple documents if not a hash" do
|
268
|
+
lambda { @document.update([1, 2]) }.should raise_error(ArgumentError)
|
269
|
+
end
|
270
|
+
|
271
|
+
context "Finding documents" do
|
272
|
+
setup do
|
273
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
274
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
275
|
+
@doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
|
276
|
+
end
|
277
|
+
|
278
|
+
should "raise document not found if nothing provided" do
|
279
|
+
lambda { @document.find }.should raise_error(MongoMapper::DocumentNotFound)
|
280
|
+
end
|
281
|
+
|
282
|
+
context "with a single id" do
|
283
|
+
should "work" do
|
284
|
+
@document.find(@doc1.id).should == @doc1
|
285
|
+
end
|
286
|
+
|
287
|
+
should "raise error if document not found" do
|
288
|
+
lambda { @document.find(123) }.should raise_error(MongoMapper::DocumentNotFound)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
context "with multiple id's" do
|
293
|
+
should "work as arguments" do
|
294
|
+
@document.find(@doc1.id, @doc2.id).should == [@doc1, @doc2]
|
295
|
+
end
|
296
|
+
|
297
|
+
should "work as array" do
|
298
|
+
@document.find([@doc1.id, @doc2.id]).should == [@doc1, @doc2]
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
context "with :all" do
|
303
|
+
should "find all documents" do
|
304
|
+
@document.find(:all, :order => 'first_name').should == [@doc1, @doc3, @doc2]
|
305
|
+
end
|
306
|
+
|
307
|
+
should "be able to add conditions" do
|
308
|
+
@document.find(:all, :conditions => {:first_name => 'John'}).should == [@doc1]
|
309
|
+
end
|
310
|
+
end
|
311
|
+
|
312
|
+
context "with #all" do
|
313
|
+
should "find all documents based on criteria" do
|
314
|
+
@document.all(:order => 'first_name').should == [@doc1, @doc3, @doc2]
|
315
|
+
@document.all(:conditions => {:last_name => 'Nunemaker'}, :order => 'age desc').should == [@doc1, @doc3]
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
context "with :first" do
|
320
|
+
should "find first document" do
|
321
|
+
@document.find(:first, :order => 'first_name').should == @doc1
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
context "with #first" do
|
326
|
+
should "find first document based on criteria" do
|
327
|
+
@document.first(:order => 'first_name').should == @doc1
|
328
|
+
@document.first(:conditions => {:age => 28}).should == @doc2
|
329
|
+
end
|
330
|
+
end
|
331
|
+
|
332
|
+
context "with :last" do
|
333
|
+
should "find last document" do
|
334
|
+
@document.find(:last, :order => 'age').should == @doc2
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
context "with #last" do
|
339
|
+
should "find last document based on criteria" do
|
340
|
+
@document.last(:order => 'age').should == @doc2
|
341
|
+
@document.last(:conditions => {:age => 28}).should == @doc2
|
342
|
+
end
|
343
|
+
end
|
344
|
+
|
345
|
+
context "with :find_by" do
|
346
|
+
should "find document based on argument" do
|
347
|
+
@document.find_by_first_name('John').should == @doc1
|
348
|
+
@document.find_by_last_name('Nunemaker', :order => 'age desc').should == @doc1
|
349
|
+
@document.find_by_age(27).should == @doc1
|
350
|
+
end
|
351
|
+
|
352
|
+
should "not raise error" do
|
353
|
+
@document.find_by_first_name('Mongo').should be_nil
|
354
|
+
end
|
355
|
+
|
356
|
+
should "define a method for each key" do
|
357
|
+
@document.methods(false).select { |e| e =~ /^find_by_/ }.size == @document.keys.size
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
context "with dynamic finders" do
|
362
|
+
should "find document based on all arguments" do
|
363
|
+
@document.find_by_first_name_and_last_name_and_age('John', 'Nunemaker', 27).should == @doc1
|
364
|
+
end
|
365
|
+
|
366
|
+
should "not find the document if an argument is wrong" do
|
367
|
+
@document.find_by_first_name_and_last_name_and_age('John', 'Nunemaker', 28).should be_nil
|
368
|
+
end
|
369
|
+
|
370
|
+
should "find all documents based on arguments" do
|
371
|
+
docs = @document.find_all_by_last_name('Nunemaker')
|
372
|
+
docs.should be_kind_of(Array)
|
373
|
+
docs.should include(@doc1)
|
374
|
+
docs.should include(@doc3)
|
375
|
+
end
|
376
|
+
|
377
|
+
should "find last document based on arguments" do
|
378
|
+
doc = @document.find_last_by_last_name('Nunemaker', :order => 'age')
|
379
|
+
doc.should == @doc1
|
380
|
+
end
|
381
|
+
|
382
|
+
should "initialize document with given arguments" do
|
383
|
+
doc = @document.find_or_initialize_by_first_name_and_last_name('David', 'Cuadrado')
|
384
|
+
doc.should be_new
|
385
|
+
doc.first_name.should == 'David'
|
386
|
+
end
|
387
|
+
|
388
|
+
should "not initialize document if document is found" do
|
389
|
+
doc = @document.find_or_initialize_by_first_name('John')
|
390
|
+
doc.should_not be_new
|
391
|
+
end
|
392
|
+
|
393
|
+
should "create document with given arguments" do
|
394
|
+
doc = @document.find_or_create_by_first_name_and_last_name('David', 'Cuadrado')
|
395
|
+
doc.should_not be_new
|
396
|
+
doc.first_name.should == 'David'
|
397
|
+
end
|
398
|
+
|
399
|
+
should "raise error if document is not found" do
|
400
|
+
lambda {@document.find_by_first_name_and_last_name!(1,2)}.should raise_error(MongoMapper::DocumentNotFound)
|
401
|
+
end
|
402
|
+
end
|
403
|
+
end # finding documents
|
404
|
+
|
405
|
+
context "Finding document by id" do
|
406
|
+
setup do
|
407
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
408
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
409
|
+
end
|
410
|
+
|
411
|
+
should "be able to find by id" do
|
412
|
+
@document.find_by_id(@doc1.id).should == @doc1
|
413
|
+
@document.find_by_id(@doc2.id).should == @doc2
|
414
|
+
end
|
415
|
+
|
416
|
+
should "return nil if document not found" do
|
417
|
+
@document.find_by_id(1234).should be(nil)
|
418
|
+
end
|
419
|
+
end
|
420
|
+
|
421
|
+
context "Deleting a document" do
|
422
|
+
setup do
|
423
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
424
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
425
|
+
@document.delete(@doc1.id)
|
426
|
+
end
|
427
|
+
|
428
|
+
should "remove document from collection" do
|
429
|
+
@document.count.should == 1
|
430
|
+
end
|
431
|
+
|
432
|
+
should "not remove other documents" do
|
433
|
+
@document.find(@doc2.id).should_not be(nil)
|
434
|
+
end
|
435
|
+
end
|
436
|
+
|
437
|
+
context "Deleting multiple documents" do
|
438
|
+
should "work with multiple arguments" do
|
439
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
440
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
441
|
+
@doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
|
442
|
+
@document.delete(@doc1.id, @doc2.id)
|
443
|
+
|
444
|
+
@document.count.should == 1
|
445
|
+
end
|
446
|
+
|
447
|
+
should "work with array as argument" do
|
448
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
449
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
450
|
+
@doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
|
451
|
+
@document.delete([@doc1.id, @doc2.id])
|
452
|
+
|
453
|
+
@document.count.should == 1
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
context "Deleting all documents" do
|
458
|
+
setup do
|
459
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
460
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
461
|
+
@doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
|
462
|
+
end
|
463
|
+
|
464
|
+
should "remove all documents when given no conditions" do
|
465
|
+
@document.delete_all
|
466
|
+
@document.count.should == 0
|
467
|
+
end
|
468
|
+
|
469
|
+
should "only remove matching documents when given conditions" do
|
470
|
+
@document.delete_all({:first_name => 'John'})
|
471
|
+
@document.count.should == 2
|
472
|
+
end
|
473
|
+
|
474
|
+
should "convert the conditions to mongo criteria" do
|
475
|
+
@document.delete_all(:age => [26, 27])
|
476
|
+
@document.count.should == 1
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
context "Destroying a document" do
|
481
|
+
setup do
|
482
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
483
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
484
|
+
@document.destroy(@doc1.id)
|
485
|
+
end
|
486
|
+
|
487
|
+
should "remove document from collection" do
|
488
|
+
@document.count.should == 1
|
489
|
+
end
|
490
|
+
|
491
|
+
should "not remove other documents" do
|
492
|
+
@document.find(@doc2.id).should_not be(nil)
|
493
|
+
end
|
494
|
+
end
|
495
|
+
|
496
|
+
context "Destroying multiple documents" do
|
497
|
+
should "work with multiple arguments" do
|
498
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
499
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
500
|
+
@doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
|
501
|
+
@document.destroy(@doc1.id, @doc2.id)
|
502
|
+
|
503
|
+
@document.count.should == 1
|
504
|
+
end
|
505
|
+
|
506
|
+
should "work with array as argument" do
|
507
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
508
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
509
|
+
@doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
|
510
|
+
@document.destroy([@doc1.id, @doc2.id])
|
511
|
+
|
512
|
+
@document.count.should == 1
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
context "Destroying all documents" do
|
517
|
+
setup do
|
518
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
519
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
520
|
+
@doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
|
521
|
+
end
|
522
|
+
|
523
|
+
should "remove all documents when given no conditions" do
|
524
|
+
@document.destroy_all
|
525
|
+
@document.count.should == 0
|
526
|
+
end
|
527
|
+
|
528
|
+
should "only remove matching documents when given conditions" do
|
529
|
+
@document.destroy_all(:first_name => 'John')
|
530
|
+
@document.count.should == 2
|
531
|
+
@document.destroy_all(:age => 26)
|
532
|
+
@document.count.should == 1
|
533
|
+
end
|
534
|
+
|
535
|
+
should "convert the conditions to mongo criteria" do
|
536
|
+
@document.destroy_all(:age => [26, 27])
|
537
|
+
@document.count.should == 1
|
538
|
+
end
|
539
|
+
end
|
540
|
+
|
541
|
+
context ":dependent" do
|
542
|
+
setup do
|
543
|
+
# FIXME: make use of already defined models
|
544
|
+
class ::Property
|
545
|
+
include MongoMapper::Document
|
546
|
+
end
|
547
|
+
Property.delete_all
|
548
|
+
|
549
|
+
class ::Thing
|
550
|
+
include MongoMapper::Document
|
551
|
+
key :name, String
|
552
|
+
end
|
553
|
+
Thing.delete_all
|
554
|
+
end
|
555
|
+
|
556
|
+
teardown do
|
557
|
+
Object.send :remove_const, 'Property' if defined?(::Property)
|
558
|
+
Object.send :remove_const, 'Thing' if defined?(::Thing)
|
559
|
+
end
|
560
|
+
|
561
|
+
context "many" do
|
562
|
+
context "=> destroy" do
|
563
|
+
setup do
|
564
|
+
Property.key :thing_id, String
|
565
|
+
Property.belongs_to :thing, :dependent => :destroy
|
566
|
+
Thing.many :properties, :dependent => :destroy
|
567
|
+
|
568
|
+
@thing = Thing.create(:name => "Tree")
|
569
|
+
@property1 = Property.create
|
570
|
+
@property2 = Property.create
|
571
|
+
@property3 = Property.create
|
572
|
+
@thing.properties << @property1
|
573
|
+
@thing.properties << @property2
|
574
|
+
@thing.properties << @property3
|
575
|
+
end
|
576
|
+
|
577
|
+
should "should destroy the associated documents" do
|
578
|
+
@thing.properties.count.should == 3
|
579
|
+
@thing.destroy
|
580
|
+
@thing.properties.count.should == 0
|
581
|
+
Property.count.should == 0
|
582
|
+
end
|
583
|
+
end
|
584
|
+
|
585
|
+
context "=> delete_all" do
|
586
|
+
setup do
|
587
|
+
Property.key :thing_id, String
|
588
|
+
Property.belongs_to :thing
|
589
|
+
Thing.has_many :properties, :dependent => :delete_all
|
590
|
+
|
591
|
+
@thing = Thing.create(:name => "Tree")
|
592
|
+
@property1 = Property.create
|
593
|
+
@property2 = Property.create
|
594
|
+
@property3 = Property.create
|
595
|
+
@thing.properties << @property1
|
596
|
+
@thing.properties << @property2
|
597
|
+
@thing.properties << @property3
|
598
|
+
end
|
599
|
+
|
600
|
+
should "should delete associated documents" do
|
601
|
+
@thing.properties.count.should == 3
|
602
|
+
@thing.destroy
|
603
|
+
@thing.properties.count.should == 0
|
604
|
+
Property.count.should == 0
|
605
|
+
end
|
606
|
+
end
|
607
|
+
|
608
|
+
context "=> nullify" do
|
609
|
+
setup do
|
610
|
+
Property.key :thing_id, String
|
611
|
+
Property.belongs_to :thing
|
612
|
+
Thing.has_many :properties, :dependent => :nullify
|
613
|
+
|
614
|
+
@thing = Thing.create(:name => "Tree")
|
615
|
+
@property1 = Property.create
|
616
|
+
@property2 = Property.create
|
617
|
+
@property3 = Property.create
|
618
|
+
@thing.properties << @property1
|
619
|
+
@thing.properties << @property2
|
620
|
+
@thing.properties << @property3
|
621
|
+
end
|
622
|
+
|
623
|
+
should "should nullify relationship but not destroy associated documents" do
|
624
|
+
@thing.properties.count.should == 3
|
625
|
+
@thing.destroy
|
626
|
+
@thing.properties.count.should == 0
|
627
|
+
Property.count.should == 3
|
628
|
+
end
|
629
|
+
end
|
630
|
+
end
|
631
|
+
|
632
|
+
context "belongs_to" do
|
633
|
+
context "=> destroy" do
|
634
|
+
setup do
|
635
|
+
Property.key :thing_id, String
|
636
|
+
Property.belongs_to :thing, :dependent => :destroy
|
637
|
+
Thing.has_many :properties
|
638
|
+
|
639
|
+
@thing = Thing.create(:name => "Tree")
|
640
|
+
@property1 = Property.create
|
641
|
+
@property2 = Property.create
|
642
|
+
@property3 = Property.create
|
643
|
+
@thing.properties << @property1
|
644
|
+
@thing.properties << @property2
|
645
|
+
@thing.properties << @property3
|
646
|
+
end
|
647
|
+
|
648
|
+
should "destroy the thing" do
|
649
|
+
Thing.count.should == 1
|
650
|
+
@property1.destroy
|
651
|
+
Thing.count.should == 0
|
652
|
+
@property1.thing.should be_frozen
|
653
|
+
end
|
654
|
+
end
|
655
|
+
end
|
656
|
+
end
|
657
|
+
|
658
|
+
context "Counting documents in collection" do
|
659
|
+
setup do
|
660
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
661
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
662
|
+
@doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
|
663
|
+
end
|
664
|
+
|
665
|
+
should "count all with no arguments" do
|
666
|
+
@document.count.should == 3
|
667
|
+
end
|
668
|
+
|
669
|
+
should "return 0 if there are no documents in the collection" do
|
670
|
+
@document.delete_all
|
671
|
+
@document.count.should == 0
|
672
|
+
end
|
673
|
+
|
674
|
+
should "return 0 if the collection does not exist" do
|
675
|
+
klass = Class.new do
|
676
|
+
include MongoMapper::Document
|
677
|
+
collection 'foobarbazwickdoesnotexist'
|
678
|
+
end
|
679
|
+
|
680
|
+
klass.count.should == 0
|
681
|
+
end
|
682
|
+
|
683
|
+
should "return count for matching documents if conditions provided" do
|
684
|
+
@document.count(:age => 27).should == 1
|
685
|
+
end
|
686
|
+
|
687
|
+
should "convert the conditions to mongo criteria" do
|
688
|
+
@document.count(:age => [26, 27]).should == 2
|
689
|
+
end
|
690
|
+
end
|
691
|
+
|
692
|
+
context "Indexing" do
|
693
|
+
setup do
|
694
|
+
@document.collection.drop_indexes
|
695
|
+
end
|
696
|
+
|
697
|
+
should "allow creating index for a key" do
|
698
|
+
index_name = nil
|
699
|
+
lambda {
|
700
|
+
index_name = @document.ensure_index :first_name
|
701
|
+
}.should change { @document.collection.index_information.size }.by(1)
|
702
|
+
|
703
|
+
index_name.should == 'first_name_1'
|
704
|
+
index = @document.collection.index_information[index_name]
|
705
|
+
index.should_not be_nil
|
706
|
+
index.should include(['first_name', 1])
|
707
|
+
end
|
708
|
+
|
709
|
+
should "allow creating unique index for a key" do
|
710
|
+
@document.collection.expects(:create_index).with(:first_name, true)
|
711
|
+
@document.ensure_index :first_name, :unique => true
|
712
|
+
end
|
713
|
+
|
714
|
+
should "allow creating index on multiple keys" do
|
715
|
+
index_name = nil
|
716
|
+
lambda {
|
717
|
+
index_name = @document.ensure_index [[:first_name, 1], [:last_name, -1]]
|
718
|
+
}.should change { @document.collection.index_information.size }.by(1)
|
719
|
+
|
720
|
+
[ 'first_name_1_last_name_-1', 'last_name_-1_first_name_1' ].should include(index_name)
|
721
|
+
|
722
|
+
index = @document.collection.index_information[index_name]
|
723
|
+
index.should_not be_nil
|
724
|
+
index.should include(['first_name', 1])
|
725
|
+
index.should include(['last_name', -1])
|
726
|
+
end
|
727
|
+
|
728
|
+
should "work with :index shortcut when defining key" do
|
729
|
+
@document.expects(:ensure_index).with('father').returns(nil)
|
730
|
+
@document.key :father, String, :index => true
|
731
|
+
end
|
732
|
+
end
|
733
|
+
end # Document Class Methods
|
734
|
+
|
735
|
+
context "Saving a new document" do
|
736
|
+
setup do
|
737
|
+
@doc = @document.new(:first_name => 'John', :age => '27')
|
738
|
+
@doc.save
|
739
|
+
end
|
740
|
+
|
741
|
+
should "insert document into the collection" do
|
742
|
+
@document.count.should == 1
|
743
|
+
end
|
744
|
+
|
745
|
+
should "assign an id for the document" do
|
746
|
+
@doc.id.should_not be(nil)
|
747
|
+
@doc.id.size.should == 24
|
748
|
+
end
|
749
|
+
|
750
|
+
should "save attributes" do
|
751
|
+
@doc.first_name.should == 'John'
|
752
|
+
@doc.age.should == 27
|
753
|
+
end
|
754
|
+
|
755
|
+
should "update attributes in the database" do
|
756
|
+
from_db = @document.find(@doc.id)
|
757
|
+
from_db.should == @doc
|
758
|
+
from_db.first_name.should == 'John'
|
759
|
+
from_db.age.should == 27
|
760
|
+
end
|
761
|
+
|
762
|
+
should "allow to add custom attributes to the document" do
|
763
|
+
@doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male', :tags => [1, "2"])
|
764
|
+
@doc.save
|
765
|
+
from_db = @document.find(@doc.id)
|
766
|
+
from_db.gender.should == 'male'
|
767
|
+
from_db.tags.should == [1, "2"]
|
768
|
+
end
|
769
|
+
|
770
|
+
should "allow to use custom methods to assign properties" do
|
771
|
+
person = RealPerson.new(:realname => "David")
|
772
|
+
person.save
|
773
|
+
from_db = RealPerson.find(person.id)
|
774
|
+
from_db.name.should == "David"
|
775
|
+
end
|
776
|
+
end
|
777
|
+
|
778
|
+
context "Saving an existing document" do
|
779
|
+
setup do
|
780
|
+
@doc = @document.create(:first_name => 'John', :age => '27')
|
781
|
+
@doc.first_name = 'Johnny'
|
782
|
+
@doc.age = 30
|
783
|
+
@doc.save
|
784
|
+
end
|
785
|
+
|
786
|
+
should "not insert document into collection" do
|
787
|
+
@document.count.should == 1
|
788
|
+
end
|
789
|
+
|
790
|
+
should "update attributes" do
|
791
|
+
@doc.first_name.should == 'Johnny'
|
792
|
+
@doc.age.should == 30
|
793
|
+
end
|
794
|
+
|
795
|
+
should "update attributes in the database" do
|
796
|
+
from_db = @document.find(@doc.id)
|
797
|
+
from_db.first_name.should == 'Johnny'
|
798
|
+
from_db.age.should == 30
|
799
|
+
end
|
800
|
+
|
801
|
+
should "allow to update custom attributes" do
|
802
|
+
@doc = @document.new(:first_name => 'David', :age => '26', :gender => 'male')
|
803
|
+
@doc.gender = 'Male'
|
804
|
+
@doc.save
|
805
|
+
from_db = @document.find(@doc.id)
|
806
|
+
from_db.gender.should == 'Male'
|
807
|
+
end
|
808
|
+
end
|
809
|
+
|
810
|
+
context "Calling update attributes on a new document" do
|
811
|
+
setup do
|
812
|
+
@doc = @document.new(:first_name => 'John', :age => '27')
|
813
|
+
@doc.update_attributes(:first_name => 'Johnny', :age => 30)
|
814
|
+
end
|
815
|
+
|
816
|
+
should "insert document into the collection" do
|
817
|
+
@document.count.should == 1
|
818
|
+
end
|
819
|
+
|
820
|
+
should "assign an id for the document" do
|
821
|
+
@doc.id.should_not be(nil)
|
822
|
+
@doc.id.size.should == 24
|
823
|
+
end
|
824
|
+
|
825
|
+
should "save attributes" do
|
826
|
+
@doc.first_name.should == 'Johnny'
|
827
|
+
@doc.age.should == 30
|
828
|
+
end
|
829
|
+
|
830
|
+
should "update attributes in the database" do
|
831
|
+
from_db = @document.find(@doc.id)
|
832
|
+
from_db.should == @doc
|
833
|
+
from_db.first_name.should == 'Johnny'
|
834
|
+
from_db.age.should == 30
|
835
|
+
end
|
836
|
+
|
837
|
+
should "allow to update custom attributes" do
|
838
|
+
@doc.update_attributes(:gender => 'mALe')
|
839
|
+
from_db = @document.find(@doc.id)
|
840
|
+
from_db.gender.should == 'mALe'
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
context "Updating an existing document using update attributes" do
|
845
|
+
setup do
|
846
|
+
@doc = @document.create(:first_name => 'John', :age => '27')
|
847
|
+
@doc.update_attributes(:first_name => 'Johnny', :age => 30)
|
848
|
+
end
|
849
|
+
|
850
|
+
should "not insert document into collection" do
|
851
|
+
@document.count.should == 1
|
852
|
+
end
|
853
|
+
|
854
|
+
should "update attributes" do
|
855
|
+
@doc.first_name.should == 'Johnny'
|
856
|
+
@doc.age.should == 30
|
857
|
+
end
|
858
|
+
|
859
|
+
should "update attributes in the database" do
|
860
|
+
from_db = @document.find(@doc.id)
|
861
|
+
from_db.first_name.should == 'Johnny'
|
862
|
+
from_db.age.should == 30
|
863
|
+
end
|
864
|
+
end
|
865
|
+
|
866
|
+
context "update_attributes" do
|
867
|
+
setup do
|
868
|
+
@document.key :foo, String, :required => true
|
869
|
+
end
|
870
|
+
|
871
|
+
should "return true if document valid" do
|
872
|
+
@document.new.update_attributes(:foo => 'bar').should be_true
|
873
|
+
end
|
874
|
+
|
875
|
+
should "return false if document not valid" do
|
876
|
+
@document.new.update_attributes({}).should be_false
|
877
|
+
end
|
878
|
+
end
|
879
|
+
|
880
|
+
context "Destroying a document that exists" do
|
881
|
+
setup do
|
882
|
+
@doc = @document.create(:first_name => 'John', :age => '27')
|
883
|
+
@doc.destroy
|
884
|
+
end
|
885
|
+
|
886
|
+
should "remove the document from the collection" do
|
887
|
+
@document.count.should == 0
|
888
|
+
end
|
889
|
+
|
890
|
+
should "raise error if assignment is attempted" do
|
891
|
+
lambda { @doc.first_name = 'Foo' }.should raise_error(TypeError)
|
892
|
+
end
|
893
|
+
|
894
|
+
should "do nothing if destroy is called again" do
|
895
|
+
@doc.destroy.should be_false
|
896
|
+
end
|
897
|
+
end
|
898
|
+
|
899
|
+
context "Destroying a document that is a new" do
|
900
|
+
setup do
|
901
|
+
setup do
|
902
|
+
@doc = @document.new(:first_name => 'John Nunemaker', :age => '27')
|
903
|
+
@doc.destroy
|
904
|
+
end
|
905
|
+
|
906
|
+
should "not affect collection count" do
|
907
|
+
@document.collection.count.should == 0
|
908
|
+
end
|
909
|
+
|
910
|
+
should "raise error if assignment is attempted" do
|
911
|
+
lambda { @doc.first_name = 'Foo' }.should raise_error(TypeError)
|
912
|
+
end
|
913
|
+
end
|
914
|
+
end
|
915
|
+
|
916
|
+
context "timestamping" do
|
917
|
+
setup do
|
918
|
+
@document.timestamps!
|
919
|
+
end
|
920
|
+
|
921
|
+
should "set created_at and updated_at on create" do
|
922
|
+
doc = @document.new(:first_name => 'John', :age => 27)
|
923
|
+
doc.created_at.should be(nil)
|
924
|
+
doc.updated_at.should be(nil)
|
925
|
+
doc.save
|
926
|
+
doc.created_at.should_not be(nil)
|
927
|
+
doc.updated_at.should_not be(nil)
|
928
|
+
end
|
929
|
+
|
930
|
+
should "set updated_at on field update but leave created_at alone" do
|
931
|
+
doc = @document.create(:first_name => 'John', :age => 27)
|
932
|
+
old_created_at = doc.created_at
|
933
|
+
old_updated_at = doc.updated_at
|
934
|
+
doc.first_name = 'Johnny'
|
935
|
+
doc.save
|
936
|
+
doc.created_at.should == old_created_at
|
937
|
+
doc.updated_at.should_not == old_updated_at
|
938
|
+
end
|
939
|
+
|
940
|
+
should "set updated_at on document update but leave created_at alone" do
|
941
|
+
doc = @document.create(:first_name => 'John', :age => 27)
|
942
|
+
old_created_at = doc.created_at
|
943
|
+
old_updated_at = doc.updated_at
|
944
|
+
sleep 1 # this annoys me
|
945
|
+
@document.update(doc._id, { :first_name => 'Johnny' })
|
946
|
+
|
947
|
+
from_db = @document.find(doc.id)
|
948
|
+
from_db.created_at.to_i.should == old_created_at.to_i
|
949
|
+
from_db.updated_at.to_i.should_not == old_updated_at.to_i
|
950
|
+
end
|
951
|
+
end
|
952
|
+
end
|