mcmire-shoulda-matchers 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 (164) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.travis.yml +32 -0
  4. data/.yardopts +7 -0
  5. data/Appraisals +45 -0
  6. data/CONTRIBUTING.md +41 -0
  7. data/Gemfile +31 -0
  8. data/Gemfile.lock +166 -0
  9. data/MIT-LICENSE +22 -0
  10. data/NEWS.md +299 -0
  11. data/README.md +163 -0
  12. data/Rakefile +116 -0
  13. data/doc_config/gh-pages/index.html.erb +9 -0
  14. data/doc_config/yard/setup.rb +22 -0
  15. data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +5967 -0
  16. data/doc_config/yard/templates/default/fulldoc/html/css/full_list.css +12 -0
  17. data/doc_config/yard/templates/default/fulldoc/html/css/global.css +45 -0
  18. data/doc_config/yard/templates/default/fulldoc/html/css/solarized.css +69 -0
  19. data/doc_config/yard/templates/default/fulldoc/html/css/style.css +283 -0
  20. data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +32 -0
  21. data/doc_config/yard/templates/default/fulldoc/html/full_list_class.erb +1 -0
  22. data/doc_config/yard/templates/default/fulldoc/html/full_list_method.erb +8 -0
  23. data/doc_config/yard/templates/default/fulldoc/html/js/app.js +300 -0
  24. data/doc_config/yard/templates/default/fulldoc/html/js/full_list.js +1 -0
  25. data/doc_config/yard/templates/default/fulldoc/html/js/jquery.stickyheaders.js +289 -0
  26. data/doc_config/yard/templates/default/fulldoc/html/js/underscore.min.js +6 -0
  27. data/doc_config/yard/templates/default/fulldoc/html/setup.rb +8 -0
  28. data/doc_config/yard/templates/default/layout/html/breadcrumb.erb +14 -0
  29. data/doc_config/yard/templates/default/layout/html/fonts.erb +1 -0
  30. data/doc_config/yard/templates/default/layout/html/layout.erb +23 -0
  31. data/doc_config/yard/templates/default/layout/html/search.erb +13 -0
  32. data/doc_config/yard/templates/default/layout/html/setup.rb +8 -0
  33. data/doc_config/yard/templates/default/method_details/html/source.erb +10 -0
  34. data/doc_config/yard/templates/default/module/html/box_info.erb +31 -0
  35. data/features/rails_integration.feature +113 -0
  36. data/features/step_definitions/rails_steps.rb +162 -0
  37. data/features/support/env.rb +5 -0
  38. data/gemfiles/3.0.gemfile +24 -0
  39. data/gemfiles/3.0.gemfile.lock +150 -0
  40. data/gemfiles/3.1.gemfile +27 -0
  41. data/gemfiles/3.1.gemfile.lock +173 -0
  42. data/gemfiles/3.2.gemfile +27 -0
  43. data/gemfiles/3.2.gemfile.lock +171 -0
  44. data/gemfiles/4.0.0.gemfile +28 -0
  45. data/gemfiles/4.0.0.gemfile.lock +172 -0
  46. data/gemfiles/4.0.1.gemfile +28 -0
  47. data/gemfiles/4.0.1.gemfile.lock +172 -0
  48. data/lib/shoulda-matchers.rb +1 -0
  49. data/lib/shoulda/matchers.rb +11 -0
  50. data/lib/shoulda/matchers/action_controller.rb +17 -0
  51. data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +64 -0
  52. data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +97 -0
  53. data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +81 -0
  54. data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +117 -0
  55. data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +114 -0
  56. data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +154 -0
  57. data/lib/shoulda/matchers/action_controller/route_matcher.rb +116 -0
  58. data/lib/shoulda/matchers/action_controller/route_params.rb +48 -0
  59. data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +164 -0
  60. data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +296 -0
  61. data/lib/shoulda/matchers/active_model.rb +30 -0
  62. data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +167 -0
  63. data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +314 -0
  64. data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +46 -0
  65. data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +160 -0
  66. data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +417 -0
  67. data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +337 -0
  68. data/lib/shoulda/matchers/active_model/errors.rb +10 -0
  69. data/lib/shoulda/matchers/active_model/exception_message_finder.rb +58 -0
  70. data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +92 -0
  71. data/lib/shoulda/matchers/active_model/helpers.rb +46 -0
  72. data/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
  73. data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +75 -0
  74. data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +27 -0
  75. data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +41 -0
  76. data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +27 -0
  77. data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +26 -0
  78. data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +112 -0
  79. data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +77 -0
  80. data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +121 -0
  81. data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +380 -0
  82. data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +89 -0
  83. data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +372 -0
  84. data/lib/shoulda/matchers/active_model/validation_matcher.rb +97 -0
  85. data/lib/shoulda/matchers/active_model/validation_message_finder.rb +69 -0
  86. data/lib/shoulda/matchers/active_record.rb +22 -0
  87. data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +204 -0
  88. data/lib/shoulda/matchers/active_record/association_matcher.rb +901 -0
  89. data/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
  90. data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +41 -0
  91. data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +41 -0
  92. data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +81 -0
  93. data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +65 -0
  94. data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +94 -0
  95. data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
  96. data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +41 -0
  97. data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +63 -0
  98. data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +261 -0
  99. data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +149 -0
  100. data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +72 -0
  101. data/lib/shoulda/matchers/active_record/serialize_matcher.rb +181 -0
  102. data/lib/shoulda/matchers/assertion_error.rb +19 -0
  103. data/lib/shoulda/matchers/error.rb +6 -0
  104. data/lib/shoulda/matchers/integrations/rspec.rb +20 -0
  105. data/lib/shoulda/matchers/integrations/test_unit.rb +30 -0
  106. data/lib/shoulda/matchers/rails_shim.rb +50 -0
  107. data/lib/shoulda/matchers/version.rb +6 -0
  108. data/lib/shoulda/matchers/warn.rb +8 -0
  109. data/shoulda-matchers.gemspec +23 -0
  110. data/spec/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +22 -0
  111. data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +42 -0
  112. data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +78 -0
  113. data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +63 -0
  114. data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +63 -0
  115. data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +31 -0
  116. data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +70 -0
  117. data/spec/shoulda/matchers/action_controller/route_params_spec.rb +30 -0
  118. data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +51 -0
  119. data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +153 -0
  120. data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +111 -0
  121. data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +170 -0
  122. data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +81 -0
  123. data/spec/shoulda/matchers/active_model/ensure_exclusion_of_matcher_spec.rb +95 -0
  124. data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +320 -0
  125. data/spec/shoulda/matchers/active_model/ensure_length_of_matcher_spec.rb +166 -0
  126. data/spec/shoulda/matchers/active_model/exception_message_finder_spec.rb +111 -0
  127. data/spec/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb +20 -0
  128. data/spec/shoulda/matchers/active_model/helpers_spec.rb +158 -0
  129. data/spec/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +169 -0
  130. data/spec/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +59 -0
  131. data/spec/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +59 -0
  132. data/spec/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +57 -0
  133. data/spec/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +139 -0
  134. data/spec/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +41 -0
  135. data/spec/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +47 -0
  136. data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +331 -0
  137. data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +180 -0
  138. data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +398 -0
  139. data/spec/shoulda/matchers/active_model/validation_message_finder_spec.rb +127 -0
  140. data/spec/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +107 -0
  141. data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +860 -0
  142. data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +247 -0
  143. data/spec/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +111 -0
  144. data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +78 -0
  145. data/spec/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +41 -0
  146. data/spec/shoulda/matchers/active_record/serialize_matcher_spec.rb +86 -0
  147. data/spec/spec_helper.rb +26 -0
  148. data/spec/support/active_model_versions.rb +13 -0
  149. data/spec/support/active_resource_builder.rb +29 -0
  150. data/spec/support/activemodel_helpers.rb +19 -0
  151. data/spec/support/capture_helpers.rb +19 -0
  152. data/spec/support/class_builder.rb +42 -0
  153. data/spec/support/controller_builder.rb +74 -0
  154. data/spec/support/fail_with_message_including_matcher.rb +33 -0
  155. data/spec/support/fail_with_message_matcher.rb +32 -0
  156. data/spec/support/i18n_faker.rb +10 -0
  157. data/spec/support/mailer_builder.rb +10 -0
  158. data/spec/support/model_builder.rb +81 -0
  159. data/spec/support/rails_versions.rb +18 -0
  160. data/spec/support/shared_examples/numerical_submatcher.rb +19 -0
  161. data/spec/support/shared_examples/numerical_type_submatcher.rb +17 -0
  162. data/spec/support/test_application.rb +120 -0
  163. data/yard.watchr +5 -0
  164. metadata +281 -0
@@ -0,0 +1,46 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # @private
5
+ module Helpers
6
+ def pretty_error_messages(obj)
7
+ obj.errors.map do |attribute, model|
8
+ msg = "#{attribute} #{model}"
9
+ if attribute.to_sym != :base && obj.respond_to?(attribute)
10
+ msg << " (#{obj.__send__(attribute).inspect})"
11
+ end
12
+ msg
13
+ end
14
+ end
15
+
16
+ # Helper method that determines the default error message used by Active
17
+ # Record. Works for both existing Rails 2.1 and Rails 2.2 with the newly
18
+ # introduced I18n module used for localization. Use with Rails 3.0 and
19
+ # up will delegate to ActiveModel::Errors.generate_error if a model
20
+ # instance is given.
21
+ #
22
+ # default_error_message(:blank)
23
+ # default_error_message(:too_short, count: 5)
24
+ # default_error_message(:too_long, count: 60)
25
+ # default_error_message(:blank, model_name: 'user', attribute: 'name')
26
+ # default_error_message(:blank, instance: #<Model>, attribute: 'name')
27
+ def default_error_message(key, options = {})
28
+ model_name = options.delete(:model_name)
29
+ attribute = options.delete(:attribute)
30
+ instance = options.delete(:instance)
31
+
32
+ if instance && instance.errors.respond_to?(:generate_message)
33
+ instance.errors.generate_message(attribute.to_sym, key, options)
34
+ else
35
+ default_translation = [ :"activerecord.errors.models.#{model_name}.#{key}",
36
+ :"activerecord.errors.messages.#{key}",
37
+ :"errors.attributes.#{attribute}.#{key}",
38
+ :"errors.messages.#{key}" ]
39
+ I18n.translate(:"activerecord.errors.models.#{model_name}.attributes.#{attribute}.#{key}",
40
+ { default: default_translation }.merge(options))
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,9 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # @private
5
+ module NumericalityMatchers
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,75 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ module NumericalityMatchers
5
+ # @private
6
+ class ComparisonMatcher < ValidationMatcher
7
+ def initialize(numericality_matcher, value, operator)
8
+ unless numericality_matcher.respond_to? :diff_to_compare
9
+ raise ArgumentError, 'numericality_matcher is invalid'
10
+ end
11
+ @numericality_matcher = numericality_matcher
12
+ @value = value
13
+ @operator = operator
14
+ @message = nil
15
+ end
16
+
17
+ def for(attribute)
18
+ @attribute = attribute
19
+ self
20
+ end
21
+
22
+ def matches?(subject)
23
+ @subject = subject
24
+ all_bounds_correct?
25
+ end
26
+
27
+ def with_message(message)
28
+ @message = message
29
+ end
30
+
31
+ def comparison_description
32
+ "#{expectation} #{@value}"
33
+ end
34
+
35
+ private
36
+
37
+ def comparison_combos
38
+ allow = :allows_value_of
39
+ disallow = :disallows_value_of
40
+ checker_types =
41
+ case @operator
42
+ when :> then [allow, disallow, disallow]
43
+ when :>= then [allow, allow, disallow]
44
+ when :== then [disallow, allow, disallow]
45
+ when :< then [disallow, disallow, allow]
46
+ when :<= then [disallow, allow, allow]
47
+ end
48
+ diffs_to_compare.zip(checker_types)
49
+ end
50
+
51
+ def diffs_to_compare
52
+ diff = @numericality_matcher.diff_to_compare
53
+ [diff, 0, -diff]
54
+ end
55
+
56
+ def expectation
57
+ case @operator
58
+ when :> then "greater than"
59
+ when :>= then "greater than or equal to"
60
+ when :== then "equal to"
61
+ when :< then "less than"
62
+ when :<= then "less than or equal to"
63
+ end
64
+ end
65
+
66
+ def all_bounds_correct?
67
+ comparison_combos.all? do |diff, checker_type|
68
+ __send__(checker_type, @value + diff, @message)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,27 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ module NumericalityMatchers
5
+ # @private
6
+ class EvenNumberMatcher < NumericTypeMatcher
7
+ NON_EVEN_NUMBER_VALUE = 1
8
+
9
+ def initialize(attribute, options = {})
10
+ @attribute = attribute
11
+ @disallow_value_matcher = DisallowValueMatcher.new(NON_EVEN_NUMBER_VALUE).
12
+ for(@attribute).
13
+ with_message(:even)
14
+ end
15
+
16
+ def allowed_type
17
+ 'even numbers'
18
+ end
19
+
20
+ def diff_to_compare
21
+ 2
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,41 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ module NumericalityMatchers
5
+ # @private
6
+ class NumericTypeMatcher
7
+ def initialize
8
+ raise NotImplementedError
9
+ end
10
+
11
+ def matches?(subject)
12
+ @disallow_value_matcher.matches?(subject)
13
+ end
14
+
15
+ def with_message(message)
16
+ @disallow_value_matcher.with_message(message)
17
+ self
18
+ end
19
+
20
+ def allowed_type
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def diff_to_compare
25
+ raise NotImplementedError
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,27 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ module NumericalityMatchers
5
+ # @private
6
+ class OddNumberMatcher < NumericTypeMatcher
7
+ NON_ODD_NUMBER_VALUE = 2
8
+
9
+ def initialize(attribute, options = {})
10
+ @attribute = attribute
11
+ @disallow_value_matcher = DisallowValueMatcher.new(NON_ODD_NUMBER_VALUE).
12
+ for(@attribute).
13
+ with_message(:odd)
14
+ end
15
+
16
+ def allowed_type
17
+ 'odd numbers'
18
+ end
19
+
20
+ def diff_to_compare
21
+ 2
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ module NumericalityMatchers
5
+ # @private
6
+ class OnlyIntegerMatcher
7
+ NON_INTEGER_VALUE = 0.1
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 allowed_type
16
+ 'integers'
17
+ end
18
+
19
+ def diff_to_compare
20
+ 1
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,112 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # The `validate_absence_of` matcher tests the usage of the
5
+ # `validates_absence_of` validation.
6
+ #
7
+ # class Artillery
8
+ # include ActiveModel::Model
9
+ #
10
+ # validates_absence_of :arms
11
+ # end
12
+ #
13
+ # # RSpec
14
+ # describe Artillery do
15
+ # it { should validate_absence_of(:arms) }
16
+ # end
17
+ #
18
+ # # Test::Unit
19
+ # class ArtilleryTest < ActiveSupport::TestCase
20
+ # should validate_absence_of(:arms)
21
+ # end
22
+ #
23
+ # #### Qualifiers
24
+ #
25
+ # ##### with_message
26
+ #
27
+ # Use `with_message` if you are using a custom validation message.
28
+ #
29
+ # class Artillery
30
+ # include ActiveModel::Model
31
+ #
32
+ # validates_absence_of :arms, message: "We're fresh outta arms here, soldier!"
33
+ # end
34
+ #
35
+ # # RSpec
36
+ # describe Artillery do
37
+ # it do
38
+ # should validate_absence_of(:arms).
39
+ # with_message("We're fresh outta arms here, soldier!")
40
+ # end
41
+ # end
42
+ #
43
+ # # Test::Unit
44
+ # class ArtilleryTest < ActiveSupport::TestCase
45
+ # should validate_absence_of(:arms).
46
+ # with_message("We're fresh outta arms here, soldier!")
47
+ # end
48
+ #
49
+ # @return [ValidateAbsenceOfMatcher}
50
+ #
51
+ def validate_absence_of(attr)
52
+ ValidateAbsenceOfMatcher.new(attr)
53
+ end
54
+
55
+ # @private
56
+ class ValidateAbsenceOfMatcher < ValidationMatcher
57
+ def with_message(message)
58
+ @expected_message = message
59
+ self
60
+ end
61
+
62
+ def matches?(subject)
63
+ super(subject)
64
+ @expected_message ||= :present
65
+ disallows_value_of(value, @expected_message)
66
+ end
67
+
68
+ def description
69
+ "require #{@attribute} to not be set"
70
+ end
71
+
72
+ private
73
+
74
+ def value
75
+ if reflection
76
+ obj = reflection.klass.new
77
+ if collection?
78
+ [ obj ]
79
+ else
80
+ obj
81
+ end
82
+ elsif attribute_class == Fixnum
83
+ 1
84
+ elsif !attribute_class || attribute_class == String
85
+ 'an arbitrary value'
86
+ else
87
+ attribute_class.new
88
+ end
89
+ end
90
+
91
+ def attribute_class
92
+ @subject.class.respond_to?(:columns_hash) &&
93
+ @subject.class.columns_hash[@attribute].respond_to?(:klass) &&
94
+ @subject.class.columns_hash[@attribute].klass
95
+ end
96
+
97
+ def collection?
98
+ if reflection
99
+ [:has_many, :has_and_belongs_to_many].include?(reflection.macro)
100
+ else
101
+ false
102
+ end
103
+ end
104
+
105
+ def reflection
106
+ @subject.class.respond_to?(:reflect_on_association) &&
107
+ @subject.class.reflect_on_association(@attribute)
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,77 @@
1
+ module Shoulda
2
+ module Matchers
3
+ module ActiveModel
4
+ # The `validate_acceptance_of` matcher tests usage of the
5
+ # `validates_acceptance_of` validation.
6
+ #
7
+ # class Registration
8
+ # include ActiveModel::Model
9
+ #
10
+ # validates_acceptance_of :eula
11
+ # end
12
+ #
13
+ # # RSpec
14
+ # describe Registration do
15
+ # it { should validate_acceptance_of(:eula) }
16
+ # end
17
+ #
18
+ # # Test::Unit
19
+ # class RegistrationTest < ActiveSupport::TestCase
20
+ # should validate_acceptance_of(:eula)
21
+ # end
22
+ #
23
+ # #### Qualifiers
24
+ #
25
+ # ##### with_message
26
+ #
27
+ # Use `with_message` if you are using a custom validation message.
28
+ #
29
+ # class Registration
30
+ # include ActiveModel::Model
31
+ #
32
+ # validates_acceptance_of :terms_of_service,
33
+ # message: 'You must accept the terms of service'
34
+ # end
35
+ #
36
+ # # RSpec
37
+ # describe Registration do
38
+ # it do
39
+ # should validate_acceptance_of(:terms_of_service).
40
+ # with_message('You must accept the terms of service')
41
+ # end
42
+ # end
43
+ #
44
+ # # Test::Unit
45
+ # class RegistrationTest < ActiveSupport::TestCase
46
+ # should validate_acceptance_of(:terms_of_service).
47
+ # with_message('You must accept the terms of service')
48
+ # end
49
+ #
50
+ # @return [ValidateAcceptanceOfMatcher]
51
+ #
52
+ def validate_acceptance_of(attr)
53
+ ValidateAcceptanceOfMatcher.new(attr)
54
+ end
55
+
56
+ # @private
57
+ class ValidateAcceptanceOfMatcher < ValidationMatcher
58
+ def with_message(message)
59
+ if message
60
+ @expected_message = message
61
+ end
62
+ self
63
+ end
64
+
65
+ def matches?(subject)
66
+ super(subject)
67
+ @expected_message ||= :accepted
68
+ disallows_value_of(false, @expected_message)
69
+ end
70
+
71
+ def description
72
+ "require #{@attribute} to be accepted"
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end