string_validator 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: a5a08752d7f73d74fa45b4dec5d17815e71bb547c30a1364540923bb10e5649a
4
+ data.tar.gz: 75c37d3612370ccefa9504d2b466b0a06a9e618363c2a6d0bd53df43663f4136
5
+ SHA512:
6
+ metadata.gz: 192ae47895b814e8b8bbbb268009e8f9a6ed77135e8718f6948758fb92eb25bd46317d59ba3b40e04c1c14e44774aeb47e4081cbbeede870063ba51d3213b360
7
+ data.tar.gz: 1da02180de5a833956a6ae3a6aae356bc5518240f9a9452a981fb0e4f0adf92a15e61dd30d83975e8d7bc0b01eb0784ce3cf1a5d9f32c851c4a731b3e670a4e0
data/CHANGELOG.md ADDED
@@ -0,0 +1,10 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2025-02-16
4
+
5
+ ### Added
6
+
7
+ - Initial release
8
+ - Validators: `is_email?`, `is_url?`, `is_ip?`, `is_empty?`, `is_length?`, `is_alpha?`, `is_alphanumeric?`, `is_numeric?`, `is_int?`, `is_float?`, `is_boolean?`, `is_json?`, `is_base64?`, `is_hex_color?`, `is_uuid?`, `is_ascii?`, `is_slug?`, `is_mac_address?`, `is_credit_card?`, `is_md5?`, `is_hexadecimal?`, `contains?`, `equals?`, `is_in?`
9
+ - Sanitizers: `escape`, `unescape`, `trim`, `blacklist`, `whitelist`, `strip_low`, `to_boolean`, `to_int`, `to_float`
10
+ - `StringValidator.valid?(str, :validator_name, **options)` for safe validation with NotStringError
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025
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,146 @@
1
+ # StringValidator
2
+
3
+ A Ruby port of [validator.js](https://github.com/validatorjs/validator.js) — a comprehensive library of **string validators** and **sanitizers** for Ruby and Rails.
4
+
5
+ Validator.js has 23k+ GitHub stars and 18M+ weekly npm downloads. Until now, Ruby had no single gem offering the same API. Use this for API params, form input, background jobs, or any place you need to validate or sanitize strings.
6
+
7
+ ## Installation
8
+
9
+ Add to your Gemfile:
10
+
11
+ ```ruby
12
+ gem "string_validator"
13
+ ```
14
+
15
+ Then run:
16
+
17
+ ```bash
18
+ bundle install
19
+ ```
20
+
21
+ Or install globally:
22
+
23
+ ```bash
24
+ gem install string_validator
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ All methods are on the `StringValidator` module. Input must be a String (validator.js is strings-only too).
30
+
31
+ ### Validators
32
+
33
+ Return `true` or `false`.
34
+
35
+ ```ruby
36
+ require "string_validator"
37
+
38
+ StringValidator.is_email?("foo@bar.com") # => true
39
+ StringValidator.is_email?("invalid") # => false
40
+
41
+ StringValidator.is_url?("https://example.com") # => true
42
+ StringValidator.is_url?("not a url") # => false
43
+
44
+ StringValidator.is_ip?("192.168.1.1") # => true
45
+ StringValidator.is_ip?("::1", version: 6) # => true
46
+
47
+ StringValidator.is_empty?(" ") # => false
48
+ StringValidator.is_empty?(" ", ignore_whitespace: true) # => true
49
+
50
+ StringValidator.is_length?("hello", min: 1, max: 10) # => true
51
+
52
+ StringValidator.is_alpha?("abc") # => true
53
+ StringValidator.is_alphanumeric?("user123") # => true
54
+ StringValidator.is_numeric?("42.5") # => true
55
+ StringValidator.is_int?("42", min: 0, max: 100) # => true
56
+ StringValidator.is_float?("3.14") # => true
57
+
58
+ StringValidator.is_boolean?("true") # => true
59
+ StringValidator.is_boolean?("yes", loose: true) # => true
60
+
61
+ StringValidator.is_json?('{"a":1}') # => true
62
+ StringValidator.is_base64?("SGVsbG8=") # => true
63
+ StringValidator.is_hex_color?("#ff0000") # => true
64
+ StringValidator.is_uuid?("550e8400-e29b-41d4-a716-446655440000") # => true
65
+ StringValidator.is_ascii?("Hello") # => true
66
+ StringValidator.is_slug?("my-post-title") # => true
67
+ StringValidator.is_mac_address?("01:02:03:04:05:ab") # => true
68
+ StringValidator.is_credit_card?("4111111111111111") # => true (Luhn check)
69
+ StringValidator.is_md5?("d41d8cd98f00b204e9800998ecf8427e") # => true
70
+ StringValidator.is_hexadecimal?("0a1f") # => true
71
+
72
+ StringValidator.contains?("hello world", "world") # => true
73
+ StringValidator.equals?("foo", "foo") # => true
74
+ StringValidator.is_in?("red", %w[red green blue]) # => true
75
+ ```
76
+
77
+ ### Sanitizers
78
+
79
+ Return a transformed string (or the original if not a string).
80
+
81
+ ```ruby
82
+ StringValidator.escape("<script>alert('x')</script>")
83
+ # => "&lt;script&gt;alert(&#x27;x&#x27;)&lt;&#x2F;script&gt;"
84
+
85
+ StringValidator.trim(" hello ") # => "hello"
86
+ StringValidator.blacklist("hello world", "ol") # => "he wrd"
87
+ StringValidator.whitelist("abc123", "0123456789") # => "123"
88
+ StringValidator.whitelist("abc123", "a-c0-9") # => "abc123"
89
+ StringValidator.strip_low("hello\x00world") # => "helloworld"
90
+
91
+ StringValidator.to_boolean("true") # => true
92
+ StringValidator.to_boolean("false") # => false
93
+ StringValidator.to_boolean("yes", strict: false) # => true
94
+ StringValidator.to_int("42") # => 42
95
+ StringValidator.to_float("3.14") # => 3.14
96
+
97
+ StringValidator.unescape("&lt;tag&gt;") # => "<tag>"
98
+ ```
99
+
100
+ ### Rails / ActiveModel
101
+
102
+ Use in custom validators or in models:
103
+
104
+ ```ruby
105
+ # app/validators/email_validator.rb
106
+ class EmailValidator < ActiveModel::EachValidator
107
+ def validate_each(record, attribute, value)
108
+ return if value.blank?
109
+ unless StringValidator.is_email?(value.to_s)
110
+ record.errors.add(attribute, options[:message] || "is not a valid email")
111
+ end
112
+ end
113
+ end
114
+ ```
115
+
116
+ Or inline:
117
+
118
+ ```ruby
119
+ validate :email_format
120
+
121
+ def email_format
122
+ return if email.blank?
123
+ errors.add(:email, "invalid") unless StringValidator.is_email?(email)
124
+ end
125
+ ```
126
+
127
+ ### Safe validation helper
128
+
129
+ Raises `StringValidator::NotStringError` if the input is not a String:
130
+
131
+ ```ruby
132
+ StringValidator.valid?("user@example.com", :is_email?) # => true
133
+ StringValidator.valid?(nil, :is_email?) # => raises NotStringError
134
+ ```
135
+
136
+ ## Supported Ruby
137
+
138
+ Ruby 3.0+.
139
+
140
+ ## License
141
+
142
+ MIT. See [LICENSE](LICENSE.txt).
143
+
144
+ ## Credits
145
+
146
+ API and behavior are inspired by [validator.js](https://github.com/validatorjs/validator.js) (MIT). This gem is an independent Ruby implementation for the Ruby community.
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StringValidator
4
+ module Sanitizers
5
+ HTML_ESCAPE = {
6
+ "&" => "&amp;",
7
+ "<" => "&lt;",
8
+ ">" => "&gt;",
9
+ '"' => "&quot;",
10
+ "'" => "&#x27;",
11
+ "/" => "&#x2F;"
12
+ }.freeze
13
+
14
+ def escape(str)
15
+ return str unless str.is_a?(String)
16
+ str.gsub(/[&<>"'\/]/, HTML_ESCAPE)
17
+ end
18
+
19
+ def trim(str, chars = nil)
20
+ return str unless str.is_a?(String)
21
+ if chars
22
+ str.gsub(/\A[#{Regexp.escape(chars)}]+|[#{Regexp.escape(chars)}]+\z/, "")
23
+ else
24
+ str.strip
25
+ end
26
+ end
27
+
28
+ def blacklist(str, chars)
29
+ return str unless str.is_a?(String)
30
+ str.delete(chars)
31
+ end
32
+
33
+ def whitelist(str, chars)
34
+ return str unless str.is_a?(String)
35
+ str.each_char.select { |c| chars.include?(c) }.join
36
+ end
37
+
38
+ def strip_low(str, keep_new_lines: false)
39
+ return str unless str.is_a?(String)
40
+ if keep_new_lines
41
+ str.each_char.select { |c| c.ord >= 32 || c == "\n" }.join
42
+ else
43
+ str.each_char.select { |c| c.ord >= 32 }.join
44
+ end
45
+ end
46
+
47
+ def to_boolean(str, strict: true)
48
+ return str unless str.is_a?(String)
49
+ s = str.downcase.strip
50
+ return true if s == "true" || s == "1"
51
+ return false if s == "false" || s == "0"
52
+ return true if !strict && %w[yes y].include?(s)
53
+ return false if !strict && %w[no n].include?(s)
54
+ str
55
+ end
56
+
57
+ def to_int(str, radix: 10)
58
+ return str unless str.is_a?(String)
59
+ Integer(str, radix)
60
+ rescue ArgumentError
61
+ str
62
+ end
63
+
64
+ def to_float(str)
65
+ return str unless str.is_a?(String)
66
+ Float(str)
67
+ rescue ArgumentError
68
+ str
69
+ end
70
+
71
+ def unescape(str)
72
+ return str unless str.is_a?(String)
73
+ str
74
+ .gsub("&amp;", "&")
75
+ .gsub("&lt;", "<")
76
+ .gsub("&gt;", ">")
77
+ .gsub("&quot;", '"')
78
+ .gsub("&#x27;", "'")
79
+ .gsub("&#x2F;", "/")
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,228 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "uri"
4
+ require "json"
5
+ require "base64"
6
+
7
+ module StringValidator
8
+ module Validators
9
+ # RFC 5322 simplified email regex (validator.js compatible)
10
+ EMAIL_REGEX = /\A[^\s@]+@[^\s@]+\.[^\s@]+\z/
11
+
12
+ def is_email?(str, allow_display_name: false, require_tld: true)
13
+ return false unless str.is_a?(String)
14
+ s = str.strip
15
+ s = s.gsub(/\A.*<([^>]+)>\z/, '\1') if allow_display_name
16
+ return false if s.include?("..") || s.start_with?(".") || s.include?("@.")
17
+ return false if require_tld && !s.include?(".")
18
+ s.match?(EMAIL_REGEX)
19
+ end
20
+
21
+ def is_url?(str, protocols: %w[http https ftp], require_protocol: false, require_tld: true)
22
+ return false unless str.is_a?(String)
23
+ s = str.strip
24
+ return false if s.include?(" ")
25
+ if require_protocol
26
+ return false unless protocols.any? { |p| s.downcase.start_with?("#{p}:") }
27
+ end
28
+ begin
29
+ uri = ::URI.parse(s)
30
+ return false if uri.host.nil? && uri.opaque.nil?
31
+ if require_tld && uri.host && !uri.host.include?(".")
32
+ return false
33
+ end
34
+ true
35
+ rescue ::URI::InvalidURIError
36
+ false
37
+ end
38
+ end
39
+
40
+ def is_ip?(str, version: nil)
41
+ return false unless str.is_a?(String)
42
+ case version
43
+ when 4, "4"
44
+ str.match?(/\A(?:(?:25[0-5]|2[0-4]\d|1?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|1?\d\d?)\z/)
45
+ when 6, "6"
46
+ str.match?(/\A(?:[0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}\z/) ||
47
+ str.match?(/\A::(?:[0-9a-fA-F]{1,4}:){0,6}[0-9a-fA-F]{1,4}\z/) ||
48
+ str.match?(/\A(?:[0-9a-fA-F]{1,4}:){1,7}:\z/) # simplified IPv6
49
+ else
50
+ is_ip?(str, version: 4) || is_ip?(str, version: 6)
51
+ end
52
+ end
53
+
54
+ def is_empty?(str, ignore_whitespace: false)
55
+ return false unless str.is_a?(String)
56
+ s = ignore_whitespace ? str.strip : str
57
+ s.empty?
58
+ end
59
+
60
+ def is_length?(str, min: 0, max: nil)
61
+ return false unless str.is_a?(String)
62
+ len = str.length
63
+ return false if len < min
64
+ return false if max && len > max
65
+ true
66
+ end
67
+
68
+ def is_alpha?(str, locale: "en-US")
69
+ return false unless str.is_a?(String)
70
+ return str.match?(/\A[a-zA-Z]+\z/) if locale.to_s.downcase.start_with?("en")
71
+ str.match?(/\A\p{L}+\z/)
72
+ end
73
+
74
+ def is_alphanumeric?(str, locale: "en-US")
75
+ return false unless str.is_a?(String)
76
+ return str.match?(/\A[a-zA-Z0-9]+\z/) if locale.to_s.downcase.start_with?("en")
77
+ str.match?(/\A[\p{L}0-9]+\z/)
78
+ end
79
+
80
+ def is_numeric?(str, no_symbols: false)
81
+ return false unless str.is_a?(String)
82
+ return str.match?(/\A\d+\z/) if no_symbols
83
+ str.match?(/\A[-+]?\d*\.?\d+\z/)
84
+ end
85
+
86
+ def is_int?(str, min: nil, max: nil, allow_leading_zeroes: true)
87
+ return false unless str.is_a?(String)
88
+ return false if !allow_leading_zeroes && str.match?(/\A0\d+\z/)
89
+ return false unless str.match?(/\A[-+]?\d+\z/)
90
+ n = str.to_i
91
+ return false if min && n < min
92
+ return false if max && n > max
93
+ true
94
+ end
95
+
96
+ def is_float?(str, min: nil, max: nil)
97
+ return false unless str.is_a?(String)
98
+ return false unless str.match?(/\A[-+]?\d*\.?\d+(?:[eE][-+]?\d+)?\z/)
99
+ n = str.to_f
100
+ return false if min && n < min
101
+ return false if max && n > max
102
+ true
103
+ end
104
+
105
+ def is_boolean?(str, loose: false)
106
+ return false unless str.is_a?(String)
107
+ strict = %w[true false 0 1]
108
+ loose_list = strict + %w[yes no]
109
+ list = loose ? loose_list : strict
110
+ list.include?(str.downcase)
111
+ end
112
+
113
+ def is_json?(str, allow_primitives: false)
114
+ return false unless str.is_a?(String)
115
+ return true if allow_primitives && %w[true false null].include?(str.strip)
116
+ ::JSON.parse(str)
117
+ true
118
+ rescue ::JSON::ParserError
119
+ false
120
+ end
121
+
122
+ def is_base64?(str, url_safe: false)
123
+ return false unless str.is_a?(String)
124
+ regex = url_safe ? /\A[A-Za-z0-9_-]*\z/ : /\A[A-Za-z0-9+\/=]*\z/
125
+ return false unless str.match?(regex)
126
+ return true if str.empty?
127
+ s = url_safe ? str.tr("-_", "+/") : str
128
+ ::Base64.strict_decode64(s)
129
+ true
130
+ rescue ::ArgumentError
131
+ false
132
+ end
133
+
134
+ def is_hex_color?(str, require_hashtag: false)
135
+ return false unless str.is_a?(String)
136
+ return false if require_hashtag && !str.start_with?("#")
137
+ str.match?(/\A#?([0-9a-fA-F]{3}){1,2}\z/)
138
+ end
139
+
140
+ def is_uuid?(str, version: nil)
141
+ return false unless str.is_a?(String)
142
+ uuid_regex = /\A[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-5][0-9a-fA-F]{3}-[89ab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}\z/
143
+ return str.match?(uuid_regex) if version.nil?
144
+ v = version.to_s
145
+ return false unless str.match?(uuid_regex)
146
+ str[14] == v
147
+ end
148
+
149
+ def is_ascii?(str)
150
+ return false unless str.is_a?(String)
151
+ str.ascii_only?
152
+ end
153
+
154
+ def is_slug?(str)
155
+ return false unless str.is_a?(String)
156
+ str.match?(/\A[a-z0-9]+(?:-[a-z0-9]+)*\z/)
157
+ end
158
+
159
+ def is_mac_address?(str, no_separators: false)
160
+ return false unless str.is_a?(String)
161
+ if no_separators
162
+ str.match?(/\A[0-9a-fA-F]{12}\z/)
163
+ else
164
+ str.match?(/\A[0-9a-fA-F]{2}(:[0-9a-fA-F]{2}){5}\z/) ||
165
+ str.match?(/\A[0-9a-fA-F]{2}(-[0-9a-fA-F]{2}){5}\z/) ||
166
+ str.match?(/\A[0-9a-fA-F]{2}(\.[0-9a-fA-F]{2}){5}\z/) ||
167
+ str.match?(/\A[0-9a-fA-F]{2}( [0-9a-fA-F]{2}){5}\z/)
168
+ end
169
+ end
170
+
171
+ def contains?(str, seed, ignore_case: false, min_occurrences: 1)
172
+ return false unless str.is_a?(String)
173
+ s = str
174
+ s = s.downcase if ignore_case
175
+ se = ignore_case ? seed.downcase : seed
176
+ count = s.scan(Regexp.escape(se)).size
177
+ count >= min_occurrences
178
+ end
179
+
180
+ def equals?(str, comparison)
181
+ return false unless str.is_a?(String)
182
+ str == comparison.to_s
183
+ end
184
+
185
+ def is_in?(str, values)
186
+ return false unless str.is_a?(String)
187
+ Array(values).map(&:to_s).include?(str)
188
+ end
189
+
190
+ def is_credit_card?(str, provider: nil)
191
+ return false unless str.is_a?(String)
192
+ digits = str.gsub(/\D/, "")
193
+ return false unless digits.length >= 13 && digits.length <= 19
194
+ return false unless luhn_valid?(digits)
195
+ return true if provider.nil? || provider.to_s.empty?
196
+ case provider.to_s.downcase
197
+ when "visa" then digits.start_with?("4")
198
+ when "mastercard", "master" then digits.match?(/\A5[1-5]\d{14}\z/) || digits.match?(/\A2(?:2[2-9]|[3-6]\d|7[01])\d{12}\z/)
199
+ when "amex" then digits.match?(/\A3[47]\d{13}\z/)
200
+ when "discover" then digits.start_with?("6011", "65", "644", "645", "646", "647", "648", "649") || digits.match?(/\A62\d{14}\z/)
201
+ else true
202
+ end
203
+ end
204
+
205
+ def is_md5?(str)
206
+ return false unless str.is_a?(String)
207
+ str.match?(/\A[0-9a-fA-F]{32}\z/)
208
+ end
209
+
210
+ def is_hexadecimal?(str)
211
+ return false unless str.is_a?(String)
212
+ str.match?(/\A[0-9a-fA-F]+\z/)
213
+ end
214
+
215
+ private
216
+
217
+ def luhn_valid?(digits)
218
+ sum = 0
219
+ digits.reverse.chars.each_with_index do |c, i|
220
+ n = c.to_i
221
+ n *= 2 if i.odd?
222
+ n -= 9 if n > 9
223
+ sum += n
224
+ end
225
+ (sum % 10).zero?
226
+ end
227
+ end
228
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module StringValidator
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "string_validator/version"
4
+ require "string_validator/validators"
5
+ require "string_validator/sanitizers"
6
+
7
+ module StringValidator
8
+ extend Validators
9
+ extend Sanitizers
10
+
11
+ class Error < StandardError; end
12
+ class NotStringError < Error; end
13
+
14
+ def self.valid?(str, validator_name, **options)
15
+ raise NotStringError, "input must be a String" unless str.is_a?(String)
16
+ public_send(validator_name, str, **options)
17
+ end
18
+ end
metadata ADDED
@@ -0,0 +1,55 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: string_validator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Raj Panchal
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-02-16 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: A comprehensive library of string validators (is_email?, is_url?, is_ip?,
14
+ etc.) and sanitizers (escape, trim, etc.) for Ruby. Brings validator.js-style API
15
+ to Ruby/Rails.
16
+ email:
17
+ - rajpanchal2810@gmail.com
18
+ executables: []
19
+ extensions: []
20
+ extra_rdoc_files: []
21
+ files:
22
+ - CHANGELOG.md
23
+ - LICENSE.txt
24
+ - README.md
25
+ - lib/string_validator.rb
26
+ - lib/string_validator/sanitizers.rb
27
+ - lib/string_validator/validators.rb
28
+ - lib/string_validator/version.rb
29
+ homepage: https://github.com/rajpanchal01/string_validator
30
+ licenses:
31
+ - MIT
32
+ metadata:
33
+ homepage_uri: https://github.com/rajpanchal01/string_validator
34
+ source_code_uri: https://github.com/rajpanchal01/string_validator
35
+ changelog_uri: https://github.com/rajpanchal01/string_validator/blob/main/CHANGELOG.md
36
+ post_install_message:
37
+ rdoc_options: []
38
+ require_paths:
39
+ - lib
40
+ required_ruby_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: 3.0.0
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ requirements: []
51
+ rubygems_version: 3.0.9
52
+ signing_key:
53
+ specification_version: 4
54
+ summary: String validators and sanitizers for Ruby - port of validator.js
55
+ test_files: []