mcmire-shoulda-matchers 2.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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,30 @@
|
|
1
|
+
require 'shoulda/matchers/active_model/helpers'
|
2
|
+
require 'shoulda/matchers/active_model/validation_matcher'
|
3
|
+
require 'shoulda/matchers/active_model/validation_message_finder'
|
4
|
+
require 'shoulda/matchers/active_model/exception_message_finder'
|
5
|
+
require 'shoulda/matchers/active_model/allow_value_matcher'
|
6
|
+
require 'shoulda/matchers/active_model/disallow_value_matcher'
|
7
|
+
require 'shoulda/matchers/active_model/ensure_length_of_matcher'
|
8
|
+
require 'shoulda/matchers/active_model/ensure_inclusion_of_matcher'
|
9
|
+
require 'shoulda/matchers/active_model/ensure_exclusion_of_matcher'
|
10
|
+
require 'shoulda/matchers/active_model/validate_absence_of_matcher'
|
11
|
+
require 'shoulda/matchers/active_model/validate_presence_of_matcher'
|
12
|
+
require 'shoulda/matchers/active_model/validate_uniqueness_of_matcher'
|
13
|
+
require 'shoulda/matchers/active_model/validate_acceptance_of_matcher'
|
14
|
+
require 'shoulda/matchers/active_model/validate_confirmation_of_matcher'
|
15
|
+
require 'shoulda/matchers/active_model/validate_numericality_of_matcher'
|
16
|
+
require 'shoulda/matchers/active_model/numericality_matchers/numeric_type_matcher'
|
17
|
+
require 'shoulda/matchers/active_model/numericality_matchers/comparison_matcher'
|
18
|
+
require 'shoulda/matchers/active_model/numericality_matchers/odd_number_matcher'
|
19
|
+
require 'shoulda/matchers/active_model/numericality_matchers/even_number_matcher'
|
20
|
+
require 'shoulda/matchers/active_model/numericality_matchers/only_integer_matcher'
|
21
|
+
require 'shoulda/matchers/active_model/allow_mass_assignment_of_matcher'
|
22
|
+
require 'shoulda/matchers/active_model/errors'
|
23
|
+
require 'shoulda/matchers/active_model/have_secure_password_matcher'
|
24
|
+
|
25
|
+
module Shoulda
|
26
|
+
module Matchers
|
27
|
+
module ActiveModel
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel
|
4
|
+
# The `allow_mass_assignment_of` matcher tests usage of Rails 3's
|
5
|
+
# `attr_accessible` and `attr_protected` macros, asserting that an
|
6
|
+
# attribute in your model is contained in either the whitelist or
|
7
|
+
# blacklist and thus can or cannot be set via mass assignment.
|
8
|
+
#
|
9
|
+
# class Post
|
10
|
+
# include ActiveModel::Model
|
11
|
+
#
|
12
|
+
# attr_accessible :title
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# class User
|
16
|
+
# include ActiveModel::Model
|
17
|
+
#
|
18
|
+
# attr_protected :encrypted_password
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# # RSpec
|
22
|
+
# describe Post do
|
23
|
+
# it { should allow_mass_assignment_of(:title) }
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# describe User do
|
27
|
+
# it { should_not allow_mass_assignment_of(:encrypted_password) }
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# # Test::Unit
|
31
|
+
# class PostTest < ActiveSupport::TestCase
|
32
|
+
# should allow_mass_assignment_of(:title)
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# class UserTest < ActiveSupport::TestCase
|
36
|
+
# should_not allow_mass_assignment_of(:encrypted_password)
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# #### Qualifiers
|
40
|
+
#
|
41
|
+
# ##### as
|
42
|
+
#
|
43
|
+
# Use `as` if your mass-assignment rules apply only under a certain role
|
44
|
+
# (Rails >= 3.1 only).
|
45
|
+
#
|
46
|
+
# class Post
|
47
|
+
# include ActiveModel::Model
|
48
|
+
#
|
49
|
+
# attr_accessible :title, as: :admin
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# # RSpec
|
53
|
+
# describe Post do
|
54
|
+
# it { should allow_mass_assignment_of(:title).as(:admin) }
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# # Test::Unit
|
58
|
+
# class PostTest < ActiveSupport::TestCase
|
59
|
+
# should allow_mass_assignment_of(:title).as(:admin)
|
60
|
+
# end
|
61
|
+
#
|
62
|
+
# @return [AllowMassAssignmentOfMatcher]
|
63
|
+
#
|
64
|
+
def allow_mass_assignment_of(value)
|
65
|
+
AllowMassAssignmentOfMatcher.new(value)
|
66
|
+
end
|
67
|
+
|
68
|
+
# @private
|
69
|
+
class AllowMassAssignmentOfMatcher
|
70
|
+
attr_reader :failure_message, :failure_message_when_negated
|
71
|
+
|
72
|
+
alias failure_message_for_should failure_message
|
73
|
+
alias failure_message_for_should_not failure_message_when_negated
|
74
|
+
|
75
|
+
def initialize(attribute)
|
76
|
+
@attribute = attribute.to_s
|
77
|
+
@options = {}
|
78
|
+
end
|
79
|
+
|
80
|
+
def as(role)
|
81
|
+
if active_model_less_than_3_1?
|
82
|
+
raise 'You can specify role only in Rails 3.1 or greater'
|
83
|
+
end
|
84
|
+
@options[:role] = role
|
85
|
+
self
|
86
|
+
end
|
87
|
+
|
88
|
+
def matches?(subject)
|
89
|
+
@subject = subject
|
90
|
+
if attr_mass_assignable?
|
91
|
+
if whitelisting?
|
92
|
+
@failure_message_when_negated = "#{@attribute} was made accessible"
|
93
|
+
else
|
94
|
+
if protected_attributes.empty?
|
95
|
+
@failure_message_when_negated = 'no attributes were protected'
|
96
|
+
else
|
97
|
+
@failure_message_when_negated = "#{class_name} is protecting " <<
|
98
|
+
"#{protected_attributes.to_a.to_sentence}, " <<
|
99
|
+
"but not #{@attribute}."
|
100
|
+
end
|
101
|
+
end
|
102
|
+
true
|
103
|
+
else
|
104
|
+
if whitelisting?
|
105
|
+
@failure_message = "Expected #{@attribute} to be accessible"
|
106
|
+
else
|
107
|
+
@failure_message = "Did not expect #{@attribute} to be protected"
|
108
|
+
end
|
109
|
+
false
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def description
|
114
|
+
[base_description, role_description].compact.join(' ')
|
115
|
+
end
|
116
|
+
|
117
|
+
private
|
118
|
+
|
119
|
+
def base_description
|
120
|
+
"allow mass assignment of #{@attribute}"
|
121
|
+
end
|
122
|
+
|
123
|
+
def role_description
|
124
|
+
if role != :default
|
125
|
+
"as #{role}"
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def role
|
130
|
+
@options[:role] || :default
|
131
|
+
end
|
132
|
+
|
133
|
+
def protected_attributes
|
134
|
+
@protected_attributes ||= (@subject.class.protected_attributes || [])
|
135
|
+
end
|
136
|
+
|
137
|
+
def accessible_attributes
|
138
|
+
@accessible_attributes ||= (@subject.class.accessible_attributes || [])
|
139
|
+
end
|
140
|
+
|
141
|
+
def whitelisting?
|
142
|
+
authorizer.kind_of?(::ActiveModel::MassAssignmentSecurity::WhiteList)
|
143
|
+
end
|
144
|
+
|
145
|
+
def attr_mass_assignable?
|
146
|
+
!authorizer.deny?(@attribute)
|
147
|
+
end
|
148
|
+
|
149
|
+
def authorizer
|
150
|
+
if active_model_less_than_3_1?
|
151
|
+
@subject.class.active_authorizer
|
152
|
+
else
|
153
|
+
@subject.class.active_authorizer[role]
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def class_name
|
158
|
+
@subject.class.name
|
159
|
+
end
|
160
|
+
|
161
|
+
def active_model_less_than_3_1?
|
162
|
+
::ActiveModel::VERSION::STRING.to_f < 3.1
|
163
|
+
end
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
@@ -0,0 +1,314 @@
|
|
1
|
+
module Shoulda
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel
|
4
|
+
# The `allow_value` matcher is used to test that an attribute of a model
|
5
|
+
# can or cannot be set to a particular value or values. It is most
|
6
|
+
# commonly used in conjunction with the `validates_format_of` validation.
|
7
|
+
#
|
8
|
+
# #### should
|
9
|
+
#
|
10
|
+
# In the positive form, `allow_value` asserts that an attribute can be
|
11
|
+
# set to one or more values, succeeding if none of the values cause the
|
12
|
+
# record to be invalid:
|
13
|
+
#
|
14
|
+
# class UserProfile
|
15
|
+
# include ActiveModel::Model
|
16
|
+
#
|
17
|
+
# validates_format_of :website_url, with: URI.regexp
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # RSpec
|
21
|
+
# describe UserProfile do
|
22
|
+
# it { should allow_value('http://foo.com', 'http://bar.com/baz').for(:website_url) }
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# # Test::Unit
|
26
|
+
# class UserProfileTest < ActiveSupport::TestCase
|
27
|
+
# should allow_value('http://foo.com', 'http://bar.com/baz').for(:website_url)
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# #### should_not
|
31
|
+
#
|
32
|
+
# In the negative form, `allow_value` asserts that an attribute cannot be
|
33
|
+
# set to one or more values, succeeding if the *first* value causes the
|
34
|
+
# record to be invalid.
|
35
|
+
#
|
36
|
+
# **This can be surprising** so in this case if you need to check that
|
37
|
+
# *all* of the values are invalid, use separate assertions:
|
38
|
+
#
|
39
|
+
# class UserProfile
|
40
|
+
# include ActiveModel::Model
|
41
|
+
#
|
42
|
+
# validates_format_of :website_url, with: URI.regexp
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# describe UserProfile do
|
46
|
+
# # One assertion: 'buz' and 'bar' will not be tested
|
47
|
+
# it { should_not allow_value('fiz', 'buz', 'bar').for(:website_url) }
|
48
|
+
#
|
49
|
+
# # Three assertions, all tested separately
|
50
|
+
# it { should_not allow_value('fiz').for(:website_url) }
|
51
|
+
# it { should_not allow_value('buz').for(:website_url) }
|
52
|
+
# it { should_not allow_value('bar').for(:website_url) }
|
53
|
+
# end
|
54
|
+
#
|
55
|
+
# #### Qualifiers
|
56
|
+
#
|
57
|
+
# ##### on
|
58
|
+
#
|
59
|
+
# Use `on` if your validation applies only under a certain context.
|
60
|
+
#
|
61
|
+
# class UserProfile
|
62
|
+
# include ActiveModel::Model
|
63
|
+
#
|
64
|
+
# validates_format_of :birthday_as_string,
|
65
|
+
# with: /^(\d+)-(\d+)-(\d+)$/,
|
66
|
+
# on: :create
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# # RSpec
|
70
|
+
# describe UserProfile do
|
71
|
+
# it do
|
72
|
+
# should allow_value('2013-01-01').
|
73
|
+
# for(:birthday_as_string).
|
74
|
+
# on(:create)
|
75
|
+
# end
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# # Test::Unit
|
79
|
+
# class UserProfileTest < ActiveSupport::TestCase
|
80
|
+
# should allow_value('2013-01-01').
|
81
|
+
# for(:birthday_as_string).
|
82
|
+
# on(:create)
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# ##### with_message
|
86
|
+
#
|
87
|
+
# Use `with_message` if you are using a custom validation message.
|
88
|
+
#
|
89
|
+
# class UserProfile
|
90
|
+
# include ActiveModel::Model
|
91
|
+
#
|
92
|
+
# validates_format_of :state,
|
93
|
+
# with: /^(open|closed)$/,
|
94
|
+
# message: 'State must be open or closed'
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# # RSpec
|
98
|
+
# describe UserProfile do
|
99
|
+
# it do
|
100
|
+
# should allow_value('open', 'closed').
|
101
|
+
# for(:state).
|
102
|
+
# with_message('State must be open or closed')
|
103
|
+
# end
|
104
|
+
# end
|
105
|
+
#
|
106
|
+
# # Test::Unit
|
107
|
+
# class UserProfileTest < ActiveSupport::TestCase
|
108
|
+
# should allow_value('open', 'closed').
|
109
|
+
# for(:state).
|
110
|
+
# with_message('State must be open or closed')
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# Use `with_message` with the `:against` option if the attribute the
|
114
|
+
# validation message is stored under is different from the attribute
|
115
|
+
# being validated.
|
116
|
+
#
|
117
|
+
# class UserProfile
|
118
|
+
# include ActiveModel::Model
|
119
|
+
#
|
120
|
+
# validate :sports_team_must_be_valid
|
121
|
+
#
|
122
|
+
# private
|
123
|
+
#
|
124
|
+
# def sports_team_must_be_valid
|
125
|
+
# if sports_team !~ /^(Broncos|Titans)$/i
|
126
|
+
# self.errors.add :chosen_sports_team, 'Must be either a Broncos fan or a Titans fan'
|
127
|
+
# end
|
128
|
+
# end
|
129
|
+
# end
|
130
|
+
#
|
131
|
+
# # RSpec
|
132
|
+
# describe UserProfile do
|
133
|
+
# it do
|
134
|
+
# should allow_value('Broncos', 'Titans').
|
135
|
+
# for(:sports_team).
|
136
|
+
# with_message('Must be either a Broncos or Titans fan',
|
137
|
+
# against: :chosen_sports_team
|
138
|
+
# )
|
139
|
+
# end
|
140
|
+
# end
|
141
|
+
#
|
142
|
+
# # Test::Unit
|
143
|
+
# class UserProfileTest < ActiveSupport::TestCase
|
144
|
+
# should allow_value('Broncos', 'Titans').
|
145
|
+
# for(:sports_team).
|
146
|
+
# with_message('Must be either a Broncos or Titans fan',
|
147
|
+
# against: :chosen_sports_team
|
148
|
+
# )
|
149
|
+
# end
|
150
|
+
#
|
151
|
+
# @return [AllowValueMatcher]
|
152
|
+
#
|
153
|
+
def allow_value(*values)
|
154
|
+
if values.empty?
|
155
|
+
raise ArgumentError, 'need at least one argument'
|
156
|
+
else
|
157
|
+
AllowValueMatcher.new(*values)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
# @private
|
162
|
+
class AllowValueMatcher
|
163
|
+
include Helpers
|
164
|
+
|
165
|
+
attr_accessor :attribute_with_message
|
166
|
+
attr_accessor :options
|
167
|
+
|
168
|
+
def initialize(*values)
|
169
|
+
self.values_to_match = values
|
170
|
+
self.message_finder_factory = ValidationMessageFinder
|
171
|
+
self.options = {}
|
172
|
+
end
|
173
|
+
|
174
|
+
def for(attribute)
|
175
|
+
self.attribute_to_set = attribute
|
176
|
+
self.attribute_to_check_message_against = attribute
|
177
|
+
self
|
178
|
+
end
|
179
|
+
|
180
|
+
def on(context)
|
181
|
+
@context = context
|
182
|
+
self
|
183
|
+
end
|
184
|
+
|
185
|
+
def with_message(message, options={})
|
186
|
+
self.options[:expected_message] = message
|
187
|
+
if options.key?(:against)
|
188
|
+
self.attribute_to_check_message_against = options[:against]
|
189
|
+
end
|
190
|
+
self
|
191
|
+
end
|
192
|
+
|
193
|
+
def strict
|
194
|
+
self.message_finder_factory = ExceptionMessageFinder
|
195
|
+
self
|
196
|
+
end
|
197
|
+
|
198
|
+
def matches?(instance)
|
199
|
+
self.instance = instance
|
200
|
+
|
201
|
+
values_to_match.none? do |value|
|
202
|
+
self.value = value
|
203
|
+
instance.__send__("#{attribute_to_set}=", value)
|
204
|
+
errors_match?
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
def failure_message
|
209
|
+
"Did not expect #{expectation}, got error: #{matched_error}"
|
210
|
+
end
|
211
|
+
alias failure_message_for_should failure_message
|
212
|
+
|
213
|
+
def failure_message_when_negated
|
214
|
+
"Expected #{expectation}, got #{error_description}"
|
215
|
+
end
|
216
|
+
alias failure_message_for_should_not failure_message_when_negated
|
217
|
+
|
218
|
+
def description
|
219
|
+
message_finder.allow_description(allowed_values)
|
220
|
+
end
|
221
|
+
|
222
|
+
private
|
223
|
+
|
224
|
+
attr_accessor :values_to_match, :message_finder_factory,
|
225
|
+
:instance, :attribute_to_set, :attribute_to_check_message_against,
|
226
|
+
:context, :value, :matched_error
|
227
|
+
|
228
|
+
def errors_match?
|
229
|
+
has_messages? && errors_for_attribute_match?
|
230
|
+
end
|
231
|
+
|
232
|
+
def has_messages?
|
233
|
+
message_finder.has_messages?
|
234
|
+
end
|
235
|
+
|
236
|
+
def errors_for_attribute_match?
|
237
|
+
if expected_message
|
238
|
+
self.matched_error = errors_match_regexp? || errors_match_string?
|
239
|
+
else
|
240
|
+
errors_for_attribute.compact.any?
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
def errors_for_attribute
|
245
|
+
message_finder.messages
|
246
|
+
end
|
247
|
+
|
248
|
+
def errors_match_regexp?
|
249
|
+
if Regexp === expected_message
|
250
|
+
errors_for_attribute.detect { |e| e =~ expected_message }
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
def errors_match_string?
|
255
|
+
if errors_for_attribute.include?(expected_message)
|
256
|
+
expected_message
|
257
|
+
end
|
258
|
+
end
|
259
|
+
|
260
|
+
def expectation
|
261
|
+
includes_expected_message = expected_message ? "to include #{expected_message.inspect}" : ''
|
262
|
+
[error_source, includes_expected_message, "when #{attribute_to_set} is set to #{value.inspect}"].join(' ')
|
263
|
+
end
|
264
|
+
|
265
|
+
def error_source
|
266
|
+
message_finder.source_description
|
267
|
+
end
|
268
|
+
|
269
|
+
def error_description
|
270
|
+
message_finder.messages_description
|
271
|
+
end
|
272
|
+
|
273
|
+
def allowed_values
|
274
|
+
if values_to_match.length > 1
|
275
|
+
"any of [#{values_to_match.map(&:inspect).join(', ')}]"
|
276
|
+
else
|
277
|
+
values_to_match.first.inspect
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
def expected_message
|
282
|
+
if options.key?(:expected_message)
|
283
|
+
if Symbol === options[:expected_message]
|
284
|
+
default_expected_message
|
285
|
+
else
|
286
|
+
options[:expected_message]
|
287
|
+
end
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def default_expected_message
|
292
|
+
message_finder.expected_message_from(default_attribute_message)
|
293
|
+
end
|
294
|
+
|
295
|
+
def default_attribute_message
|
296
|
+
default_error_message(
|
297
|
+
options[:expected_message],
|
298
|
+
model_name: model_name,
|
299
|
+
instance: instance,
|
300
|
+
attribute: attribute_to_set
|
301
|
+
)
|
302
|
+
end
|
303
|
+
|
304
|
+
def model_name
|
305
|
+
instance.class.to_s.underscore
|
306
|
+
end
|
307
|
+
|
308
|
+
def message_finder
|
309
|
+
message_finder_factory.new(instance, attribute_to_check_message_against, context)
|
310
|
+
end
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|
314
|
+
end
|