ratnikov-shoulda 2.0.6.3 → 2.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.
Files changed (60) hide show
  1. data/README.rdoc +3 -2
  2. data/Rakefile +1 -1
  3. data/lib/shoulda/active_record/assertions.rb +10 -31
  4. data/lib/shoulda/active_record/helpers.rb +40 -0
  5. data/lib/shoulda/active_record/macros.rb +171 -325
  6. data/lib/shoulda/active_record/matchers/allow_mass_assignment_of_matcher.rb +83 -0
  7. data/lib/shoulda/active_record/matchers/allow_value_matcher.rb +102 -0
  8. data/lib/shoulda/active_record/matchers/association_matcher.rb +226 -0
  9. data/lib/shoulda/active_record/matchers/ensure_inclusion_of_matcher.rb +87 -0
  10. data/lib/shoulda/active_record/matchers/ensure_length_of_matcher.rb +141 -0
  11. data/lib/shoulda/active_record/matchers/have_db_column_matcher.rb +169 -0
  12. data/lib/shoulda/active_record/matchers/have_index_matcher.rb +105 -0
  13. data/lib/shoulda/active_record/matchers/have_named_scope_matcher.rb +125 -0
  14. data/lib/shoulda/active_record/matchers/have_readonly_attribute_matcher.rb +59 -0
  15. data/lib/shoulda/active_record/matchers/validate_acceptance_of_matcher.rb +41 -0
  16. data/lib/shoulda/active_record/matchers/validate_numericality_of_matcher.rb +39 -0
  17. data/lib/shoulda/active_record/matchers/validate_presence_of_matcher.rb +60 -0
  18. data/lib/shoulda/active_record/matchers/validate_uniqueness_of_matcher.rb +148 -0
  19. data/lib/shoulda/active_record/matchers/validation_matcher.rb +56 -0
  20. data/lib/shoulda/active_record/matchers.rb +42 -0
  21. data/lib/shoulda/active_record.rb +4 -0
  22. data/lib/shoulda/assertions.rb +12 -0
  23. data/lib/shoulda/autoload_macros.rb +46 -0
  24. data/lib/shoulda/rails.rb +1 -8
  25. data/lib/shoulda/rspec.rb +5 -0
  26. data/lib/shoulda/tasks/list_tests.rake +6 -1
  27. data/lib/shoulda/test_unit.rb +19 -0
  28. data/lib/shoulda.rb +5 -17
  29. data/rails/init.rb +1 -1
  30. data/test/README +2 -2
  31. data/test/fail_macros.rb +2 -2
  32. data/test/matchers/allow_mass_assignment_of_matcher_test.rb +68 -0
  33. data/test/matchers/allow_value_matcher_test.rb +41 -0
  34. data/test/matchers/association_matcher_test.rb +258 -0
  35. data/test/matchers/ensure_inclusion_of_matcher_test.rb +80 -0
  36. data/test/matchers/ensure_length_of_matcher_test.rb +158 -0
  37. data/test/matchers/have_db_column_matcher_test.rb +169 -0
  38. data/test/matchers/have_index_matcher_test.rb +74 -0
  39. data/test/matchers/have_named_scope_matcher_test.rb +65 -0
  40. data/test/matchers/have_readonly_attributes_matcher_test.rb +29 -0
  41. data/test/matchers/validate_acceptance_of_matcher_test.rb +44 -0
  42. data/test/matchers/validate_numericality_of_matcher_test.rb +52 -0
  43. data/test/matchers/validate_presence_of_matcher_test.rb +86 -0
  44. data/test/matchers/validate_uniqueness_of_matcher_test.rb +141 -0
  45. data/test/model_builder.rb +61 -0
  46. data/test/other/autoload_macro_test.rb +18 -0
  47. data/test/other/helpers_test.rb +58 -0
  48. data/test/rails_root/config/database.yml +1 -1
  49. data/test/rails_root/config/environments/{sqlite3.rb → test.rb} +0 -0
  50. data/test/rails_root/test/shoulda_macros/custom_macro.rb +6 -0
  51. data/test/rails_root/vendor/gems/gem_with_macro-0.0.1/shoulda_macros/gem_macro.rb +6 -0
  52. data/test/rails_root/vendor/plugins/plugin_with_macro/shoulda_macros/plugin_macro.rb +6 -0
  53. data/test/test_helper.rb +3 -1
  54. data/test/unit/address_test.rb +1 -1
  55. data/test/unit/dog_test.rb +1 -1
  56. data/test/unit/post_test.rb +4 -4
  57. data/test/unit/product_test.rb +2 -2
  58. data/test/unit/tag_test.rb +2 -1
  59. data/test/unit/user_test.rb +8 -7
  60. metadata +49 -3
@@ -1,32 +1,15 @@
1
1
  module Shoulda # :nodoc:
2
2
  module ActiveRecord # :nodoc:
3
- module MacroHelpers # :nodoc:
4
- # Helper method that determines the default error message used by Active
5
- # Record. Works for both existing Rails 2.1 and Rails 2.2 with the newly
6
- # introduced I18n module used for localization.
7
- #
8
- # default_error_message(:blank)
9
- # default_error_message(:too_short, :count => 5)
10
- # default_error_message(:too_long, :count => 60)
11
- def default_error_message(key, values = {})
12
- if Object.const_defined?(:I18n) # Rails >= 2.2
13
- I18n.translate("activerecord.errors.messages.#{key}", values)
14
- else # Rails <= 2.1.x
15
- ::ActiveRecord::Errors.default_error_messages[key] % values[:count]
16
- end
17
- end
18
- end
19
-
20
3
  # = Macro test helpers for your active record models
21
4
  #
22
5
  # These helpers will test most of the validations and associations for your ActiveRecord models.
23
6
  #
24
7
  # class UserTest < Test::Unit::TestCase
25
- # should_require_attributes :name, :phone_number
8
+ # should_validate_presence_of :name, :phone_number
26
9
  # should_not_allow_values_for :phone_number, "abcd", "1234"
27
10
  # should_allow_values_for :phone_number, "(123) 456-7890"
28
11
  #
29
- # should_protect_attributes :password
12
+ # should_not_allow_mass_assignment_of :password
30
13
  #
31
14
  # should_have_one :profile
32
15
  # should_have_many :dogs
@@ -37,7 +20,8 @@ module Shoulda # :nodoc:
37
20
  # For all of these helpers, the last parameter may be a hash of options.
38
21
  #
39
22
  module Macros
40
- include MacroHelpers
23
+ include Helpers
24
+ include Matchers
41
25
 
42
26
  # <b>DEPRECATED:</b> Use <tt>fixtures :all</tt> instead
43
27
  #
@@ -58,24 +42,26 @@ module Shoulda # :nodoc:
58
42
  # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.blank')</tt>
59
43
  #
60
44
  # Example:
61
- # should_require_attributes :name, :phone_number
45
+ # should_validate_presence_of :name, :phone_number
62
46
  #
63
- def should_require_attributes(*attributes)
47
+ def should_validate_presence_of(*attributes)
64
48
  message = get_options!(attributes, :message)
65
- message ||= default_error_message(:blank)
66
49
  klass = model_class
67
50
 
68
51
  attributes.each do |attribute|
69
- should "require #{attribute} to be set" do
70
- reflection = klass.reflect_on_association(attribute)
71
- if reflection && [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
72
- assert_bad_value(klass, attribute, [], message)
73
- else
74
- assert_bad_value(klass, attribute, nil, message)
75
- end
52
+ matcher = validate_presence_of(attribute).with_message(message)
53
+ should matcher.description do
54
+ assert_accepts(matcher, get_instance_of(klass))
76
55
  end
77
56
  end
78
57
  end
58
+
59
+ # Deprecated. See should_validate_presence_of
60
+ def should_require_attributes(*attributes)
61
+ warn "[DEPRECATION] should_require_attributes is deprecated. " <<
62
+ "Use should_validate_presence_of instead."
63
+ should_validate_presence_of(*attributes)
64
+ end
79
65
 
80
66
  # Ensures that the model cannot be saved if one of the attributes listed is not unique.
81
67
  # Requires an existing record
@@ -89,75 +75,75 @@ module Shoulda # :nodoc:
89
75
  # exact match. Ignored by non-text attributes. Default = <tt>true</tt>
90
76
  #
91
77
  # Examples:
92
- # should_require_unique_attributes :keyword, :username
93
- # should_require_unique_attributes :name, :message => "O NOES! SOMEONE STOELED YER NAME!"
94
- # should_require_unique_attributes :email, :scoped_to => :name
95
- # should_require_unique_attributes :address, :scoped_to => [:first_name, :last_name]
96
- # should_require_unique_attributes :email, :case_sensitive => false
78
+ # should_validate_uniqueness_of :keyword, :username
79
+ # should_validate_uniqueness_of :name, :message => "O NOES! SOMEONE STOELED YER NAME!"
80
+ # should_validate_uniqueness_of :email, :scoped_to => :name
81
+ # should_validate_uniqueness_of :address, :scoped_to => [:first_name, :last_name]
82
+ # should_validate_uniqueness_of :email, :case_sensitive => false
97
83
  #
98
- def should_require_unique_attributes(*attributes)
84
+ def should_validate_uniqueness_of(*attributes)
99
85
  message, scope, case_sensitive = get_options!(attributes, :message, :scoped_to, :case_sensitive)
100
86
  scope = [*scope].compact
101
- message ||= default_error_message(:taken)
102
87
  case_sensitive = true if case_sensitive.nil?
103
88
 
104
89
  klass = model_class
90
+
105
91
  attributes.each do |attribute|
106
- attribute = attribute.to_sym
107
- should "require#{' case insensitive' unless case_sensitive} unique value for #{attribute}#{" scoped to #{scope.join(', ')}" unless scope.blank?}" do
108
- assert existing = klass.find(:first), "Can't find first #{klass}"
109
- object = klass.new
110
- existing_value = existing.send(attribute)
111
- existing_value.swapcase! if existing_value.respond_to?(:swapcase!) unless case_sensitive
112
-
113
- if !scope.blank?
114
- scope.each do |s|
115
- assert_respond_to object, :"#{s}=", "#{klass.name} doesn't seem to have a #{s} attribute."
116
- object.send("#{s}=", existing.send(s))
117
- end
118
- end
119
-
120
- assert_bad_value(object, attribute, existing_value, message)
121
-
122
- # Now test that the object is valid when changing the scoped attribute
123
- # TODO: There is a chance that we could change the scoped field
124
- # to a value that's already taken. An alternative implementation
125
- # could actually find all values for scope and create a unique
126
- # one.
127
- if !scope.blank?
128
- scope.each do |s|
129
- # Assume the scope is a foreign key if the field is nil
130
- object.send("#{s}=", existing.send(s).nil? ? 1 : existing.send(s).next)
131
- assert_good_value(object, attribute, existing_value, message)
132
- end
133
- end
92
+ matcher = validate_uniqueness_of(attribute).
93
+ with_message(message).scoped_to(scope)
94
+ matcher = matcher.case_insensitive unless case_sensitive
95
+ should matcher.description do
96
+ assert_accepts(matcher, get_instance_of(klass))
97
+ end
98
+ end
99
+ end
100
+
101
+ # Deprecated. See should_validate_uniqueness_of
102
+ def should_require_unique_attributes(*attributes)
103
+ warn "[DEPRECATION] should_require_unique_attributes is deprecated. " <<
104
+ "Use should_validate_uniqueness_of instead."
105
+ should_validate_uniqueness_of(*attributes)
106
+ end
107
+
108
+ # Ensures that the attribute can be set on mass update.
109
+ #
110
+ # should_allow_mass_assignment_of :first_name, :last_name
111
+ #
112
+ def should_allow_mass_assignment_of(*attributes)
113
+ get_options!(attributes)
114
+ klass = model_class
115
+
116
+ attributes.each do |attribute|
117
+ matcher = allow_mass_assignment_of(attribute)
118
+ should matcher.description do
119
+ assert_accepts matcher, klass.new
134
120
  end
135
121
  end
136
122
  end
137
123
 
138
124
  # Ensures that the attribute cannot be set on mass update.
139
125
  #
140
- # should_protect_attributes :password, :admin_flag
126
+ # should_not_allow_mass_assignment_of :password, :admin_flag
141
127
  #
142
- def should_protect_attributes(*attributes)
128
+ def should_not_allow_mass_assignment_of(*attributes)
143
129
  get_options!(attributes)
144
130
  klass = model_class
145
131
 
146
132
  attributes.each do |attribute|
147
- attribute = attribute.to_sym
148
- should "protect #{attribute} from mass updates" do
149
- protected = klass.protected_attributes || []
150
- accessible = klass.accessible_attributes || []
151
-
152
- assert protected.include?(attribute.to_s) ||
153
- (!accessible.empty? && !accessible.include?(attribute.to_s)),
154
- (accessible.empty? ?
155
- "#{klass} is protecting #{protected.to_a.to_sentence}, but not #{attribute}." :
156
- "#{klass} has made #{attribute} accessible")
133
+ matcher = allow_mass_assignment_of(attribute)
134
+ should "not #{matcher.description}" do
135
+ assert_rejects matcher, klass.new
157
136
  end
158
137
  end
159
138
  end
160
139
 
140
+ # Deprecated. See should_not_allow_mass_assignment_of
141
+ def should_protect_attributes(*attributes)
142
+ warn "[DEPRECATION] should_protect_attributes is deprecated. " <<
143
+ "Use should_not_allow_mass_assignment_of instead."
144
+ should_not_allow_mass_assignment_of(*attributes)
145
+ end
146
+
161
147
  # Ensures that the attribute cannot be changed once the record has been created.
162
148
  #
163
149
  # should_have_readonly_attributes :password, :admin_flag
@@ -167,14 +153,9 @@ module Shoulda # :nodoc:
167
153
  klass = model_class
168
154
 
169
155
  attributes.each do |attribute|
170
- attribute = attribute.to_sym
171
- should "make #{attribute} read-only" do
172
- readonly = klass.readonly_attributes || []
173
-
174
- assert readonly.include?(attribute.to_s),
175
- (readonly.empty? ?
176
- "#{klass} attribute #{attribute} is not read-only" :
177
- "#{klass} is making #{readonly.to_a.to_sentence} read-only, but not #{attribute}.")
156
+ matcher = have_readonly_attribute(attribute)
157
+ should matcher.description do
158
+ assert_accepts matcher, klass.new
178
159
  end
179
160
  end
180
161
  end
@@ -194,11 +175,11 @@ module Shoulda # :nodoc:
194
175
  #
195
176
  def should_not_allow_values_for(attribute, *bad_values)
196
177
  message = get_options!(bad_values, :message)
197
- message ||= default_error_message(:invalid)
198
178
  klass = model_class
199
- bad_values.each do |v|
200
- should "not allow #{attribute} to be set to #{v.inspect}" do
201
- assert_bad_value(klass, attribute, v, message)
179
+ bad_values.each do |value|
180
+ matcher = allow_value(value).for(attribute).with_message(message)
181
+ should "not #{matcher.description}" do
182
+ assert_rejects matcher, get_instance_of(klass)
202
183
  end
203
184
  end
204
185
  end
@@ -215,9 +196,11 @@ module Shoulda # :nodoc:
215
196
  def should_allow_values_for(attribute, *good_values)
216
197
  get_options!(good_values)
217
198
  klass = model_class
218
- good_values.each do |v|
219
- should "allow #{attribute} to be set to #{v.inspect}" do
220
- assert_good_value(klass, attribute, v)
199
+ klass = model_class
200
+ good_values.each do |value|
201
+ matcher = allow_value(value).for(attribute)
202
+ should matcher.description do
203
+ assert_accepts matcher, get_instance_of(klass)
221
204
  end
222
205
  end
223
206
  end
@@ -238,39 +221,19 @@ module Shoulda # :nodoc:
238
221
  # should_ensure_length_in_range :password, (6..20)
239
222
  #
240
223
  def should_ensure_length_in_range(attribute, range, opts = {})
241
- short_message, long_message = get_options!([opts], :short_message, :long_message)
242
- short_message ||= default_error_message(:too_short, :count => range.first)
243
- long_message ||= default_error_message(:too_long, :count => range.last)
244
-
224
+ short_message, long_message = get_options!([opts],
225
+ :short_message,
226
+ :long_message)
245
227
  klass = model_class
246
- min_length = range.first
247
- max_length = range.last
248
- same_length = (min_length == max_length)
249
-
250
- if min_length > 0
251
- should "not allow #{attribute} to be less than #{min_length} chars long" do
252
- min_value = "x" * (min_length - 1)
253
- assert_bad_value(klass, attribute, min_value, short_message)
254
- end
255
- end
256
228
 
257
- if min_length > 0
258
- should "allow #{attribute} to be exactly #{min_length} chars long" do
259
- min_value = "x" * min_length
260
- assert_good_value(klass, attribute, min_value, short_message)
261
- end
262
- end
229
+ matcher = ensure_length_of(attribute).
230
+ is_at_least(range.first).
231
+ with_short_message(short_message).
232
+ is_at_most(range.last).
233
+ with_long_message(long_message)
263
234
 
264
- should "not allow #{attribute} to be more than #{max_length} chars long" do
265
- max_value = "x" * (max_length + 1)
266
- assert_bad_value(klass, attribute, max_value, long_message)
267
- end
268
-
269
- unless same_length
270
- should "allow #{attribute} to be exactly #{max_length} chars long" do
271
- max_value = "x" * max_length
272
- assert_good_value(klass, attribute, max_value, long_message)
273
- end
235
+ should matcher.description do
236
+ assert_accepts matcher, get_instance_of(klass)
274
237
  end
275
238
  end
276
239
 
@@ -289,19 +252,14 @@ module Shoulda # :nodoc:
289
252
  #
290
253
  def should_ensure_length_at_least(attribute, min_length, opts = {})
291
254
  short_message = get_options!([opts], :short_message)
292
- short_message ||= default_error_message(:too_short, :count => min_length)
293
-
294
255
  klass = model_class
295
256
 
296
- if min_length > 0
297
- min_value = "x" * (min_length - 1)
298
- should "not allow #{attribute} to be less than #{min_length} chars long" do
299
- assert_bad_value(klass, attribute, min_value, short_message)
300
- end
301
- end
302
- should "allow #{attribute} to be at least #{min_length} chars long" do
303
- valid_value = "x" * (min_length)
304
- assert_good_value(klass, attribute, valid_value, short_message)
257
+ matcher = ensure_length_of(attribute).
258
+ is_at_least(min_length).
259
+ with_short_message(short_message)
260
+
261
+ should matcher.description do
262
+ assert_accepts matcher, get_instance_of(klass)
305
263
  end
306
264
  end
307
265
 
@@ -320,23 +278,13 @@ module Shoulda # :nodoc:
320
278
  #
321
279
  def should_ensure_length_is(attribute, length, opts = {})
322
280
  message = get_options!([opts], :message)
323
- message ||= default_error_message(:wrong_length, :count => length)
281
+ klass = model_class
282
+ matcher = ensure_length_of(attribute).
283
+ is_equal_to(length).
284
+ with_message(message)
324
285
 
325
- klass = model_class
326
-
327
- should "not allow #{attribute} to be less than #{length} chars long" do
328
- min_value = "x" * (length - 1)
329
- assert_bad_value(klass, attribute, min_value, message)
330
- end
331
-
332
- should "not allow #{attribute} to be greater than #{length} chars long" do
333
- max_value = "x" * (length + 1)
334
- assert_bad_value(klass, attribute, max_value, message)
335
- end
336
-
337
- should "allow #{attribute} to be #{length} chars long" do
338
- valid_value = "x" * (length)
339
- assert_good_value(klass, attribute, valid_value, message)
286
+ should matcher.description do
287
+ assert_accepts matcher, get_instance_of(klass)
340
288
  end
341
289
  end
342
290
 
@@ -356,32 +304,15 @@ module Shoulda # :nodoc:
356
304
  # should_ensure_value_in_range :age, (0..100)
357
305
  #
358
306
  def should_ensure_value_in_range(attribute, range, opts = {})
359
- low_message, high_message = get_options!([opts], :low_message, :high_message)
360
- low_message ||= default_error_message(:inclusion)
361
- high_message ||= default_error_message(:inclusion)
307
+ message = get_options!([opts], :message)
308
+ message ||= default_error_message(:inclusion)
362
309
 
363
310
  klass = model_class
364
- min = range.first
365
- max = range.last
366
-
367
- should "not allow #{attribute} to be less than #{min}" do
368
- v = min - 1
369
- assert_bad_value(klass, attribute, v, low_message)
370
- end
371
-
372
- should "allow #{attribute} to be #{min}" do
373
- v = min
374
- assert_good_value(klass, attribute, v, low_message)
375
- end
376
-
377
- should "not allow #{attribute} to be more than #{max}" do
378
- v = max + 1
379
- assert_bad_value(klass, attribute, v, high_message)
380
- end
381
-
382
- should "allow #{attribute} to be #{max}" do
383
- v = max
384
- assert_good_value(klass, attribute, v, high_message)
311
+ matcher = ensure_inclusion_of(attribute).
312
+ in_range(range).
313
+ with_message(message)
314
+ should matcher.description do
315
+ assert_accepts matcher, get_instance_of(klass)
385
316
  end
386
317
  end
387
318
 
@@ -396,20 +327,27 @@ module Shoulda # :nodoc:
396
327
  # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.not_a_number')</tt>
397
328
  #
398
329
  # Example:
399
- # should_only_allow_numeric_values_for :age
330
+ # should_validate_numericality_of :age
400
331
  #
401
- def should_only_allow_numeric_values_for(*attributes)
332
+ def should_validate_numericality_of(*attributes)
402
333
  message = get_options!(attributes, :message)
403
- message ||= default_error_message(:not_a_number)
404
334
  klass = model_class
405
335
  attributes.each do |attribute|
406
- attribute = attribute.to_sym
407
- should "only allow numeric values for #{attribute}" do
408
- assert_bad_value(klass, attribute, "abcd", message)
336
+ matcher = validate_numericality_of(attribute).
337
+ with_message(message)
338
+ should matcher.description do
339
+ assert_accepts matcher, get_instance_of(klass)
409
340
  end
410
341
  end
411
342
  end
412
343
 
344
+ # Deprecated. See should_validate_uniqueness_of
345
+ def should_only_allow_numeric_values_for(*attributes)
346
+ warn "[DEPRECATION] should_only_allow_numeric_values_for is " <<
347
+ "deprecated. Use should_validate_numericality_of instead."
348
+ should_validate_numericality_of(*attributes)
349
+ end
350
+
413
351
  # Ensures that the has_many relationship exists. Will also test that the
414
352
  # associated table has the required columns. Works with polymorphic
415
353
  # associations.
@@ -427,42 +365,9 @@ module Shoulda # :nodoc:
427
365
  through, dependent = get_options!(associations, :through, :dependent)
428
366
  klass = model_class
429
367
  associations.each do |association|
430
- name = "have many #{association}"
431
- name += " through #{through}" if through
432
- name += " dependent => #{dependent}" if dependent
433
- should name do
434
- reflection = klass.reflect_on_association(association)
435
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
436
- assert_equal :has_many, reflection.macro
437
-
438
- if through
439
- through_reflection = klass.reflect_on_association(through)
440
- assert through_reflection, "#{klass.name} does not have any relationship to #{through}"
441
- assert_equal(through, reflection.options[:through])
442
- end
443
-
444
- if dependent
445
- assert_equal dependent.to_s,
446
- reflection.options[:dependent].to_s,
447
- "#{association} should have #{dependent} dependency"
448
- end
449
-
450
- # Check for the existence of the foreign key on the other table
451
- unless reflection.options[:through]
452
- if reflection.options[:foreign_key]
453
- fk = reflection.options[:foreign_key]
454
- elsif reflection.options[:as]
455
- fk = reflection.options[:as].to_s.foreign_key
456
- else
457
- fk = reflection.primary_key_name
458
- end
459
-
460
- associated_klass_name = (reflection.options[:class_name] || association.to_s.classify)
461
- associated_klass = associated_klass_name.constantize
462
-
463
- assert associated_klass.column_names.include?(fk.to_s),
464
- "#{associated_klass.name} does not have a #{fk} foreign key."
465
- end
368
+ matcher = have_many(association).through(through).dependent(dependent)
369
+ should matcher.description do
370
+ assert_accepts(matcher, klass.new)
466
371
  end
467
372
  end
468
373
  end
@@ -481,33 +386,9 @@ module Shoulda # :nodoc:
481
386
  dependent = get_options!(associations, :dependent)
482
387
  klass = model_class
483
388
  associations.each do |association|
484
- name = "have one #{association}"
485
- name += " dependent => #{dependent}" if dependent
486
- should name do
487
- reflection = klass.reflect_on_association(association)
488
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
489
- assert_equal :has_one, reflection.macro
490
-
491
- associated_klass = (reflection.options[:class_name] || association.to_s.camelize).constantize
492
-
493
- if reflection.options[:foreign_key]
494
- fk = reflection.options[:foreign_key]
495
- elsif reflection.options[:as]
496
- fk = reflection.options[:as].to_s.foreign_key
497
- fk_type = fk.gsub(/_id$/, '_type')
498
- assert associated_klass.column_names.include?(fk_type),
499
- "#{associated_klass.name} does not have a #{fk_type} column."
500
- else
501
- fk = klass.name.foreign_key
502
- end
503
- assert associated_klass.column_names.include?(fk.to_s),
504
- "#{associated_klass.name} does not have a #{fk} foreign key."
505
-
506
- if dependent
507
- assert_equal dependent.to_s,
508
- reflection.options[:dependent].to_s,
509
- "#{association} should have #{dependent} dependency"
510
- end
389
+ matcher = have_one(association).dependent(dependent)
390
+ should matcher.description do
391
+ assert_accepts(matcher, klass.new)
511
392
  end
512
393
  end
513
394
  end
@@ -522,12 +403,9 @@ module Shoulda # :nodoc:
522
403
  klass = model_class
523
404
 
524
405
  associations.each do |association|
525
- should "should have and belong to many #{association}" do
526
- reflection = klass.reflect_on_association(association)
527
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
528
- assert_equal :has_and_belongs_to_many, reflection.macro
529
- table = reflection.options[:join_table]
530
- assert ::ActiveRecord::Base.connection.tables.include?(table.to_s), "table #{table} doesn't exist"
406
+ matcher = have_and_belong_to_many(association)
407
+ should matcher.description do
408
+ assert_accepts(matcher, klass.new)
531
409
  end
532
410
  end
533
411
  end
@@ -540,22 +418,9 @@ module Shoulda # :nodoc:
540
418
  dependent = get_options!(associations, :dependent)
541
419
  klass = model_class
542
420
  associations.each do |association|
543
- should "belong_to #{association}" do
544
- reflection = klass.reflect_on_association(association)
545
- assert reflection, "#{klass.name} does not have any relationship to #{association}"
546
- assert_equal :belongs_to, reflection.macro
547
-
548
- unless reflection.options[:polymorphic]
549
- associated_klass = (reflection.options[:class_name] || association.to_s.camelize).constantize
550
- fk = reflection.options[:foreign_key] || reflection.primary_key_name
551
- assert klass.column_names.include?(fk.to_s), "#{klass.name} does not have a #{fk} foreign key."
552
- end
553
-
554
- if dependent
555
- assert_equal dependent.to_s,
556
- reflection.options[:dependent].to_s,
557
- "#{association} should have #{dependent} dependency"
558
- end
421
+ matcher = belong_to(association).dependent(dependent)
422
+ should matcher.description do
423
+ assert_accepts(matcher, klass.new)
559
424
  end
560
425
  end
561
426
  end
@@ -589,41 +454,36 @@ module Shoulda # :nodoc:
589
454
  end
590
455
 
591
456
  # Ensure that the given columns are defined on the models backing SQL table.
457
+ # Also aliased to should_have_index for readability.
458
+ # Takes the same options available in migrations:
459
+ # :type, :precision, :limit, :default, :null, and :scale
460
+ #
461
+ # Examples:
592
462
  #
593
463
  # should_have_db_columns :id, :email, :name, :created_at
594
464
  #
465
+ # should_have_db_column :email, :type => "string", :limit => 255
466
+ # should_have_db_column :salary, :decimal, :precision => 15, :scale => 2
467
+ # should_have_db_column :admin, :default => false, :null => false
468
+ #
595
469
  def should_have_db_columns(*columns)
596
- column_type = get_options!(columns, :type)
470
+ column_type, precision, limit, default, null, scale, sql_type =
471
+ get_options!(columns, :type, :precision, :limit,
472
+ :default, :null, :scale, :sql_type)
597
473
  klass = model_class
598
474
  columns.each do |name|
599
- test_name = "have column #{name}"
600
- test_name += " of type #{column_type}" if column_type
601
- should test_name do
602
- column = klass.columns.detect {|c| c.name == name.to_s }
603
- assert column, "#{klass.name} does not have column #{name}"
604
- end
605
- end
606
- end
607
-
608
- # Ensure that the given column is defined on the models backing SQL table. The options are the same as
609
- # the instance variables defined on the column definition: :precision, :limit, :default, :null,
610
- # :primary, :type, :scale, and :sql_type.
611
- #
612
- # should_have_db_column :email, :type => "string", :default => nil, :precision => nil, :limit => 255,
613
- # :null => true, :primary => false, :scale => nil, :sql_type => 'varchar(255)'
614
- #
615
- def should_have_db_column(name, opts = {})
616
- klass = model_class
617
- test_name = "have column named :#{name}"
618
- test_name += " with options " + opts.inspect unless opts.empty?
619
- should test_name do
620
- column = klass.columns.detect {|c| c.name == name.to_s }
621
- assert column, "#{klass.name} does not have column #{name}"
622
- opts.each do |k, v|
623
- assert_equal column.instance_variable_get("@#{k}").to_s, v.to_s, ":#{name} column on table for #{klass} does not match option :#{k}"
475
+ matcher = have_db_column(name).
476
+ of_type(column_type).
477
+ with_options(:precision => precision, :limit => limit,
478
+ :default => default, :null => null,
479
+ :scale => scale, :sql_type => sql_type)
480
+ should matcher.description do
481
+ assert_accepts(matcher, klass.new)
624
482
  end
625
483
  end
626
484
  end
485
+
486
+ alias_method :should_have_db_column, :should_have_db_columns
627
487
 
628
488
  # Ensures that there are DB indices on the given columns or tuples of columns.
629
489
  # Also aliased to should_have_index for readability
@@ -643,19 +503,12 @@ module Shoulda # :nodoc:
643
503
  #
644
504
  def should_have_indices(*columns)
645
505
  unique = get_options!(columns, :unique)
646
- table = model_class.table_name
647
- indices = ::ActiveRecord::Base.connection.indexes(table)
648
- index_types = { true => "unique", false => "non-unique" }
649
- index_type = index_types[unique] || "an"
650
-
506
+ klass = model_class
507
+
651
508
  columns.each do |column|
652
- should "have #{index_type} index on #{table} for #{column.inspect}" do
653
- columns = [column].flatten.map(&:to_s)
654
- index = indices.detect {|ind| ind.columns == columns }
655
- assert index, "#{table} does not have an index for #{column.inspect}"
656
- if [true, false].include?(unique)
657
- assert_equal unique, index.unique, "Expected #{index_type} index but was #{index_types[index.unique]}."
658
- end
509
+ matcher = have_index(column).unique(unique)
510
+ should matcher.description do
511
+ assert_accepts(matcher, klass.new)
659
512
  end
660
513
  end
661
514
  end
@@ -673,20 +526,27 @@ module Shoulda # :nodoc:
673
526
  # Regexp or string. Default = <tt>I18n.translate('activerecord.errors.messages.accepted')</tt>
674
527
  #
675
528
  # Example:
676
- # should_require_acceptance_of :eula
529
+ # should_validate_acceptance_of :eula
677
530
  #
678
- def should_require_acceptance_of(*attributes)
531
+ def should_validate_acceptance_of(*attributes)
679
532
  message = get_options!(attributes, :message)
680
- message ||= default_error_message(:accepted)
681
533
  klass = model_class
682
534
 
683
535
  attributes.each do |attribute|
684
- should "require #{attribute} to be accepted" do
685
- assert_bad_value(klass, attribute, false, message)
536
+ matcher = validate_acceptance_of(attribute).with_message(message)
537
+ should matcher.description do
538
+ assert_accepts matcher, get_instance_of(klass)
686
539
  end
687
540
  end
688
541
  end
689
542
 
543
+ # Deprecated. See should_validate_uniqueness_of
544
+ def should_require_acceptance_of(*attributes)
545
+ warn "[DEPRECATION] should_require_acceptance_of is deprecated. " <<
546
+ "Use should_validate_acceptance_of instead."
547
+ should_validate_acceptance_of(*attributes)
548
+ end
549
+
690
550
  # Ensures that the model has a method named scope_name that returns a NamedScope object with the
691
551
  # proxy options set to the options you supply. scope_name can be either a symbol, or a method
692
552
  # call which will be evaled against the model. The eval'd method call has access to all the same
@@ -722,25 +582,11 @@ module Shoulda # :nodoc:
722
582
  # scoped(:limit => c)
723
583
  # end
724
584
  #
725
- def should_have_named_scope(scope_call, *args)
585
+ def should_have_named_scope(scope_call, find_options = nil)
726
586
  klass = model_class
727
- scope_opts = args.extract_options!
728
- scope_call = scope_call.to_s
729
-
730
- context scope_call do
731
- setup do
732
- @scope = eval("#{klass}.#{scope_call}")
733
- end
734
-
735
- should "return a scope object" do
736
- assert_equal ::ActiveRecord::NamedScope::Scope, @scope.class
737
- end
738
-
739
- unless scope_opts.empty?
740
- should "scope itself to #{scope_opts.inspect}" do
741
- assert_equal scope_opts, @scope.proxy_options
742
- end
743
- end
587
+ matcher = have_named_scope(scope_call).finding(find_options)
588
+ should matcher.description do
589
+ assert_accepts matcher.in_context(self), klass.new
744
590
  end
745
591
  end
746
592
  end