ndd-url_checker 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 6ffa576d85139cd2300b55ba9c340fdda8608850
4
+ data.tar.gz: 58835f36306bd29f9375f0f6c65afc7045b7e411
5
+ SHA512:
6
+ metadata.gz: 84a715175f934072d1b5ec7244302d4c8385d7db3d724d1ac7596e9e98ca00f58bccf12f68c4d88ef4cac4fb865f7ea192f435bcc939783e0dd8a4fcec77281b
7
+ data.tar.gz: 62ee300dbe403c80a42e8387dc79657549c9bbf0f0be3d3788ed692d2847517bc70ecd460a130be42517d6086b909885b7e47dc29c48c79f72e5ab0eebb80e37
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour --format documentation --tag focused
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ ndd-url_checker
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-2.1.5
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.5
5
+ before_install:
6
+ - gem update --system
7
+ - gem --version
8
+ branches:
9
+ only:
10
+ - master
11
+ - development
12
+ addons:
13
+ code_climate:
14
+ repo_token:
15
+ secure: "jwblN6fY+HN0u+JfNWKNRNRExOlDH0NbuPprLxqac3kjE3ER11pJieXJKOoeFMcYJ8Z/33qPn4KhOPeYV/0HcZW5YD3G9pjry+c2ha9S7YjIQi8O0BR/iUmqzhvYOT6OoR+khJl9pf5/li3OfXUbAVNdcEDAKARuzl/XnGpqsdE="
data/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # NDD URL Checker changelog
2
+
3
+ ## Version 0.1.0
4
+
5
+ * initial commit
data/Gemfile ADDED
@@ -0,0 +1,50 @@
1
+ source 'http://rubygems.org'
2
+
3
+ HOST_OS = RbConfig::CONFIG['host_os']
4
+
5
+
6
+ # ------------------------------------------------------------------------------
7
+ # Dependencies required to use the gem.
8
+ gem 'cod', '~> 0.6'
9
+ gem 'logging', '~> 1.8'
10
+
11
+
12
+ # ------------------------------------------------------------------------------
13
+ # Dependencies to develop the gem.
14
+ # Everything needed to run rake, tests, features, etc.
15
+ group :development do
16
+
17
+ gem 'bundler', '~> 1.7', require: false
18
+ gem 'guard', '~> 2.8', require: false
19
+ gem 'guard-bundler', '~> 2.0', require: false
20
+ gem 'guard-rspec', '~> 4.3', require: false
21
+ gem 'guard-spork', '~> 2.0', require: false
22
+ gem 'jeweler', '~> 2.0', require: false
23
+ gem 'rdoc', '~> 4.1', require: false
24
+ gem 'rspec', '~> 3.1', require: false
25
+ gem 'rspec-collection_matchers', '~> 1.1', require: false
26
+ gem 'simplecov', '~> 0.9', require: false
27
+ gem 'spork', '~> 0.9', require: false
28
+ gem 'webmock', '~> 1.20', require: false
29
+ gem 'yard', '~> 0.8', require: false
30
+
31
+ case HOST_OS
32
+ when /darwin/i
33
+ gem 'growl'
34
+ gem 'rb-fsevent'
35
+ when /linux/i
36
+ gem 'libnotify'
37
+ gem 'rb-inotify'
38
+ when /mswin|windows/i
39
+ gem 'rb-fchange'
40
+ gem 'rb-notifu'
41
+ gem 'win32console'
42
+ else
43
+ raise "Platform '#{HOST_OS}' is not supported"
44
+ end
45
+
46
+ end
47
+
48
+ group :test do
49
+ gem 'codeclimate-test-reporter', '~> 0.4', require: false
50
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,152 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ addressable (2.3.6)
5
+ builder (3.2.2)
6
+ celluloid (0.16.0)
7
+ timers (~> 4.0.0)
8
+ childprocess (0.5.5)
9
+ ffi (~> 1.0, >= 1.0.11)
10
+ cod (0.6.0)
11
+ codeclimate-test-reporter (0.4.3)
12
+ simplecov (>= 0.7.1, < 1.0.0)
13
+ coderay (1.1.0)
14
+ crack (0.4.2)
15
+ safe_yaml (~> 1.0.0)
16
+ descendants_tracker (0.0.4)
17
+ thread_safe (~> 0.3, >= 0.3.1)
18
+ diff-lcs (1.2.5)
19
+ docile (1.1.5)
20
+ faraday (0.9.0)
21
+ multipart-post (>= 1.2, < 3)
22
+ ffi (1.9.6)
23
+ formatador (0.2.5)
24
+ git (1.2.8)
25
+ github_api (0.12.2)
26
+ addressable (~> 2.3)
27
+ descendants_tracker (~> 0.0.4)
28
+ faraday (~> 0.8, < 0.10)
29
+ hashie (>= 3.3)
30
+ multi_json (>= 1.7.5, < 2.0)
31
+ nokogiri (~> 1.6.3)
32
+ oauth2
33
+ guard (2.8.2)
34
+ formatador (>= 0.2.4)
35
+ listen (~> 2.7)
36
+ lumberjack (~> 1.0)
37
+ pry (>= 0.9.12)
38
+ thor (>= 0.18.1)
39
+ guard-bundler (2.0.0)
40
+ bundler (~> 1.0)
41
+ guard (~> 2.2)
42
+ guard-compat (0.3.0)
43
+ guard-rspec (4.4.2)
44
+ guard (~> 2.1)
45
+ guard-compat (~> 0.1)
46
+ rspec (>= 2.99.0, < 4.0)
47
+ guard-spork (2.0.1)
48
+ childprocess (>= 0.2.3)
49
+ guard (~> 2.8.2)
50
+ spork (>= 0.8.4)
51
+ hashie (3.3.2)
52
+ highline (1.6.21)
53
+ hitimes (1.2.2)
54
+ jeweler (2.0.1)
55
+ builder
56
+ bundler (>= 1.0)
57
+ git (>= 1.2.5)
58
+ github_api
59
+ highline (>= 1.6.15)
60
+ nokogiri (>= 1.5.10)
61
+ rake
62
+ rdoc
63
+ json (1.8.1)
64
+ jwt (1.2.0)
65
+ libnotify (0.8.4)
66
+ ffi (>= 1.0.11)
67
+ listen (2.8.3)
68
+ celluloid (>= 0.15.2)
69
+ rb-fsevent (>= 0.9.3)
70
+ rb-inotify (>= 0.9)
71
+ little-plugger (1.1.3)
72
+ logging (1.8.2)
73
+ little-plugger (>= 1.1.3)
74
+ multi_json (>= 1.8.4)
75
+ lumberjack (1.0.9)
76
+ method_source (0.8.2)
77
+ mini_portile (0.6.1)
78
+ multi_json (1.10.1)
79
+ multi_xml (0.5.5)
80
+ multipart-post (2.0.0)
81
+ nokogiri (1.6.5)
82
+ mini_portile (~> 0.6.0)
83
+ oauth2 (1.0.0)
84
+ faraday (>= 0.8, < 0.10)
85
+ jwt (~> 1.0)
86
+ multi_json (~> 1.3)
87
+ multi_xml (~> 0.5)
88
+ rack (~> 1.2)
89
+ pry (0.10.1)
90
+ coderay (~> 1.1.0)
91
+ method_source (~> 0.8.1)
92
+ slop (~> 3.4)
93
+ rack (1.5.2)
94
+ rake (10.4.2)
95
+ rb-fsevent (0.9.4)
96
+ rb-inotify (0.9.5)
97
+ ffi (>= 0.5.0)
98
+ rdoc (4.2.0)
99
+ json (~> 1.4)
100
+ rspec (3.1.0)
101
+ rspec-core (~> 3.1.0)
102
+ rspec-expectations (~> 3.1.0)
103
+ rspec-mocks (~> 3.1.0)
104
+ rspec-collection_matchers (1.1.2)
105
+ rspec-expectations (>= 2.99.0.beta1)
106
+ rspec-core (3.1.7)
107
+ rspec-support (~> 3.1.0)
108
+ rspec-expectations (3.1.2)
109
+ diff-lcs (>= 1.2.0, < 2.0)
110
+ rspec-support (~> 3.1.0)
111
+ rspec-mocks (3.1.3)
112
+ rspec-support (~> 3.1.0)
113
+ rspec-support (3.1.2)
114
+ safe_yaml (1.0.4)
115
+ simplecov (0.9.1)
116
+ docile (~> 1.1.0)
117
+ multi_json (~> 1.0)
118
+ simplecov-html (~> 0.8.0)
119
+ simplecov-html (0.8.0)
120
+ slop (3.6.0)
121
+ spork (0.9.2)
122
+ thor (0.19.1)
123
+ thread_safe (0.3.4)
124
+ timers (4.0.1)
125
+ hitimes
126
+ webmock (1.20.4)
127
+ addressable (>= 2.3.6)
128
+ crack (>= 0.3.2)
129
+ yard (0.8.7.6)
130
+
131
+ PLATFORMS
132
+ ruby
133
+
134
+ DEPENDENCIES
135
+ bundler (~> 1.7)
136
+ cod (~> 0.6)
137
+ codeclimate-test-reporter (~> 0.4)
138
+ guard (~> 2.8)
139
+ guard-bundler (~> 2.0)
140
+ guard-rspec (~> 4.3)
141
+ guard-spork (~> 2.0)
142
+ jeweler (~> 2.0)
143
+ libnotify
144
+ logging (~> 1.8)
145
+ rb-inotify
146
+ rdoc (~> 4.1)
147
+ rspec (~> 3.1)
148
+ rspec-collection_matchers (~> 1.1)
149
+ simplecov (~> 0.9)
150
+ spork (~> 0.9)
151
+ webmock (~> 1.20)
152
+ yard (~> 0.8)
data/Guardfile ADDED
@@ -0,0 +1,38 @@
1
+
2
+ # ----------------------------------------------------------------------------------------------------------------------
3
+ # bundler
4
+ # ----------------------------------------------------------------------------------------------------------------------
5
+
6
+ guard 'bundler' do
7
+ watch('Gemfile')
8
+ end
9
+
10
+
11
+ # ----------------------------------------------------------------------------------------------------------------------
12
+ # spork (must be before rspec and cucumber)
13
+ # ----------------------------------------------------------------------------------------------------------------------
14
+
15
+ guard 'spork',
16
+ wait: 60 do
17
+
18
+ watch('Gemfile')
19
+ watch('Gemfile.lock')
20
+
21
+ # ----- spec directory
22
+ watch('spec/spec_helper.rb')
23
+ end
24
+
25
+
26
+ # ----------------------------------------------------------------------------------------------------------------------
27
+ # rspec
28
+ # ----------------------------------------------------------------------------------------------------------------------
29
+
30
+ guard :rspec, cmd: 'bundle exec rspec' do
31
+
32
+ # ----- lib directory
33
+ watch(%r{^lib/(.+)\.rb$}) { |m| %W(spec/#{m[1]}_spec.rb) }
34
+
35
+ # ----- spec directory
36
+ watch(%r{^spec/.+_spec\.rb$})
37
+ watch(%r{^spec/support/(.+)\.rb$}) { %W(spec) }
38
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 David DIDIER
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NON-INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # NDD URL Checker
2
+
3
+ [![Build Status](https://secure.travis-ci.org/ddidier/ndd-url_checker.png)](http://travis-ci.org/ddidier/ndd-url_checker)
4
+ [![Dependency Status](https://gemnasium.com/ddidier/ndd-url_checker.png)](https://gemnasium.com/ddidier/ndd-url_checker)
5
+ [![Code Climate](https://codeclimate.com/github/ddidier/ndd-url_checker/badges/gpa.svg)](https://codeclimate.com/github/ddidier/ndd-url_checker)
6
+ [![Test Coverage](https://codeclimate.com/github/ddidier/ndd-url_checker/badges/coverage.svg)](https://codeclimate.com/github/ddidier/ndd-url_checker)
7
+
8
+ URL validator.
9
+
10
+ ## Prerequisites
11
+
12
+ This gem is tested with Ruby 2.1.x.
13
+
14
+ ## TODO
15
+
16
+ ## Copyright
17
+
18
+ Copyright (c) 2014 David DIDIER.
19
+ See `LICENSE.txt` for further details.
data/Rakefile ADDED
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts 'Run `bundle install` to install missing gems'
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification
17
+ # see http://guides.rubygems.org/specification-reference/ for more options
18
+ gem.name = 'ndd-url_checker'
19
+ gem.summary = 'Validate URLs'
20
+ gem.description = 'Validate URLs'
21
+ gem.homepage = 'http://github.com/ddidier/ndd-url_checker'
22
+ gem.license = 'MIT'
23
+ gem.email = 'c_inconnu2@yahoo.fr'
24
+ gem.authors = ['David DIDIER']
25
+ # dependencies defined in Gemfile
26
+ end
27
+ Jeweler::RubygemsDotOrgTasks.new
28
+
29
+ require 'rspec/core'
30
+ require 'rspec/core/rake_task'
31
+ RSpec::Core::RakeTask.new(:spec) do |spec|
32
+ spec.pattern = FileList['spec/**/*_spec.rb']
33
+ end
34
+
35
+ desc 'Code coverage detail'
36
+ task :simplecov do
37
+ ENV['COVERAGE'] = 'true'
38
+ Rake::Task['spec'].execute
39
+ end
40
+
41
+ task :default => :spec
42
+
43
+ require 'rdoc/task'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "NDD URL Checker #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,6 @@
1
+ require 'ndd/url_checker/abstract_url_checker'
2
+ require 'ndd/url_checker/blocking_url_checker'
3
+ require 'ndd/url_checker/forked_url_checker'
4
+ require 'ndd/url_checker/parallel_url_checker'
5
+ require 'ndd/url_checker/status'
6
+ require 'ndd/url_checker/threaded_url_checker'
@@ -0,0 +1,30 @@
1
+ require 'ndd/url_checker/status'
2
+
3
+ module NDD
4
+ module UrlChecker
5
+
6
+ # Abstract class, not very ruby-ish :)
7
+ # @author David DIDIER
8
+ class AbstractUrlChecker
9
+
10
+ # Checks that the given URL are valid.
11
+ # If there is only a single URL parameter, returns a NDD::UrlChecker::Status.
12
+ # If there is only multiple URL parameters, returns a Hash of NDD::UrlChecker::Status indexed by their URI.
13
+ # @param urls [String|Array<String>] the URLs to check.
14
+ # @return [NDD::UrlChecker::Status|Hash<String => NDD::UrlChecker::Status>]
15
+ def check(*urls)
16
+ raise 'NDD::UrlChecker::UrlChecker#check must be implemented'
17
+ end
18
+
19
+ # Validates that the given URL are valid.
20
+ # If there is only a single URL parameter, returns a boolean.
21
+ # If there is only multiple URL parameters, returns a Hash of boolean indexed by their URI.
22
+ # @param urls [String|Array<String>] the URLs to validate.
23
+ # @return [NDD::UrlChecker::Status|Hash<String => Boolean>]
24
+ def validate(*urls)
25
+ raise 'NDD::UrlChecker::UrlChecker#validate must be implemented'
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,134 @@
1
+ require 'logging'
2
+ require 'ndd/url_checker/abstract_url_checker'
3
+ require 'ndd/url_checker/status'
4
+ require 'net/http'
5
+ require 'net/https'
6
+
7
+ module NDD
8
+ module UrlChecker
9
+
10
+ # An URL checker using the blocking Net::HTTP class.
11
+ # @author David DIDIER
12
+ class BlockingUrlChecker < AbstractUrlChecker
13
+
14
+ # Create a new instance.
15
+ # @param [Fixnum] maximum_redirects the maximum number of redirects before failing.
16
+ # @param [Fixnum] timeout the number of seconds to wait before failing.
17
+ def initialize(maximum_redirects=5, timeout=5)
18
+ @logger = Logging.logger[self]
19
+ @maximum_redirects = maximum_redirects
20
+ @timeout = timeout
21
+ end
22
+
23
+ # Checks that the given URL are valid.
24
+ # If there is only a single URL parameter, returns a NDD::UrlChecker::Status.
25
+ # If there is only multiple URL parameters, returns a Hash of NDD::UrlChecker::Status indexed by their URI.
26
+ # @param [String|Array<String>] urls
27
+ # @return [NDD::UrlChecker::Status|Hash<String => NDD::UrlChecker::Status>]
28
+ def check(*urls)
29
+ @logger.info "Checking #{urls.size} URL(s)"
30
+ return check_single(urls.first) if urls.size == 1
31
+ Hash[urls.map { |url| [url, check_single(url)] }]
32
+ end
33
+
34
+ # Validates that the given URL are valid.
35
+ # If there is only a single URL parameter, returns a boolean.
36
+ # If there is only multiple URL parameters, returns a Hash of boolean indexed by their URI.
37
+ # @param [String|Array<String>] urls
38
+ # @return [NDD::UrlChecker::Status|Hash<String => Boolean>]
39
+ def validate(*urls)
40
+ @logger.info "Validating #{urls.size} URL(s)"
41
+ return validate_single(urls.first) if urls.size == 1
42
+ Hash[urls.map { |url| [url, validate_single(url)] }]
43
+ end
44
+
45
+
46
+ private
47
+
48
+ # Checks that the given URL is valid.
49
+ # @param [String] url
50
+ # @return [NDD::UrlChecker::Status]
51
+ def check_single(url)
52
+ begin
53
+ @logger.debug "Checking: #{url}"
54
+ status = check_uri(URI.parse(url), Status.new(url))
55
+ rescue => error
56
+ status = if unknown_host?(error)
57
+ Status.new(url).unknown_host
58
+ else
59
+ Status.new(url).failed(error)
60
+ end
61
+ end
62
+ @logger.debug "Checked: #{url} -> #{status.code.upcase}"
63
+ status
64
+ end
65
+
66
+ # Validates that the given URL are valid.
67
+ # @param [String] url
68
+ # @return [Boolean]
69
+ def validate_single(url)
70
+ @logger.debug "Validating: #{url}"
71
+ check_single(url).valid?
72
+ end
73
+
74
+ # Checks that the given URL is valid.
75
+ # @param [URI::HTTP] uri the URI to check
76
+ # @param [NDD::UrlChecker::Status] status the current status of the stack
77
+ # @return [NDD::UrlChecker::Status]
78
+ def check_uri(uri, status)
79
+ if status.uris.size() > @maximum_redirects
80
+ return status.too_many_redirects
81
+ end
82
+
83
+ http = Net::HTTP.new(uri.host, uri.port)
84
+ http.read_timeout = @timeout
85
+ http.use_ssl = true if uri.scheme == 'https'
86
+ http.start do
87
+ path = (uri.path.empty?) ? '/' : uri.path
88
+ http.request_get(path) do |response|
89
+ case response
90
+ when Net::HTTPSuccess then
91
+ return on_success(uri, response, status)
92
+ when Net::HTTPRedirection then
93
+ return on_redirection(uri, response, status)
94
+ else
95
+ return on_error(uri, response, status)
96
+ end
97
+ end
98
+ end
99
+ end
100
+
101
+ def on_success(uri, response, status)
102
+ return status if status.redirected?
103
+ status.direct
104
+ end
105
+
106
+ def on_redirection(uri, response, status)
107
+ redirected_uri = redirected_uri(uri, response)
108
+ redirected_status = status.redirected(redirected_uri)
109
+ check_uri(redirected_uri, redirected_status)
110
+ end
111
+
112
+ def on_error(uri, response, status)
113
+ status.failed(response)
114
+ end
115
+
116
+ def redirected_uri(uri, response)
117
+ if response['location'].match(/https?:\/\//)
118
+ URI(response['location'])
119
+ else
120
+ # If the redirect is relative we need to build a new URI using the current URI as a base.
121
+ URI.join("#{uri.scheme}://#{uri.host}:#{uri.port}", response['location'])
122
+ end
123
+ end
124
+
125
+ # FIXME: platform dependent?
126
+ UNKNOWN_HOST_MESSAGE = 'getaddrinfo: Name or service not known'
127
+
128
+ def unknown_host?(error)
129
+ error.is_a?(SocketError) && error.message == UNKNOWN_HOST_MESSAGE
130
+ end
131
+
132
+ end
133
+ end
134
+ end