mongoid-rspec 1.8.2 → 1.9.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/.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
|