mongo_mapper 0.7.6 → 0.8.0

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 (134) hide show
  1. data/README.rdoc +4 -8
  2. data/bin/mmconsole +1 -1
  3. data/examples/keys.rb +37 -0
  4. data/examples/plugins.rb +41 -0
  5. data/examples/querying.rb +35 -0
  6. data/examples/scopes.rb +52 -0
  7. data/lib/mongo_mapper.rb +77 -97
  8. data/lib/mongo_mapper/connection.rb +83 -0
  9. data/lib/mongo_mapper/document.rb +10 -252
  10. data/lib/mongo_mapper/embedded_document.rb +7 -46
  11. data/lib/mongo_mapper/exceptions.rb +30 -0
  12. data/lib/mongo_mapper/extensions/array.rb +19 -0
  13. data/lib/mongo_mapper/extensions/binary.rb +22 -0
  14. data/lib/mongo_mapper/extensions/boolean.rb +44 -0
  15. data/lib/mongo_mapper/extensions/date.rb +25 -0
  16. data/lib/mongo_mapper/extensions/float.rb +14 -0
  17. data/lib/mongo_mapper/extensions/hash.rb +14 -0
  18. data/lib/mongo_mapper/extensions/integer.rb +19 -0
  19. data/lib/mongo_mapper/extensions/kernel.rb +9 -0
  20. data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
  21. data/lib/mongo_mapper/extensions/object.rb +27 -0
  22. data/lib/mongo_mapper/extensions/object_id.rb +30 -0
  23. data/lib/mongo_mapper/extensions/set.rb +20 -0
  24. data/lib/mongo_mapper/extensions/string.rb +18 -0
  25. data/lib/mongo_mapper/extensions/time.rb +29 -0
  26. data/lib/mongo_mapper/plugins.rb +1 -21
  27. data/lib/mongo_mapper/plugins/accessible.rb +44 -0
  28. data/lib/mongo_mapper/plugins/associations.rb +7 -24
  29. data/lib/mongo_mapper/plugins/associations/base.rb +1 -0
  30. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +5 -6
  31. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +5 -6
  32. data/lib/mongo_mapper/plugins/associations/collection.rb +1 -0
  33. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +2 -1
  34. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +22 -39
  35. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +4 -4
  36. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +22 -23
  37. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +1 -0
  38. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +1 -0
  39. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +1 -0
  40. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +2 -3
  41. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +6 -7
  42. data/lib/mongo_mapper/plugins/associations/proxy.rb +8 -6
  43. data/lib/mongo_mapper/plugins/caching.rb +21 -0
  44. data/lib/mongo_mapper/plugins/callbacks.rb +4 -3
  45. data/lib/mongo_mapper/plugins/clone.rb +10 -4
  46. data/lib/mongo_mapper/plugins/descendants.rb +1 -0
  47. data/lib/mongo_mapper/plugins/dirty.rb +1 -0
  48. data/lib/mongo_mapper/plugins/document.rb +41 -0
  49. data/lib/mongo_mapper/plugins/dynamic_querying.rb +41 -0
  50. data/lib/mongo_mapper/{support/find.rb → plugins/dynamic_querying/dynamic_finder.rb} +3 -36
  51. data/lib/mongo_mapper/plugins/embedded_document.rb +49 -0
  52. data/lib/mongo_mapper/plugins/equality.rb +3 -9
  53. data/lib/mongo_mapper/plugins/identity_map.rb +8 -10
  54. data/lib/mongo_mapper/plugins/indexes.rb +12 -0
  55. data/lib/mongo_mapper/plugins/inspect.rb +1 -0
  56. data/lib/mongo_mapper/plugins/keys.rb +15 -27
  57. data/lib/mongo_mapper/plugins/keys/key.rb +14 -3
  58. data/lib/mongo_mapper/plugins/logger.rb +1 -0
  59. data/lib/mongo_mapper/plugins/modifiers.rb +3 -2
  60. data/lib/mongo_mapper/plugins/pagination.rb +5 -15
  61. data/lib/mongo_mapper/plugins/persistence.rb +12 -11
  62. data/lib/mongo_mapper/plugins/protected.rb +8 -0
  63. data/lib/mongo_mapper/plugins/querying.rb +236 -0
  64. data/lib/mongo_mapper/plugins/querying/decorator.rb +46 -0
  65. data/lib/mongo_mapper/plugins/rails.rb +1 -0
  66. data/lib/mongo_mapper/plugins/safe.rb +28 -0
  67. data/lib/mongo_mapper/plugins/sci.rb +32 -0
  68. data/lib/mongo_mapper/plugins/scopes.rb +21 -0
  69. data/lib/mongo_mapper/plugins/serialization.rb +1 -0
  70. data/lib/mongo_mapper/plugins/timestamps.rb +1 -0
  71. data/lib/mongo_mapper/plugins/userstamps.rb +1 -0
  72. data/lib/mongo_mapper/plugins/validations.rb +5 -1
  73. data/lib/mongo_mapper/support/descendant_appends.rb +5 -6
  74. data/lib/mongo_mapper/version.rb +2 -1
  75. data/test/NOTE_ON_TESTING +1 -0
  76. data/test/active_model_lint_test.rb +13 -0
  77. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  78. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  79. data/test/functional/associations/test_in_array_proxy.rb +319 -0
  80. data/test/functional/associations/test_many_documents_as_proxy.rb +229 -0
  81. data/test/functional/associations/test_many_documents_proxy.rb +536 -0
  82. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +176 -0
  83. data/test/functional/associations/test_many_embedded_proxy.rb +256 -0
  84. data/test/functional/associations/test_many_polymorphic_proxy.rb +302 -0
  85. data/test/functional/associations/test_one_embedded_proxy.rb +58 -0
  86. data/test/functional/associations/test_one_proxy.rb +182 -0
  87. data/test/functional/test_accessible.rb +168 -0
  88. data/test/functional/test_associations.rb +44 -0
  89. data/test/functional/test_binary.rb +27 -0
  90. data/test/functional/test_caching.rb +76 -0
  91. data/test/functional/test_callbacks.rb +151 -0
  92. data/test/functional/test_dirty.rb +163 -0
  93. data/test/functional/test_document.rb +253 -0
  94. data/test/functional/test_dynamic_querying.rb +75 -0
  95. data/test/functional/test_embedded_document.rb +210 -0
  96. data/test/functional/test_identity_map.rb +506 -0
  97. data/test/functional/test_indexes.rb +42 -0
  98. data/test/functional/test_logger.rb +20 -0
  99. data/test/functional/test_modifiers.rb +416 -0
  100. data/test/functional/test_pagination.rb +91 -0
  101. data/test/functional/test_protected.rb +175 -0
  102. data/test/functional/test_querying.rb +873 -0
  103. data/test/functional/test_safe.rb +76 -0
  104. data/test/functional/test_sci.rb +230 -0
  105. data/test/functional/test_scopes.rb +171 -0
  106. data/test/functional/test_string_id_compatibility.rb +67 -0
  107. data/test/functional/test_timestamps.rb +62 -0
  108. data/test/functional/test_userstamps.rb +27 -0
  109. data/test/functional/test_validations.rb +342 -0
  110. data/test/models.rb +227 -0
  111. data/test/test_helper.rb +98 -0
  112. data/test/unit/associations/test_base.rb +212 -0
  113. data/test/unit/associations/test_proxy.rb +105 -0
  114. data/test/unit/serializers/test_json_serializer.rb +202 -0
  115. data/test/unit/test_clone.rb +69 -0
  116. data/test/unit/test_descendant_appends.rb +71 -0
  117. data/test/unit/test_document.rb +213 -0
  118. data/test/unit/test_dynamic_finder.rb +125 -0
  119. data/test/unit/test_embedded_document.rb +644 -0
  120. data/test/unit/test_extensions.rb +380 -0
  121. data/test/unit/test_key.rb +185 -0
  122. data/test/unit/test_keys.rb +55 -0
  123. data/test/unit/test_mongo_mapper.rb +110 -0
  124. data/test/unit/test_pagination.rb +11 -0
  125. data/test/unit/test_plugins.rb +50 -0
  126. data/test/unit/test_rails.rb +181 -0
  127. data/test/unit/test_rails_compatibility.rb +52 -0
  128. data/test/unit/test_serialization.rb +51 -0
  129. data/test/unit/test_time_zones.rb +39 -0
  130. data/test/unit/test_validations.rb +544 -0
  131. metadata +113 -44
  132. data/lib/mongo_mapper/plugins/pagination/proxy.rb +0 -72
  133. data/lib/mongo_mapper/query.rb +0 -23
  134. data/lib/mongo_mapper/support.rb +0 -196
@@ -0,0 +1,67 @@
1
+ require 'test_helper'
2
+
3
+ class StringIdCompatibilityTest < Test::Unit::TestCase
4
+ def setup
5
+ @note_class = EDoc do
6
+ key :_id, String
7
+ end
8
+
9
+ @task_class = Doc do
10
+ key :_id, String
11
+ key :project_id, String
12
+ belongs_to :project
13
+ end
14
+
15
+ @project_class = Doc do
16
+ include MongoMapper::Document
17
+ key :_id, String
18
+ end
19
+
20
+ @task_class.belongs_to :project, :class => @project_class
21
+ @project_class.many :notes, :class => @note_class
22
+ @project_class.many :tasks, :class => @task_class, :foreign_key => 'project_id', :order => :position.asc
23
+ end
24
+
25
+ should "assign correct _id for documents" do
26
+ project = @project_class.create
27
+ project._id.should == project.id
28
+ project._id.should be_instance_of(String)
29
+ project.id.size.should == 24
30
+ lambda {
31
+ BSON::ObjectID.from_string(project.id)
32
+ }.should_not raise_error
33
+ end
34
+
35
+ should "assign correct _id for embedded documents" do
36
+ note = @note_class.new
37
+ note.id.should == note._id
38
+ note.id.size.should == 24
39
+ end
40
+
41
+ should "find records" do
42
+ project = @project_class.create
43
+ @project_class.find(project.id).should == project
44
+ end
45
+
46
+ should "save embedded docs" do
47
+ n1 = @note_class.new
48
+ n2 = @note_class.new
49
+ n3 = @note_class.new
50
+ project = @project_class.create(:notes => [n1, n2, n3])
51
+
52
+ project = project.reload
53
+ project.notes.size.should == 3
54
+ project.notes.should == [n1, n2, n3]
55
+ end
56
+
57
+ should "be able to associate records" do
58
+ t1 = @task_class.new(:body => 'First task', :position => 1)
59
+ t2 = @task_class.new(:body => 'Second task', :position => 2)
60
+ t3 = @task_class.new(:body => 'Third task', :position => 3)
61
+ project = @project_class.create(:name => 'MM', :tasks => [t1, t2, t3])
62
+
63
+ project = project.reload
64
+ project.tasks.count.should == 3
65
+ project.tasks.should == [t1, t2, t3]
66
+ end
67
+ end
@@ -0,0 +1,62 @@
1
+ require 'test_helper'
2
+
3
+ class TimestampsTest < Test::Unit::TestCase
4
+ context "timestamping" do
5
+ setup do
6
+ @klass = Doc do
7
+ key :first_name, String
8
+ key :last_name, String
9
+ key :age, Integer
10
+ key :date, Date
11
+ end
12
+ @klass.timestamps!
13
+ end
14
+
15
+ should "set created_at and updated_at on create" do
16
+ doc = @klass.new(:first_name => 'John', :age => 27)
17
+ doc.created_at.should be(nil)
18
+ doc.updated_at.should be(nil)
19
+ doc.save
20
+ doc.created_at.should_not be(nil)
21
+ doc.updated_at.should_not be(nil)
22
+ end
23
+
24
+ should "not overwrite created_at if it already exists" do
25
+ original_created_at = 1.month.ago
26
+ doc = @klass.new(:first_name => 'John', :age => 27, :created_at => original_created_at)
27
+ doc.created_at.to_i.should == original_created_at.to_i
28
+ doc.updated_at.should be_nil
29
+ doc.save
30
+ doc.created_at.to_i.should == original_created_at.to_i
31
+ doc.updated_at.should_not be_nil
32
+ end
33
+
34
+ should "set updated_at on field update but leave created_at alone" do
35
+ doc = @klass.create(:first_name => 'John', :age => 27)
36
+ old_created_at = doc.created_at
37
+ old_updated_at = doc.updated_at
38
+ doc.first_name = 'Johnny'
39
+
40
+ Timecop.freeze(Time.now + 5.seconds) do
41
+ doc.save
42
+ end
43
+
44
+ doc.created_at.should == old_created_at
45
+ doc.updated_at.should_not == old_updated_at
46
+ end
47
+
48
+ should "set updated_at on document update but leave created_at alone" do
49
+ doc = @klass.create(:first_name => 'John', :age => 27)
50
+ old_created_at = doc.created_at
51
+ old_updated_at = doc.updated_at
52
+
53
+ Timecop.freeze(Time.now + 5.seconds) do
54
+ @klass.update(doc._id, { :first_name => 'Johnny' })
55
+ end
56
+
57
+ doc = doc.reload
58
+ doc.created_at.should == old_created_at
59
+ doc.updated_at.should_not == old_updated_at
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+ require 'test_helper'
2
+
3
+ class UserstampsTest < Test::Unit::TestCase
4
+ context "userstamping" do
5
+ setup do
6
+ @document = Doc do
7
+ userstamps!
8
+ end
9
+ end
10
+
11
+ should "add creator_id key" do
12
+ @document.keys.keys.should include('creator_id')
13
+ end
14
+
15
+ should "add updater_id key" do
16
+ @document.keys.keys.should include('updater_id')
17
+ end
18
+
19
+ should "add belongs_to creator" do
20
+ @document.associations.keys.should include('creator')
21
+ end
22
+
23
+ should "add belongs_to updater" do
24
+ @document.associations.keys.should include('updater')
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,342 @@
1
+ require 'test_helper'
2
+
3
+ class ValidationsTest < Test::Unit::TestCase
4
+ context "Saving a new document that is invalid" do
5
+ setup do
6
+ @document = Doc do
7
+ key :name, String, :required => true
8
+ end
9
+ end
10
+
11
+ should "not insert document" do
12
+ doc = @document.new
13
+ doc.save
14
+ @document.count.should == 0
15
+ end
16
+
17
+ should "populate document's errors" do
18
+ doc = @document.new
19
+ doc.errors.size.should == 0
20
+ doc.save
21
+ doc.errors.full_messages.should == ["Name can't be empty"]
22
+ end
23
+ end
24
+
25
+ context "Saving a document that is invalid (destructive)" do
26
+ setup do
27
+ @document = Doc do
28
+ key :name, String, :required => true
29
+ end
30
+ end
31
+
32
+ should "raise error" do
33
+ doc = @document.new
34
+ lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
35
+ end
36
+ end
37
+
38
+ context "Creating a document that is invalid (destructive)" do
39
+ setup do
40
+ @document = Doc do
41
+ key :name, String, :required => true
42
+ end
43
+ end
44
+
45
+ should "raise error" do
46
+ lambda { @document.create! }.should raise_error(MongoMapper::DocumentNotValid)
47
+ end
48
+
49
+ should "create a new document" do
50
+ instance = @document.create!(:name => "James")
51
+ instance.new_record?.should be_false
52
+ end
53
+ end
54
+
55
+ context "Saving an existing document that is invalid" do
56
+ setup do
57
+ @document = Doc do
58
+ key :name, String, :required => true
59
+ end
60
+
61
+ @doc = @document.create(:name => 'John Nunemaker')
62
+ end
63
+
64
+ should "not update document" do
65
+ @doc.name = nil
66
+ @doc.save
67
+ @doc.reload.name.should == 'John Nunemaker'
68
+ end
69
+
70
+ should "populate document's errors" do
71
+ @doc.name = nil
72
+ @doc.save
73
+ @doc.errors.full_messages.should == ["Name can't be empty"]
74
+ end
75
+ end
76
+
77
+ context "Adding validation errors" do
78
+ setup do
79
+ @document = Doc do
80
+ key :action, String
81
+ def action_present
82
+ errors.add(:action, 'is invalid') if action.blank?
83
+ end
84
+ end
85
+ end
86
+
87
+ should "work with validate_on_create callback" do
88
+ @document.validate_on_create :action_present
89
+
90
+ doc = @document.new
91
+ doc.action = nil
92
+ doc.should have_error_on(:action)
93
+
94
+ doc.action = 'kick'
95
+ doc.should_not have_error_on(:action)
96
+ doc.save
97
+
98
+ doc.action = nil
99
+ doc.should_not have_error_on(:action)
100
+ end
101
+
102
+ should "work with validate_on_update callback" do
103
+ @document.validate_on_update :action_present
104
+
105
+ doc = @document.new
106
+ doc.action = nil
107
+ doc.should_not have_error_on(:action)
108
+ doc.save
109
+
110
+ doc.action = nil
111
+ doc.should have_error_on(:action)
112
+
113
+ doc.action = 'kick'
114
+ doc.should_not have_error_on(:action)
115
+ end
116
+ end
117
+
118
+ context "validating uniqueness of" do
119
+ setup do
120
+ @document = Doc do
121
+ key :name, String
122
+ validates_uniqueness_of :name
123
+ end
124
+ end
125
+
126
+ should "not fail if object is new" do
127
+ doc = @document.new
128
+ doc.should_not have_error_on(:name)
129
+ end
130
+
131
+ should "not fail when new object is out of scope" do
132
+ document = Doc do
133
+ key :name
134
+ key :adult
135
+ validates_uniqueness_of :name, :scope => :adult
136
+ end
137
+ doc = document.new("name" => "joe", :adult => true)
138
+ doc.save.should be_true
139
+
140
+ doc2 = document.new("name" => "joe", :adult => false)
141
+ doc2.should be_valid
142
+ end
143
+
144
+ should "allow to update an object" do
145
+ doc = @document.new("name" => "joe")
146
+ doc.save.should be_true
147
+
148
+ @document \
149
+ .stubs(:first) \
150
+ .with(:name => 'joe') \
151
+ .returns(doc)
152
+
153
+ doc.name = "joe"
154
+ doc.valid?.should be_true
155
+ doc.should_not have_error_on(:name)
156
+ end
157
+
158
+ should "fail if object name is not unique" do
159
+ doc = @document.new("name" => "joe")
160
+ doc.save.should be_true
161
+
162
+ @document \
163
+ .stubs(:first) \
164
+ .with(:name => 'joe') \
165
+ .returns(doc)
166
+
167
+ doc2 = @document.new("name" => "joe")
168
+ doc2.should have_error_on(:name)
169
+ end
170
+
171
+ should "allow multiple blank entries if :allow_blank => true" do
172
+ document = Doc do
173
+ key :name
174
+ validates_uniqueness_of :name, :allow_blank => :true
175
+ end
176
+
177
+ doc = document.new("name" => "")
178
+ doc.save.should be_true
179
+
180
+ document \
181
+ .stubs(:first) \
182
+ .with(:name => '') \
183
+ .returns(doc)
184
+
185
+ doc2 = document.new("name" => "")
186
+ doc2.should_not have_error_on(:name)
187
+ end
188
+
189
+ should "allow multiple nil entries if :allow_nil => true" do
190
+ document = Doc do
191
+ key :name
192
+ validates_uniqueness_of :name, :allow_nil => :true
193
+ end
194
+
195
+ doc = document.new('name' => nil)
196
+ doc.save.should be_true
197
+
198
+ doc2 = document.new('name' => nil)
199
+ doc2.should_not have_error_on(:name)
200
+ end
201
+
202
+ should "allow entries that differ only in case by default" do
203
+ document = Doc do
204
+ key :name
205
+ validates_uniqueness_of :name
206
+ end
207
+
208
+ doc = document.new("name" => "BLAMMO")
209
+ doc.save.should be_true
210
+
211
+ doc2 = document.new("name" => "blammo")
212
+ doc2.should_not have_error_on(:name)
213
+ end
214
+
215
+ context "with :case_sensitive => false" do
216
+ setup do
217
+ @document = Doc do
218
+ key :name
219
+ validates_uniqueness_of :name, :case_sensitive => false
220
+ end
221
+ end
222
+
223
+ should "fail on entries that differ only in case" do
224
+ doc = @document.new("name" => "BLAMMO")
225
+ doc.save.should be_true
226
+
227
+ doc2 = @document.new("name" => "blammo")
228
+ doc2.should have_error_on(:name)
229
+ end
230
+
231
+ should "not raise an error if value is nil" do
232
+ doc = @document.new("name" => nil)
233
+ lambda { doc.valid? }.should_not raise_error
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
248
+ end
249
+
250
+ context "scoped by a single attribute" do
251
+ setup do
252
+ @document = Doc do
253
+ key :name, String
254
+ key :scope, String
255
+ validates_uniqueness_of :name, :scope => :scope
256
+ end
257
+ end
258
+
259
+ should "fail if the same name exists in the scope" do
260
+ doc = @document.new("name" => "joe", "scope" => "one")
261
+ doc.save.should be_true
262
+
263
+ @document \
264
+ .stubs(:first) \
265
+ .with(:name => 'joe', :scope => "one") \
266
+ .returns(doc)
267
+
268
+ doc2 = @document.new("name" => "joe", "scope" => "one")
269
+ doc2.should have_error_on(:name)
270
+ end
271
+
272
+ should "pass if the same name exists in a different scope" do
273
+ doc = @document.new("name" => "joe", "scope" => "one")
274
+ doc.save.should be_true
275
+
276
+ @document \
277
+ .stubs(:first) \
278
+ .with(:name => 'joe', :scope => 'two') \
279
+ .returns(nil)
280
+
281
+ doc2 = @document.new("name" => "joe", "scope" => "two")
282
+ doc2.should_not have_error_on(:name)
283
+ end
284
+ end
285
+
286
+ context "scoped by a multiple attributes" do
287
+ setup do
288
+ @document = Doc do
289
+ key :name, String
290
+ key :first_scope, String
291
+ key :second_scope, String
292
+ validates_uniqueness_of :name, :scope => [:first_scope, :second_scope]
293
+ end
294
+ end
295
+
296
+ should "fail if the same name exists in the scope" do
297
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
298
+ doc.save.should be_true
299
+
300
+ @document \
301
+ .stubs(:first) \
302
+ .with(:name => 'joe', :first_scope => 'one', :second_scope => 'two') \
303
+ .returns(doc)
304
+
305
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
306
+ doc2.should have_error_on(:name)
307
+ end
308
+
309
+ should "pass if the same name exists in a different scope" do
310
+ doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
311
+ doc.save.should be_true
312
+
313
+ @document \
314
+ .stubs(:first) \
315
+ .with(:name => 'joe', :first_scope => 'one', :second_scope => 'one') \
316
+ .returns(nil)
317
+
318
+ doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "one")
319
+ doc2.should_not have_error_on(:name)
320
+ end
321
+ end
322
+ end
323
+
324
+ context "validates uniqueness of with :unique shortcut" do
325
+ should "work" do
326
+ @document = Doc do
327
+ key :name, String, :unique => true
328
+ end
329
+
330
+ doc = @document.create(:name => 'John')
331
+ doc.should_not have_error_on(:name)
332
+
333
+ @document \
334
+ .stubs(:first) \
335
+ .with(:name => 'John') \
336
+ .returns(doc)
337
+
338
+ second_john = @document.create(:name => 'John')
339
+ second_john.should have_error_on(:name, 'has already been taken')
340
+ end
341
+ end
342
+ end