Class: UrlValidator

Inherits:
ActiveModel::EachValidator
  • Object
show all
Defined in:
lib/url_validation.rb

Overview

Validates URLs. Uses the following I18n error message keys:

| | | |:———————–|:—————————————————————-| | `invalid_url` | URL is improperly formatted. | | `url_not_accessible` | Couldn't connect to the URL. | | `url_invalid_response` | Got a bad HTTP response (not of an acceptable type, e.g., 2xx). |

## Options

### Basic options

| | | |:—————|:———————————————-| | `:allow_nil` | If `true`, `nil` values are allowed. | | `:allow_blank` | If `true`, `nil` or empty values are allowed. |

### Error messages

| | | |:——————————–|:————————————————————-| | `:invalid_url_message` | A custom message to use in place of `:invalid_url`. | | `:incorrect_url_type_message` | A custom message to use in place of `:incorrect_url_type`. | | `:url_not_accessible_message` | A custom message to use in place of `:url_not_accessible`. | | `:url_invalid_response_message` | A custom message to use in place of `:url_invalid_response`. |

### Networkless URL validation

| | | |:——————|:——————————————————————————————————————————————————————————————————————————| | `: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. | | `: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), “whoops.com” will be tried instead. |

### Over-the-network URL validation

The HTTPI gem is used to provide a generic interface to whatever HTTP client you wish to use. This allows you to drop in, e.g., a Curl client if you want. You can set the HTTPI adapter with the `:httpi_adapter` option.

| | | |:—————–|:——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————-| | `: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. | | `: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. | | `:httpi_adapter` | The HTTPI adapter to use for checking HTTP and HTTPS URLs (default set by the HTTPI gem). |

### Other options

| | | |:——————–|:————————————————————————————————————————————————————————————————–| | `: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. |

Examples:

Checks the syntax only

validates :link, url: true

Ensures the host is available but does not check the path

validates :link, url: {check_host: true}

Ensures that the host is available and that a request for the path does not return a 4xx or 5xx response

validates :link, url: {check_path: true}

Ensures that the host is available and that a request for the path does not return a 3xx, 4xx, or 5xx response

validates :link, url: {check_path: [300..399, 400..499, 500..599]}

Checks for host accessibility with a custom timeout

validates :link, url: {
  check_host: true,
  request_callback: ->(request) { request.timeout = 30 }
}

Constant Summary collapse

CODES =
{
    continue:                        100,
    switching_protocols:             101,
    processing:                      102,
    ok:                              200,
    created:                         201,
    accepted:                        202,
    non_authoritative_information:   203,
    no_content:                      204,
    reset_content:                   205,
    partial_content:                 206,
    multi_status:                    207,
    im_used:                         226,
    multiple_choices:                300,
    moved_permanently:               301,
    found:                           302,
    see_other:                       303,
    not_modified:                    304,
    use_proxy:                       305,
    reserved:                        306,
    temporary_redirect:              307,
    bad_request:                     400,
    unauthorized:                    401,
    payment_required:                402,
    forbidden:                       403,
    not_found:                       404,
    method_not_allowed:              405,
    not_acceptable:                  406,
    proxy_authentication_required:   407,
    request_timeout:                 408,
    conflict:                        409,
    gone:                            410,
    length_required:                 411,
    precondition_failed:             412,
    request_entity_too_large:        413,
    request_uri_too_long:            414,
    unsupported_media_type:          415,
    requested_range_not_satisfiable: 416,
    expectation_failed:              417,
    unprocessable_entity:            422,
    locked:                          423,
    failed_dependency:               424,
    upgrade_required:                426,
    internal_server_error:           500,
    not_implemented:                 501,
    bad_gateway:                     502,
    service_unavailable:             503,
    gateway_timeout:                 504,
    http_version_not_supported:      505,
    variant_also_negotiates:         506,
    insufficient_storage:            507,
    not_extended:                    510
}

Instance Method Summary collapse

Instance Method Details

#validate_each(record, attribute, value) ⇒ Object


133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# File 'lib/url_validation.rb', line 133

def validate_each(record, attribute, value)
  return if value.blank?

  begin
    uri = Addressable::URI.parse(value)

    if uri.scheme.nil? and options[:default_scheme]
      uri = Addressable::URI.parse("#{options[:default_scheme]}://#{value}")
    end
  rescue Addressable::URI::InvalidURIError
    record.errors.add(attribute, options[:invalid_url_message] || :invalid_url) if uri.nil? || !url_format_valid?(uri, options)
    return
  end

  record.errors.add(attribute, options[:invalid_url_message] || :invalid_url) unless url_format_valid?(uri, options)
  record.errors.add(attribute, options[:url_not_accessible_message] || :url_not_accessible) unless response = url_accessible?(uri, options)
  record.errors.add(attribute, options[:url_invalid_response_message] || :url_invalid_response) unless url_response_valid?(response, options)
end