headhunter 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +5 -0
- data/lib/headhunter/css_hunter.rb +36 -64
- data/lib/headhunter/css_validator.rb +88 -139
- data/lib/headhunter/html_validator.rb +26 -33
- data/lib/headhunter/rack/capturing_middleware.rb +1 -1
- data/lib/headhunter/runner.rb +9 -9
- data/lib/headhunter/version.rb +1 -1
- data/lib/tidy/tidy +0 -0
- metadata +3 -3
- data/lib/headhunter/local_response.rb +0 -20
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 14ca57c35b2c4f399b3f3d3a475489f9ae3b50fc
|
4
|
+
data.tar.gz: 8d3fe32d258b3952fe649ff35d375635ad162afc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3478cf3aac060a39857d989640622f6b0eeaea7ead4fb1add028c4513336ce38e972a4a6ce757864b2b6ae7a7c9c91a821c61a468fea6917cbb7a5d825dc384e
|
7
|
+
data.tar.gz: f9861651aeed1cc736d042ab0f86d567b6c5b0ea3c8522fb05825e641c8907c34972b9fde3c82845cf311614468c400a79c7aa4e9e8958b7de62398980b9ede6
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -6,6 +6,8 @@
|
|
6
6
|
[![Code Climate](https://codeclimate.com/github/jmuheim/headhunter.png)](https://codeclimate.com/github/jmuheim/headhunter)
|
7
7
|
[![Travis CI](https://api.travis-ci.org/jmuheim/headhunter.png)](https://travis-ci.org/jmuheim/headhunter)
|
8
8
|
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/jmuheim/headhunter/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
|
9
|
+
[![Dependency Status](https://gemnasium.com/jmuheim/headhunter.png)](https://gemnasium.com/jmuheim/headhunter)
|
10
|
+
[![Coverage Status](https://coveralls.io/repos/jmuheim/headhunter/badge.png)](https://coveralls.io/r/jmuheim/headhunter)
|
9
11
|
|
10
12
|
Headhunter is an HTML and CSS validation tool that injects itself into your Rails feature tests and auto<strong>magic</strong>ally checks all your generated HTML and CSS for validity.
|
11
13
|
|
@@ -59,8 +61,11 @@ You need a **Java Runtime Environment** to run CSS validation. This should norma
|
|
59
61
|
- In feature tests, the same views are getting rendered again and again. These same sources shouldn't be validated over and over again!
|
60
62
|
- More configuration options needed! CSS1, CSS2, CSS2.1, CSS3! XHTML, HTML5, etc.
|
61
63
|
- Better output needed! With context lines, etc.
|
64
|
+
- HTML output: info about GET/POST/PUT/DELETE!
|
62
65
|
- Look out for multiple used IDs on the same page and raise error!
|
63
66
|
- Look out for invalid tags and raise error (tidy doesn't seem to do this?!)!
|
67
|
+
- Cache HTML validations: use MD5 to prevent validating the same source again and again! (Maybe even keep the cache between running specs!)
|
68
|
+
- Add option to CssHunter that ignores "bare" rules like applet, blockquote, etc. (which usually stem from libraries like Compass reset or Normalize)
|
64
69
|
|
65
70
|
## Disclaimer
|
66
71
|
|
@@ -1,104 +1,76 @@
|
|
1
1
|
require 'css_parser'
|
2
2
|
require 'nokogiri'
|
3
|
-
require 'open-uri'
|
4
3
|
|
5
4
|
module Headhunter
|
6
5
|
class CssHunter
|
7
|
-
|
6
|
+
attr_reader :unused_selectors, :used_selectors, :error_selectors
|
7
|
+
|
8
|
+
def initialize(stylesheets = [])
|
8
9
|
@stylesheets = stylesheets
|
9
|
-
@parsed_rules = {}
|
10
10
|
@unused_selectors = []
|
11
11
|
@used_selectors = []
|
12
12
|
@error_selectors = []
|
13
13
|
|
14
|
-
|
14
|
+
@stylesheets.each do |stylesheet|
|
15
|
+
add_css_selectors_from(IO.read(stylesheet))
|
16
|
+
end
|
15
17
|
end
|
16
18
|
|
17
|
-
def process
|
18
|
-
|
19
|
+
def process(html)
|
20
|
+
detect_used_selectors_in(html).each do |selector|
|
21
|
+
@used_selectors << selector
|
19
22
|
@unused_selectors.delete(selector)
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
23
|
-
def
|
24
|
-
|
25
|
-
puts "#{@used_selectors.size} selectors are in use.".green if @used_selectors.size > 0
|
26
|
-
puts "#{@unused_selectors.size} selectors are not in use: #{@unused_selectors.sort.join(', ').red}".red if @unused_selectors.size > 0
|
27
|
-
puts "#{@error_selectors.size} selectors could not be parsed: #{@error_selectors.sort.join(', ').red}".red if @unused_selectors.size > 0
|
28
|
-
puts
|
29
|
-
end
|
26
|
+
def statistics
|
27
|
+
lines = []
|
30
28
|
|
31
|
-
|
29
|
+
lines << "Found #{used_selectors.size + unused_selectors.size + error_selectors.size} CSS selectors.".yellow
|
30
|
+
lines << 'All selectors are in use.'.green if unused_selectors.size + error_selectors.size == 0
|
31
|
+
lines << "#{unused_selectors.size} selectors are not in use: #{unused_selectors.sort.join(', ').red}".red if unused_selectors.size > 0
|
32
|
+
lines << "#{error_selectors.size} selectors could not be parsed: #{error_selectors.sort.join(', ').red}".red if error_selectors.size > 0
|
32
33
|
|
33
|
-
|
34
|
-
|
34
|
+
lines.join("\n")
|
35
|
+
end
|
35
36
|
|
36
|
-
|
37
|
-
|
38
|
-
# but we report on the selector with its pseudo classes.
|
39
|
-
stripped_selector = strip(selector)
|
37
|
+
def detect_used_selectors_in(html)
|
38
|
+
document = Nokogiri::HTML(html)
|
40
39
|
|
41
|
-
|
40
|
+
@unused_selectors.collect do |selector, declarations|
|
41
|
+
bare_selector = bare_selector_from(selector)
|
42
42
|
|
43
43
|
begin
|
44
|
-
if
|
45
|
-
@used_selectors << selector
|
46
|
-
selector
|
47
|
-
end
|
44
|
+
selector if document.search(bare_selector).any?
|
48
45
|
rescue Nokogiri::CSS::SyntaxError => e
|
49
46
|
@error_selectors << selector
|
47
|
+
@unused_selectors.delete(selector)
|
50
48
|
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def load_css!
|
55
|
-
@stylesheets.each do |stylesheet|
|
56
|
-
new_selector_count = add_css!(fetch(stylesheet))
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def fetch(path)
|
61
|
-
loc = path
|
62
|
-
|
63
|
-
begin
|
64
|
-
open(loc).read
|
65
|
-
rescue Errno::ENOENT
|
66
|
-
raise FetchError.new("#{loc} was not found")
|
67
|
-
rescue OpenURI::HTTPError => e
|
68
|
-
raise FetchError.new("retrieving #{loc} raised an HTTP error: #{e.message}")
|
69
|
-
end
|
49
|
+
end.compact # FIXME: Why is compact needed?
|
70
50
|
end
|
71
51
|
|
72
|
-
def
|
52
|
+
def add_css_selectors_from(css)
|
73
53
|
parser = CssParser::Parser.new
|
74
54
|
parser.add_block!(css)
|
75
55
|
|
76
|
-
selector_count = 0
|
77
|
-
|
78
56
|
parser.each_selector do |selector, declarations, specificity|
|
79
|
-
next if @unused_selectors.include?(selector)
|
80
|
-
next if selector
|
81
|
-
next if has_pseudo_classes(selector) and @unused_selectors.include?(strip(selector))
|
82
|
-
|
57
|
+
# next if @unused_selectors.include?(selector)
|
58
|
+
# next if has_pseudo_classes?(selector) and @unused_selectors.include?(bare_selector_from(selector))
|
83
59
|
@unused_selectors << selector
|
84
|
-
@parsed_rules[selector] = declarations
|
85
|
-
|
86
|
-
selector_count += 1
|
87
60
|
end
|
88
|
-
|
89
|
-
selector_count
|
90
61
|
end
|
91
62
|
|
92
|
-
def has_pseudo_classes(selector)
|
93
|
-
|
63
|
+
# def has_pseudo_classes?(selector)
|
64
|
+
# selector =~ /::?[\w\-]+/
|
65
|
+
# end
|
66
|
+
|
67
|
+
def bare_selector_from(selector)
|
68
|
+
# Add more clean up stuff here, e.g. stuff like @keyframe (Deadweight implemented this)?
|
69
|
+
remove_pseudo_classes_from(selector)
|
94
70
|
end
|
95
71
|
|
96
|
-
def
|
97
|
-
selector
|
98
|
-
selector = selector.gsub(/:.*/, '') # input#x:nth-child(2):not(#z.o[type='file'])
|
99
|
-
selector
|
72
|
+
def remove_pseudo_classes_from(selector)
|
73
|
+
selector.gsub(/:.*/, '') # input#x:nth-child(2):not(#z.o[type='file'])
|
100
74
|
end
|
101
75
|
end
|
102
|
-
|
103
|
-
class FetchError < StandardError; end
|
104
76
|
end
|
@@ -1,73 +1,65 @@
|
|
1
1
|
require 'net/http'
|
2
|
-
require '
|
2
|
+
require 'nokogiri/xml'
|
3
3
|
|
4
4
|
module Headhunter
|
5
|
-
class
|
6
|
-
|
7
|
-
|
8
|
-
def initialize(body)
|
9
|
-
@body = body
|
10
|
-
@headers = {'x-w3c-validator-status' => valid?(body)}
|
11
|
-
end
|
5
|
+
class CssValidator
|
6
|
+
VALIDATOR_PATH = Gem.loaded_specs['headhunter'].full_gem_path + '/lib/css-validator/'
|
12
7
|
|
13
|
-
|
14
|
-
@headers[key]
|
15
|
-
end
|
8
|
+
attr_reader :stylesheets, :responses
|
16
9
|
|
17
|
-
|
10
|
+
def initialize(stylesheets = [], profile = 'css3', vextwarning = true)
|
11
|
+
@stylesheets = stylesheets
|
12
|
+
@profile = profile # TODO!
|
13
|
+
@vextwarning = vextwarning # TODO!
|
18
14
|
|
19
|
-
|
20
|
-
|
15
|
+
@responses = @stylesheets.map do |stylesheet|
|
16
|
+
validate(stylesheet)
|
17
|
+
end
|
21
18
|
end
|
22
|
-
end
|
23
19
|
|
24
|
-
|
25
|
-
|
20
|
+
def validate(uri)
|
21
|
+
# See http://stackoverflow.com/questions/1137884/is-there-an-open-source-css-validator-that-can-be-run-locally
|
22
|
+
# More config options see http://jigsaw.w3.org/css-validator/manual.html
|
23
|
+
results = if File.exists?(uri)
|
24
|
+
Dir.chdir(VALIDATOR_PATH) { `java -jar css-validator.jar --output=soap12 file:#{uri}` }
|
25
|
+
else
|
26
|
+
raise "Couldn't locate uri #{uri}"
|
27
|
+
end
|
26
28
|
|
27
|
-
|
28
|
-
@profile = 'css3' # TODO: Option for profile css1 and css21
|
29
|
-
@stylesheets = stylesheets
|
30
|
-
@messages_per_stylesheet = {}
|
29
|
+
Response.new(results)
|
31
30
|
end
|
32
31
|
|
33
|
-
def
|
34
|
-
@
|
32
|
+
def valid_responses
|
33
|
+
@responses.select(&:valid?)
|
35
34
|
end
|
36
35
|
|
37
|
-
def
|
38
|
-
@
|
39
|
-
css = fetch(stylesheet)
|
40
|
-
css = ' ' if css.empty? # The validator returns a 500 error if it receives an empty string
|
41
|
-
|
42
|
-
response = get_validation_response({text: css, profile: @profile, vextwarning: 'true'})
|
43
|
-
unless response_indicates_valid?(response)
|
44
|
-
process_errors(stylesheet, css, response)
|
45
|
-
end
|
46
|
-
end
|
36
|
+
def invalid_responses
|
37
|
+
@responses.reject(&:valid?)
|
47
38
|
end
|
48
39
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
40
|
+
def statistics
|
41
|
+
lines = []
|
42
|
+
|
43
|
+
lines << "Validated #{responses.size} stylesheets.".yellow
|
44
|
+
lines << "All stylesheets are valid.".green if invalid_responses.size == 0
|
45
|
+
lines << "#{x_stylesheets_be(invalid_responses.size)} invalid.".red if invalid_responses.size > 0
|
53
46
|
|
54
|
-
|
55
|
-
|
47
|
+
invalid_responses.each do |response|
|
48
|
+
lines << " #{extract_filename(response.uri)}:".red
|
56
49
|
|
57
|
-
|
50
|
+
response.errors.each do |error|
|
51
|
+
lines << " - #{error.to_s}".red
|
52
|
+
end
|
58
53
|
end
|
59
54
|
|
60
|
-
|
55
|
+
lines.join("\n")
|
61
56
|
end
|
62
57
|
|
63
|
-
private
|
64
|
-
|
65
|
-
# Converts a path like public/assets/application-d205d6f344d8623ca0323cb6f6bd7ca1.css to application.css
|
66
58
|
def extract_filename(path)
|
67
|
-
if matches = path.match(
|
68
|
-
matches[1] + matches[3]
|
59
|
+
if matches = path.match(/public\/assets\/([a-z\-_]*)-([a-z0-9]{32})(\.css)$/)
|
60
|
+
matches[1] + matches[3] # application-d205d6f344d8623ca0323cb6f6bd7ca1.css becomes application.css
|
69
61
|
else
|
70
|
-
|
62
|
+
File.basename(path)
|
71
63
|
end
|
72
64
|
end
|
73
65
|
|
@@ -79,115 +71,72 @@ module Headhunter
|
|
79
71
|
end
|
80
72
|
end
|
81
73
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
REXML::Document.new(response.body).root.each_element('//m:error') do |e|
|
86
|
-
@messages_per_stylesheet[file] << "Line #{extract_line_from_error(e)}: #{extract_message_from_error(e)}"
|
74
|
+
class Response
|
75
|
+
def initialize(response = nil)
|
76
|
+
@document = Nokogiri::XML(convert_soap_to_xml(response)) if response
|
87
77
|
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def extract_line_from_error(e)
|
91
|
-
e.elements['m:line'].text
|
92
|
-
end
|
93
78
|
|
94
|
-
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
def fetch(path) # TODO: Move to Headhunter!
|
99
|
-
loc = path
|
100
|
-
|
101
|
-
begin
|
102
|
-
open(loc).read
|
103
|
-
rescue Errno::ENOENT
|
104
|
-
raise FetchError.new("#{loc} was not found")
|
105
|
-
rescue OpenURI::HTTPError => e
|
106
|
-
raise FetchError.new("retrieving #{loc} raised an HTTP error: #{e.message}")
|
79
|
+
def [](key)
|
80
|
+
@headers[key]
|
107
81
|
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def get_validation_response(query_params)
|
111
|
-
query_params.merge!({:output => 'soap12'})
|
112
82
|
|
113
|
-
|
114
|
-
|
115
|
-
else
|
116
|
-
call_remote_validator(query_params)
|
83
|
+
def valid?
|
84
|
+
@document.css('validity').text == 'true'
|
117
85
|
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def response_indicates_valid?(response)
|
121
|
-
response['x-w3c-validator-status'] == 'Valid'
|
122
|
-
end
|
123
|
-
|
124
|
-
def call_remote_validator(query_params = {})
|
125
|
-
boundary = Digest::MD5.hexdigest(Time.now.to_s)
|
126
|
-
data = encode_multipart_params(boundary, query_params)
|
127
|
-
response = http_start(validator_host).post2(validator_path,
|
128
|
-
data,
|
129
|
-
'Content-type' => "multipart/form-data; boundary=#{boundary}")
|
130
86
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
Dir.chdir(path) do
|
142
|
-
File.open(css_file, 'a') { |f| f.write query_params[:text] }
|
143
|
-
|
144
|
-
# See http://stackoverflow.com/questions/1137884/is-there-an-open-source-css-validator-that-can-be-run-locally
|
145
|
-
if system "java -jar css-validator.jar --output=soap12 file:#{css_file} > #{results_file}"
|
146
|
-
results = IO.read results_file
|
147
|
-
else
|
148
|
-
raise 'Could not execute local validation!'
|
87
|
+
def errors
|
88
|
+
@document.css('errors error').map do |error|
|
89
|
+
Error.new( error.css('line').text.strip.to_i,
|
90
|
+
error.css('message').text.strip[0..-3],
|
91
|
+
errortype: error.css('errortype').text.strip,
|
92
|
+
context: error.css('context').text.strip,
|
93
|
+
errorsubtype: error.css('errorsubtype').text.strip,
|
94
|
+
skippedstring: error.css('skippedstring').text.strip
|
95
|
+
)
|
149
96
|
end
|
97
|
+
end
|
150
98
|
|
151
|
-
|
152
|
-
|
99
|
+
def uri
|
100
|
+
@document.css('cssvalidationresponse > uri').text
|
153
101
|
end
|
154
102
|
|
155
|
-
|
156
|
-
end
|
103
|
+
private
|
157
104
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
ret << "\r\n--#{boundary}\r\n"
|
163
|
-
ret << "Content-Disposition: form-data; name=\"#{k.to_s}\"\r\n\r\n"
|
164
|
-
ret << v
|
165
|
-
end
|
105
|
+
def convert_soap_to_xml(soap)
|
106
|
+
sanitize_prefixed_tags_from(
|
107
|
+
remove_first_line_from(soap)
|
108
|
+
)
|
166
109
|
end
|
167
|
-
ret << "\r\n--#{boundary}--\r\n"
|
168
|
-
ret
|
169
|
-
end
|
170
110
|
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
111
|
+
# The first line of the validator's response contains parameter options like this:
|
112
|
+
#
|
113
|
+
# {vextwarning=false, output=soap, lang=en, warning=2, medium=all, profile=css3}
|
114
|
+
#
|
115
|
+
# We remove this so Nokogiri can parse the document as XML.
|
116
|
+
def remove_first_line_from(soap)
|
117
|
+
soap.split("\n")[1..-1].join("\n")
|
178
118
|
end
|
179
|
-
end
|
180
119
|
|
181
|
-
|
182
|
-
|
183
|
-
|
120
|
+
# The validator's response contains strange SOAP tags like `m:error` or `env:body` which need to be sanitized for Nokogiri.
|
121
|
+
#
|
122
|
+
# We simply remove the `m:` and `env:` prefixes from the source, so e.g. `<env:body>` becomes `<body>`.
|
123
|
+
def sanitize_prefixed_tags_from(soap)
|
124
|
+
soap.gsub /(m|env):/, ''
|
125
|
+
end
|
184
126
|
|
185
|
-
|
186
|
-
|
187
|
-
|
127
|
+
class Error
|
128
|
+
attr_reader :line, :message, :details
|
129
|
+
|
130
|
+
def initialize(line, message, details = {})
|
131
|
+
@line = line
|
132
|
+
@message = message
|
133
|
+
@details = details
|
134
|
+
end
|
188
135
|
|
189
|
-
|
190
|
-
|
136
|
+
def to_s
|
137
|
+
"Line #{@line}: #{@message}."
|
138
|
+
end
|
139
|
+
end
|
191
140
|
end
|
192
141
|
end
|
193
142
|
end
|
@@ -2,46 +2,43 @@ require 'html_validation'
|
|
2
2
|
|
3
3
|
module Headhunter
|
4
4
|
class HtmlValidator
|
5
|
+
attr_reader :responses
|
6
|
+
|
5
7
|
def initialize
|
6
|
-
@
|
7
|
-
@invalid_results = []
|
8
|
+
@responses = []
|
8
9
|
end
|
9
10
|
|
10
|
-
def
|
11
|
-
|
12
|
-
|
11
|
+
def validate(url, html)
|
12
|
+
# Docs for Tidy: http://tidy.sourceforge.net/docs/quickref.html
|
13
|
+
@responses << PageValidations::HTMLValidation.new.validation(html, url)
|
14
|
+
@responses.last
|
13
15
|
end
|
14
16
|
|
15
|
-
def
|
16
|
-
|
17
|
-
html.gsub! '{{VALID_RESULTS}}', prepare_results_for(@valid_results)
|
18
|
-
html.gsub! '{{INVALID_RESULTS}}', prepare_results_for(@invalid_results)
|
19
|
-
File.open('.validation/results.html', 'w') { |file| file.write(html) }
|
17
|
+
def valid_responses
|
18
|
+
@responses.select(&:valid?)
|
20
19
|
end
|
21
20
|
|
22
|
-
def
|
23
|
-
|
24
|
-
|
21
|
+
def invalid_responses
|
22
|
+
@responses.reject(&:valid?)
|
23
|
+
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
full_result_html.gsub! '{{EXCEPTIONS}}', exceptions_html
|
29
|
-
full_result_html.gsub! '{{HTML_CONTEXT}}', 'context'
|
30
|
-
full_result_html.gsub! '{{LINK}}', "#{result.resource}.html.txt"
|
25
|
+
def statistics
|
26
|
+
lines = []
|
31
27
|
|
32
|
-
|
33
|
-
|
34
|
-
|
28
|
+
lines << "Validated #{responses.size} pages.".yellow
|
29
|
+
lines << "All pages are valid.".green if invalid_responses.size == 0
|
30
|
+
lines << "#{x_pages_be(invalid_responses.size)} invalid.".red if invalid_responses.size > 0
|
35
31
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
32
|
+
invalid_responses.each do |response|
|
33
|
+
lines << " #{response.resource}:".red
|
34
|
+
|
35
|
+
([response.exceptions].flatten).each do |exception|
|
36
|
+
lines << " - #{exception.strip}".red
|
37
|
+
end
|
38
|
+
end
|
43
39
|
|
44
|
-
|
40
|
+
lines.join("\n")
|
41
|
+
end
|
45
42
|
|
46
43
|
def x_pages_be(size)
|
47
44
|
if size <= 1
|
@@ -50,9 +47,5 @@ module Headhunter
|
|
50
47
|
"#{size} pages are"
|
51
48
|
end
|
52
49
|
end
|
53
|
-
|
54
|
-
def random_name
|
55
|
-
(0...8).map { (65 + rand(26)).chr }.join
|
56
|
-
end
|
57
50
|
end
|
58
51
|
end
|
data/lib/headhunter/runner.rb
CHANGED
@@ -16,12 +16,11 @@ module Headhunter
|
|
16
16
|
@css_hunter = CssHunter.new(stylesheets)
|
17
17
|
|
18
18
|
@css_validator = CssValidator.new(stylesheets)
|
19
|
-
@css_validator.process!
|
20
19
|
end
|
21
20
|
|
22
|
-
def process
|
23
|
-
@html_validator.
|
24
|
-
@css_hunter.process
|
21
|
+
def process(url, html)
|
22
|
+
@html_validator.validate(url, html)
|
23
|
+
@css_hunter.process(url, html)
|
25
24
|
end
|
26
25
|
|
27
26
|
def clean_up!
|
@@ -31,11 +30,12 @@ module Headhunter
|
|
31
30
|
end
|
32
31
|
|
33
32
|
def report
|
34
|
-
@html_validator.
|
33
|
+
puts [ @html_validator.statistics,
|
34
|
+
@css_validator.statistics,
|
35
|
+
@css_hunter.statistics
|
36
|
+
].join "\n\n"
|
35
37
|
|
36
|
-
|
37
|
-
@css_validator.report
|
38
|
-
@css_hunter.report
|
38
|
+
puts
|
39
39
|
end
|
40
40
|
|
41
41
|
private
|
@@ -58,7 +58,7 @@ module Headhunter
|
|
58
58
|
end
|
59
59
|
|
60
60
|
def stylesheets
|
61
|
-
Dir["#{ASSETS_PATH}/*.css"]
|
61
|
+
Dir["#{::Rails.root}/#{ASSETS_PATH}/*.css"]
|
62
62
|
end
|
63
63
|
end
|
64
64
|
end
|
data/lib/headhunter/version.rb
CHANGED
data/lib/tidy/tidy
ADDED
Binary file
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: headhunter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Joshua Muheim
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-02-
|
11
|
+
date: 2014-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: nokogiri
|
@@ -195,7 +195,6 @@ files:
|
|
195
195
|
- lib/headhunter/css_hunter.rb
|
196
196
|
- lib/headhunter/css_validator.rb
|
197
197
|
- lib/headhunter/html_validator.rb
|
198
|
-
- lib/headhunter/local_response.rb
|
199
198
|
- lib/headhunter/rack/capturing_middleware.rb
|
200
199
|
- lib/headhunter/rails.rb
|
201
200
|
- lib/headhunter/runner.rb
|
@@ -203,6 +202,7 @@ files:
|
|
203
202
|
- lib/headhunter/templates/results.html
|
204
203
|
- lib/headhunter/version.rb
|
205
204
|
- lib/headhunter.rb
|
205
|
+
- lib/tidy/tidy
|
206
206
|
- Gemfile
|
207
207
|
- Gemfile.lock
|
208
208
|
- Rakefile
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'rexml/document'
|
2
|
-
|
3
|
-
module Headhunter
|
4
|
-
class LocalResponse
|
5
|
-
attr_reader :body
|
6
|
-
|
7
|
-
def initialize(body)
|
8
|
-
@body = body
|
9
|
-
@headers = {'x-w3c-validator-status' => valid?}
|
10
|
-
end
|
11
|
-
|
12
|
-
def [](key)
|
13
|
-
@headers[key]
|
14
|
-
end
|
15
|
-
|
16
|
-
def valid?
|
17
|
-
REXML::Document.new(@body).root.each_element('//m:validity') { |e| return e.text == 'true' }
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|