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..7d2ed7c --- /dev/null +++ b/.ruby-version @@ -0,0 +1 @@ +2.1.4 diff --git a/Gemfile b/Gemfile index b6a9705..f35b878 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,4 @@ -source :rubygems +source 'https://rubygems.org' gem 'addressable', :require => 'addressable/uri' # for unicode URIs gem 'activesupport' diff --git a/Gemfile.lock b/Gemfile.lock index bbf69f5..e66d772 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,43 +1,86 @@ 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) + RedCloth (4.2.9) + activemodel (4.1.7) + activesupport (= 4.1.7) + builder (~> 3.1) + activerecord (4.1.7) + activemodel (= 4.1.7) + activesupport (= 4.1.7) + arel (~> 5.0.0) + activesupport (4.1.7) + i18n (~> 0.6, >= 0.6.9) + json (~> 1.7, >= 1.7.7) + minitest (~> 5.1) + thread_safe (~> 0.1) + tzinfo (~> 1.1) + addressable (2.3.6) + arel (5.0.1.20140414130214) + builder (3.2.2) + descendants_tracker (0.0.4) + thread_safe (~> 0.3, >= 0.3.1) + diff-lcs (1.2.5) + faraday (0.9.0) + multipart-post (>= 1.2, < 3) + git (1.2.8) + github_api (0.12.2) + addressable (~> 2.3) + descendants_tracker (~> 0.0.4) + faraday (~> 0.8, < 0.10) + hashie (>= 3.3) + multi_json (>= 1.7.5, < 2.0) + nokogiri (~> 1.6.3) + oauth2 + hashie (3.3.1) + highline (1.6.21) + httpi (2.2.7) rack - i18n (0.5.0) - jeweler (1.6.0) - bundler (~> 1.0.0) + i18n (0.6.11) + jeweler (2.0.1) + builder + bundler (>= 1.0) git (>= 1.2.5) + github_api + highline (>= 1.6.15) + nokogiri (>= 1.5.10) 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 + json (1.8.1) + jwt (1.0.0) + mini_portile (0.6.1) + minitest (5.4.2) + multi_json (1.10.1) + multi_xml (0.5.5) + multipart-post (2.0.0) + nokogiri (1.6.4.1) + mini_portile (~> 0.6.0) + oauth2 (1.0.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (~> 1.2) + rack (1.5.2) + rake (10.3.2) + rdoc (4.1.2) + json (~> 1.4) + rspec (3.1.0) + rspec-core (~> 3.1.0) + rspec-expectations (~> 3.1.0) + rspec-mocks (~> 3.1.0) + rspec-core (3.1.7) + rspec-support (~> 3.1.0) + rspec-expectations (3.1.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.1.0) + rspec-mocks (3.1.3) + rspec-support (~> 3.1.0) + rspec-support (3.1.2) + thread_safe (0.3.4) + tzinfo (1.2.2) + thread_safe (~> 0.1) + yard (0.8.7.6) PLATFORMS ruby diff --git a/VERSION b/VERSION index afaf360..1cc5f65 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.0.0 \ No newline at end of file +1.1.0 \ No newline at end of file diff --git a/lib/url_validation.rb b/lib/url_validation.rb index 0313568..41a576e 100644 --- a/lib/url_validation.rb +++ b/lib/url_validation.rb @@ -2,6 +2,7 @@ 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: # @@ -59,7 +60,6 @@ # h3. 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. | class UrlValidator < ActiveModel::EachValidator # @private @@ -120,23 +120,27 @@ class UrlValidator < ActiveModel::EachValidator # @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] then + uri = Addressable::URI.parse("#{options[:default_scheme]}://#{value}") + end + rescue Addressable::URI::InvalidURIError + record.errors.add(attribute, options[:invalid_url_message] || :invalid_url) unless 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[: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.try(:scheme)) case uri.scheme when 'http', 'https' @@ -175,9 +179,8 @@ def http_url_accessible?(uri, options) 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... diff --git a/spec/url_validator_spec.rb b/spec/url_validator_spec.rb index 5e6a365..6b2d6d5 100644 --- a/spec/url_validator_spec.rb +++ b/spec/url_validator_spec.rb @@ -15,13 +15,13 @@ class Record it "should allow nil if :allow_nil is set" do @validator = UrlValidator.new(:attributes => [ :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.validate_each(@record, :field, "") - @record.errors.should be_empty + expect(@record.errors).to be_empty end end @@ -29,27 +29,27 @@ class Record 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 + expect(@record.errors).to be_empty @validator.validate_each(@record, :field, "https://www.apple.com") - @record.errors[:field].first.should include('invalid_url') + 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 + expect(@record.errors).to be_empty @validator.validate_each(@record, :field, "https://www.apple.com") - @record.errors.should be_empty + expect(@record.errors).to be_empty @validator.validate_each(@record, :field, "ftp://www.apple.com") - @record.errors[:field].first.should include('invalid_url') + 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 + expect(@record.errors).to be_empty end context "[HTTP(S)]" do @@ -66,7 +66,7 @@ class Record ].each do |uri| @record.errors.clear @validator.validate_each(@record, :field, "www.apple.com") - @record.errors[:field].first.should include('invalid_url') + expect(@record.errors[:field].first).to include('invalid_url') end end end @@ -77,45 +77,45 @@ class Record 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 + 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') + 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 + 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') + 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 + 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') + expect(@record.errors[:field].first).to 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') + 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 + expect(@record.errors).to be_empty end end @@ -123,98 +123,98 @@ class Record 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') + 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 + 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') + 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 + 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') + 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 + expect(@record.errors[:field]).to 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') + 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 + 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') + 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 + 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') + 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 + 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 + 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') + 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 + expect(@record.errors[:field]).to be_empty end end 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) + 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 @@ -222,18 +222,9 @@ class Record 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 = UrlValidator.new(:attributes => [ :field ], :check_host => true, :request_callback => lambda { |request| called = true; expect(request).to be_kind_of(HTTPI::Request) }) @validator.validate_each(@record, :field, "http://www.google.com/sdgsdgf") - called.should be_true - 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 + expect(called).to eql(true) end end end diff --git a/url_validation.gemspec b/url_validation.gemspec index 3ad181b..992c4c9 100644 --- a/url_validation.gemspec +++ b/url_validation.gemspec @@ -2,16 +2,18 @@ # DO NOT EDIT THIS FILE DIRECTLY # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec' # -*- encoding: utf-8 -*- +# stub: url_validation 1.1.0 ruby lib Gem::Specification.new do |s| - s.name = %q{url_validation} - s.version = "1.0.0" + s.name = "url_validation" + s.version = "1.1.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.require_paths = ["lib"] + s.authors = ["Tim Morgan"] + s.date = "2014-11-08" + s.description = "A simple, localizable EachValidator for URL fields in ActiveRecord 3.0." + s.email = "git@timothymorgan.info" s.extra_rdoc_files = [ "LICENSE", "README.textile" @@ -19,6 +21,8 @@ Gem::Specification.new do |s| s.files = [ ".document", ".rspec", + ".ruby-gemset", + ".ruby-version", "Gemfile", "Gemfile.lock", "LICENSE", @@ -30,14 +34,13 @@ 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.homepage = "http://github.com/riscfuture/url_validation" 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.rubygems_version = "2.4.2" + s.summary = "Simple URL validation in Rails 3" 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"])