validation_kit 1.0.5 → 2.0.1
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 +4 -4
- data/CHANGELOG.md +150 -0
- data/LICENSE +21 -0
- data/README.md +200 -0
- data/lib/validation_kit/country_option.rb +35 -0
- data/lib/validation_kit/email_validator/email_validator.rb +26 -13
- data/lib/validation_kit/mixed_case_validator/config/locales/en.yml +4 -5
- data/lib/validation_kit/mixed_case_validator/config/locales/fr.yml +4 -5
- data/lib/validation_kit/mixed_case_validator/mixed_case_validator.rb +29 -28
- data/lib/validation_kit/phone_validator/phone_validator.rb +102 -80
- data/lib/validation_kit/postal_code_validator/postal_code_validator.rb +46 -47
- data/lib/validation_kit/version.rb +3 -1
- data/lib/validation_kit.rb +31 -6
- metadata +31 -20
- data/.gitignore +0 -4
- data/Gemfile +0 -4
- data/Rakefile +0 -1
- data/lib/validation_kit/email_validator/README +0 -32
- data/lib/validation_kit/mixed_case_validator/README +0 -59
- data/lib/validation_kit/phone_validator/README +0 -69
- data/lib/validation_kit/postal_code_validator/README +0 -44
- data/validation_kit.gemspec +0 -21
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 83f8486330f7a2179f4655e0cd938355d9b79c9b96279238829b4c3c7a860151
|
|
4
|
+
data.tar.gz: 479ec62978042d99be1efb5985c3efad7e18615f5450374cd131ebd2a907ff06
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b2940c397a76ebedbcbcb9802081159eb58b837c7284a6f77e5487081164b23447a30092485b7e765c295af88e8fdb6ba6fd5aac355cf46a1795b7beaad451fd
|
|
7
|
+
data.tar.gz: 5d59577b6a5d73bb96f1063b1d8b8125856d23d4d48d01c7e67584a47067e3df43054db60aa768c4c1b2bf175eea8fff8198000f8d1f0262b2175b3470c59d25
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 2.0.1 (2026-10-02)
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
|
|
7
|
+
- The email length limits (254 characters in all, 64 before the `@`) now count
|
|
8
|
+
every character, as RFC 5321 does, including the quotes around a quoted local
|
|
9
|
+
part and each backslash escape. So a quoted local part of 63 characters plus
|
|
10
|
+
its quotes, which used to pass, is now too long. The limits are checked by
|
|
11
|
+
the validator and the new `ValidationKit::EmailValidator.valid_address?`, not
|
|
12
|
+
by `EMAIL_ADDRESS_RE`: code that uses the regex directly no longer gets them,
|
|
13
|
+
and should call `valid_address?` instead.
|
|
14
|
+
- An `area_key:` that isn't an Australian state or territory (NSW, ACT, VIC,
|
|
15
|
+
TAS, QLD, SA, NT, WA, in any case) now raises `ArgumentError` when the model
|
|
16
|
+
is defined, and `format_as_phone` raises for one too. It used to silently add
|
|
17
|
+
the NSW/ACT area code (02), so `"Victoria"` or a typo stored a wrong number.
|
|
18
|
+
Leaving `area_key:` out still means NSW/ACT.
|
|
19
|
+
- A symbol `country:` option that's neither a method on the model nor a
|
|
20
|
+
two-letter country code (such as a misspelled method name,
|
|
21
|
+
`country: :billing_contry`) now raises `ArgumentError` when validating. It
|
|
22
|
+
used to fall back to the model's `country` method, or skip validation
|
|
23
|
+
silently.
|
|
24
|
+
- The bundled mixed case messages no longer start with the attribute's name, so
|
|
25
|
+
`full_messages` names it once ("First name cannot be in all caps", not "First
|
|
26
|
+
name First name cannot be in all caps"), like Rails' own messages:
|
|
27
|
+
- `errors[:first_name]` is now `["cannot be in all caps"]`. Code that shows
|
|
28
|
+
`errors[attribute]` on its own should use `full_messages_for` instead.
|
|
29
|
+
- `attribute_name:` no longer changes the default message. `full_messages`
|
|
30
|
+
uses the attribute's translated name (`human_attribute_name`) instead; to
|
|
31
|
+
rename it, translate the attribute. `attribute_name:` still fills in
|
|
32
|
+
`%{item}` in custom messages and translations, which keep working.
|
|
33
|
+
- Apps that set `errors.format` to `"%{message}"` to hide the doubled name now
|
|
34
|
+
get messages with no name at all, and can drop that setting.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- US and Canadian phone numbers with extra digits right after the number and no
|
|
39
|
+
extension marker in front of them (any letter, as in `x`, `ext` or
|
|
40
|
+
`extension`, or `#`, `#` or `№`) are now invalid, with or without `set:`. A
|
|
41
|
+
mistyped number such as `212-555-12345` used to pass, and `set:` stored it as
|
|
42
|
+
`(212) 555-1234 5`. This includes non-ASCII digits such as `5`.
|
|
43
|
+
- Without `set:`, a US or Canadian number with letters among its first ten
|
|
44
|
+
digits (`519 444 000 ext 123`) is now invalid, as it already was with `set:`.
|
|
45
|
+
- With `set:`, a stored extension no longer keeps a leading `,` or `;` or
|
|
46
|
+
surrounding spaces (including non-breaking ones): `212-555-1234, ext 5`
|
|
47
|
+
becomes `(212) 555-1234 ext 5`. An extension written with non-ASCII digits
|
|
48
|
+
(`x5`) is kept instead of being dropped. Other marked extensions (`x5`,
|
|
49
|
+
`ext. 99`, `(x5)`, `#5`, …) are stored as before.
|
|
50
|
+
### Fixed
|
|
51
|
+
|
|
52
|
+
- The email validator still backtracked catastrophically on input with quote
|
|
53
|
+
characters, despite the 2.0.0 fix: `'a"' * 28 + "@"` (57 characters) took
|
|
54
|
+
about a minute to reject. The two length checks at the start of the regex
|
|
55
|
+
caused it; they're now plain length checks, and that input fails in well
|
|
56
|
+
under a millisecond.
|
|
57
|
+
- Email addresses with non-ASCII letters that case-fold to ASCII ones, such as
|
|
58
|
+
`a@ſx.com`, are rejected, like every other non-ASCII address.
|
|
59
|
+
- `country: :CA` (or `:ca`) now validates as Canada, as the README says. A
|
|
60
|
+
symbol was only ever treated as a method name, so without a `CA` method the
|
|
61
|
+
validator used the model's `country` method instead, or skipped validation
|
|
62
|
+
silently.
|
|
63
|
+
- The phone validator's `area_key:` (and `format_as_phone`'s area key) now
|
|
64
|
+
accepts Australian states in any case, as a string or symbol. `:vic` or
|
|
65
|
+
`"vic"` used to silently get the NSW/ACT area code (02).
|
|
66
|
+
|
|
67
|
+
### Documentation
|
|
68
|
+
|
|
69
|
+
- The README's error-type example used `errors.added?(:first_name, :all_caps)`,
|
|
70
|
+
which is always false for mixed case errors because they carry an `item:`
|
|
71
|
+
option. It now uses `of_kind?`, and explains when `added?` needs `item:`.
|
|
72
|
+
|
|
73
|
+
## 2.0.0 (2026-10-01)
|
|
74
|
+
|
|
75
|
+
### Breaking changes
|
|
76
|
+
|
|
77
|
+
- **Ruby 3.4+ and activemodel 7.2+ are required.** The gem now declares its
|
|
78
|
+
activemodel dependency and loads without Rails.
|
|
79
|
+
- **Errors are added by type instead of as translated strings.** All four
|
|
80
|
+
validators call `errors.add(attribute, :invalid)` (mixed case:
|
|
81
|
+
`:all_caps` / `:all_lowercase`), so `errors.added?(:email, :invalid)` and
|
|
82
|
+
`errors.details` work, and ActiveModel does the message lookup.
|
|
83
|
+
- Existing `activerecord.errors.models.<model>.attributes.<attribute>.<type>`
|
|
84
|
+
translations keep working.
|
|
85
|
+
- Non-ActiveRecord models now look under `activemodel.*` instead of
|
|
86
|
+
`activerecord.*`, and fall back to Rails' "is invalid".
|
|
87
|
+
- A `message:` (or `all_caps:` / `all_lowercase:`) option now takes
|
|
88
|
+
precedence over translations, as it does for Rails' own validators.
|
|
89
|
+
- The bundled mixed case messages moved from `activerecord.errors.messages`
|
|
90
|
+
to `errors.messages`.
|
|
91
|
+
- **`ActiveModel::Validations::EmailValidator` (and `PhoneValidator`,
|
|
92
|
+
`PostalCodeValidator`, `MixedCaseValidator`) no longer exist.** Use
|
|
93
|
+
`ValidationKit::EmailValidator` and so on. `validates :email, email: true`
|
|
94
|
+
is unchanged, and an app's own validator with the same name now takes
|
|
95
|
+
precedence over the gem's.
|
|
96
|
+
- **`MixedCaseValidator::ALL_CAPS` / `ALL_LOWERCASE`** are now the symbols
|
|
97
|
+
`:all_caps` / `:all_lowercase` instead of `1` / `-1`.
|
|
98
|
+
- **Validation is stricter**, so some values that used to pass are now
|
|
99
|
+
invalid:
|
|
100
|
+
- postal codes of the wrong length (e.g. a 7-digit US ZIP code)
|
|
101
|
+
- email addresses containing a newline
|
|
102
|
+
- phone and postal input that's blank once cleaned up (e.g. `"n/a"`), even
|
|
103
|
+
with `allow_blank`
|
|
104
|
+
- **`format_as_phone` returns `nil`** for numbers it can't format, instead of
|
|
105
|
+
the raw digits.
|
|
106
|
+
|
|
107
|
+
### Fixed
|
|
108
|
+
|
|
109
|
+
- The email regex no longer backtracks catastrophically: crafted input that
|
|
110
|
+
took over a minute to reject now fails in under a millisecond.
|
|
111
|
+
- Postal codes of the wrong length were accepted and then replaced with `nil`
|
|
112
|
+
by `set:`.
|
|
113
|
+
- With `allow_blank`, junk phone and postal input was accepted and replaced
|
|
114
|
+
with `""` or `nil`.
|
|
115
|
+
- US/CA numbers with a leading 1 (`1-212-555-1234`) were stored with a
|
|
116
|
+
trailing space.
|
|
117
|
+
- Phone extensions could be taken from the wrong part of the input when the
|
|
118
|
+
last four digits appeared earlier in it.
|
|
119
|
+
- Country codes in lowercase or as symbols (`"us"`, `:US`) silently skipped
|
|
120
|
+
validation.
|
|
121
|
+
- Integer values (e.g. a ZIP code from an integer column) raised instead of
|
|
122
|
+
validating.
|
|
123
|
+
- Mixed case errors were missing the attribute name and ignored the
|
|
124
|
+
`attribute_name:` option, and the bundled English and French messages were
|
|
125
|
+
never loaded.
|
|
126
|
+
- Values with no letters (`"123"`) or in scripts without case failed as "all
|
|
127
|
+
caps", and short accented names skipped the check.
|
|
128
|
+
- A stray `|` in the AU phone regex character classes.
|
|
129
|
+
|
|
130
|
+
### Added
|
|
131
|
+
|
|
132
|
+
- UK postcode validation and formatting (`UK` or `GB`).
|
|
133
|
+
- `source_code_uri`, `bug_tracker_uri`, and `changelog_uri` gemspec metadata,
|
|
134
|
+
and a declared MIT license.
|
|
135
|
+
|
|
136
|
+
### Documentation
|
|
137
|
+
|
|
138
|
+
- One `README.md` replaces the per-validator READMEs, with current usage,
|
|
139
|
+
options, and a translation key reference. Added `LICENSE` and this
|
|
140
|
+
changelog.
|
|
141
|
+
|
|
142
|
+
### Internal
|
|
143
|
+
|
|
144
|
+
- Minitest suite, CI on Ruby 3.4/4.0 against activemodel 7.2/8.0/8.1,
|
|
145
|
+
rubocop, Dependabot, and a trusted publishing release workflow. The gem now
|
|
146
|
+
packages only `lib/`, `README.md`, `LICENSE`, and `CHANGELOG.md`.
|
|
147
|
+
|
|
148
|
+
## 1.0.5
|
|
149
|
+
|
|
150
|
+
Last release before 2.0.0.
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2008-2026 The validation_kit contributors (see README.md)
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# validation_kit
|
|
2
|
+
|
|
3
|
+
ActiveModel validators for email addresses, phone numbers, postal codes, and
|
|
4
|
+
mixed-case text.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Add the gem to your Gemfile:
|
|
9
|
+
|
|
10
|
+
```ruby
|
|
11
|
+
gem "validation_kit"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The validators are available in any class that includes
|
|
15
|
+
`ActiveModel::Validations`, including ActiveRecord models. If your app defines
|
|
16
|
+
its own validator with the same name (e.g. `app/validators/email_validator.rb`),
|
|
17
|
+
yours is used instead.
|
|
18
|
+
|
|
19
|
+
## Email
|
|
20
|
+
|
|
21
|
+
Validates that an attribute is an email address.
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
class Person < ActiveRecord::Base
|
|
25
|
+
validates :email, email: true
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Person.new(email: "Bob").valid? # => false
|
|
29
|
+
Person.new(email: "joe@foobar.com").valid? # => true
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Addresses are limited to 254 characters, and 64 before the `@` (RFC 5321). To
|
|
33
|
+
check an address outside a model, use
|
|
34
|
+
`ValidationKit::EmailValidator.valid_address?("joe@foobar.com")`.
|
|
35
|
+
|
|
36
|
+
## Mixed case
|
|
37
|
+
|
|
38
|
+
Validates that a string isn't all capitals or all lowercase. Values with fewer
|
|
39
|
+
than three letters that have a case are skipped, so initials, digits, and
|
|
40
|
+
scripts without case (e.g. Chinese) never fail.
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
class Person < ActiveRecord::Base
|
|
44
|
+
validates :first_name, mixed_case: true
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
Person.new(first_name: "BOB").valid? # => false, "First name cannot be in all caps"
|
|
48
|
+
Person.new(first_name: "bob").valid? # => false, "First name cannot be in all lowercase"
|
|
49
|
+
Person.new(first_name: "Bob").valid? # => true
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
| Option | Description |
|
|
53
|
+
| ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
54
|
+
| `attribute_name:` | Name for `%{item}` in a custom message, used when the attribute has no translation (a translation wins). It doesn’t change `full_messages`, which use the attribute’s translated name. |
|
|
55
|
+
| `all_caps:` | Custom message for all-caps values. |
|
|
56
|
+
| `all_lowercase:` | Custom message for all-lowercase values. |
|
|
57
|
+
|
|
58
|
+
## Phone
|
|
59
|
+
|
|
60
|
+
Strict validation for phone numbers in the United States (`US`), Canada
|
|
61
|
+
(`CA`), and Australia (`AU`).
|
|
62
|
+
|
|
63
|
+
```ruby
|
|
64
|
+
class Person < ActiveRecord::Base
|
|
65
|
+
validates :phone, phone: { country: "US", set: true }, allow_blank: true
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Option | Description |
|
|
70
|
+
| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
71
|
+
| `country:` | See [Specifying the country](#specifying-the-country). |
|
|
72
|
+
| `set:` | Reformat the stored value: `"1-212-555-1234 x5"` becomes `"(212) 555-1234 x5"`, `"0298765432"` becomes `"(02) 9876 5432"`. |
|
|
73
|
+
| `area_key:` | For Australian landlines entered without an area code, the state whose area code to add: `NSW`, `ACT`, `VIC`, `TAS`, `QLD`, `SA`, `NT` or `WA`, in any case, as a string or symbol (`"VIC"`, `:qld`). It's always a fixed state, not a method name. Leave it out (or blank) for NSW/ACT (`02`); any other value raises `ArgumentError`. |
|
|
74
|
+
|
|
75
|
+
A US or Canadian number may end with an extension, which needs a marker in
|
|
76
|
+
front of its digits: any letter (as in `x`, `ext` or `extension`), or `#`, `#`
|
|
77
|
+
or `№` (`"212-555-1234 ext 5"`). Digits right after the number with no marker,
|
|
78
|
+
as in `"212-555-12345"`, make it invalid.
|
|
79
|
+
|
|
80
|
+
## Postal code
|
|
81
|
+
|
|
82
|
+
Strict validation for postal and ZIP codes in the United States (`US`), Canada
|
|
83
|
+
(`CA`), the United Kingdom (`UK` or `GB`), Australia (`AU`), and New Zealand
|
|
84
|
+
(`NZ`).
|
|
85
|
+
|
|
86
|
+
```ruby
|
|
87
|
+
class Person < ActiveRecord::Base
|
|
88
|
+
validates :postal_code, postal_code: { country: "CA", set: true }, allow_blank: true
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
| Option | Description |
|
|
93
|
+
| ---------- | ----------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| `country:` | See [Specifying the country](#specifying-the-country). |
|
|
95
|
+
| `set:` | Reformat the stored value: Canadian and UK codes get a space (`K1A 0B1`, `SW1A 1AA`), US ZIP+4 a hyphen (`10001-1234`). |
|
|
96
|
+
|
|
97
|
+
## Specifying the country
|
|
98
|
+
|
|
99
|
+
The phone and postal code validators need a country. Country codes are
|
|
100
|
+
case-insensitive (`"CA"`, `"ca"`, or `:CA`), and validation is skipped when the
|
|
101
|
+
country isn't supported.
|
|
102
|
+
|
|
103
|
+
```ruby
|
|
104
|
+
# A fixed country code, as a string or a two-letter symbol
|
|
105
|
+
validates :phone, phone: { country: "CA" }
|
|
106
|
+
validates :phone, phone: { country: :ca }
|
|
107
|
+
|
|
108
|
+
# The name of a method on your model that returns the country code
|
|
109
|
+
validates :phone, phone: { country: :billing_country }
|
|
110
|
+
|
|
111
|
+
# Neither: the validator calls your model's country method
|
|
112
|
+
validates :phone, phone: true
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
A symbol is a method name if your model has that public method; otherwise a
|
|
116
|
+
two-letter symbol is a country code. Any other symbol (such as a misspelled
|
|
117
|
+
method name) raises `ArgumentError` when validating, rather than skipping
|
|
118
|
+
validation. Two things that check can't catch: a misspelled two-letter method
|
|
119
|
+
name is read as a country code (and skipped if that country isn't supported),
|
|
120
|
+
and with `allow_nil:` or `allow_blank:` it only runs once the attribute has a
|
|
121
|
+
value.
|
|
122
|
+
|
|
123
|
+
## Error messages
|
|
124
|
+
|
|
125
|
+
Errors are added with standard types, so the usual ActiveModel tools work:
|
|
126
|
+
|
|
127
|
+
```ruby
|
|
128
|
+
person.errors.of_kind?(:email, :invalid) # email, phone, postal code
|
|
129
|
+
person.errors.of_kind?(:first_name, :all_caps) # mixed case (also :all_lowercase)
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
`of_kind?` checks the attribute and error type. `added?` also compares the
|
|
133
|
+
error's options, and mixed case errors carry an `item:` option (the
|
|
134
|
+
attribute's name, for messages that use `%{item}`; see
|
|
135
|
+
[Translation keys](#translation-keys) for how it's chosen), so for them
|
|
136
|
+
`added?` only matches if you pass the same `item:`.
|
|
137
|
+
|
|
138
|
+
Pass `message:` (or `all_caps:` / `all_lowercase:` for mixed case) for a custom
|
|
139
|
+
message, or translate it like any other Rails validation error:
|
|
140
|
+
|
|
141
|
+
```yaml
|
|
142
|
+
en:
|
|
143
|
+
activerecord:
|
|
144
|
+
errors:
|
|
145
|
+
models:
|
|
146
|
+
person:
|
|
147
|
+
attributes:
|
|
148
|
+
email:
|
|
149
|
+
invalid: "doesn’t look like an email address"
|
|
150
|
+
first_name:
|
|
151
|
+
all_caps: "can’t be all capitals"
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Like Rails’ own messages, these leave out the attribute’s name, because
|
|
155
|
+
`full_messages` adds it: "First name can’t be all capitals". Use `activemodel`
|
|
156
|
+
instead of `activerecord` for classes that aren't ActiveRecord models. The gem includes English and French messages for the
|
|
157
|
+
mixed case errors.
|
|
158
|
+
|
|
159
|
+
## Translation keys
|
|
160
|
+
|
|
161
|
+
Each validator adds an error of the type below. Rails looks up its message in
|
|
162
|
+
this order, using the first key it finds. These are the keys for an
|
|
163
|
+
ActiveRecord model `Person`; use `activemodel` instead of `activerecord` for
|
|
164
|
+
other ActiveModel classes:
|
|
165
|
+
|
|
166
|
+
1. `activerecord.errors.models.person.attributes.<attribute>.<type>`
|
|
167
|
+
2. `activerecord.errors.models.person.<type>`
|
|
168
|
+
3. `activerecord.errors.messages.<type>`
|
|
169
|
+
4. `errors.attributes.<attribute>.<type>`
|
|
170
|
+
5. `errors.messages.<type>`
|
|
171
|
+
|
|
172
|
+
A `message:` option (or `all_caps:` / `all_lowercase:`) skips the lookup.
|
|
173
|
+
|
|
174
|
+
| Validator | Error type | Default message (en) | Default message (fr) |
|
|
175
|
+
| ------------- | ---------------- | ---------------------------- | ------------------------------------------ |
|
|
176
|
+
| `email` | `:invalid` | "is invalid" (from Rails) | from Rails' locale files (e.g. rails-i18n) |
|
|
177
|
+
| `phone` | `:invalid` | "is invalid" (from Rails) | from Rails' locale files (e.g. rails-i18n) |
|
|
178
|
+
| `postal_code` | `:invalid` | "is invalid" (from Rails) | from Rails' locale files (e.g. rails-i18n) |
|
|
179
|
+
| `mixed_case` | `:all_caps` | "cannot be in all caps" | "ne peut pas être en majuscules" |
|
|
180
|
+
| `mixed_case` | `:all_lowercase` | "cannot be in all lowercase" | "ne peut pas être en minuscules" |
|
|
181
|
+
|
|
182
|
+
Like Rails' own messages, the mixed case messages leave out the attribute's
|
|
183
|
+
name, which `full_messages` adds: "First name cannot be in all caps". A custom
|
|
184
|
+
message or translation can include it as `%{item}`: the attribute's
|
|
185
|
+
translation at `activerecord.attributes.person.<attribute>`, then the
|
|
186
|
+
`attribute_name:` option, then the humanized attribute name.
|
|
187
|
+
|
|
188
|
+
## Credits
|
|
189
|
+
|
|
190
|
+
- **Email, mixed case, and postal code validators:** [Paul Schreiber](https://paulschreiber.com/), 2010–2011.
|
|
191
|
+
- **Phone validator:** written by Kristina Lim, copyright © 2008 Syndeo
|
|
192
|
+
Media. Paul Schreiber added US
|
|
193
|
+
and Canada support (September 2010) and extension support (November 2010).
|
|
194
|
+
It is named after, and was originally built on, Jerrod Blavos's
|
|
195
|
+
[validates_as_phone](https://code.google.com/archive/p/validates-as-phone/) plugin.
|
|
196
|
+
- **Gem:** co-authored by Wes Morgan.
|
|
197
|
+
|
|
198
|
+
## License
|
|
199
|
+
|
|
200
|
+
Released under the MIT License. See [LICENSE](LICENSE).
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ValidationKit
|
|
4
|
+
# The country: option shared by the phone and postal code validators.
|
|
5
|
+
module CountryOption
|
|
6
|
+
private
|
|
7
|
+
|
|
8
|
+
# The country code to validate against, or nil to skip validation:
|
|
9
|
+
# - a String is the code itself ("CA")
|
|
10
|
+
# - a Symbol is the name of a method on the record that returns the code
|
|
11
|
+
# (:billing_country); if there's no such method, a two-letter symbol is
|
|
12
|
+
# the code itself (:CA), and anything else is a mistake, so it raises
|
|
13
|
+
# rather than silently skipping validation
|
|
14
|
+
# - without the option, the record's country method, if it has one
|
|
15
|
+
def country_for(record)
|
|
16
|
+
option = options[:country]
|
|
17
|
+
|
|
18
|
+
case option
|
|
19
|
+
when String
|
|
20
|
+
option
|
|
21
|
+
when Symbol
|
|
22
|
+
if record.respond_to?(option)
|
|
23
|
+
record.public_send(option)
|
|
24
|
+
elsif option.match?(/\A[a-z]{2}\z/i)
|
|
25
|
+
option
|
|
26
|
+
else
|
|
27
|
+
raise ArgumentError, "country: #{option.inspect} is neither a public method on " \
|
|
28
|
+
"#{record.class.name || record.class.inspect} nor a two-letter country code"
|
|
29
|
+
end
|
|
30
|
+
else
|
|
31
|
+
record.country if record.respond_to?(:country)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -1,21 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ValidationKit
|
|
2
4
|
class EmailValidator < ActiveModel::EachValidator
|
|
3
|
-
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
5
|
+
# RFC 5321's length limits: the whole address, and the part before the @.
|
|
6
|
+
# They're checked in validate_each rather than as lookaheads at the start of
|
|
7
|
+
# EMAIL_ADDRESS_RE, where they backtracked exponentially on input like
|
|
8
|
+
# 'a"a"a"…@' (about a minute for 57 characters).
|
|
9
|
+
MAX_LENGTH = 254
|
|
10
|
+
MAX_LOCAL_PART_LENGTH = 64
|
|
11
|
+
|
|
12
|
+
# The address syntax only; validate_each also applies the length limits.
|
|
13
|
+
EMAIL_ADDRESS_RE = /\A(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22))(?:\.(?:(?:[\x21\x23-\x27\x2A\x2B\x2D\x2F-\x39\x3D\x3F\x5E-\x7E]+)|(?:\x22(?:[\x01-\x08\x0B\x0C\x0E-\x1F\x21\x23-\x5B\x5D-\x7F]|(?:\x5C[\x00-\x7F]))*\x22)))*@(?:(?:(?!.*[^.]{64,})(?:(?:xn--)?[a-z0-9]+(?:-[a-z0-9]+)*\.){1,126}(?:(?:[a-z][a-z0-9]*)|(?:(?:xn--)[a-z0-9]+))(?:-[a-z0-9]+)*)|(?:\[(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){7})|(?:(?!(?:.*[a-f0-9][:\]]){7,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,5})?)))|(?:(?:IPv6:(?:(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){5}:)|(?:(?!(?:.*[a-f0-9]:){5,})(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3})?::(?:[a-f0-9]{1,4}(?::[a-f0-9]{1,4}){0,3}:)?)))?(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))(?:\.(?:(?:25[0-5])|(?:2[0-4][0-9])|(?:1[0-9]{2})|(?:[1-9]?[0-9]))){3}))\]))\z/i
|
|
7
14
|
|
|
8
|
-
|
|
15
|
+
# Whether address is a valid email address: the syntax and the length
|
|
16
|
+
# limits. For use outside a model; EMAIL_ADDRESS_RE alone skips the limits.
|
|
17
|
+
def self.valid_address?(address)
|
|
18
|
+
address = address.to_s
|
|
19
|
+
# The syntax is ASCII-only, but the regex's /i matching would also let
|
|
20
|
+
# Unicode case-folded letters (ſ, the Kelvin sign) through as s and k.
|
|
21
|
+
return false unless address.ascii_only?
|
|
22
|
+
return false if address.length > MAX_LENGTH
|
|
23
|
+
|
|
24
|
+
# The domain can't contain an @, so the last one ends the local part.
|
|
25
|
+
address.rpartition("@").first.length <= MAX_LOCAL_PART_LENGTH && EMAIL_ADDRESS_RE.match?(address)
|
|
26
|
+
end
|
|
9
27
|
|
|
10
28
|
def validate_each(record, attribute, value)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
:default => [:"activerecord.errors.models.#{model_name.underscore}.invalid",
|
|
15
|
-
options[:message],
|
|
16
|
-
:'activerecord.errors.messages.invalid'])
|
|
17
|
-
record.errors.add(attribute, message)
|
|
18
|
-
end
|
|
29
|
+
return if self.class.valid_address?(value)
|
|
30
|
+
|
|
31
|
+
record.errors.add(attribute, :invalid, message: options[:message])
|
|
19
32
|
end
|
|
20
33
|
end
|
|
21
34
|
end
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
fr:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
all_lowercase: "%{item} ne peut pas être en minuscules"
|
|
2
|
+
errors:
|
|
3
|
+
messages:
|
|
4
|
+
all_caps: "ne peut pas être en majuscules"
|
|
5
|
+
all_lowercase: "ne peut pas être en minuscules"
|
|
@@ -1,42 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module ValidationKit
|
|
2
4
|
class MixedCaseValidator < ActiveModel::EachValidator
|
|
3
|
-
|
|
4
|
-
|
|
5
|
+
# Error types, also used as the I18n keys and option names for custom messages.
|
|
6
|
+
ALL_CAPS = :all_caps
|
|
7
|
+
ALL_LOWERCASE = :all_lowercase
|
|
5
8
|
|
|
6
9
|
def validate_each(record, attribute, value)
|
|
7
10
|
return if value.nil?
|
|
8
|
-
return if value.gsub(/\W/, "").size < 3 # skip very short words
|
|
9
|
-
error = nil
|
|
10
|
-
|
|
11
|
-
if (value.upcase == value)
|
|
12
|
-
error = ALL_CAPS
|
|
13
|
-
elsif (value.downcase == value)
|
|
14
|
-
error = ALL_LOWERCASE
|
|
15
|
-
end
|
|
16
11
|
|
|
17
|
-
|
|
12
|
+
# Only letters that have a case count, so digits, punctuation, and
|
|
13
|
+
# scripts without case (e.g. Chinese) never trigger an error.
|
|
14
|
+
upper = value.to_s.scan(/\p{Lu}/).size
|
|
15
|
+
lower = value.to_s.scan(/\p{Ll}/).size
|
|
16
|
+
return if upper + lower < 3 # skip very short words
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
error = if lower.zero?
|
|
19
|
+
ALL_CAPS
|
|
20
|
+
elsif upper.zero?
|
|
21
|
+
ALL_LOWERCASE
|
|
22
|
+
end
|
|
20
23
|
|
|
21
|
-
|
|
22
|
-
:default => nil) or options[:attribute_name] or attribute
|
|
24
|
+
return if error.nil?
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
:'activerecord.errors.messages.all_caps'])
|
|
30
|
-
elsif error == ALL_LOWERCASE
|
|
31
|
-
message = I18n.t("activerecord.errors.models.#{model_name.underscore}.attributes.#{attribute}.all_lowercase",
|
|
32
|
-
:item => item_name,
|
|
33
|
-
:default => [:"activerecord.errors.models.#{model_name.underscore}.all_lowercase",
|
|
34
|
-
options[:all_lowercase],
|
|
35
|
-
:'activerecord.errors.messages.all_lowercase'])
|
|
36
|
-
end
|
|
26
|
+
# The bundled messages leave the name to full_messages, like Rails' own.
|
|
27
|
+
# item: is still passed for custom messages and translations that use
|
|
28
|
+
# %{item}.
|
|
29
|
+
record.errors.add(attribute, error, item: item_name(record, attribute), message: options[error])
|
|
30
|
+
end
|
|
37
31
|
|
|
38
|
-
|
|
32
|
+
private
|
|
39
33
|
|
|
34
|
+
# A translated attribute name wins, then the :attribute_name option, then
|
|
35
|
+
# the humanized attribute name.
|
|
36
|
+
def item_name(record, attribute)
|
|
37
|
+
scope = [record.class.i18n_scope, :attributes, record.class.model_name.i18n_key]
|
|
38
|
+
I18n.t(attribute, scope:, default: nil) ||
|
|
39
|
+
options[:attribute_name] ||
|
|
40
|
+
record.class.human_attribute_name(attribute)
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
end
|