diff --git a/.rspec b/.rspec index 62c58f0..8c18f1a 100644 --- a/.rspec +++ b/.rspec @@ -1 +1,2 @@ --cfs +--format documentation +--color diff --git a/.ruby-gemset b/.ruby-gemset new file mode 100644 index 0000000..f26935d --- /dev/null +++ b/.ruby-gemset @@ -0,0 +1 @@ +url_validation diff --git a/.ruby-version b/.ruby-version new file mode 100644 index 0000000..8e8299d --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.4.2 diff --git a/Gemfile b/Gemfile index b6a9705..dbf5df6 100644 --- a/Gemfile +++ b/Gemfile @@ -1,13 +1,18 @@ -source :rubygems +source 'https://rubygems.org' -gem 'addressable', :require => 'addressable/uri' # for unicode URIs +gem 'addressable', require: 'addressable/uri' # for unicode URIs gem 'activesupport' gem 'activerecord' gem 'httpi' group :development do - gem 'jeweler' - gem 'yard' - gem 'RedCloth', require: 'redcloth' + # PUBLISHING + gem 'juwelier' + + # DOCS + gem 'yard', require: nil + gem 'redcarpet', require: nil + + # SPECS gem 'rspec' end diff --git a/Gemfile.lock b/Gemfile.lock index bbf69f5..5809ee1 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,53 +1,107 @@ GEM - remote: http://rubygems.org/ + remote: https://rubygems.org/ specs: - RedCloth (4.2.7) - activemodel (3.0.7) - activesupport (= 3.0.7) - builder (~> 2.1.2) - i18n (~> 0.5.0) - activerecord (3.0.7) - activemodel (= 3.0.7) - activesupport (= 3.0.7) - arel (~> 2.0.2) - tzinfo (~> 0.3.23) - activesupport (3.0.7) - addressable (2.2.6) - arel (2.0.10) - builder (2.1.2) - diff-lcs (1.1.2) - git (1.2.5) - httpi (0.9.4) - pyu-ntlm-http (>= 0.1.3.1) + activemodel (5.1.5) + activesupport (= 5.1.5) + activerecord (5.1.5) + activemodel (= 5.1.5) + activesupport (= 5.1.5) + arel (~> 8.0) + activesupport (5.1.5) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + addressable (2.5.2) + public_suffix (>= 2.0.2, < 4.0) + arel (8.0.0) + builder (3.2.3) + concurrent-ruby (1.0.5) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) + diff-lcs (1.3) + faraday (0.12.2) + multipart-post (>= 1.2, < 3) + git (1.3.0) + github_api (0.18.2) + addressable (~> 2.4) + descendants_tracker (~> 0.0.4) + faraday (~> 0.8) + hashie (~> 3.5, >= 3.5.2) + oauth2 (~> 1.0) + hashie (3.5.7) + highline (1.7.10) + httpi (2.4.3) rack - i18n (0.5.0) - jeweler (1.6.0) - bundler (~> 1.0.0) - git (>= 1.2.5) + socksify + i18n (0.9.5) + concurrent-ruby (~> 1.0) + juwelier (2.4.9) + builder + bundler + git + github_api + highline + kamelcase (~> 0) + nokogiri + psych rake - pyu-ntlm-http (0.1.3.1) - rack (1.3.0) - rake (0.9.0) - rspec (2.6.0) - rspec-core (~> 2.6.0) - rspec-expectations (~> 2.6.0) - rspec-mocks (~> 2.6.0) - rspec-core (2.6.3) - rspec-expectations (2.6.0) - diff-lcs (~> 1.1.2) - rspec-mocks (2.6.0) - tzinfo (0.3.27) - yard (0.7.1) + rdoc + semver2 + jwt (1.5.6) + kamelcase (0.0.2) + semver2 (~> 3) + mini_portile2 (2.3.0) + minitest (5.11.3) + multi_json (1.13.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + nokogiri (1.8.2) + mini_portile2 (~> 2.3.0) + oauth2 (1.4.0) + faraday (>= 0.8, < 0.13) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + psych (3.0.2) + public_suffix (3.0.2) + rack (2.0.4) + rake (12.3.1) + rdoc (6.0.2) + redcarpet (3.4.0) + rspec (3.7.0) + rspec-core (~> 3.7.0) + rspec-expectations (~> 3.7.0) + rspec-mocks (~> 3.7.0) + rspec-core (3.7.1) + rspec-support (~> 3.7.0) + rspec-expectations (3.7.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.7.0) + rspec-mocks (3.7.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.7.0) + rspec-support (3.7.1) + semver2 (3.4.2) + socksify (1.7.1) + thread_safe (0.3.6) + tzinfo (1.2.5) + thread_safe (~> 0.1) + yard (0.9.12) PLATFORMS ruby DEPENDENCIES - RedCloth activerecord activesupport addressable httpi - jeweler + juwelier + redcarpet rspec yard + +BUNDLED WITH + 1.16.1 diff --git a/README.md b/README.md new file mode 100644 index 0000000..5896c84 --- /dev/null +++ b/README.md @@ -0,0 +1,40 @@ +# url_validation + +Simple URL validator for Rails 3. + +| | | +|:------------|:--------------------------------| +| **Author** | Tim Morgan | +| **Version** | 1.0 (May 9, 2011) | +| **License** | Released under the MIT license. | + +## About + +This gem adds a very simple URL format validator to be used with Active Record +models in Rails 3.0. It supports localized error messages. It can validate many +different kinds of URLs, including HTTP and HTTPS. It supports advanced +validation features like sending `HEAD` requests to URLS to verify that they are +valid endpoints. + +## Installation + +Add the gem to your project's Gemfile: + +``` ruby +gem 'url_validation' +``` + +## Usage + +This gem is an `EachValidator`, and thus is used with the `validates` method: + +``` ruby +class User < ActiveRecord::Base + validates :terms_of_service_link, + presence: true, + url: true +end +``` + +There are other options to fine-tune your validation; see the {UrlValidator} +class for more, and for a list of error message localization keys. diff --git a/README.textile b/README.textile deleted file mode 100644 index 713ebcb..0000000 --- a/README.textile +++ /dev/null @@ -1,36 +0,0 @@ -h1. url_validation -- Simple URL validator for Rails 3 - -| *Author* | Tim Morgan | -| *Version* | 1.0 (May 9, 2011) | -| *License* | Released under the MIT license. | - -h2. About - -This gem adds a very simple URL format validator to be used with ActiveRecord -models in Rails 3.0. It supports localized error messages. It can validate many -different kinds of URLs, including HTTP and HTTPS. It supports advanced -validation features like sending @HEAD@ requests to URLS to verify that they are -valid endpoints. - -h2. Installation - -Add the gem to your project's @Gemfile@: - -

-gem 'url_validation'
-
- -h2. Usage - -This gem is an @EachValidator@, and thus is used with the @validates@ method: - -

-class User < ActiveRecord::Base
-  validates :terms_of_service_link,
-            :presence => true,
-            :url => true
-end
-
- -There are other options to fine-tune your validation; see the {UrlValidator} -class for more, and for a list of error message localization keys. diff --git a/Rakefile b/Rakefile index 61308f6..7902e2a 100644 --- a/Rakefile +++ b/Rakefile @@ -9,30 +9,30 @@ rescue Bundler::BundlerError => e end require 'rake' -require 'jeweler' -Jeweler::Tasks.new do |gem| - gem.name = "url_validation" - gem.summary = %Q{Simple URL validation in Rails 3} +require 'juwelier' +Juwelier::Tasks.new do |gem| + gem.name = 'url_validation' + gem.summary = %Q{Simple URL validation in Rails 3+} gem.description = %Q{A simple, localizable EachValidator for URL fields in ActiveRecord 3.0.} - gem.email = "git@timothymorgan.info" - gem.homepage = "http://github.com/riscfuture/url_validation" - gem.authors = [ "Tim Morgan" ] - gem.required_ruby_version = '>= 1.8.7' + gem.email = 'git@timothymorgan.info' + gem.homepage = 'http://github.com/riscfuture/url_validation' + gem.authors = ['Tim Morgan'] + gem.required_ruby_version = '>= 2.0.0' end -Jeweler::RubygemsDotOrgTasks.new +Juwelier::RubygemsDotOrgTasks.new require 'yard' YARD::Rake::YardocTask.new('doc') do |doc| - doc.options << "-m" << "textile" - doc.options << "--protected" << "--no-private" - doc.options << "-r" << "README.textile" - doc.options << "-o" << "doc" - doc.options << "--title" << "url_validation Documentation".inspect - - doc.files = [ 'lib/*_validator.rb', 'README.textile' ] + doc.options << '-m' << 'markdown' << '-M' << 'redcarpet' + doc.options << '--protected' << '--no-private' + doc.options << '-r' << 'README.md' + doc.options << '-o' << 'doc' + doc.options << '--title' << 'url_validation Documentation'.inspect + + doc.files = %w(lib/**/*.rb README.md) end require 'rspec/core/rake_task' RSpec::Core::RakeTask.new -task :default => :spec +task default: :spec diff --git a/VERSION b/VERSION index afaf360..867e524 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 \ No newline at end of file +1.2.0 \ No newline at end of file diff --git a/lib/url_validation.rb b/lib/url_validation.rb index 0313568..2f97a88 100644 --- a/lib/url_validation.rb +++ b/lib/url_validation.rb @@ -2,163 +2,183 @@ require 'httpi' require 'active_support/core_ext/hash/except' require 'active_model/validator' +require 'active_support/core_ext/array/wrap' # 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). | +# | | | +# |:-----------------------|:----------------------------------------------------------------| +# | `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). | # # @example Checks the syntax only -# validates :link, :url => true +# validates :link, url: true # # @example Ensures the host is available but does not check the path -# validates :link, :url => { :check_host => true } +# validates :link, url: {check_host: true} # # @example 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 } +# validates :link, url: {check_path: true} # # @example 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 ] } +# validates :link, url: {check_path: [300..399, 400..499, 500..599]} # # @example Checks for host accessibility with a custom timeout -# validates :link, :url => { -# :check_host => true, -# :request_callback => lambda { |request| request.timeout = 30 } +# validates :link, url: { +# check_host: true, +# request_callback: ->(request) { request.timeout = 30 } # } # -# h2. Options +# ## Options # -# h3. Basic options +# ### Basic options # -# | @:allow_nil@ | If @true@, @nil@ values are allowed. | -# | @:allow_blank@ | If @true@, @nil@ or empty values are allowed. | +# | | | +# |:------------------|:------------------------------------------------------------| +# | `:allow_nil` | If `true`, `nil` values are allowed. | +# | `:allow_blank` | If `true`, `nil` or empty values are allowed. | +# | `:require_domain` | If `true`, A domain must be present to be considered valid. | # -# h3. Error messages +# ### 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@. | +# | | | +# |:--------------------------------|:-------------------------------------------------------------| +# | `: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`. | # -# h3. Networkless URL validation +# ### 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), "http://whoops.com" will be tried instead. | +# | | | +# |:------------------|:------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +# | `: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), "http://whoops.com" will be tried instead. | # -# h3. Over-the-network URL validation +# ### 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. +# 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). | +# | | | +# |:-----------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +# | `: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). | # -# h3. Other options +# ### 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. | -# | @:response_callback@ | A proc that receives the response object after it is executed by @:check_path@, when it is enabled. This is useful for checking redirect URL's, etc. | +# | | | +# |:--------------------|:--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +# | `: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. | class UrlValidator < ActiveModel::EachValidator # @private 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 + 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 } - - + # @private def validate_each(record, attribute, value) - return if options[:allow_nil] and value.nil? - return if options[:allow_blank] and value.blank? - - uri = Addressable::URI.parse(value) - if uri.scheme.nil? and options[:default_scheme] then - uri = Addressable::URI.parse("#{options[:default_scheme]}://#{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, record, attribute, value, options) + + 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 - + private - + def url_format_valid?(uri, options) - return false unless Array.wrap(options[:scheme] || %w( http https )).include?(uri.scheme) - + return false unless Array.wrap(options[:scheme] || %w(http https)).include?(uri.scheme) + case uri.scheme when 'http', 'https' - return http_url_format_valid?(uri) + return http_url_format_valid?(uri, options) else return true end end - - def http_url_format_valid?(uri) - uri.host.present? and not uri.path.nil? + + def http_url_format_valid?(uri, options) + valid_format = uri.host.present? and not uri.path.nil? + return valid_format unless options[:require_domain] + + valid_format and uri.domain.present? end - + def url_accessible?(uri, options) return true unless options[:check_host] or options[:check_path] - + check_host = options[:check_host] - check_host ||= %w( http https ) if options[:check_path] - if (schemes = Array.wrap(check_host)) and schemes.all? { |scheme| scheme.kind_of?(String) } then + check_host ||= %w(http https) if options[:check_path] + if (schemes = Array.wrap(check_host)) and schemes.all? { |scheme| scheme.kind_of?(String) } return true unless schemes.include?(uri.scheme) end - + case uri.scheme when 'http', 'https' return http_url_accessible?(uri, options) @@ -174,16 +194,15 @@ def http_url_accessible?(uri, options) rescue return false end - - def url_response_valid?(response, record, attribute, value, options) + + def url_response_valid?(response, options) return true unless response.kind_of?(HTTPI::Response) and options[:check_path] - options[:response_callback].call(response, record, attribute, value) if options[:response_callback].respond_to?(:call) response_codes = options[:check_path] == true ? [400..499, 500..599] : Array.wrap(options[:check_path]).flatten return response_codes.none? do |code| # it's good if it's not a bad response case code # and it's a bad response if... when Range code.include? response.code - when Fixnum + when Integer code == response.code when Symbol CODES.include?(code) && CODES[code] == response.code diff --git a/spec/url_validator_spec.rb b/spec/url_validator_spec.rb index 5e6a365..6676443 100644 --- a/spec/url_validator_spec.rb +++ b/spec/url_validator_spec.rb @@ -11,229 +11,233 @@ class Record @record = Record.new end - context "[basic]" do + context '[basic]' do it "should allow nil if :allow_nil is set" do - @validator = UrlValidator.new(:attributes => [ :field ], :allow_nil => true) + @validator = UrlValidator.new(attributes: %i(field), allow_nil: true) @validator.validate_each(@record, :field, nil) - @record.errors.should be_empty + expect(@record.errors).to be_empty end it "should allow \"\" if :allow_blank is set" do - @validator = UrlValidator.new(:attributes => [ :field ], :allow_blank => true) + @validator = UrlValidator.new(attributes: %i(field), allow_blank: true) @validator.validate_each(@record, :field, "") - @record.errors.should be_empty + expect(@record.errors).to be_empty end end - - context "[format]" do + + context '[format]' do it "should only allow HTTP URLs if :scheme is set to 'http'" do - @validator = UrlValidator.new(:attributes => [ :field ], :scheme => 'http') - @validator.validate_each(@record, :field, "http://www.apple.com") - @record.errors.should be_empty + @validator = UrlValidator.new(attributes: %i(field), scheme: 'http') + @validator.validate_each(@record, :field, 'http://www.apple.com') + expect(@record.errors).to be_empty - @validator.validate_each(@record, :field, "https://www.apple.com") - @record.errors[:field].first.should include('invalid_url') + @validator.validate_each(@record, :field, 'https://www.apple.com') + expect(@record.errors[:field].first).to include('invalid_url') end - it "should only allow HTTP and HTTPS URLs if :scheme is set to %w( http https )" do - @validator = UrlValidator.new(:attributes => [ :field ], :scheme => %w( http https )) - @validator.validate_each(@record, :field, "http://www.apple.com") - @record.errors.should be_empty - @validator.validate_each(@record, :field, "https://www.apple.com") - @record.errors.should be_empty + it "should only allow HTTP and HTTPS URLs if :scheme is set to %w(http https)" do + @validator = UrlValidator.new(attributes: %i(field), scheme: %w(http https)) + @validator.validate_each(@record, :field, 'http://www.apple.com') + expect(@record.errors).to be_empty + @validator.validate_each(@record, :field, 'https://www.apple.com') + expect(@record.errors).to be_empty - @validator.validate_each(@record, :field, "ftp://www.apple.com") - @record.errors[:field].first.should include('invalid_url') + @validator.validate_each(@record, :field, 'ftp://www.apple.com') + expect(@record.errors[:field].first).to include('invalid_url') end it "should try a default scheme if :default_scheme is set" do - @validator = UrlValidator.new(:attributes => [ :field ], :scheme => 'http', :default_scheme => 'http') - @validator.validate_each(@record, :field, "www.apple.com") - @record.errors.should be_empty + @validator = UrlValidator.new(attributes: %i(field), scheme: 'http', default_scheme: 'http') + @validator.validate_each(@record, :field, 'www.apple.com') + expect(@record.errors).to be_empty end - - context "[HTTP(S)]" do + + it "should only allow URLs with a domain if :require_domain is set" do + @validator = UrlValidator.new(attributes: %i(field), require_domain: true) + @validator.validate_each(@record, :field, 'http://apple.com') + expect(@record.errors).to be_empty + + @validator.validate_each(@record, :field, 'http://apple') + expect(@record.errors[:field].first).to include('invalid_url') + end + + context '[HTTP(S)]' do it "should not allow garbage URLs that still somehow pass the ridiculously open-ended RFC" do - @validator = UrlValidator.new(:attributes => [ :field ]) - - [ - 'http:sdg.sdfg/', - 'http/sdg.d', - 'http:://dsfg.dsfg/', - 'http//sdg..g', - 'http://://sdfg.f', - 'http://dsaf.com://sdg.com' - ].each do |uri| + @validator = UrlValidator.new(attributes: %i(field)) + + %w( + http:sdg.sdfg/ + http/sdg.d + http:://dsfg.dsfg/ + http//sdg..g + http://://sdfg.f).each do |uri| @record.errors.clear - @validator.validate_each(@record, :field, "www.apple.com") - @record.errors[:field].first.should include('invalid_url') + @validator.validate_each(@record, :field, uri) + expect(@record.errors[:field].first).to include('invalid_url') end end + + it "should not allow invalid scheme formats" do + @validator = UrlValidator.new(attributes: %i(field)) + @validator.validate_each(@record, :field, ' https://www.apple.com') + expect(@record.errors[:field].first).to include('invalid_url') + end end end - - context "[accessibility]" do - context "[:check_host]" do + + context '[accessibility]' do + context '[:check_host]' do it "should only validate if the host is accessible when :check_host is set" do - @validator = UrlValidator.new(:attributes => [ :field ]) - @validator.validate_each(@record, :field, "http://www.invalid.tld") - @record.errors.should be_empty + @validator = UrlValidator.new(attributes: %i(field)) + @validator.validate_each(@record, :field, 'http://www.invalid.tld') + expect(@record.errors).to be_empty - @validator = UrlValidator.new(:attributes => [ :field ], :check_host => true) - @validator.validate_each(@record, :field, "http://www.invalid.tld") - @record.errors[:field].first.should include('url_not_accessible') + @validator = UrlValidator.new(attributes: %i(field), check_host: true) + @validator.validate_each(@record, :field, 'http://www.invalid.tld') + expect(@record.errors[:field].first).to include('url_not_accessible') end it "should not perform the accessibility check if :check_host is set to 'http' and the URL scheme is not HTTP" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_host => 'http') - @validator.validate_each(@record, :field, "https://www.invalid.tld") - @record.errors.should be_empty + @validator = UrlValidator.new(attributes: %i(field), check_host: 'http') + @validator.validate_each(@record, :field, 'https://www.invalid.tld') + expect(@record.errors).to be_empty end it "should only validate if the host is accessible when :check_host is set to 'http' and the URL scheme is HTTP" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_host => 'http') - @validator.validate_each(@record, :field, "http://www.invalid.tld") - @record.errors[:field].first.should include('url_not_accessible') + @validator = UrlValidator.new(attributes: %i(field), check_host: 'http') + @validator.validate_each(@record, :field, 'http://www.invalid.tld') + expect(@record.errors[:field].first).to include('url_not_accessible') end - it "should not perform the accessibility check if :check_host is set to %w( http https ) and the URL scheme is not HTTP(S)" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_host => %w( http https ), :scheme => %w( ftp http https )) - @validator.validate_each(@record, :field, "ftp://www.invalid.tld") - @record.errors.should be_empty + it "should not perform the accessibility check if :check_host is set to %w(http https) and the URL scheme is not HTTP(S)" do + @validator = UrlValidator.new(attributes: %i(field), check_host: %w(http https), scheme: %w(ftp http https)) + @validator.validate_each(@record, :field, 'ftp://www.invalid.tld') + expect(@record.errors).to be_empty end - - it "should only validate if the host is accessible when :check_host is set to %w( http https ) and the URL scheme is HTTP(S)" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_host => %w( http https )) - @validator.validate_each(@record, :field, "http://www.invalid.tld") - @record.errors[:field].first.should include('url_not_accessible') - @validator = UrlValidator.new(:attributes => [ :field ], :check_host => %w( http https )) - @validator.validate_each(@record, :field, "https://www.invalid.tld") - @record.errors[:field].first.should include('url_not_accessible') + it "should only validate if the host is accessible when :check_host is set to %w(http https) and the URL scheme is HTTP(S)" do + @validator = UrlValidator.new(attributes: %i(field), check_host: %w(http https)) + @validator.validate_each(@record, :field, 'http://www.invalid.tld') + expect(@record.errors[:field].first).to include('url_not_accessible') + + @validator = UrlValidator.new(attributes: %i(field), check_host: %w(http https)) + @validator.validate_each(@record, :field, 'https://www.invalid.tld') + expect(@record.errors[:field].first).to include('url_not_accessible') end it "should only validate the host" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_host => true) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors.should be_empty + @validator = UrlValidator.new(attributes: %i(field), check_host: true) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors).to be_empty end end - - context "[:check_path]" do - it "should not validate if the response code is equal to the Fixnum value of this option" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => 404) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].first.should include('url_invalid_response') - + + context '[:check_path]' do + it "should not validate if the response code is equal to the Integer value of this option" do + @validator = UrlValidator.new(attributes: %i(field), check_path: 404) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field].first).to include('url_invalid_response') + @record.errors.clear - - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => 405) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].should be_empty + + @validator = UrlValidator.new(attributes: %i(field), check_path: 405) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field]).to be_empty end - + it "should not validate if the response code is equal to the Symbol value of this option" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => :not_found) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].first.should include('url_invalid_response') - + @validator = UrlValidator.new(attributes: %i(field), check_path: :not_found) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field].first).to include('url_invalid_response') + @record.errors.clear - - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => :unauthorized) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].should be_empty + + @validator = UrlValidator.new(attributes: %i(field), check_path: :unauthorized) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field]).to be_empty end - + it "should not validate if the response code is within the Range value of this option" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => 400..499) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].first.should include('url_invalid_response') - + @validator = UrlValidator.new(attributes: %i(field), check_path: 400..499) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field].first).to include('url_invalid_response') + @record.errors.clear - - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => 500..599) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].should be_empty - end - - it "should not validate if the response code is equal to the Fixnum value contained in the Array value of this option" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => [ 404, 405 ]) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].first.should include('url_invalid_response') - + + @validator = UrlValidator.new(attributes: %i(field), check_path: 500..599) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field]).to be_empty + end + + it "should not validate if the response code is equal to the Integer value contained in the Array value of this option" do + @validator = UrlValidator.new(attributes: %i(field), check_path: [404, 405]) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field].first).to include('url_invalid_response') + @record.errors.clear - - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => [ 405, 406 ]) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].should be_empty + + @validator = UrlValidator.new(attributes: %i(field), check_path: [405, 406]) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field]).to be_empty end - + it "should not validate if the response code is equal to the Symbol value contained in the Array value of this option" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => [ :not_found, :unauthorized ]) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].first.should include('url_invalid_response') - + @validator = UrlValidator.new(attributes: %i(field), check_path: %i(not_found unauthorized)) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field].first).to include('url_invalid_response') + @record.errors.clear - - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => [ :unauthorized, :moved_permanently ]) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].should be_empty + + @validator = UrlValidator.new(attributes: %i(field), check_path: %i(unauthorized moved_permanently)) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field]).to be_empty end - + it "should not validate if the response code is equal to the Range value contained in the Array value of this option" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => [ 400..499, 500..599 ]) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].first.should include('url_invalid_response') - + @validator = UrlValidator.new(attributes: %i(field), check_path: [400..499, 500..599]) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field].first).to include('url_invalid_response') + @record.errors.clear - - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => [ 500..599, 300..399 ]) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].should be_empty + + @validator = UrlValidator.new(attributes: %i(field), check_path: [500..599, 300..399]) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field]).to be_empty end - + it "should skip validation by default" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => nil) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].should be_empty + @validator = UrlValidator.new(attributes: %i(field), check_path: nil) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field]).to be_empty end - + it "should not validate 4xx and 5xx response codes if the value is true" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => true) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - @record.errors[:field].first.should include('url_invalid_response') + @validator = UrlValidator.new(attributes: %i(field), check_path: true) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(@record.errors[:field].first).to include('url_invalid_response') end - + it "should skip validation for non-HTTP URLs" do - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => true, :scheme => %w( ftp http https )) - @validator.validate_each(@record, :field, "ftp://ftp.sdgasdgohaodgh.com/sdgjsdg") - @record.errors[:field].should be_empty + @validator = UrlValidator.new(attributes: %i(field), check_path: true, scheme: %w(ftp http https)) + @validator.validate_each(@record, :field, 'ftp://ftp.sdgasdgohaodgh.com/sdgjsdg') + expect(@record.errors[:field]).to be_empty end end - - context "[:httpi_adapter]" do + + context '[:httpi_adapter]' do it "should use the specified HTTPI adapter" do - @validator = UrlValidator.new(:attributes => [ :field ], :httpi_adapter => :curl, :check_host => true) - HTTPI.should_receive(:get).once.with(an_instance_of(HTTPI::Request), :curl).and_return(false) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - end - end - - context "[:request_callback]" do - it "should be yielded the HTTPI request" do - called = false - @validator = UrlValidator.new(:attributes => [ :field ], :check_host => true, :request_callback => lambda { |request| called = true; request.should be_kind_of(HTTPI::Request) }) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - called.should be_true + @validator = UrlValidator.new(attributes: %i(field), httpi_adapter: :curl, check_host: true) + expect(HTTPI).to receive(:get).once.with(an_instance_of(HTTPI::Request), :curl).and_return(false) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') end end - context "[:response_callback]" do - it "should be yielded the HTTPI response" do - called = false - @validator = UrlValidator.new(:attributes => [ :field ], :check_path => true, :response_callback => lambda { |response, record, attribute, value| called = true; response.should be_kind_of(HTTPI::Response) }) - @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - called.should be_true + context '[:request_callback]' do + it "should be yielded the HTTPI request" do + called = false + @validator = UrlValidator.new(attributes: %i(field), check_host: true, request_callback: ->(request) { called = true; expect(request).to be_kind_of(HTTPI::Request) }) + @validator.validate_each(@record, :field, 'http://www.google.com/sdgsdgf') + expect(called).to eql(true) end end end diff --git a/url_validation.gemspec b/url_validation.gemspec index 3ad181b..af643e0 100644 --- a/url_validation.gemspec +++ b/url_validation.gemspec @@ -1,28 +1,32 @@ -# Generated by jeweler +# Generated by juwelier # DO NOT EDIT THIS FILE DIRECTLY -# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' +# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- +# stub: url_validation 1.2.0 ruby lib Gem::Specification.new do |s| - s.name = %q{url_validation} - s.version = "1.0.0" + s.name = "url_validation".freeze + s.version = "1.2.0" - s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= - s.authors = [%q{Tim Morgan}] - s.date = %q{2011-05-27} - s.description = %q{A simple, localizable EachValidator for URL fields in ActiveRecord 3.0.} - s.email = %q{git@timothymorgan.info} + s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version= + s.require_paths = ["lib".freeze] + s.authors = ["Tim Morgan".freeze] + s.date = "2017-05-31" + s.description = "A simple, localizable EachValidator for URL fields in ActiveRecord 3.0.".freeze + s.email = "git@timothymorgan.info".freeze s.extra_rdoc_files = [ "LICENSE", - "README.textile" + "README.md" ] s.files = [ ".document", ".rspec", + ".ruby-gemset", + ".ruby-version", "Gemfile", "Gemfile.lock", "LICENSE", - "README.textile", + "README.md", "Rakefile", "VERSION", "lib/url_validation.rb", @@ -30,43 +34,42 @@ Gem::Specification.new do |s| "spec/url_validator_spec.rb", "url_validation.gemspec" ] - s.homepage = %q{http://github.com/riscfuture/url_validation} - s.require_paths = [%q{lib}] - s.required_ruby_version = Gem::Requirement.new(">= 1.8.7") - s.rubygems_version = %q{1.8.3} - s.summary = %q{Simple URL validation in Rails 3} + s.homepage = "http://github.com/riscfuture/url_validation".freeze + s.required_ruby_version = Gem::Requirement.new(">= 2.0.0".freeze) + s.rubygems_version = "2.6.12".freeze + s.summary = "Simple URL validation in Rails 3+".freeze if s.respond_to? :specification_version then - s.specification_version = 3 + s.specification_version = 4 if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_runtime_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) - s.add_development_dependency(%q, [">= 0"]) + s.add_runtime_dependency(%q.freeze, [">= 0"]) + s.add_runtime_dependency(%q.freeze, [">= 0"]) + s.add_runtime_dependency(%q.freeze, [">= 0"]) + s.add_runtime_dependency(%q.freeze, [">= 0"]) + s.add_development_dependency(%q.freeze, [">= 0"]) + s.add_development_dependency(%q.freeze, [">= 0"]) + s.add_development_dependency(%q.freeze, [">= 0"]) + s.add_development_dependency(%q.freeze, [">= 0"]) else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) end else - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) - s.add_dependency(%q, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) + s.add_dependency(%q.freeze, [">= 0"]) end end