mongoid-rspec 1.8.2 → 1.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +9 -1
- data/README.md +144 -123
- data/lib/matchers/associations.rb +73 -17
- data/lib/matchers/document.rb +2 -2
- data/lib/matchers/indexes.rb +1 -1
- data/lib/matchers/validations/acceptance_of.rb +2 -2
- data/lib/matchers/validations/associated.rb +5 -5
- data/lib/matchers/validations/confirmation_of.rb +2 -2
- data/lib/matchers/validations/exclusion_of.rb +8 -1
- data/lib/matchers/validations/format_of.rb +21 -21
- data/lib/matchers/validations/numericality_of.rb +10 -10
- data/lib/matchers/validations/presence_of.rb +2 -2
- data/lib/matchers/validations/uniqueness_of.rb +12 -12
- data/lib/mongoid-rspec/version.rb +1 -1
- data/mongoid-rspec.gemspec +1 -1
- data/spec/models/article.rb +12 -12
- data/spec/models/comment.rb +2 -2
- data/spec/models/movie_article.rb +6 -5
- data/spec/models/permalink.rb +1 -1
- data/spec/models/person.rb +0 -3
- data/spec/models/profile.rb +7 -7
- data/spec/models/record.rb +1 -1
- data/spec/models/site.rb +3 -4
- data/spec/models/user.rb +14 -13
- data/spec/unit/allow_mass_assignment_spec.rb +1 -1
- data/spec/unit/associations_spec.rb +2 -2
- data/spec/unit/indexes_spec.rb +1 -1
- data/spec/unit/validations_spec.rb +1 -0
- data/spec/validators/ssn_validator.rb +3 -1
- metadata +6 -6
data/.travis.yml
CHANGED
data/README.md
CHANGED
@@ -17,160 +17,181 @@ Add to your Gemfile
|
|
17
17
|
|
18
18
|
Drop in existing or dedicated support file in spec/support (spec/support/mongoid.rb)
|
19
19
|
|
20
|
-
|
21
|
-
|
22
|
-
|
20
|
+
```ruby
|
21
|
+
RSpec.configure do |configuration|
|
22
|
+
configuration.include Mongoid::Matchers
|
23
|
+
end
|
24
|
+
```
|
23
25
|
|
24
26
|
Association Matchers
|
25
27
|
-
|
26
|
-
describe User do
|
27
|
-
it { should have_many(:articles).with_foreign_key(:author_id).ordered_by(:title) }
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
```ruby
|
30
|
+
describe User do
|
31
|
+
it { should have_many(:articles).with_foreign_key(:author_id).ordered_by(:title) }
|
32
32
|
|
33
|
-
|
33
|
+
it { should have_one(:record) }
|
34
|
+
#can verify autobuild is set to true
|
35
|
+
it { should have_one(:record).with_autobuild }
|
34
36
|
|
35
|
-
|
36
|
-
it { should have_many(:comments).with_dependent(:destroy) }
|
37
|
-
#can verify autosave is set to true
|
38
|
-
it { should have_many(:comments).with_autosave }
|
37
|
+
it { should have_many :comments }
|
39
38
|
|
40
|
-
|
39
|
+
#can also specify with_dependent to test if :dependent => :destroy/:destroy_all/:delete is set
|
40
|
+
it { should have_many(:comments).with_dependent(:destroy) }
|
41
|
+
#can verify autosave is set to true
|
42
|
+
it { should have_many(:comments).with_autosave }
|
41
43
|
|
42
|
-
|
43
|
-
it { should have_and_belong_to_many(:children).of_type(User) }
|
44
|
-
end
|
44
|
+
it { should embed_one :profile }
|
45
45
|
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
it { should have_and_belong_to_many(:children) }
|
47
|
+
it { should have_and_belong_to_many(:children).of_type(User) }
|
48
|
+
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
it { should embed_many(:comments) }
|
54
|
-
end
|
50
|
+
describe Profile do
|
51
|
+
it { should be_embedded_in(:user).as_inverse_of(:profile) }
|
52
|
+
end
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
54
|
+
describe Article do
|
55
|
+
it { should belong_to(:author).of_type(User).as_inverse_of(:articles) }
|
56
|
+
it { should belong_to(:author).of_type(User).as_inverse_of(:articles).with_index }
|
57
|
+
it { should embed_many(:comments) }
|
58
|
+
end
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
|
60
|
+
describe Comment do
|
61
|
+
it { should be_embedded_in(:article).as_inverse_of(:comments) }
|
62
|
+
it { should belong_to(:user).as_inverse_of(:comments) }
|
63
|
+
end
|
64
64
|
|
65
|
-
|
66
|
-
|
67
|
-
|
65
|
+
describe Record do
|
66
|
+
it { should belong_to(:user).as_inverse_of(:record) }
|
67
|
+
end
|
68
|
+
|
69
|
+
describe Site do
|
70
|
+
it { should have_many(:users).as_inverse_of(:site).ordered_by(:email.asc) }
|
71
|
+
end
|
72
|
+
```
|
68
73
|
|
69
74
|
Mass Assignment Matcher
|
70
75
|
-
|
71
|
-
describe User do
|
72
|
-
it { should allow_mass_assignment_of(:login) }
|
73
|
-
it { should allow_mass_assignment_of(:email) }
|
74
|
-
it { should allow_mass_assignment_of(:age) }
|
75
|
-
it { should allow_mass_assignment_of(:password) }
|
76
|
-
it { should allow_mass_assignment_of(:password) }
|
77
|
-
it { should allow_mass_assignment_of(:role).as(:admin) }
|
78
76
|
|
79
|
-
|
80
|
-
|
77
|
+
```ruby
|
78
|
+
describe User do
|
79
|
+
it { should allow_mass_assignment_of(:login) }
|
80
|
+
it { should allow_mass_assignment_of(:email) }
|
81
|
+
it { should allow_mass_assignment_of(:age) }
|
82
|
+
it { should allow_mass_assignment_of(:password) }
|
83
|
+
it { should allow_mass_assignment_of(:password) }
|
84
|
+
it { should allow_mass_assignment_of(:role).as(:admin) }
|
85
|
+
|
86
|
+
it { should_not allow_mass_assignment_of(:role) }
|
87
|
+
end
|
88
|
+
```
|
81
89
|
|
82
90
|
Validation Matchers
|
83
91
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
92
|
+
|
93
|
+
```ruby
|
94
|
+
describe Site do
|
95
|
+
it { should validate_presence_of(:name) }
|
96
|
+
it { should validate_uniqueness_of(:name) }
|
97
|
+
end
|
98
|
+
|
99
|
+
describe User do
|
100
|
+
it { should validate_presence_of(:login) }
|
101
|
+
it { should validate_uniqueness_of(:login).scoped_to(:site) }
|
102
|
+
it { should validate_uniqueness_of(:email).case_insensitive.with_message("is already taken") }
|
103
|
+
it { should validate_format_of(:login).to_allow("valid_login").not_to_allow("invalid login") }
|
104
|
+
it { should validate_associated(:profile) }
|
105
|
+
it { should validate_exclusion_of(:login).to_not_allow("super", "index", "edit") }
|
106
|
+
it { should validate_inclusion_of(:role).to_allow("admin", "member") }
|
107
|
+
it { should validate_confirmation_of(:email) }
|
108
|
+
it { should validate_presence_of(:age).on(:create, :update) }
|
109
|
+
it { should validate_numericality_of(:age).on(:create, :update) }
|
110
|
+
it { should validate_inclusion_of(:age).to_allow(23..42).on([:create, :update]) }
|
111
|
+
it { should validate_presence_of(:password).on(:create) }
|
112
|
+
it { should validate_presence_of(:provider_uid).on(:create) }
|
113
|
+
it { should validate_inclusion_of(:locale).to_allow([:en, :ru]) }
|
114
|
+
end
|
115
|
+
|
116
|
+
describe Article do
|
117
|
+
it { should validate_length_of(:title).within(8..16) }
|
118
|
+
end
|
119
|
+
|
120
|
+
describe Profile do
|
121
|
+
it { should validate_numericality_of(:age).greater_than(0) }
|
122
|
+
end
|
123
|
+
|
124
|
+
describe MovieArticle do
|
125
|
+
it { should validate_numericality_of(:rating).to_allow(:greater_than => 0).less_than_or_equal_to(5) }
|
126
|
+
it { should validate_numericality_of(:classification).to_allow(:even => true, :only_integer => true, :nil => false) }
|
127
|
+
end
|
128
|
+
|
129
|
+
describe Person do
|
130
|
+
# in order to be able to use the custom_validate matcher, the custom validator class (in this case SsnValidator)
|
131
|
+
# should redefine the kind method to return :custom, i.e. "def self.kind() :custom end"
|
132
|
+
it { should custom_validate(:ssn).with_validator(SsnValidator) }
|
133
|
+
end
|
134
|
+
```
|
124
135
|
|
125
136
|
Accepts Nested Attributes Matcher
|
126
137
|
-
|
127
|
-
describe User do
|
128
|
-
it { should accept_nested_attributes_for(:articles) }
|
129
|
-
it { should accept_nested_attributes_for(:comments) }
|
130
|
-
end
|
131
138
|
|
132
|
-
|
133
|
-
|
134
|
-
|
139
|
+
```ruby
|
140
|
+
describe User do
|
141
|
+
it { should accept_nested_attributes_for(:articles) }
|
142
|
+
it { should accept_nested_attributes_for(:comments) }
|
143
|
+
end
|
144
|
+
|
145
|
+
describe Article do
|
146
|
+
it { should accept_nested_attributes_for(:permalink) }
|
147
|
+
end
|
148
|
+
```
|
135
149
|
|
136
150
|
Index Matcher
|
137
151
|
-
|
138
|
-
describe Article do
|
139
|
-
it { should have_index_for(published: 1) }
|
140
|
-
it { should have_index_for(title: 1).with_options(unique: true, background: true) }
|
141
|
-
end
|
142
152
|
|
143
|
-
|
144
|
-
|
145
|
-
|
153
|
+
```ruby
|
154
|
+
describe Article do
|
155
|
+
it { should have_index_for(published: 1) }
|
156
|
+
it { should have_index_for(title: 1).with_options(unique: true, background: true) }
|
157
|
+
end
|
158
|
+
|
159
|
+
describe Profile do
|
160
|
+
it { should have_index_for(first_name: 1, last_name: 1) }
|
161
|
+
end
|
162
|
+
```
|
146
163
|
|
147
164
|
Others
|
148
165
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
describe User do
|
169
|
+
it { should have_fields(:email, :login) }
|
170
|
+
it { should have_field(:s).with_alias(:status) }
|
171
|
+
it { should have_fields(:birthdate, :registered_at).of_type(DateTime) }
|
172
|
+
|
173
|
+
# if you're declaring 'include Mongoid::Timestamps'
|
174
|
+
# or any of 'include Mongoid::Timestamps::Created' and 'Mongoid::Timestamps::Updated'
|
175
|
+
it { should be_timestamped_document }
|
176
|
+
it { should be_timestamped_document.with(:created) }
|
177
|
+
it { should_not be_timestamped_document.with(:updated) }
|
178
|
+
|
179
|
+
it { should be_versioned_document } # if you're declaring `include Mongoid::Versioning`
|
180
|
+
it { should be_paranoid_document } # if you're declaring `include Mongoid::Paranoia`
|
181
|
+
it { should be_multiparameted_document } # if you're declaring `include Mongoid::MultiParameterAttributes`
|
182
|
+
end
|
183
|
+
|
184
|
+
describe Log do
|
185
|
+
it { should be_stored_in :logs }
|
186
|
+
end
|
187
|
+
|
188
|
+
describe Article do
|
189
|
+
it { should have_field(:published).of_type(Boolean).with_default_value_of(false) }
|
190
|
+
it { should have_field(:allow_comments).of_type(Boolean).with_default_value_of(true) }
|
191
|
+
it { should_not have_field(:allow_comments).of_type(Boolean).with_default_value_of(false) }
|
192
|
+
it { should_not have_field(:number_of_comments).of_type(Integer).with_default_value_of(1) }
|
193
|
+
end
|
194
|
+
```
|
174
195
|
|
175
196
|
Known issues
|
176
197
|
-
|
@@ -76,8 +76,28 @@ module Mongoid
|
|
76
76
|
self
|
77
77
|
end
|
78
78
|
|
79
|
+
def with_polymorphism
|
80
|
+
@association[:polymorphic] = true
|
81
|
+
@expectation_message << " which specifies polymorphic as #{@association[:polymorphic].to_s}"
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def with_cascading_callbacks
|
86
|
+
@association[:cascade_callbacks] = true
|
87
|
+
@expectation_message << " which specifies cascade_callbacks as #{@association[:cascade_callbacks].to_s}"
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def cyclic
|
92
|
+
@association[:cyclic] = true
|
93
|
+
@expectation_message << " which specifies cyclic as #{@association[:cyclic].to_s}"
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
79
97
|
def stored_as(store_as)
|
80
|
-
|
98
|
+
@association[:store_as] = store_as.to_s
|
99
|
+
@expectation_message << " which is stored as #{@association[:store_as].inspect}"
|
100
|
+
self
|
81
101
|
end
|
82
102
|
|
83
103
|
def with_foreign_key(foreign_key)
|
@@ -166,6 +186,42 @@ module Mongoid
|
|
166
186
|
end
|
167
187
|
end
|
168
188
|
|
189
|
+
if @association[:polymorphic]
|
190
|
+
if metadata.polymorphic? != true
|
191
|
+
@negative_result_message = "#{@positive_result_message} which did not set polymorphic"
|
192
|
+
return false
|
193
|
+
else
|
194
|
+
@positive_result_message = "#{@positive_result_message} which set polymorphic"
|
195
|
+
end
|
196
|
+
end
|
197
|
+
|
198
|
+
if @association[:cascade_callbacks]
|
199
|
+
if metadata.cascading_callbacks? != true
|
200
|
+
@negative_result_message = "#{@positive_result_message} which did not set cascade_callbacks"
|
201
|
+
return false
|
202
|
+
else
|
203
|
+
@positive_result_message = "#{@positive_result_message} which set cascade_callbacks"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
if @association[:cyclic]
|
208
|
+
if metadata.cyclic? != true
|
209
|
+
@negative_result_message = "#{@positive_result_message} which did not set cyclic"
|
210
|
+
return false
|
211
|
+
else
|
212
|
+
@positive_result_message = "#{@positive_result_message} which set cyclic"
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
if @association[:store_as]
|
217
|
+
if metadata.store_as != @association[:store_as]
|
218
|
+
@negative_result_message = "#{@positive_result_message} which is stored as #{metadata.store_as}"
|
219
|
+
return false
|
220
|
+
else
|
221
|
+
@positive_result_message = "#{@positive_result_message} which is stored as #{metadata.store_as}"
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
169
225
|
if @association[:index]
|
170
226
|
if metadata.index != true
|
171
227
|
@negative_result_message = "#{@positive_result_message} which did not set index"
|
@@ -202,22 +258,22 @@ module Mongoid
|
|
202
258
|
def type_description(type = nil, passive = true)
|
203
259
|
type ||= @association[:type]
|
204
260
|
case type.name
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
261
|
+
when EMBEDS_ONE.name
|
262
|
+
(passive ? 'embed' : 'embeds') << ' one'
|
263
|
+
when EMBEDS_MANY.name
|
264
|
+
(passive ? 'embed' : 'embeds') << ' many'
|
265
|
+
when EMBEDDED_IN.name
|
266
|
+
(passive ? 'be' : 'is') << ' embedded in'
|
267
|
+
when HAS_ONE.name
|
268
|
+
(passive ? 'reference' : 'references') << ' one'
|
269
|
+
when HAS_MANY.name
|
270
|
+
(passive ? 'reference' : 'references') << ' many'
|
271
|
+
when HAS_AND_BELONGS_TO_MANY.name
|
272
|
+
(passive ? 'reference' : 'references') << ' and referenced in many'
|
273
|
+
when BELONGS_TO.name
|
274
|
+
(passive ? 'be referenced in' : 'referenced in')
|
275
|
+
else
|
276
|
+
raise "Unknown association type '%s'" % type
|
221
277
|
end
|
222
278
|
end
|
223
279
|
|
data/lib/matchers/document.rb
CHANGED
@@ -14,7 +14,7 @@ module Mongoid
|
|
14
14
|
@type = type
|
15
15
|
self
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def with_alias(field_alias)
|
19
19
|
@field_alias = field_alias
|
20
20
|
self
|
@@ -54,7 +54,7 @@ module Mongoid
|
|
54
54
|
@errors.push "is not localized #{attr.inspect}"
|
55
55
|
end
|
56
56
|
end
|
57
|
-
|
57
|
+
|
58
58
|
else
|
59
59
|
@errors.push "no field named #{attr.inspect}"
|
60
60
|
end
|
data/lib/matchers/indexes.rb
CHANGED
@@ -1,19 +1,19 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Matchers
|
3
|
-
module Validations
|
3
|
+
module Validations
|
4
4
|
class ValidateAssociatedMatcher < HaveValidationMatcher
|
5
5
|
def initialize(name)
|
6
6
|
super(name, :associated)
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def description
|
10
10
|
"validate associated #{@field.inspect}"
|
11
11
|
end
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def validate_associated(association_name)
|
15
15
|
ValidateAssociatedMatcher.new(association_name)
|
16
|
-
end
|
16
|
+
end
|
17
17
|
end
|
18
18
|
end
|
19
|
-
end
|
19
|
+
end
|
@@ -15,7 +15,14 @@ module Mongoid
|
|
15
15
|
return false unless result = super(actual)
|
16
16
|
|
17
17
|
if @not_allowed_values
|
18
|
-
|
18
|
+
raw_validator_not_allowed_values = @validator.options[:in]
|
19
|
+
|
20
|
+
validator_not_allowed_values = case raw_validator_not_allowed_values
|
21
|
+
when Range then raw_validator_not_allowed_values.to_a
|
22
|
+
when Proc then raw_validator_not_allowed_values.call(actual)
|
23
|
+
else raw_validator_not_allowed_values end
|
24
|
+
|
25
|
+
allowed_values = @not_allowed_values - validator_not_allowed_values
|
19
26
|
if allowed_values.empty?
|
20
27
|
@positive_result_message = @positive_result_message << " not allowing all values mentioned"
|
21
28
|
else
|
@@ -1,71 +1,71 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Matchers
|
3
|
-
module Validations
|
3
|
+
module Validations
|
4
4
|
class ValidateFormatOfMatcher < HaveValidationMatcher
|
5
5
|
def initialize(field)
|
6
6
|
super(field, :format)
|
7
7
|
end
|
8
|
-
|
8
|
+
|
9
9
|
def with_format(format)
|
10
10
|
@format = format
|
11
11
|
self
|
12
12
|
end
|
13
|
-
|
13
|
+
|
14
14
|
def to_allow(valid_value)
|
15
15
|
@valid_value = valid_value
|
16
|
-
self
|
16
|
+
self
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def not_to_allow(invalid_value)
|
20
20
|
@invalid_value = invalid_value
|
21
|
-
self
|
21
|
+
self
|
22
22
|
end
|
23
|
-
|
23
|
+
|
24
24
|
def matches?(actual)
|
25
25
|
return false unless result = super(actual)
|
26
|
-
|
26
|
+
|
27
27
|
if @format
|
28
28
|
if @validator.options[:with] == @format
|
29
29
|
@positive_result_message = @positive_result_message << " with format #{@validator.options[:format].inspect}"
|
30
30
|
else
|
31
31
|
@negative_result_message = @negative_result_message << " with format #{@validator.options[:format].inspect}"
|
32
32
|
result = false
|
33
|
-
end
|
33
|
+
end
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
if @valid_value
|
37
37
|
if @validator.options[:with] =~ @valid_value
|
38
38
|
@positive_result_message = @positive_result_message << " with #{@valid_value.inspect} as a valid value"
|
39
39
|
else
|
40
40
|
@negative_result_message = @negative_result_message << " with #{@valid_value.inspect} as an invalid value"
|
41
41
|
result = false
|
42
|
-
end
|
42
|
+
end
|
43
43
|
end
|
44
|
-
|
45
|
-
if @invalid_value
|
44
|
+
|
45
|
+
if @invalid_value
|
46
46
|
if !(@invalid_value =~ @validator.options[:with])
|
47
47
|
@positive_result_message = @positive_result_message << " with #{@invalid_value.inspect} as an invalid value"
|
48
48
|
else
|
49
49
|
@negative_result_message = @negative_result_message << " with #{@invalid_value.inspect} as a valid value"
|
50
50
|
result = false
|
51
|
-
end
|
51
|
+
end
|
52
52
|
end
|
53
|
-
|
54
|
-
result
|
53
|
+
|
54
|
+
result
|
55
55
|
end
|
56
|
-
|
56
|
+
|
57
57
|
def description
|
58
58
|
options_desc = []
|
59
59
|
options_desc << " with format #{@format.inspect}" if @format
|
60
60
|
options_desc << " allowing the value #{@valid_value.inspect}" if @valid_value
|
61
|
-
options_desc << " not allowing the value #{@invalid_value.inspect}" if @invalid_value
|
61
|
+
options_desc << " not allowing the value #{@invalid_value.inspect}" if @invalid_value
|
62
62
|
super << options_desc.to_sentence
|
63
|
-
end
|
63
|
+
end
|
64
64
|
end
|
65
65
|
|
66
66
|
def validate_format_of(field)
|
67
67
|
ValidateFormatOfMatcher.new(field)
|
68
|
-
end
|
68
|
+
end
|
69
69
|
end
|
70
70
|
end
|
71
|
-
end
|
71
|
+
end
|
@@ -2,14 +2,14 @@ module Mongoid
|
|
2
2
|
module Matchers
|
3
3
|
module Validations
|
4
4
|
class ValidateNumericalityOfMatcher < HaveValidationMatcher
|
5
|
-
@@allowed_options = [:equal_to, :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to,
|
5
|
+
@@allowed_options = [:equal_to, :greater_than, :greater_than_or_equal_to, :less_than, :less_than_or_equal_to,
|
6
6
|
:even, :odd, :only_integer, :allow_nil, :nil]
|
7
|
-
|
7
|
+
|
8
8
|
def initialize(field)
|
9
9
|
super(field, :numericality)
|
10
10
|
@options = {}
|
11
11
|
end
|
12
|
-
|
12
|
+
|
13
13
|
def to_allow(options)
|
14
14
|
options[:equal_to] = options if options.is_a?(Numeric)
|
15
15
|
options[:allow_nil] = options.delete(:nil) if options.has_key?(:nil)
|
@@ -21,7 +21,7 @@ module Mongoid
|
|
21
21
|
|
22
22
|
def matches?(actual)
|
23
23
|
return false unless result = super(actual)
|
24
|
-
|
24
|
+
|
25
25
|
@@allowed_options.each do |comparator|
|
26
26
|
if @options.has_key?(comparator) and !([:even, :odd, :only_integer].include?(comparator) and !@validator.options.include?(comparator))
|
27
27
|
result &= (@validator.options[comparator] == @options[comparator])
|
@@ -35,12 +35,12 @@ module Mongoid
|
|
35
35
|
def description
|
36
36
|
super << options_message(@options)
|
37
37
|
end
|
38
|
-
|
38
|
+
|
39
39
|
protected
|
40
|
-
|
40
|
+
|
41
41
|
def options_message(options)
|
42
42
|
type_msg = []
|
43
|
-
comp_msg = []
|
43
|
+
comp_msg = []
|
44
44
|
options.each_pair do |key, value|
|
45
45
|
case key
|
46
46
|
when :allow_nil
|
@@ -52,10 +52,10 @@ module Mongoid
|
|
52
52
|
comp_msg << "#{key.to_s.gsub("_", " ")} #{value.inspect}"
|
53
53
|
end
|
54
54
|
end
|
55
|
-
allow_nil = (options[:allow_nil] ? "nil" : "non-nil") if options.has_key?(:allow_nil)
|
55
|
+
allow_nil = (options[:allow_nil] ? "nil" : "non-nil") if options.has_key?(:allow_nil)
|
56
56
|
["", "allowing", allow_nil, type_msg.any? ? type_msg.to_sentence : nil, "values", comp_msg.any? ? comp_msg.to_sentence : nil].compact.join(" ")
|
57
|
-
end
|
58
|
-
|
57
|
+
end
|
58
|
+
|
59
59
|
def method_missing(m, *args, &block)
|
60
60
|
if @@allowed_options.include?(m.to_sym)
|
61
61
|
raise ArgumentError, "wrong number of arguments (#{args.length} for 1)" if args.length > 1
|
@@ -1,23 +1,23 @@
|
|
1
1
|
module Mongoid
|
2
2
|
module Matchers
|
3
|
-
module Validations
|
3
|
+
module Validations
|
4
4
|
class ValidateUniquenessOfMatcher < HaveValidationMatcher
|
5
5
|
include WithMessage
|
6
6
|
def initialize(field)
|
7
7
|
super(field, :uniqueness)
|
8
8
|
end
|
9
|
-
|
9
|
+
|
10
10
|
def scoped_to(*scope)
|
11
11
|
@scope = [scope].flatten.map(&:to_sym)
|
12
12
|
self
|
13
|
-
end
|
13
|
+
end
|
14
14
|
alias_method :scoped_on, :scoped_to
|
15
|
-
|
15
|
+
|
16
16
|
def case_insensitive
|
17
17
|
@case_insensitive = true
|
18
18
|
self
|
19
19
|
end
|
20
|
-
|
20
|
+
|
21
21
|
def allow_blank?(allow_blank)
|
22
22
|
@allow_blank = allow_blank
|
23
23
|
self
|
@@ -25,12 +25,12 @@ module Mongoid
|
|
25
25
|
|
26
26
|
def matches?(actual)
|
27
27
|
return false unless @result = super(actual)
|
28
|
-
|
28
|
+
|
29
29
|
check_scope if @scope
|
30
30
|
check_allow_blank if @allow_blank
|
31
31
|
check_case_sensitivity if @case_insensitive
|
32
32
|
check_expected_message if @expected_message
|
33
|
-
|
33
|
+
|
34
34
|
@result
|
35
35
|
end
|
36
36
|
|
@@ -42,7 +42,7 @@ module Mongoid
|
|
42
42
|
options_desc << " with message '#{@expected_message}'" if @expected_message
|
43
43
|
super << options_desc.to_sentence
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
private
|
47
47
|
|
48
48
|
def check_allow_blank
|
@@ -63,7 +63,7 @@ module Mongoid
|
|
63
63
|
@result = false
|
64
64
|
end
|
65
65
|
end
|
66
|
-
|
66
|
+
|
67
67
|
def check_case_sensitivity
|
68
68
|
if @validator.options[:case_sensitive] == false
|
69
69
|
@positive_result_message << " with case insensitive values"
|
@@ -73,10 +73,10 @@ module Mongoid
|
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
76
|
-
|
76
|
+
|
77
77
|
def validate_uniqueness_of(field)
|
78
78
|
ValidateUniquenessOfMatcher.new(field)
|
79
|
-
end
|
79
|
+
end
|
80
80
|
end
|
81
81
|
end
|
82
|
-
end
|
82
|
+
end
|
data/mongoid-rspec.gemspec
CHANGED
data/spec/models/article.rb
CHANGED
@@ -5,24 +5,24 @@ class Article
|
|
5
5
|
include Mongoid::Versioning
|
6
6
|
include Mongoid::MultiParameterAttributes
|
7
7
|
|
8
|
-
field :title, :
|
8
|
+
field :title, localize: true
|
9
9
|
field :content
|
10
|
-
field :published, :
|
11
|
-
field :allow_comments, :
|
12
|
-
field :number_of_comments, :
|
13
|
-
field :status, :
|
10
|
+
field :published, type: Boolean, default: false
|
11
|
+
field :allow_comments, type: Boolean, default: true
|
12
|
+
field :number_of_comments, type: Integer
|
13
|
+
field :status, type: Symbol
|
14
14
|
|
15
|
-
embeds_many :comments
|
15
|
+
embeds_many :comments, cascade_callbacks: true
|
16
16
|
embeds_one :permalink
|
17
|
-
belongs_to :author, :
|
17
|
+
belongs_to :author, class_name: 'User', inverse_of: :articles, index: true
|
18
18
|
|
19
|
-
validates :title, :
|
19
|
+
validates :title, presence: true
|
20
20
|
|
21
|
-
validates_inclusion_of :status, :
|
22
|
-
validates_inclusion_of :status, :
|
21
|
+
validates_inclusion_of :status, in: [:pending], on: :create
|
22
|
+
validates_inclusion_of :status, in: [:approved, :rejected ], on: :update
|
23
23
|
|
24
|
-
validates_length_of :title, :
|
25
|
-
validates_length_of :content, :
|
24
|
+
validates_length_of :title, within: 8..16
|
25
|
+
validates_length_of :content, minimum: 200
|
26
26
|
|
27
27
|
index({ title: 1 }, { unique: true, background: true })
|
28
28
|
index({ published: 1 })
|
data/spec/models/comment.rb
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
class MovieArticle < Article
|
2
|
-
|
3
|
-
field :
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
|
3
|
+
field :rating, type: Float
|
4
|
+
field :classification, type: Integer
|
5
|
+
|
6
|
+
validates :rating, numericality: { greater_than: 0, less_than_or_equal_to: 5 }
|
7
|
+
validates :classification, numericality: { even: true, only_integer: true, allow_nil: false }
|
7
8
|
end
|
data/spec/models/permalink.rb
CHANGED
data/spec/models/person.rb
CHANGED
data/spec/models/profile.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
class Profile
|
2
2
|
include Mongoid::Document
|
3
|
-
|
3
|
+
|
4
4
|
field :first_name
|
5
5
|
field :last_name
|
6
6
|
field :age
|
7
7
|
field :hobbies, type: Array, default: []
|
8
8
|
|
9
|
-
embedded_in :user, :
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
9
|
+
embedded_in :user, inverse_of: :profile
|
10
|
+
|
11
|
+
validates :age, numericality: { greater_than: 0 }
|
12
|
+
validates :terms_of_service, acceptance: true
|
13
|
+
validates :hobbies, length: { minimum: 1, message: "requires at least one hobby" }
|
14
14
|
|
15
|
-
index({first_name: 1, last_name: 1 })
|
15
|
+
index({ first_name: 1, last_name: 1 })
|
16
16
|
end
|
data/spec/models/record.rb
CHANGED
data/spec/models/site.rb
CHANGED
@@ -3,8 +3,7 @@ class Site
|
|
3
3
|
|
4
4
|
field :name
|
5
5
|
|
6
|
-
has_many :users, :
|
6
|
+
has_many :users, inverse_of: :site, order: :email.desc
|
7
7
|
|
8
|
-
validates :name, :
|
9
|
-
|
10
|
-
end
|
8
|
+
validates :name, presence: true, uniqueness: true
|
9
|
+
end
|
data/spec/models/user.rb
CHANGED
@@ -10,25 +10,26 @@ class User
|
|
10
10
|
field :provider_uid
|
11
11
|
field :locale
|
12
12
|
|
13
|
-
belongs_to :site, :
|
14
|
-
has_many :articles, :
|
15
|
-
has_many :comments, :
|
16
|
-
has_and_belongs_to_many :children, :
|
17
|
-
has_one :record, :
|
13
|
+
belongs_to :site, inverse_of: :users
|
14
|
+
has_many :articles, foreign_key: :author_id, order: :title
|
15
|
+
has_many :comments, dependent: :destroy, autosave: true
|
16
|
+
has_and_belongs_to_many :children, class_name: "User"
|
17
|
+
has_one :record, autobuild: true
|
18
18
|
|
19
19
|
embeds_one :profile
|
20
20
|
|
21
|
-
validates :login, :
|
22
|
-
validates :email, :
|
23
|
-
validates :role, :
|
24
|
-
validates :profile, :
|
25
|
-
validates :age, :
|
26
|
-
validates :password, :
|
21
|
+
validates :login, presence: true, uniqueness: { scope: :site }, format: { with: /^[\w\-]+$/ }, exclusion: { in: ["super", "index", "edit"] }
|
22
|
+
validates :email, uniqueness: { case_sensitive: false, scope: :site, message: "is already taken" }, confirmation: true
|
23
|
+
validates :role, presence: true, inclusion: { in: ["admin", "moderator", "member"] }
|
24
|
+
validates :profile, presence: true, associated: true
|
25
|
+
validates :age, presence: true, numericality: true, inclusion: { in: 23..42 }, on: [:create, :update]
|
26
|
+
validates :password, presence: true, on: [:create, :update]
|
27
|
+
validates :password, exclusion: { in: ->(user) { ['password'] } }
|
27
28
|
validates :provider_uid, presence: true
|
28
|
-
validates :locale, :
|
29
|
+
validates :locale, inclusion: { in: ->(user) { [:en, :ru] } }
|
29
30
|
|
30
31
|
attr_accessible :login, :email, :age, :password
|
31
|
-
attr_accessible :role, :
|
32
|
+
attr_accessible :role, as: :admin
|
32
33
|
|
33
34
|
accepts_nested_attributes_for :articles, :comments
|
34
35
|
|
@@ -8,7 +8,7 @@ describe "AllowMassAssignment" do
|
|
8
8
|
it { should allow_mass_assignment_of(:password) }
|
9
9
|
it { should allow_mass_assignment_of(:password) }
|
10
10
|
it { should allow_mass_assignment_of(:role).as(:admin) }
|
11
|
-
|
11
|
+
|
12
12
|
it { should_not allow_mass_assignment_of(:role) }
|
13
13
|
end
|
14
14
|
end
|
@@ -19,12 +19,12 @@ describe "Associations" do
|
|
19
19
|
|
20
20
|
describe Article do
|
21
21
|
it { should belong_to(:author).of_type(User).as_inverse_of(:articles).with_index }
|
22
|
-
it { should embed_many(:comments) }
|
22
|
+
it { should embed_many(:comments).with_cascading_callbacks }
|
23
23
|
it { should embed_one(:permalink) }
|
24
24
|
end
|
25
25
|
|
26
26
|
describe Comment do
|
27
|
-
it { should be_embedded_in(:article).as_inverse_of(:comments) }
|
27
|
+
it { should be_embedded_in(:article).as_inverse_of(:comments).with_polymorphism }
|
28
28
|
it { should belong_to(:user).as_inverse_of(:comments) }
|
29
29
|
end
|
30
30
|
|
data/spec/unit/indexes_spec.rb
CHANGED
@@ -13,6 +13,7 @@ describe "Validations" do
|
|
13
13
|
it { should validate_format_of(:login).to_allow("valid_login").not_to_allow("invalid login") }
|
14
14
|
it { should validate_associated(:profile) }
|
15
15
|
it { should validate_exclusion_of(:login).to_not_allow("super", "index", "edit") }
|
16
|
+
it { should validate_exclusion_of(:password).to_not_allow("password") }
|
16
17
|
it { should validate_inclusion_of(:role).to_allow("admin", "member") }
|
17
18
|
it { should validate_confirmation_of(:email) }
|
18
19
|
it { should validate_presence_of(:age).on(:create, :update) }
|
@@ -1,7 +1,9 @@
|
|
1
1
|
class SsnValidator < ActiveModel::EachValidator
|
2
2
|
|
3
3
|
def validate_each(record, attribute, value)
|
4
|
-
|
4
|
+
unless valid_ssn?(record, attribute, value)
|
5
|
+
record.errors[attribute] << "#{value} is not a valid Social Security Number"
|
6
|
+
end
|
5
7
|
end
|
6
8
|
|
7
9
|
def self.kind() :custom end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongoid-rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.9.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-08-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -50,7 +50,7 @@ dependencies:
|
|
50
50
|
requirements:
|
51
51
|
- - ! '>='
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version: '2.
|
53
|
+
version: '2.14'
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -58,7 +58,7 @@ dependencies:
|
|
58
58
|
requirements:
|
59
59
|
- - ! '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '2.
|
61
|
+
version: '2.14'
|
62
62
|
description: RSpec matches for Mongoid models, including association and validation
|
63
63
|
matchers
|
64
64
|
email: evansagge@gmail.com
|
@@ -130,7 +130,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
130
130
|
version: '0'
|
131
131
|
segments:
|
132
132
|
- 0
|
133
|
-
hash:
|
133
|
+
hash: 2533253103994848490
|
134
134
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
135
135
|
none: false
|
136
136
|
requirements:
|
@@ -139,7 +139,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
139
|
version: '0'
|
140
140
|
segments:
|
141
141
|
- 0
|
142
|
-
hash:
|
142
|
+
hash: 2533253103994848490
|
143
143
|
requirements: []
|
144
144
|
rubyforge_project: mongoid-rspec
|
145
145
|
rubygems_version: 1.8.25
|