mongoid4-rspec 1.11.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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +6 -0
  4. data/.ruby-gemset +1 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +21 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +20 -0
  9. data/README.md +207 -0
  10. data/Rakefile +17 -0
  11. data/lib/matchers/accept_nested_attributes.rb +67 -0
  12. data/lib/matchers/allow_mass_assignment.rb +101 -0
  13. data/lib/matchers/associations.rb +316 -0
  14. data/lib/matchers/collections.rb +9 -0
  15. data/lib/matchers/document.rb +163 -0
  16. data/lib/matchers/indexes.rb +84 -0
  17. data/lib/matchers/validations.rb +81 -0
  18. data/lib/matchers/validations/acceptance_of.rb +9 -0
  19. data/lib/matchers/validations/associated.rb +19 -0
  20. data/lib/matchers/validations/confirmation_of.rb +9 -0
  21. data/lib/matchers/validations/custom_validation_of.rb +47 -0
  22. data/lib/matchers/validations/exclusion_of.rb +49 -0
  23. data/lib/matchers/validations/format_of.rb +71 -0
  24. data/lib/matchers/validations/inclusion_of.rb +49 -0
  25. data/lib/matchers/validations/length_of.rb +147 -0
  26. data/lib/matchers/validations/numericality_of.rb +74 -0
  27. data/lib/matchers/validations/presence_of.rb +9 -0
  28. data/lib/matchers/validations/uniqueness_of.rb +82 -0
  29. data/lib/matchers/validations/with_message.rb +27 -0
  30. data/lib/mongoid-rspec.rb +33 -0
  31. data/lib/mongoid-rspec/version.rb +5 -0
  32. data/mongoid-rspec.gemspec +25 -0
  33. data/mongoid4-rspec.gemspec +25 -0
  34. data/spec/models/article.rb +29 -0
  35. data/spec/models/comment.rb +6 -0
  36. data/spec/models/log.rb +4 -0
  37. data/spec/models/movie_article.rb +8 -0
  38. data/spec/models/permalink.rb +5 -0
  39. data/spec/models/person.rb +10 -0
  40. data/spec/models/profile.rb +16 -0
  41. data/spec/models/record.rb +5 -0
  42. data/spec/models/site.rb +9 -0
  43. data/spec/models/user.rb +36 -0
  44. data/spec/spec_helper.rb +32 -0
  45. data/spec/unit/accept_nested_attributes_spec.rb +12 -0
  46. data/spec/unit/associations_spec.rb +42 -0
  47. data/spec/unit/collections_spec.rb +7 -0
  48. data/spec/unit/document_spec.rb +21 -0
  49. data/spec/unit/indexes_spec.rb +13 -0
  50. data/spec/unit/validations_spec.rb +52 -0
  51. data/spec/validators/ssn_validator.rb +16 -0
  52. metadata +137 -0
@@ -0,0 +1,316 @@
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
+ raise "#{@association[:type].inspect} does not respond to :inverse_of" unless [HAS_MANY, HAS_AND_BELONGS_TO_MANY, BELONGS_TO, EMBEDDED_IN].include?(@association[:type])
32
+ @association[:inverse_of] = association_inverse_name.to_s
33
+ @expectation_message << " which is an inverse of #{@association[:inverse_of].inspect}"
34
+ self
35
+ end
36
+
37
+ def ordered_by(association_field_name)
38
+ raise "#{@association[:type].inspect} does not respond to :order" unless [HAS_MANY, HAS_AND_BELONGS_TO_MANY, EMBEDS_MANY].include?(@association[:type])
39
+ @association[:order] = association_field_name.to_s
40
+ @expectation_message << " ordered by #{@association[:order].inspect}"
41
+
42
+ if association_field_name.is_a? Origin::Key
43
+ @association[:order_operator] = association_field_name.operator
44
+ @expectation_message << " #{order_way(@association[:order_operator])}"
45
+ end
46
+
47
+ self
48
+ end
49
+
50
+ def with_dependent(method_name)
51
+ @association[:dependent] = method_name
52
+ @expectation_message << " which specifies dependent as #{@association[:dependent].to_s}"
53
+ self
54
+ end
55
+
56
+ def with_autosave
57
+ @association[:autosave] = true
58
+ @expectation_message << " which specifies autosave as #{@association[:autosave].to_s}"
59
+ self
60
+ end
61
+
62
+ def with_index
63
+ @association[:index] = true
64
+ @expectation_message << " which specifies index as #{@association[:index].to_s}"
65
+ self
66
+ end
67
+
68
+ def with_autobuild
69
+ @association[:autobuild] = true
70
+ @expectation_message << " which specifies autobuild as #{@association[:autobuild].to_s}"
71
+ self
72
+ end
73
+
74
+ def with_polymorphism
75
+ @association[:polymorphic] = true
76
+ @expectation_message << " which specifies polymorphic as #{@association[:polymorphic].to_s}"
77
+ self
78
+ end
79
+
80
+ def with_cascading_callbacks
81
+ @association[:cascade_callbacks] = true
82
+ @expectation_message << " which specifies cascade_callbacks as #{@association[:cascade_callbacks].to_s}"
83
+ self
84
+ end
85
+
86
+ def cyclic
87
+ @association[:cyclic] = true
88
+ @expectation_message << " which specifies cyclic as #{@association[:cyclic].to_s}"
89
+ self
90
+ end
91
+
92
+ def stored_as(store_as)
93
+ @association[:store_as] = store_as.to_s
94
+ @expectation_message << " which is stored as #{@association[:store_as].inspect}"
95
+ self
96
+ end
97
+
98
+ def with_foreign_key(foreign_key)
99
+ @association[:foreign_key] = foreign_key.to_s
100
+ @expectation_message << " using foreign key #{@association[:foreign_key].inspect}"
101
+ self
102
+ end
103
+
104
+ def matches?(actual)
105
+ @actual = actual.is_a?(Class) ? actual : actual.class
106
+ metadata = @actual.relations[@association[:name]]
107
+
108
+ if metadata.nil?
109
+ @negative_result_message = "no association named #{@association[:name]}"
110
+ return false
111
+ else
112
+ @positive_result_message = "association named #{@association[:name]}"
113
+ end
114
+
115
+ relation = metadata.relation
116
+ if relation != @association[:type]
117
+ @negative_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name]}"
118
+ return false
119
+ else
120
+ @positive_result_message = "#{@actual.inspect} #{type_description(relation, false)} #{@association[:name]}"
121
+ end
122
+
123
+ if !@association[:class].nil? and @association[:class] != metadata.klass
124
+ @negative_result_message = "#{@positive_result_message} of type #{metadata.klass.inspect}"
125
+ return false
126
+ else
127
+ @positive_result_message = "#{@positive_result_message}#{" of type #{metadata.klass.inspect}" if @association[:class]}"
128
+ end
129
+
130
+ if @association[:inverse_of]
131
+ if @association[:inverse_of].to_s != metadata.inverse_of.to_s
132
+ @negative_result_message = "#{@positive_result_message} which is an inverse of #{metadata.inverse_of}"
133
+ return false
134
+ else
135
+ @positive_result_message = "#{@positive_result_message} which is an inverse of #{metadata.inverse_of}"
136
+ end
137
+ end
138
+
139
+ if @association[:order]
140
+ if @association[:order].to_s != metadata.order.to_s
141
+ @negative_result_message = "#{@positive_result_message} ordered by #{metadata.order}"
142
+ return false
143
+ else
144
+ @positive_result_message = "#{@positive_result_message} ordered by #{metadata.order}"
145
+ end
146
+ end
147
+
148
+ if @association[:order_operator]
149
+ if @association[:order_operator] != metadata.order.operator
150
+ @negative_result_message = "#{@positive_result_message} #{order_way(@association[:order_operator] * -1)}"
151
+ return false
152
+ else
153
+ @positive_result_message = "#{@positive_result_message} #{order_way(@association[:order_operator])}"
154
+ end
155
+ end
156
+
157
+ if @association[:dependent]
158
+ if @association[:dependent].to_s != metadata.dependent.to_s
159
+ @negative_result_message = "#{@positive_result_message} which specified dependent as #{metadata.dependent}"
160
+ return false
161
+ else
162
+ @positive_result_message = "#{@positive_result_message} which specified dependent as #{metadata.dependent}"
163
+ end
164
+ end
165
+
166
+ if @association[:autosave]
167
+ if metadata.autosave != true
168
+ @negative_result_message = "#{@positive_result_message} which did not set autosave"
169
+ return false
170
+ else
171
+ @positive_result_message = "#{@positive_result_message} which set autosave"
172
+ end
173
+ end
174
+
175
+ if @association[:autobuild]
176
+ if metadata.autobuilding? != true
177
+ @negative_result_message = "#{@positive_result_message} which did not set autobuild"
178
+ return false
179
+ else
180
+ @positive_result_message = "#{@positive_result_message} which set autobuild"
181
+ end
182
+ end
183
+
184
+ if @association[:polymorphic]
185
+ if metadata.polymorphic? != true
186
+ @negative_result_message = "#{@positive_result_message} which did not set polymorphic"
187
+ return false
188
+ else
189
+ @positive_result_message = "#{@positive_result_message} which set polymorphic"
190
+ end
191
+ end
192
+
193
+ if @association[:cascade_callbacks]
194
+ if metadata.cascading_callbacks? != true
195
+ @negative_result_message = "#{@positive_result_message} which did not set cascade_callbacks"
196
+ return false
197
+ else
198
+ @positive_result_message = "#{@positive_result_message} which set cascade_callbacks"
199
+ end
200
+ end
201
+
202
+ if @association[:cyclic]
203
+ if metadata.cyclic? != true
204
+ @negative_result_message = "#{@positive_result_message} which did not set cyclic"
205
+ return false
206
+ else
207
+ @positive_result_message = "#{@positive_result_message} which set cyclic"
208
+ end
209
+ end
210
+
211
+ if @association[:store_as]
212
+ if metadata.store_as != @association[:store_as]
213
+ @negative_result_message = "#{@positive_result_message} which is stored as #{metadata.store_as}"
214
+ return false
215
+ else
216
+ @positive_result_message = "#{@positive_result_message} which is stored as #{metadata.store_as}"
217
+ end
218
+ end
219
+
220
+ if @association[:index]
221
+ if metadata.index != true
222
+ @negative_result_message = "#{@positive_result_message} which did not set index"
223
+ return false
224
+ else
225
+ @positive_result_message = "#{@positive_result_message} which set index"
226
+ end
227
+ end
228
+
229
+ if @association[:foreign_key]
230
+ if metadata.foreign_key != @association[:foreign_key]
231
+ @negative_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
232
+ return false
233
+ else
234
+ @positive_result_message = "#{@positive_result_message} with foreign key #{metadata.foreign_key.inspect}"
235
+ end
236
+ end
237
+
238
+ return true
239
+ end
240
+
241
+ def failure_message_for_should
242
+ "Expected #{@actual.inspect} to #{@expectation_message}, got #{@negative_result_message}"
243
+ end
244
+
245
+ def failure_message_for_should_not
246
+ "Expected #{@actual.inspect} to not #{@expectation_message}, got #{@positive_result_message}"
247
+ end
248
+
249
+ alias :failure_message :failure_message_for_should
250
+ alias :failure_message_when_negated :failure_message_for_should_not
251
+
252
+ def description
253
+ @expectation_message
254
+ end
255
+
256
+ def type_description(type = nil, passive = true)
257
+ type ||= @association[:type]
258
+ case type.name
259
+ when EMBEDS_ONE.name
260
+ (passive ? 'embed' : 'embeds') << ' one'
261
+ when EMBEDS_MANY.name
262
+ (passive ? 'embed' : 'embeds') << ' many'
263
+ when EMBEDDED_IN.name
264
+ (passive ? 'be' : 'is') << ' embedded in'
265
+ when HAS_ONE.name
266
+ (passive ? 'reference' : 'references') << ' one'
267
+ when HAS_MANY.name
268
+ (passive ? 'reference' : 'references') << ' many'
269
+ when HAS_AND_BELONGS_TO_MANY.name
270
+ (passive ? 'reference' : 'references') << ' and referenced in many'
271
+ when BELONGS_TO.name
272
+ (passive ? 'be referenced in' : 'referenced in')
273
+ else
274
+ raise "Unknown association type '%s'" % type
275
+ end
276
+ end
277
+
278
+ private
279
+ def order_way(operator)
280
+ [nil, "ascending", "descending"][operator]
281
+ end
282
+ end
283
+
284
+ def embed_one(association_name)
285
+ HaveAssociationMatcher.new(association_name, EMBEDS_ONE)
286
+ end
287
+
288
+ def embed_many(association_name)
289
+ HaveAssociationMatcher.new(association_name, EMBEDS_MANY)
290
+ end
291
+
292
+ def be_embedded_in(association_name)
293
+ HaveAssociationMatcher.new(association_name, EMBEDDED_IN)
294
+ end
295
+
296
+ def have_one_related(association_name)
297
+ HaveAssociationMatcher.new(association_name, HAS_ONE)
298
+ end
299
+ alias :have_one :have_one_related
300
+
301
+ def have_many_related(association_name)
302
+ HaveAssociationMatcher.new(association_name, HAS_MANY)
303
+ end
304
+ alias :have_many :have_many_related
305
+
306
+ def have_and_belong_to_many(association_name)
307
+ HaveAssociationMatcher.new(association_name, HAS_AND_BELONGS_TO_MANY)
308
+ end
309
+
310
+ def belong_to_related(association_name)
311
+ HaveAssociationMatcher.new(association_name, BELONGS_TO)
312
+ end
313
+ alias :belong_to :belong_to_related
314
+ end
315
+ end
316
+ end
@@ -0,0 +1,9 @@
1
+ RSpec::Matchers.define :be_stored_in do |collection_name|
2
+ match do |doc|
3
+ doc.class.collection_name == collection_name
4
+ end
5
+
6
+ description do
7
+ "be stored in #{collection_name.to_s}"
8
+ end
9
+ end
@@ -0,0 +1,163 @@
1
+ module Mongoid
2
+ module Matchers
3
+ class HaveFieldMatcher # :nodoc:
4
+ def initialize(*attrs)
5
+ @attributes = attrs.collect(&:to_s)
6
+ end
7
+
8
+ def localized
9
+ @localized = true
10
+ self
11
+ end
12
+
13
+ def of_type(type)
14
+ @type = type
15
+ self
16
+ end
17
+
18
+ def with_alias(field_alias)
19
+ @field_alias = field_alias
20
+ self
21
+ end
22
+
23
+ def with_default_value_of(default)
24
+ @default = default
25
+ self
26
+ end
27
+
28
+ def matches?(klass)
29
+ @klass = klass.is_a?(Class) ? klass : klass.class
30
+ @errors = []
31
+ @attributes.each do |attr|
32
+ if @klass.fields.include?(attr)
33
+ error = ""
34
+ if @type and @klass.fields[attr].type != @type
35
+ error << " of type #{@klass.fields[attr].type}"
36
+ end
37
+
38
+ if !@default.nil?
39
+ if @klass.fields[attr].default_val.nil?
40
+ error << " with default not set"
41
+ elsif @klass.fields[attr].default_val != @default
42
+ error << " with default value of #{@klass.fields[attr].default_val}"
43
+ end
44
+ end
45
+
46
+ if @field_alias and @klass.fields[attr].options[:as] != @field_alias
47
+ error << " with alias #{@klass.fields[attr].options[:as]}"
48
+ end
49
+
50
+ @errors.push("field #{attr.inspect}" << error) unless error.blank?
51
+
52
+ if @localized
53
+ unless @klass.fields[attr].localized?
54
+ @errors.push "is not localized #{attr.inspect}"
55
+ end
56
+ end
57
+
58
+ else
59
+ @errors.push "no field named #{attr.inspect}"
60
+ end
61
+ end
62
+ @errors.empty?
63
+ end
64
+
65
+ def failure_message_for_should
66
+ "Expected #{@klass.inspect} to #{description}, got #{@errors.to_sentence}"
67
+ end
68
+
69
+ def failure_message_for_should_not
70
+ "Expected #{@klass.inspect} to not #{description}, got #{@klass.inspect} to #{description}"
71
+ end
72
+
73
+ alias :failure_message :failure_message_for_should
74
+ alias :failure_message_when_negated :failure_message_for_should_not
75
+
76
+ def description
77
+ desc = "have #{@attributes.size > 1 ? 'fields' : 'field'} named #{@attributes.collect(&:inspect).to_sentence}"
78
+ desc << " of type #{@type.inspect}" if @type
79
+ desc << " with alias #{@field_alias}" if @field_alias
80
+ desc << " with default value of #{@default.inspect}" if !@default.nil?
81
+ desc
82
+ end
83
+ end
84
+
85
+ def have_field(*args)
86
+ HaveFieldMatcher.new(*args)
87
+ end
88
+ alias_method :have_fields, :have_field
89
+ end
90
+ end
91
+
92
+ RSpec::Matchers.define :have_instance_method do |name|
93
+ match do |klass|
94
+ klass.instance_methods.include?(name.to_sym)
95
+ end
96
+
97
+ description do
98
+ "have instance method #{name.to_s}"
99
+ end
100
+ end
101
+
102
+ RSpec::Matchers.define :be_mongoid_document do
103
+ match do |doc|
104
+ doc.class.included_modules.include?(Mongoid::Document)
105
+ end
106
+
107
+ description do
108
+ "be a Mongoid document"
109
+ end
110
+ end
111
+
112
+ RSpec::Matchers.define :be_versioned_document do
113
+ match do |doc|
114
+ doc.class.included_modules.include?(Mongoid::Versioning)
115
+ end
116
+
117
+ description do
118
+ "be a versioned Mongoid document"
119
+ end
120
+ end
121
+
122
+ RSpec::Matchers.define :be_timestamped_document do
123
+ match do |doc|
124
+ if [*@timestamped_module].any?
125
+ modules = [*@timestamped_module].map{|m| "Mongoid::Timestamps::#{m.to_s.classify}".constantize }
126
+ (modules - doc.class.included_modules).empty?
127
+ else
128
+ doc.class.included_modules.include?(Mongoid::Timestamps) or
129
+ doc.class.included_modules.include?(Mongoid::Timestamps::Created) or
130
+ doc.class.included_modules.include?(Mongoid::Timestamps::Updated)
131
+ end
132
+ end
133
+
134
+ chain :with do |timestamped_module|
135
+ @timestamped_module = timestamped_module
136
+ end
137
+
138
+ description do
139
+ desc = "be a timestamped Mongoid document"
140
+ desc << " with #{@timestamped_module}" if @timestamped_module
141
+ desc
142
+ end
143
+ end
144
+
145
+ RSpec::Matchers.define :be_paranoid_document do
146
+ match do |doc|
147
+ doc.class.included_modules.include?(Mongoid::Paranoia)
148
+ end
149
+
150
+ description do
151
+ "be a paranoid Mongoid document"
152
+ end
153
+ end
154
+
155
+ RSpec::Matchers.define :be_multiparameted_document do
156
+ match do |doc|
157
+ doc.class.included_modules.include?(Mongoid::MultiParameterAttributes)
158
+ end
159
+
160
+ description do
161
+ "be a multiparameted Mongoid document"
162
+ end
163
+ end