djsun-mongomapper 0.3.1

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.
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