Permalink
Browse files

added nokogiri-manipulator, removed old stuff from spec_helper

  • Loading branch information...
juliocesar committed Dec 4, 2010
1 parent f4e4d8c commit bd2ee6bc115aaac00048a603505117a3c4c8c6ed
Showing with 152 additions and 63 deletions.
  1. +10 −11 Rakefile
  2. +1 −1 VERSION
  3. +1 −0 lib/rack-pagespeed.rb
  4. +101 −0 lib/rack/pagespeed.rb
  5. +10 −0 lib/rack/pagespeed/manipulator.rb
  6. +26 −0 spec/fixtures/complex.html
  7. +0 −17 spec/fixtures/index.html
  8. +3 −34 spec/spec_helper.rb
View
@@ -1,25 +1,24 @@
require 'rubygems'
require 'rspec/core/rake_task'
-RSpec::Core::RakeTask.new do |t|
+RSpec::Core::RakeTask.new do |t|
t.pattern = 'spec/**/*_spec.rb'
t.rspec_opts = ['-c', '-f nested', '-r ./spec/spec_helper']
end
begin
require 'jeweler'
Jeweler::Tasks.new do |gem|
- gem.name = "rack-bundle"
- gem.summary = "Javascript and CSS bundling at the Rack level"
- gem.description = "Javascript and CSS bundling at the Rack level"
- gem.email = "julio.ody@gmail.com"
- gem.homepage = "http://github.com/juliocesar/rack-bundle"
+ gem.name = "rack-pagespeed"
+ gem.summary = "Web page speed enhancements at the Rack level"
+ gem.description = "Web page speed enhancements at the Rack level"
+ gem.email = "julio@awesomebydesign.com"
+ gem.homepage = "http://github.com/juliocesar/rack-pagespeed"
gem.authors = "Julio Cesar Ody"
- gem.add_dependency 'rack', '= 1.2.1'
- gem.add_dependency 'nokogiri', '= 1.4.4'
- gem.add_development_dependency 'rspec', '= 2.1.0'
- gem.add_development_dependency 'rake', '>= 0.8.7'
+ gem.add_dependency 'nokogiri', '1.2.1'
+ gem.add_dependency 'nokogiri', '1.4.4'
+ gem.add_development_dependency 'rspec', '2.1.0'
end
rescue LoadError
- puts 'Jeweler (or a dependency) not available. Install it with: gem install jeweler'
+ puts 'Jeweler not available. gemspec tasks OFF.'
end
View
@@ -1 +1 @@
-0.2.3
+0.1.0
View
@@ -0,0 +1 @@
+require 'rack/pagespeed'
View
@@ -0,0 +1,101 @@
+require 'rack'
+require 'nokogiri'
+require 'nokogiri/manipulator'
+
+module Rack
+ class PageSpeed
+ attr_accessor :storage, :document, :public_dir
+ autoload :Manipulator, 'rack/pagespeed/manipulator'
+
+ def initialize app
+ @app = app
+ end
+
+ def call env
+ if match = %r(^/rack-bundle-(\w+)).match(env['PATH_INFO'])
+ bundle = @storage.find_bundle_by_hash match[1]
+ bundle ? respond_with(bundle) : not_found
+ else
+ status, headers, @response = @app.call(env)
+ return [status, headers, @response] unless headers['Content-Type'] =~ /html/
+ parse!
+ replace_javascripts!
+ replace_stylesheets!
+ body = @document.to_html
+ headers['Content-Length'] = body.length.to_s if headers['Content-Length'] # Not UTF-8 safe
+ [status, headers, [body]]
+ end
+ end
+
+ def parse!
+ body = ""
+ @response.each do |part| body << part end
+ @document = Nokogiri::HTML(body)
+ end
+
+ def replace_javascripts!
+ return unless @document.css(SELECTORS.js).count > 1
+ bundle = JSBundle.new *scripts
+ @storage.add bundle unless @storage.has_bundle? bundle
+ bundle_node = @document.create_element 'script',
+ :type => 'text/javascript',
+ :src => bundle.path,
+ :charset => 'utf-8'
+ @document.css(SELECTORS.js).first.before(bundle_node)
+ @document.css(SELECTORS.js).slice(1..-1).remove
+ @document
+ end
+
+ def replace_stylesheets!
+ return unless local_css_nodes.count > 1
+ styles = local_css_nodes.group_by { |node| node.attribute('media').value rescue nil }
+ styles.each do |media, nodes|
+ next unless nodes.count > 1
+ stylesheets = stylesheets_for nodes
+ bundle = CSSBundle.new *stylesheets
+ @storage.add bundle unless @storage.has_bundle? bundle
+ node = @document.create_element 'link',
+ :rel => 'stylesheet',
+ :type => 'text/css',
+ :href => bundle.path,
+ :media => media
+ nodes.first.before(node)
+ nodes.map { |node| node.remove }
+ end
+ @document
+ end
+
+ private
+ def local_javascript_nodes
+ @document.css(SELECTORS.js)
+ end
+
+ def local_css_nodes
+ @document.css(SELECTORS.css)
+ end
+
+ def scripts
+ local_javascript_nodes.inject([]) do |files, node|
+ path = ::File.join(@public_dir, node.attribute('src').value)
+ files << ::File.open(path) if ::File.exists?(path)
+ files
+ end
+ end
+
+ def stylesheets_for nodes
+ nodes.inject([]) do |files, node|
+ path = ::File.join(@public_dir, node.attribute('href').value)
+ files << ::File.open(path) if ::File.exists?(path)
+ files
+ end
+ end
+
+ def not_found
+ [404, {'Content-Type' => 'text/plain'}, ['Not Found']]
+ end
+
+ def respond_with bundle
+ [200, {'Content-Type' => bundle.mime_type}, [bundle.contents]]
+ end
+ end
+end
@@ -0,0 +1,10 @@
+require
+
+class Manipulator
+ def initialize document
+
+ end
+
+ def move
+ end
+end
View
@@ -0,0 +1,26 @@
+<!DOCTYPE html>
+<html>
+ <head>
+ <script type="text/javascript" charset="utf-8" src="http://somewhere.com/somejs.js"></script>
+ <link rel="stylesheet" href="reset.css" type="text/css" media="screen" />
+ <link rel="stylesheet" href="screen.css" type="text/css" media="screen" />
+ <link rel="stylesheet" href="iphone.css" type="text/css" media="handheld" />
+ <link rel="stylesheet" href="medialess1.css" type="text/css"/>
+ <link rel="stylesheet" href="medialess2.css" type="text/css"/>
+ </head>
+ <body>
+ <header>
+ <h1>Hello page</h1>
+ </header>
+ <p>A fixture page</p>
+ <p>With 2 paragraphs<p>
+ <img src="/foobar.jpg" />
+ <img src="/dog.jpg" />
+ <p id="test">A test paragraph</p>
+ <p id="foo">Foo bar</p>
+ <div id="footer">And a footer, in poorly semantic HTML</div>
+ </body>
+ <script type="text/javascript" charset="utf-8" src="jquery-1.4.1.min.js"></script>
+ <script type="text/javascript" charset="utf-8" src="mylib.js"></script>
+ <script type="text/javascript" charset="utf-8" src="http://externallyhosted.com/javascript.js"></script>
+</html>
View
@@ -1,17 +0,0 @@
-<!DOCTYPE html>
-<html>
- <head>
- <script type="text/javascript" charset="utf-8" src="jquery-1.4.1.min.js"></script>
- <script type="text/javascript" charset="utf-8" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.1/jquery.min.js"></script>
- <script type="text/javascript" charset="utf-8" src="mylib.js"></script>
- <link rel="stylesheet" href="reset.css" type="text/css" media="screen" />
- <link rel="stylesheet" href="screen.css" type="text/css" media="screen" />
- <link rel="stylesheet" href="hh-reset.css" type="text/css" media="handheld" />
- <link rel="stylesheet" href="iphone.css" type="text/css" media="handheld" />
- <link rel="stylesheet" href="medialess1.css" type="text/css"/>
- <link rel="stylesheet" href="medialess2.css" type="text/css"/>
- </head>
- <body>
- <p>A fixture page</p>
- </body>
-</html>
View
@@ -1,8 +1,7 @@
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
require 'rspec'
require 'fileutils'
-require 'rack/bundle'
-require 'rack/bundle/bundles/base'
+require 'rack/pagespeed'
require 'tmpdir'
include Rack::Utils
@@ -14,36 +13,6 @@ def fixture name
File.open(File.join(FIXTURES_PATH, name))
end
-def make_js_bundle
- Rack::Bundle::JSBundle.new $jquery, $mylib
+def page
+ lambda { |env| [200, { 'Content-Type' => 'text/html' }, [fixture('complex.html').contents]] }
end
-
-def make_css_bundle
- Rack::Bundle::CSSBundle.new $reset, $screen
-end
-
-def filename file
- File.basename file.path
-end
-
-def index_page
- lambda { |env| [200, { 'Content-Type' => 'text/html' }, [fixture('index.html').contents]] }
-end
-
-def simple_page
- lambda { |env| [200, { 'Content-Type' => 'text/html' }, [fixture('simple.html').contents]] }
-end
-
-def plain_text
- lambda { |env| [200, { 'Content-Type' => 'text/plain' }, ['plain texto']] }
-end
-
-RSpec.configure do |config|
- $jquery, $mylib = fixture('jquery-1.4.1.min.js'), fixture('mylib.js')
- $reset, $screen = fixture('reset.css'), fixture('screen.css')
- $index = fixture('index.html')
- $doc = Nokogiri::HTML($index)
- config.after(:all) do
- `rm -f #{FIXTURES_PATH}/rack-bundle*`
- end
-end

0 comments on commit bd2ee6b

Please sign in to comment.