aanand-deadweight 0.0.1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.sw?
2
+ .DS_Store
3
+ coverage
4
+ rdoc
5
+ pkg
6
+ test.log
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Aanand Prasad
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,59 @@
1
+ = deadweight
2
+
3
+ Deadweight is RCov for CSS, kind of. Given a set of stylesheets and a set of URLs, it determines which selectors are actually used and reports which can be "safely" deleted.
4
+
5
+ Simple example:
6
+ # lib/tasks/deadweight.rake
7
+
8
+ require 'deadweight'
9
+
10
+ description "run Deadweight (script/server needs to be running)"
11
+ task :deadweight do
12
+ dw = Deadweight.new
13
+ dw.stylesheets = %w( /stylesheets/style.css )
14
+ dw.pages = %w( / /page/1 /about )
15
+ puts dw.run
16
+ end
17
+
18
+ This will output all unused selectors, one per line.
19
+
20
+ Things to note:
21
+ - By default, it looks at http://localhost:3000.
22
+ - It's completely dumb about any classes, IDs or tags that are only added by your Javascript layer, but you can filter them out by setting +ignore_selectors+.
23
+ - You can optionally tell it to use Mechanize, and set up more complicated targets for scraping by specifying them as Procs.
24
+
25
+ More complex example:
26
+
27
+ # lib/tasks/deadweight.rake
28
+
29
+ require 'deadweight'
30
+
31
+ description "run Deadweight on staging server"
32
+ task :deadweight do
33
+ dw = Deadweight.new
34
+
35
+ dw.mechanize = true
36
+
37
+ dw.root = 'http://staging.example.com'
38
+
39
+ dw.stylesheets = %w( /stylesheets/style.css )
40
+
41
+ dw.pages = %w( / /page/1 /about )
42
+
43
+ dw.pages << proc {
44
+ fetch('/login')
45
+ form = agent.page.forms.first
46
+ form.username = 'username'
47
+ form.password = 'password'
48
+ agent.submit(form)
49
+ fetch('/secret-page')
50
+ }
51
+
52
+ dw.ignore_selectors = /hover|lightbox|superimposed_kittens/
53
+
54
+ puts dw.run
55
+ end
56
+
57
+ == Copyright
58
+
59
+ Copyright (c) 2009 Aanand Prasad. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,58 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "deadweight"
8
+ gem.summary = "RCov for CSS"
9
+ gem.email = "aanand.prasad@gmail.com"
10
+ gem.homepage = "http://github.com/aanand/deadweight"
11
+ gem.authors = ["Aanand Prasad"]
12
+
13
+ gem.add_dependency('css_parser')
14
+ gem.add_dependency('hpricot')
15
+ end
16
+
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/*_test.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/*_test.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ if File.exist?('VERSION.yml')
47
+ config = YAML.load(File.read('VERSION.yml'))
48
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
49
+ else
50
+ version = ""
51
+ end
52
+
53
+ rdoc.rdoc_dir = 'rdoc'
54
+ rdoc.title = "deadweight #{version}"
55
+ rdoc.rdoc_files.include('README*')
56
+ rdoc.rdoc_files.include('lib/**/*.rb')
57
+ end
58
+
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,55 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{deadweight}
5
+ s.version = "0.0.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Aanand Prasad"]
9
+ s.date = %q{2009-07-21}
10
+ s.email = %q{aanand.prasad@gmail.com}
11
+ s.extra_rdoc_files = [
12
+ "LICENSE",
13
+ "README.rdoc"
14
+ ]
15
+ s.files = [
16
+ ".document",
17
+ ".gitignore",
18
+ "LICENSE",
19
+ "README.rdoc",
20
+ "Rakefile",
21
+ "VERSION",
22
+ "deadweight.gemspec",
23
+ "lib/deadweight.rb",
24
+ "test/deadweight_test.rb",
25
+ "test/fixtures/index.html",
26
+ "test/fixtures/index2.html",
27
+ "test/fixtures/style.css",
28
+ "test/test_helper.rb"
29
+ ]
30
+ s.homepage = %q{http://github.com/aanand/deadweight}
31
+ s.rdoc_options = ["--charset=UTF-8"]
32
+ s.require_paths = ["lib"]
33
+ s.rubygems_version = %q{1.3.4}
34
+ s.summary = %q{RCov for CSS}
35
+ s.test_files = [
36
+ "test/deadweight_test.rb",
37
+ "test/test_helper.rb"
38
+ ]
39
+
40
+ if s.respond_to? :specification_version then
41
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
42
+ s.specification_version = 3
43
+
44
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
45
+ s.add_runtime_dependency(%q<css_parser>, [">= 0"])
46
+ s.add_runtime_dependency(%q<hpricot>, [">= 0"])
47
+ else
48
+ s.add_dependency(%q<css_parser>, [">= 0"])
49
+ s.add_dependency(%q<hpricot>, [">= 0"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<css_parser>, [">= 0"])
53
+ s.add_dependency(%q<hpricot>, [">= 0"])
54
+ end
55
+ end
data/lib/deadweight.rb ADDED
@@ -0,0 +1,110 @@
1
+ require 'css_parser'
2
+ require 'hpricot'
3
+ require 'open-uri'
4
+ require 'logger'
5
+
6
+ class Deadweight
7
+ attr_accessor :root, :stylesheets, :pages, :ignore_selectors, :mechanize, :log_file
8
+
9
+ def initialize
10
+ @root = 'http://localhost:3000'
11
+ @stylesheets = []
12
+ @pages = []
13
+ @ignore_selectors = []
14
+ @mechanize = false
15
+ @log_file = STDERR
16
+ end
17
+
18
+ def run
19
+ css = CssParser::Parser.new
20
+
21
+ @stylesheets.each do |path|
22
+ css.add_block!(fetch(path))
23
+ end
24
+
25
+ unused_selectors = []
26
+ total_selectors = 0
27
+
28
+ css.each_selector do |selector, declarations, specificity|
29
+ unless unused_selectors.include?(selector)
30
+ total_selectors += 1
31
+ unused_selectors << selector unless selector =~ ignore_selectors
32
+ end
33
+ end
34
+
35
+ pages.each do |page|
36
+ case page
37
+ when String
38
+ html = fetch(page)
39
+ else
40
+ result = instance_eval(&page)
41
+
42
+ html = case result
43
+ when String
44
+ result
45
+ else
46
+ @agent.page.body
47
+ end
48
+ end
49
+
50
+ doc = Hpricot(html)
51
+
52
+ found_selectors = []
53
+
54
+ unused_selectors.each do |selector|
55
+ unless doc.search(selector).empty?
56
+ log.info(" #{selector}")
57
+ found_selectors << selector
58
+ end
59
+ end
60
+
61
+ unused_selectors -= found_selectors
62
+ end
63
+
64
+ log.info "found #{unused_selectors.size} unused selectors out of #{total_selectors} total"
65
+
66
+ unused_selectors
67
+ end
68
+
69
+ private
70
+
71
+ def agent
72
+ @agent ||= initialize_agent
73
+ end
74
+
75
+ def log
76
+ @log ||= Logger.new(@log_file)
77
+ end
78
+
79
+ def initialize_agent
80
+ begin
81
+ require 'mechanize'
82
+ return WWW::Mechanize.new
83
+ rescue LoadError
84
+ log.info %{
85
+ =================================================================
86
+ Couldn't load 'mechanize', which is required for remote scraping.
87
+ Install it like so: gem install mechanize
88
+ =================================================================
89
+ }
90
+
91
+ raise
92
+ end
93
+ end
94
+
95
+ def fetch(path)
96
+ log.info(path)
97
+
98
+ loc = root + path
99
+
100
+ if @mechanize
101
+ loc = "file://#{File.expand_path(loc)}" unless loc =~ %r{^\w+://}
102
+ page = agent.get(loc)
103
+ log.warn("#{path} redirected to #{page.uri}") unless page.uri.to_s == loc
104
+ page.body
105
+ else
106
+ open(loc).read
107
+ end
108
+ end
109
+ end
110
+
@@ -0,0 +1,33 @@
1
+ require 'test_helper'
2
+
3
+ class DeadweightTest < Test::Unit::TestCase
4
+ def setup
5
+ @dw = Deadweight.new
6
+ @dw.log_file = 'test.log'
7
+ @dw.root = File.dirname(__FILE__) + '/fixtures'
8
+ @dw.stylesheets << '/style.css'
9
+ @dw.pages << '/index.html'
10
+
11
+ @result = @dw.run
12
+ end
13
+
14
+ should "report unused selectors" do
15
+ assert @result.include?('#foo .bar .baz')
16
+ end
17
+
18
+ should "not report used selectors" do
19
+ assert !@result.include?('#foo')
20
+ assert !@result.include?('#foo .bar')
21
+ end
22
+
23
+ should "accept Procs as targets" do
24
+ @dw.mechanize = true
25
+
26
+ @dw.pages << proc {
27
+ fetch('/index.html')
28
+ agent.page.links.first.click
29
+ }
30
+
31
+ assert @dw.run.empty?
32
+ end
33
+ end
@@ -0,0 +1,16 @@
1
+ <html>
2
+ <head>
3
+ <link rel="stylesheet" type="text/css" href="style.css">
4
+ </head>
5
+
6
+ <body>
7
+ <div id="foo">
8
+ <div class="bar">
9
+ hello
10
+ </div>
11
+ </div>
12
+
13
+ <a href="index2.html">next</a>
14
+ </body>
15
+ </html>
16
+
@@ -0,0 +1,17 @@
1
+ <html>
2
+ <head>
3
+ <link rel="stylesheet" type="text/css" href="style.css">
4
+ </head>
5
+
6
+ <body>
7
+ <div id="foo">
8
+ <div class="bar">
9
+ <div class="baz">
10
+ hello again
11
+ </div>
12
+ </div>
13
+ </div>
14
+ </body>
15
+ </html>
16
+
17
+
@@ -0,0 +1,12 @@
1
+ #foo {
2
+ color: green;
3
+ }
4
+
5
+ #foo .bar {
6
+ color: blue;
7
+ }
8
+
9
+ #foo .bar .baz {
10
+ color: red;
11
+ }
12
+
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'deadweight'
8
+
9
+ class Test::Unit::TestCase
10
+ end
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: aanand-deadweight
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Aanand Prasad
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-21 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: css_parser
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: hpricot
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: "0"
34
+ version:
35
+ description:
36
+ email: aanand.prasad@gmail.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION
51
+ - deadweight.gemspec
52
+ - lib/deadweight.rb
53
+ - test/deadweight_test.rb
54
+ - test/fixtures/index.html
55
+ - test/fixtures/index2.html
56
+ - test/fixtures/style.css
57
+ - test/test_helper.rb
58
+ has_rdoc: false
59
+ homepage: http://github.com/aanand/deadweight
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "0"
70
+ version:
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: "0"
76
+ version:
77
+ requirements: []
78
+
79
+ rubyforge_project:
80
+ rubygems_version: 1.2.0
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: RCov for CSS
84
+ test_files:
85
+ - test/deadweight_test.rb
86
+ - test/test_helper.rb