mongo_mapper 0.7.1 → 0.7.2

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.
@@ -317,6 +317,14 @@ class ModifierTest < Test::Unit::TestCase
317
317
  page2.reload
318
318
  page.tags.should == %w(foo)
319
319
  end
320
- end
320
+
321
+ should "be able to pop with modifier hashes" do
322
+ page = @page_class.create(:tags => %w(foo bar))
323
+
324
+ page.pop({:tags => 1})
321
325
 
326
+ page.reload
327
+ page.tags.should == %w(foo)
328
+ end
329
+ end
322
330
  end
@@ -1,19 +1,19 @@
1
1
  require 'test_helper'
2
2
 
3
- class ValidationsTest < Test::Unit::TestCase
3
+ class ValidationsTest < Test::Unit::TestCase
4
4
  context "Saving a new document that is invalid" do
5
5
  setup do
6
6
  @document = Doc do
7
7
  key :name, String, :required => true
8
8
  end
9
9
  end
10
-
10
+
11
11
  should "not insert document" do
12
12
  doc = @document.new
13
13
  doc.save
14
14
  @document.count.should == 0
15
15
  end
16
-
16
+
17
17
  should "populate document's errors" do
18
18
  doc = @document.new
19
19
  doc.errors.size.should == 0
@@ -28,7 +28,7 @@ class ValidationsTest < Test::Unit::TestCase
28
28
  key :name, String, :required => true
29
29
  end
30
30
  end
31
-
31
+
32
32
  should "raise error" do
33
33
  doc = @document.new
34
34
  lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
@@ -41,7 +41,7 @@ class ValidationsTest < Test::Unit::TestCase
41
41
  key :name, String, :required => true
42
42
  end
43
43
  end
44
-
44
+
45
45
  should "raise error" do
46
46
  lambda { @document.create! }.should raise_error(MongoMapper::DocumentNotValid)
47
47
  end
@@ -51,29 +51,29 @@ class ValidationsTest < Test::Unit::TestCase
51
51
  instance.new_record?.should be_false
52
52
  end
53
53
  end
54
-
54
+
55
55
  context "Saving an existing document that is invalid" do
56
56
  setup do
57
57
  @document = Doc do
58
58
  key :name, String, :required => true
59
59
  end
60
-
60
+
61
61
  @doc = @document.create(:name => 'John Nunemaker')
62
62
  end
63
-
63
+
64
64
  should "not update document" do
65
65
  @doc.name = nil
66
66
  @doc.save
67
67
  @doc.reload.name.should == 'John Nunemaker'
68
68
  end
69
-
69
+
70
70
  should "populate document's errors" do
71
71
  @doc.name = nil
72
72
  @doc.save
73
73
  @doc.errors.full_messages.should == ["Name can't be empty"]
74
74
  end
75
75
  end
76
-
76
+
77
77
  context "Adding validation errors" do
78
78
  setup do
79
79
  @document = Doc do
@@ -83,38 +83,38 @@ class ValidationsTest < Test::Unit::TestCase
83
83
  end
84
84
  end
85
85
  end
86
-
86
+
87
87
  should "work with validate_on_create callback" do
88
88
  @document.validate_on_create :action_present
89
-
89
+
90
90
  doc = @document.new
91
91
  doc.action = nil
92
92
  doc.should have_error_on(:action)
93
-
93
+
94
94
  doc.action = 'kick'
95
95
  doc.should_not have_error_on(:action)
96
96
  doc.save
97
-
97
+
98
98
  doc.action = nil
99
99
  doc.should_not have_error_on(:action)
100
100
  end
101
-
101
+
102
102
  should "work with validate_on_update callback" do
103
103
  @document.validate_on_update :action_present
104
-
104
+
105
105
  doc = @document.new
106
106
  doc.action = nil
107
107
  doc.should_not have_error_on(:action)
108
108
  doc.save
109
-
109
+
110
110
  doc.action = nil
111
111
  doc.should have_error_on(:action)
112
-
112
+
113
113
  doc.action = 'kick'
114
114
  doc.should_not have_error_on(:action)
115
115
  end
116
116
  end
117
-
117
+
118
118
  context "validating uniqueness of" do
119
119
  setup do
120
120
  @document = Doc do
@@ -167,13 +167,13 @@ class ValidationsTest < Test::Unit::TestCase
167
167
  doc2 = @document.new("name" => "joe")
168
168
  doc2.should have_error_on(:name)
169
169
  end
170
-
170
+
171
171
  should "allow multiple blank entries if :allow_blank => true" do
172
172
  document = Doc do
173
173
  key :name
174
174
  validates_uniqueness_of :name, :allow_blank => :true
175
175
  end
176
-
176
+
177
177
  doc = document.new("name" => "")
178
178
  doc.save.should be_true
179
179
 
@@ -219,7 +219,7 @@ class ValidationsTest < Test::Unit::TestCase
219
219
  validates_uniqueness_of :name, :case_sensitive => false
220
220
  end
221
221
  end
222
-
222
+
223
223
  should "fail on entries that differ only in case" do
224
224
  doc = @document.new("name" => "BLAMMO")
225
225
  doc.save.should be_true
@@ -232,6 +232,19 @@ class ValidationsTest < Test::Unit::TestCase
232
232
  doc = @document.new("name" => nil)
233
233
  lambda { doc.valid? }.should_not raise_error
234
234
  end
235
+
236
+ should "not raise an error if special Regexp characters used" do
237
+ doc = @document.new("name" => '?')
238
+ lambda { doc.valid? }.should_not raise_error
239
+ end
240
+
241
+ should "check for uniqueness using entire string" do
242
+ doc = @document.new("name" => "John Doe")
243
+ doc.save.should be_true
244
+
245
+ doc2 = @document.new("name" => "John")
246
+ doc2.valid?.should be_true
247
+ end
235
248
  end
236
249
 
237
250
  context "scoped by a single attribute" do
@@ -246,7 +259,7 @@ class ValidationsTest < Test::Unit::TestCase
246
259
  should "fail if the same name exists in the scope" do
247
260
  doc = @document.new("name" => "joe", "scope" => "one")
248
261
  doc.save.should be_true
249
-
262
+
250
263
  @document \
251
264
  .stubs(:first) \
252
265
  .with(:name => 'joe', :scope => "one") \
@@ -279,11 +292,11 @@ class ValidationsTest < Test::Unit::TestCase
279
292
  validates_uniqueness_of :name, :scope => [:first_scope, :second_scope]
280
293
  end
281
294
  end
282
-
295
+
283
296
  should "fail if the same name exists in the scope" do
284
297
  doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
285
298
  doc.save.should be_true
286
-
299
+
287
300
  @document \
288
301
  .stubs(:first) \
289
302
  .with(:name => 'joe', :first_scope => 'one', :second_scope => 'two') \
@@ -292,11 +305,11 @@ class ValidationsTest < Test::Unit::TestCase
292
305
  doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
293
306
  doc2.should have_error_on(:name)
294
307
  end
295
-
308
+
296
309
  should "pass if the same name exists in a different scope" do
297
310
  doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
298
311
  doc.save.should be_true
299
-
312
+
300
313
  @document \
301
314
  .stubs(:first) \
302
315
  .with(:name => 'joe', :first_scope => 'one', :second_scope => 'one') \
@@ -307,21 +320,21 @@ class ValidationsTest < Test::Unit::TestCase
307
320
  end
308
321
  end
309
322
  end
310
-
323
+
311
324
  context "validates uniqueness of with :unique shortcut" do
312
325
  should "work" do
313
326
  @document = Doc do
314
327
  key :name, String, :unique => true
315
328
  end
316
-
329
+
317
330
  doc = @document.create(:name => 'John')
318
331
  doc.should_not have_error_on(:name)
319
-
332
+
320
333
  @document \
321
334
  .stubs(:first) \
322
335
  .with(:name => 'John') \
323
336
  .returns(doc)
324
-
337
+
325
338
  second_john = @document.create(:name => 'John')
326
339
  second_john.should have_error_on(:name, 'has already been taken')
327
340
  end
@@ -1,4 +1,5 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/../lib/mongo_mapper')
2
+ require 'fileutils'
2
3
 
3
4
  gem 'jnunemaker-matchy', '0.4.0'
4
5
  gem 'shoulda', '2.10.2'
@@ -105,12 +105,6 @@ class DocumentTest < Test::Unit::TestCase
105
105
  @document.new._id.should be_instance_of(Mongo::ObjectID)
106
106
  end
107
107
 
108
- should "have to_param that is string representation of id" do
109
- doc = @document.new(:id => Mongo::ObjectID.new)
110
- doc.to_param.should == doc.id.to_s
111
- doc.to_param.should be_instance_of(String)
112
- end
113
-
114
108
  should "have access to logger" do
115
109
  doc = @document.new
116
110
  doc.logger.should == @document.logger
@@ -11,7 +11,7 @@ module KeyOverride
11
11
  end
12
12
 
13
13
  class EmbeddedDocumentTest < Test::Unit::TestCase
14
- context "" do
14
+ context "EmbeddedDocuments" do
15
15
  setup do
16
16
  class ::Grandparent
17
17
  include MongoMapper::EmbeddedDocument
@@ -221,12 +221,6 @@ class EmbeddedDocumentTest < Test::Unit::TestCase
221
221
  end
222
222
  end
223
223
 
224
- should "have to_param that is string representation of id" do
225
- doc = @document.new
226
- doc.to_param.should == doc.id.to_s
227
- doc.to_param.should be_instance_of(String)
228
- end
229
-
230
224
  should "have access to class logger" do
231
225
  doc = @document.new
232
226
  doc.logger.should == @document.logger
@@ -3,25 +3,25 @@ require 'models'
3
3
 
4
4
  class QueryTest < Test::Unit::TestCase
5
5
  include MongoMapper
6
-
6
+
7
7
  should "raise error if provided something other than a hash" do
8
8
  lambda { Query.new(Room) }.should raise_error(ArgumentError)
9
9
  lambda { Query.new(Room, 1) }.should raise_error(ArgumentError)
10
10
  end
11
-
11
+
12
12
  should "symbolize the keys of the hash provided" do
13
13
  Query.new(Room, 'offset' => 1).options.keys.map do |key|
14
14
  key.should be_instance_of(Symbol)
15
15
  end
16
16
  end
17
-
17
+
18
18
  context "Converting conditions to criteria" do
19
19
  should "not add _type to query if model does not have superclass that is single collection inherited" do
20
20
  Query.new(Message, :foo => 'bar').criteria.should == {
21
21
  :foo => 'bar'
22
22
  }
23
23
  end
24
-
24
+
25
25
  should "not add _type to nested conditions" do
26
26
  Query.new(Enter, :foo => 'bar', :age => {'$gt' => 21}).criteria.should == {
27
27
  :foo => 'bar',
@@ -29,14 +29,14 @@ class QueryTest < Test::Unit::TestCase
29
29
  :_type => 'Enter'
30
30
  }
31
31
  end
32
-
32
+
33
33
  should "automatically add _type to query if model is single collection inherited" do
34
34
  Query.new(Enter, :foo => 'bar').criteria.should == {
35
35
  :foo => 'bar',
36
36
  :_type => 'Enter'
37
37
  }
38
38
  end
39
-
39
+
40
40
  %w{gt lt gte lte ne in nin mod all size where exists}.each do |operator|
41
41
  next if operator == 'size' && RUBY_VERSION >= '1.9.1' # 1.9 defines Symbol#size
42
42
 
@@ -46,75 +46,75 @@ class QueryTest < Test::Unit::TestCase
46
46
  }
47
47
  end
48
48
  end
49
-
49
+
50
50
  should "normalize value when using symbol operators" do
51
51
  time = Time.now.in_time_zone('Indiana (East)')
52
52
  criteria = Query.new(Room, :created_at.gt => time).criteria
53
53
  criteria[:created_at]['$gt'].should be_utc
54
54
  end
55
-
55
+
56
56
  should "work with simple criteria" do
57
57
  Query.new(Room, :foo => 'bar').criteria.should == {
58
58
  :foo => 'bar'
59
59
  }
60
-
60
+
61
61
  Query.new(Room, :foo => 'bar', :baz => 'wick').criteria.should == {
62
- :foo => 'bar',
62
+ :foo => 'bar',
63
63
  :baz => 'wick'
64
64
  }
65
65
  end
66
-
66
+
67
67
  should "convert id to _id" do
68
68
  id = Mongo::ObjectID.new
69
69
  Query.new(Room, :id => id).criteria.should == {:_id => id}
70
70
  end
71
-
71
+
72
72
  should "convert id with symbol operator to _id with modifier" do
73
73
  id = Mongo::ObjectID.new
74
74
  Query.new(Room, :id.ne => id).criteria.should == {
75
75
  :_id => {'$ne' => id}
76
76
  }
77
77
  end
78
-
78
+
79
79
  should "make sure that _id's are object ids" do
80
80
  id = Mongo::ObjectID.new
81
81
  Query.new(Room, :_id => id.to_s).criteria.should == {:_id => id}
82
82
  end
83
-
83
+
84
84
  should "work fine with _id's that are object ids" do
85
85
  id = Mongo::ObjectID.new
86
86
  Query.new(Room, :_id => id).criteria.should == {:_id => id}
87
87
  end
88
-
88
+
89
89
  should "make sure other object id typed keys get converted" do
90
90
  id = Mongo::ObjectID.new
91
91
  Query.new(Message, :room_id => id.to_s).criteria.should == {:room_id => id}
92
92
  end
93
-
93
+
94
94
  should "work fine with object ids for object id typed keys" do
95
95
  id = Mongo::ObjectID.new
96
96
  Query.new(Message, :room_id => id).criteria.should == {:room_id => id}
97
97
  end
98
-
98
+
99
99
  should "convert times to utc if they aren't already" do
100
100
  time = Time.now.in_time_zone('Indiana (East)')
101
101
  criteria = Query.new(Room, :created_at => time).criteria
102
102
  criteria[:created_at].utc?.should be_true
103
103
  end
104
-
104
+
105
105
  should "not funk with times already in utc" do
106
106
  time = Time.now.utc
107
107
  criteria = Query.new(Room, :created_at => time).criteria
108
108
  criteria[:created_at].utc?.should be_true
109
109
  criteria[:created_at].should == time
110
110
  end
111
-
111
+
112
112
  should "use $in for arrays" do
113
113
  Query.new(Room, :foo => [1,2,3]).criteria.should == {
114
114
  :foo => {'$in' => [1,2,3]}
115
115
  }
116
116
  end
117
-
117
+
118
118
  should "not use $in for arrays if already using array operator" do
119
119
  Query.new(Room, :foo => {'$all' => [1,2,3]}).criteria.should == {
120
120
  :foo => {'$all' => [1,2,3]}
@@ -124,155 +124,179 @@ class QueryTest < Test::Unit::TestCase
124
124
  :foo => {'$any' => [1,2,3]}
125
125
  }
126
126
  end
127
-
127
+
128
+ should "use $in for sets" do
129
+ Query.new(Room, :foo => Set.new([1,2,3])).criteria.should == {
130
+ :foo => {'$in' => [1,2,3]}
131
+ }
132
+ end
133
+
134
+ should "not use $in for sets if already using array operator" do
135
+ Query.new(Room, :foo => {'$all' => Set.new([1,2,3])}).criteria.should == {
136
+ :foo => {'$all' => [1,2,3]}
137
+ }
138
+
139
+ Query.new(Room, :foo => {'$any' => Set.new([1,2,3])}).criteria.should == {
140
+ :foo => {'$any' => [1,2,3]}
141
+ }
142
+ end
143
+
128
144
  should "work arbitrarily deep" do
129
145
  Query.new(Room, :foo => {:bar => [1,2,3]}).criteria.should == {
130
146
  :foo => {:bar => {'$in' => [1,2,3]}}
131
147
  }
132
-
148
+
133
149
  Query.new(Room, :foo => {:bar => {'$any' => [1,2,3]}}).criteria.should == {
134
150
  :foo => {:bar => {'$any' => [1,2,3]}}
135
151
  }
136
152
  end
153
+
154
+ should "make sure that ids in array are object ids" do
155
+ id1, id2, id3 = Mongo::ObjectID.new, Mongo::ObjectID.new, Mongo::ObjectID.new
156
+
157
+ Query.new(Room, :_id => [id1.to_s, id2.to_s, id3.to_s]).criteria.should == {
158
+ :_id => {'$in' => [id1, id2, id3]}
159
+ }
160
+ end
137
161
  end
138
-
162
+
139
163
  context "ordering" do
140
164
  should "single field with ascending direction" do
141
165
  sort = [['foo', 1]]
142
166
  Query.new(Room, :order => 'foo asc').options[:sort].should == sort
143
167
  Query.new(Room, :order => 'foo ASC').options[:sort].should == sort
144
168
  end
145
-
169
+
146
170
  should "single field with descending direction" do
147
171
  sort = [['foo', -1]]
148
172
  Query.new(Room, :order => 'foo desc').options[:sort].should == sort
149
173
  Query.new(Room, :order => 'foo DESC').options[:sort].should == sort
150
174
  end
151
-
175
+
152
176
  should "convert order operators to mongo sort" do
153
177
  Query.new(Room, :order => :foo.asc).options[:sort].should == [['foo', 1]]
154
178
  Query.new(Room, :order => :foo.desc).options[:sort].should == [['foo', -1]]
155
179
  end
156
-
180
+
157
181
  should "convert array of order operators to mongo sort" do
158
182
  Query.new(Room, :order => [:foo.asc, :bar.desc]).options[:sort].should == [['foo', 1], ['bar', -1]]
159
183
  end
160
-
184
+
161
185
  should "convert field without direction to ascending" do
162
186
  sort = [['foo', 1]]
163
187
  Query.new(Room, :order => 'foo').options[:sort].should == sort
164
188
  end
165
-
189
+
166
190
  should "convert multiple fields with directions" do
167
191
  sort = [['foo', -1], ['bar', 1], ['baz', -1]]
168
192
  Query.new(Room, :order => 'foo desc, bar asc, baz desc').options[:sort].should == sort
169
193
  end
170
-
194
+
171
195
  should "convert multiple fields with some missing directions" do
172
196
  sort = [['foo', -1], ['bar', 1], ['baz', 1]]
173
197
  Query.new(Room, :order => 'foo desc, bar, baz').options[:sort].should == sort
174
198
  end
175
-
199
+
176
200
  should "just use sort if sort and order are present" do
177
201
  sort = [['$natural', 1]]
178
202
  Query.new(Room, :sort => sort, :order => 'foo asc').options[:sort].should == sort
179
203
  end
180
-
204
+
181
205
  should "normalize id to _id" do
182
206
  Query.new(Room, :order => :id.asc).options[:sort].should == [['_id', 1]]
183
207
  end
184
-
208
+
185
209
  should "convert natural in order to proper" do
186
210
  sort = [['$natural', 1]]
187
211
  Query.new(Room, :order => '$natural asc').options[:sort].should == sort
188
212
  sort = [['$natural', -1]]
189
213
  Query.new(Room, :order => '$natural desc').options[:sort].should == sort
190
214
  end
191
-
215
+
192
216
  should "work for natural order ascending" do
193
217
  Query.new(Room, :sort => {'$natural' => 1}).options[:sort]['$natural'].should == 1
194
218
  end
195
-
219
+
196
220
  should "work for natural order descending" do
197
221
  Query.new(Room, :sort => {'$natural' => -1}).options[:sort]['$natural'].should == -1
198
222
  end
199
223
  end
200
-
224
+
201
225
  context "skip" do
202
226
  should "default to 0" do
203
227
  Query.new(Room, {}).options[:skip].should == 0
204
228
  end
205
-
229
+
206
230
  should "use skip provided" do
207
231
  Query.new(Room, :skip => 2).options[:skip].should == 2
208
232
  end
209
-
233
+
210
234
  should "covert string to integer" do
211
235
  Query.new(Room, :skip => '2').options[:skip].should == 2
212
236
  end
213
-
237
+
214
238
  should "convert offset to skip" do
215
239
  Query.new(Room, :offset => 1).options[:skip].should == 1
216
240
  end
217
241
  end
218
-
242
+
219
243
  context "limit" do
220
244
  should "default to 0" do
221
245
  Query.new(Room, {}).options[:limit].should == 0
222
246
  end
223
-
247
+
224
248
  should "use limit provided" do
225
249
  Query.new(Room, :limit => 2).options[:limit].should == 2
226
250
  end
227
-
251
+
228
252
  should "covert string to integer" do
229
253
  Query.new(Room, :limit => '2').options[:limit].should == 2
230
254
  end
231
255
  end
232
-
256
+
233
257
  context "fields" do
234
258
  should "default to nil" do
235
259
  Query.new(Room, {}).options[:fields].should be(nil)
236
260
  end
237
-
261
+
238
262
  should "be converted to nil if empty string" do
239
263
  Query.new(Room, :fields => '').options[:fields].should be(nil)
240
264
  end
241
-
265
+
242
266
  should "be converted to nil if []" do
243
267
  Query.new(Room, :fields => []).options[:fields].should be(nil)
244
268
  end
245
-
269
+
246
270
  should "should work with array" do
247
271
  Query.new(Room, {:fields => %w(a b)}).options[:fields].should == %w(a b)
248
272
  end
249
-
273
+
250
274
  should "convert comma separated list to array" do
251
275
  Query.new(Room, {:fields => 'a, b'}).options[:fields].should == %w(a b)
252
276
  end
253
-
277
+
254
278
  should "also work as select" do
255
279
  Query.new(Room, :select => %w(a b)).options[:fields].should == %w(a b)
256
280
  end
257
-
281
+
258
282
  should "also work with select as array of symbols" do
259
283
  Query.new(Room, :select => [:a, :b]).options[:fields].should == [:a, :b]
260
284
  end
261
285
  end
262
-
286
+
263
287
  context "Condition auto-detection" do
264
288
  should "know :conditions are criteria" do
265
289
  finder = Query.new(Room, :conditions => {:foo => 'bar'})
266
290
  finder.criteria.should == {:foo => 'bar'}
267
291
  finder.options.keys.should_not include(:conditions)
268
292
  end
269
-
293
+
270
294
  should "know fields is an option" do
271
295
  finder = Query.new(Room, :fields => ['foo'])
272
296
  finder.options[:fields].should == ['foo']
273
297
  finder.criteria.keys.should_not include(:fields)
274
298
  end
275
-
299
+
276
300
  # select gets converted to fields so just checking keys
277
301
  should "know select is an option" do
278
302
  finder = Query.new(Room, :select => 'foo')
@@ -280,13 +304,13 @@ class QueryTest < Test::Unit::TestCase
280
304
  finder.criteria.keys.should_not include(:select)
281
305
  finder.criteria.keys.should_not include(:fields)
282
306
  end
283
-
307
+
284
308
  should "know skip is an option" do
285
309
  finder = Query.new(Room, :skip => 10)
286
310
  finder.options[:skip].should == 10
287
311
  finder.criteria.keys.should_not include(:skip)
288
312
  end
289
-
313
+
290
314
  # offset gets converted to skip so just checking keys
291
315
  should "know offset is an option" do
292
316
  finder = Query.new(Room, :offset => 10)
@@ -313,7 +337,7 @@ class QueryTest < Test::Unit::TestCase
313
337
  finder.options.keys.should include(:sort)
314
338
  finder.criteria.keys.should_not include(:sort)
315
339
  end
316
-
340
+
317
341
  should "work with full range of things" do
318
342
  query_options = Query.new(Room, {
319
343
  :foo => 'bar',
@@ -323,12 +347,12 @@ class QueryTest < Test::Unit::TestCase
323
347
  :limit => 10,
324
348
  :skip => 10,
325
349
  })
326
-
350
+
327
351
  query_options.criteria.should == {
328
352
  :foo => 'bar',
329
353
  :baz => true,
330
354
  }
331
-
355
+
332
356
  query_options.options.should == {
333
357
  :sort => [['foo', 1]],
334
358
  :fields => ['foo', 'baz'],