djsun-mongomapper 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +7 -0
  2. data/History +51 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/Rakefile +71 -0
  6. data/VERSION +1 -0
  7. data/bin/mmconsole +56 -0
  8. data/lib/mongomapper.rb +96 -0
  9. data/lib/mongomapper/associations.rb +61 -0
  10. data/lib/mongomapper/associations/base.rb +71 -0
  11. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +32 -0
  12. data/lib/mongomapper/associations/belongs_to_proxy.rb +22 -0
  13. data/lib/mongomapper/associations/many_documents_proxy.rb +85 -0
  14. data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongomapper/associations/many_embedded_proxy.rb +17 -0
  16. data/lib/mongomapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongomapper/associations/many_proxy.rb +6 -0
  18. data/lib/mongomapper/associations/proxy.rb +67 -0
  19. data/lib/mongomapper/callbacks.rb +106 -0
  20. data/lib/mongomapper/document.rb +278 -0
  21. data/lib/mongomapper/embedded_document.rb +237 -0
  22. data/lib/mongomapper/finder_options.rb +96 -0
  23. data/lib/mongomapper/key.rb +80 -0
  24. data/lib/mongomapper/observing.rb +50 -0
  25. data/lib/mongomapper/pagination.rb +52 -0
  26. data/lib/mongomapper/rails_compatibility/document.rb +15 -0
  27. data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
  28. data/lib/mongomapper/save_with_validation.rb +19 -0
  29. data/lib/mongomapper/serialization.rb +55 -0
  30. data/lib/mongomapper/serializers/json_serializer.rb +79 -0
  31. data/lib/mongomapper/validations.rb +47 -0
  32. data/mongomapper.gemspec +139 -0
  33. data/test/NOTE_ON_TESTING +1 -0
  34. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +39 -0
  35. data/test/functional/associations/test_belongs_to_proxy.rb +35 -0
  36. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +131 -0
  37. data/test/functional/associations/test_many_embedded_proxy.rb +106 -0
  38. data/test/functional/associations/test_many_polymorphic_proxy.rb +267 -0
  39. data/test/functional/associations/test_many_proxy.rb +236 -0
  40. data/test/functional/test_associations.rb +40 -0
  41. data/test/functional/test_callbacks.rb +85 -0
  42. data/test/functional/test_document.rb +691 -0
  43. data/test/functional/test_pagination.rb +81 -0
  44. data/test/functional/test_rails_compatibility.rb +31 -0
  45. data/test/functional/test_validations.rb +172 -0
  46. data/test/models.rb +108 -0
  47. data/test/test_helper.rb +67 -0
  48. data/test/unit/serializers/test_json_serializer.rb +103 -0
  49. data/test/unit/test_association_base.rb +136 -0
  50. data/test/unit/test_document.rb +125 -0
  51. data/test/unit/test_embedded_document.rb +370 -0
  52. data/test/unit/test_finder_options.rb +214 -0
  53. data/test/unit/test_key.rb +217 -0
  54. data/test/unit/test_mongo_id.rb +35 -0
  55. data/test/unit/test_mongomapper.rb +28 -0
  56. data/test/unit/test_observing.rb +101 -0
  57. data/test/unit/test_pagination.rb +113 -0
  58. data/test/unit/test_rails_compatibility.rb +34 -0
  59. data/test/unit/test_serializations.rb +52 -0
  60. data/test/unit/test_validations.rb +259 -0
  61. metadata +189 -0
@@ -0,0 +1,236 @@
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 "Finding scoped to association" do
102
+ setup do
103
+ @project1 = Project.new(:name => 'Project 1')
104
+ @brand_new = Status.create(:name => 'New')
105
+ @complete = Status.create(:name => 'Complete')
106
+ @project1.statuses = [@brand_new, @complete]
107
+ @project1.save
108
+
109
+ @project2 = Project.create(:name => 'Project 2')
110
+ @in_progress = Status.create(:name => 'In Progress')
111
+ @archived = Status.create(:name => 'Archived')
112
+ @another_complete = Status.create(:name => 'Complete')
113
+ @project2.statuses = [@in_progress, @archived, @another_complete]
114
+ @project2.save
115
+ end
116
+
117
+ context "with :all" do
118
+ should "work" do
119
+ @project1.statuses.find(:all).should == [@brand_new, @complete]
120
+ end
121
+
122
+ should "work with conditions" do
123
+ statuses = @project1.statuses.find(:all, :conditions => {'name' => 'Complete'})
124
+ statuses.should == [@complete]
125
+ end
126
+
127
+ should "work with order" do
128
+ statuses = @project1.statuses.find(:all, :order => 'name asc')
129
+ statuses.should == [@complete, @brand_new]
130
+ end
131
+ end
132
+
133
+ context "with #all" do
134
+ should_eventually "work" do
135
+ @project1.statuses.all.should == [@brand_new, @complete]
136
+ end
137
+
138
+ should "work with conditions" do
139
+ statuses = @project1.statuses.all(:conditions => {'name' => 'Complete'})
140
+ statuses.should == [@complete]
141
+ end
142
+
143
+ should "work with order" do
144
+ statuses = @project1.statuses.all(:order => 'name asc')
145
+ statuses.should == [@complete, @brand_new]
146
+ end
147
+ end
148
+
149
+ context "with :first" do
150
+ should "work" do
151
+ @project1.statuses.find(:first).should == @brand_new
152
+ end
153
+
154
+ should "work with conditions" do
155
+ status = @project1.statuses.find(:first, :conditions => {:name => 'Complete'})
156
+ status.should == @complete
157
+ end
158
+ end
159
+
160
+ context "with #first" do
161
+ should "work" do
162
+ @project1.statuses.first.should == @brand_new
163
+ end
164
+
165
+ should "work with conditions" do
166
+ status = @project1.statuses.first(:conditions => {:name => 'Complete'})
167
+ status.should == @complete
168
+ end
169
+ end
170
+
171
+ context "with :last" do
172
+ should "work" do
173
+ @project1.statuses.find(:last).should == @complete
174
+ end
175
+
176
+ should "work with conditions" do
177
+ status = @project1.statuses.find(:last, :conditions => {:name => 'New'})
178
+ status.should == @brand_new
179
+ end
180
+ end
181
+
182
+ context "with #last" do
183
+ should "work" do
184
+ @project1.statuses.last.should == @complete
185
+ end
186
+
187
+ should "work with conditions" do
188
+ status = @project1.statuses.last(:conditions => {:name => 'New'})
189
+ status.should == @brand_new
190
+ end
191
+ end
192
+
193
+ context "with one id" do
194
+ should "work for id in association" do
195
+ @project1.statuses.find(@complete.id).should == @complete
196
+ end
197
+
198
+ should "not work for id not in association" do
199
+ lambda {
200
+ @project1.statuses.find(@archived.id)
201
+ }.should raise_error(MongoMapper::DocumentNotFound)
202
+ end
203
+ end
204
+
205
+ context "with multiple ids" do
206
+ should "work for ids in association" do
207
+ statuses = @project1.statuses.find(@brand_new.id, @complete.id)
208
+ statuses.should == [@brand_new, @complete]
209
+ end
210
+
211
+ should "not work for ids not in association" do
212
+ lambda {
213
+ @project1.statuses.find(@brand_new.id, @complete.id, @archived.id)
214
+ }.should raise_error(MongoMapper::DocumentNotFound)
215
+ end
216
+ end
217
+
218
+ context "with #paginate" do
219
+ setup do
220
+ @statuses = @project2.statuses.paginate(:per_page => 2, :page => 1, :order => '$natural asc')
221
+ end
222
+
223
+ should "return total pages" do
224
+ @statuses.total_pages.should == 2
225
+ end
226
+
227
+ should "return total entries" do
228
+ @statuses.total_entries.should == 3
229
+ end
230
+
231
+ should "return the subject" do
232
+ @statuses.should == [@in_progress, @archived]
233
+ end
234
+ end
235
+ end
236
+ end
@@ -0,0 +1,40 @@
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
+ many :posts, :class_name => 'AssociationsTest::AwesomePost', :foreign_key => :creator_id
13
+ end
14
+
15
+ class AwesomeTag
16
+ include MongoMapper::EmbeddedDocument
17
+ key :name, String
18
+ belongs_to :post, :class_name => 'AssociationsTest::AwesomeUser'
19
+ end
20
+
21
+ class AwesomePost
22
+ include MongoMapper::Document
23
+ belongs_to :creator, :class_name => 'AssociationsTest::AwesomeUser'
24
+ many :tags, :class_name => 'AssociationsTest::AwesomeTag', :foreign_key => :post_id
25
+ end
26
+
27
+ AwesomeUser.collection.clear
28
+ AwesomePost.collection.clear
29
+
30
+ user = AwesomeUser.create
31
+ tag1 = AwesomeTag.new(:name => 'awesome')
32
+ tag2 = AwesomeTag.new(:name => 'grand')
33
+ post1 = AwesomePost.create(:creator => user, :tags => [tag1])
34
+ post2 = AwesomePost.create(:creator => user, :tags => [tag2])
35
+ user.posts.should == [post1, post2]
36
+
37
+ post1_from_db = AwesomePost.find(post1.id)
38
+ post1_from_db.tags.should == [tag1]
39
+ end
40
+ 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,691 @@
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 "Document Class Methods" do
19
+ context "Using key with type Array" do
20
+ setup do
21
+ @document.key :tags, Array
22
+ end
23
+
24
+ should "give correct default" do
25
+ doc = @document.new
26
+ doc.tags.should == []
27
+ end
28
+
29
+ should "work with assignment" do
30
+ doc = @document.new
31
+ doc.tags = %w(foo bar)
32
+ doc.tags.should == %w(foo bar)
33
+ end
34
+
35
+ should "work with assignment after saving" do
36
+ doc = @document.new
37
+ doc.tags = %w(foo bar)
38
+ doc.save
39
+ doc.tags.should == %w(foo bar)
40
+ @document.find(doc.id).tags.should == %w(foo bar)
41
+ end
42
+
43
+ should "work with assignment then <<" do
44
+ doc = @document.new
45
+ doc.tags = []
46
+ doc.tags << "foo"
47
+ doc.tags.should == ["foo"]
48
+ end
49
+
50
+ should "work with <<" do
51
+ doc = @document.new
52
+ doc.tags << "foo"
53
+ doc.tags.should == ["foo"]
54
+ end
55
+
56
+ should_eventually "work with << then save" do
57
+ doc = @document.new
58
+ doc.tags << "foo"
59
+ doc.tags << "bar"
60
+ doc.save
61
+ doc.tags.should == %w(foo bar)
62
+ @document.find(doc.id).tags.should == %w(foo bar)
63
+ end
64
+
65
+ end
66
+
67
+ context "Using key with type Hash" do
68
+ setup do
69
+ @document.key :foo, Hash
70
+ end
71
+
72
+ should "give correct default" do
73
+ doc = @document.new
74
+ doc.foo.should == {}
75
+ end
76
+
77
+ should "work with []=" do
78
+ doc = @document.new
79
+ doc.foo["quux"] = "bar"
80
+ doc.foo["quux"].should == "bar"
81
+ doc.foo.should == { "quux" => "bar" }
82
+ end
83
+
84
+ should "work with indifferent access" do
85
+ doc = @document.new
86
+ doc.foo = {:baz => 'bar'}
87
+ doc.foo[:baz].should == 'bar'
88
+ doc.foo['baz'].should == 'bar'
89
+ end
90
+
91
+ should "work with indifferent access after save" do
92
+ doc = @document.new
93
+ doc.foo = {:baz => 'bar'}
94
+ doc.save
95
+ doc = @document.find(doc.id)
96
+ doc.foo[:baz].should == 'bar'
97
+ doc.foo['baz'].should == 'bar'
98
+ end
99
+ end
100
+
101
+ context "Saving a document with an embedded document" do
102
+ setup do
103
+ @document.class_eval do
104
+ key :foo, Address
105
+ end
106
+ end
107
+
108
+ should "embed embedded document" do
109
+ address = Address.new(:city => 'South Bend', :state => 'IN')
110
+ doc = @document.new(:foo => address)
111
+ doc.save
112
+ doc.foo.city.should == 'South Bend'
113
+ doc.foo.state.should == 'IN'
114
+
115
+ from_db = @document.find(doc.id)
116
+ from_db.foo.city.should == 'South Bend'
117
+ from_db.foo.state.should == 'IN'
118
+ end
119
+ end
120
+
121
+ context "Creating a single document" do
122
+ setup do
123
+ @doc_instance = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
124
+ end
125
+
126
+ should "create a document in correct collection" do
127
+ @document.count.should == 1
128
+ end
129
+
130
+ should "automatically set id" do
131
+ @doc_instance.id.should_not be_nil
132
+ @doc_instance.id.size.should == 24
133
+ end
134
+
135
+ should "return instance of document" do
136
+ @doc_instance.should be_instance_of(@document)
137
+ @doc_instance.first_name.should == 'John'
138
+ @doc_instance.last_name.should == 'Nunemaker'
139
+ @doc_instance.age.should == 27
140
+ end
141
+ end
142
+
143
+ context "Creating a document with no attributes provided" do
144
+ setup do
145
+ @document = Class.new do
146
+ include MongoMapper::Document
147
+ end
148
+ @document.collection.clear
149
+ end
150
+
151
+ should "create the document" do
152
+ lambda {
153
+ @document.create
154
+ }.should change { @document.count }.by(1)
155
+ end
156
+ end
157
+
158
+ context "Creating multiple documents" do
159
+ setup do
160
+ @doc_instances = @document.create([
161
+ {:first_name => 'John', :last_name => 'Nunemaker', :age => '27'},
162
+ {:first_name => 'Steve', :last_name => 'Smith', :age => '28'},
163
+ ])
164
+ end
165
+
166
+ should "create multiple documents" do
167
+ @document.count.should == 2
168
+ end
169
+
170
+ should "return an array of doc instances" do
171
+ @doc_instances.map do |doc_instance|
172
+ doc_instance.should be_instance_of(@document)
173
+ end
174
+ end
175
+ end
176
+
177
+ context "Updating a document" do
178
+ setup do
179
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
180
+ @doc_instance = @document.update(doc.id, {:age => 40})
181
+ end
182
+
183
+ should "update attributes provided" do
184
+ @doc_instance.age.should == 40
185
+ end
186
+
187
+ should "not update existing attributes that were not set to update" do
188
+ @doc_instance.first_name.should == 'John'
189
+ @doc_instance.last_name.should == 'Nunemaker'
190
+ end
191
+
192
+ should "not create new document" do
193
+ @document.count.should == 1
194
+ end
195
+ end
196
+
197
+ should "raise error when updating single doc if not provided id and attributes" do
198
+ doc = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
199
+ lambda { @document.update }.should raise_error(ArgumentError)
200
+ lambda { @document.update(doc.id) }.should raise_error(ArgumentError)
201
+ lambda { @document.update(doc.id, [1]) }.should raise_error(ArgumentError)
202
+ end
203
+
204
+ context "Updating multiple documents" do
205
+ setup do
206
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
207
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
208
+
209
+ @doc_instances = @document.update({
210
+ @doc1.id => {:age => 30},
211
+ @doc2.id => {:age => 30},
212
+ })
213
+ end
214
+
215
+ should "not create any new documents" do
216
+ @document.count.should == 2
217
+ end
218
+
219
+ should "should return an array of doc instances" do
220
+ @doc_instances.map do |doc_instance|
221
+ doc_instance.should be_instance_of(@document)
222
+ end
223
+ end
224
+
225
+ should "update the documents" do
226
+ @document.find(@doc1.id).age.should == 30
227
+ @document.find(@doc2.id).age.should == 30
228
+ end
229
+ end
230
+
231
+ should "raise error when updating multiple documents if not a hash" do
232
+ lambda { @document.update([1, 2]) }.should raise_error(ArgumentError)
233
+ end
234
+
235
+ context "Finding documents" do
236
+ setup do
237
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
238
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
239
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
240
+ end
241
+
242
+ should "raise document not found if nothing provided" do
243
+ lambda { @document.find }.should raise_error(MongoMapper::DocumentNotFound)
244
+ end
245
+
246
+ context "with a single id" do
247
+ should "work" do
248
+ @document.find(@doc1.id).should == @doc1
249
+ end
250
+
251
+ should "raise error if document not found" do
252
+ lambda { @document.find(MongoID.new) }.should raise_error(MongoMapper::DocumentNotFound)
253
+ end
254
+
255
+ should_eventually "raise error if id is illegal" do
256
+ lambda { @document.find(1) }.should raise_error(MongoMapper::IllegalID)
257
+ end
258
+ end
259
+
260
+ context "with multiple id's" do
261
+ should "work as arguments" do
262
+ @document.find(@doc1.id, @doc2.id).should == [@doc1, @doc2]
263
+ end
264
+
265
+ should "work as array" do
266
+ @document.find([@doc1.id, @doc2.id]).should == [@doc1, @doc2]
267
+ end
268
+ end
269
+
270
+ context "with :all" do
271
+ should "find all documents" do
272
+ @document.find(:all, :order => 'first_name').should == [@doc1, @doc3, @doc2]
273
+ end
274
+
275
+ should "be able to add conditions" do
276
+ @document.find(:all, :conditions => {:first_name => 'John'}).should == [@doc1]
277
+ end
278
+ end
279
+
280
+ context "with #all" do
281
+ should "find all documents based on criteria" do
282
+ @document.all(:order => 'first_name').should == [@doc1, @doc3, @doc2]
283
+ @document.all(:conditions => {:last_name => 'Nunemaker'}).should == [@doc1, @doc3]
284
+ end
285
+ end
286
+
287
+ context "with :first" do
288
+ should "find first document" do
289
+ @document.find(:first, :order => 'first_name').should == @doc1
290
+ end
291
+ end
292
+
293
+ context "with #first" do
294
+ should "find first document based on criteria" do
295
+ @document.first(:order => 'first_name').should == @doc1
296
+ @document.first(:conditions => {:age => 28}).should == @doc2
297
+ end
298
+ end
299
+
300
+ context "with :last" do
301
+ should "find last document" do
302
+ @document.find(:last).should == @doc3
303
+ end
304
+ end
305
+
306
+ context "with #last" do
307
+ should "find last document based on criteria" do
308
+ @document.last.should == @doc3
309
+ @document.last(:conditions => {:age => 28}).should == @doc2
310
+ end
311
+ end
312
+
313
+ end # finding documents
314
+
315
+ context "Finding document by id" do
316
+ setup do
317
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
318
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
319
+ end
320
+
321
+ should "be able to find by id" do
322
+ @document.find_by_id(@doc1.id).should == @doc1
323
+ @document.find_by_id(@doc2.id).should == @doc2
324
+ end
325
+
326
+ should "return nil if document not found" do
327
+ @document.find_by_id(MongoID.new).should be(nil)
328
+ end
329
+ end
330
+
331
+ context "Deleting a document" do
332
+ setup do
333
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
334
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
335
+ @document.delete(@doc1.id)
336
+ end
337
+
338
+ should "remove document from collection" do
339
+ @document.count.should == 1
340
+ end
341
+
342
+ should "not remove other documents" do
343
+ @document.find(@doc2.id).should_not be(nil)
344
+ end
345
+ end
346
+
347
+ context "Deleting multiple documents" do
348
+ should "work with multiple arguments" do
349
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
350
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
351
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
352
+ @document.delete(@doc1.id, @doc2.id)
353
+
354
+ @document.count.should == 1
355
+ end
356
+
357
+ should "work with array as argument" do
358
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
359
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
360
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
361
+ @document.delete([@doc1.id, @doc2.id])
362
+
363
+ @document.count.should == 1
364
+ end
365
+ end
366
+
367
+ context "Deleting all documents" do
368
+ setup do
369
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
370
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
371
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
372
+ end
373
+
374
+ should "remove all documents when given no conditions" do
375
+ @document.delete_all
376
+ @document.count.should == 0
377
+ end
378
+
379
+ should "only remove matching documents when given conditions" do
380
+ @document.delete_all({:first_name => 'John'})
381
+ @document.count.should == 2
382
+ end
383
+
384
+ should "convert the conditions to mongo criteria" do
385
+ @document.delete_all(:age => [26, 27])
386
+ @document.count.should == 1
387
+ end
388
+ end
389
+
390
+ context "Destroying a document" do
391
+ setup do
392
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
393
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
394
+ @document.destroy(@doc1.id)
395
+ end
396
+
397
+ should "remove document from collection" do
398
+ @document.count.should == 1
399
+ end
400
+
401
+ should "not remove other documents" do
402
+ @document.find(@doc2.id).should_not be(nil)
403
+ end
404
+ end
405
+
406
+ context "Destroying multiple documents" do
407
+ should "work with multiple arguments" do
408
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
409
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
410
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
411
+ @document.destroy(@doc1.id, @doc2.id)
412
+
413
+ @document.count.should == 1
414
+ end
415
+
416
+ should "work with array as argument" do
417
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
418
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
419
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
420
+ @document.destroy([@doc1.id, @doc2.id])
421
+
422
+ @document.count.should == 1
423
+ end
424
+ end
425
+
426
+ context "Destroying all documents" do
427
+ setup do
428
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
429
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
430
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
431
+ end
432
+
433
+ should "remove all documents when given no conditions" do
434
+ @document.destroy_all
435
+ @document.count.should == 0
436
+ end
437
+
438
+ should "only remove matching documents when given conditions" do
439
+ @document.destroy_all(:first_name => 'John')
440
+ @document.count.should == 2
441
+ @document.destroy_all(:age => 26)
442
+ @document.count.should == 1
443
+ end
444
+
445
+ should "convert the conditions to mongo criteria" do
446
+ @document.destroy_all(:age => [26, 27])
447
+ @document.count.should == 1
448
+ end
449
+ end
450
+
451
+ context "Counting documents in collection" do
452
+ setup do
453
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
454
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
455
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
456
+ end
457
+
458
+ should "count all with no arguments" do
459
+ @document.count.should == 3
460
+ end
461
+
462
+ should "return 0 if there are no documents in the collection" do
463
+ @document.delete_all
464
+ @document.count.should == 0
465
+ end
466
+
467
+ should "return 0 if the collection does not exist" do
468
+ klass = Class.new do
469
+ include MongoMapper::Document
470
+ collection 'foobarbazwickdoesnotexist'
471
+ end
472
+
473
+ klass.count.should == 0
474
+ end
475
+
476
+ should "return count for matching documents if conditions provided" do
477
+ @document.count(:age => 27).should == 1
478
+ end
479
+
480
+ should "convert the conditions to mongo criteria" do
481
+ @document.count(:age => [26, 27]).should == 2
482
+ end
483
+ end
484
+
485
+ context "Indexing" do
486
+ setup do
487
+ @document.collection.drop_indexes
488
+ end
489
+
490
+ should "allow creating index for a key" do
491
+ index_name = nil
492
+ lambda {
493
+ index_name = @document.ensure_index :first_name
494
+ }.should change { @document.collection.index_information.size }.by(1)
495
+
496
+ index_name.should == 'first_name_1'
497
+ index = @document.collection.index_information[index_name]
498
+ index.should_not be_nil
499
+ index.should include(['first_name', 1])
500
+ end
501
+
502
+ should "allow creating unique index for a key" do
503
+ @document.collection.expects(:create_index).with(:first_name, true)
504
+ @document.ensure_index :first_name, :unique => true
505
+ end
506
+
507
+ should_eventually "allow creating index on multiple keys" do
508
+ index_name = nil
509
+ lambda {
510
+ index_name = @document.ensure_index [[:first_name, 1], [:last_name, -1]]
511
+ }.should change { @document.collection.index_information.size }.by(1)
512
+
513
+ index_name.should == 'first_name_1_last_name_-1'
514
+
515
+ index = @document.collection.index_information[index_name]
516
+ index.should_not be_nil
517
+ index.should include(['first_name', 1])
518
+ index.should include(['last_name', -1])
519
+ end
520
+
521
+ should "work with :index shortcut when defining key" do
522
+ @document.expects(:ensure_index).with('father').returns(nil)
523
+ @document.key :father, String, :index => true
524
+ end
525
+ end
526
+ end # Document Class Methods
527
+
528
+ context "Saving a new document" do
529
+ setup do
530
+ @doc = @document.new(:first_name => 'John', :age => '27')
531
+ @doc.save
532
+ end
533
+
534
+ should "insert document into the collection" do
535
+ @document.count.should == 1
536
+ end
537
+
538
+ should "assign an id for the document" do
539
+ @doc.id.should_not be(nil)
540
+ @doc.id.size.should == 24
541
+ end
542
+
543
+ should "save attributes" do
544
+ @doc.first_name.should == 'John'
545
+ @doc.age.should == 27
546
+ end
547
+
548
+ should "update attributes in the database" do
549
+ from_db = @document.find(@doc.id)
550
+ from_db.should == @doc
551
+ from_db.first_name.should == 'John'
552
+ from_db.age.should == 27
553
+ end
554
+ end
555
+
556
+ context "Saving an existing document" do
557
+ setup do
558
+ @doc = @document.create(:first_name => 'John', :age => '27')
559
+ @doc.first_name = 'Johnny'
560
+ @doc.age = 30
561
+ @doc.save
562
+ end
563
+
564
+ should "not insert document into collection" do
565
+ @document.count.should == 1
566
+ end
567
+
568
+ should "update attributes" do
569
+ @doc.first_name.should == 'Johnny'
570
+ @doc.age.should == 30
571
+ end
572
+
573
+ should "update attributes in the database" do
574
+ from_db = @document.find(@doc.id)
575
+ from_db.first_name.should == 'Johnny'
576
+ from_db.age.should == 30
577
+ end
578
+ end
579
+
580
+ context "Calling update attributes on a new document" do
581
+ setup do
582
+ @doc = @document.new(:first_name => 'John', :age => '27')
583
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
584
+ end
585
+
586
+ should "insert document into the collection" do
587
+ @document.count.should == 1
588
+ end
589
+
590
+ should "assign an id for the document" do
591
+ @doc.id.should_not be(nil)
592
+ @doc.id.size.should == 24
593
+ end
594
+
595
+ should "save attributes" do
596
+ @doc.first_name.should == 'Johnny'
597
+ @doc.age.should == 30
598
+ end
599
+
600
+ should "update attributes in the database" do
601
+ from_db = @document.find(@doc.id)
602
+ from_db.should == @doc
603
+ from_db.first_name.should == 'Johnny'
604
+ from_db.age.should == 30
605
+ end
606
+ end
607
+
608
+ context "Updating an existing document using update attributes" do
609
+ setup do
610
+ @doc = @document.create(:first_name => 'John', :age => '27')
611
+ @doc.update_attributes(:first_name => 'Johnny', :age => 30)
612
+ end
613
+
614
+ should "not insert document into collection" do
615
+ @document.count.should == 1
616
+ end
617
+
618
+ should "update attributes" do
619
+ @doc.first_name.should == 'Johnny'
620
+ @doc.age.should == 30
621
+ end
622
+
623
+ should "update attributes in the database" do
624
+ from_db = @document.find(@doc.id)
625
+ from_db.first_name.should == 'Johnny'
626
+ from_db.age.should == 30
627
+ end
628
+ end
629
+
630
+ context "Destroying a document that exists" do
631
+ setup do
632
+ @doc = @document.create(:first_name => 'John', :age => '27')
633
+ @doc.destroy
634
+ end
635
+
636
+ should "remove the document from the collection" do
637
+ @document.count.should == 0
638
+ end
639
+
640
+ should "raise error if assignment is attempted" do
641
+ lambda { @doc.first_name = 'Foo' }.should raise_error(TypeError)
642
+ end
643
+ end
644
+
645
+ context "Destroying a document that is a new" do
646
+ setup do
647
+ setup do
648
+ @doc = @document.new(:first_name => 'John Nunemaker', :age => '27')
649
+ @doc.destroy
650
+ end
651
+
652
+ should "not affect collection count" do
653
+ @document.collection.count.should == 0
654
+ end
655
+
656
+ should "raise error if assignment is attempted" do
657
+ lambda { @doc.first_name = 'Foo' }.should raise_error(TypeError)
658
+ end
659
+ end
660
+ end
661
+
662
+ context "timestamping" do
663
+ should "set created_at and updated_at on create" do
664
+ doc = @document.new(:first_name => 'John', :age => 27)
665
+ doc.created_at.should be(nil)
666
+ doc.updated_at.should be(nil)
667
+ doc.save
668
+ doc.created_at.should_not be(nil)
669
+ doc.updated_at.should_not be(nil)
670
+ end
671
+
672
+ should "set updated_at on field update but leave created_at alone" do
673
+ doc = @document.create(:first_name => 'John', :age => 27)
674
+ old_created_at = doc.created_at
675
+ old_updated_at = doc.updated_at
676
+ doc.first_name = 'Johnny'
677
+ doc.save
678
+ doc.created_at.should == old_created_at
679
+ doc.updated_at.should_not == old_updated_at
680
+ end
681
+
682
+ should_eventually "set updated_at on document update but leave created_at alone" do
683
+ doc = @document.create(:first_name => 'John', :age => 27)
684
+ old_created_at = doc.created_at
685
+ old_updated_at = doc.updated_at
686
+ doc = @document.update(old._id, { :first_name => 'Johnny' })
687
+ doc.created_at.should == old_created_at
688
+ doc.updated_at.should_not == old_updated_at
689
+ end
690
+ end
691
+ end