mongo_mapper 0.5.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.
- data/.gitignore +7 -0
- data/LICENSE +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +87 -0
- data/VERSION +1 -0
- data/bin/mmconsole +55 -0
- data/lib/mongo_mapper.rb +92 -0
- data/lib/mongo_mapper/associations.rb +86 -0
- data/lib/mongo_mapper/associations/base.rb +83 -0
- data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
- data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
- data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +27 -0
- data/lib/mongo_mapper/associations/many_documents_proxy.rb +116 -0
- data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
- data/lib/mongo_mapper/associations/many_embedded_proxy.rb +67 -0
- data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
- data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
- data/lib/mongo_mapper/associations/proxy.rb +64 -0
- data/lib/mongo_mapper/callbacks.rb +106 -0
- data/lib/mongo_mapper/document.rb +317 -0
- data/lib/mongo_mapper/dynamic_finder.rb +35 -0
- data/lib/mongo_mapper/embedded_document.rb +354 -0
- data/lib/mongo_mapper/finder_options.rb +94 -0
- data/lib/mongo_mapper/key.rb +32 -0
- data/lib/mongo_mapper/observing.rb +50 -0
- data/lib/mongo_mapper/pagination.rb +51 -0
- data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
- data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
- data/lib/mongo_mapper/save_with_validation.rb +19 -0
- data/lib/mongo_mapper/serialization.rb +55 -0
- data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
- data/lib/mongo_mapper/support.rb +157 -0
- data/lib/mongo_mapper/validations.rb +69 -0
- data/mongo_mapper.gemspec +156 -0
- data/test/NOTE_ON_TESTING +1 -0
- data/test/custom_matchers.rb +48 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +54 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +46 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +244 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +132 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +174 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +297 -0
- data/test/functional/associations/test_many_proxy.rb +331 -0
- data/test/functional/test_associations.rb +48 -0
- data/test/functional/test_binary.rb +18 -0
- data/test/functional/test_callbacks.rb +85 -0
- data/test/functional/test_document.rb +951 -0
- data/test/functional/test_embedded_document.rb +97 -0
- data/test/functional/test_pagination.rb +87 -0
- data/test/functional/test_rails_compatibility.rb +30 -0
- data/test/functional/test_validations.rb +279 -0
- data/test/models.rb +169 -0
- data/test/test_helper.rb +29 -0
- data/test/unit/serializers/test_json_serializer.rb +189 -0
- data/test/unit/test_association_base.rb +144 -0
- data/test/unit/test_document.rb +165 -0
- data/test/unit/test_dynamic_finder.rb +125 -0
- data/test/unit/test_embedded_document.rb +645 -0
- data/test/unit/test_finder_options.rb +193 -0
- data/test/unit/test_key.rb +163 -0
- data/test/unit/test_mongomapper.rb +28 -0
- data/test/unit/test_observing.rb +101 -0
- data/test/unit/test_pagination.rb +109 -0
- data/test/unit/test_rails_compatibility.rb +39 -0
- data/test/unit/test_serializations.rb +52 -0
- data/test/unit/test_support.rb +272 -0
- data/test/unit/test_time_zones.rb +40 -0
- data/test/unit/test_validations.rb +503 -0
- metadata +204 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'models'
|
3
|
+
|
4
|
+
class EmbeddedDocumentTest < Test::Unit::TestCase
|
5
|
+
def setup
|
6
|
+
@document = Class.new do
|
7
|
+
include MongoMapper::Document
|
8
|
+
set_collection_name 'users'
|
9
|
+
|
10
|
+
key :first_name, String
|
11
|
+
key :last_name, String
|
12
|
+
end
|
13
|
+
@document.collection.clear
|
14
|
+
end
|
15
|
+
|
16
|
+
context "Saving a document with an embedded document" do
|
17
|
+
setup do
|
18
|
+
@document.class_eval do
|
19
|
+
key :foo, Address
|
20
|
+
end
|
21
|
+
|
22
|
+
@address = Address.new(:city => 'South Bend', :state => 'IN')
|
23
|
+
@doc = @document.new(:foo => @address)
|
24
|
+
end
|
25
|
+
|
26
|
+
should "embed embedded document" do
|
27
|
+
@doc.save
|
28
|
+
@doc.foo.city.should == 'South Bend'
|
29
|
+
@doc.foo.state.should == 'IN'
|
30
|
+
|
31
|
+
from_db = @document.find(@doc.id)
|
32
|
+
from_db.foo.city.should == 'South Bend'
|
33
|
+
from_db.foo.state.should == 'IN'
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
context "new?" do
|
38
|
+
setup do
|
39
|
+
@document.class_eval do
|
40
|
+
key :foo, Address
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
should "be new until document is saved" do
|
45
|
+
address = Address.new(:city => 'South Bend', :state => 'IN')
|
46
|
+
doc = @document.new(:foo => address)
|
47
|
+
address.new?.should == true
|
48
|
+
end
|
49
|
+
|
50
|
+
should "not be new after document is saved" do
|
51
|
+
address = Address.new(:city => 'South Bend', :state => 'IN')
|
52
|
+
doc = @document.new(:foo => address)
|
53
|
+
doc.save
|
54
|
+
doc.foo.new?.should == false
|
55
|
+
end
|
56
|
+
|
57
|
+
should "not be new when document is read back" do
|
58
|
+
address = Address.new(:city => 'South Bend', :state => 'IN')
|
59
|
+
doc = @document.new(:foo => address)
|
60
|
+
doc.save
|
61
|
+
read_doc = @document.find(doc.id)
|
62
|
+
read_doc.foo.new?.should == false
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
context "save" do
|
67
|
+
should "save the root document" do
|
68
|
+
person = RealPerson.create
|
69
|
+
|
70
|
+
pet = Pet.new :name => 'sparky'
|
71
|
+
person.pets << pet
|
72
|
+
pet.save
|
73
|
+
|
74
|
+
doc = RealPerson.find(person.id)
|
75
|
+
doc.pets.first.should == pet
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "update_attributes" do
|
80
|
+
should "save the root document" do
|
81
|
+
person = RealPerson.create
|
82
|
+
|
83
|
+
pet = Pet.new(:name => 'sparky')
|
84
|
+
person.pets << pet
|
85
|
+
pet.save
|
86
|
+
|
87
|
+
doc = RealPerson.find(person.id)
|
88
|
+
pet = doc.pets.first
|
89
|
+
pet.update_attributes :name => 'koda'
|
90
|
+
|
91
|
+
doc = RealPerson.find(person.id)
|
92
|
+
embedded = doc.pets.first
|
93
|
+
embedded.id.should == pet.id
|
94
|
+
embedded.name.should == 'koda'
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PaginationTest < Test::Unit::TestCase
|
4
|
+
context "Paginating" do
|
5
|
+
setup do
|
6
|
+
@document = Class.new do
|
7
|
+
include MongoMapper::Document
|
8
|
+
set_collection_name 'users'
|
9
|
+
|
10
|
+
key :first_name, String
|
11
|
+
key :last_name, String
|
12
|
+
key :age, Integer
|
13
|
+
|
14
|
+
def self.per_page; 1 end
|
15
|
+
end
|
16
|
+
@document.collection.clear
|
17
|
+
|
18
|
+
@doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
|
19
|
+
@doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
|
20
|
+
@doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
|
21
|
+
end
|
22
|
+
|
23
|
+
should "return the total pages" do
|
24
|
+
result = @document.paginate(:per_page => 2, :page => 1)
|
25
|
+
result.total_pages.should == 2
|
26
|
+
end
|
27
|
+
|
28
|
+
should "return the total pages when defaulting to the document class per_page" do
|
29
|
+
result = @document.paginate(:page => 1)
|
30
|
+
result.total_pages.should == 3
|
31
|
+
end
|
32
|
+
|
33
|
+
should "return the total of records" do
|
34
|
+
result = @document.paginate(:per_page => 2, :page => 1)
|
35
|
+
result.total_entries.should == 3
|
36
|
+
end
|
37
|
+
|
38
|
+
should "return the items" do
|
39
|
+
result = @document.paginate(:per_page => 2, :page => 1, :order => 'first_name')
|
40
|
+
result.size.should == 2
|
41
|
+
result.should == [@doc1, @doc3]
|
42
|
+
end
|
43
|
+
|
44
|
+
should "accept conditions" do
|
45
|
+
result = @document.paginate({
|
46
|
+
:conditions => {:last_name => 'Nunemaker'},
|
47
|
+
:order => "age DESC",
|
48
|
+
:per_page => 2,
|
49
|
+
:page => 1,
|
50
|
+
})
|
51
|
+
result.should == [@doc1, @doc3]
|
52
|
+
result.first.age.should == 27
|
53
|
+
end
|
54
|
+
|
55
|
+
should "withstand rigor" do
|
56
|
+
result = @document.paginate({
|
57
|
+
:per_page => 1,
|
58
|
+
:page => 1,
|
59
|
+
:order => 'age desc',
|
60
|
+
:conditions => {:last_name => 'Nunemaker'}
|
61
|
+
})
|
62
|
+
result.should == [@doc1]
|
63
|
+
result.total_entries.should == 2
|
64
|
+
result.total_pages.should == 2
|
65
|
+
|
66
|
+
result = @document.paginate({
|
67
|
+
:per_page => 1,
|
68
|
+
:page => 2,
|
69
|
+
:order => 'age desc',
|
70
|
+
:conditions => {:last_name => 'Nunemaker'}
|
71
|
+
})
|
72
|
+
result.should == [@doc3]
|
73
|
+
result.total_entries.should == 2
|
74
|
+
result.total_pages.should == 2
|
75
|
+
|
76
|
+
result = @document.paginate({
|
77
|
+
:per_page => 2,
|
78
|
+
:page => 1,
|
79
|
+
:order => 'age desc',
|
80
|
+
:conditions => {:last_name => 'Nunemaker'}
|
81
|
+
})
|
82
|
+
result.should == [@doc1, @doc3]
|
83
|
+
result.total_entries.should == 2
|
84
|
+
result.total_pages.should == 1
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class TestRailsCompatibility < Test::Unit::TestCase
|
4
|
+
class Item
|
5
|
+
include MongoMapper::EmbeddedDocument
|
6
|
+
key :for_all, String
|
7
|
+
end
|
8
|
+
|
9
|
+
class Order
|
10
|
+
include MongoMapper::Document
|
11
|
+
many :items, :class_name => 'TestRailsCompatibility::Item'
|
12
|
+
key :order_only, String
|
13
|
+
end
|
14
|
+
|
15
|
+
context "Document" do
|
16
|
+
setup do
|
17
|
+
Order.collection.clear
|
18
|
+
end
|
19
|
+
|
20
|
+
should "have to_param that returns id" do
|
21
|
+
instance = Order.create('_id' => 1234)
|
22
|
+
instance.to_param.should == '1234'
|
23
|
+
end
|
24
|
+
|
25
|
+
should "alias new to new_record?" do
|
26
|
+
instance = Order.new
|
27
|
+
instance.new_record?.should == instance.new?
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,279 @@
|
|
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 = Class.new do
|
7
|
+
include MongoMapper::Document
|
8
|
+
set_collection_name 'test'
|
9
|
+
key :name, String, :required => true
|
10
|
+
end
|
11
|
+
@document.collection.clear
|
12
|
+
end
|
13
|
+
|
14
|
+
should "not insert document" do
|
15
|
+
doc = @document.new
|
16
|
+
doc.save
|
17
|
+
@document.count.should == 0
|
18
|
+
end
|
19
|
+
|
20
|
+
should "populate document's errors" do
|
21
|
+
doc = @document.new
|
22
|
+
doc.errors.size.should == 0
|
23
|
+
doc.save
|
24
|
+
doc.errors.full_messages.should == ["Name can't be empty"]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
context "Saving a document that is invalid (destructive)" do
|
29
|
+
setup do
|
30
|
+
@document = Class.new do
|
31
|
+
include MongoMapper::Document
|
32
|
+
set_collection_name 'test'
|
33
|
+
key :name, String, :required => true
|
34
|
+
end
|
35
|
+
@document.collection.clear
|
36
|
+
end
|
37
|
+
|
38
|
+
should "raise error" do
|
39
|
+
doc = @document.new
|
40
|
+
lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "Saving an existing document that is invalid" do
|
45
|
+
setup do
|
46
|
+
@document = Class.new do
|
47
|
+
include MongoMapper::Document
|
48
|
+
set_collection_name 'test'
|
49
|
+
key :name, String, :required => true
|
50
|
+
end
|
51
|
+
@document.collection.clear
|
52
|
+
|
53
|
+
@doc = @document.create(:name => 'John Nunemaker')
|
54
|
+
end
|
55
|
+
|
56
|
+
should "not update document" do
|
57
|
+
@doc.name = nil
|
58
|
+
@doc.save
|
59
|
+
@document.find(@doc.id).name.should == 'John Nunemaker'
|
60
|
+
end
|
61
|
+
|
62
|
+
should "populate document's errors" do
|
63
|
+
@doc.name = nil
|
64
|
+
@doc.save
|
65
|
+
@doc.errors.full_messages.should == ["Name can't be empty"]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "Adding validation errors" do
|
70
|
+
setup do
|
71
|
+
@document = Class.new do
|
72
|
+
include MongoMapper::Document
|
73
|
+
set_collection_name 'test'
|
74
|
+
|
75
|
+
key :action, String
|
76
|
+
def action_present
|
77
|
+
errors.add(:action, 'is invalid') if action.blank?
|
78
|
+
end
|
79
|
+
end
|
80
|
+
@document.collection.clear
|
81
|
+
end
|
82
|
+
|
83
|
+
should "work with validate_on_create callback" do
|
84
|
+
@document.validate_on_create :action_present
|
85
|
+
|
86
|
+
doc = @document.new
|
87
|
+
doc.action = nil
|
88
|
+
doc.should have_error_on(:action)
|
89
|
+
|
90
|
+
doc.action = 'kick'
|
91
|
+
doc.should_not have_error_on(:action)
|
92
|
+
doc.save
|
93
|
+
|
94
|
+
doc.action = nil
|
95
|
+
doc.should_not have_error_on(:action)
|
96
|
+
end
|
97
|
+
|
98
|
+
should "work with validate_on_update callback" do
|
99
|
+
@document.validate_on_update :action_present
|
100
|
+
|
101
|
+
doc = @document.new
|
102
|
+
doc.action = nil
|
103
|
+
doc.should_not have_error_on(:action)
|
104
|
+
doc.save
|
105
|
+
|
106
|
+
doc.action = nil
|
107
|
+
doc.should have_error_on(:action)
|
108
|
+
|
109
|
+
doc.action = 'kick'
|
110
|
+
doc.should_not have_error_on(:action)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context "validating uniqueness of" do
|
115
|
+
setup do
|
116
|
+
@document = Class.new do
|
117
|
+
include MongoMapper::Document
|
118
|
+
set_collection_name 'test'
|
119
|
+
|
120
|
+
key :name, String
|
121
|
+
validates_uniqueness_of :name
|
122
|
+
end
|
123
|
+
@document.collection.clear
|
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 = Class.new do
|
133
|
+
include MongoMapper::Document
|
134
|
+
set_collection_name 'test'
|
135
|
+
|
136
|
+
key :name
|
137
|
+
key :adult
|
138
|
+
validates_uniqueness_of :name, :scope => :adult
|
139
|
+
end
|
140
|
+
doc = document.new("name" => "joe", :adult => true)
|
141
|
+
doc.save.should be_true
|
142
|
+
|
143
|
+
doc2 = document.new("name" => "joe", :adult => false)
|
144
|
+
doc2.should be_valid
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
should "allow to update an object" do
|
149
|
+
doc = @document.new("name" => "joe")
|
150
|
+
doc.save.should be_true
|
151
|
+
|
152
|
+
@document \
|
153
|
+
.stubs(:find) \
|
154
|
+
.with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
|
155
|
+
.returns(doc)
|
156
|
+
|
157
|
+
doc.name = "joe"
|
158
|
+
doc.valid?.should be_true
|
159
|
+
doc.should_not have_error_on(:name)
|
160
|
+
end
|
161
|
+
|
162
|
+
should "fail if object name is not unique" do
|
163
|
+
doc = @document.new("name" => "joe")
|
164
|
+
doc.save.should be_true
|
165
|
+
|
166
|
+
@document \
|
167
|
+
.stubs(:find) \
|
168
|
+
.with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
|
169
|
+
.returns(doc)
|
170
|
+
|
171
|
+
doc2 = @document.new("name" => "joe")
|
172
|
+
doc2.should have_error_on(:name)
|
173
|
+
end
|
174
|
+
|
175
|
+
context "scoped by a single attribute" do
|
176
|
+
setup do
|
177
|
+
@document = Class.new do
|
178
|
+
include MongoMapper::Document
|
179
|
+
set_collection_name 'test'
|
180
|
+
|
181
|
+
key :name, String
|
182
|
+
key :scope, String
|
183
|
+
validates_uniqueness_of :name, :scope => :scope
|
184
|
+
end
|
185
|
+
@document.collection.clear
|
186
|
+
end
|
187
|
+
|
188
|
+
should "fail if the same name exists in the scope" do
|
189
|
+
doc = @document.new("name" => "joe", "scope" => "one")
|
190
|
+
doc.save.should be_true
|
191
|
+
|
192
|
+
@document \
|
193
|
+
.stubs(:find) \
|
194
|
+
.with(:first, :conditions => {:name => 'joe', :scope => "one"}, :limit => 1) \
|
195
|
+
.returns(doc)
|
196
|
+
|
197
|
+
doc2 = @document.new("name" => "joe", "scope" => "one")
|
198
|
+
doc2.should have_error_on(:name)
|
199
|
+
end
|
200
|
+
|
201
|
+
should "pass if the same name exists in a different scope" do
|
202
|
+
doc = @document.new("name" => "joe", "scope" => "one")
|
203
|
+
doc.save.should be_true
|
204
|
+
|
205
|
+
@document \
|
206
|
+
.stubs(:find) \
|
207
|
+
.with(:first, :conditions => {:name => 'joe', :scope => "two"}, :limit => 1) \
|
208
|
+
.returns(nil)
|
209
|
+
|
210
|
+
doc2 = @document.new("name" => "joe", "scope" => "two")
|
211
|
+
doc2.should_not have_error_on(:name)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
context "scoped by a multiple attributes" do
|
216
|
+
setup do
|
217
|
+
@document = Class.new do
|
218
|
+
include MongoMapper::Document
|
219
|
+
set_collection_name 'test'
|
220
|
+
|
221
|
+
key :name, String
|
222
|
+
key :first_scope, String
|
223
|
+
key :second_scope, String
|
224
|
+
validates_uniqueness_of :name, :scope => [:first_scope, :second_scope]
|
225
|
+
end
|
226
|
+
@document.collection.clear
|
227
|
+
end
|
228
|
+
|
229
|
+
should "fail if the same name exists in the scope" do
|
230
|
+
doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
|
231
|
+
doc.save.should be_true
|
232
|
+
|
233
|
+
@document \
|
234
|
+
.stubs(:find) \
|
235
|
+
.with(:first, :conditions => {:name => 'joe', :first_scope => "one", :second_scope => "two"}, :limit => 1) \
|
236
|
+
.returns(doc)
|
237
|
+
|
238
|
+
doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
|
239
|
+
doc2.should have_error_on(:name)
|
240
|
+
end
|
241
|
+
|
242
|
+
should "pass if the same name exists in a different scope" do
|
243
|
+
doc = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "two")
|
244
|
+
doc.save.should be_true
|
245
|
+
|
246
|
+
@document \
|
247
|
+
.stubs(:find) \
|
248
|
+
.with(:first, :conditions => {:name => 'joe', :first_scope => "one", :second_scope => "one"}, :limit => 1) \
|
249
|
+
.returns(nil)
|
250
|
+
|
251
|
+
doc2 = @document.new("name" => "joe", "first_scope" => "one", "second_scope" => "one")
|
252
|
+
doc2.should_not have_error_on(:name)
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
|
257
|
+
context "validates uniqueness of with :unique shortcut" do
|
258
|
+
should "work" do
|
259
|
+
@document = Class.new do
|
260
|
+
include MongoMapper::Document
|
261
|
+
set_collection_name 'test'
|
262
|
+
|
263
|
+
key :name, String, :unique => true
|
264
|
+
end
|
265
|
+
@document.collection.clear
|
266
|
+
|
267
|
+
doc = @document.create(:name => 'John')
|
268
|
+
doc.should_not have_error_on(:name)
|
269
|
+
|
270
|
+
@document \
|
271
|
+
.stubs(:find) \
|
272
|
+
.with(:first, :conditions => {:name => 'John'}, :limit => 1) \
|
273
|
+
.returns(doc)
|
274
|
+
|
275
|
+
second_john = @document.create(:name => 'John')
|
276
|
+
second_john.should have_error_on(:name, 'has already been taken')
|
277
|
+
end
|
278
|
+
end
|
279
|
+
end
|