ndd-url_checker 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -2
- data/Guardfile +1 -1
- data/README.md +63 -3
- data/VERSION +1 -1
- data/lib/ndd/url_checker/abstract_url_checker.rb +5 -14
- data/lib/ndd/url_checker/blocking_url_checker.rb +18 -37
- data/lib/ndd/url_checker/forked_url_checker.rb +25 -29
- data/lib/ndd/url_checker/parallel_url_checker.rb +12 -11
- data/lib/ndd/url_checker/reporting_url_checker.html.erb +97 -0
- data/lib/ndd/url_checker/reporting_url_checker.rb +83 -0
- data/lib/ndd/url_checker/status.rb +20 -20
- data/lib/ndd/url_checker/threaded_url_checker.rb +10 -8
- data/ndd-url_checker.gemspec +123 -0
- data/spec/ndd/url_checker/abstract_url_checker_spec.rb +0 -6
- data/spec/ndd/url_checker/reporting_url_checker/multiple_urls.html +111 -0
- data/spec/ndd/url_checker/reporting_url_checker/single_url.html +97 -0
- data/spec/ndd/url_checker/reporting_url_checker_spec.rb +70 -0
- data/spec/spec_helper.rb +1 -1
- data/spec/support/multiple_url_checker_spec.rb +3 -11
- data/spec/support/single_url_checker_spec.rb +0 -36
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 99d36713ad66fe4e91a94f8244232de75f4c9e2c
|
4
|
+
data.tar.gz: 49b3313cc7ef54b102f1af8a2e921f9b96e5c70e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: eb756c557e6c76cb1f5ba947b383e3b3a5baec440f7b4ae1763251744fb925050a5724588d779ea51f1901948abf2777c77bd5f86c57734117a533794144a7b6
|
7
|
+
data.tar.gz: 3d1aa11d6dec0ea8159bb643b6cebe52f33d84b979e0f2bd858356085a616e5c170af888e72cb8f5872a0617421c0e34541b5ace207e12eaf7ff537a6d3e1727
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
|
-
#
|
1
|
+
# NDD URL Checker changelog
|
2
|
+
|
3
|
+
## Version 0.2.0
|
4
|
+
|
5
|
+
- Switched to named parameters
|
6
|
+
- Fixed documentation
|
7
|
+
- Removed the `UrlChecker#validate` method.
|
8
|
+
- Changed the type of the return value of `UrlChecker#check`.
|
2
9
|
|
3
10
|
## Version 0.1.0
|
4
11
|
|
5
|
-
|
12
|
+
- initial commit
|
data/Guardfile
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
|
2
1
|
# ----------------------------------------------------------------------------------------------------------------------
|
3
2
|
# bundler
|
4
3
|
# ----------------------------------------------------------------------------------------------------------------------
|
@@ -31,6 +30,7 @@ guard :rspec, cmd: 'bundle exec rspec' do
|
|
31
30
|
|
32
31
|
# ----- lib directory
|
33
32
|
watch(%r{^lib/(.+)\.rb$}) { |m| %W(spec/#{m[1]}_spec.rb) }
|
33
|
+
watch(%r{^lib/(.+)\.(.+)\.erb$}) { |m| %W(spec/#{m[1]}_spec.rb) }
|
34
34
|
|
35
35
|
# ----- spec directory
|
36
36
|
watch(%r{^spec/.+_spec\.rb$})
|
data/README.md
CHANGED
@@ -5,13 +5,73 @@
|
|
5
5
|
[![Code Climate](https://codeclimate.com/github/ddidier/ndd-url_checker/badges/gpa.svg)](https://codeclimate.com/github/ddidier/ndd-url_checker)
|
6
6
|
[![Test Coverage](https://codeclimate.com/github/ddidier/ndd-url_checker/badges/coverage.svg)](https://codeclimate.com/github/ddidier/ndd-url_checker)
|
7
7
|
|
8
|
-
URL validator.
|
8
|
+
An URL validator.
|
9
|
+
|
10
|
+
The API documentation can be find at [RubyDoc](http://www.rubydoc.info/github/ddidier/ndd-url_checker).
|
9
11
|
|
10
12
|
## Prerequisites
|
11
13
|
|
12
|
-
This gem
|
14
|
+
This gem requires Ruby 2.x and is tested with:
|
15
|
+
|
16
|
+
- Ruby 2.0.0
|
17
|
+
- Ruby 2.1.x
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
This gem provides several types of URL checker which may be composed using the [decorator pattern](http://en.wikipedia.org/wiki/Decorator_pattern). An URL checker exposes a [`#check(*urls)`](http://www.rubydoc.info/github/ddidier/ndd-url_checker/NDD/UrlChecker/AbstractUrlChecker#check-instance_method) method which has 2 variants:
|
22
|
+
|
23
|
+
- if a single URL is passed as an argument, it returns a single [`NDD::UrlChecker::Status`](http://www.rubydoc.info/github/ddidier/ndd-url_checker/NDD/UrlChecker/Status)
|
24
|
+
- if multiple URL are passed as arguments, it returns an array of [`NDD::UrlChecker::Status`](http://www.rubydoc.info/github/ddidier/ndd-url_checker/NDD/UrlChecker/Status)
|
25
|
+
|
26
|
+
A status has a code reflecting the result of the URL check. For the time being:
|
27
|
+
|
28
|
+
- valid codes are `direct` and `redirected`
|
29
|
+
- invalid codes are `failed`, `too_many_redirects` and `unknown_host`
|
30
|
+
|
31
|
+
### BlockingUrlChecker
|
32
|
+
|
33
|
+
[`NDD::UrlChecker::BlockingUrlChecker`](http://www.rubydoc.info/github/ddidier/ndd-url_checker/NDD/UrlChecker/BlockingUrlChecker) provides a serial URL checker using the standard [`Net:HTTP`](http://ruby-doc.org/stdlib-2.1.5/libdoc/net/http/rdoc/Net/HTTP.html) implementation.
|
34
|
+
|
35
|
+
```ruby
|
36
|
+
checker = NDD::UrlChecker::BlockingUrlChecker.new
|
37
|
+
|
38
|
+
status = checker.check('http://www.invalid123456789.com/')
|
39
|
+
status.valid? # => false
|
40
|
+
status.code # => :unknown_host
|
41
|
+
|
42
|
+
status = checker.check('http://www.google.com/')
|
43
|
+
status.valid? # => true
|
44
|
+
status.code # => :direct
|
45
|
+
|
46
|
+
statuses = checker.check('http://www.invalid123456789.com/', 'http://www.google.com/')
|
47
|
+
statuses[0].uri # => 'http://www.invalid123456789.com/'
|
48
|
+
statuses[0].valid? # => false
|
49
|
+
statuses[0].code # => :unknown_host
|
50
|
+
statuses[1].uri # => 'http://www.google.com/'
|
51
|
+
statuses[1].valid? # => true
|
52
|
+
statuses[1].code # => :direct
|
53
|
+
```
|
54
|
+
|
55
|
+
### ParallelUrlChecker
|
56
|
+
|
57
|
+
But this will be very time consuming if there is a lot of URL to check. Meet [`NDD::UrlChecker::ParallelUrlChecker`](http://www.rubydoc.info/github/ddidier/ndd-url_checker/NDD/UrlChecker/ParallelUrlChecker) which enables a very significant processing boost. For the time being, only a forked implementation is provided but a threaded one is planed.
|
58
|
+
|
59
|
+
```ruby
|
60
|
+
checker = NDD::UrlChecker::ParallelUrlChecker.new(parallelism: 100)
|
61
|
+
checker.check('http://www.invalid123456789.com/')
|
62
|
+
checker.check('http://www.google.com/')
|
63
|
+
```
|
64
|
+
|
65
|
+
### ReportingUrlChecker
|
66
|
+
|
67
|
+
For a nice looking report, use [`NDD::UrlChecker::ReportingUrlChecker`](http://www.rubydoc.info/github/ddidier/ndd-url_checker/NDD/UrlChecker/ReportingUrlChecker) which enables reporting capabilities using ERB templates. Several built-in templates are provided: CSV (planed), HTML and JSON (planed).
|
13
68
|
|
14
|
-
|
69
|
+
```ruby
|
70
|
+
checker = NDD::UrlChecker:: ReportingUrlChecker.new(delegate_checker)
|
71
|
+
statuses = checker.check('http://www.invalid123456789.com/', 'http://www.google.com/')
|
72
|
+
report_as_text = checker.report(:html, '/some/report.html')
|
73
|
+
report_as_text = checker.report('/some/template.erb', '/some/report.html')
|
74
|
+
```
|
15
75
|
|
16
76
|
## Copyright
|
17
77
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -4,27 +4,18 @@ module NDD
|
|
4
4
|
module UrlChecker
|
5
5
|
|
6
6
|
# Abstract class, not very ruby-ish :)
|
7
|
+
# @abstract
|
7
8
|
# @author David DIDIER
|
8
9
|
class AbstractUrlChecker
|
9
10
|
|
10
|
-
# Checks that the given
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# @return [NDD::UrlChecker::Status|Hash<String => NDD::UrlChecker::Status>]
|
11
|
+
# Checks that the given URLs are valid.
|
12
|
+
# @param urls [String, Array<String>] the URLs to check
|
13
|
+
# @return [NDD::UrlChecker::Status, Array<NDD::UrlChecker::Status>] a single status for a single URL, an array
|
14
|
+
# of status for multiple parameters
|
15
15
|
def check(*urls)
|
16
16
|
raise 'NDD::UrlChecker::UrlChecker#check must be implemented'
|
17
17
|
end
|
18
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
19
|
end
|
29
20
|
end
|
30
21
|
end
|
@@ -7,47 +7,36 @@ require 'net/https'
|
|
7
7
|
module NDD
|
8
8
|
module UrlChecker
|
9
9
|
|
10
|
-
# An URL checker using the blocking Net::HTTP class.
|
10
|
+
# An URL checker using the blocking {Net::HTTP} class.
|
11
11
|
# @author David DIDIER
|
12
12
|
class BlockingUrlChecker < AbstractUrlChecker
|
13
13
|
|
14
14
|
# Create a new instance.
|
15
|
-
# @param [Fixnum]
|
16
|
-
# @param [Fixnum]
|
17
|
-
def initialize(maximum_redirects
|
15
|
+
# @param maximum_redirects [Fixnum] the maximum number of redirects to follow before failing.
|
16
|
+
# @param timeout [Fixnum] the number of seconds to wait before failing.
|
17
|
+
def initialize(maximum_redirects: 5, timeout: 5)
|
18
18
|
@logger = Logging.logger[self]
|
19
19
|
@maximum_redirects = maximum_redirects
|
20
20
|
@timeout = timeout
|
21
21
|
end
|
22
22
|
|
23
|
-
# Checks that the given
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
# @return [NDD::UrlChecker::Status|Hash<String => NDD::UrlChecker::Status>]
|
23
|
+
# Checks that the given URLs are valid.
|
24
|
+
# @param urls [String, Array<String>] the URLs to check
|
25
|
+
# @return [NDD::UrlChecker::Status, Array<NDD::UrlChecker::Status>] a single status for a single URL, an array
|
26
|
+
# of status for multiple parameters
|
28
27
|
def check(*urls)
|
29
28
|
@logger.info "Checking #{urls.size} URL(s)"
|
30
|
-
return check_single(urls.first) if urls.size
|
31
|
-
|
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)] }]
|
29
|
+
return check_single(urls.first) if urls.size < 2
|
30
|
+
urls.map { |url| check_single(url) }
|
43
31
|
end
|
44
32
|
|
45
33
|
|
34
|
+
# -------------------------------------------------------------------------------------------------- private -----
|
46
35
|
private
|
47
36
|
|
48
37
|
# Checks that the given URL is valid.
|
49
|
-
#
|
50
|
-
#
|
38
|
+
# @param url [String] the URL to check
|
39
|
+
# @return [NDD::UrlChecker::Status]
|
51
40
|
def check_single(url)
|
52
41
|
begin
|
53
42
|
@logger.debug "Checking: #{url}"
|
@@ -63,18 +52,10 @@ module NDD
|
|
63
52
|
status
|
64
53
|
end
|
65
54
|
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
|
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]
|
55
|
+
# Checks that the given URI is valid.
|
56
|
+
# @param uri [URI::HTTP] the URI to check.
|
57
|
+
# @param status [NDD::UrlChecker::Status] the current status of the stack.
|
58
|
+
# @return [NDD::UrlChecker::Status]
|
78
59
|
def check_uri(uri, status)
|
79
60
|
if status.uris.size() > @maximum_redirects
|
80
61
|
return status.too_many_redirects
|
@@ -122,7 +103,7 @@ module NDD
|
|
122
103
|
end
|
123
104
|
end
|
124
105
|
|
125
|
-
#
|
106
|
+
# FIXME: platform dependent?
|
126
107
|
UNKNOWN_HOST_MESSAGE = 'getaddrinfo: Name or service not known'
|
127
108
|
|
128
109
|
def unknown_host?(error)
|
@@ -9,49 +9,43 @@ module NDD
|
|
9
9
|
|
10
10
|
# An URL checker using forks to parallelize processing. To be used with MRI.
|
11
11
|
# @author David DIDIER
|
12
|
+
# @attr_reader delegate [#check] the delegate URL checker.
|
13
|
+
# @attr_reader parallelism [Fixnum] the number of processes.
|
12
14
|
class ForkedUrlChecker < AbstractUrlChecker
|
13
15
|
|
14
16
|
attr_reader :delegate
|
15
17
|
attr_reader :parallelism
|
16
18
|
|
17
19
|
# Create a new instance.
|
18
|
-
# @param [AbstractUrlChecker]
|
19
|
-
# @param [Fixnum]
|
20
|
-
def initialize(delegate_checker
|
20
|
+
# @param delegate_checker [AbstractUrlChecker] defaults to {NDD::UrlChecker::BlockingUrlChecker}.
|
21
|
+
# @param parallelism [Fixnum] the number of processes.
|
22
|
+
def initialize(delegate_checker: nil, parallelism: 10)
|
21
23
|
@logger = Logging.logger[self]
|
22
24
|
@delegate = delegate_checker || BlockingUrlChecker.new
|
23
25
|
@parallelism = parallelism
|
24
26
|
end
|
25
27
|
|
28
|
+
# Checks that the given URLs are valid.
|
29
|
+
# @param urls [String, Array<String>] the URLs to check
|
30
|
+
# @return [NDD::UrlChecker::Status, Array<NDD::UrlChecker::Status>] a single status for a single URL, an array
|
31
|
+
# of status for multiple parameters
|
26
32
|
def check(*urls)
|
27
33
|
return delegate.check(*urls) if urls.size < 2
|
28
|
-
process(urls, :check)
|
29
|
-
end
|
30
|
-
|
31
|
-
def validate(*urls)
|
32
|
-
return delegate.validate(*urls) if urls.size < 2
|
33
|
-
process(urls, :validate)
|
34
|
-
end
|
35
|
-
|
36
34
|
|
37
|
-
private
|
38
|
-
|
39
|
-
def process(urls, method)
|
40
35
|
# for receiving results
|
41
36
|
result_pipe = Cod.pipe
|
42
37
|
# partition the URLs, but not too much :)
|
43
38
|
url_slices = partition(urls, [parallelism, urls.size].min)
|
44
39
|
# and distribute them among the workers
|
45
40
|
pids = url_slices.each_with_index.map do |url_slice, index|
|
46
|
-
fork { Worker.new(index, result_pipe, delegate).
|
41
|
+
fork { Worker.new(index, result_pipe, delegate).check(url_slice) }
|
47
42
|
end
|
48
43
|
|
49
44
|
# read back the results
|
50
|
-
results = urls.
|
45
|
+
results = urls.map do |_|
|
51
46
|
result = result_pipe.get
|
52
|
-
hash.merge!(result)
|
53
47
|
@logger.debug("Processed URLs #{hash.size}/#{urls.size}")
|
54
|
-
|
48
|
+
result
|
55
49
|
end
|
56
50
|
|
57
51
|
# kill all the workers
|
@@ -60,9 +54,15 @@ module NDD
|
|
60
54
|
results
|
61
55
|
end
|
62
56
|
|
63
|
-
|
64
|
-
#
|
65
|
-
|
57
|
+
|
58
|
+
# -------------------------------------------------------------------------------------------------- private -----
|
59
|
+
private
|
60
|
+
|
61
|
+
# Evenly distributes data into buckets.
|
62
|
+
#
|
63
|
+
# @example
|
64
|
+
# partition([1, 2, 3], 2) #=> [[1, 3], [2]]
|
65
|
+
# partition([1, 2, 3, 4, 5, 6], 3) #=> [[1, 4], [2, 5], [3, 6]]
|
66
66
|
def partition(data, buckets)
|
67
67
|
Array.new.tap do |slices|
|
68
68
|
buckets.times.each { |_| slices << Array.new }
|
@@ -71,7 +71,9 @@ module NDD
|
|
71
71
|
end
|
72
72
|
|
73
73
|
|
74
|
+
# ---------------------------------------------------------------------------------------------------- class -----
|
74
75
|
# A simple worker class processing URL one by one.
|
76
|
+
# @private
|
75
77
|
class Worker
|
76
78
|
def initialize(id, result_pipe, url_checker)
|
77
79
|
@logger = Logging.logger[self]
|
@@ -83,18 +85,12 @@ module NDD
|
|
83
85
|
def check(urls)
|
84
86
|
@logger.debug("[worker #{@id}] Checking #{urls.size} URLs")
|
85
87
|
urls.each do |url|
|
86
|
-
@result_pipe.put(
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def validate(urls)
|
91
|
-
@logger.debug("[worker #{@id}] Validating #{urls.size} URLs")
|
92
|
-
urls.each do |url|
|
93
|
-
@result_pipe.put({url => @url_checker.validate(url)})
|
88
|
+
@result_pipe.put(@url_checker.check(url))
|
94
89
|
end
|
95
90
|
end
|
96
91
|
end
|
97
92
|
|
93
|
+
private_constant :Worker
|
98
94
|
end
|
99
95
|
end
|
100
96
|
end
|
@@ -7,39 +7,40 @@ require 'ndd/url_checker/threaded_url_checker'
|
|
7
7
|
module NDD
|
8
8
|
module UrlChecker
|
9
9
|
|
10
|
-
# Wraps an instance of ThreadedUrlChecker or ForkedUrlChecker
|
11
|
-
#
|
10
|
+
# Wraps an instance of {NDD::UrlChecker::ThreadedUrlChecker} or {NDD::UrlChecker::ForkedUrlChecker}
|
11
|
+
# depending of the underlying Ruby implementation.
|
12
12
|
# @author David DIDIER
|
13
|
+
# @attr_reader delegate [#check] the delegate URL checker.
|
13
14
|
class ParallelUrlChecker < AbstractUrlChecker
|
14
15
|
|
15
16
|
attr_reader :delegate
|
16
17
|
|
17
18
|
# Create a new instance.
|
18
|
-
# @param [AbstractUrlChecker]
|
19
|
-
# @param [Fixnum]
|
20
|
-
def initialize(delegate_checker
|
19
|
+
# @param delegate_checker [AbstractUrlChecker] defaults to {NDD::UrlChecker::BlockingUrlChecker}.
|
20
|
+
# @param parallelism [Fixnum] the number of threads or processes.
|
21
|
+
def initialize(delegate_checker: nil, parallelism: 10)
|
21
22
|
@logger = Logging.logger[self]
|
22
23
|
|
23
24
|
@logger.debug "Ruby engine is #{RUBY_ENGINE}"
|
24
25
|
if RUBY_ENGINE == 'jruby'
|
25
26
|
@logger.info 'Creating a threaded URL checker'
|
26
|
-
parallel_checker = ThreadedUrlChecker.new(delegate_checker, parallelism)
|
27
|
+
parallel_checker = ThreadedUrlChecker.new(delegate_checker: delegate_checker, parallelism: parallelism)
|
27
28
|
else
|
28
29
|
@logger.info 'Creating a forked URL checker'
|
29
|
-
parallel_checker = ForkedUrlChecker.new(delegate_checker, parallelism)
|
30
|
+
parallel_checker = ForkedUrlChecker.new(delegate_checker: delegate_checker, parallelism: parallelism)
|
30
31
|
end
|
31
32
|
|
32
33
|
@delegate = parallel_checker
|
33
34
|
end
|
34
35
|
|
36
|
+
# Checks that the given URLs are valid.
|
37
|
+
# @param urls [String, Array<String>] the URLs to check
|
38
|
+
# @return [NDD::UrlChecker::Status, Array<NDD::UrlChecker::Status>] a single status for a single URL, an array
|
39
|
+
# of status for multiple parameters
|
35
40
|
def check(*urls)
|
36
41
|
@delegate.check(*urls)
|
37
42
|
end
|
38
43
|
|
39
|
-
def validate(*urls)
|
40
|
-
@delegate.validate(*urls)
|
41
|
-
end
|
42
|
-
|
43
44
|
end
|
44
45
|
end
|
45
46
|
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
|
4
|
+
<head>
|
5
|
+
<meta charset="UTF-8">
|
6
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css">
|
7
|
+
<style>
|
8
|
+
body {
|
9
|
+
padding: 1em;
|
10
|
+
}
|
11
|
+
</style>
|
12
|
+
|
13
|
+
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
|
14
|
+
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
|
15
|
+
<script language="JavaScript">
|
16
|
+
$(document).ready(function() {
|
17
|
+
$('input[type=radio][name=urlsFilter]').change(function() {
|
18
|
+
if (this.value == 'all') {
|
19
|
+
$('tr').show();
|
20
|
+
}
|
21
|
+
else if (this.value == 'invalid') {
|
22
|
+
$('tr').not('.danger').hide();
|
23
|
+
}
|
24
|
+
});
|
25
|
+
});
|
26
|
+
</script>
|
27
|
+
</head>
|
28
|
+
|
29
|
+
<body>
|
30
|
+
|
31
|
+
<h1>URL Checker Report</h1>
|
32
|
+
|
33
|
+
<table class="table table-condensed table-hover" style="width: auto">
|
34
|
+
<tbody>
|
35
|
+
<tr>
|
36
|
+
<td>User CPU time</td>
|
37
|
+
<td><%= benchmark.utime %> second(s)</td>
|
38
|
+
</tr>
|
39
|
+
<tr>
|
40
|
+
<td>System CPU time</td>
|
41
|
+
<td><%= benchmark.stime %> second(s)</td>
|
42
|
+
</tr>
|
43
|
+
<tr>
|
44
|
+
<td>Total time</td>
|
45
|
+
<td><%= benchmark.total %> second(s)</td>
|
46
|
+
</tr>
|
47
|
+
<tr>
|
48
|
+
<td>Elapsed real time</td>
|
49
|
+
<td><%= benchmark.real %> second(s)</td>
|
50
|
+
</tr>
|
51
|
+
<tr>
|
52
|
+
<td>Number of URLs</td>
|
53
|
+
<td><%= results.size %></td>
|
54
|
+
</tr>
|
55
|
+
<tr>
|
56
|
+
<td>Average time for 1 URL</td>
|
57
|
+
<td><%= benchmark.real/results.size %> second(s)</td>
|
58
|
+
</tr>
|
59
|
+
</tbody>
|
60
|
+
</table>
|
61
|
+
|
62
|
+
<div class="radio">
|
63
|
+
<label>
|
64
|
+
<input type="radio" name="urlsFilter" id="showAllUrls" value="all" checked="true" />
|
65
|
+
Show all URLs
|
66
|
+
</label>
|
67
|
+
</div>
|
68
|
+
<div class="radio">
|
69
|
+
<label>
|
70
|
+
<input type="radio" name="urlsFilter" id="showInvalidUrls" value="invalid" />
|
71
|
+
Show invalid URLs
|
72
|
+
</label>
|
73
|
+
</div>
|
74
|
+
|
75
|
+
<table class="table table-condensed table-hover" style="width: auto">
|
76
|
+
<thead>
|
77
|
+
<tr>
|
78
|
+
<th>#</th>
|
79
|
+
<th>URL</th>
|
80
|
+
<th>Result</th>
|
81
|
+
<th>Comment</th>
|
82
|
+
</tr>
|
83
|
+
</thead>
|
84
|
+
<tbody>
|
85
|
+
<% results.each_with_index do |status, index| %>
|
86
|
+
<tr class="<%= status.invalid? ? 'danger' : '' %>">
|
87
|
+
<td><%= index+1 %></td>
|
88
|
+
<td><a href="<%= status.uri %>" target="_blank"><%= status.uri %></a></td>
|
89
|
+
<td><%= status.class %></td>
|
90
|
+
<td><% if status.invalid? %><%= status.error %><% end %></td>
|
91
|
+
</tr>
|
92
|
+
<% end %>
|
93
|
+
</tbody>
|
94
|
+
</table>
|
95
|
+
|
96
|
+
</body>
|
97
|
+
</html>
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'benchmark'
|
2
|
+
require 'erb'
|
3
|
+
require 'logging'
|
4
|
+
require 'ndd/url_checker'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
module NDD
|
8
|
+
module UrlChecker
|
9
|
+
|
10
|
+
# Wraps an instance of URL checker and provides reporting capabilities using ERB templates.
|
11
|
+
# @author David DIDIER
|
12
|
+
# @attr_reader delegate [#check] the delegate URL checker.
|
13
|
+
class ReportingUrlChecker < AbstractUrlChecker
|
14
|
+
|
15
|
+
attr_reader :delegate
|
16
|
+
|
17
|
+
# Create a new instance.
|
18
|
+
# @param delegate_checker [AbstractUrlChecker].
|
19
|
+
def initialize(delegate_checker)
|
20
|
+
@logger = Logging.logger[self]
|
21
|
+
@delegate = delegate_checker
|
22
|
+
end
|
23
|
+
|
24
|
+
def check(*urls)
|
25
|
+
results = nil
|
26
|
+
benchmark = Benchmark.measure { results = @delegate.check(*urls) }
|
27
|
+
@logger.debug "Checking #{urls.size} URL(s) benchmark: #{benchmark}"
|
28
|
+
|
29
|
+
if urls.size > 1
|
30
|
+
@context = OpenStruct.new(
|
31
|
+
{
|
32
|
+
benchmark: benchmark,
|
33
|
+
results: results.sort_by { |status| status.uri }
|
34
|
+
})
|
35
|
+
else
|
36
|
+
@context = OpenStruct.new(
|
37
|
+
{
|
38
|
+
benchmark: benchmark,
|
39
|
+
results: [results]
|
40
|
+
})
|
41
|
+
end
|
42
|
+
|
43
|
+
results
|
44
|
+
end
|
45
|
+
|
46
|
+
# Creates a report about the previous check using the specified template. The result may be written to a file.
|
47
|
+
# @param template [Symbol, String] a predefined template (:csv, :html, :json) or the path of a template file.
|
48
|
+
# @param output_path [String, nil] the path of the output file.
|
49
|
+
# @return [String] the report.
|
50
|
+
def report(template, output_path=nil)
|
51
|
+
template_path = template_path(template)
|
52
|
+
template_content = ERB.new(File.new(template_path).read)
|
53
|
+
report = template_content.result(@context.instance_eval { binding })
|
54
|
+
|
55
|
+
if output_path
|
56
|
+
@logger.info "Reporting to #{output_path}"
|
57
|
+
File.open(output_path, 'w') { |file| file.write(report) }
|
58
|
+
end
|
59
|
+
|
60
|
+
report
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
# -------------------------------------------------------------------------------------------------- private -----
|
65
|
+
private
|
66
|
+
|
67
|
+
def template_path(symbol_or_path)
|
68
|
+
case symbol_or_path
|
69
|
+
when :csv then
|
70
|
+
# File.expand_path(File.join(File.dirname(__FILE__), 'reporting_url_checker.csv.erb'))
|
71
|
+
raise 'TODO'
|
72
|
+
when :html then
|
73
|
+
File.expand_path(File.join(File.dirname(__FILE__), 'reporting_url_checker.html.erb'))
|
74
|
+
when :json then
|
75
|
+
# File.expand_path(File.join(File.dirname(__FILE__), 'reporting_url_checker.json.erb'))
|
76
|
+
raise 'TODO'
|
77
|
+
else
|
78
|
+
symbol_or_path
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|