shoulda-matchers 2.8.0.rc1 → 2.8.0.rc2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/.hound.yml +3 -0
  3. data/.hound_config/ruby.yml +5 -0
  4. data/.travis.yml +4 -0
  5. data/.yardopts +1 -1
  6. data/Appraisals +5 -2
  7. data/Gemfile +1 -1
  8. data/Gemfile.lock +7 -5
  9. data/NEWS.md +24 -0
  10. data/README.md +9 -0
  11. data/gemfiles/3.0.gemfile +1 -1
  12. data/gemfiles/3.0.gemfile.lock +7 -5
  13. data/gemfiles/3.1.gemfile +1 -1
  14. data/gemfiles/3.1.gemfile.lock +7 -5
  15. data/gemfiles/3.1_1.9.2.gemfile +1 -3
  16. data/gemfiles/3.1_1.9.2.gemfile.lock +7 -12
  17. data/gemfiles/3.2.gemfile +1 -1
  18. data/gemfiles/3.2.gemfile.lock +7 -5
  19. data/gemfiles/3.2_1.9.2.gemfile +1 -3
  20. data/gemfiles/3.2_1.9.2.gemfile.lock +5 -10
  21. data/gemfiles/4.0.0.gemfile +1 -1
  22. data/gemfiles/4.0.0.gemfile.lock +7 -5
  23. data/gemfiles/4.0.1.gemfile +1 -1
  24. data/gemfiles/4.0.1.gemfile.lock +7 -5
  25. data/gemfiles/4.1.gemfile +1 -1
  26. data/gemfiles/4.1.gemfile.lock +7 -5
  27. data/gemfiles/4.2.gemfile +5 -3
  28. data/gemfiles/4.2.gemfile.lock +26 -8
  29. data/lib/shoulda/matchers/action_controller/set_flash_matcher.rb +2 -0
  30. data/lib/shoulda/matchers/active_model.rb +3 -2
  31. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +44 -32
  32. data/lib/shoulda/matchers/active_model/helpers.rb +8 -22
  33. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +66 -19
  34. data/lib/shoulda/matchers/active_model/strict_validator.rb +51 -0
  35. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +14 -13
  36. data/lib/shoulda/matchers/active_model/validator.rb +113 -0
  37. data/lib/shoulda/matchers/active_model/validator_with_captured_range_error.rb +12 -0
  38. data/lib/shoulda/matchers/active_record/association_matcher.rb +19 -1
  39. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +23 -2
  40. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +14 -11
  41. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +1 -0
  42. data/lib/shoulda/matchers/active_record/uniqueness/model.rb +1 -0
  43. data/lib/shoulda/matchers/active_record/uniqueness/namespace.rb +1 -0
  44. data/lib/shoulda/matchers/active_record/uniqueness/test_model_creator.rb +1 -0
  45. data/lib/shoulda/matchers/active_record/uniqueness/test_models.rb +1 -0
  46. data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +22 -15
  47. data/lib/shoulda/matchers/matcher_context.rb +1 -0
  48. data/lib/shoulda/matchers/rails_shim.rb +22 -0
  49. data/lib/shoulda/matchers/util.rb +5 -0
  50. data/lib/shoulda/matchers/version.rb +1 -1
  51. data/script/update_gem_in_all_appraisals +15 -0
  52. data/shoulda-matchers.gemspec +1 -1
  53. data/spec/support/unit/helpers/active_model_helpers.rb +3 -1
  54. data/spec/support/unit/helpers/active_model_versions.rb +8 -0
  55. data/spec/support/unit/helpers/active_record_versions.rb +16 -0
  56. data/spec/support/unit/helpers/rails_versions.rb +4 -4
  57. data/spec/support/unit/matchers/fail_with_message_matcher.rb +9 -8
  58. data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +74 -0
  59. data/spec/unit/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +75 -0
  60. data/spec/unit/shoulda/matchers/active_model/validate_inclusion_of_matcher_spec.rb +24 -0
  61. data/spec/unit/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +78 -5
  62. data/spec/unit/shoulda/matchers/active_record/association_matcher_spec.rb +31 -2
  63. data/spec/unit_spec_helper.rb +2 -0
  64. metadata +11 -8
  65. data/lib/shoulda/matchers/active_model/exception_message_finder.rb +0 -58
  66. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +0 -69
  67. data/spec/unit/shoulda/matchers/active_model/exception_message_finder_spec.rb +0 -111
  68. data/spec/unit/shoulda/matchers/active_model/validation_message_finder_spec.rb +0 -129
@@ -42,25 +42,80 @@ module Shoulda
42
42
  "#{expectation} #{@value}"
43
43
  end
44
44
 
45
+ def failure_message
46
+ last_failing_submatcher.failure_message
47
+ end
48
+ alias failure_message_for_should failure_message
49
+
50
+ def failure_message_when_negated
51
+ last_failing_submatcher.failure_message_when_negated
52
+ end
53
+ alias failure_message_for_should_not failure_message_when_negated
54
+
45
55
  private
46
56
 
57
+ def all_bounds_correct?
58
+ failing_submatchers.empty?
59
+ end
60
+
61
+ def failing_submatchers
62
+ submatchers_and_results.
63
+ select { |x| !x[:matched] }.
64
+ map { |x| x[:matcher] }
65
+ end
66
+
67
+ def last_failing_submatcher
68
+ failing_submatchers.last
69
+ end
70
+
71
+ def submatchers
72
+ @_submatchers ||=
73
+ comparison_combos.map do |diff, submatcher_method_name|
74
+ matcher = __send__(submatcher_method_name, @value + diff, nil)
75
+ matcher.with_message(@message, values: { count: @value })
76
+ matcher
77
+ end
78
+ end
79
+
80
+ def submatchers_and_results
81
+ @_submatchers_and_results ||=
82
+ submatchers.map do |matcher|
83
+ { matcher: matcher, matched: matcher.matches?(@subject) }
84
+ end
85
+ end
86
+
47
87
  def comparison_combos
48
- allow = :allows_value_of
49
- disallow = :disallows_value_of
50
- checker_types =
51
- case @operator
52
- when :> then [allow, disallow, disallow]
53
- when :>= then [allow, allow, disallow]
54
- when :== then [disallow, allow, disallow]
55
- when :< then [disallow, disallow, allow]
56
- when :<= then [disallow, allow, allow]
88
+ diffs_to_compare.zip(submatcher_method_names)
89
+ end
90
+
91
+ def submatcher_method_names
92
+ assertions.map do |value|
93
+ if value
94
+ :allow_value_matcher
95
+ else
96
+ :disallow_value_matcher
57
97
  end
58
- diffs_to_compare.zip(checker_types)
98
+ end
99
+ end
100
+
101
+ def assertions
102
+ case @operator
103
+ when :>
104
+ [false, false, true]
105
+ when :>=
106
+ [false, true, true]
107
+ when :==
108
+ [false, true, false]
109
+ when :<
110
+ [true, false, false]
111
+ when :<=
112
+ [true, true, false]
113
+ end
59
114
  end
60
115
 
61
116
  def diffs_to_compare
62
117
  diff = @numericality_matcher.diff_to_compare
63
- [diff, 0, -diff]
118
+ [-diff, 0, diff]
64
119
  end
65
120
 
66
121
  def expectation
@@ -72,14 +127,6 @@ module Shoulda
72
127
  when :<= then "less than or equal to"
73
128
  end
74
129
  end
75
-
76
- def all_bounds_correct?
77
- @comparison_combos.all? do |diff, checker_type|
78
- __send__(checker_type, @value + diff) do |matcher|
79
- matcher.with_message(@message, values: { count: @value })
80
- end
81
- end
82
- end
83
130
  end
84
131
  end
85
132
  end
@@ -0,0 +1,51 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # @private
5
+ module StrictValidator
6
+ def allow_description(allowed_values)
7
+ "doesn't raise when #{attribute} is set to #{allowed_values}"
8
+ end
9
+
10
+ def expected_message_from(attribute_message)
11
+ "#{human_attribute_name} #{attribute_message}"
12
+ end
13
+
14
+ def formatted_messages
15
+ [messages.first.message]
16
+ end
17
+
18
+ def messages_description
19
+ if has_messages?
20
+ ': ' + messages.first.message.inspect
21
+ else
22
+ ' no exception'
23
+ end
24
+ end
25
+
26
+ def expected_messages_description(expected_message)
27
+ if expected_message
28
+ "exception to include #{expected_message.inspect}"
29
+ else
30
+ 'an exception to have been raised'
31
+ end
32
+ end
33
+
34
+ protected
35
+
36
+ def collect_messages
37
+ validation_exceptions
38
+ end
39
+
40
+ private
41
+
42
+ def validation_exceptions
43
+ record.valid?(context)
44
+ []
45
+ rescue ::ActiveModel::StrictValidationFailed => exception
46
+ [exception]
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -355,7 +355,7 @@ module Shoulda
355
355
 
356
356
  def matches?(subject)
357
357
  @subject = subject
358
- submatchers_match?
358
+ failing_submatchers.empty?
359
359
  end
360
360
 
361
361
  def description
@@ -363,12 +363,12 @@ module Shoulda
363
363
  end
364
364
 
365
365
  def failure_message
366
- submatcher_failure_messages_for_should.last
366
+ last_failing_submatcher.failure_message
367
367
  end
368
368
  alias failure_message_for_should failure_message
369
369
 
370
370
  def failure_message_when_negated
371
- submatcher_failure_messages_for_should_not.last
371
+ last_failing_submatcher.failure_message_when_negated
372
372
  end
373
373
  alias failure_message_for_should_not failure_message_when_negated
374
374
 
@@ -403,20 +403,21 @@ module Shoulda
403
403
  @diff_to_compare = [@diff_to_compare, matcher.diff_to_compare].max
404
404
  end
405
405
 
406
- def submatchers_match?
407
- failing_submatchers.empty?
408
- end
409
-
410
- def submatcher_failure_messages_for_should
411
- failing_submatchers.map(&:failure_message)
406
+ def submatchers_and_results
407
+ @_submatchers_and_results ||=
408
+ @submatchers.map do |matcher|
409
+ { matcher: matcher, matched: matcher.matches?(@subject) }
410
+ end
412
411
  end
413
412
 
414
- def submatcher_failure_messages_for_should_not
415
- failing_submatchers.map(&:failure_message_when_negated)
413
+ def failing_submatchers
414
+ submatchers_and_results.
415
+ select { |x| !x[:matched] }.
416
+ map { |x| x[:matcher] }
416
417
  end
417
418
 
418
- def failing_submatchers
419
- @failing_submatchers ||= @submatchers.select { |matcher| !matcher.matches?(@subject) }
419
+ def last_failing_submatcher
420
+ failing_submatchers.last
420
421
  end
421
422
 
422
423
  def allowed_types
@@ -0,0 +1,113 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # @private
5
+ class Validator
6
+ include Helpers
7
+
8
+ attr_writer :attribute, :context, :record
9
+
10
+ def initialize
11
+ reset
12
+ end
13
+
14
+ def reset
15
+ @messages = nil
16
+ end
17
+
18
+ def strict=(strict)
19
+ @strict = strict
20
+
21
+ if strict
22
+ extend StrictValidator
23
+ end
24
+ end
25
+
26
+ def capture_range_error(exception)
27
+ @captured_range_error = exception
28
+ extend ValidatorWithCapturedRangeError
29
+ end
30
+
31
+ def allow_description(allowed_values)
32
+ "allow #{attribute} to be set to #{allowed_values}"
33
+ end
34
+
35
+ def expected_message_from(attribute_message)
36
+ attribute_message
37
+ end
38
+
39
+ def messages
40
+ @messages ||= collect_messages
41
+ end
42
+
43
+ def formatted_messages
44
+ messages
45
+ end
46
+
47
+ def has_messages?
48
+ messages.any?
49
+ end
50
+
51
+ def messages_description
52
+ if has_messages?
53
+ " errors:\n#{pretty_error_messages(record)}"
54
+ else
55
+ ' no errors'
56
+ end
57
+ end
58
+
59
+ def expected_messages_description(expected_message)
60
+ if expected_message
61
+ "errors to include #{expected_message.inspect}"
62
+ else
63
+ 'errors'
64
+ end
65
+ end
66
+
67
+ def captured_range_error?
68
+ !!captured_range_error
69
+ end
70
+
71
+ protected
72
+
73
+ attr_reader :attribute, :context, :strict, :record,
74
+ :captured_range_error
75
+
76
+ def collect_messages
77
+ validation_errors
78
+ end
79
+
80
+ private
81
+
82
+ def strict?
83
+ !!@strict
84
+ end
85
+
86
+ def collect_errors_or_exceptions
87
+ collect_messages
88
+ rescue RangeError => exception
89
+ capture_range_error(exception)
90
+ []
91
+ end
92
+
93
+ def validation_errors
94
+ if context
95
+ record.valid?(context)
96
+ else
97
+ record.valid?
98
+ end
99
+
100
+ if record.errors.respond_to?(:[])
101
+ record.errors[attribute]
102
+ else
103
+ record.errors.on(attribute)
104
+ end
105
+ end
106
+
107
+ def human_attribute_name
108
+ record.class.human_attribute_name(attribute)
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,12 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # @private
5
+ module ValidatorWithCapturedRangeError
6
+ def messages_description
7
+ ' RangeError: ' + captured_range_error.message.inspect
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -162,6 +162,24 @@ module Shoulda
162
162
  # should belong_to(:world).dependent(:destroy)
163
163
  # end
164
164
  #
165
+ # To assert that *any* `:dependent` option was specified, use `true`:
166
+ #
167
+ # # RSpec
168
+ # describe Person do
169
+ # it { should belong_to(:world).dependent(true) }
170
+ # end
171
+ #
172
+ # To assert that *no* `:dependent` option was specified, use `false`:
173
+ #
174
+ # class Person < ActiveRecord::Base
175
+ # belongs_to :company
176
+ # end
177
+ #
178
+ # # RSpec
179
+ # describe Person do
180
+ # it { should belong_to(:company).dependent(false) }
181
+ # end
182
+ #
165
183
  # ##### counter_cache
166
184
  #
167
185
  # Use `counter_cache` to assert that the `:counter_cache` option was
@@ -993,7 +1011,7 @@ module Shoulda
993
1011
  end
994
1012
 
995
1013
  def macro_supports_primary_key?
996
- macro == :belongs_to ||
1014
+ macro == :belongs_to ||
997
1015
  ([:has_many, :has_one].include?(macro) && !through?)
998
1016
  end
999
1017
 
@@ -19,10 +19,10 @@ module Shoulda
19
19
  def matches?(subject)
20
20
  self.subject = ModelReflector.new(subject, name)
21
21
 
22
- if option_verifier.correct_for_string?(:dependent, dependent)
22
+ if option_matches?
23
23
  true
24
24
  else
25
- self.missing_option = "#{name} should have #{dependent} dependency"
25
+ self.missing_option = generate_missing_option
26
26
  false
27
27
  end
28
28
  end
@@ -31,9 +31,30 @@ module Shoulda
31
31
 
32
32
  attr_accessor :subject, :dependent, :name
33
33
 
34
+ private
35
+
34
36
  def option_verifier
35
37
  @option_verifier ||= OptionVerifier.new(subject)
36
38
  end
39
+
40
+ def option_matches?
41
+ option_verifier.correct_for?(option_type, :dependent, dependent)
42
+ end
43
+
44
+ def option_type
45
+ case dependent
46
+ when true, false then :boolean
47
+ else :string
48
+ end
49
+ end
50
+
51
+ def generate_missing_option
52
+ [
53
+ "#{name} should have",
54
+ (dependent == true ? 'a' : dependent),
55
+ 'dependency'
56
+ ].join(' ')
57
+ end
37
58
  end
38
59
  end
39
60
  end
@@ -32,6 +32,20 @@ module Shoulda
32
32
  correct_for?(:relation_clause, name, expected_value)
33
33
  end
34
34
 
35
+ def correct_for?(*args)
36
+ expected_value, name, type = args.reverse
37
+ if expected_value.nil?
38
+ true
39
+ else
40
+ expected_value = type_cast(
41
+ type,
42
+ expected_value_for(type, name, expected_value)
43
+ )
44
+ actual_value = type_cast(type, actual_value_for(name))
45
+ expected_value == actual_value
46
+ end
47
+ end
48
+
35
49
  def actual_value_for(name)
36
50
  if RELATION_OPTIONS.include?(name)
37
51
  actual_value_for_relation_clause(name)
@@ -49,17 +63,6 @@ module Shoulda
49
63
 
50
64
  attr_reader :reflector
51
65
 
52
- def correct_for?(*args)
53
- expected_value, name, type = args.reverse
54
- if expected_value.nil?
55
- true
56
- else
57
- expected_value = type_cast(type, expected_value_for(type, name, expected_value))
58
- actual_value = type_cast(type, actual_value_for(name))
59
- expected_value == actual_value
60
- end
61
- end
62
-
63
66
  def type_cast(type, value)
64
67
  case type
65
68
  when :string, :relation_clause then value.to_s