sqweeze 0.0.2
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/Manifest +20 -0
- data/README.rdoc +28 -0
- data/Rakefile +15 -0
- data/bin/sqweeze +116 -0
- data/lib/compilers/assetLinker.rb +33 -0
- data/lib/compilers/cssDomCompiler.rb +23 -0
- data/lib/compilers/jsDomCompiler.rb +24 -0
- data/lib/compressor.rb +128 -0
- data/lib/compressors/cssCompressor.rb +103 -0
- data/lib/compressors/gifCompressor.rb +13 -0
- data/lib/compressors/jpegCompressor.rb +6 -0
- data/lib/compressors/jsCompressor.rb +27 -0
- data/lib/compressors/pngCompressor.rb +11 -0
- data/lib/confManager.rb +191 -0
- data/lib/domCompiler.rb +51 -0
- data/lib/sqweezeUtils.rb +86 -0
- data/spec/confManager_spec.rb +61 -0
- data/spec/domCompiler_spec.rb +41 -0
- data/spec/sqw_spec_helper.rb +24 -0
- data/sqweeze.gemspec +41 -0
- metadata +151 -0
data/Manifest
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Manifest
|
2
|
+
README.rdoc
|
3
|
+
Rakefile
|
4
|
+
bin/sqweeze
|
5
|
+
lib/compilers/assetLinker.rb
|
6
|
+
lib/compilers/cssDomCompiler.rb
|
7
|
+
lib/compilers/jsDomCompiler.rb
|
8
|
+
lib/compressor.rb
|
9
|
+
lib/compressors/cssCompressor.rb
|
10
|
+
lib/compressors/gifCompressor.rb
|
11
|
+
lib/compressors/jpegCompressor.rb
|
12
|
+
lib/compressors/jsCompressor.rb
|
13
|
+
lib/compressors/pngCompressor.rb
|
14
|
+
lib/confManager.rb
|
15
|
+
lib/domCompiler.rb
|
16
|
+
lib/sqweezeUtils.rb
|
17
|
+
spec/confManager_spec.rb
|
18
|
+
spec/domCompiler_spec.rb
|
19
|
+
spec/sqw_spec_helper.rb
|
20
|
+
sqweeze.gemspec
|
data/README.rdoc
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
== Sqweeze
|
2
|
+
A command line web asset optimisation tool.
|
3
|
+
|
4
|
+
=== Features
|
5
|
+
|
6
|
+
_Sqweeze_ cuts the unnecessary bytes out of your web assets, helping you delivering your web project more fast and efficiently.
|
7
|
+
With a little help from a number of well known open source third party tools, _Sqweeze_ does the following:
|
8
|
+
|
9
|
+
* Lossless compresses .PNG, .JPEG and .GIF image assets.
|
10
|
+
* Compresses and concatenates into single files multiple Stylesheets and Javascript files.
|
11
|
+
* Embeds the compressed assets referenced into the stylesheet, using using {Data-uris}[https://developer.mozilla.org/En/The_data_URL_scheme] (for proper browsers and IE8) and {MHTML}[http://en.wikipedia.org/wiki/MHTML] (for IE6-7).
|
12
|
+
* Finally, it provides some basic functionality to linking the compressed Javascript and CSS files back to the document.
|
13
|
+
|
14
|
+
=== Installing it
|
15
|
+
|
16
|
+
Sqweeze relies on a few command-line image compression tools, namely Pngcrush, Jpegtrain, and Gifsicle, which have to be installed first.
|
17
|
+
On an Ubuntu box, you can install these by running a single command line
|
18
|
+
|
19
|
+
sudo apt-get install pngcrush gifsicle libjpeg-progs
|
20
|
+
|
21
|
+
(Mac OSX users can install the first two as Macports packages. The third (part of libjpeg) instead, has to be
|
22
|
+
{compiled manually}[http://www.phpied.com/installing-jpegtran-mac-unix-linux]. However, Jpegtrain will not be needed as long as you do not have Jpeg files into your project.)
|
23
|
+
|
24
|
+
Once installed the requirements above, you can install sqweeze, and its ruby dependencies, through ruby gems
|
25
|
+
|
26
|
+
gem install sqweeze
|
27
|
+
|
28
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'echoe'
|
4
|
+
|
5
|
+
Echoe.new('sqweeze', '0.0.1') do |p|
|
6
|
+
p.description = "A command line web-asset optimisation tool"
|
7
|
+
p.url = "http://github.com/premasagar/sqwidget-builder"
|
8
|
+
p.author = "Andrea Fiore"
|
9
|
+
p.email = "and@inventati.org"
|
10
|
+
p.spec_pattern=['spec']
|
11
|
+
p.ignore_pattern = ["doc/", 'spec/test_dir_sqweezed',"webapp"]
|
12
|
+
p.runtime_dependencies = ['hpricot >= 0.8.2','closure-compiler >=0.2.2','yui-compressor >= 0.9.1']
|
13
|
+
|
14
|
+
end
|
15
|
+
|
data/bin/sqweeze
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# rubygems
|
3
|
+
%w[rubygems yui/compressor closure-compiler hpricot].each{|lib| require lib}
|
4
|
+
# ruby standard library
|
5
|
+
%w[logger singleton yaml base64 open-uri pathname optparse].each{|lib| require lib}
|
6
|
+
|
7
|
+
# add lib sub-directories to load path
|
8
|
+
SQWEEZE_DIR=File.expand_path( File.dirname(File.dirname(__FILE__)))
|
9
|
+
$LOAD_PATH.unshift(SQWEEZE_DIR) unless $LOAD_PATH.include?(SQWEEZE_DIR)
|
10
|
+
['lib','lib/compressor', 'lib/compilers'].each do |sub_dir|
|
11
|
+
$LOAD_PATH.unshift("#{SQWEEZE_DIR}/#{sub_dir}")
|
12
|
+
end
|
13
|
+
|
14
|
+
%w[sqweezeUtils confManager compressor domCompiler
|
15
|
+
compressors/cssCompressor compressors/jsCompressor compressors/gifCompressor compressors/pngCompressor compressors/jpegCompressor
|
16
|
+
compilers/cssDomCompiler compilers/jsDomCompiler compilers/assetLinker
|
17
|
+
].each{|lib| require lib}
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
options = {}
|
23
|
+
op=OptionParser.new do |opts|
|
24
|
+
|
25
|
+
opts.banner = "Usage: sqweeze [options] SOURCE_DIR [TARGET_DIR]"
|
26
|
+
opts.program_name= 'sqweeze'
|
27
|
+
opts.on("-s", "--optimisation-strategy=ALL_IN_ONE",
|
28
|
+
"JavaScript/CSS optimisation strategy to be adopted ") do |s|
|
29
|
+
raise OptionParser::InvalidArgument unless ['all_inline','all_in_one'].include?(s)
|
30
|
+
options[:optimisation_strategy] = s.downcase.to_sym
|
31
|
+
end
|
32
|
+
|
33
|
+
opts.separator ""
|
34
|
+
opts.separator "File Selection:"
|
35
|
+
opts.on('-i','--include-files=PATTERN', "Include a file/directory pattern (e.g. images/**/*.png ) ") do |inc|
|
36
|
+
options[:include_files] = inc
|
37
|
+
end
|
38
|
+
opts.on('-e','--exclude-files=PATTERN', "Exclude a file/directory pattern (e.g. doc/**/*.* )") do |exc|
|
39
|
+
options[:exclude_files] = exc
|
40
|
+
end
|
41
|
+
opts.on('-d','--dom-documents=PATTERN', "Dom document/s to which CSS/JS should be linked (e.g. **/index.html )") do |dom|
|
42
|
+
options[:dom_documents] = dom
|
43
|
+
end
|
44
|
+
|
45
|
+
opts.separator ""
|
46
|
+
opts.separator "Image Compression:"
|
47
|
+
opts.on("--[no-]png", "do/do-not use the PNG Compressor ") do |png|
|
48
|
+
options[:compress_png] = png
|
49
|
+
end
|
50
|
+
opts.on("--[no-]jpeg", "do/do-not use the Jpeg Compressor ") do |jpeg|
|
51
|
+
options[:compress_jpeg] = jpeg
|
52
|
+
end
|
53
|
+
opts.on("--[no-]gif", "do/do-not use the GIF Compressor ") do |gif|
|
54
|
+
options[:compress_gif] = gif
|
55
|
+
end
|
56
|
+
opts.separator ""
|
57
|
+
opts.separator "Stylesheets:"
|
58
|
+
|
59
|
+
opts.on("--[no-]css", "do/do-not use the CSS Compressor ") do |css|
|
60
|
+
options[:compress_css] = css
|
61
|
+
end
|
62
|
+
opts.on("-m","--mhtml-root=[ABSOLUTE_URL]", 'An absolute url pointing at the project stylesheets directory on the web') do |mr|
|
63
|
+
# raise OptionParser::InvalidArgument unless URI(mr).absolute?
|
64
|
+
options[:mhtml_root] = mr
|
65
|
+
end
|
66
|
+
opts.separator ""
|
67
|
+
opts.separator "JavaScript:"
|
68
|
+
|
69
|
+
opts.on("--[no-]js", "do/do-not use the JS Compressor ") do |js|
|
70
|
+
options[:compress_js] = js
|
71
|
+
end
|
72
|
+
opts.on('-a','--append_scripts_to=HEAD', "Append script elements to the HEAD or the BODY of the document") do |elm|
|
73
|
+
options[:append_scripts_to] = elm
|
74
|
+
end
|
75
|
+
opts.on("-j","--default-js-compressor=YUI", "Sets the default JavaScript compressor (can be either YUI or Closure) ") do |j|
|
76
|
+
raise OptionParser::InvalidArgument unless ['yui','closure'].include?(j.downcase)
|
77
|
+
options[:default_js_compressor] = j.downcase.to_sym
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
|
82
|
+
begin
|
83
|
+
op.parse!
|
84
|
+
Proc.new { puts "Missing SOURCE_DIR argument"; exit(1) }.call unless ARGV[0]
|
85
|
+
Proc.new { puts "source directory #{ARGV[0]} does not exist, please provide a valid path"
|
86
|
+
exit(1) }.call unless File.exists?(ARGV[0])
|
87
|
+
|
88
|
+
|
89
|
+
$log=Logger.new( STDERR )
|
90
|
+
cm=ConfManager.instance
|
91
|
+
cm.prepare(ARGV[0], ARGV[1], options)
|
92
|
+
|
93
|
+
# Compress images
|
94
|
+
{ :compress_png => PngCompressor.new,
|
95
|
+
:compress_gif => GifCompressor.new,
|
96
|
+
:compress_jpeg => JpegCompressor.new
|
97
|
+
}.each do |opt, cmp|
|
98
|
+
cmp.compress unless cm.get_conf(opt) == false
|
99
|
+
end
|
100
|
+
|
101
|
+
case cm.get_conf(:optimisation_strategy)
|
102
|
+
when :all_inline
|
103
|
+
puts "not implemented yet.."
|
104
|
+
exit
|
105
|
+
|
106
|
+
|
107
|
+
when :all_in_one
|
108
|
+
{ :compress_js => JsCompressor.new,
|
109
|
+
:compress_css => CssCompressor.new
|
110
|
+
}.each do |opt, cmp|
|
111
|
+
cmp.compress unless cm.get_conf(opt) == false
|
112
|
+
end
|
113
|
+
# link assets to main HTML file
|
114
|
+
AssetLinker.new.compile unless cm.get_conf(:dom_documents).empty?
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
class AssetLinker < DOMCompiler
|
2
|
+
|
3
|
+
def compile
|
4
|
+
iterate_over("link[@rel='stylesheet'], script[@src]") do |elm,doc|
|
5
|
+
# Skip if the script has an absolute link
|
6
|
+
next if elm.get_attribute('src') =~ /^http:\/\//
|
7
|
+
# Otherwise delete.
|
8
|
+
elm.parent.children.delete(elm)
|
9
|
+
end
|
10
|
+
dom_documents.each do |path|
|
11
|
+
# Append concatenated JavaScript file.
|
12
|
+
doc = Hpricot(open(path))
|
13
|
+
doc.search(@cm.get_conf(:append_scripts_to).to_s.downcase).append( "<script type='text/javascript' src='javascripts.min.js'></script>")
|
14
|
+
|
15
|
+
stylesheets_html=<<EOF
|
16
|
+
<!--[if lte IE 8]>
|
17
|
+
<link href="stylesheets.min.mhtml.css" rel="stylesheet" />
|
18
|
+
<![endif]-->
|
19
|
+
<!--[if IE 8]>
|
20
|
+
<link href="stylesheets.min.datauri.css" rel="stylesheet" />
|
21
|
+
<![endif]-->
|
22
|
+
<!--[if !IE]>
|
23
|
+
<link href="stylesheets.min.datauri.css" rel="stylesheet" />
|
24
|
+
<![endif]-->
|
25
|
+
EOF
|
26
|
+
|
27
|
+
# Append ie conditional tags
|
28
|
+
doc.search('head').append(stylesheets_html)
|
29
|
+
write_file(doc.innerHTML,path)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
class CssDomCompiler < DOMCompiler
|
2
|
+
|
3
|
+
def compile
|
4
|
+
iterate_over('link[@rel="stylesheet"]') do |elm, doc|
|
5
|
+
next unless elm.has_attribute?('href') and not elm.get_attribute('href').nil?
|
6
|
+
|
7
|
+
fbody=get_resource(elm.get_attribute('href'))
|
8
|
+
|
9
|
+
# this is a bit convoluted, but it seems to be the right way of doing it
|
10
|
+
elm.parent.children.delete(elm)
|
11
|
+
|
12
|
+
style_html = "<style type=\"text/css\">\n" + css_cdata(fbody,:compress) + '</style>'
|
13
|
+
doc.search('head').append(style_html)
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
def compress(fbody)
|
20
|
+
YUI::CssCompressor.new.compress(fbody)
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class JsDomCompiler < DOMCompiler
|
2
|
+
|
3
|
+
def compile
|
4
|
+
iterate_over('script[@src]') do |element, doc |
|
5
|
+
|
6
|
+
res_body=get_resource(element.get_attribute('src'))
|
7
|
+
element.remove_attribute('src')
|
8
|
+
# do the YUI/CLOSURE thing here
|
9
|
+
element.innerHTML=js_cdata(res_body,:compress)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def compress(fbody)
|
15
|
+
unless @cm.default_js_compressor == :closure
|
16
|
+
compressor, method = YUI::JavaScriptCompressor.new( :munge => true), :compress
|
17
|
+
else
|
18
|
+
compressor, method = Closure::Compiler.new, :compiler
|
19
|
+
end
|
20
|
+
compressor.send(method,fbody)
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
end
|
data/lib/compressor.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# Pseudo-abstract class inherited by all the other classes performing asset-compression operations
|
2
|
+
|
3
|
+
class Compressor
|
4
|
+
include SqweezeUtils
|
5
|
+
|
6
|
+
# Sets the file extensions that a compressor accepts
|
7
|
+
#
|
8
|
+
# *Note*: As this class cannot be instanciated directly, this method is only used by subclasses.
|
9
|
+
def initialize(input_file_extensions)
|
10
|
+
|
11
|
+
# Behave as a pseudo abstract class
|
12
|
+
raise "Cannot instantiate #{self.class} directly" if self.class == Compressor
|
13
|
+
|
14
|
+
@cm=ConfManager.instance
|
15
|
+
@input_file_extensions=input_file_extensions
|
16
|
+
|
17
|
+
|
18
|
+
# Get the project file list from the configuration manager
|
19
|
+
@input_files=@cm.files
|
20
|
+
|
21
|
+
# The commands used by this compressor,
|
22
|
+
# listed in order of priority
|
23
|
+
|
24
|
+
@commands={}
|
25
|
+
|
26
|
+
# Set the default shell command to use for this compressor.
|
27
|
+
# issue: what's the point of calling this default command and not just default..
|
28
|
+
@default_command=nil
|
29
|
+
|
30
|
+
# whether or not concatenating the input files (it is the case of css and javascripts)
|
31
|
+
@concatenate_input=false
|
32
|
+
|
33
|
+
# Store the overall byte weight of the assets, before compressions
|
34
|
+
@byteweight_before = collect_filepaths.inject(0){|sum,f| sum+File.size(f)}
|
35
|
+
|
36
|
+
#set later, after compression
|
37
|
+
@byteweight_after = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
attr_reader :input_file_extensions, #something here
|
42
|
+
:byteweight_before,
|
43
|
+
:byteweight_after
|
44
|
+
|
45
|
+
|
46
|
+
# Sets the system command to be invoked by the compressor.
|
47
|
+
#
|
48
|
+
# This raises a runtime error if the third party command cannot be found in the file system.
|
49
|
+
def set_command(libname,command)
|
50
|
+
|
51
|
+
raise "missing library #{libname}" unless @cm.get_conf(:bin_paths).keys.include?(libname)
|
52
|
+
@default_command=libname if @commands.empty?
|
53
|
+
@commands[libname]=command
|
54
|
+
end
|
55
|
+
|
56
|
+
# Turns a string or an array of strings containing several file extension names into regular expressions.
|
57
|
+
|
58
|
+
def filextension2regexpstr(ext=@input_file_extensions)
|
59
|
+
if @input_file_extensions.is_a?(Array) and @input_file_extensions.size > 1
|
60
|
+
"(#{@input_file_extensions.collect{|ext|"\.#{ext}"}.join('|')})$"
|
61
|
+
else
|
62
|
+
"\.#{@input_file_extensions}$"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
|
67
|
+
# Collects the paths of files having the compressor extension name.
|
68
|
+
def collect_filepaths
|
69
|
+
exp_str = filextension2regexpstr
|
70
|
+
@cm.files.select{|path| path =~ Regexp.new(exp_str,true) }
|
71
|
+
end
|
72
|
+
|
73
|
+
# Applies a system command to a list of file paths.
|
74
|
+
#
|
75
|
+
# This method is overridden by both CssCompressor and JsCompressor, as these two classes do not rely on
|
76
|
+
# command line executable but use ruby bindings to YUI and Google Closure.
|
77
|
+
|
78
|
+
def process(inputpath,cmd=nil)
|
79
|
+
output_path =@cm.get_target_path(inputpath)
|
80
|
+
|
81
|
+
cl= cmd.gsub('%executable%', @cm.get_conf(:bin_paths)[@default_command]).
|
82
|
+
gsub('%input%',inputpath).
|
83
|
+
gsub('%output%', output_path)
|
84
|
+
system(cl)
|
85
|
+
$log.debug("run command #{cl}, pid=#{$?.pid} exitstatus=#{$?.exitstatus}")
|
86
|
+
output_path
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
# Iterates over the list of file paths matching the compressor extension/s and applies the process method to each of them.
|
91
|
+
|
92
|
+
def compress
|
93
|
+
files=@input_files=collect_filepaths
|
94
|
+
|
95
|
+
# This will consist of only an entry (the concatenated file body)
|
96
|
+
# when compressing javascript or css files
|
97
|
+
|
98
|
+
files=[concatenate_files] if @concatenate_input
|
99
|
+
cmd= (@commands.empty?)? nil : @commands[ @default_command ]
|
100
|
+
|
101
|
+
files.each do |path|
|
102
|
+
output_path=process(path,cmd)
|
103
|
+
size_check(path,output_path) unless %w(.css .js).include?( File.extname(output_path))
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Makes sure that the byteweight of a compressed file is not larger that the original one.
|
108
|
+
|
109
|
+
def size_check(input_path,output_path)
|
110
|
+
before_size, after_size = byteweight(input_path), byteweight(output_path)
|
111
|
+
# if the compressed file is bigger than the original one, copy the original in the target dir
|
112
|
+
if after_size < before_size
|
113
|
+
$log.info("compressed #{input_path} to the #{ compression_percentage(before_size,after_size)}% of its original size (from: #{before_size} to #{after_size} bytes ) ")
|
114
|
+
@byteweight_after+=after_size
|
115
|
+
else
|
116
|
+
$log.debug("Oppss!!! output(#{after_size}) >= input(#{before_size}), using the original file #{input_path} ")
|
117
|
+
FileUtils.cp(input_path,output_path)
|
118
|
+
@byteweight_after+=before_size
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Concatenates several files into a single string (used in both CSS and JS compression).
|
123
|
+
|
124
|
+
def concatenate_files
|
125
|
+
@input_files.collect{|f|File.open(f,'r').read}.join("\n")
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# Compresses stylesheets using the YUI compressor, creating three new files in the target directory:
|
2
|
+
# [<code>stylesheets.min.css</code>]
|
3
|
+
# compressed and concatenated stylesheets without embedded assets (suitable for all browsers).
|
4
|
+
#
|
5
|
+
# [<code>stylesheets.datauir.css</code>]
|
6
|
+
# compressed and concatenated stylesheets with assets embedded as data-uris (suitable for both Webkit and Geko based browser, and ie8).
|
7
|
+
#
|
8
|
+
# [<code>stylesheets.mhtml.css</code>]
|
9
|
+
# compressed and concatenated stylesheets with assets embedded as data-uris (suitable for IE6 and IE7).
|
10
|
+
#
|
11
|
+
# *Note*: some of the methods used by this class are copied from Jash kenas's excellent {Jammit}[http://github.com/documentcloud/jammit/], an asset optimisation tool for Rails.
|
12
|
+
|
13
|
+
class CssCompressor < Compressor
|
14
|
+
|
15
|
+
# 32k maximum size for embeddable images (an IE8 limitation).
|
16
|
+
MAX_IMAGE_SIZE = 32768
|
17
|
+
|
18
|
+
# CSS asset-embedding regexes for URL rewriting.
|
19
|
+
EMBED_DETECTOR = /url\(['"]?([^\s)]+\.[a-z]+)(\?\d+)?['"]?\)/
|
20
|
+
|
21
|
+
# MHTML file constants.
|
22
|
+
MHTML_START = "/*\nContent-Type: multipart/related; boundary=\"SQWEEZED_ASSET\"\n"
|
23
|
+
MHTML_SEPARATOR= "--SQWEEZED_ASSET"
|
24
|
+
MHTML_END = "*/\n"
|
25
|
+
|
26
|
+
def initialize
|
27
|
+
super('css')
|
28
|
+
|
29
|
+
@concatenate_input=true
|
30
|
+
@concatenated_file=nil
|
31
|
+
# This hash is populated while generating the data uris
|
32
|
+
# in order to be reused later in the MHTML file
|
33
|
+
@assets={}
|
34
|
+
# TODO fonts..
|
35
|
+
end
|
36
|
+
|
37
|
+
def process(input_str,cmd=nil)
|
38
|
+
fout= (@concatenate_input)? "#{@cm.target_dir}/all.min.css" : @cm.get_target_path(inputpath)
|
39
|
+
compressed_output=YUI::CssCompressor.new.compress(input_str)
|
40
|
+
|
41
|
+
unless compressed_output.chomp.empty?
|
42
|
+
write_file(compressed_output,"#{@cm.target_dir}/stylesheets.min.css")
|
43
|
+
embed_datauris( compressed_output )
|
44
|
+
embed_mhtml( compressed_output ) if @cm.get_conf(:mhtml_root)
|
45
|
+
end
|
46
|
+
# this return is pointless
|
47
|
+
fout
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
52
|
+
private
|
53
|
+
def mhtml_location(path)
|
54
|
+
p=Pathname.new(path)
|
55
|
+
p.relative_path_from( Pathname.new(@cm.target_dir))
|
56
|
+
end
|
57
|
+
|
58
|
+
def embed_datauris(compressed_css)
|
59
|
+
out=compressed_css.gsub(EMBED_DETECTOR) do |url|
|
60
|
+
compressed_asset_path=remap_filepath($1)
|
61
|
+
mime_t=mime_type(compressed_asset_path)
|
62
|
+
if compressed_asset_path and File.exists?(compressed_asset_path) and File.size(compressed_asset_path) < MAX_IMAGE_SIZE and mime_t
|
63
|
+
base64_asset=encoded_contents( compressed_asset_path )
|
64
|
+
$log.debug("file:#{compressed_asset_path}; mime-type: #{mime_type($1)}#")
|
65
|
+
# label the image
|
66
|
+
@assets[ compressed_asset_path ] = base64_asset
|
67
|
+
"url(\"data:#{mime_t};charset=utf-8;base64,#{base64_asset}\")"
|
68
|
+
else
|
69
|
+
"url(#{$1})"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
write_file(out,"#{@cm.target_dir}/stylesheets.min.datauri.css")
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
def embed_mhtml(compressed_css)
|
79
|
+
mhtml= MHTML_START
|
80
|
+
|
81
|
+
@assets.each do |mhtml_loc,base64|
|
82
|
+
mhtml <<"
|
83
|
+
#{MHTML_SEPARATOR}
|
84
|
+
Content-location: #{ mhtml_loc }
|
85
|
+
Content-Transfer-Encoding:base64
|
86
|
+
|
87
|
+
#{base64}\n"
|
88
|
+
end
|
89
|
+
mhtml << "*/\n"
|
90
|
+
mhtml << compressed_css.gsub(EMBED_DETECTOR) do |css|
|
91
|
+
compressed_asset_path=remap_filepath($1)
|
92
|
+
if compressed_asset_path
|
93
|
+
"url(mhtml:#{@cm.get_conf(:mhtml_root).gsub(/\/$/,'')}/stylesheets.min.mhtml.css!#{mhtml_location( compressed_asset_path)})"
|
94
|
+
else
|
95
|
+
"url(#{$1})"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
write_file(mhtml,"#{@cm.target_dir}/stylesheets.min.mhtml.css")
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
class JsCompressor < Compressor
|
2
|
+
|
3
|
+
def initialize
|
4
|
+
super('js')
|
5
|
+
@concatenate_input=true
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
def process(input_str,cmd=nil)
|
10
|
+
unless @cm.get_conf(:default_js_compressor) == :closure
|
11
|
+
compressor, method = YUI::JavaScriptCompressor.new( :munge => true), :compress
|
12
|
+
else
|
13
|
+
compressor, method = Closure::Compiler.new, :compiler
|
14
|
+
end
|
15
|
+
|
16
|
+
fout= (@concatenate_input)? "#{@cm.target_dir}/javascripts.min.js" : @cm.get_target_path(inputpath)
|
17
|
+
|
18
|
+
File.open(fout,'w') do |f|
|
19
|
+
f.write(compressor.send( method, input_str))
|
20
|
+
end
|
21
|
+
|
22
|
+
fout
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
|
data/lib/confManager.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
# Main component for handling Sqweeze's file and command-line configuration parameters
|
2
|
+
|
3
|
+
|
4
|
+
class ConfManager
|
5
|
+
include Singleton
|
6
|
+
# Class constructor.
|
7
|
+
#
|
8
|
+
# As ConfManager implements the singleton pattern, it is actually instanciated only once.
|
9
|
+
# The same instance might be retrieved from different places by calling the method:
|
10
|
+
#
|
11
|
+
# <code>ConfManager.instance()</code>
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@conf_filename='.sqweeze.yml'
|
15
|
+
@source_dir=nil
|
16
|
+
@target_dir=nil
|
17
|
+
@files=[]
|
18
|
+
|
19
|
+
@conf={
|
20
|
+
:bin_paths => {},
|
21
|
+
:dom_documents => [],
|
22
|
+
:include_files => [],
|
23
|
+
:exclude_files => [],
|
24
|
+
:compress_png => true,
|
25
|
+
:compress_jpeg => true,
|
26
|
+
:compress_gif => true,
|
27
|
+
:compress_js => true,
|
28
|
+
:compress_css => true,
|
29
|
+
:append_scripts_to => :head,
|
30
|
+
:default_js_compressor => :yui,
|
31
|
+
:optimisation_strategy => :all_in_one
|
32
|
+
}
|
33
|
+
end
|
34
|
+
attr_reader :target_dir,:source_dir,
|
35
|
+
:files,:conf_filename,:conf
|
36
|
+
|
37
|
+
|
38
|
+
# Setter method used to populate the <code>@conf</code> attribute with key value pairs.
|
39
|
+
#
|
40
|
+
# *Note*: string keys are automatically converted to symbols.
|
41
|
+
|
42
|
+
|
43
|
+
def set_conf(key,value);@conf[key.to_sym]=value;end
|
44
|
+
|
45
|
+
# Getter method for retrieving configuration values.
|
46
|
+
|
47
|
+
def get_conf(key);@conf[key.to_sym];end
|
48
|
+
|
49
|
+
# Sets the source directory, the target directory, and parse configuration files.
|
50
|
+
#
|
51
|
+
# [source] the source directory
|
52
|
+
#
|
53
|
+
# [target] the target directory
|
54
|
+
#
|
55
|
+
# [override_conf] a Hash which may be used to override file-based configuration.
|
56
|
+
|
57
|
+
def prepare(source,target=nil,override_conf={})
|
58
|
+
|
59
|
+
@source_dir=source
|
60
|
+
@target_dir=unless target
|
61
|
+
"#{File.basename(source)}_sqweezed"
|
62
|
+
else
|
63
|
+
target
|
64
|
+
end
|
65
|
+
|
66
|
+
write_globalconf
|
67
|
+
# Parses the global configuration file in $HOME.
|
68
|
+
parse_conf
|
69
|
+
# Parses the local configuration file in source directory.
|
70
|
+
local_conf=mkpath(@conf_filename)
|
71
|
+
parse_conf(local_conf) if File.exists?(local_conf)
|
72
|
+
|
73
|
+
# CLI/Overrides of the values already set in config files.
|
74
|
+
override_conf.each{|k,v| set_conf(k, v)} unless override_conf.empty?
|
75
|
+
|
76
|
+
# Creates the list of the files in the project directory.
|
77
|
+
|
78
|
+
list_files
|
79
|
+
copy_source
|
80
|
+
end
|
81
|
+
|
82
|
+
# Copies the source into the target directory.
|
83
|
+
def copy_source
|
84
|
+
FileUtils.cp_r(@source_dir,@target_dir)
|
85
|
+
# avoid nesting into each other multiple copies of the same directory
|
86
|
+
nested_dir=[@target_dir,File.basename(@source_dir)].join('/')
|
87
|
+
FileUtils.rm_r(nested_dir) if File.directory?(nested_dir)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Remaps a filepath from the source to the target directory.
|
91
|
+
#
|
92
|
+
# TODO:check if this works with relative paths (i.e. <code>../imgs/bar.png</code>)
|
93
|
+
|
94
|
+
def get_target_path(infile_path)
|
95
|
+
infile_path.gsub( Regexp.new("^#{@source_dir}"), @target_dir)
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
# Generates a file pattern suitable to be expanded by ruby's {Dir[]}[http://ruby-doc.org/core/classes/Dir.html] method.
|
100
|
+
|
101
|
+
def mkpath(pattern,dir=@source_dir)
|
102
|
+
dir.to_a.push(pattern).join('/')
|
103
|
+
end
|
104
|
+
|
105
|
+
# Defines a global <code>.sqweeze.yml</code> file and places it in the user's home directory.
|
106
|
+
#
|
107
|
+
# The golobal config files sets the default path of the image compression binaries
|
108
|
+
# (see @conf[:bin_paths]).
|
109
|
+
|
110
|
+
def write_globalconf
|
111
|
+
unless File.exists?("#{ENV['HOME']}/#{@conf_filename}")
|
112
|
+
File.open("#{ENV['HOME']}/#{@conf_filename}",'w') do |f|
|
113
|
+
f.write("bin_paths:
|
114
|
+
- pngcrush: /usr/bin/pngcrush
|
115
|
+
- jpegtran: /usr/bin/jpegtran
|
116
|
+
- gifsicle: /usr/bin/gifsicle
|
117
|
+
")
|
118
|
+
f.close
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# Parses configuration files and sets user-defined file inclusion/exlusion patterns.
|
124
|
+
|
125
|
+
def parse_conf(configfile="#{ENV['HOME']}/#{@conf_filename}")
|
126
|
+
$log.debug("Parsing configuration file: #{configfile}")
|
127
|
+
conf=YAML::load_file( configfile )
|
128
|
+
|
129
|
+
bin_paths={}
|
130
|
+
# do not select commands if their path cannot be found on disk
|
131
|
+
conf['bin_paths'].each do |h|
|
132
|
+
if File.exists?(h.values.first)
|
133
|
+
bin_paths[h.keys.first.to_sym] = h.values.first
|
134
|
+
else
|
135
|
+
$log.warn("Command #{h.keys.first} not found in #{h.values.first}")
|
136
|
+
end
|
137
|
+
end
|
138
|
+
set_conf(:bin_paths,bin_paths) unless bin_paths.empty?
|
139
|
+
|
140
|
+
# Expand the dom document pattern excluding the files located in the source directory.
|
141
|
+
if conf['dom_documents']
|
142
|
+
|
143
|
+
domdoc_file_patterns = Dir[conf['dom_documents']].collect{|f| f.gsub(Regexp.new("^#{@source_dir}"),@target_dir)}
|
144
|
+
set_conf(:dom_documents, domdoc_file_patterns)
|
145
|
+
end
|
146
|
+
|
147
|
+
# Sets the favourite js compressor ( can either be :yui or :closure, defaults on YUI)
|
148
|
+
compressor = ( conf['default_js_compressor'] == :closure) ? :closure : :yui
|
149
|
+
set_conf(:default_js_compressor, compressor)
|
150
|
+
|
151
|
+
# others..
|
152
|
+
%w(include exclude).each {|k| set_conf(k, conf[k].to_a)}
|
153
|
+
%w(optimisation_strategy append_scripts_to).each{|k| set_conf(k,conf[k]) if conf[k]}
|
154
|
+
end
|
155
|
+
|
156
|
+
# Explodes the inclusion/exclusion patterns provided by the user into a list, and populates the @files attribute.
|
157
|
+
|
158
|
+
def list_files
|
159
|
+
# unless the user defined file inclusion list is empty, select only the files specified by the user.
|
160
|
+
# Otherwise consider all the files in the project directory as potentially compressible.
|
161
|
+
|
162
|
+
@files = unless get_conf(:include).empty?
|
163
|
+
files=get_files( get_conf(:include));$log.debug("Including #{files.size} file/s from user list")
|
164
|
+
files
|
165
|
+
else
|
166
|
+
get_files
|
167
|
+
end
|
168
|
+
|
169
|
+
# always exclude files explicitly blacklisted by the use
|
170
|
+
exclude_files=get_files(get_conf(:exclude))
|
171
|
+
|
172
|
+
|
173
|
+
$log.debug("Excluding #{exclude_files.size} file/s from user list")
|
174
|
+
@files -= exclude_files
|
175
|
+
$log.info("#{@files.size} file/s found")
|
176
|
+
end
|
177
|
+
|
178
|
+
|
179
|
+
# Get all the files matching an array of patterns
|
180
|
+
#
|
181
|
+
# (Any file expansion patterns accepted ruby's Dir[] method can be used).
|
182
|
+
|
183
|
+
def get_files(pathlist=['**/*'])
|
184
|
+
pathlist.collect{|pattern|
|
185
|
+
|
186
|
+
Dir[ mkpath(pattern) ].
|
187
|
+
reject{|f|File.directory?(f) or not File.exists?(f)}
|
188
|
+
}.flatten
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
data/lib/domCompiler.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
# Pseudo-abstract inherited by all classes doing dom manipulations
|
2
|
+
class DOMCompiler
|
3
|
+
include SqweezeUtils
|
4
|
+
attr_reader :dom_documents
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
# @dom_extnames=['.html','.svg']
|
8
|
+
@cm=ConfManager.instance
|
9
|
+
#if @cm.link_assets_to and not @cm.link_assets_to.empty?
|
10
|
+
@dom_documents=@cm.get_conf(:dom_documents).to_a
|
11
|
+
end
|
12
|
+
# Retrieves a resource, being this either a URL or a file.
|
13
|
+
def get_resource(path_or_url)
|
14
|
+
f=open(remap_filepath(path_or_url))
|
15
|
+
(f.is_a? File)? f.read : f
|
16
|
+
end
|
17
|
+
# Iterates over a DOM element and allows to apply a custom block over it.
|
18
|
+
|
19
|
+
def iterate_over(selector)
|
20
|
+
@dom_documents.each do |path|
|
21
|
+
doc=Hpricot(open(path))
|
22
|
+
if doc
|
23
|
+
doc.search(selector).each do |element|
|
24
|
+
|
25
|
+
#$log.debug..
|
26
|
+
yield(element, doc)
|
27
|
+
# save document
|
28
|
+
#write_file(doc.innerHTML, [@cm.target_dir, File.basename(path) ].join('/') )
|
29
|
+
write_file(doc.innerHTML, path )
|
30
|
+
end
|
31
|
+
else
|
32
|
+
$log.error("DOMCompiler cannot parse #{path}")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# Wraps a string into CDATA escaped in order to be embedded into a <code><script></code> element.
|
38
|
+
# [text] a text string
|
39
|
+
# [callback] an instance method used to process the string (normally compile).
|
40
|
+
def js_cdata(text,callback)
|
41
|
+
["\n<!--//--><![CDATA[//><!--", self.send(callback,text), "//--><!]]>\n"].join("\n")
|
42
|
+
end
|
43
|
+
|
44
|
+
# Wraps a string into CDATA escaped in order to be embedded into a <code><style></code> element.
|
45
|
+
# [text] a text string
|
46
|
+
# [callback] an instance method used to process the string (normally compile).
|
47
|
+
def css_cdata(text,callback)
|
48
|
+
["/* <![CDATA[ */", self.send(callback,text), "/* ]]> */\n"].join("\n")
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
data/lib/sqweezeUtils.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
module SqweezeUtils
|
2
|
+
|
3
|
+
# Mapping from extension to mime-type of all embeddable assets.
|
4
|
+
EMBED_MIME_TYPES = {
|
5
|
+
'.png' => 'image/png',
|
6
|
+
'.jpg' => 'image/jpeg',
|
7
|
+
'.jpeg' => 'image/jpeg',
|
8
|
+
'.gif' => 'image/gif',
|
9
|
+
'.tif' => 'image/tiff',
|
10
|
+
'.tiff' => 'image/tiff',
|
11
|
+
'.ttf' => 'font/truetype',
|
12
|
+
'.otf' => 'font/opentype'
|
13
|
+
}
|
14
|
+
|
15
|
+
def write_file(fbody,fpath)
|
16
|
+
File.open(fpath,'w') do |f|
|
17
|
+
f.write(fbody)
|
18
|
+
f.close
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Grab the mime-type of an asset, by filename.
|
23
|
+
def mime_type(asset_path)
|
24
|
+
EMBED_MIME_TYPES[File.extname(asset_path)] if asset_path
|
25
|
+
end
|
26
|
+
|
27
|
+
# Remaps a file in the source directory to its compressed
|
28
|
+
# version in the target directory
|
29
|
+
#
|
30
|
+
# if the resource is an absolute URL fine,
|
31
|
+
# Otherwise, if the resource is a relative url in the source dir, try
|
32
|
+
# to map it to its compressed version in the target directory
|
33
|
+
|
34
|
+
def remap_filepath(path)
|
35
|
+
path=Pathname.new(path)
|
36
|
+
parent_dir, path_basename = path.parent, path.basename
|
37
|
+
is_absolute = URI.parse(path).absolute?
|
38
|
+
unless is_absolute
|
39
|
+
find_file_in_targetdir( [parent_dir, path_basename].join('/'))
|
40
|
+
else
|
41
|
+
path.to_s
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# Return the Base64-encoded contents of an asset on a single line.
|
46
|
+
def encoded_contents(asset_path)
|
47
|
+
data = open(asset_path, 'rb'){|f| f.read }
|
48
|
+
Base64.encode64(data).gsub(/\n/, '')
|
49
|
+
end
|
50
|
+
|
51
|
+
|
52
|
+
# Gets the byte weight of input strames, wether these are file paths or just strings
|
53
|
+
|
54
|
+
def byteweight(path_or_string)
|
55
|
+
path_or_string="" if path_or_string.nil?
|
56
|
+
|
57
|
+
if File.exists?(path_or_string)
|
58
|
+
File.size(path_or_string)
|
59
|
+
else
|
60
|
+
bweight=0
|
61
|
+
path_or_string.each_byte{|b|bweight+=1}
|
62
|
+
bweight
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def compression_percentage(before_bweight,after_bweight)
|
67
|
+
sprintf('%.2f',after_bweight/before_bweight.to_f * 100)
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# Find a file in the target directory
|
72
|
+
# endpath is a string containing parent directory and file name
|
73
|
+
# (e.g imgs/separator.png)
|
74
|
+
|
75
|
+
# TODO: rewrite this! (it sucks..)
|
76
|
+
|
77
|
+
def find_file_in_targetdir(endpath)
|
78
|
+
pattern= [@cm.target_dir,"**/*#{File.extname(endpath)}"].join('/')
|
79
|
+
#puts "searcing for files in #{pattern} ending with #{endpath}"
|
80
|
+
Dir[pattern].find{|path| path =~ Regexp.new("#{endpath}$")}
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
|
86
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'sqw_spec_helper'
|
2
|
+
|
3
|
+
|
4
|
+
describe ConfManager do
|
5
|
+
context "Starting up" do
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@cm = ConfManager.instance
|
9
|
+
end
|
10
|
+
|
11
|
+
after(:each) do
|
12
|
+
@cm=nil
|
13
|
+
# clean up config files
|
14
|
+
%w( ENV['home'] test_dir ).each do |f|
|
15
|
+
f << "/.sqweeze.yml"
|
16
|
+
FileUtils.rm(f) if File.exists?(f)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "Should create a .sqweeze.yml file in the home directory" do
|
21
|
+
@cm.prepare('test_dir')
|
22
|
+
File.exists?("#{ENV['HOME']}/.sqweeze.yml").should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "Should select the list of files provided by the user if the *include:* section of the .sqweeze.yml is not empty" do
|
26
|
+
extra_lines= ["include:","- js/**/*.js",'- color/**/*.png']
|
27
|
+
write_configfile(extra_lines[0..1].join("\n"))
|
28
|
+
@cm.prepare('test_dir')
|
29
|
+
@cm.should have(3).files
|
30
|
+
|
31
|
+
write_configfile(extra_lines.join("\n"))
|
32
|
+
@cm.prepare('test_dir')
|
33
|
+
@cm.should have(5).files
|
34
|
+
end
|
35
|
+
|
36
|
+
it "Should select all files in the project if the include: section of .sqweeze.yml is left empty" do
|
37
|
+
project_file_count= Dir['test_dir/**/*'].reject{|f| File.directory?(f)}.size
|
38
|
+
@cm.prepare('test_dir')
|
39
|
+
@cm.should have(project_file_count).files
|
40
|
+
end
|
41
|
+
|
42
|
+
it "Should always exclude the files specified by the user in the .sqweeze.yml exclude section " do
|
43
|
+
write_configfile(["exclude:","- js/**/*.js"].join("\n"))
|
44
|
+
project_file_count= Dir['test_dir/**/*'].reject{|f| File.directory?(f)}.size
|
45
|
+
@cm.prepare('test_dir')
|
46
|
+
@cm.should have(46).files
|
47
|
+
end
|
48
|
+
|
49
|
+
it "Should search *dom_documents* in the the target directory " do
|
50
|
+
write_configfile("dom_documents: test_dir/*/page.tpl.php")
|
51
|
+
@cm.prepare('test_dir')
|
52
|
+
@cm.get_conf(:dom_documents).should include('test_dir_sqweezed/tpl/page.tpl.php')
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require '../lib/confManager'
|
2
|
+
require '../lib/DomCompiler'
|
3
|
+
require '../lib/jsDomCompiler'
|
4
|
+
require '../lib/cssDomCompiler'
|
5
|
+
require 'sqw_spec_helper'
|
6
|
+
|
7
|
+
describe DOMCompiler do
|
8
|
+
before(:each) do
|
9
|
+
wget_webpage('http://www.dreamhost.com/domains.html','dom-testdir')
|
10
|
+
cm = ConfManager.instance
|
11
|
+
cm.prepare('dom-testdir','dom-testdir-build')
|
12
|
+
@js_c=JSDOMCompiler.new
|
13
|
+
end
|
14
|
+
|
15
|
+
after(:each) do
|
16
|
+
FileUtils.rm_r('dom-testdir')
|
17
|
+
FileUtils.rm_r('dom-testdir-build')
|
18
|
+
end
|
19
|
+
|
20
|
+
it "(all subclasses) should look for *dom_documents* in target directory" do
|
21
|
+
@js_c.should have(1).dom_documents
|
22
|
+
@js_c.dom_documents.should include('dom-testdir-build/index.html')
|
23
|
+
end
|
24
|
+
|
25
|
+
it "Should fill each script element having a src attribute with the compressed content of the javascript file therin referenciated" do
|
26
|
+
@js_c.compile
|
27
|
+
|
28
|
+
Hpricot( open('dom-testdir-build/index.html')).search('script').each do |element|
|
29
|
+
element.innerHTML.should_not be_empty
|
30
|
+
element.get_attribute('src').should be_nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "Should fill each link tag having a href attribute with the compressed content of the css file therein referenced" do
|
35
|
+
@css_c=CSSDOMCompiler.new
|
36
|
+
@css_c.compile()
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
#
|
@@ -0,0 +1,24 @@
|
|
1
|
+
SQW_CONF_FILE='test_dir/.sqweeze.yml'
|
2
|
+
SQW_FBODY=<<EOF
|
3
|
+
bin_paths:
|
4
|
+
- pngcrush: /usr/bin/pngcrush
|
5
|
+
- jpegtran: /usr/bin/jpegtran
|
6
|
+
- gifsicle: /usr/bin/gifsicle
|
7
|
+
#- cssembed: /home/and/installed/cssembed.jar
|
8
|
+
EOF
|
9
|
+
|
10
|
+
def write_configfile(extra_lines='')
|
11
|
+
fbody = SQW_FBODY + extra_lines
|
12
|
+
f=File.open(SQW_CONF_FILE,'w')
|
13
|
+
f.write( fbody)
|
14
|
+
f.close
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete_configfile
|
18
|
+
FileUtils.rm_r(SQW_CONF_FILE) if File.exists?(SQW_CONF_FILE)
|
19
|
+
end
|
20
|
+
|
21
|
+
def wget_webpage(url,dir_prefix)
|
22
|
+
domain=URI.parse(url).host
|
23
|
+
system("wget --no-parent --timestamping --convert-links --page-requisites --no-directories --no-host-directories -erobots=off --quiet --directory-prefix=#{dir_prefix} #{url}")
|
24
|
+
end
|
data/sqweeze.gemspec
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{sqweeze}
|
5
|
+
s.version = "0.0.2"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Andrea Fiore"]
|
9
|
+
s.date = %q{2010-05-01}
|
10
|
+
s.default_executable = %q{sqweeze}
|
11
|
+
s.description = %q{A command line web-asset optimisation tool}
|
12
|
+
s.email = %q{and@inventati.org}
|
13
|
+
s.executables = ["sqweeze"]
|
14
|
+
s.extra_rdoc_files = ["README.rdoc", "bin/sqweeze", "lib/compilers/assetLinker.rb", "lib/compilers/cssDomCompiler.rb", "lib/compilers/jsDomCompiler.rb", "lib/compressor.rb", "lib/compressors/cssCompressor.rb", "lib/compressors/gifCompressor.rb", "lib/compressors/jpegCompressor.rb", "lib/compressors/jsCompressor.rb", "lib/compressors/pngCompressor.rb", "lib/confManager.rb", "lib/domCompiler.rb", "lib/sqweezeUtils.rb"]
|
15
|
+
s.files = ["Manifest", "README.rdoc", "Rakefile", "bin/sqweeze", "lib/compilers/assetLinker.rb", "lib/compilers/cssDomCompiler.rb", "lib/compilers/jsDomCompiler.rb", "lib/compressor.rb", "lib/compressors/cssCompressor.rb", "lib/compressors/gifCompressor.rb", "lib/compressors/jpegCompressor.rb", "lib/compressors/jsCompressor.rb", "lib/compressors/pngCompressor.rb", "lib/confManager.rb", "lib/domCompiler.rb", "lib/sqweezeUtils.rb", "spec/confManager_spec.rb", "spec/domCompiler_spec.rb", "spec/sqw_spec_helper.rb", "sqweeze.gemspec"]
|
16
|
+
s.homepage = %q{http://github.com/premasagar/sqwidget-builder}
|
17
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Sqweeze", "--main", "README.rdoc"]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.rubyforge_project = %q{sqweeze}
|
20
|
+
s.rubygems_version = %q{1.3.5}
|
21
|
+
s.summary = %q{A command line web-asset optimisation tool}
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 3
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
28
|
+
s.add_runtime_dependency(%q<hpricot>, [">= 0", "= 0.8.2"])
|
29
|
+
s.add_runtime_dependency(%q<closure-compiler>, [">= 0.2.2"])
|
30
|
+
s.add_runtime_dependency(%q<yui-compressor>, [">= 0", "= 0.9.1"])
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<hpricot>, [">= 0", "= 0.8.2"])
|
33
|
+
s.add_dependency(%q<closure-compiler>, [">= 0.2.2"])
|
34
|
+
s.add_dependency(%q<yui-compressor>, [">= 0", "= 0.9.1"])
|
35
|
+
end
|
36
|
+
else
|
37
|
+
s.add_dependency(%q<hpricot>, [">= 0", "= 0.8.2"])
|
38
|
+
s.add_dependency(%q<closure-compiler>, [">= 0.2.2"])
|
39
|
+
s.add_dependency(%q<yui-compressor>, [">= 0", "= 0.9.1"])
|
40
|
+
end
|
41
|
+
end
|
metadata
ADDED
@@ -0,0 +1,151 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: sqweeze
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Andrea Fiore
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-05-01 00:00:00 +01:00
|
18
|
+
default_executable: sqweeze
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: hpricot
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 0
|
29
|
+
version: "0"
|
30
|
+
- - "="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
segments:
|
33
|
+
- 0
|
34
|
+
- 8
|
35
|
+
- 2
|
36
|
+
version: 0.8.2
|
37
|
+
type: :runtime
|
38
|
+
version_requirements: *id001
|
39
|
+
- !ruby/object:Gem::Dependency
|
40
|
+
name: closure-compiler
|
41
|
+
prerelease: false
|
42
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
segments:
|
47
|
+
- 0
|
48
|
+
- 2
|
49
|
+
- 2
|
50
|
+
version: 0.2.2
|
51
|
+
type: :runtime
|
52
|
+
version_requirements: *id002
|
53
|
+
- !ruby/object:Gem::Dependency
|
54
|
+
name: yui-compressor
|
55
|
+
prerelease: false
|
56
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
57
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
segments:
|
61
|
+
- 0
|
62
|
+
version: "0"
|
63
|
+
- - "="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
segments:
|
66
|
+
- 0
|
67
|
+
- 9
|
68
|
+
- 1
|
69
|
+
version: 0.9.1
|
70
|
+
type: :runtime
|
71
|
+
version_requirements: *id003
|
72
|
+
description: A command line web-asset optimisation tool
|
73
|
+
email: and@inventati.org
|
74
|
+
executables:
|
75
|
+
- sqweeze
|
76
|
+
extensions: []
|
77
|
+
|
78
|
+
extra_rdoc_files:
|
79
|
+
- README.rdoc
|
80
|
+
- bin/sqweeze
|
81
|
+
- lib/compilers/assetLinker.rb
|
82
|
+
- lib/compilers/cssDomCompiler.rb
|
83
|
+
- lib/compilers/jsDomCompiler.rb
|
84
|
+
- lib/compressor.rb
|
85
|
+
- lib/compressors/cssCompressor.rb
|
86
|
+
- lib/compressors/gifCompressor.rb
|
87
|
+
- lib/compressors/jpegCompressor.rb
|
88
|
+
- lib/compressors/jsCompressor.rb
|
89
|
+
- lib/compressors/pngCompressor.rb
|
90
|
+
- lib/confManager.rb
|
91
|
+
- lib/domCompiler.rb
|
92
|
+
- lib/sqweezeUtils.rb
|
93
|
+
files:
|
94
|
+
- Manifest
|
95
|
+
- README.rdoc
|
96
|
+
- Rakefile
|
97
|
+
- bin/sqweeze
|
98
|
+
- lib/compilers/assetLinker.rb
|
99
|
+
- lib/compilers/cssDomCompiler.rb
|
100
|
+
- lib/compilers/jsDomCompiler.rb
|
101
|
+
- lib/compressor.rb
|
102
|
+
- lib/compressors/cssCompressor.rb
|
103
|
+
- lib/compressors/gifCompressor.rb
|
104
|
+
- lib/compressors/jpegCompressor.rb
|
105
|
+
- lib/compressors/jsCompressor.rb
|
106
|
+
- lib/compressors/pngCompressor.rb
|
107
|
+
- lib/confManager.rb
|
108
|
+
- lib/domCompiler.rb
|
109
|
+
- lib/sqweezeUtils.rb
|
110
|
+
- spec/confManager_spec.rb
|
111
|
+
- spec/domCompiler_spec.rb
|
112
|
+
- spec/sqw_spec_helper.rb
|
113
|
+
- sqweeze.gemspec
|
114
|
+
has_rdoc: true
|
115
|
+
homepage: http://github.com/premasagar/sqwidget-builder
|
116
|
+
licenses: []
|
117
|
+
|
118
|
+
post_install_message:
|
119
|
+
rdoc_options:
|
120
|
+
- --line-numbers
|
121
|
+
- --inline-source
|
122
|
+
- --title
|
123
|
+
- Sqweeze
|
124
|
+
- --main
|
125
|
+
- README.rdoc
|
126
|
+
require_paths:
|
127
|
+
- lib
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
129
|
+
requirements:
|
130
|
+
- - ">="
|
131
|
+
- !ruby/object:Gem::Version
|
132
|
+
segments:
|
133
|
+
- 0
|
134
|
+
version: "0"
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
segments:
|
140
|
+
- 1
|
141
|
+
- 2
|
142
|
+
version: "1.2"
|
143
|
+
requirements: []
|
144
|
+
|
145
|
+
rubyforge_project: sqweeze
|
146
|
+
rubygems_version: 1.3.6
|
147
|
+
signing_key:
|
148
|
+
specification_version: 3
|
149
|
+
summary: A command line web-asset optimisation tool
|
150
|
+
test_files: []
|
151
|
+
|