url_validation 1.1.0 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 682d17ebc6fc4e9cc86e12f9040c24266b4f9caf
4
- data.tar.gz: c9e43aa2a8b465f1d099f8303478ed55ea4f4552
2
+ SHA256:
3
+ metadata.gz: 3060391d73d4937f667c371401a15591f538001c9db4c9e60457646b31625ffb
4
+ data.tar.gz: 3450f357cf02db8279a324e369beeb69eeb1842242d2ff6cc59102fdd1006ad3
5
5
  SHA512:
6
- metadata.gz: 23c5029413f8796d6b1225d3e58db091f801c636006344499dae7cc9dc14f7f665110dab8b8c2fe3e3502b19899fe1fd7f2792a0b4ea6cb15b6309d93b79ffa9
7
- data.tar.gz: 719a8dcd6c4b5f85e483a1c0597ba15e82bccf8b5ae1352569ecbaf303fdbc81f692642c467037b8a3ff2b133cc580b7951b1899ed82d08338f4eb898352864d
6
+ metadata.gz: 7111f507cf4f8f06f1e8c182846aed69633e404d0b4f5a1530ee2822ec165714de37850a243dc0b9616d262dc389c44bed9beba9c34b8e2a57f7cb84b0f0e97d
7
+ data.tar.gz: 5e58055eadde1913e32045b4f4f52efc838dbb6c88affafcba75c541b032dfc7c8635cbcca90c8201854f228f401c567428f65ab38c9f53b4ee6eac73f154721
data/CHANGELOG.md ADDED
@@ -0,0 +1,52 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [2.0.0] - 2026-05-14
9
+
10
+ ### Breaking
11
+
12
+ - Drops the runtime dependency on `activerecord`. The gem only ever needed
13
+ `ActiveModel::EachValidator`, so it now depends on `activemodel` (and
14
+ `activesupport`) directly. Apps that pulled `activerecord` in transitively
15
+ through this gem will need to declare it themselves if required.
16
+ - Bumps `required_ruby_version` to `>= 3.1`.
17
+ - Bumps `activemodel` / `activesupport` minimum to `>= 6.1`.
18
+
19
+ ### Added
20
+
21
+ - New `:http_method` option (default `:head`) lets callers choose the HTTP
22
+ verb used for accessibility checks. Useful for servers that do not handle
23
+ `HEAD` correctly.
24
+ - `webmock` is wired into the spec suite and `WebMock.disable_net_connect!`
25
+ is enabled, so the test suite no longer makes live network calls.
26
+ - GitHub Actions workflow tests Ruby 3.1-3.4 against ActiveModel 7.0-8.0.
27
+ - `bin/console`, `bin/setup`, modern `Rakefile`, hand-written gemspec.
28
+ - `CHANGELOG.md`.
29
+
30
+ ### Fixed
31
+
32
+ - **Accessibility check now uses `HEAD` requests, as documented.** Previously
33
+ the code called `HTTPI.get`, contradicting the README. It now calls
34
+ `HTTPI.request(:head, ...)` by default.
35
+ - **`allow_blank: false` is now honored.** Previously the validator's
36
+ `return if value.blank?` short-circuit overrode an explicit
37
+ `allow_blank: false` option. Blank values now flow through to
38
+ format validation when `allow_blank` is false.
39
+ - **Rescue branch no longer references a re-parsed URI.** When the
40
+ `default_scheme` re-parse raised, the rescue branch could call
41
+ `url_format_valid?` against a previously-set URI in an inconsistent
42
+ state. The rescue branch now adds `:invalid_url` unconditionally and
43
+ returns, which matches the documented behavior.
44
+
45
+ ### Removed
46
+
47
+ - `.travis.yml`, juwelier-generated gemspec, `VERSION` text file.
48
+ - Runtime `activerecord` dependency.
49
+
50
+ ## [1.2.0]
51
+
52
+ - Last juwelier-managed release.
data/README.md ADDED
@@ -0,0 +1,143 @@
1
+ # url_validation
2
+
3
+ [![CI](https://github.com/RISCfuture/url_validation/actions/workflows/ci.yml/badge.svg)](https://github.com/RISCfuture/url_validation/actions/workflows/ci.yml)
4
+ [![Gem Version](https://img.shields.io/gem/v/url_validation.svg)](https://rubygems.org/gems/url_validation)
5
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
6
+
7
+ A simple, localizable `ActiveModel::EachValidator` for URL fields.
8
+
9
+ | | |
10
+ |:------------|:--------------------------------|
11
+ | **Author** | Tim Morgan |
12
+ | **License** | Released under the MIT license. |
13
+
14
+ ## About
15
+
16
+ `url_validation` adds a URL validator usable in any `ActiveModel` (or `ActiveRecord`)
17
+ class. It supports localized error messages, multiple schemes, and optional
18
+ over-the-network reachability checks (`HEAD` by default, configurable).
19
+
20
+ ## Installation
21
+
22
+ Add to your `Gemfile`:
23
+
24
+ ```ruby
25
+ gem "url_validation"
26
+ ```
27
+
28
+ Then `bundle install`.
29
+
30
+ This gem depends on `activemodel >= 6.1`. It does not require `activerecord`.
31
+
32
+ ## Usage
33
+
34
+ It's an `EachValidator`, so use it with `validates`:
35
+
36
+ ```ruby
37
+ class User
38
+ include ActiveModel::Model
39
+ include ActiveModel::Validations
40
+
41
+ attr_accessor :terms_of_service_link
42
+ validates :terms_of_service_link, presence: true, url: true
43
+ end
44
+ ```
45
+
46
+ ### Examples
47
+
48
+ ```ruby
49
+ # Format only (no network)
50
+ validates :link, url: true
51
+
52
+ # Restrict to a specific scheme (or list)
53
+ validates :link, url: {scheme: "https"}
54
+ validates :link, url: {scheme: %w[http https ftp]}
55
+
56
+ # If the user types "example.com", treat it as "http://example.com"
57
+ validates :link, url: {default_scheme: "http"}
58
+
59
+ # Verify host is reachable (sends a HEAD request)
60
+ validates :link, url: {check_host: true}
61
+
62
+ # Verify the path resolves to something other than 4xx/5xx
63
+ validates :link, url: {check_path: true}
64
+ validates :link, url: {check_path: [300..399, 400..499, 500..599]}
65
+
66
+ # Use GET instead of HEAD (for servers that don't handle HEAD properly)
67
+ validates :link, url: {check_host: true, http_method: :get}
68
+
69
+ # Customize the HTTPI request (timeouts, headers, etc.)
70
+ validates :link, url: {
71
+ check_host: true,
72
+ request_callback: ->(request) { request.open_timeout = 5; request.read_timeout = 5 }
73
+ }
74
+ ```
75
+
76
+ ## Options
77
+
78
+ ### Basic
79
+
80
+ | Option | Description |
81
+ |:---------------|:-------------------------------------------------------------------------|
82
+ | `:allow_nil` | If `true`, `nil` values are allowed. |
83
+ | `:allow_blank` | If `true` (the default), `nil` or empty values are allowed without running format checks. Set to `false` to flag blank values as `:invalid_url`. |
84
+
85
+ ### Error messages
86
+
87
+ Override the I18n key the validator uses when adding errors:
88
+
89
+ | Option | Replaces I18n key |
90
+ |:--------------------------------|:------------------------|
91
+ | `:invalid_url_message` | `:invalid_url` |
92
+ | `:url_not_accessible_message` | `:url_not_accessible` |
93
+ | `:url_invalid_response_message` | `:url_invalid_response` |
94
+
95
+ ### Format checks (no network)
96
+
97
+ | Option | Description |
98
+ |:------------------|:-------------------------------------------------------------------------------------------------------------------------------------|
99
+ | `:scheme` | A string or array of strings indicating acceptable URL schemes. Defaults to `%w[http https]`. |
100
+ | `:default_scheme` | If set (e.g., `"http"`), a URL without a scheme will have this scheme prepended before validation, so `"example.com"` parses fine. |
101
+
102
+ ### Network checks
103
+
104
+ `url_validation` uses [HTTPI](https://rubygems.org/gems/httpi) to issue requests,
105
+ so you can pick your underlying HTTP client.
106
+
107
+ | Option | Description |
108
+ |:------------------|:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
109
+ | `:check_host` | If `true`, perform a request to the host to verify connectivity. Only runs for HTTP(S) URLs unless overridden. |
110
+ | `:check_path` | Treat specific response codes as invalid. Pass an Integer, a Symbol (e.g. `:not_found`), a Range (`400..499`), or an Array of these. `true` means "4xx or 5xx is invalid." Implies `:check_host`. |
111
+ | `:httpi_adapter` | The HTTPI adapter to use (default: HTTPI's default). |
112
+ | `:http_method` | The HTTP verb used for the accessibility check, as a Symbol. Defaults to `:head`. Use `:get` for servers that mis-handle `HEAD`. |
113
+ | `:request_callback` | A `Proc`/lambda invoked with the `HTTPI::Request` before it executes. Use for custom timeouts, headers, auth, etc. |
114
+
115
+ ## Localization
116
+
117
+ The validator emits the standard ActiveModel error symbols (`:invalid_url`,
118
+ `:url_not_accessible`, `:url_invalid_response`). Provide translations under the
119
+ usual `activemodel.errors.messages` namespace (or use the `Model.errors.messages`
120
+ fallback). Example `config/locales/en.yml`:
121
+
122
+ ```yaml
123
+ en:
124
+ errors:
125
+ messages:
126
+ invalid_url: "is not a valid URL"
127
+ url_not_accessible: "is not reachable"
128
+ url_invalid_response: "returned a bad response"
129
+ ```
130
+
131
+ ## Development
132
+
133
+ ```sh
134
+ bin/setup
135
+ bundle exec rspec
136
+ ```
137
+
138
+ The spec suite uses [webmock](https://rubygems.org/gems/webmock) with
139
+ `disable_net_connect!`, so tests never touch the network.
140
+
141
+ ## License
142
+
143
+ MIT. See `LICENSE`.
data/Rakefile CHANGED
@@ -1,38 +1,8 @@
1
- require 'rubygems'
2
- require 'bundler'
3
- begin
4
- Bundler.setup(:default, :development)
5
- rescue Bundler::BundlerError => e
6
- $stderr.puts e.message
7
- $stderr.puts "Run `bundle install` to install missing gems"
8
- exit e.status_code
9
- end
10
- require 'rake'
1
+ # frozen_string_literal: true
11
2
 
12
- require 'jeweler'
13
- Jeweler::Tasks.new do |gem|
14
- gem.name = "url_validation"
15
- gem.summary = %Q{Simple URL validation in Rails 3}
16
- gem.description = %Q{A simple, localizable EachValidator for URL fields in ActiveRecord 3.0.}
17
- gem.email = "git@timothymorgan.info"
18
- gem.homepage = "http://github.com/riscfuture/url_validation"
19
- gem.authors = [ "Tim Morgan" ]
20
- gem.required_ruby_version = '>= 1.8.7'
21
- end
22
- Jeweler::RubygemsDotOrgTasks.new
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
23
5
 
24
- require 'yard'
25
- YARD::Rake::YardocTask.new('doc') do |doc|
26
- doc.options << "-m" << "textile"
27
- doc.options << "--protected" << "--no-private"
28
- doc.options << "-r" << "README.textile"
29
- doc.options << "-o" << "doc"
30
- doc.options << "--title" << "url_validation Documentation".inspect
31
-
32
- doc.files = [ 'lib/*_validator.rb', 'README.textile' ]
33
- end
6
+ RSpec::Core::RakeTask.new(:spec)
34
7
 
35
- require 'rspec/core/rake_task'
36
- RSpec::Core::RakeTask.new
37
-
38
- task :default => :spec
8
+ task default: :spec
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # UrlValidator's version. Lives in its own module so the gemspec can
4
+ # `require_relative "lib/url_validation/version"` without forcing ActiveModel
5
+ # to load at gem-spec-evaluation time. The UrlValidator class picks the
6
+ # constant up and assigns it as `UrlValidator::VERSION`.
7
+ module UrlValidation
8
+ VERSION = "2.0.0"
9
+ end
@@ -1,170 +1,197 @@
1
- require 'addressable/uri'
2
- require 'httpi'
3
- require 'active_support/core_ext/hash/except'
4
- require 'active_model/validator'
5
- require 'active_support/core_ext/array/wrap'
1
+ # frozen_string_literal: true
2
+
3
+ require "addressable/uri"
4
+ require "httpi"
5
+ require "active_support/core_ext/hash/except"
6
+ require "active_model/validator"
7
+ require "active_support/core_ext/array/wrap"
8
+
9
+ require "url_validation/version"
6
10
 
7
11
  # Validates URLs. Uses the following I18n error message keys:
8
12
  #
9
- # | @invalid_url@ | URL is improperly formatted. |
10
- # | @url_not_accessible@ | Couldn't connect to the URL. |
11
- # | @url_invalid_response@ | Got a bad HTTP response (not of an acceptable type, e.g., 2xx). |
13
+ # | | |
14
+ # |:-----------------------|:----------------------------------------------------------------|
15
+ # | `invalid_url` | URL is improperly formatted. |
16
+ # | `url_not_accessible` | Couldn't connect to the URL. |
17
+ # | `url_invalid_response` | Got a bad HTTP response (not of an acceptable type, e.g., 2xx). |
12
18
  #
13
19
  # @example Checks the syntax only
14
- # validates :link, :url => true
20
+ # validates :link, url: true
15
21
  #
16
22
  # @example Ensures the host is available but does not check the path
17
- # validates :link, :url => { :check_host => true }
23
+ # validates :link, url: {check_host: true}
18
24
  #
19
25
  # @example Ensures that the host is available and that a request for the path does not return a 4xx or 5xx response
20
- # validates :link, :url => { :check_path => true }
26
+ # validates :link, url: {check_path: true}
21
27
  #
22
28
  # @example Ensures that the host is available and that a request for the path does not return a 3xx, 4xx, or 5xx response
23
- # validates :link, :url => { :check_path => [ 300..399, 400..499, 500..599 ] }
29
+ # validates :link, url: {check_path: [300..399, 400..499, 500..599]}
24
30
  #
25
31
  # @example Checks for host accessibility with a custom timeout
26
- # validates :link, :url => {
27
- # :check_host => true,
28
- # :request_callback => lambda { |request| request.timeout = 30 }
32
+ # validates :link, url: {
33
+ # check_host: true,
34
+ # request_callback: ->(request) { request.timeout = 30 }
29
35
  # }
30
36
  #
31
- # h2. Options
37
+ # @example Uses a GET request instead of the default HEAD request
38
+ # validates :link, url: {check_host: true, http_method: :get}
39
+ #
40
+ # ## Options
32
41
  #
33
- # h3. Basic options
42
+ # ### Basic options
34
43
  #
35
- # | @:allow_nil@ | If @true@, @nil@ values are allowed. |
36
- # | @:allow_blank@ | If @true@, @nil@ or empty values are allowed. |
44
+ # | | |
45
+ # |:---------------|:----------------------------------------------|
46
+ # | `:allow_nil` | If `true`, `nil` values are allowed. |
47
+ # | `:allow_blank` | If `true`, `nil` or empty values are allowed. |
37
48
  #
38
- # h3. Error messages
49
+ # ### Error messages
39
50
  #
40
- # | @:invalid_url_message@ | A custom message to use in place of @:invalid_url@. |
41
- # | @:incorrect_url_type_message@ | A custom message to use in place of @:incorrect_url_type@. |
42
- # | @:url_not_accessible_message@ | A custom message to use in place of @:url_not_accessible@. |
43
- # | @:url_invalid_response_message@ | A custom message to use in place of @:url_invalid_response@. |
51
+ # | | |
52
+ # |:--------------------------------|:-------------------------------------------------------------|
53
+ # | `:invalid_url_message` | A custom message to use in place of `:invalid_url`. |
54
+ # | `:incorrect_url_type_message` | A custom message to use in place of `:incorrect_url_type`. |
55
+ # | `:url_not_accessible_message` | A custom message to use in place of `:url_not_accessible`. |
56
+ # | `:url_invalid_response_message` | A custom message to use in place of `:url_invalid_response`. |
44
57
  #
45
- # h3. Networkless URL validation
58
+ # ### Networkless URL validation
46
59
  #
47
- # | @:scheme@ | A string or array of strings, such as "http" or "ftp", indicating which URL schemes are valid. By default only ==HTTP(S)== URLs are accepted. |
48
- # | @:default_scheme@ | A default URL scheme to try for improper URLs. If this is set to, e.g., "http", then when a URL like "whoops.com" is given (which would otherwise fail due to an improper format), "http://whoops.com" will be tried instead. |
60
+ # | | |
61
+ # |:------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
62
+ # | `:scheme` | A string or array of strings, such as "http" or "ftp", indicating which URL schemes are valid. By default only HTTP(S) URLs are accepted. |
63
+ # | `:default_scheme` | A default URL scheme to try for improper URLs. If this is set to, e.g., "http", then when a URL like "whoops.com" is given (which would otherwise fail due to an improper format), "http://whoops.com" will be tried instead. |
49
64
  #
50
- # h3. Over-the-network URL validation
65
+ # ### Over-the-network URL validation
51
66
  #
52
67
  # The HTTPI gem is used to provide a generic interface to whatever HTTP client
53
68
  # you wish to use. This allows you to drop in, e.g., a Curl client if you want.
54
- # You can set the HTTPI adapter with the @:httpi_adapter@ option.
69
+ # You can set the HTTPI adapter with the `:httpi_adapter` option.
55
70
  #
56
- # | @:check_host@ | If @true@, the validator will perform a network test to verify that it can connect to the server and access the host (at the "/" path). This check will only be performed for ==HTTP(S)== URLs. |
57
- # | @:check_path@ | An integer or symbol (or array of integers or symbols), such as 301 or @:moved_permanently@, indicating what response codes are unacceptable. You can also use ranges, and include them in an array, such as @[ :moved_permanently, 400..404, 500..599 ]@. By default, this is @nil@, and therefore only host accessibility is checked. If @true@ is given, uses a default set of invalid error codes (4xx and 5xx). Implies @:check_host@ is also true. |
58
- # | @:httpi_adapter@ | The HTTPI adapter to use for checking HTTP and HTTPS URLs (default set by the HTTPI gem). |
71
+ # By default, HEAD requests are used for accessibility checks. If a server does
72
+ # not support HEAD requests, you can set `:http_method` to `:get` (or any other
73
+ # verb supported by HTTPI).
59
74
  #
60
- # h3. Other options
75
+ # | | |
76
+ # |:-----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
77
+ # | `:check_host` | If `true`, the validator will perform a network test to verify that it can connect to the server and access the host (at the "/" path). This check will only be performed for HTTP(S) URLs. |
78
+ # | `:check_path` | An integer or symbol (or array of integers or symbols), such as 301 or `:moved_permanently`, indicating what response codes are unacceptable. You can also use ranges, and include them in an array, such as `[:moved_permanently, 400..404, 500..599]`. By default, this is `nil`, and therefore only host accessibility is checked. If `true` is given, uses a default set of invalid error codes (4xx and 5xx). Implies `:check_host` is also true. |
79
+ # | `:httpi_adapter` | The HTTPI adapter to use for checking HTTP and HTTPS URLs (default set by the HTTPI gem). |
80
+ # | `:http_method` | The HTTP verb (as a Symbol) to use for the accessibility check. Defaults to `:head`. Set to `:get` for servers that do not support HEAD. |
61
81
  #
62
- # | @:request_callback@ | A proc that receives the request object (for ==HTTP(S)== requests, the @HTTPI::Request@ object) before it is executed. You can use this proc to set, e.g., custom headers or timeouts on the request. |
82
+ # ### Other options
83
+ #
84
+ # | | |
85
+ # |:--------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
86
+ # | `:request_callback` | A proc that receives the request object (for HTTP(S) requests, the `HTTPI::Request` object) before it is executed. You can use this proc to set, e.g., custom headers or timeouts on the request. |
63
87
 
64
88
  class UrlValidator < ActiveModel::EachValidator
89
+ VERSION = UrlValidation::VERSION
90
+
65
91
  # @private
66
92
  CODES = {
67
- :continue => 100,
68
- :switching_protocols => 101,
69
- :processing => 102,
70
- :ok => 200,
71
- :created => 201,
72
- :accepted => 202,
73
- :non_authoritative_information => 203,
74
- :no_content => 204,
75
- :reset_content => 205,
76
- :partial_content => 206,
77
- :multi_status => 207,
78
- :im_used => 226,
79
- :multiple_choices => 300,
80
- :moved_permanently => 301,
81
- :found => 302,
82
- :see_other => 303,
83
- :not_modified => 304,
84
- :use_proxy => 305,
85
- :reserved => 306,
86
- :temporary_redirect => 307,
87
- :bad_request => 400,
88
- :unauthorized => 401,
89
- :payment_required => 402,
90
- :forbidden => 403,
91
- :not_found => 404,
92
- :method_not_allowed => 405,
93
- :not_acceptable => 406,
94
- :proxy_authentication_required => 407,
95
- :request_timeout => 408,
96
- :conflict => 409,
97
- :gone => 410,
98
- :length_required => 411,
99
- :precondition_failed => 412,
100
- :request_entity_too_large => 413,
101
- :request_uri_too_long => 414,
102
- :unsupported_media_type => 415,
103
- :requested_range_not_satisfiable => 416,
104
- :expectation_failed => 417,
105
- :unprocessable_entity => 422,
106
- :locked => 423,
107
- :failed_dependency => 424,
108
- :upgrade_required => 426,
109
- :internal_server_error => 500,
110
- :not_implemented => 501,
111
- :bad_gateway => 502,
112
- :service_unavailable => 503,
113
- :gateway_timeout => 504,
114
- :http_version_not_supported => 505,
115
- :variant_also_negotiates => 506,
116
- :insufficient_storage => 507,
117
- :not_extended => 510
118
- }
119
-
120
-
93
+ continue: 100,
94
+ switching_protocols: 101,
95
+ processing: 102,
96
+ ok: 200,
97
+ created: 201,
98
+ accepted: 202,
99
+ non_authoritative_information: 203,
100
+ no_content: 204,
101
+ reset_content: 205,
102
+ partial_content: 206,
103
+ multi_status: 207,
104
+ im_used: 226,
105
+ multiple_choices: 300,
106
+ moved_permanently: 301,
107
+ found: 302,
108
+ see_other: 303,
109
+ not_modified: 304,
110
+ use_proxy: 305,
111
+ reserved: 306,
112
+ temporary_redirect: 307,
113
+ bad_request: 400,
114
+ unauthorized: 401,
115
+ payment_required: 402,
116
+ forbidden: 403,
117
+ not_found: 404,
118
+ method_not_allowed: 405,
119
+ not_acceptable: 406,
120
+ proxy_authentication_required: 407,
121
+ request_timeout: 408,
122
+ conflict: 409,
123
+ gone: 410,
124
+ length_required: 411,
125
+ precondition_failed: 412,
126
+ request_entity_too_large: 413,
127
+ request_uri_too_long: 414,
128
+ unsupported_media_type: 415,
129
+ requested_range_not_satisfiable: 416,
130
+ expectation_failed: 417,
131
+ unprocessable_entity: 422,
132
+ locked: 423,
133
+ failed_dependency: 424,
134
+ upgrade_required: 426,
135
+ internal_server_error: 500,
136
+ not_implemented: 501,
137
+ bad_gateway: 502,
138
+ service_unavailable: 503,
139
+ gateway_timeout: 504,
140
+ http_version_not_supported: 505,
141
+ variant_also_negotiates: 506,
142
+ insufficient_storage: 507,
143
+ not_extended: 510
144
+ }.freeze
145
+
121
146
  # @private
122
147
  def validate_each(record, attribute, value)
123
- return if value.blank?
148
+ return if value.blank? && options.fetch(:allow_blank, true)
124
149
 
150
+ uri = nil
125
151
  begin
126
152
  uri = Addressable::URI.parse(value)
127
- if uri.scheme.nil? and options[:default_scheme] then
153
+
154
+ if uri.scheme.nil? && options[:default_scheme]
128
155
  uri = Addressable::URI.parse("#{options[:default_scheme]}://#{value}")
129
156
  end
130
157
  rescue Addressable::URI::InvalidURIError
131
- record.errors.add(attribute, options[:invalid_url_message] || :invalid_url) unless url_format_valid?(uri, options)
158
+ record.errors.add(attribute, options[:invalid_url_message] || :invalid_url)
132
159
  return
133
160
  end
134
-
135
- record.errors.add(attribute, options[:invalid_url_message] || :invalid_url) unless url_format_valid?(uri, options)
136
- record.errors.add(attribute, options[:url_not_accessible_message] || :url_not_accessible) unless response = url_accessible?(uri, options)
161
+
162
+ record.errors.add(attribute, options[:invalid_url_message] || :invalid_url) unless url_format_valid?(uri, options)
163
+ record.errors.add(attribute, options[:url_not_accessible_message] || :url_not_accessible) unless (response = url_accessible?(uri, options))
137
164
  record.errors.add(attribute, options[:url_invalid_response_message] || :url_invalid_response) unless url_response_valid?(response, options)
138
165
  end
139
-
166
+
140
167
  private
141
-
168
+
142
169
  def url_format_valid?(uri, options)
143
- return false unless Array.wrap(options[:scheme] || %w( http https )).include?(uri.scheme)
144
-
170
+ return false unless Array.wrap(options[:scheme] || %w[http https]).include?(uri.scheme)
171
+
145
172
  case uri.scheme
146
- when 'http', 'https'
173
+ when "http", "https"
147
174
  return http_url_format_valid?(uri)
148
175
  else
149
176
  return true
150
177
  end
151
178
  end
152
-
179
+
153
180
  def http_url_format_valid?(uri)
154
- uri.host.present? and not uri.path.nil?
181
+ uri.host.present? && !uri.path.nil?
155
182
  end
156
-
183
+
157
184
  def url_accessible?(uri, options)
158
- return true unless options[:check_host] or options[:check_path]
159
-
185
+ return true unless options[:check_host] || options[:check_path]
186
+
160
187
  check_host = options[:check_host]
161
- check_host ||= %w( http https ) if options[:check_path]
162
- if (schemes = Array.wrap(check_host)) and schemes.all? { |scheme| scheme.kind_of?(String) } then
163
- return true unless schemes.include?(uri.scheme)
188
+ check_host ||= %w[http https] if options[:check_path]
189
+ if (schemes = Array.wrap(check_host)) && schemes.all?(String) && !schemes.include?(uri.scheme)
190
+ return true
164
191
  end
165
-
192
+
166
193
  case uri.scheme
167
- when 'http', 'https'
194
+ when "http", "https"
168
195
  return http_url_accessible?(uri, options)
169
196
  else
170
197
  return true
@@ -174,19 +201,21 @@ class UrlValidator < ActiveModel::EachValidator
174
201
  def http_url_accessible?(uri, options)
175
202
  request = HTTPI::Request.new(uri.to_s)
176
203
  options[:request_callback].call(request) if options[:request_callback].respond_to?(:call)
177
- return HTTPI.get(request, options[:httpi_adapter])
178
- rescue
204
+ method = options[:http_method] || :head
205
+ return HTTPI.request(method, request, options[:httpi_adapter])
206
+ rescue StandardError
179
207
  return false
180
208
  end
181
-
209
+
182
210
  def url_response_valid?(response, options)
183
- return true unless response.kind_of?(HTTPI::Response) and options[:check_path]
184
- response_codes = options[:check_path] == true ? [400..499, 500..599] : Array.wrap(options[:check_path]).flatten
211
+ return true unless response.kind_of?(HTTPI::Response) && options[:check_path]
212
+
213
+ response_codes = (options[:check_path] == true) ? [400..499, 500..599] : Array.wrap(options[:check_path]).flatten
185
214
  return response_codes.none? do |code| # it's good if it's not a bad response
186
215
  case code # and it's a bad response if...
187
216
  when Range
188
217
  code.include? response.code
189
- when Fixnum
218
+ when Integer
190
219
  code == response.code
191
220
  when Symbol
192
221
  CODES.include?(code) && CODES[code] == response.code