shoulda-matchers 2.4.0 → 2.5.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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -4
  3. data/Appraisals +19 -7
  4. data/Gemfile.lock +1 -1
  5. data/NEWS.md +35 -0
  6. data/README.md +1204 -46
  7. data/features/step_definitions/rails_steps.rb +1 -1
  8. data/gemfiles/3.0.gemfile.lock +1 -1
  9. data/gemfiles/3.1.gemfile.lock +1 -1
  10. data/gemfiles/3.2.gemfile.lock +1 -1
  11. data/gemfiles/{4.0.gemfile → 4.0.0.gemfile} +4 -4
  12. data/gemfiles/{4.0.gemfile.lock → 4.0.0.gemfile.lock} +24 -24
  13. data/gemfiles/4.0.1.gemfile +19 -0
  14. data/gemfiles/4.0.1.gemfile.lock +161 -0
  15. data/lib/shoulda/matchers/action_controller.rb +1 -0
  16. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +4 -2
  17. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +6 -3
  18. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +6 -3
  19. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +4 -2
  20. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +4 -2
  21. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +4 -2
  22. data/lib/shoulda/matchers/action_controller/route_matcher.rb +13 -19
  23. data/lib/shoulda/matchers/action_controller/route_params.rb +47 -0
  24. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +4 -2
  25. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +4 -2
  26. data/lib/shoulda/matchers/active_model.rb +4 -3
  27. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +9 -6
  28. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +4 -2
  29. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +6 -4
  30. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +4 -1
  31. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +8 -1
  32. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +4 -2
  33. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +59 -0
  34. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_even_number_matcher.rb +51 -0
  35. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +41 -0
  36. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +77 -0
  37. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +14 -12
  38. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +6 -6
  39. data/lib/shoulda/matchers/active_model/validation_matcher.rb +10 -7
  40. data/lib/shoulda/matchers/active_record.rb +2 -0
  41. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +4 -2
  42. data/lib/shoulda/matchers/active_record/association_matcher.rb +11 -3
  43. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +80 -0
  44. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +20 -55
  45. data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +40 -0
  46. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +4 -2
  47. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +4 -2
  48. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +7 -4
  49. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +4 -2
  50. data/lib/shoulda/matchers/integrations/test_unit.rb +3 -3
  51. data/lib/shoulda/matchers/rails_shim.rb +14 -6
  52. data/lib/shoulda/matchers/version.rb +1 -1
  53. data/spec/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +1 -1
  54. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +2 -2
  55. data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +5 -0
  56. data/spec/shoulda/matchers/action_controller/route_params_spec.rb +30 -0
  57. data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +1 -1
  58. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +1 -1
  59. data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +2 -2
  60. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +10 -0
  61. data/spec/shoulda/matchers/active_model/ensure_length_of_matcher_spec.rb +7 -0
  62. data/spec/shoulda/matchers/active_model/{comparison_matcher_spec.rb → numericality_matchers/comparison_matcher_spec.rb} +2 -2
  63. data/spec/shoulda/matchers/active_model/{odd_even_number_matcher_spec.rb → numericality_matchers/odd_even_number_matcher_spec.rb} +4 -4
  64. data/spec/shoulda/matchers/active_model/{only_integer_matcher_spec.rb → numericality_matchers/only_integer_matcher_spec.rb} +3 -3
  65. data/spec/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +139 -0
  66. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +7 -7
  67. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +48 -38
  68. data/spec/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +6 -6
  69. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +21 -8
  70. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +247 -0
  71. data/spec/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +1 -1
  72. data/spec/shoulda/matchers/active_record/serialize_matcher_spec.rb +3 -3
  73. data/spec/spec_helper.rb +9 -15
  74. data/spec/support/active_resource_builder.rb +2 -0
  75. data/spec/support/controller_builder.rb +4 -10
  76. data/spec/support/model_builder.rb +6 -2
  77. data/spec/support/rails_versions.rb +18 -0
  78. data/spec/support/shared_examples/numerical_submatcher_spec.rb +4 -4
  79. data/spec/support/test_application.rb +97 -0
  80. metadata +30 -14
  81. data/lib/shoulda/matchers/active_model/comparison_matcher.rb +0 -57
  82. data/lib/shoulda/matchers/active_model/odd_even_number_matcher.rb +0 -47
  83. data/lib/shoulda/matchers/active_model/only_integer_matcher.rb +0 -37
@@ -0,0 +1,41 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActiveModel # :nodoc:
4
+ module NumericalityMatchers
5
+ class OnlyIntegerMatcher # :nodoc:
6
+ NON_INTEGER_VALUE = 0.1
7
+
8
+ def initialize(attribute)
9
+ @attribute = attribute
10
+ @disallow_value_matcher = DisallowValueMatcher.new(NON_INTEGER_VALUE).
11
+ for(attribute).
12
+ with_message(:not_an_integer)
13
+ end
14
+
15
+ def matches?(subject)
16
+ @disallow_value_matcher.matches?(subject)
17
+ end
18
+
19
+ def with_message(message)
20
+ @disallow_value_matcher.with_message(message)
21
+ self
22
+ end
23
+
24
+ def allowed_types
25
+ 'integer'
26
+ end
27
+
28
+ def failure_message
29
+ @disallow_value_matcher.failure_message
30
+ end
31
+ alias failure_message_for_should failure_message
32
+
33
+ def failure_message_when_negated
34
+ @disallow_value_matcher.failure_message_when_negated
35
+ end
36
+ alias failure_message_for_should_not failure_message_when_negated
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,77 @@
1
+ module Shoulda # :nodoc:
2
+ module Matchers
3
+ module ActiveModel # :nodoc:
4
+
5
+ # Ensures that the model is not valid if the given attribute is present.
6
+ #
7
+ # Options:
8
+ # * <tt>with_message</tt> - value the test expects to find in
9
+ # <tt>errors.on(:attribute)</tt>. <tt>Regexp</tt> or <tt>String</tt>.
10
+ # Defaults to the translation for <tt>:present</tt>.
11
+ #
12
+ # Examples:
13
+ # it { should validate_absence_of(:name) }
14
+ # it { should validate_absence_of(:name).
15
+ # with_message(/may not be set/) }
16
+ def validate_absence_of(attr)
17
+ ValidateAbsenceOfMatcher.new(attr)
18
+ end
19
+
20
+ class ValidateAbsenceOfMatcher < ValidationMatcher # :nodoc:
21
+
22
+ def with_message(message)
23
+ @expected_message = message
24
+ self
25
+ end
26
+
27
+ def matches?(subject)
28
+ super(subject)
29
+ @expected_message ||= :present
30
+ disallows_value_of(value, @expected_message)
31
+ end
32
+
33
+ def description
34
+ "require #{@attribute} to not be set"
35
+ end
36
+
37
+ private
38
+
39
+ def value
40
+ if reflection
41
+ obj = reflection.klass.new
42
+ if collection?
43
+ [ obj ]
44
+ else
45
+ obj
46
+ end
47
+ elsif attribute_class == Fixnum
48
+ 1
49
+ elsif !attribute_class || attribute_class == String
50
+ 'an arbitrary value'
51
+ else
52
+ attribute_class.new
53
+ end
54
+ end
55
+
56
+ def attribute_class
57
+ @subject.class.respond_to?(:columns_hash) &&
58
+ @subject.class.columns_hash[@attribute].respond_to?(:klass) &&
59
+ @subject.class.columns_hash[@attribute].klass
60
+ end
61
+
62
+ def collection?
63
+ if reflection
64
+ [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
65
+ else
66
+ false
67
+ end
68
+ end
69
+
70
+ def reflection
71
+ @subject.class.respond_to?(:reflect_on_association) &&
72
+ @subject.class.reflect_on_association(@attribute)
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -32,43 +32,43 @@ module Shoulda # :nodoc:
32
32
  end
33
33
 
34
34
  def only_integer
35
- add_submatcher(OnlyIntegerMatcher.new(@attribute))
35
+ add_submatcher(NumericalityMatchers::OnlyIntegerMatcher.new(@attribute))
36
36
  self
37
37
  end
38
38
 
39
39
  def is_greater_than(value)
40
- add_submatcher(ComparisonMatcher.new(value, :>).for(@attribute))
40
+ add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :>).for(@attribute))
41
41
  self
42
42
  end
43
43
 
44
44
  def is_greater_than_or_equal_to(value)
45
- add_submatcher(ComparisonMatcher.new(value, :>=).for(@attribute))
45
+ add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :>=).for(@attribute))
46
46
  self
47
47
  end
48
48
 
49
49
  def is_equal_to(value)
50
- add_submatcher(ComparisonMatcher.new(value, :==).for(@attribute))
50
+ add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :==).for(@attribute))
51
51
  self
52
52
  end
53
53
 
54
54
  def is_less_than(value)
55
- add_submatcher(ComparisonMatcher.new(value, :<).for(@attribute))
55
+ add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :<).for(@attribute))
56
56
  self
57
57
  end
58
58
 
59
59
  def is_less_than_or_equal_to(value)
60
- add_submatcher(ComparisonMatcher.new(value, :<=).for(@attribute))
60
+ add_submatcher(NumericalityMatchers::ComparisonMatcher.new(value, :<=).for(@attribute))
61
61
  self
62
62
  end
63
63
 
64
64
  def odd
65
- odd_number_matcher = OddEvenNumberMatcher.new(@attribute, :odd => true)
65
+ odd_number_matcher = NumericalityMatchers::OddEvenNumberMatcher.new(@attribute, :odd => true)
66
66
  add_submatcher(odd_number_matcher)
67
67
  self
68
68
  end
69
69
 
70
70
  def even
71
- even_number_matcher = OddEvenNumberMatcher.new(@attribute, :even => true)
71
+ even_number_matcher = NumericalityMatchers::OddEvenNumberMatcher.new(@attribute, :even => true)
72
72
  add_submatcher(even_number_matcher)
73
73
  self
74
74
  end
@@ -87,13 +87,15 @@ module Shoulda # :nodoc:
87
87
  "only allow #{allowed_types} values for #{@attribute}"
88
88
  end
89
89
 
90
- def failure_message_for_should
90
+ def failure_message
91
91
  submatcher_failure_messages_for_should.last
92
92
  end
93
+ alias failure_message_for_should failure_message
93
94
 
94
- def failure_message_for_should_not
95
+ def failure_message_when_negated
95
96
  submatcher_failure_messages_for_should_not.last
96
97
  end
98
+ alias failure_message_for_should_not failure_message_when_negated
97
99
 
98
100
  private
99
101
 
@@ -114,11 +116,11 @@ module Shoulda # :nodoc:
114
116
  end
115
117
 
116
118
  def submatcher_failure_messages_for_should
117
- failing_submatchers.map(&:failure_message_for_should)
119
+ failing_submatchers.map(&:failure_message)
118
120
  end
119
121
 
120
122
  def submatcher_failure_messages_for_should_not
121
- failing_submatchers.map(&:failure_message_for_should_not)
123
+ failing_submatchers.map(&:failure_message_when_negated)
122
124
  end
123
125
 
124
126
  def failing_submatchers
@@ -3,7 +3,7 @@ module Shoulda # :nodoc:
3
3
  module ActiveModel # :nodoc:
4
4
  # Ensures that the model is invalid if the given attribute is not unique.
5
5
  # It uses the first existing record or creates a new one if no record
6
- # exists in the database. It simply uses `:validate => false` to get
6
+ # exists in the database. It simply uses `validate: false` to get
7
7
  # around validations, so it will probably fail if there are `NOT NULL`
8
8
  # constraints. In that case, you must create a record before calling
9
9
  # `validate_uniqueness_of`.
@@ -110,12 +110,12 @@ module Shoulda # :nodoc:
110
110
  if options[:nil_value]
111
111
  value = nil
112
112
  else
113
- value = "arbitrary_string"
113
+ value = 'a'
114
114
  end
115
115
 
116
116
  @subject.class.new.tap do |instance|
117
117
  instance.send("#{@attribute}=", value)
118
- instance.save(:validate => false)
118
+ instance.save(validate: false)
119
119
  end
120
120
  end
121
121
 
@@ -127,7 +127,7 @@ module Shoulda # :nodoc:
127
127
  @subject.send(setter, existing_record.send(scope))
128
128
  true
129
129
  else
130
- @failure_message_for_should = "#{class_name} doesn't seem to have a #{scope} attribute."
130
+ @failure_message = "#{class_name} doesn't seem to have a #{scope} attribute."
131
131
  false
132
132
  end
133
133
  end
@@ -173,11 +173,11 @@ module Shoulda # :nodoc:
173
173
  if allows_value_of(existing_value, @expected_message)
174
174
  @subject.send("#{scope}=", previous_value)
175
175
 
176
- @failure_message_for_should_not <<
176
+ @failure_message_when_negated <<
177
177
  " (with different value of #{scope})"
178
178
  true
179
179
  else
180
- @failure_message_for_should << " (with different value of #{scope})"
180
+ @failure_message << " (with different value of #{scope})"
181
181
  false
182
182
  end
183
183
  end
@@ -2,7 +2,9 @@ module Shoulda # :nodoc:
2
2
  module Matchers
3
3
  module ActiveModel # :nodoc:
4
4
  class ValidationMatcher # :nodoc:
5
- attr_reader :failure_message_for_should
5
+ attr_reader :failure_message
6
+
7
+ alias failure_message_for_should failure_message
6
8
 
7
9
  def initialize(attribute)
8
10
  @attribute = attribute
@@ -19,9 +21,10 @@ module Shoulda # :nodoc:
19
21
  self
20
22
  end
21
23
 
22
- def failure_message_for_should_not
23
- @failure_message_for_should_not || @failure_message_for_should
24
+ def failure_message_when_negated
25
+ @failure_message_when_negated || @failure_message
24
26
  end
27
+ alias failure_message_for_should_not failure_message_when_negated
25
28
 
26
29
  def matches?(subject)
27
30
  @subject = subject
@@ -35,10 +38,10 @@ module Shoulda # :nodoc:
35
38
  yield allow if block_given?
36
39
 
37
40
  if allow.matches?(@subject)
38
- @failure_message_for_should_not = allow.failure_message_for_should_not
41
+ @failure_message_when_negated = allow.failure_message_when_negated
39
42
  true
40
43
  else
41
- @failure_message_for_should = allow.failure_message_for_should
44
+ @failure_message = allow.failure_message
42
45
  false
43
46
  end
44
47
  end
@@ -48,10 +51,10 @@ module Shoulda # :nodoc:
48
51
  yield disallow if block_given?
49
52
 
50
53
  if disallow.matches?(@subject)
51
- @failure_message_for_should_not = disallow.failure_message_for_should_not
54
+ @failure_message_when_negated = disallow.failure_message_when_negated
52
55
  true
53
56
  else
54
- @failure_message_for_should = disallow.failure_message_for_should
57
+ @failure_message = disallow.failure_message
55
58
  false
56
59
  end
57
60
  end
@@ -3,7 +3,9 @@ require 'shoulda/matchers/active_record/association_matchers/counter_cache_match
3
3
  require 'shoulda/matchers/active_record/association_matchers/order_matcher'
4
4
  require 'shoulda/matchers/active_record/association_matchers/through_matcher'
5
5
  require 'shoulda/matchers/active_record/association_matchers/dependent_matcher'
6
+ require 'shoulda/matchers/active_record/association_matchers/source_matcher'
6
7
  require 'shoulda/matchers/active_record/association_matchers/model_reflector'
8
+ require 'shoulda/matchers/active_record/association_matchers/model_reflection'
7
9
  require 'shoulda/matchers/active_record/association_matchers/option_verifier'
8
10
  require 'shoulda/matchers/active_record/have_db_column_matcher'
9
11
  require 'shoulda/matchers/active_record/have_db_index_matcher'
@@ -50,13 +50,15 @@ module Shoulda
50
50
  update_only_correct?
51
51
  end
52
52
 
53
- def failure_message_for_should
53
+ def failure_message
54
54
  "Expected #{expectation} (#{@problem})"
55
55
  end
56
+ alias failure_message_for_should failure_message
56
57
 
57
- def failure_message_for_should_not
58
+ def failure_message_when_negated
58
59
  "Did not expect #{expectation}"
59
60
  end
61
+ alias failure_message_for_should_not failure_message_when_negated
60
62
 
61
63
  def description
62
64
  description = "accepts_nested_attributes_for :#{@name}"
@@ -1,4 +1,4 @@
1
- require 'forwardable'
1
+ require 'active_support/core_ext/module/delegation'
2
2
 
3
3
  module Shoulda # :nodoc:
4
4
  module Matchers
@@ -109,6 +109,12 @@ module Shoulda # :nodoc:
109
109
  self
110
110
  end
111
111
 
112
+ def source(source)
113
+ source_matcher = AssociationMatchers::SourceMatcher.new(source, name)
114
+ add_submatcher(source_matcher)
115
+ self
116
+ end
117
+
112
118
  def conditions(conditions)
113
119
  @options[:conditions] = conditions
114
120
  self
@@ -140,13 +146,15 @@ module Shoulda # :nodoc:
140
146
  [description, submatchers.map(&:description)].flatten.join(' ')
141
147
  end
142
148
 
143
- def failure_message_for_should
149
+ def failure_message
144
150
  "Expected #{expectation} (#{missing_options})"
145
151
  end
152
+ alias failure_message_for_should failure_message
146
153
 
147
- def failure_message_for_should_not
154
+ def failure_message_when_negated
148
155
  "Did not expect #{expectation}"
149
156
  end
157
+ alias failure_message_for_should_not failure_message_when_negated
150
158
 
151
159
  def matches?(subject)
152
160
  @subject = subject
@@ -0,0 +1,80 @@
1
+ require 'delegate'
2
+
3
+ module Shoulda
4
+ module Matchers
5
+ module ActiveRecord
6
+ module AssociationMatchers
7
+ class ModelReflection < SimpleDelegator
8
+ def initialize(reflection)
9
+ super(reflection)
10
+ @reflection = reflection
11
+ @subject = reflection.active_record
12
+ end
13
+
14
+ def associated_class
15
+ reflection.klass
16
+ end
17
+
18
+ def through?
19
+ reflection.options[:through]
20
+ end
21
+
22
+ def join_table
23
+ join_table =
24
+ if reflection.respond_to?(:join_table)
25
+ reflection.join_table
26
+ else
27
+ reflection.options[:join_table]
28
+ end
29
+
30
+ join_table.to_s
31
+ end
32
+
33
+ def association_relation
34
+ if reflection.respond_to?(:scope)
35
+ convert_scope_to_relation(reflection.scope)
36
+ else
37
+ convert_options_to_relation(reflection.options)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ attr_reader :reflection, :subject
44
+
45
+ def convert_scope_to_relation(scope)
46
+ relation = associated_class.all
47
+
48
+ if scope
49
+ # Source: AR::Associations::AssociationScope#eval_scope
50
+ relation.instance_exec(subject, &scope)
51
+ else
52
+ relation
53
+ end
54
+ end
55
+
56
+ def convert_options_to_relation(options)
57
+ relation = associated_class.scoped
58
+ relation = extend_relation_with(relation, :where, options[:conditions])
59
+ relation = extend_relation_with(relation, :includes, options[:include])
60
+ relation = extend_relation_with(relation, :order, options[:order])
61
+ relation = extend_relation_with(relation, :group, options[:group])
62
+ relation = extend_relation_with(relation, :having, options[:having])
63
+ relation = extend_relation_with(relation, :limit, options[:limit])
64
+ relation = extend_relation_with(relation, :offset, options[:offset])
65
+ relation = extend_relation_with(relation, :select, options[:select])
66
+ relation
67
+ end
68
+
69
+ def extend_relation_with(relation, method_name, value)
70
+ if value
71
+ relation.__send__(method_name, value)
72
+ else
73
+ relation
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end