blinkr 0.2.9 → 0.3.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 39d18a782d87077bd68ed1c41307c385f082b4fc
4
- data.tar.gz: 87a369d2d62eb1ac451b1500222469e20a454357
3
+ metadata.gz: 2b54b1fe0983042827e5f00868dcf9e65ef5c2f2
4
+ data.tar.gz: f90a1abe798a9e3cfaebe7544f0e618b4379116d
5
5
  SHA512:
6
- metadata.gz: ebe1ff145b684dc28e397c5f2cf87ca392d52535c807d8be53452746c564e1e323eaa71bb23a67a1c1c07ddac2cc22df0080b124d74828183defb1af6ae7d7d2
7
- data.tar.gz: 0a20c4ee727298906e7f46b82a622cec2a83f2ce67e529cc29ed9f07df6c35c1954aee150554aba4e60435392dccf2873774eb469c0589d11d776b54b4b1fd65
6
+ metadata.gz: bdbee3d503b16a6354a29171b775ff5bfb1667203529b158cc2e26ce3f58749402fea5098dd1a349387050d76e8454861dbe8919d8473cdf57e7d6205b6503c7
7
+ data.tar.gz: bfc4cc60420fed8c53c20bcec5b224fb341a3e3a3d53a054aaa2e4cdff2575d5f5b0f62b391c459b4df89cae5875e7b1353ab23723ac99138cd70d13aba7d460
data/Gemfile CHANGED
@@ -1,4 +1,7 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ platform :jruby do
4
+ gem 'manticore'
5
+ end
3
6
  # Specify your gem's dependencies in blinkr.gemspec
4
7
  gemspec
data/bin/blinkr CHANGED
@@ -22,6 +22,7 @@ OptionParser.new do |opts|
22
22
  end
23
23
  opts.on("-s", "--single-url URL", "test a single URL, outputting the response to the console") do |opt|
24
24
  options[:single_url] = opt
25
+ options[:base_url] = opt
25
26
  end
26
27
  end.parse!
27
28
 
data/blinkr.gemspec CHANGED
@@ -4,26 +4,34 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'blinkr/version'
5
5
 
6
6
  Gem::Specification.new do |spec|
7
- spec.name = "blinkr"
7
+ spec.name = 'blinkr'
8
8
  spec.version = Blinkr::VERSION
9
- spec.authors = ["Pete Muir"]
10
- spec.email = ["pmuir@bleepbleep.org.uk"]
9
+ spec.authors = ['Pete Muir', 'Jason Porter']
10
+ spec.email = %w(pmuir@bleepbleep.org.uk lightguard.jp@gmail.com)
11
11
  spec.summary = %q{A simple broken link checker}
12
12
  spec.description = %q{A broken page and link checker for websites. Optionally uses phantomjs to render pages to check resource loading, links created by JS, and report any JS page load errors.}
13
- spec.homepage = "https://github.com/pmuir/blinkr"
14
- spec.license = "Apache-2.0"
13
+ spec.homepage = 'https://github.com/pmuir/blinkr'
14
+ spec.license = 'Apache-2.0'
15
15
 
16
16
  spec.files = `git ls-files`.split($/)
17
+ spec.files -= ['.gitignore', '.ruby-version', '.ruby-gemset']
18
+
19
+
17
20
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
21
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
- spec.require_paths = ["lib"]
22
+ spec.require_paths = ['lib']
20
23
 
21
- spec.required_ruby_version = '~> 2.0'
24
+ spec.required_ruby_version = '~> 1.9'
22
25
 
23
- spec.add_development_dependency "bundler", "~> 1.5"
26
+ spec.add_development_dependency 'bundler', '~> 1.5'
24
27
  spec.add_development_dependency 'rake', '~> 10.3'
25
28
  spec.add_dependency 'nokogiri', '~> 1.5'
26
29
  spec.add_dependency 'typhoeus', '~> 0.7'
27
30
  spec.add_dependency 'slim', '~> 3.0'
28
31
  spec.add_dependency 'parallel', '~> 1.3'
32
+
33
+ if defined? JRUBY_VERSION
34
+ spec.platform = 'java'
35
+ spec.add_dependency 'manticore', '~> 0.4'
36
+ end
29
37
  end
data/lib/blinkr.rb CHANGED
@@ -2,12 +2,13 @@ require 'blinkr/version'
2
2
  require 'blinkr/engine'
3
3
  require 'blinkr/report'
4
4
  require 'blinkr/config'
5
+ require 'blinkr/error'
5
6
  require 'blinkr/typhoeus_wrapper'
6
7
  require 'yaml'
7
8
 
8
9
  module Blinkr
9
10
  def self.run(base_url, config = 'blinkr.yaml', single, verbose, vverbose)
10
- args = { :base_url => base_url, :verbose => verbose, :vverbose => vverbose }
11
+ args = {:base_url => base_url, :verbose => verbose, :vverbose => vverbose}
11
12
  if !config.nil? && File.exists?(config)
12
13
  config = Blinkr::Config.read config, args
13
14
  else
data/lib/blinkr/cache.rb CHANGED
@@ -9,7 +9,11 @@ module Blinkr
9
9
  end
10
10
 
11
11
  def set(request, response)
12
- @memory[request] = response unless response.timed_out?
12
+ if request.is_a? String # HACK for caching resource and js errors
13
+ @memory[request] = response
14
+ else
15
+ @memory[request] = response unless response.timed_out?
16
+ end
13
17
  end
14
18
 
15
19
  def size
data/lib/blinkr/config.rb CHANGED
@@ -8,7 +8,8 @@ module Blinkr
8
8
  Config.new(YAML.load_file(file).merge(args).merge({ :config_file => file }))
9
9
  end
10
10
 
11
- DEFAULTS = {:skips => [], :ignores => [], :max_retrys => 3, :browser => 'typhoeus', :viewport => 1200, :phantomjs_threads => 8, :report => 'blinkr.html'}
11
+ DEFAULTS = {:skips => [], :ignores => [], :max_retrys => 3, :browser => 'typhoeus',
12
+ :viewport => 1200, :phantomjs_threads => 8, :report => 'blinkr.html'}
12
13
 
13
14
  def initialize(hash={})
14
15
  super(DEFAULTS.merge(hash))
data/lib/blinkr/engine.rb CHANGED
@@ -9,6 +9,8 @@ require 'blinkr/extensions/javascript'
9
9
  require 'blinkr/extensions/resources'
10
10
  require 'blinkr/extensions/pipeline'
11
11
  require 'json'
12
+ require 'pathname'
13
+ require 'fileutils'
12
14
  require 'ostruct'
13
15
 
14
16
  # Monkeypatch OpenStruct
@@ -27,7 +29,7 @@ module Blinkr
27
29
  include HttpUtils
28
30
  include Sitemap
29
31
 
30
- def initialize config
32
+ def initialize(config)
31
33
  @config = config.validate
32
34
  @extensions = []
33
35
  load_pipeline
@@ -35,15 +37,25 @@ module Blinkr
35
37
 
36
38
  def run
37
39
  context = OpenStruct.new({:pages => {}})
38
- browser = TyphoeusWrapper.new(@config, context)
40
+ if defined?(JRUBY_VERSION) && @config.browser == 'manticore'
41
+ require 'blinkr/manticore_wrapper'
42
+ bulk_browser = browser = ManticoreWrapper.new(@config, context)
43
+ else
44
+ bulk_browser = browser = TyphoeusWrapper.new(@config, context)
45
+ end
39
46
  browser = PhantomJSWrapper.new(@config, context) if @config.browser == 'phantomjs'
40
47
  page_count = 0
41
- browser.process_all(sitemap_locations, @config.max_page_retrys) do |response, resource_errors, javascript_errors|
48
+ urls = sitemap_locations.uniq
49
+ puts "Fetching #{urls.size} pages from sitemap"
50
+ browser.process_all(urls, @config.max_page_retrys) do |response, resource_errors, javascript_errors|
51
+ url = response.request.base_url
42
52
  if response.success?
43
- url = response.request.base_url
44
53
  puts "Loaded page #{url}" if @config.verbose
45
54
  body = Nokogiri::HTML(response.body)
46
- page = OpenStruct.new({ :response => response, :body => body, :errors => ErrorArray.new(@config), :resource_errors => resource_errors || [], :javascript_errors => javascript_errors || [] })
55
+ page = OpenStruct.new({:response => response, :body => body.freeze,
56
+ :errors => ErrorArray.new(@config),
57
+ :resource_errors => resource_errors || [],
58
+ :javascript_errors => javascript_errors || []})
47
59
  context.pages[url] = page
48
60
  collect page
49
61
  page_count += 1
@@ -51,25 +63,26 @@ module Blinkr
51
63
  puts "#{response.code} #{response.status_message} Unable to load page #{url} #{'(' + response.return_message + ')' unless response.return_message.nil?}"
52
64
  end
53
65
  end
54
- typhoeus.hydra.run if @config.browser == 'typhoeus'
55
- analyze context, typhoeus
56
- puts "Loaded #{page_count} pages using #{browser.name}. Performed #{typhoeus.count} requests using typhoeus."
57
- context.pages.reject! { |url, page| page.errors.empty? }
66
+ puts 'Executing Typhoeus::Hydra.run, this could take awhile' if @config.browser == 'typhoeus'
67
+ # browser.hydra.run if @config.browser == 'typhoeus'
68
+ puts "Loaded #{page_count} pages using #{browser.name}."
69
+ puts 'Analyzing pages'
70
+ analyze context, bulk_browser
71
+ context.pages.reject! { |_, page| page.errors.empty? }
72
+
58
73
  unless @config.export.nil?
59
- File.open(@config.export, 'w') do |file|
60
- file.write(context.to_json)
61
- end
74
+ FileUtils.mkdir_p Pathname.new(@config.report).parent
62
75
  end
63
76
  Blinkr::Report.new(context, self, @config).render
64
77
  end
65
78
 
66
- def append context
67
- exec :append, context
79
+ def append(context)
80
+ execute :append, context
68
81
  end
69
-
70
- def transform page, error, &block
82
+
83
+ def transform(page, error, &block)
71
84
  default = yield
72
- result = exec(:transform, page, error, default)
85
+ result = execute(:transform, page, error, default)
73
86
  if result.empty?
74
87
  default
75
88
  else
@@ -77,33 +90,33 @@ module Blinkr
77
90
  end
78
91
  end
79
92
 
80
- def analyze context, typhoeus
81
- exec :analyze, context, typhoeus
93
+ def analyze(context, typhoeus)
94
+ execute :analyze, context, typhoeus
82
95
  end
83
96
 
84
- def collect page
85
- exec :collect, page
97
+ def collect(page)
98
+ execute :collect, page
86
99
  end
87
100
 
88
101
  private
89
102
 
90
103
  class ErrorArray < Array
91
104
 
92
- def initialize config
105
+ def initialize(config)
93
106
  @config = config
94
107
  end
95
108
 
96
- def << error
97
- unless @config.ignored?(error.url, error.code, error.message)
98
- super
99
- else
109
+ def <<(error)
110
+ if @config.ignored?(error.url, error.code, error.message)
100
111
  self
112
+ else
113
+ super
101
114
  end
102
115
  end
103
116
 
104
117
  end
105
118
 
106
- def extension ext
119
+ def extension(ext)
107
120
  @extensions << ext
108
121
  end
109
122
 
@@ -113,7 +126,7 @@ module Blinkr
113
126
  extension Blinkr::Extensions::Resources.new @config
114
127
  end
115
128
 
116
- def exec method, *args
129
+ def execute(method, *args)
117
130
  result = []
118
131
  @extensions.each do |e|
119
132
  result << e.send(method, *args) if e.respond_to? method
@@ -122,24 +135,24 @@ module Blinkr
122
135
  end
123
136
 
124
137
  def load_pipeline
125
- unless @config.pipeline.nil?
138
+ if @config.pipeline.nil?
139
+ puts 'Loaded default pipeline' if @config.verbose
140
+ default_pipeline
141
+ else
126
142
  pipeline_file = File.join(File.dirname(@config.config_file), @config.pipeline)
127
- if File.exists?( pipeline_file )
128
- p = eval(File.read( pipeline_file ), nil, pipeline_file, 1).load @config
143
+ if File.exists?(pipeline_file)
144
+ p = eval(File.read(pipeline_file), nil, pipeline_file, 1).load @config
129
145
  p.extensions.each do |e|
130
- extension( e )
146
+ extension(e)
131
147
  end
132
148
  if @config.verbose
133
149
  puts "Loaded custom pipeline from #{@config.pipeline}"
134
- pipeline = @extensions.inject{|memo, v| "#{memo}, #{v}" }
150
+ pipeline = @extensions.inject { |memo, v| "#{memo}, #{v}" }
135
151
  puts "Pipeline: #{pipeline}"
136
152
  end
137
153
  else
138
154
  raise "Cannot find pipeline file #{pipeline_file}"
139
155
  end
140
- else
141
- puts "Loaded default pipeline" if @config.verbose
142
- default_pipeline
143
156
  end
144
157
  end
145
158
 
@@ -0,0 +1,30 @@
1
+ module Blinkr
2
+ SEVERITY = [:success, :info, :warning, :danger]
3
+ class Error
4
+ attr_reader :severity, :category, :type, :title, :message, :snippet, :icon, :code, :detail, :url
5
+
6
+ def initialize (opts = {})
7
+ raise TypeError 'severity must be a string or symbol' unless opts[:severity].is_a?(String) || opts[:severity].is_a?(Symbol)
8
+ raise 'severity not a recognized value' unless SEVERITY.include? opts[:severity].to_sym
9
+
10
+ @severity = opts[:severity]
11
+ @category = opts[:category]
12
+ @type = opts[:type]
13
+ @title = opts[:title]
14
+ @message = opts[:message]
15
+ @snippet = opts[:snippet]
16
+ @icon = opts[:icon]
17
+ @code = opts[:code]
18
+ @url = opts[:url]
19
+ @detail = opts[:detail]
20
+ end
21
+
22
+ def to_json(*args)
23
+ content = {}
24
+ instance_variables.each do |v|
25
+ content[v.to_s[1..-1]] = instance_variable_get v
26
+ end
27
+ content.to_json
28
+ end
29
+ end
30
+ end
@@ -1,3 +1,5 @@
1
+ require 'blinkr/error'
2
+
1
3
  module Blinkr
2
4
  module Extensions
3
5
  class ATitle
@@ -8,7 +10,11 @@ module Blinkr
8
10
 
9
11
  def collect page
10
12
  page.body.css('a:not([title])').each do |a|
11
- page.errors << OpenStruct.new({ :severity => 'info', :category => 'SEO', :type => '<a title=""> missing', :title => "#{a['href']} (line #{a.line})", :message => '<a title=""> missing', :snippet => a.to_s, :icon => 'fa-info' })
13
+ page.errors << Blinkr::Error.new({:severity => 'info', :category => 'SEO',
14
+ :type => '<a title=""> missing',
15
+ :title => "#{a['href']} (line #{a.line})",
16
+ :message => '<a title=""> missing',
17
+ :snippet => a.to_s, :icon => 'fa-info'})
12
18
  end
13
19
  end
14
20
 
@@ -1,3 +1,5 @@
1
+ require 'blinkr/error'
2
+
1
3
  module Blinkr
2
4
  module Extensions
3
5
  class EmptyAHref
@@ -9,7 +11,11 @@ module Blinkr
9
11
  def collect page
10
12
  page.body.css('a[href]').each do |a|
11
13
  if a['href'].empty?
12
- page.errors << OpenStruct.new({ :severity => 'info', :category => 'HTML Compatibility/Correctness', :type => '<a href=""> empty', :title => %Q{<a href=""> empty (line #{a.line})}, :message => %Q{<a href=""> empty}, :snippet => a.to_s, :icon => 'fa-info' })
14
+ page.errors << Blinkr::Error.new({:severity => 'info', :category => 'HTML Compatibility/Correctness',
15
+ :type => '<a href=""> empty',
16
+ :title => %Q{<a href=""> empty (line #{a.line})},
17
+ :message => %Q{<a href=""> empty}, :snippet => a.to_s,
18
+ :icon => 'fa-info'})
13
19
  end
14
20
  end
15
21
  end
@@ -1,3 +1,5 @@
1
+ require 'blinkr/error'
2
+
1
3
  module Blinkr
2
4
  module Extensions
3
5
  class ImgAlt
@@ -8,7 +10,11 @@ module Blinkr
8
10
 
9
11
  def collect page
10
12
  page.body.css('img:not([alt])').each do |img|
11
- page.errors << OpenStruct.new({ :severity => 'warning', :category => 'SEO', :type => '<img alt=""> missing', :title => "#{img['src']} (line #{img.line})", :message => '<img alt=""> missing', :snippet => img.to_s, :icon => 'fa-info' })
13
+ page.errors << OpenStruct.new({:severity => 'warning', :category => 'SEO',
14
+ :type => '<img alt=""> missing',
15
+ :title => "#{img['src']} (line #{img.line})",
16
+ :message => '<img alt=""> missing', :snippet => img.to_s,
17
+ :icon => 'fa-info'})
12
18
  end
13
19
  end
14
20
 
@@ -1,3 +1,5 @@
1
+ require 'blinkr/error'
2
+
1
3
  module Blinkr
2
4
  module Extensions
3
5
  class InlineCss
@@ -9,9 +11,18 @@ module Blinkr
9
11
  def collect page
10
12
  page.body.css('[style]').each do |elm|
11
13
  if elm['style'] == ""
12
- page.errors << OpenStruct.new({ :severity => 'info', :category => 'HTML Compatibility/Correctness', :type => 'style attribute is empty', :title => %Q{"#{elm['style']}" (line #{elm.line})}, :message => 'style attribute is empty', :snippet => elm.to_s, :icon => 'fa-info' })
14
+ page.errors << Blinkr::Error.new({:severity => 'info', :category => 'HTML Compatibility/Correctness',
15
+ :type => 'style attribute is empty',
16
+ :title => %Q{"#{elm['style']}" (line #{elm.line})},
17
+ :message => 'style attribute is empty', :snippet => elm.to_s,
18
+ :icon => 'fa-info'})
13
19
  else
14
- page.errors << OpenStruct.new({ :severity => 'info', :category => 'HTML Compatibility/Correctness', :type => 'Inline CSS detected', :title => %Q{"#{elm['style']}" (line #{elm.line})}, :message => 'inline style', :snippet => elm.to_s, :icon => 'fa-info' })
20
+ page.errors << Blinkr::Error.new({:severity => 'info',
21
+ :category => 'HTML Compatibility/Correctness',
22
+ :type => 'Inline CSS detected',
23
+ :title => %Q{"#{elm['style']}" (line #{elm.line})},
24
+ :message => 'inline style', :snippet => elm.to_s,
25
+ :icon => 'fa-info'})
15
26
  end
16
27
  end
17
28
  end
@@ -1,3 +1,5 @@
1
+ require 'blinkr/error'
2
+
1
3
  module Blinkr
2
4
  module Extensions
3
5
  class JavaScript
@@ -8,7 +10,9 @@ module Blinkr
8
10
 
9
11
  def collect page
10
12
  page.javascript_errors.each do |error|
11
- page.errors << OpenStruct.new({ :severity => 'danger', :category => 'JavaScript', :type => 'JavaScript error', :title => error['msg'], :snippet => error['trace'], :icon => 'fa-gears' })
13
+ page.errors << Blinkr::Error.new(:severity => 'danger', :category => 'JavaScript',
14
+ :type => 'JavaScript error', :title => error['msg'],
15
+ :snippet => error['trace'], :icon => 'fa-gears')
12
16
  end
13
17
  end
14
18
 
@@ -1,50 +1,94 @@
1
+ require 'uri'
2
+ require 'blinkr/error'
1
3
  require 'blinkr/http_utils'
2
4
 
3
5
  module Blinkr
4
6
  module Extensions
5
7
  class Links
6
- include HttpUtils
8
+ include Blinkr::HttpUtils
7
9
 
8
- def initialize config
10
+ def initialize(config)
9
11
  @config = config
10
12
  @links = {}
11
13
  end
12
14
 
13
- def collect page
15
+ def collect(page)
14
16
  page.body.css('a[href]').each do |a|
15
17
  attr = a.attribute('href')
16
18
  src = page.response.effective_url
17
19
  url = attr.value
18
- url = sanitize url, src
19
- unless url.nil? || @config.skipped?(url)
20
- @links[url] ||= []
21
- @links[url] << {:page => page, :line => attr.line, :snippet => attr.parent.to_s}
20
+ unless @config.skipped?(url)
21
+ url = sanitize url, src
22
+ unless url.nil?
23
+ @links[url] ||= []
24
+ @links[url] << {:page => page, :line => attr.line, :snippet => attr.parent.to_s}
25
+ end
22
26
  end
23
27
  end
24
28
  end
25
29
 
26
- def analyze context, typhoeus
27
- puts "----------------------" if @config.verbose
28
- puts " #{@links.length} links to check " if @config.verbose
29
- puts "----------------------" if @config.verbose
30
- @links.each do |url, metadata|
31
- typhoeus.process(url, @config.max_retrys) do |resp|
32
- puts "Loaded #{url} via typhoeus #{'(cached)' if resp.cached?}" if @config.verbose
33
- unless resp.success? || resp.code == 200
30
+ def analyze(context, browser)
31
+ puts '----------------------'
32
+ puts " #{@links.length} links to check "
33
+ puts '----------------------'
34
+ external_links = @links.reject { |k| k.start_with? @config.base_url }
35
+ processed = 0
36
+ # Find the internal links
37
+ @links.select{|k| k.start_with? @config.base_url}.each do |url, locations|
38
+ link = URI.parse(url)
39
+ link.fragment = nil
40
+ link.query = nil
41
+ unless context.pages.keys.include?(link.to_s) || context.pages.keys.include?((link.to_s + '/'))
42
+ locations.each do |location|
43
+ location[:page].errors << Blinkr::Error.new({:severity => :warning,
44
+ :category => 'Resource missing from sitemap',
45
+ :type => '<a href=""> target missing from sitemap',
46
+ :url => url, :title => "#{url} (line #{location[:line]})",
47
+ :code => nil,
48
+ :message => 'Missing from sitemap',
49
+ :detail => 'Checked with Typheous',
50
+ :snippet => location[:snippet],
51
+ :icon => 'fa-bookmark-o'
52
+ })
53
+ # It wasn't in the sitemap, so we'll add it to the "external_links" to still be checked
54
+ external_links[url] = locations
55
+ end
56
+ end
57
+ end
58
+ external_links.each do |url, metadata|
59
+ # if link start_with? @config.base_url check to see if it's in the sitemap.xml
60
+ browser.process(url, @config.max_retrys, :method => :get, :followlocation => true) do |resp|
61
+ puts "Loaded #{url} via #{browser.name} #{'(cached)' if resp.cached?}" if @config.verbose
62
+ if resp.code.to_i < 200 || resp.code.to_i > 300
63
+ response = resp
64
+
34
65
  metadata.each do |src|
35
- code = resp.code.to_i unless resp.code.nil? || resp.code == 0
36
- if resp.status_message.nil?
37
- message = resp.return_message
66
+ detail = nil
67
+ if response.status_message.nil?
68
+ message = response.return_message
38
69
  else
39
- message = resp.status_message
40
- detail = resp.return_message unless resp.return_message == "No error"
70
+ message = response.status_message
71
+ detail = response.return_message unless resp.return_message == 'No error'
72
+ end
73
+
74
+ severity = :danger
75
+ if response.code.to_i >= 300 && response.code.to_i < 400
76
+ severity = :warning
41
77
  end
42
- src[:page].errors << OpenStruct.new({ :severity => 'danger', :category => 'Resources missing', :type => '<a href=""> target cannot be loaded', :url => url, :title => "#{url} (line #{src[:line]})", :code => code, :message => message, :detail => detail, :snippet => src[:snippet], :icon => 'fa-bookmark-o' })
78
+ src[:page].errors << Blinkr::Error.new({:severity => severity,
79
+ :category => 'Resources missing',
80
+ :type => '<a href=""> target cannot be loaded',
81
+ :url => url, :title => "#{url} (line #{src[:line]})",
82
+ :code => response.code.to_i, :message => message,
83
+ :detail => detail, :snippet => src[:snippet],
84
+ :icon => 'fa-bookmark-o'}) unless response.success?
43
85
  end
44
86
  end
87
+ processed += 1
88
+ puts "Processed #{processed} of #{external_links.size}" if @config.verbose
45
89
  end
46
90
  end
47
- typhoeus.hydra.run
91
+ browser.hydra.run if browser.is_a? Blinkr::TyphoeusWrapper
48
92
  end
49
93
 
50
94
  end