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.
- checksums.yaml +7 -0
- data/.gitignore +12 -0
- data/.travis.yml +32 -0
- data/.yardopts +7 -0
- data/Appraisals +45 -0
- data/CONTRIBUTING.md +41 -0
- data/Gemfile +31 -0
- data/Gemfile.lock +166 -0
- data/MIT-LICENSE +22 -0
- data/NEWS.md +299 -0
- data/README.md +163 -0
- data/Rakefile +116 -0
- data/doc_config/gh-pages/index.html.erb +9 -0
- data/doc_config/yard/setup.rb +22 -0
- data/doc_config/yard/templates/default/fulldoc/html/css/bootstrap.css +5967 -0
- data/doc_config/yard/templates/default/fulldoc/html/css/full_list.css +12 -0
- data/doc_config/yard/templates/default/fulldoc/html/css/global.css +45 -0
- data/doc_config/yard/templates/default/fulldoc/html/css/solarized.css +69 -0
- data/doc_config/yard/templates/default/fulldoc/html/css/style.css +283 -0
- data/doc_config/yard/templates/default/fulldoc/html/full_list.erb +32 -0
- data/doc_config/yard/templates/default/fulldoc/html/full_list_class.erb +1 -0
- data/doc_config/yard/templates/default/fulldoc/html/full_list_method.erb +8 -0
- data/doc_config/yard/templates/default/fulldoc/html/js/app.js +300 -0
- data/doc_config/yard/templates/default/fulldoc/html/js/full_list.js +1 -0
- data/doc_config/yard/templates/default/fulldoc/html/js/jquery.stickyheaders.js +289 -0
- data/doc_config/yard/templates/default/fulldoc/html/js/underscore.min.js +6 -0
- data/doc_config/yard/templates/default/fulldoc/html/setup.rb +8 -0
- data/doc_config/yard/templates/default/layout/html/breadcrumb.erb +14 -0
- data/doc_config/yard/templates/default/layout/html/fonts.erb +1 -0
- data/doc_config/yard/templates/default/layout/html/layout.erb +23 -0
- data/doc_config/yard/templates/default/layout/html/search.erb +13 -0
- data/doc_config/yard/templates/default/layout/html/setup.rb +8 -0
- data/doc_config/yard/templates/default/method_details/html/source.erb +10 -0
- data/doc_config/yard/templates/default/module/html/box_info.erb +31 -0
- data/features/rails_integration.feature +113 -0
- data/features/step_definitions/rails_steps.rb +162 -0
- data/features/support/env.rb +5 -0
- data/gemfiles/3.0.gemfile +24 -0
- data/gemfiles/3.0.gemfile.lock +150 -0
- data/gemfiles/3.1.gemfile +27 -0
- data/gemfiles/3.1.gemfile.lock +173 -0
- data/gemfiles/3.2.gemfile +27 -0
- data/gemfiles/3.2.gemfile.lock +171 -0
- data/gemfiles/4.0.0.gemfile +28 -0
- data/gemfiles/4.0.0.gemfile.lock +172 -0
- data/gemfiles/4.0.1.gemfile +28 -0
- data/gemfiles/4.0.1.gemfile.lock +172 -0
- data/lib/shoulda-matchers.rb +1 -0
- data/lib/shoulda/matchers.rb +11 -0
- data/lib/shoulda/matchers/action_controller.rb +17 -0
- data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +64 -0
- data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +97 -0
- data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +81 -0
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +117 -0
- data/lib/shoulda/matchers/action_controller/rescue_from_matcher.rb +114 -0
- data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +154 -0
- data/lib/shoulda/matchers/action_controller/route_matcher.rb +116 -0
- data/lib/shoulda/matchers/action_controller/route_params.rb +48 -0
- data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +164 -0
- data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +296 -0
- data/lib/shoulda/matchers/active_model.rb +30 -0
- data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +167 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +314 -0
- data/lib/shoulda/matchers/active_model/disallow_value_matcher.rb +46 -0
- data/lib/shoulda/matchers/active_model/ensure_exclusion_of_matcher.rb +160 -0
- data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +417 -0
- data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +337 -0
- data/lib/shoulda/matchers/active_model/errors.rb +10 -0
- data/lib/shoulda/matchers/active_model/exception_message_finder.rb +58 -0
- data/lib/shoulda/matchers/active_model/have_secure_password_matcher.rb +92 -0
- data/lib/shoulda/matchers/active_model/helpers.rb +46 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers.rb +9 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/comparison_matcher.rb +75 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/even_number_matcher.rb +27 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher.rb +41 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher.rb +27 -0
- data/lib/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher.rb +26 -0
- data/lib/shoulda/matchers/active_model/validate_absence_of_matcher.rb +112 -0
- data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +77 -0
- data/lib/shoulda/matchers/active_model/validate_confirmation_of_matcher.rb +121 -0
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +380 -0
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +89 -0
- data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +372 -0
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +97 -0
- data/lib/shoulda/matchers/active_model/validation_message_finder.rb +69 -0
- data/lib/shoulda/matchers/active_record.rb +22 -0
- data/lib/shoulda/matchers/active_record/accept_nested_attributes_for_matcher.rb +204 -0
- data/lib/shoulda/matchers/active_record/association_matcher.rb +901 -0
- data/lib/shoulda/matchers/active_record/association_matchers.rb +9 -0
- data/lib/shoulda/matchers/active_record/association_matchers/counter_cache_matcher.rb +41 -0
- data/lib/shoulda/matchers/active_record/association_matchers/dependent_matcher.rb +41 -0
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflection.rb +81 -0
- data/lib/shoulda/matchers/active_record/association_matchers/model_reflector.rb +65 -0
- data/lib/shoulda/matchers/active_record/association_matchers/option_verifier.rb +94 -0
- data/lib/shoulda/matchers/active_record/association_matchers/order_matcher.rb +41 -0
- data/lib/shoulda/matchers/active_record/association_matchers/source_matcher.rb +41 -0
- data/lib/shoulda/matchers/active_record/association_matchers/through_matcher.rb +63 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +261 -0
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +149 -0
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +72 -0
- data/lib/shoulda/matchers/active_record/serialize_matcher.rb +181 -0
- data/lib/shoulda/matchers/assertion_error.rb +19 -0
- data/lib/shoulda/matchers/error.rb +6 -0
- data/lib/shoulda/matchers/integrations/rspec.rb +20 -0
- data/lib/shoulda/matchers/integrations/test_unit.rb +30 -0
- data/lib/shoulda/matchers/rails_shim.rb +50 -0
- data/lib/shoulda/matchers/version.rb +6 -0
- data/lib/shoulda/matchers/warn.rb +8 -0
- data/shoulda-matchers.gemspec +23 -0
- data/spec/shoulda/matchers/action_controller/filter_param_matcher_spec.rb +22 -0
- data/spec/shoulda/matchers/action_controller/redirect_to_matcher_spec.rb +42 -0
- data/spec/shoulda/matchers/action_controller/render_template_matcher_spec.rb +78 -0
- data/spec/shoulda/matchers/action_controller/render_with_layout_matcher_spec.rb +63 -0
- data/spec/shoulda/matchers/action_controller/rescue_from_matcher_spec.rb +63 -0
- data/spec/shoulda/matchers/action_controller/respond_with_matcher_spec.rb +31 -0
- data/spec/shoulda/matchers/action_controller/route_matcher_spec.rb +70 -0
- data/spec/shoulda/matchers/action_controller/route_params_spec.rb +30 -0
- data/spec/shoulda/matchers/action_controller/set_session_matcher_spec.rb +51 -0
- data/spec/shoulda/matchers/action_controller/set_the_flash_matcher_spec.rb +153 -0
- data/spec/shoulda/matchers/active_model/allow_mass_assignment_of_matcher_spec.rb +111 -0
- data/spec/shoulda/matchers/active_model/allow_value_matcher_spec.rb +170 -0
- data/spec/shoulda/matchers/active_model/disallow_value_matcher_spec.rb +81 -0
- data/spec/shoulda/matchers/active_model/ensure_exclusion_of_matcher_spec.rb +95 -0
- data/spec/shoulda/matchers/active_model/ensure_inclusion_of_matcher_spec.rb +320 -0
- data/spec/shoulda/matchers/active_model/ensure_length_of_matcher_spec.rb +166 -0
- data/spec/shoulda/matchers/active_model/exception_message_finder_spec.rb +111 -0
- data/spec/shoulda/matchers/active_model/have_secure_password_matcher_spec.rb +20 -0
- data/spec/shoulda/matchers/active_model/helpers_spec.rb +158 -0
- data/spec/shoulda/matchers/active_model/numericality_matchers/comparison_matcher_spec.rb +169 -0
- data/spec/shoulda/matchers/active_model/numericality_matchers/even_number_matcher_spec.rb +59 -0
- data/spec/shoulda/matchers/active_model/numericality_matchers/odd_number_matcher_spec.rb +59 -0
- data/spec/shoulda/matchers/active_model/numericality_matchers/only_integer_matcher_spec.rb +57 -0
- data/spec/shoulda/matchers/active_model/validate_absence_of_matcher_spec.rb +139 -0
- data/spec/shoulda/matchers/active_model/validate_acceptance_of_matcher_spec.rb +41 -0
- data/spec/shoulda/matchers/active_model/validate_confirmation_of_matcher_spec.rb +47 -0
- data/spec/shoulda/matchers/active_model/validate_numericality_of_matcher_spec.rb +331 -0
- data/spec/shoulda/matchers/active_model/validate_presence_of_matcher_spec.rb +180 -0
- data/spec/shoulda/matchers/active_model/validate_uniqueness_of_matcher_spec.rb +398 -0
- data/spec/shoulda/matchers/active_model/validation_message_finder_spec.rb +127 -0
- data/spec/shoulda/matchers/active_record/accept_nested_attributes_for_matcher_spec.rb +107 -0
- data/spec/shoulda/matchers/active_record/association_matcher_spec.rb +860 -0
- data/spec/shoulda/matchers/active_record/association_matchers/model_reflection_spec.rb +247 -0
- data/spec/shoulda/matchers/active_record/have_db_column_matcher_spec.rb +111 -0
- data/spec/shoulda/matchers/active_record/have_db_index_matcher_spec.rb +78 -0
- data/spec/shoulda/matchers/active_record/have_readonly_attributes_matcher_spec.rb +41 -0
- data/spec/shoulda/matchers/active_record/serialize_matcher_spec.rb +86 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/support/active_model_versions.rb +13 -0
- data/spec/support/active_resource_builder.rb +29 -0
- data/spec/support/activemodel_helpers.rb +19 -0
- data/spec/support/capture_helpers.rb +19 -0
- data/spec/support/class_builder.rb +42 -0
- data/spec/support/controller_builder.rb +74 -0
- data/spec/support/fail_with_message_including_matcher.rb +33 -0
- data/spec/support/fail_with_message_matcher.rb +32 -0
- data/spec/support/i18n_faker.rb +10 -0
- data/spec/support/mailer_builder.rb +10 -0
- data/spec/support/model_builder.rb +81 -0
- data/spec/support/rails_versions.rb +18 -0
- data/spec/support/shared_examples/numerical_submatcher.rb +19 -0
- data/spec/support/shared_examples/numerical_type_submatcher.rb +17 -0
- data/spec/support/test_application.rb +120 -0
- data/yard.watchr +5 -0
- metadata +281 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel
|
4
|
+
# @private
|
5
|
+
class DisallowValueMatcher
|
6
|
+
def initialize(value)
|
7
|
+
@allow_matcher = AllowValueMatcher.new(value)
|
8
|
+
end
|
9
|
+
|
10
|
+
def matches?(subject)
|
11
|
+
!@allow_matcher.matches?(subject)
|
12
|
+
end
|
13
|
+
|
14
|
+
def for(attribute)
|
15
|
+
@allow_matcher.for(attribute)
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def on(context)
|
20
|
+
@allow_matcher.on(context)
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def with_message(message, options={})
|
25
|
+
@allow_matcher.with_message(message, options)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def failure_message
|
30
|
+
@allow_matcher.failure_message_when_negated
|
31
|
+
end
|
32
|
+
alias failure_message_for_should failure_message
|
33
|
+
|
34
|
+
def failure_message_when_negated
|
35
|
+
@allow_matcher.failure_message
|
36
|
+
end
|
37
|
+
alias failure_message_for_should_not failure_message_when_negated
|
38
|
+
|
39
|
+
def strict
|
40
|
+
@allow_matcher.strict
|
41
|
+
self
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel
|
4
|
+
# The `ensure_exclusion_of` matcher tests usage of the
|
5
|
+
# `validates_exclusion_of` validation, asserting that an attribute cannot
|
6
|
+
# take a blacklist of values, and inversely, can take values outside of
|
7
|
+
# this list.
|
8
|
+
#
|
9
|
+
# #### Qualifiers
|
10
|
+
#
|
11
|
+
# `in_array` or `in_range` are used to test usage of the `:in` option,
|
12
|
+
# and so one must be used.
|
13
|
+
#
|
14
|
+
# ##### in_array
|
15
|
+
#
|
16
|
+
# Use `in_array` if your blacklist is an array of values.
|
17
|
+
#
|
18
|
+
# class Game
|
19
|
+
# include ActiveModel::Model
|
20
|
+
#
|
21
|
+
# validates_exclusion_of :supported_os, in: ['Mac', 'Linux']
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # RSpec
|
25
|
+
# describe Game do
|
26
|
+
# it { should ensure_exclusion_of(:supported_os).in_array(['Mac', 'Linux']) }
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # Test::Unit
|
30
|
+
# class GameTest < ActiveSupport::TestCase
|
31
|
+
# should ensure_exclusion_of(:supported_os).in_array(['Mac', 'Linux'])
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# ##### in_range
|
35
|
+
#
|
36
|
+
# Use `in_range` if your blacklist is a range of values.
|
37
|
+
#
|
38
|
+
# class Game
|
39
|
+
# include ActiveModel::Model
|
40
|
+
#
|
41
|
+
# validates_exclusion_of :supported_os, in: ['Mac', 'Linux']
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# # RSpec
|
45
|
+
# describe Game do
|
46
|
+
# it { should ensure_exclusion_of(:floors_with_enemies).in_range(5..8) }
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
# # Test::Unit
|
50
|
+
# class GameTest < ActiveSupport::TestCase
|
51
|
+
# should ensure_exclusion_of(:floors_with_enemies).in_range(5..8)
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# ##### with_message
|
55
|
+
#
|
56
|
+
# Use `with_message` if you are using a custom validation message.
|
57
|
+
#
|
58
|
+
# class Game
|
59
|
+
# validates_exclusion_of :weapon,
|
60
|
+
# in: ['pistol', 'paintball gun', 'stick'],
|
61
|
+
# message: 'You chose a puny weapon'
|
62
|
+
# end
|
63
|
+
#
|
64
|
+
# # RSpec
|
65
|
+
# describe Game do
|
66
|
+
# it do
|
67
|
+
# should ensure_exclusion_of(:weapon).
|
68
|
+
# in_array(['pistol', 'paintball gun', 'stick']).
|
69
|
+
# with_message('You chose a puny weapon')
|
70
|
+
# end
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# # Test::Unit
|
74
|
+
# class GameTest < ActiveSupport::TestCase
|
75
|
+
# should ensure_exclusion_of(:weapon).
|
76
|
+
# in_array(['pistol', 'paintball gun', 'stick']).
|
77
|
+
# with_message('You chose a puny weapon')
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# @return [EnsureExclusionOfMatcher]
|
81
|
+
#
|
82
|
+
def ensure_exclusion_of(attr)
|
83
|
+
EnsureExclusionOfMatcher.new(attr)
|
84
|
+
end
|
85
|
+
|
86
|
+
# @private
|
87
|
+
class EnsureExclusionOfMatcher < ValidationMatcher
|
88
|
+
def in_array(array)
|
89
|
+
@array = array
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
def in_range(range)
|
94
|
+
@range = range
|
95
|
+
@minimum = range.first
|
96
|
+
@maximum = range.max
|
97
|
+
self
|
98
|
+
end
|
99
|
+
|
100
|
+
def with_message(message)
|
101
|
+
@expected_message = message if message
|
102
|
+
self
|
103
|
+
end
|
104
|
+
|
105
|
+
def description
|
106
|
+
"ensure exclusion of #{@attribute} in #{inspect_message}"
|
107
|
+
end
|
108
|
+
|
109
|
+
def matches?(subject)
|
110
|
+
super(subject)
|
111
|
+
|
112
|
+
if @range
|
113
|
+
allows_lower_value &&
|
114
|
+
disallows_minimum_value &&
|
115
|
+
allows_higher_value &&
|
116
|
+
disallows_maximum_value
|
117
|
+
elsif @array
|
118
|
+
disallows_all_values_in_array?
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
private
|
123
|
+
|
124
|
+
def disallows_all_values_in_array?
|
125
|
+
@array.all? do |value|
|
126
|
+
disallows_value_of(value, expected_message)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def allows_lower_value
|
131
|
+
@minimum == 0 || allows_value_of(@minimum - 1, expected_message)
|
132
|
+
end
|
133
|
+
|
134
|
+
def allows_higher_value
|
135
|
+
allows_value_of(@maximum + 1, expected_message)
|
136
|
+
end
|
137
|
+
|
138
|
+
def disallows_minimum_value
|
139
|
+
disallows_value_of(@minimum, expected_message)
|
140
|
+
end
|
141
|
+
|
142
|
+
def disallows_maximum_value
|
143
|
+
disallows_value_of(@maximum, expected_message)
|
144
|
+
end
|
145
|
+
|
146
|
+
def expected_message
|
147
|
+
@expected_message || :exclusion
|
148
|
+
end
|
149
|
+
|
150
|
+
def inspect_message
|
151
|
+
if @range
|
152
|
+
@range.inspect
|
153
|
+
else
|
154
|
+
@array.inspect
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
@@ -0,0 +1,417 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel
|
4
|
+
# The `ensure_inclusion_of` matcher tests usage of the
|
5
|
+
# `validates_inclusion_of` validation, asserting that an attribute can
|
6
|
+
# take a whitelist of values and cannot take values outside of this list.
|
7
|
+
#
|
8
|
+
# #### Qualifiers
|
9
|
+
#
|
10
|
+
# `in_array` or `in_range` are used to test usage of the `:in` option,
|
11
|
+
# and so one must be used.
|
12
|
+
#
|
13
|
+
# ##### in_array
|
14
|
+
#
|
15
|
+
# Use `in_array` if your whitelist is an array of values.
|
16
|
+
#
|
17
|
+
# class Issue
|
18
|
+
# include ActiveModel::Model
|
19
|
+
#
|
20
|
+
# validates_inclusion_of :state, in: %w(open resolved unresolved)
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # RSpec
|
24
|
+
# describe Issue do
|
25
|
+
# it { should ensure_inclusion_of(:state).in_array(%w(open resolved unresolved)) }
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # Test::Unit
|
29
|
+
# class IssueTest < ActiveSupport::TestCase
|
30
|
+
# should ensure_inclusion_of(:state).in_array(%w(open resolved unresolved))
|
31
|
+
# end
|
32
|
+
#
|
33
|
+
# ##### in_range
|
34
|
+
#
|
35
|
+
# Use `in_range` if your whitelist is a range of values.
|
36
|
+
#
|
37
|
+
# class Issue
|
38
|
+
# include ActiveModel::Model
|
39
|
+
#
|
40
|
+
# validates_inclusion_of :priority, in: 1..5
|
41
|
+
# end
|
42
|
+
#
|
43
|
+
# # RSpec
|
44
|
+
# describe Issue do
|
45
|
+
# it { should ensure_inclusion_of(:state).in_range(1..5) }
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# # Test::Unit
|
49
|
+
# class IssueTest < ActiveSupport::TestCase
|
50
|
+
# should ensure_inclusion_of(:state).in_range(1..5)
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# ##### with_message
|
54
|
+
#
|
55
|
+
# Use `with_message` if you are using a custom validation message.
|
56
|
+
#
|
57
|
+
# class Issue
|
58
|
+
# include ActiveModel::Model
|
59
|
+
#
|
60
|
+
# validates_inclusion_of :severity,
|
61
|
+
# in: %w(low medium high),
|
62
|
+
# message: 'Severity must be low, medium, or high'
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# # RSpec
|
66
|
+
# describe Issue do
|
67
|
+
# it do
|
68
|
+
# should ensure_inclusion_of(:severity).
|
69
|
+
# in_array(%w(low medium high)).
|
70
|
+
# with_message('Severity must be low, medium, or high')
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
#
|
74
|
+
# # Test::Unit
|
75
|
+
# class IssueTest < ActiveSupport::TestCase
|
76
|
+
# should ensure_inclusion_of(:severity).
|
77
|
+
# in_array(%w(low medium high)).
|
78
|
+
# with_message('Severity must be low, medium, or high')
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# ##### with_low_message
|
82
|
+
#
|
83
|
+
# Use `with_low_message` if you have a custom validation message for when
|
84
|
+
# a given value is too low.
|
85
|
+
#
|
86
|
+
# class Person
|
87
|
+
# include ActiveModel::Model
|
88
|
+
#
|
89
|
+
# validate :age_must_be_valid
|
90
|
+
#
|
91
|
+
# private
|
92
|
+
#
|
93
|
+
# def age_must_be_valid
|
94
|
+
# if age < 65
|
95
|
+
# self.errors.add :age, 'You do not receive any benefits'
|
96
|
+
# end
|
97
|
+
# end
|
98
|
+
# end
|
99
|
+
#
|
100
|
+
# # RSpec
|
101
|
+
# describe Person do
|
102
|
+
# it do
|
103
|
+
# should ensure_inclusion_of(:age).
|
104
|
+
# in_range(0..65).
|
105
|
+
# with_low_message('You do not receive any benefits')
|
106
|
+
# end
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# # Test::Unit
|
110
|
+
# class PersonTest < ActiveSupport::TestCase
|
111
|
+
# should ensure_inclusion_of(:age).
|
112
|
+
# in_range(0..65).
|
113
|
+
# with_low_message('You do not receive any benefits')
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# ##### with_high_message
|
117
|
+
#
|
118
|
+
# Use `with_high_message` if you have a custom validation message for
|
119
|
+
# when a given value is too high.
|
120
|
+
#
|
121
|
+
# class Person
|
122
|
+
# include ActiveModel::Model
|
123
|
+
#
|
124
|
+
# validate :age_must_be_valid
|
125
|
+
#
|
126
|
+
# private
|
127
|
+
#
|
128
|
+
# def age_must_be_valid
|
129
|
+
# if age > 21
|
130
|
+
# self.errors.add :age, "You're too old for this stuff"
|
131
|
+
# end
|
132
|
+
# end
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# # RSpec
|
136
|
+
# describe Person do
|
137
|
+
# it do
|
138
|
+
# should ensure_inclusion_of(:age).
|
139
|
+
# in_range(0..21).
|
140
|
+
# with_high_message("You're too old for this stuff")
|
141
|
+
# end
|
142
|
+
# end
|
143
|
+
#
|
144
|
+
# # Test::Unit
|
145
|
+
# class PersonTest < ActiveSupport::TestCase
|
146
|
+
# should ensure_inclusion_of(:age).
|
147
|
+
# in_range(0..21).
|
148
|
+
# with_high_message("You're too old for this stuff")
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# ##### allow_nil
|
152
|
+
#
|
153
|
+
# Use `allow_nil` to assert that the attribute allows nil.
|
154
|
+
#
|
155
|
+
# class Issue
|
156
|
+
# include ActiveModel::Model
|
157
|
+
#
|
158
|
+
# validates_presence_of :state
|
159
|
+
# validates_inclusion_of :state,
|
160
|
+
# in: %w(open resolved unresolved),
|
161
|
+
# allow_nil: true
|
162
|
+
# end
|
163
|
+
#
|
164
|
+
# # RSpec
|
165
|
+
# describe Issue do
|
166
|
+
# it do
|
167
|
+
# should ensure_inclusion_of(:state).
|
168
|
+
# in_array(%w(open resolved unresolved)).
|
169
|
+
# allow_nil
|
170
|
+
# end
|
171
|
+
# end
|
172
|
+
#
|
173
|
+
# # Test::Unit
|
174
|
+
# class IssueTest < ActiveSupport::TestCase
|
175
|
+
# should ensure_inclusion_of(:state).
|
176
|
+
# in_array(%w(open resolved unresolved)).
|
177
|
+
# allow_nil
|
178
|
+
# end
|
179
|
+
#
|
180
|
+
# ##### allow_blank
|
181
|
+
#
|
182
|
+
# Use `allow_blank` to assert that the attribute allows blank.
|
183
|
+
#
|
184
|
+
# class Issue
|
185
|
+
# include ActiveModel::Model
|
186
|
+
#
|
187
|
+
# validates_presence_of :state
|
188
|
+
# validates_inclusion_of :state,
|
189
|
+
# in: %w(open resolved unresolved),
|
190
|
+
# allow_blank: true
|
191
|
+
# end
|
192
|
+
#
|
193
|
+
# # RSpec
|
194
|
+
# describe Issue do
|
195
|
+
# it do
|
196
|
+
# should ensure_inclusion_of(:state).
|
197
|
+
# in_array(%w(open resolved unresolved)).
|
198
|
+
# allow_blank
|
199
|
+
# end
|
200
|
+
# end
|
201
|
+
#
|
202
|
+
# # Test::Unit
|
203
|
+
# class IssueTest < ActiveSupport::TestCase
|
204
|
+
# should ensure_inclusion_of(:state).
|
205
|
+
# in_array(%w(open resolved unresolved)).
|
206
|
+
# allow_blank
|
207
|
+
# end
|
208
|
+
#
|
209
|
+
# @return [EnsureInclusionOfMatcher]
|
210
|
+
#
|
211
|
+
def ensure_inclusion_of(attr)
|
212
|
+
EnsureInclusionOfMatcher.new(attr)
|
213
|
+
end
|
214
|
+
|
215
|
+
# @private
|
216
|
+
class EnsureInclusionOfMatcher < ValidationMatcher
|
217
|
+
ARBITRARY_OUTSIDE_STRING = 'shouldamatchersteststring'
|
218
|
+
ARBITRARY_OUTSIDE_FIXNUM = 123456789
|
219
|
+
ARBITRARY_OUTSIDE_DECIMAL = 0.123456789
|
220
|
+
BOOLEAN_ALLOWS_BOOLEAN_MESSAGE = <<EOT
|
221
|
+
You are using `ensure_inclusion_of` to assert that a boolean column allows
|
222
|
+
boolean values and disallows non-boolean ones. Assuming you are using
|
223
|
+
`validates_format_of` in your model, be aware that it is not possible to fully
|
224
|
+
test this, and in fact the validation is superfluous, as boolean columns will
|
225
|
+
automatically convert non-boolean values to boolean ones. Hence, you should
|
226
|
+
consider removing this test and the corresponding validation.
|
227
|
+
EOT
|
228
|
+
BOOLEAN_ALLOWS_NIL_MESSAGE = <<EOT
|
229
|
+
You are using `ensure_inclusion_of` to assert that a boolean column allows nil.
|
230
|
+
Be aware that it is not possible to fully test this, as anything other than
|
231
|
+
true, false or nil will be converted to false. Hence, you should consider
|
232
|
+
removing this test and the corresponding validation.
|
233
|
+
EOT
|
234
|
+
BOOLEAN_ALLOWS_NIL_WITH_NOT_NULL_MESSAGE = <<EOT
|
235
|
+
You have specified that your model's #{@attribute} should ensure inclusion of nil.
|
236
|
+
However, #{@attribute} is a boolean column which does not allow null values.
|
237
|
+
Hence, this test will fail and there is no way to make it pass.
|
238
|
+
EOT
|
239
|
+
|
240
|
+
def initialize(attribute)
|
241
|
+
super(attribute)
|
242
|
+
@options = {}
|
243
|
+
end
|
244
|
+
|
245
|
+
def in_array(array)
|
246
|
+
@array = array
|
247
|
+
self
|
248
|
+
end
|
249
|
+
|
250
|
+
def in_range(range)
|
251
|
+
@range = range
|
252
|
+
@minimum = range.first
|
253
|
+
@maximum = range.max
|
254
|
+
self
|
255
|
+
end
|
256
|
+
|
257
|
+
def allow_blank(allow_blank = true)
|
258
|
+
@options[:allow_blank] = allow_blank
|
259
|
+
self
|
260
|
+
end
|
261
|
+
|
262
|
+
def allow_nil(allow_nil = true)
|
263
|
+
@options[:allow_nil] = allow_nil
|
264
|
+
self
|
265
|
+
end
|
266
|
+
|
267
|
+
def with_message(message)
|
268
|
+
if message
|
269
|
+
@low_message = message
|
270
|
+
@high_message = message
|
271
|
+
end
|
272
|
+
self
|
273
|
+
end
|
274
|
+
|
275
|
+
def with_low_message(message)
|
276
|
+
@low_message = message if message
|
277
|
+
self
|
278
|
+
end
|
279
|
+
|
280
|
+
def with_high_message(message)
|
281
|
+
@high_message = message if message
|
282
|
+
self
|
283
|
+
end
|
284
|
+
|
285
|
+
def description
|
286
|
+
"ensure inclusion of #{@attribute} in #{inspect_message}"
|
287
|
+
end
|
288
|
+
|
289
|
+
def matches?(subject)
|
290
|
+
super(subject)
|
291
|
+
|
292
|
+
if @range
|
293
|
+
@low_message ||= :inclusion
|
294
|
+
@high_message ||= :inclusion
|
295
|
+
|
296
|
+
disallows_lower_value &&
|
297
|
+
allows_minimum_value &&
|
298
|
+
disallows_higher_value &&
|
299
|
+
allows_maximum_value
|
300
|
+
elsif @array
|
301
|
+
if allows_all_values_in_array? && allows_blank_value? && allows_nil_value? && disallows_value_outside_of_array?
|
302
|
+
true
|
303
|
+
else
|
304
|
+
@failure_message = "#{@array} doesn't match array in validation"
|
305
|
+
false
|
306
|
+
end
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
private
|
311
|
+
|
312
|
+
def allows_blank_value?
|
313
|
+
if @options.key?(:allow_blank)
|
314
|
+
blank_values = ['', ' ', "\n", "\r", "\t", "\f"]
|
315
|
+
@options[:allow_blank] == blank_values.all? { |value| allows_value_of(value) }
|
316
|
+
else
|
317
|
+
true
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
def allows_nil_value?
|
322
|
+
if @options.key?(:allow_nil)
|
323
|
+
@options[:allow_nil] == allows_value_of(nil)
|
324
|
+
else
|
325
|
+
true
|
326
|
+
end
|
327
|
+
end
|
328
|
+
|
329
|
+
def inspect_message
|
330
|
+
@range.nil? ? @array.inspect : @range.inspect
|
331
|
+
end
|
332
|
+
|
333
|
+
def allows_all_values_in_array?
|
334
|
+
@array.all? do |value|
|
335
|
+
allows_value_of(value, @low_message)
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def disallows_lower_value
|
340
|
+
@minimum == 0 || disallows_value_of(@minimum - 1, @low_message)
|
341
|
+
end
|
342
|
+
|
343
|
+
def disallows_higher_value
|
344
|
+
disallows_value_of(@maximum + 1, @high_message)
|
345
|
+
end
|
346
|
+
|
347
|
+
def allows_minimum_value
|
348
|
+
allows_value_of(@minimum, @low_message)
|
349
|
+
end
|
350
|
+
|
351
|
+
def allows_maximum_value
|
352
|
+
allows_value_of(@maximum, @high_message)
|
353
|
+
end
|
354
|
+
|
355
|
+
def disallows_value_outside_of_array?
|
356
|
+
if attribute_column.type == :boolean
|
357
|
+
case @array
|
358
|
+
when [true, false]
|
359
|
+
Shoulda::Matchers.warn BOOLEAN_ALLOWS_BOOLEAN_MESSAGE
|
360
|
+
return true
|
361
|
+
when [nil]
|
362
|
+
if attribute_column.null
|
363
|
+
Shoulda::Matchers.warn BOOLEAN_ALLOWS_NIL_MESSAGE
|
364
|
+
return true
|
365
|
+
else
|
366
|
+
raise NonNullableBooleanError, BOOLEAN_ALLOWS_NIL_WITH_NOT_NULL_MESSAGE
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
!allows_value_of(*values_outside_of_array)
|
372
|
+
end
|
373
|
+
|
374
|
+
def values_outside_of_array
|
375
|
+
if !(@array & outside_values).empty?
|
376
|
+
raise CouldNotDetermineValueOutsideOfArray
|
377
|
+
else
|
378
|
+
outside_values
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def outside_values
|
383
|
+
case attribute_column.type
|
384
|
+
when :boolean
|
385
|
+
boolean_outside_values
|
386
|
+
when :integer, :float
|
387
|
+
[ARBITRARY_OUTSIDE_FIXNUM]
|
388
|
+
when :decimal
|
389
|
+
[ARBITRARY_OUTSIDE_DECIMAL]
|
390
|
+
else
|
391
|
+
[ARBITRARY_OUTSIDE_STRING]
|
392
|
+
end
|
393
|
+
end
|
394
|
+
|
395
|
+
def boolean_outside_values
|
396
|
+
values = []
|
397
|
+
|
398
|
+
values << case @array
|
399
|
+
when [true] then false
|
400
|
+
when [false] then true
|
401
|
+
else raise CouldNotDetermineValueOutsideOfArray
|
402
|
+
end
|
403
|
+
|
404
|
+
if attribute_column.null
|
405
|
+
values << nil
|
406
|
+
end
|
407
|
+
|
408
|
+
values
|
409
|
+
end
|
410
|
+
|
411
|
+
def attribute_column
|
412
|
+
@subject.class.columns_hash[@attribute.to_s]
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
416
|
+
end
|
417
|
+
end
|