jnunemaker-mongomapper 0.2.0 → 0.3.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 +1 -0
- data/History +17 -0
- data/README.rdoc +6 -3
- data/Rakefile +3 -2
- data/VERSION +1 -1
- data/bin/mmconsole +56 -0
- data/lib/mongomapper.rb +48 -17
- data/lib/mongomapper/associations.rb +31 -39
- data/lib/mongomapper/associations/base.rb +40 -22
- data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +33 -0
- data/lib/mongomapper/associations/belongs_to_proxy.rb +10 -14
- data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +34 -0
- data/lib/mongomapper/associations/{has_many_embedded_proxy.rb → many_embedded_proxy.rb} +5 -5
- data/lib/mongomapper/associations/many_proxy.rb +55 -0
- data/lib/mongomapper/associations/proxy.rb +21 -14
- data/lib/mongomapper/callbacks.rb +1 -1
- data/lib/mongomapper/document.rb +82 -59
- data/lib/mongomapper/embedded_document.rb +121 -130
- data/lib/mongomapper/finder_options.rb +21 -6
- data/lib/mongomapper/key.rb +5 -7
- data/lib/mongomapper/observing.rb +1 -41
- data/lib/mongomapper/pagination.rb +52 -0
- data/lib/mongomapper/rails_compatibility/document.rb +15 -0
- data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
- data/lib/mongomapper/serialization.rb +1 -1
- data/mongomapper.gemspec +62 -36
- data/test/NOTE_ON_TESTING +1 -0
- data/test/functional/test_associations.rb +485 -0
- data/test/{test_callbacks.rb → functional/test_callbacks.rb} +2 -1
- data/test/functional/test_document.rb +636 -0
- data/test/functional/test_pagination.rb +82 -0
- data/test/functional/test_rails_compatibility.rb +31 -0
- data/test/functional/test_validations.rb +172 -0
- data/test/models.rb +92 -0
- data/test/test_helper.rb +5 -0
- data/test/{serializers → unit/serializers}/test_json_serializer.rb +0 -0
- data/test/unit/test_association_base.rb +131 -0
- data/test/unit/test_document.rb +115 -0
- data/test/{test_embedded_document.rb → unit/test_embedded_document.rb} +158 -66
- data/test/{test_finder_options.rb → unit/test_finder_options.rb} +66 -0
- data/test/{test_key.rb → unit/test_key.rb} +13 -1
- data/test/unit/test_mongo_id.rb +35 -0
- data/test/{test_mongomapper.rb → unit/test_mongomapper.rb} +0 -0
- data/test/{test_observing.rb → unit/test_observing.rb} +0 -0
- data/test/unit/test_pagination.rb +113 -0
- data/test/unit/test_rails_compatibility.rb +34 -0
- data/test/{test_serializations.rb → unit/test_serializations.rb} +0 -2
- data/test/{test_validations.rb → unit/test_validations.rb} +0 -134
- metadata +68 -36
- data/lib/mongomapper/associations/has_many_proxy.rb +0 -28
- data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +0 -31
- data/lib/mongomapper/rails_compatibility.rb +0 -23
- data/test/test_associations.rb +0 -149
- data/test/test_document.rb +0 -944
- data/test/test_rails_compatibility.rb +0 -29
@@ -47,11 +47,55 @@ class FinderOptionsTest < Test::Unit::TestCase
|
|
47
47
|
}
|
48
48
|
end
|
49
49
|
|
50
|
+
should "not use $in for arrays if already using array modifier" do
|
51
|
+
FinderOptions.to_mongo_criteria(:foo => {'$all' => [1,2,3]}).should == {
|
52
|
+
:foo => {'$all' => [1,2,3]}
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
50
56
|
should "work arbitrarily deep" do
|
51
57
|
FinderOptions.to_mongo_criteria(:foo => {:bar => [1,2,3]}).should == {
|
52
58
|
:foo => {:bar => {'$in' => [1,2,3]}}
|
53
59
|
}
|
54
60
|
end
|
61
|
+
|
62
|
+
should "convert string _ids to objectid automatically" do
|
63
|
+
id = XGen::Mongo::Driver::ObjectID.new
|
64
|
+
|
65
|
+
FinderOptions.to_mongo_criteria(:_id => id.to_s).should == {
|
66
|
+
:_id => id
|
67
|
+
}
|
68
|
+
end
|
69
|
+
|
70
|
+
should "leave objectid _ids alone" do
|
71
|
+
id = XGen::Mongo::Driver::ObjectID.new
|
72
|
+
|
73
|
+
FinderOptions.to_mongo_criteria(:_id => id).should == {
|
74
|
+
:_id => id
|
75
|
+
}
|
76
|
+
end
|
77
|
+
|
78
|
+
should "convert array of string _ids to object ids" do
|
79
|
+
id1 = XGen::Mongo::Driver::ObjectID.new
|
80
|
+
id2 = XGen::Mongo::Driver::ObjectID.new
|
81
|
+
|
82
|
+
FinderOptions.to_mongo_criteria(:_id => [id1.to_s, id2.to_s]).should == {
|
83
|
+
:_id => {'$in' => [id1, id2]}
|
84
|
+
}
|
85
|
+
end
|
86
|
+
|
87
|
+
should "convert array of string _ids when using mongo array stuff" do
|
88
|
+
id1 = XGen::Mongo::Driver::ObjectID.new
|
89
|
+
id2 = XGen::Mongo::Driver::ObjectID.new
|
90
|
+
|
91
|
+
FinderOptions.to_mongo_criteria(:_id => {'$all' => [id1.to_s, id2.to_s]}).should == {
|
92
|
+
:_id => {'$all' => [id1, id2]}
|
93
|
+
}
|
94
|
+
|
95
|
+
FinderOptions.to_mongo_criteria(:_id => {'$any' => [id1.to_s, id2.to_s]}).should == {
|
96
|
+
:_id => {'$any' => [id1, id2]}
|
97
|
+
}
|
98
|
+
end
|
55
99
|
end
|
56
100
|
|
57
101
|
context "ordering" do
|
@@ -90,6 +134,28 @@ class FinderOptionsTest < Test::Unit::TestCase
|
|
90
134
|
hash[:baz] = 1
|
91
135
|
FinderOptions.to_mongo_options(:order => 'foo desc, bar, baz')[:sort].should == hash
|
92
136
|
end
|
137
|
+
|
138
|
+
should "just use sort if sort and order are present" do
|
139
|
+
FinderOptions.to_mongo_options(:sort => {'$natural' => 1}, :order => 'foo asc')[:sort].should == {
|
140
|
+
'$natural' => 1
|
141
|
+
}
|
142
|
+
end
|
143
|
+
|
144
|
+
should "convert natural in order to proper" do
|
145
|
+
hash = OrderedHash.new
|
146
|
+
hash[:'$natural'] = 1
|
147
|
+
FinderOptions.to_mongo_options(:order => '$natural asc')[:sort].should == hash
|
148
|
+
hash[:'$natural'] = -1
|
149
|
+
FinderOptions.to_mongo_options(:order => '$natural desc')[:sort].should == hash
|
150
|
+
end
|
151
|
+
|
152
|
+
should "work for natural order ascending" do
|
153
|
+
FinderOptions.to_mongo_options(:sort => {'$natural' => 1})[:sort]['$natural'].should == 1
|
154
|
+
end
|
155
|
+
|
156
|
+
should "work for natural order descending" do
|
157
|
+
FinderOptions.to_mongo_options(:sort => {'$natural' => -1})[:sort]['$natural'].should == -1
|
158
|
+
end
|
93
159
|
end
|
94
160
|
|
95
161
|
context "offset" do
|
@@ -14,7 +14,7 @@ class KeyTest < Test::Unit::TestCase
|
|
14
14
|
|
15
15
|
context "The Key Class" do
|
16
16
|
should "have the native types defined" do
|
17
|
-
Key::NativeTypes.should == [String, Float, Time, Integer, Boolean, Array, Hash,
|
17
|
+
Key::NativeTypes.should == [String, Float, Time, Integer, Boolean, Array, Hash, MongoID]
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
@@ -30,6 +30,10 @@ class KeyTest < Test::Unit::TestCase
|
|
30
30
|
should "allow setting options" do
|
31
31
|
Key.new(:foo, Integer, :required => true).options[:required].should be(true)
|
32
32
|
end
|
33
|
+
|
34
|
+
should "default options to {}" do
|
35
|
+
Key.new(:foo, Integer, nil).options.should == {}
|
36
|
+
end
|
33
37
|
|
34
38
|
should "symbolize option keys" do
|
35
39
|
Key.new(:foo, Integer, 'required' => true).options[:required].should be(true)
|
@@ -71,6 +75,14 @@ class KeyTest < Test::Unit::TestCase
|
|
71
75
|
end
|
72
76
|
|
73
77
|
context "setting a value" do
|
78
|
+
should "correctly typecast MongoIDs" do
|
79
|
+
key = Key.new(:_id, MongoID)
|
80
|
+
id = MongoID.new
|
81
|
+
[id, id.to_s].each do |a|
|
82
|
+
key.set(a).should == id
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
74
86
|
should "correctly typecast Strings" do
|
75
87
|
key = Key.new(:foo, String)
|
76
88
|
[21, '21'].each do |a|
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class MongoIDTest < Test::Unit::TestCase
|
4
|
+
PhonyError = Class.new(StandardError)
|
5
|
+
|
6
|
+
class Phony
|
7
|
+
def to_s
|
8
|
+
raise PhonyError
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
context "mm_typecast" do
|
13
|
+
should "return value if object id" do
|
14
|
+
id = XGen::Mongo::Driver::ObjectID.new
|
15
|
+
MongoID.mm_typecast(id).should == id
|
16
|
+
end
|
17
|
+
|
18
|
+
should "return object id if string" do
|
19
|
+
id = XGen::Mongo::Driver::ObjectID.new
|
20
|
+
MongoID.mm_typecast(id.to_s).should == id
|
21
|
+
end
|
22
|
+
|
23
|
+
should "raise DocumentNotFound if invalid id" do
|
24
|
+
lambda {
|
25
|
+
MongoID.mm_typecast(1234)
|
26
|
+
}.should raise_error(MongoMapper::DocumentNotFound)
|
27
|
+
end
|
28
|
+
|
29
|
+
should "raise exception if message does not match illegal object id" do
|
30
|
+
lambda {
|
31
|
+
MongoID.mm_typecast(Phony.new)
|
32
|
+
}.should raise_error(PhonyError)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
File without changes
|
File without changes
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PaginationTest < Test::Unit::TestCase
|
4
|
+
context "Pagination proxy" do
|
5
|
+
include MongoMapper::Pagination
|
6
|
+
|
7
|
+
should "should have accessors for subject" do
|
8
|
+
subject = [1,2,3,4,5]
|
9
|
+
collection = PaginationProxy.new(25, 2)
|
10
|
+
collection.subject = subject
|
11
|
+
collection.subject.should == subject
|
12
|
+
end
|
13
|
+
|
14
|
+
should "delegate any methods not defined to the subject" do
|
15
|
+
subject = [1,2,3,4,5]
|
16
|
+
collection = PaginationProxy.new(25, 2, 10)
|
17
|
+
collection.subject = subject
|
18
|
+
collection.size.should == 5
|
19
|
+
collection.each_with_index do |value, i|
|
20
|
+
value.should == subject[i]
|
21
|
+
end
|
22
|
+
collection[0..2].should == [1,2,3]
|
23
|
+
collection.class.should == Array
|
24
|
+
end
|
25
|
+
|
26
|
+
should "return correct value for total_entries" do
|
27
|
+
PaginationProxy.new(25, 2, 10).total_entries.should == 25
|
28
|
+
PaginationProxy.new('25', 2, 10).total_entries.should == 25
|
29
|
+
end
|
30
|
+
|
31
|
+
should "return correct value for per_page" do
|
32
|
+
PaginationProxy.new(25, 2, 10).per_page.should == 10
|
33
|
+
PaginationProxy.new(25, 2, '10').per_page.should == 10
|
34
|
+
end
|
35
|
+
|
36
|
+
should "alias limit to per_page" do
|
37
|
+
PaginationProxy.new(100, 1, 300).limit.should == 300
|
38
|
+
end
|
39
|
+
|
40
|
+
should "set per_page to 25 if nil or blank" do
|
41
|
+
PaginationProxy.new(25, 2).per_page.should == 25
|
42
|
+
PaginationProxy.new(25, 2, '').per_page.should == 25
|
43
|
+
end
|
44
|
+
|
45
|
+
should "return correct value for current_page" do
|
46
|
+
PaginationProxy.new(25, 2, 10).current_page.should == 2
|
47
|
+
PaginationProxy.new(25, '2', 10).current_page.should == 2
|
48
|
+
end
|
49
|
+
|
50
|
+
should "not allow value less than 1 for current page" do
|
51
|
+
PaginationProxy.new(25, -1).current_page.should == 1
|
52
|
+
end
|
53
|
+
|
54
|
+
should "automatically calculate total_pages from total_entries and per page" do
|
55
|
+
PaginationProxy.new(25, 2, 10).total_pages.should == 3
|
56
|
+
end
|
57
|
+
|
58
|
+
should "know how many records to skip" do
|
59
|
+
PaginationProxy.new(25, 2, 10).skip.should == 10
|
60
|
+
end
|
61
|
+
|
62
|
+
should "alias offset to skip" do
|
63
|
+
PaginationProxy.new(25, 2, 10).offset.should == 10
|
64
|
+
end
|
65
|
+
|
66
|
+
context "previous_page" do
|
67
|
+
should "be nil if current page 1" do
|
68
|
+
PaginationProxy.new(25, 1, 10).previous_page.should be_nil
|
69
|
+
end
|
70
|
+
|
71
|
+
should "be one less than current page if current is > 1" do
|
72
|
+
PaginationProxy.new(25, 2, 10).previous_page.should == 1
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "next_page" do
|
77
|
+
should "be nil if current page is last page" do
|
78
|
+
PaginationProxy.new(25, 3, 10).next_page.should be_nil
|
79
|
+
end
|
80
|
+
|
81
|
+
should "work for any page that is not last" do
|
82
|
+
PaginationProxy.new(25, 1, 10).next_page.should == 2
|
83
|
+
PaginationProxy.new(25, 2, 10).next_page.should == 3
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "previous_page" do
|
88
|
+
should "be nil if current page is first page" do
|
89
|
+
PaginationProxy.new(25, 1, 10).previous_page.should be_nil
|
90
|
+
end
|
91
|
+
|
92
|
+
should "work for any page other than first" do
|
93
|
+
PaginationProxy.new(25, 2, 10).previous_page.should == 1
|
94
|
+
PaginationProxy.new(25, 3, 10).previous_page.should == 2
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "out_of_bounds?" do
|
99
|
+
should "be true if current_page is greater than total_pages" do
|
100
|
+
PaginationProxy.new(25, 10, 4).out_of_bounds?.should be_true
|
101
|
+
end
|
102
|
+
|
103
|
+
should "be false if current_page is less than total_pages" do
|
104
|
+
PaginationProxy.new(25, 10, 1).out_of_bounds?.should be_false
|
105
|
+
PaginationProxy.new(25, 2, 10).out_of_bounds?.should be_false
|
106
|
+
end
|
107
|
+
|
108
|
+
should "be false if current_page is equal to total_pages" do
|
109
|
+
PaginationProxy.new(25, 3, 10).out_of_bounds?.should be_false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end # end of pagination proxy
|
113
|
+
end # end of test case
|
@@ -0,0 +1,34 @@
|
|
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 FirstItem < Item
|
10
|
+
key :first_only, String
|
11
|
+
many :second_items
|
12
|
+
end
|
13
|
+
|
14
|
+
class SecondItem < Item
|
15
|
+
key :second_only, String
|
16
|
+
end
|
17
|
+
|
18
|
+
context "EmbeddedDocument" do
|
19
|
+
should "raise error for to_param as embedded do not have id's" do
|
20
|
+
lambda { Item.new.to_param }.should raise_error
|
21
|
+
end
|
22
|
+
|
23
|
+
should "alias many to has_many" do
|
24
|
+
FirstItem.should respond_to(:has_many)
|
25
|
+
FirstItem.method(:has_many).should == FirstItem.method(:many)
|
26
|
+
end
|
27
|
+
|
28
|
+
should "have column names" do
|
29
|
+
Item.column_names.sort.should == ['for_all']
|
30
|
+
FirstItem.column_names.sort.should == ['first_only', 'for_all']
|
31
|
+
SecondItem.column_names.sort.should == ['for_all', 'second_only']
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -20,7 +20,6 @@ class SerializationTest < Test::Unit::TestCase
|
|
20
20
|
)
|
21
21
|
end
|
22
22
|
|
23
|
-
# [:xml, :json].each do |format|
|
24
23
|
[:json].each do |format|
|
25
24
|
context format do
|
26
25
|
should "be reversable" do
|
@@ -48,7 +47,6 @@ class SerializationTest < Test::Unit::TestCase
|
|
48
47
|
assert_nil unserialized.age
|
49
48
|
assert_equal @instance.awesome, unserialized.awesome
|
50
49
|
end
|
51
|
-
|
52
50
|
end
|
53
51
|
end
|
54
52
|
end
|
@@ -154,46 +154,6 @@ class ValidationsTest < Test::Unit::TestCase
|
|
154
154
|
doc.should have_error_on(:name)
|
155
155
|
end
|
156
156
|
end
|
157
|
-
|
158
|
-
context "validating uniqueness of" do
|
159
|
-
setup do
|
160
|
-
@document.key :name, String
|
161
|
-
@document.validates_uniqueness_of :name
|
162
|
-
end
|
163
|
-
|
164
|
-
should "not fail if object is new" do
|
165
|
-
doc = @document.new
|
166
|
-
doc.should_not have_error_on(:name)
|
167
|
-
end
|
168
|
-
|
169
|
-
should "allow to update an object" do
|
170
|
-
doc = @document.new("name" => "joe")
|
171
|
-
doc.save
|
172
|
-
doc.name = "joe"
|
173
|
-
doc.valid?.should be_true
|
174
|
-
doc.should_not have_error_on(:name)
|
175
|
-
end
|
176
|
-
|
177
|
-
should "fail if object name is not unique" do
|
178
|
-
doc = @document.new("name" => "joe")
|
179
|
-
doc.save.should be_true
|
180
|
-
sleep 0.2 # hack to avoid race condition
|
181
|
-
doc2 = @document.new("name" => "joe")
|
182
|
-
doc2.should have_error_on(:name)
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
context "validates uniqueness of with :unique shortcut" do
|
187
|
-
should "work" do
|
188
|
-
@document.key :name, String, :unique => true
|
189
|
-
|
190
|
-
doc = @document.create(:name => 'John')
|
191
|
-
doc.should_not have_error_on(:name)
|
192
|
-
sleep 0.2 # hack to avoid race condition
|
193
|
-
second_john = @document.create(:name => 'John')
|
194
|
-
second_john.should have_error_on(:name, 'has already been taken')
|
195
|
-
end
|
196
|
-
end
|
197
157
|
|
198
158
|
context "validating exclusion of" do
|
199
159
|
should "throw error if enumerator not provided" do
|
@@ -274,70 +234,6 @@ class ValidationsTest < Test::Unit::TestCase
|
|
274
234
|
end
|
275
235
|
end # Validations
|
276
236
|
|
277
|
-
context "Saving a new document that is invalid" do
|
278
|
-
setup do
|
279
|
-
@document = Class.new do
|
280
|
-
include MongoMapper::Document
|
281
|
-
key :name, String, :required => true
|
282
|
-
end
|
283
|
-
|
284
|
-
@document.collection.clear
|
285
|
-
end
|
286
|
-
|
287
|
-
should "not insert document" do
|
288
|
-
doc = @document.new
|
289
|
-
doc.save
|
290
|
-
@document.count.should == 0
|
291
|
-
end
|
292
|
-
|
293
|
-
should "populate document's errors" do
|
294
|
-
doc = @document.new
|
295
|
-
doc.errors.size.should == 0
|
296
|
-
doc.save
|
297
|
-
doc.errors.full_messages.should == ["Name can't be empty"]
|
298
|
-
end
|
299
|
-
end
|
300
|
-
|
301
|
-
context "Saving a document that is invalid (destructive)" do
|
302
|
-
setup do
|
303
|
-
@document = Class.new do
|
304
|
-
include MongoMapper::Document
|
305
|
-
key :name, String, :required => true
|
306
|
-
end
|
307
|
-
|
308
|
-
@document.collection.clear
|
309
|
-
end
|
310
|
-
|
311
|
-
should "raise error" do
|
312
|
-
doc = @document.new
|
313
|
-
lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
|
314
|
-
end
|
315
|
-
end
|
316
|
-
|
317
|
-
context "Saving an existing document that is invalid" do
|
318
|
-
setup do
|
319
|
-
@document = Class.new do
|
320
|
-
include MongoMapper::Document
|
321
|
-
key :name, String, :required => true
|
322
|
-
end
|
323
|
-
|
324
|
-
@document.collection.clear
|
325
|
-
@doc = @document.create(:name => 'John Nunemaker')
|
326
|
-
end
|
327
|
-
|
328
|
-
should "not update document" do
|
329
|
-
@doc.name = nil
|
330
|
-
@doc.save
|
331
|
-
@document.find(@doc.id).name.should == 'John Nunemaker'
|
332
|
-
end
|
333
|
-
|
334
|
-
should "populate document's errors" do
|
335
|
-
@doc.name = nil
|
336
|
-
@doc.save
|
337
|
-
@doc.errors.full_messages.should == ["Name can't be empty"]
|
338
|
-
end
|
339
|
-
end
|
340
|
-
|
341
237
|
context "Adding validation errors" do
|
342
238
|
setup do
|
343
239
|
@document = Class.new do
|
@@ -359,35 +255,5 @@ class ValidationsTest < Test::Unit::TestCase
|
|
359
255
|
doc.action = 'kick'
|
360
256
|
doc.should_not have_error_on(:action)
|
361
257
|
end
|
362
|
-
|
363
|
-
should "work with validate_on_create callback" do
|
364
|
-
@document.validate_on_create :action_present
|
365
|
-
|
366
|
-
doc = @document.new
|
367
|
-
doc.action = nil
|
368
|
-
doc.should have_error_on(:action)
|
369
|
-
|
370
|
-
doc.action = 'kick'
|
371
|
-
doc.should_not have_error_on(:action)
|
372
|
-
doc.save
|
373
|
-
|
374
|
-
doc.action = nil
|
375
|
-
doc.should_not have_error_on(:action)
|
376
|
-
end
|
377
|
-
|
378
|
-
should "work with validate_on_update callback" do
|
379
|
-
@document.validate_on_update :action_present
|
380
|
-
|
381
|
-
doc = @document.new
|
382
|
-
doc.action = nil
|
383
|
-
doc.should_not have_error_on(:action)
|
384
|
-
doc.save
|
385
|
-
|
386
|
-
doc.action = nil
|
387
|
-
doc.should have_error_on(:action)
|
388
|
-
|
389
|
-
doc.action = 'kick'
|
390
|
-
doc.should_not have_error_on(:action)
|
391
|
-
end
|
392
258
|
end
|
393
259
|
end
|