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.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +20 -0
  3. data/README.md +373 -0
  4. data/Rakefile +17 -0
  5. data/lib/matchers/accept_nested_attributes.rb +67 -0
  6. data/lib/matchers/allow_mass_assignment.rb +102 -0
  7. data/lib/matchers/associations.rb +330 -0
  8. data/lib/matchers/be_dynamic_document.rb +26 -0
  9. data/lib/matchers/be_mongoid_document.rb +26 -0
  10. data/lib/matchers/be_stored_in.rb +50 -0
  11. data/lib/matchers/have_field.rb +90 -0
  12. data/lib/matchers/have_index_for.rb +63 -0
  13. data/lib/matchers/have_timestamps.rb +61 -0
  14. data/lib/matchers/validations.rb +81 -0
  15. data/lib/matchers/validations/acceptance_of.rb +9 -0
  16. data/lib/matchers/validations/associated.rb +19 -0
  17. data/lib/matchers/validations/confirmation_of.rb +22 -0
  18. data/lib/matchers/validations/custom_validation_of.rb +47 -0
  19. data/lib/matchers/validations/exclusion_of.rb +49 -0
  20. data/lib/matchers/validations/format_of.rb +71 -0
  21. data/lib/matchers/validations/inclusion_of.rb +49 -0
  22. data/lib/matchers/validations/length_of.rb +147 -0
  23. data/lib/matchers/validations/numericality_of.rb +90 -0
  24. data/lib/matchers/validations/presence_of.rb +9 -0
  25. data/lib/matchers/validations/uniqueness_of.rb +82 -0
  26. data/lib/matchers/validations/with_message.rb +27 -0
  27. data/lib/mongoid-rspec.rb +1 -0
  28. data/lib/mongoid/rspec.rb +40 -0
  29. data/lib/mongoid/rspec/version.rb +5 -0
  30. data/spec/models/article.rb +29 -0
  31. data/spec/models/comment.rb +6 -0
  32. data/spec/models/log.rb +9 -0
  33. data/spec/models/movie_article.rb +8 -0
  34. data/spec/models/permalink.rb +5 -0
  35. data/spec/models/person.rb +10 -0
  36. data/spec/models/profile.rb +16 -0
  37. data/spec/models/record.rb +5 -0
  38. data/spec/models/site.rb +9 -0
  39. data/spec/models/user.rb +37 -0
  40. data/spec/spec_helper.rb +35 -0
  41. data/spec/unit/accept_nested_attributes_spec.rb +12 -0
  42. data/spec/unit/associations_spec.rb +42 -0
  43. data/spec/unit/be_dynamic_document_spec.rb +22 -0
  44. data/spec/unit/be_mongoid_document_spec.rb +25 -0
  45. data/spec/unit/be_stored_in.rb +54 -0
  46. data/spec/unit/document_spec.rb +16 -0
  47. data/spec/unit/have_index_for_spec.rb +46 -0
  48. data/spec/unit/have_timestamps_spec.rb +71 -0
  49. data/spec/unit/validations_spec.rb +54 -0
  50. data/spec/validators/ssn_validator.rb +16 -0
  51. 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