blinkr 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/blinkr/report.rb CHANGED
@@ -6,9 +6,41 @@ module Blinkr
6
6
 
7
7
  TMPL = File.expand_path('report.html.slim', File.dirname(__FILE__))
8
8
 
9
- def self.render(errors, out_file)
10
- out_file ||= 'blinkr.html'
11
- File.open(out_file, 'w') { |file| file.write(Slim::Template.new(TMPL).render(OpenStruct.new({:errors => errors}))) }
9
+ def self.render(context, engine, config)
10
+
12
11
  end
12
+
13
+ def initialize context, engine, config
14
+ @context = context
15
+ @engine = engine
16
+ @config = config
17
+ end
18
+
19
+ def render
20
+ @context.severities = {}
21
+ @context.categories = {}
22
+ @context.types = {}
23
+ @context.pages.each do |url, page|
24
+ page.max_severity = 'success'
25
+ page.errors.each do |error|
26
+ raise "#{error.severity} not a valid severity. Must be one of #{SEVERITY.join(',')}" unless SEVERITY.include? error.severity
27
+ raise "#{error.category} must be specified." if error.category.nil?
28
+ @context.severities[error.severity] ||= OpenStruct.new({ :id => error.severity, :count => 0 })
29
+ @context.severities[error.severity].count += 1
30
+ page.max_severity = error.severity if SEVERITY.index(error.severity) > SEVERITY.index(page.max_severity)
31
+ @context.categories[error.category] ||= OpenStruct.new({ :id => @context.categories.length, :count => 0, :severities => Hash.new(0), :types => {} })
32
+ @context.categories[error.category].severities[error.severity] += 1
33
+ @context.categories[error.category].types[error.type] ||= OpenStruct.new({ :id => @context.categories[error.category].types.length, :count => 0, :severities => Hash.new(0) })
34
+ @context.categories[error.category].types[error.type].severities[error.severity] += 1
35
+ end
36
+ end
37
+ @context.error_count = @context.severities.reduce(0){ |sum, (severity, metadata)| sum += metadata.count }
38
+ File.open(@config.report, 'w') { |file| file.write(Slim::Template.new(TMPL).render(OpenStruct.new({ :blinkr => @context, :engine => @engine }))) }
39
+ end
40
+
41
+ private
42
+
43
+ SEVERITY = ['success', 'info', 'warning', 'danger']
44
+
13
45
  end
14
46
  end
@@ -0,0 +1,20 @@
1
+ module Blinkr
2
+ module Sitemap
3
+
4
+ def sitemap_locations
5
+ open.css('loc').collect { |loc| loc.content }
6
+ end
7
+
8
+ private
9
+
10
+ def open
11
+ puts "Loading sitemap from #{@config.sitemap}"
12
+ if @config.sitemap =~ URI::regexp
13
+ Nokogiri::XML(Typhoeus.get(@config.sitemap, followlocation: true).body)
14
+ else
15
+ Nokogiri::XML(File.open(@config.sitemap))
16
+ end
17
+ end
18
+
19
+ end
20
+ end
data/lib/blinkr/snap.js CHANGED
@@ -73,6 +73,8 @@ page.open(info.url, function(status) {
73
73
 
74
74
  var exit = function exit(err_code) {
75
75
  info.content = page.content;
76
+ // Re-read the URL in case we've been redirected
77
+ info.url = page.url;
76
78
  fs.write(out, JSON.stringify(info), 'w');
77
79
  if (err_code === undefined) {
78
80
  err_code = 0;
@@ -0,0 +1,98 @@
1
+ require 'typhoeus/request'
2
+ require 'typhoeus/response'
3
+ require 'typhoeus/hydra'
4
+ require 'typhoeus'
5
+ require 'blinkr/cache'
6
+ require 'blinkr/http_utils'
7
+
8
+ module Blinkr
9
+ class TyphoeusWrapper
10
+ include HttpUtils
11
+
12
+ attr_reader :count, :hydra
13
+
14
+ def initialize config, context
15
+ @config = config.validate
16
+ @hydra = Typhoeus::Hydra.new(max_concurrency: 200)
17
+ @count = 0
18
+ @context = context
19
+ end
20
+
21
+ def process_all urls, limit, &block
22
+ urls.each do |url|
23
+ process url, limit, &block
24
+ end
25
+ end
26
+
27
+ def process url, limit, &block
28
+ _process url, limit, limit, &block
29
+ end
30
+
31
+ def debug url
32
+ process(url, @config.max_retrys) do |resp|
33
+ puts "\n++++++++++"
34
+ puts "+ Blinkr +"
35
+ puts "++++++++++"
36
+ puts "\nRequest"
37
+ puts "======="
38
+ puts "Method: #{resp.request.options[:method]}"
39
+ puts "Max redirects: #{resp.request.options[:maxredirs]}"
40
+ puts "Follow location header: #{resp.request.options[:followlocation]}"
41
+ puts "Timeout (s): #{resp.request.options[:timeout] || 'none'}"
42
+ puts "Connection timeout (s): #{resp.request.options[:connecttimeout] || 'none'}"
43
+ puts "\nHeaders"
44
+ puts "-------"
45
+ unless resp.request.options[:headers].nil?
46
+ resp.request.options[:headers].each do |name, value|
47
+ puts "#{name}: #{value}"
48
+ end
49
+ end
50
+ puts "\nResponse"
51
+ puts "========"
52
+ puts "Status Code: #{resp.code}"
53
+ puts "Status Message: #{resp.status_message}"
54
+ puts "Message: #{resp.return_message}" unless resp.return_message.nil? || resp.return_message == 'No error'
55
+ puts "\nHeaders"
56
+ puts "-------"
57
+ puts resp.response_headers
58
+ end
59
+ @hydra.run
60
+ end
61
+
62
+ def name
63
+ 'typhoeus'
64
+ end
65
+
66
+ private
67
+
68
+ def _process url, limit, max, &block
69
+ unless @config.skipped? url
70
+ req = Typhoeus::Request.new(
71
+ url,
72
+ followlocation: true,
73
+ verbose: @config.vverbose
74
+ )
75
+ req.on_complete do |resp|
76
+ if retry? resp
77
+ if limit > 1
78
+ puts "Loading #{url} via typhoeus (attempt #{max - limit + 2} of #{max})" if @config.verbose
79
+ _process(url, limit - 1, max, &Proc.new)
80
+ else
81
+ puts "Loading #{url} via typhoeus failed" if @config.verbose
82
+ response = Typhoeus::Response.new(code: 0, status_message: "Server timed out after #{max} retries", mock: true)
83
+ response.request = Typhoeus::Request.new(url)
84
+ Typhoeus.stub(url).and_return(response)
85
+ block.call response, nil, nil
86
+ end
87
+ else
88
+ block.call resp, nil, nil
89
+ end
90
+ end
91
+ @hydra.queue req
92
+ @count += 1
93
+ end
94
+ end
95
+
96
+ end
97
+ end
98
+
@@ -1,4 +1,4 @@
1
1
  module Blinkr
2
- VERSION='0.1.0'
2
+ VERSION='0.2.0'
3
3
  end
4
4
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blinkr
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pete Muir
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-07-21 00:00:00.000000000 Z
11
+ date: 2014-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -113,10 +113,25 @@ files:
113
113
  - blinkr.gemspec
114
114
  - lib/blinkr.rb
115
115
  - lib/blinkr/cache.rb
116
- - lib/blinkr/check.rb
116
+ - lib/blinkr/config.rb
117
+ - lib/blinkr/engine.rb
118
+ - lib/blinkr/extensions/a_title.rb
119
+ - lib/blinkr/extensions/empty_a_href.rb
120
+ - lib/blinkr/extensions/img_alt.rb
121
+ - lib/blinkr/extensions/inline_css.rb
122
+ - lib/blinkr/extensions/javascript.rb
123
+ - lib/blinkr/extensions/links.rb
124
+ - lib/blinkr/extensions/meta.html.slim
125
+ - lib/blinkr/extensions/meta.rb
126
+ - lib/blinkr/extensions/pipeline.rb
127
+ - lib/blinkr/extensions/resources.rb
128
+ - lib/blinkr/http_utils.rb
129
+ - lib/blinkr/phantomjs_wrapper.rb
117
130
  - lib/blinkr/report.html.slim
118
131
  - lib/blinkr/report.rb
132
+ - lib/blinkr/sitemap.rb
119
133
  - lib/blinkr/snap.js
134
+ - lib/blinkr/typhoeus_wrapper.rb
120
135
  - lib/blinkr/version.rb
121
136
  homepage: ''
122
137
  licenses:
data/lib/blinkr/check.rb DELETED
@@ -1,231 +0,0 @@
1
- require 'nokogiri'
2
- require 'uri'
3
- require 'typhoeus'
4
- require 'blinkr/cache'
5
- require 'ostruct'
6
- require 'tempfile'
7
- require 'parallel'
8
-
9
- module Blinkr
10
- class Check
11
-
12
- SNAP_JS = File.expand_path('snap.js', File.dirname(__FILE__))
13
-
14
- def initialize base_url, sitemap: '', skips: [], max_retrys: 3, max_page_retrys: 3, verbose: false, vverbose: false, browser: 'typhoeus', viewport: 1200, ignore_fragments: false, ignores: [], phantomjs_threads: 8
15
- raise "Must specify base_url" if base_url.nil?
16
- unless sitemap.nil?
17
- @sitemap = sitemap
18
- else
19
- @sitemap = URI.join(base_url, 'sitemap.xml').to_s
20
- end
21
- @skips = skips || []
22
- @ignores = ignores || []
23
- @ignores.each {|ignore| raise "An ignore must be a hash" unless ignore.is_a? Hash}
24
- @base_url = base_url
25
- @max_retrys = max_retrys || 3
26
- @max_page_retrys = max_page_retrys || @max_retrys
27
- @browser = browser || 'typhoeus'
28
- @verbose = vverbose || verbose
29
- @vverbose = vverbose
30
- @viewport = viewport || 1200
31
- @ignore_fragments = ignore_fragments
32
- Typhoeus::Config.cache = @typhoeus_cache
33
- @hydra = Typhoeus::Hydra.new(max_concurrency: 200)
34
- @phantomjs_count = 0
35
- @typhoeus_count = 0
36
- @phantomjs_threads = phantomjs_threads || 8
37
- end
38
-
39
- def check
40
- @errors = OpenStruct.new({:links => {}})
41
- @errors.javascript = {} if @browser == 'phantomjs'
42
- @errors.resources = {} if @browser == 'phantomjs'
43
- puts "Loading sitemap from #{@sitemap}"
44
- if @sitemap =~ URI::regexp
45
- sitemap = Nokogiri::XML(Typhoeus.get(@sitemap, followlocation: true).body)
46
- else
47
- sitemap = Nokogiri::XML(File.open(@sitemap))
48
- end
49
- @links = {}
50
- pages(sitemap.css('loc').collect { |loc| loc.content }) do |resp|
51
- if resp.success?
52
- puts "Loaded page #{resp.request.base_url}" if @verbose
53
- body = Nokogiri::HTML(resp.body)
54
- body.css('a[href]').each do |a|
55
- attr = a.attribute('href')
56
- src = resp.request.base_url
57
- url = attr.value
58
- if !url.nil? && @skips.none? { |regex| regex.match(url) }
59
- url = sanitize url, src
60
- @links[url] ||= []
61
- @links[url] << {:src => src, :line => attr.line, :snippet => attr.parent.to_s}
62
- end
63
- end
64
- else
65
- puts "#{resp.code} #{resp.status_message} Unable to load page #{resp.request.base_url} #{'(' + resp.return_message + ')' unless resp.return_message.nil?}"
66
- end
67
- end
68
- @hydra.run
69
- puts "-----------------------------------------------" if @verbose
70
- puts " Page load complete, #{@links.size} links to check " if @verbose
71
- puts "-----------------------------------------------" if @verbose
72
- @links.each do |url, srcs|
73
- typhoeus(url) do |resp|
74
- puts "Loaded #{url} via typhoeus #{'(cached)' if resp.cached?}" if @verbose
75
- unless resp.success? || resp.code == 200
76
- srcs.each do |src|
77
- unless ignored? url, resp.code, resp.status_message || resp.return_message
78
- @errors.links[url] ||= OpenStruct.new({ :code => resp.code.nil? ? nil : resp.code.to_i, :status_message => resp.status_message, :return_message => resp.return_message, :refs => [], :uid => uid(url) })
79
- @errors.links[url].refs << OpenStruct.new({:src => src[:src], :src_location => "line #{src[:line]}", :snippet => src[:snippet]})
80
- end
81
- end
82
- end
83
- end
84
- end
85
- @hydra.run
86
- msg = ''
87
- msg << "Loaded #{@phantomjs_count} pages using phantomjs." if @phantomjs_count > 0
88
- msg << "Performed #{@typhoeus_count} requests using typhoeus."
89
- puts msg
90
- @errors
91
- end
92
-
93
- def single url
94
- typhoeus(url) do |resp|
95
- puts "\n++++++++++"
96
- puts "+ Blinkr +"
97
- puts "++++++++++"
98
- puts "\nRequest"
99
- puts "======="
100
- puts "Method: #{resp.request.options[:method]}"
101
- puts "Max redirects: #{resp.request.options[:maxredirs]}"
102
- puts "Follow location header: #{resp.request.options[:followlocation]}"
103
- puts "Timeout (s): #{resp.request.options[:timeout] || 'none'}"
104
- puts "Connection timeout (s): #{resp.request.options[:connecttimeout] || 'none'}"
105
- puts "\nHeaders"
106
- puts "-------"
107
- unless resp.request.options[:headers].nil?
108
- resp.request.options[:headers].each do |name, value|
109
- puts "#{name}: #{value}"
110
- end
111
- end
112
- puts "\nResponse"
113
- puts "========"
114
- puts "Status Code: #{resp.code}"
115
- puts "Status Message: #{resp.status_message}"
116
- puts "Message: #{resp.return_message}" unless resp.return_message.nil? || resp.return_message == 'No error'
117
- puts "\nHeaders"
118
- puts "-------"
119
- puts resp.response_headers
120
- end
121
- @hydra.run
122
- end
123
-
124
- private
125
-
126
- def ignored? url, code, message
127
- @ignores.any? { |ignore| ( ignore.has_key?('url') ? !ignore['url'].match(url).nil? : true ) && ( ignore.has_key?('code') ? ignore['code'] == code : true ) && ( ignore.has_key?('message') ? !ignore['message'].match(message).nil? : true ) }
128
- end
129
-
130
- def sanitize url, src
131
- begin
132
- uri = URI(url)
133
- uri = URI.join(src, url) if uri.path.nil? || uri.path.empty? || uri.relative?
134
- uri = URI.join(@base_url, uri) if uri.scheme.nil?
135
- uri.fragment = '' if @ignore_fragments
136
- url = uri.to_s
137
- rescue Exception => e
138
- end
139
- url.chomp('#').chomp('index.html')
140
- end
141
-
142
- def uid url
143
- url.gsub(/:|\/|\.|\?|#|%|=|&|,|~|;|\!|@|\)|\(|\s/, '_')
144
- end
145
-
146
- def pages urls
147
- if @browser == 'phantomjs'
148
- Parallel.each(urls, :in_threads => @phantomjs_threads) do |url|
149
- phantomjs url, @max_page_retrys, &Proc.new
150
- end
151
- else
152
- urls.each do |url|
153
- typhoeus url, @max_page_retrys, &Proc.new
154
- end
155
- end
156
- end
157
-
158
- def phantomjs url, limit = @max_retrys, max = -1
159
- max = limit if max == -1
160
- if @skips.none? { |regex| regex.match(url) }
161
- Tempfile.open('blinkr') do|f|
162
- if system "phantomjs #{SNAP_JS} #{url} #{@viewport} #{f.path}"
163
- json = JSON.load(File.read(f.path))
164
- json['resourceErrors'].each do |error|
165
- start = error['errorString'].rindex('server replied: ')
166
- errorString = error['errorString'].slice(start.nil? ? 0 : start + 16, error['errorString'].length) unless error['errorString'].nil?
167
- unless ignored? error['url'], error['errorCode'].nil? ? nil : error['errorCode'].to_i, errorString
168
- @errors.resources[url] ||= OpenStruct.new({:uid => uid(url), :messages => [] })
169
- @errors.resources[url].messages << OpenStruct.new(error.update({'errorString' => errorString}))
170
- end
171
- end
172
- json['javascriptErrors'].each do |error|
173
- @errors.javascript[url] ||= OpenStruct.new({:uid => uid(url), :messages => []})
174
- @errors.javascript[url].messages << OpenStruct.new(error)
175
- end
176
- response = Typhoeus::Response.new(code: 200, body: json['content'], mock: true)
177
- response.request = Typhoeus::Request.new(url)
178
- Typhoeus.stub(url).and_return(response)
179
- yield response
180
- else
181
- if limit > 1
182
- puts "Loading #{url} via phantomjs (attempt #{max - limit + 2} of #{max})" if @verbose
183
- phantomjs url, limit - 1, max, &Proc.new
184
- else
185
- puts "Loading #{url} via phantomjs failed" if @verbose
186
- response = Typhoeus::Response.new(code: 0, status_message: "Server timed out after #{max} retries", mock: true)
187
- response.request = Typhoeus::Request.new(url)
188
- Typhoeus.stub(url).and_return(response)
189
- yield response
190
- end
191
- end
192
- end
193
- @phantomjs_count += 1
194
- end
195
- end
196
-
197
- def retry? resp
198
- resp.timed_out? || (resp.code == 0 && [ "Server returned nothing (no headers, no data)", "SSL connect error", "Failure when receiving data from the peer" ].include?(resp.return_message) )
199
- end
200
-
201
- def typhoeus url, limit = @max_retrys, max = -1
202
- max = limit if max == -1
203
- if @skips.none? { |regex| regex.match(url) }
204
- req = Typhoeus::Request.new(
205
- url,
206
- followlocation: true,
207
- verbose: @vverbose
208
- )
209
- req.on_complete do |resp|
210
- if retry? resp
211
- if limit > 1
212
- puts "Loading #{url} via typhoeus (attempt #{max - limit + 2} of #{max})" if @verbose
213
- typhoeus(url, limit - 1, max, &Proc.new)
214
- else
215
- puts "Loading #{url} via typhoeus failed" if @verbose
216
- response = Typhoeus::Response.new(code: 0, status_message: "Server timed out after #{max} retries", mock: true)
217
- response.request = Typhoeus::Request.new(url)
218
- Typhoeus.stub(url).and_return(response)
219
- yield response
220
- end
221
- else
222
- yield resp
223
- end
224
- end
225
- @hydra.queue req
226
- @typhoeus_count += 1
227
- end
228
- end
229
- end
230
- end
231
-