glebtv-mongoid-rspec 1.12.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 (54) 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 +14 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE +20 -0
  9. data/README.md +203 -0
  10. data/Rakefile +18 -0
  11. data/gemfiles/mongoid-3.1.gemfile +5 -0
  12. data/gemfiles/mongoid-4.0.gemfile +5 -0
  13. data/glebtv-mongoid-rspec.gemspec +25 -0
  14. data/lib/glebtv-mongoid-rspec.rb +1 -0
  15. data/lib/matchers/accept_nested_attributes.rb +65 -0
  16. data/lib/matchers/allow_mass_assignment.rb +101 -0
  17. data/lib/matchers/associations.rb +313 -0
  18. data/lib/matchers/collections.rb +9 -0
  19. data/lib/matchers/document.rb +160 -0
  20. data/lib/matchers/indexes.rb +81 -0
  21. data/lib/matchers/validations.rb +78 -0
  22. data/lib/matchers/validations/acceptance_of.rb +9 -0
  23. data/lib/matchers/validations/associated.rb +19 -0
  24. data/lib/matchers/validations/confirmation_of.rb +9 -0
  25. data/lib/matchers/validations/custom_validation_of.rb +47 -0
  26. data/lib/matchers/validations/exclusion_of.rb +49 -0
  27. data/lib/matchers/validations/format_of.rb +71 -0
  28. data/lib/matchers/validations/inclusion_of.rb +49 -0
  29. data/lib/matchers/validations/length_of.rb +147 -0
  30. data/lib/matchers/validations/numericality_of.rb +74 -0
  31. data/lib/matchers/validations/presence_of.rb +9 -0
  32. data/lib/matchers/validations/uniqueness_of.rb +82 -0
  33. data/lib/matchers/validations/with_message.rb +27 -0
  34. data/lib/mongoid-rspec.rb +33 -0
  35. data/lib/mongoid-rspec/version.rb +5 -0
  36. data/spec/models/article.rb +29 -0
  37. data/spec/models/comment.rb +6 -0
  38. data/spec/models/log.rb +4 -0
  39. data/spec/models/movie_article.rb +8 -0
  40. data/spec/models/permalink.rb +5 -0
  41. data/spec/models/person.rb +10 -0
  42. data/spec/models/profile.rb +16 -0
  43. data/spec/models/record.rb +5 -0
  44. data/spec/models/site.rb +9 -0
  45. data/spec/models/user.rb +36 -0
  46. data/spec/spec_helper.rb +34 -0
  47. data/spec/unit/accept_nested_attributes_spec.rb +12 -0
  48. data/spec/unit/associations_spec.rb +42 -0
  49. data/spec/unit/collections_spec.rb +7 -0
  50. data/spec/unit/document_spec.rb +27 -0
  51. data/spec/unit/indexes_spec.rb +13 -0
  52. data/spec/unit/validations_spec.rb +52 -0
  53. data/spec/validators/ssn_validator.rb +16 -0
  54. metadata +163 -0
@@ -0,0 +1,313 @@
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
+ def description
250
+ @expectation_message
251
+ end
252
+
253
+ def type_description(type = nil, passive = true)
254
+ type ||= @association[:type]
255
+ case type.name
256
+ when EMBEDS_ONE.name
257
+ (passive ? 'embed' : 'embeds') << ' one'
258
+ when EMBEDS_MANY.name
259
+ (passive ? 'embed' : 'embeds') << ' many'
260
+ when EMBEDDED_IN.name
261
+ (passive ? 'be' : 'is') << ' embedded in'
262
+ when HAS_ONE.name
263
+ (passive ? 'reference' : 'references') << ' one'
264
+ when HAS_MANY.name
265
+ (passive ? 'reference' : 'references') << ' many'
266
+ when HAS_AND_BELONGS_TO_MANY.name
267
+ (passive ? 'reference' : 'references') << ' and referenced in many'
268
+ when BELONGS_TO.name
269
+ (passive ? 'be referenced in' : 'referenced in')
270
+ else
271
+ raise "Unknown association type '%s'" % type
272
+ end
273
+ end
274
+
275
+ private
276
+ def order_way(operator)
277
+ [nil, "ascending", "descending"][operator]
278
+ end
279
+ end
280
+
281
+ def embed_one(association_name)
282
+ HaveAssociationMatcher.new(association_name, EMBEDS_ONE)
283
+ end
284
+
285
+ def embed_many(association_name)
286
+ HaveAssociationMatcher.new(association_name, EMBEDS_MANY)
287
+ end
288
+
289
+ def be_embedded_in(association_name)
290
+ HaveAssociationMatcher.new(association_name, EMBEDDED_IN)
291
+ end
292
+
293
+ def have_one_related(association_name)
294
+ HaveAssociationMatcher.new(association_name, HAS_ONE)
295
+ end
296
+ alias :have_one :have_one_related
297
+
298
+ def have_many_related(association_name)
299
+ HaveAssociationMatcher.new(association_name, HAS_MANY)
300
+ end
301
+ alias :have_many :have_many_related
302
+
303
+ def have_and_belong_to_many(association_name)
304
+ HaveAssociationMatcher.new(association_name, HAS_AND_BELONGS_TO_MANY)
305
+ end
306
+
307
+ def belong_to_related(association_name)
308
+ HaveAssociationMatcher.new(association_name, BELONGS_TO)
309
+ end
310
+ alias :belong_to :belong_to_related
311
+ end
312
+ end
313
+ 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,160 @@
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
+ def description
74
+ desc = "have #{@attributes.size > 1 ? 'fields' : 'field'} named #{@attributes.collect(&:inspect).to_sentence}"
75
+ desc << " of type #{@type.inspect}" if @type
76
+ desc << " with alias #{@field_alias}" if @field_alias
77
+ desc << " with default value of #{@default.inspect}" if !@default.nil?
78
+ desc
79
+ end
80
+ end
81
+
82
+ def have_field(*args)
83
+ HaveFieldMatcher.new(*args)
84
+ end
85
+ alias_method :have_fields, :have_field
86
+ end
87
+ end
88
+
89
+ RSpec::Matchers.define :have_instance_method do |name|
90
+ match do |klass|
91
+ klass.instance_methods.include?(name.to_sym)
92
+ end
93
+
94
+ description do
95
+ "have instance method #{name.to_s}"
96
+ end
97
+ end
98
+
99
+ RSpec::Matchers.define :be_mongoid_document do
100
+ match do |doc|
101
+ doc.class.included_modules.include?(Mongoid::Document)
102
+ end
103
+
104
+ description do
105
+ "be a Mongoid document"
106
+ end
107
+ end
108
+
109
+ RSpec::Matchers.define :be_versioned_document do
110
+ match do |doc|
111
+ doc.class.included_modules.include?(Mongoid::Versioning)
112
+ end
113
+
114
+ description do
115
+ "be a versioned Mongoid document"
116
+ end
117
+ end
118
+
119
+ RSpec::Matchers.define :be_timestamped_document do
120
+ match do |doc|
121
+ if [*@timestamped_module].any?
122
+ modules = [*@timestamped_module].map{|m| "Mongoid::Timestamps::#{m.to_s.classify}".constantize }
123
+ (modules - doc.class.included_modules).empty?
124
+ else
125
+ doc.class.included_modules.include?(Mongoid::Timestamps) or
126
+ doc.class.included_modules.include?(Mongoid::Timestamps::Created) or
127
+ doc.class.included_modules.include?(Mongoid::Timestamps::Updated)
128
+ end
129
+ end
130
+
131
+ chain :with do |timestamped_module|
132
+ @timestamped_module = timestamped_module
133
+ end
134
+
135
+ description do
136
+ desc = "be a timestamped Mongoid document"
137
+ desc << " with #{@timestamped_module}" if @timestamped_module
138
+ desc
139
+ end
140
+ end
141
+
142
+ RSpec::Matchers.define :be_paranoid_document do
143
+ match do |doc|
144
+ doc.class.included_modules.include?(Mongoid::Paranoia)
145
+ end
146
+
147
+ description do
148
+ "be a paranoid Mongoid document"
149
+ end
150
+ end
151
+
152
+ RSpec::Matchers.define :be_multiparameted_document do
153
+ match do |doc|
154
+ doc.class.included_modules.include?(Mongoid::MultiParameterAttributes)
155
+ end
156
+
157
+ description do
158
+ "be a multiparameted Mongoid document"
159
+ end
160
+ end