shoulda-matchers 2.4.0 → 2.5.0

Sign up to get free protection for your applications and to get access to all the features.
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