class Deadweight

Attributes

ignore_selectors[RW]
log_file[RW]
mechanize[RW]
pages[RW]
parsed_rules[R]
root[RW]
rules[RW]
stylesheets[RW]
unused_selectors[R]

Public Class Methods

new() { |self and run| ... } click to toggle source
# File lib/deadweight.rb, line 19
def initialize
  @root = 'http://localhost:3000'
  @stylesheets = []
  @pages = []
  @rules = ""
  @ignore_selectors = []
  @mechanize = false
  @log_file = STDERR
  yield self and run if block_given?
end

Public Instance Methods

add_css!(css) click to toggle source
# File lib/deadweight.rb, line 47
def add_css!(css)
  parser = CssParser::Parser.new
  parser.add_block!(css)

  selector_count = 0

  parser.each_selector do |selector, declarations, specificity|
    next if @unused_selectors.include?(selector)
    next if selector =~ @ignore_selectors
    next if has_pseudo_classes(selector) and @unused_selectors.include?(strip(selector))

    @unused_selectors << selector
    @parsed_rules[selector] = declarations

    selector_count += 1
  end

  selector_count
end
agent() click to toggle source

Returns the Mechanize instance, if mechanize is set to true.

# File lib/deadweight.rb, line 137
def agent
  @agent ||= initialize_agent
end
analyze(html) click to toggle source
# File lib/deadweight.rb, line 30
def analyze(html)
  doc = Nokogiri::HTML(html)

  @unused_selectors.collect do |selector, declarations|
    # We test against the selector stripped of any pseudo classes,
    # but we report on the selector with its pseudo classes.
    stripped_selector = strip(selector)

    next if stripped_selector.empty?

    if doc.search(stripped_selector).any?
      log.puts("  #{selector.green}")
      selector
    end
  end
end
dump(output) click to toggle source
# File lib/deadweight.rb, line 126
def dump(output)
  output.puts(@unused_selectors)
end
fetch(path) click to toggle source

Fetch a path, using Mechanize if mechanize is set to true.

# File lib/deadweight.rb, line 142
def fetch(path)
  log.puts(path)

  loc = root + path

  if @mechanize
    loc = "file://#{File.expand_path(loc)}" unless loc =~ %r{^\w+://}

    begin
      page = agent.get(loc)
    rescue Mechanize::ResponseCodeError => e
      raise FetchError.new("#{loc} returned a response code of #{e.response_code}")
    end

    log.puts("#{loc} redirected to #{page.uri}".red) unless page.uri.to_s == loc

    page.body
  else
    begin
      open(loc).read
    rescue Errno::ENOENT
      raise FetchError.new("#{loc} was not found")
    rescue OpenURI::HTTPError => e
      raise FetchError.new("retrieving #{loc} raised an HTTP error: #{e.message}")
    end
  end
end
process!(html) click to toggle source
# File lib/deadweight.rb, line 130
def process!(html)
  analyze(html).each do |selector|
    @unused_selectors.delete(selector)
  end
end
report() click to toggle source
# File lib/deadweight.rb, line 85
def report
  log.puts
  log.puts "found #{@unused_selectors.size} unused selectors out of #{@total_selectors} total".yellow
  log.puts
end
reset!() click to toggle source
# File lib/deadweight.rb, line 67
def reset!
  @parsed_rules     = {}
  @unused_selectors = []

  @stylesheets.each do |path|
    new_selector_count = add_css!(fetch(path))
    log.puts("  found #{new_selector_count} selectors".yellow)
  end

  if @rules and !@rules.empty?
    new_selector_count = add_css!(@rules)
    log.puts
    log.puts("Added #{new_selector_count} extra selectors".yellow)
  end

  @total_selectors = @unused_selectors.size
end
run() click to toggle source

Find all unused CSS selectors and return them as an array.

# File lib/deadweight.rb, line 92
def run
  reset!

  pages.each do |page|
    log.puts

    if page.respond_to?(:read)
      html = page.read
    elsif page.respond_to?(:call)
      result = instance_eval(&page)

      html = case result
             when String
               result
             else
               @agent.page.body
             end
    else
      begin
        html = fetch(page)
      rescue FetchError => e
        log.puts(e.message.red)
        next
      end
    end

    process!(html)
  end

  report

  @unused_selectors
end

Private Instance Methods

has_pseudo_classes(selector) click to toggle source
# File lib/deadweight.rb, line 172
def has_pseudo_classes(selector)
  selector =~ /::?[\w\-]+/
end
initialize_agent() click to toggle source
# File lib/deadweight.rb, line 190
def initialize_agent
  begin
    require 'mechanize'

    unless defined?(Mechanize::VERSION) and Mechanize::VERSION >= "1.0.0"
      log.puts %{
        =================================================================
        A mechanize version of 1.0.0 or above is required.
        Install it like so: gem install mechanize
        =================================================================
      }
    end

    return Mechanize.new
  rescue LoadError
    log.puts %{
      =================================================================
      Couldn't load 'mechanize', which is required for remote scraping.
      Install it like so: gem install mechanize
      =================================================================
    }

    raise
  end
end
log() click to toggle source
# File lib/deadweight.rb, line 182
def log
  @log ||= if @log_file.respond_to?(:puts)
             @log_file
           else
             open(@log_file, 'w+')
           end
end
strip(selector) click to toggle source
# File lib/deadweight.rb, line 176
def strip(selector)
  selector = selector.gsub(/^@.*/, '') # @-webkit-keyframes ...
  selector = selector.gsub(/:.*/, '')  # input#x:nth-child(2):not(#z.o[type='file'])
  selector
end