mongoid-spec 4.0.1
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.
- checksums.yaml +7 -0
- data/LICENSE +20 -0
- data/README.md +373 -0
- data/Rakefile +17 -0
- data/lib/matchers/accept_nested_attributes.rb +67 -0
- data/lib/matchers/allow_mass_assignment.rb +102 -0
- data/lib/matchers/associations.rb +330 -0
- data/lib/matchers/be_dynamic_document.rb +26 -0
- data/lib/matchers/be_mongoid_document.rb +26 -0
- data/lib/matchers/be_stored_in.rb +50 -0
- data/lib/matchers/have_field.rb +90 -0
- data/lib/matchers/have_index_for.rb +63 -0
- data/lib/matchers/have_timestamps.rb +61 -0
- data/lib/matchers/validations.rb +81 -0
- data/lib/matchers/validations/acceptance_of.rb +9 -0
- data/lib/matchers/validations/associated.rb +19 -0
- data/lib/matchers/validations/confirmation_of.rb +22 -0
- data/lib/matchers/validations/custom_validation_of.rb +47 -0
- data/lib/matchers/validations/exclusion_of.rb +49 -0
- data/lib/matchers/validations/format_of.rb +71 -0
- data/lib/matchers/validations/inclusion_of.rb +49 -0
- data/lib/matchers/validations/length_of.rb +147 -0
- data/lib/matchers/validations/numericality_of.rb +90 -0
- data/lib/matchers/validations/presence_of.rb +9 -0
- data/lib/matchers/validations/uniqueness_of.rb +82 -0
- data/lib/matchers/validations/with_message.rb +27 -0
- data/lib/mongoid-rspec.rb +1 -0
- data/lib/mongoid/rspec.rb +40 -0
- data/lib/mongoid/rspec/version.rb +5 -0
- data/spec/models/article.rb +29 -0
- data/spec/models/comment.rb +6 -0
- data/spec/models/log.rb +9 -0
- data/spec/models/movie_article.rb +8 -0
- data/spec/models/permalink.rb +5 -0
- data/spec/models/person.rb +10 -0
- data/spec/models/profile.rb +16 -0
- data/spec/models/record.rb +5 -0
- data/spec/models/site.rb +9 -0
- data/spec/models/user.rb +37 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/unit/accept_nested_attributes_spec.rb +12 -0
- data/spec/unit/associations_spec.rb +42 -0
- data/spec/unit/be_dynamic_document_spec.rb +22 -0
- data/spec/unit/be_mongoid_document_spec.rb +25 -0
- data/spec/unit/be_stored_in.rb +54 -0
- data/spec/unit/document_spec.rb +16 -0
- data/spec/unit/have_index_for_spec.rb +46 -0
- data/spec/unit/have_timestamps_spec.rb +71 -0
- data/spec/unit/validations_spec.rb +54 -0
- data/spec/validators/ssn_validator.rb +16 -0
- metadata +171 -0
@@ -0,0 +1,102 @@
|
|
1
|
+
# this code is totally extracted from shoulda-matchers gem.
|
2
|
+
module Mongoid
|
3
|
+
module Matchers
|
4
|
+
class AllowMassAssignmentOfMatcher # :nodoc:
|
5
|
+
attr_reader :failure_message, :negative_failure_message
|
6
|
+
alias :failure_message_when_negated :negative_failure_message
|
7
|
+
|
8
|
+
def initialize(attribute)
|
9
|
+
@attribute = attribute.to_s
|
10
|
+
@options = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def as(role)
|
14
|
+
if active_model_less_than_3_1?
|
15
|
+
raise "You can specify role only in Rails 3.1 or greater"
|
16
|
+
end
|
17
|
+
@options[:role] = role
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def matches?(klass)
|
22
|
+
@klass = klass
|
23
|
+
if attr_mass_assignable?
|
24
|
+
if whitelisting?
|
25
|
+
@negative_failure_message = "#{@attribute} was made accessible"
|
26
|
+
else
|
27
|
+
if protected_attributes.empty?
|
28
|
+
@negative_failure_message = "no attributes were protected"
|
29
|
+
else
|
30
|
+
@negative_failure_message = "#{class_name} is protecting " <<
|
31
|
+
"#{protected_attributes.to_a.to_sentence}, " <<
|
32
|
+
"but not #{@attribute}."
|
33
|
+
end
|
34
|
+
end
|
35
|
+
true
|
36
|
+
else
|
37
|
+
if whitelisting?
|
38
|
+
@failure_message = "Expected #{@attribute} to be accessible"
|
39
|
+
else
|
40
|
+
@failure_message = "Did not expect #{@attribute} to be protected"
|
41
|
+
end
|
42
|
+
false
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def description
|
47
|
+
"allow mass assignment of #{@attribute}"
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def role
|
53
|
+
@options[:role] || :default
|
54
|
+
end
|
55
|
+
|
56
|
+
def protected_attributes
|
57
|
+
@protected_attributes ||= (@klass.class.protected_attributes || [])
|
58
|
+
end
|
59
|
+
|
60
|
+
def accessible_attributes
|
61
|
+
@accessible_attributes ||= (@klass.class.accessible_attributes || [])
|
62
|
+
end
|
63
|
+
|
64
|
+
def whitelisting?
|
65
|
+
authorizer.kind_of?(::ActiveModel::MassAssignmentSecurity::WhiteList)
|
66
|
+
end
|
67
|
+
|
68
|
+
def attr_mass_assignable?
|
69
|
+
!authorizer.deny?(@attribute)
|
70
|
+
end
|
71
|
+
|
72
|
+
def authorizer
|
73
|
+
if active_model_less_than_3_1?
|
74
|
+
@klass.class.active_authorizer
|
75
|
+
else
|
76
|
+
@klass.class.active_authorizer[role]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def class_name
|
81
|
+
@klass.class.name
|
82
|
+
end
|
83
|
+
|
84
|
+
def active_model_less_than_3_1?
|
85
|
+
::ActiveModel::VERSION::STRING.to_f < 3.1
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
# Ensures that the attribute can be set on mass update.
|
90
|
+
#
|
91
|
+
# it { should_not allow_mass_assignment_of(:password) }
|
92
|
+
# it { should allow_mass_assignment_of(:first_name) }
|
93
|
+
#
|
94
|
+
# In Rails 3.1 you can check role as well:
|
95
|
+
#
|
96
|
+
# it { should allow_mass_assignment_of(:first_name).as(:admin) }
|
97
|
+
#
|
98
|
+
def allow_mass_assignment_of(value)
|
99
|
+
AllowMassAssignmentOfMatcher.new(value)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
@@ -0,0 +1,330 @@
|
|
1
|
+
require 'mongoid/relations'
|
2
|
+
|
3
|
+
module Mongoid
|
4
|
+
module Matchers
|
5
|
+
module Associations
|
6
|
+
|
7
|
+
HAS_MANY = Mongoid::Relations::Referenced::Many
|
8
|
+
HAS_AND_BELONGS_TO_MANY = Mongoid::Relations::Referenced::ManyToMany
|
9
|
+
HAS_ONE = Mongoid::Relations::Referenced::One
|
10
|
+
BELONGS_TO = Mongoid::Relations::Referenced::In
|
11
|
+
EMBEDS_MANY = Mongoid::Relations::Embedded::Many
|
12
|
+
EMBEDS_ONE = Mongoid::Relations::Embedded::One
|
13
|
+
EMBEDDED_IN = Mongoid::Relations::Embedded::In
|
14
|
+
|
15
|
+
class HaveAssociationMatcher
|
16
|
+
def initialize(name, association_type)
|
17
|
+
@association = {}
|
18
|
+
@association[:name] = name.to_s
|
19
|
+
@association[:type] = association_type
|
20
|
+
@expectation_message = "#{type_description} #{@association[:name].inspect}"
|
21
|
+
@expectation_message << " of type #{@association[:class].inspect}" unless @association[:class].nil?
|
22
|
+
end
|
23
|
+
|
24
|
+
def of_type(klass)
|
25
|
+
@association[:class] = klass
|
26
|
+
@expectation_message << " of type #{@association[:class].inspect}"
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def as_inverse_of(association_inverse_name)
|
31
|
+
@association[:inverse_of] = association_inverse_name.to_s
|
32
|
+
@expectation_message << " which is an inverse of #{@association[:inverse_of].inspect}"
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def ordered_by(association_field_name)
|
37
|
+
raise "#{@association[:type].inspect} does not respond to :order" unless [HAS_MANY, HAS_AND_BELONGS_TO_MANY, EMBEDS_MANY].include?(@association[:type])
|
38
|
+
@association[:order] = association_field_name.to_s
|
39
|
+
@expectation_message << " ordered by #{@association[:order].inspect}"
|
40
|
+
|
41
|
+
if association_field_name.is_a? Mongoid::Criteria::Queryable::Key
|
42
|
+
@association[:order_operator] = association_field_name.operator
|
43
|
+
@expectation_message << " #{order_way(@association[:order_operator])}"
|
44
|
+
end
|
45
|
+
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def with_dependent(method_name)
|
50
|
+
@association[:dependent] = method_name
|
51
|
+
@expectation_message << " which specifies dependent as #{@association[:dependent].to_s}"
|
52
|
+
self
|
53
|
+
end
|
54
|
+
|
55
|
+
def with_autosave
|
56
|
+
@association[:autosave] = true
|
57
|
+
@expectation_message << " which specifies autosave as #{@association[:autosave].to_s}"
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def with_index
|
62
|
+
@association[:index] = true
|
63
|
+
@expectation_message << " which specifies index as #{@association[:index].to_s}"
|
64
|
+
self
|
65
|
+
end
|
66
|
+
|
67
|
+
def with_autobuild
|
68
|
+
@association[:autobuild] = true
|
69
|
+
@expectation_message << " which specifies autobuild as #{@association[:autobuild].to_s}"
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def with_polymorphism
|
74
|
+
@association[:polymorphic] = true
|
75
|
+
@expectation_message << " which specifies polymorphic as #{@association[:polymorphic].to_s}"
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def with_cascading_callbacks
|
80
|
+
@association[:cascade_callbacks] = true
|
81
|
+
@expectation_message << " which specifies cascade_callbacks as #{@association[:cascade_callbacks].to_s}"
|
82
|
+
self
|
83
|
+
end
|
84
|
+
|
85
|
+
def cyclic
|
86
|
+
@association[:cyclic] = true
|
87
|
+
@expectation_message << " which specifies cyclic as #{@association[:cyclic].to_s}"
|
88
|
+
self
|
89
|
+
end
|
90
|
+
|
91
|
+
def stored_as(store_as)
|
92
|
+
@association[:store_as] = store_as.to_s
|
93
|
+
@expectation_message << " which is stored as #{@association[:store_as].inspect}"
|
94
|
+
self
|
95
|
+
end
|
96
|
+
|
97
|
+
def with_foreign_key(foreign_key)
|
98
|
+
@association[:foreign_key] = foreign_key.to_s
|
99
|
+
@expectation_message << " using foreign key #{@association[:foreign_key].inspect}"
|
100
|
+
self
|
101
|
+
end
|
102
|
+
|
103
|
+
def with_counter_cache
|
104
|
+
@association[:counter_cache] = true
|
105
|
+
@expectation_message << " which specifies counter_cache as #{@association[:counter_cache].to_s}"
|
106
|
+
self
|
107
|
+
end
|
108
|
+
|
109
|
+
def matches?(actual)
|
110
|
+
@actual = actual.is_a?(Class) ? actual : actual.class
|
111
|
+
metadata = @actual.relations[@association[:name]]
|
112
|
+
|
113
|
+
if metadata.nil?
|
114
|
+
@negative_result_message = "no association named #{@association[:name]}"
|
115
|
+
return false
|
116
|
+
else
|
117
|
+
@positive_result_message = "association named #{@association[:name]}"
|
118
|
+
end
|
119
|
+
|
120
|
+
relation = metadata.relation
|
121
|
+
if relation != @association[:type]
|
122
|
+
@negative_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name]}"
|
123
|
+
return false
|
124
|
+
else
|
125
|
+
@positive_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name]}"
|
126
|
+
end
|
127
|
+
|
128
|
+
if !@association[:class].nil? and @association[:class] != metadata.klass
|
129
|
+
@negative_result_message = "#{@positive_result_message} of type #{metadata.klass.inspect}"
|
130
|
+
return false
|
131
|
+
else
|
132
|
+
@positive_result_message = "#{@positive_result_message}#{" of type #{metadata.klass.inspect}" if @association[:class]}"
|
133
|
+
end
|
134
|
+
|
135
|
+
if @association[:inverse_of]
|
136
|
+
if @association[:inverse_of].to_s != metadata.inverse_of.to_s
|
137
|
+
@negative_result_message = "#{@positive_result_message} which is an inverse of #{metadata.inverse_of}"
|
138
|
+
return false
|
139
|
+
else
|
140
|
+
@positive_result_message = "#{@positive_result_message} which is an inverse of #{metadata.inverse_of}"
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
if @association[:order]
|
145
|
+
if @association[:order].to_s != metadata.order.to_s
|
146
|
+
@negative_result_message = "#{@positive_result_message} ordered by #{metadata.order}"
|
147
|
+
return false
|
148
|
+
else
|
149
|
+
@positive_result_message = "#{@positive_result_message} ordered by #{metadata.order}"
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
if @association[:order_operator]
|
154
|
+
if @association[:order_operator] != metadata.order.operator
|
155
|
+
@negative_result_message = "#{@positive_result_message} #{order_way(@association[:order_operator] * -1)}"
|
156
|
+
return false
|
157
|
+
else
|
158
|
+
@positive_result_message = "#{@positive_result_message} #{order_way(@association[:order_operator])}"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
if @association[:dependent]
|
163
|
+
if @association[:dependent].to_s != metadata.dependent.to_s
|
164
|
+
@negative_result_message = "#{@positive_result_message} which specified dependent as #{metadata.dependent}"
|
165
|
+
return false
|
166
|
+
else
|
167
|
+
@positive_result_message = "#{@positive_result_message} which specified dependent as #{metadata.dependent}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
if @association[:autosave]
|
172
|
+
if metadata.autosave != true
|
173
|
+
@negative_result_message = "#{@positive_result_message} which did not set autosave"
|
174
|
+
return false
|
175
|
+
else
|
176
|
+
@positive_result_message = "#{@positive_result_message} which set autosave"
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
if @association[:autobuild]
|
181
|
+
if metadata.autobuilding? != true
|
182
|
+
@negative_result_message = "#{@positive_result_message} which did not set autobuild"
|
183
|
+
return false
|
184
|
+
else
|
185
|
+
@positive_result_message = "#{@positive_result_message} which set autobuild"
|
186
|
+
end
|
187
|
+
end
|
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
|
+
|
225
|
+
if @association[:index]
|
226
|
+
if metadata.index != true
|
227
|
+
@negative_result_message = "#{@positive_result_message} which did not set index"
|
228
|
+
return false
|
229
|
+
else
|
230
|
+
@positive_result_message = "#{@positive_result_message} which set index"
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
if @association[:foreign_key]
|
235
|
+
if metadata.foreign_key != @association[:foreign_key]
|
236
|
+
@negative_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
|
237
|
+
return false
|
238
|
+
else
|
239
|
+
@positive_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
if @association[:counter_cache]
|
244
|
+
if metadata.counter_cached? != true
|
245
|
+
@negative_result_message = "#{@positive_result_message} which did not set counter_cache"
|
246
|
+
return false
|
247
|
+
else
|
248
|
+
@positive_result_message = "#{@positive_result_message} which set counter_cache"
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
return true
|
253
|
+
end
|
254
|
+
|
255
|
+
def failure_message_for_should
|
256
|
+
"Expected #{@actual.inspect} to #{@expectation_message}, got #{@negative_result_message}"
|
257
|
+
end
|
258
|
+
|
259
|
+
def failure_message_for_should_not
|
260
|
+
"Expected #{@actual.inspect} to not #{@expectation_message}, got #{@positive_result_message}"
|
261
|
+
end
|
262
|
+
|
263
|
+
alias :failure_message :failure_message_for_should
|
264
|
+
alias :failure_message_when_negated :failure_message_for_should_not
|
265
|
+
|
266
|
+
def description
|
267
|
+
@expectation_message
|
268
|
+
end
|
269
|
+
|
270
|
+
def type_description(type = nil, passive = true)
|
271
|
+
type ||= @association[:type]
|
272
|
+
case type.name
|
273
|
+
when EMBEDS_ONE.name
|
274
|
+
(passive ? 'embed' : 'embeds') << ' one'
|
275
|
+
when EMBEDS_MANY.name
|
276
|
+
(passive ? 'embed' : 'embeds') << ' many'
|
277
|
+
when EMBEDDED_IN.name
|
278
|
+
(passive ? 'be' : 'is') << ' embedded in'
|
279
|
+
when HAS_ONE.name
|
280
|
+
(passive ? 'reference' : 'references') << ' one'
|
281
|
+
when HAS_MANY.name
|
282
|
+
(passive ? 'reference' : 'references') << ' many'
|
283
|
+
when HAS_AND_BELONGS_TO_MANY.name
|
284
|
+
(passive ? 'reference' : 'references') << ' and referenced in many'
|
285
|
+
when BELONGS_TO.name
|
286
|
+
(passive ? 'be referenced in' : 'referenced in')
|
287
|
+
else
|
288
|
+
raise "Unknown association type '%s'" % type
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
private
|
293
|
+
def order_way(operator)
|
294
|
+
[nil, "ascending", "descending"][operator]
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
def embed_one(association_name)
|
299
|
+
HaveAssociationMatcher.new(association_name, EMBEDS_ONE)
|
300
|
+
end
|
301
|
+
|
302
|
+
def embed_many(association_name)
|
303
|
+
HaveAssociationMatcher.new(association_name, EMBEDS_MANY)
|
304
|
+
end
|
305
|
+
|
306
|
+
def be_embedded_in(association_name)
|
307
|
+
HaveAssociationMatcher.new(association_name, EMBEDDED_IN)
|
308
|
+
end
|
309
|
+
|
310
|
+
def have_one_related(association_name)
|
311
|
+
HaveAssociationMatcher.new(association_name, HAS_ONE)
|
312
|
+
end
|
313
|
+
alias :have_one :have_one_related
|
314
|
+
|
315
|
+
def have_many_related(association_name)
|
316
|
+
HaveAssociationMatcher.new(association_name, HAS_MANY)
|
317
|
+
end
|
318
|
+
alias :have_many :have_many_related
|
319
|
+
|
320
|
+
def have_and_belong_to_many(association_name)
|
321
|
+
HaveAssociationMatcher.new(association_name, HAS_AND_BELONGS_TO_MANY)
|
322
|
+
end
|
323
|
+
|
324
|
+
def belong_to_related(association_name)
|
325
|
+
HaveAssociationMatcher.new(association_name, BELONGS_TO)
|
326
|
+
end
|
327
|
+
alias :belong_to :belong_to_related
|
328
|
+
end
|
329
|
+
end
|
330
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Mongoid
|
2
|
+
module Matchers
|
3
|
+
def be_dynamic_document
|
4
|
+
BeDynamicDocument.new
|
5
|
+
end
|
6
|
+
|
7
|
+
class BeDynamicDocument
|
8
|
+
def matches?(actual)
|
9
|
+
@model = actual.is_a?(Class) ? actual : actual.class
|
10
|
+
@model.included_modules.include?(Mongoid::Attributes::Dynamic)
|
11
|
+
end
|
12
|
+
|
13
|
+
def description
|
14
|
+
'include Mongoid::Attributes::Dynamic'
|
15
|
+
end
|
16
|
+
|
17
|
+
def failure_message
|
18
|
+
"expect #{@model.inspect} class to #{description}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def failure_message_when_negated
|
22
|
+
"expect #{@model.inspect} class to not #{description}"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|