Class: Jammit::Compressor
- Inherits:
- Object
- Defined in:
- lib/jammit/compressor.rb
Overview
Uses the YUI Compressor or Closure Compiler to compress JavaScript. Always uses YUI to compress CSS (Which means that Java must be installed.) Also knows how to create a concatenated JST file. If “embed_assets” is turned on, creates “mhtml” and “datauri” versions of all stylesheets, with all enabled assets inlined into the css.
Constant Summary
- EMBED_MIME_TYPES = Mapping from extension to mime-type of all embeddable assets.
{ '.png' => 'image/png', '.jpg' => 'image/jpeg', '.jpeg' => 'image/jpeg', '.gif' => 'image/gif', '.tif' => 'image/tiff', '.tiff' => 'image/tiff', '.ttf' => 'font/truetype', '.otf' => 'font/opentype' }
- EMBED_EXTS = Font extensions for which we allow embedding:.
EMBED_MIME_TYPES.keys
- EMBED_FONTS =
['.ttf', '.otf']
- MAX_IMAGE_SIZE = Maximum size for embeddable images (an IE8 limitation).
32.kilobytes
- EMBED_DETECTOR = CSS asset-embedding regexes for URL rewriting.
/url\(['"]?([^\s)]+\.[a-z]+)(\?\d+)?['"]?\)/
- EMBEDDABLE =
/[\A\/]embed\//
- EMBED_REPLACER =
/url\(__EMBED__([^\s)]+)(\?\d+)?\)/
- MHTML_START = MHTML file constants.
"/*\r\nContent-Type: multipart/related; boundary=\"JAMMIT_MHTML_SEPARATOR\"\r\n\r\n"
- MHTML_SEPARATOR =
"--JAMMIT_MHTML_SEPARATOR\r\n"
- MHTML_END =
"*/\r\n"
- JST_START = JST file constants.
"(function(){"
- JST_END =
"})();"
- COMPRESSORS =
{ :yui => YUI::JavaScriptCompressor, :closure => Closure::Compiler }
- DEFAULT_OPTIONS =
{ :yui => {:munge => true}, :closure => {} }
Method Summary
- - (Object) compile_jst(paths) Compiles a single JST file by writing out a javascript that adds template properties to a top-level template namespace object.
- - (Object) compress_css(paths, variant = nil, asset_url = nil) Concatenate and compress a list of CSS stylesheets.
- - (Object) compress_js(paths) Concatenate together a list of JavaScript paths, and pass them through the YUI Compressor (with munging enabled).
- - (Compressor) initialize Creating a compressor initializes the internal YUI Compressor from the “yui-compressor” gem, or the internal Closure Compiler from the “closure-compiler” gem.
- - (Object) absolute_path(asset_pathname, css_pathname) private Get the site-absolute public path for an asset file path that may or may not be relative, given the path of the stylesheet that contains it.
- - (Object) concatenate(paths) private Concatenate together a list of asset files.
- - (Object) concatenate_and_tag_assets(paths, variant = nil) private In order to support embedded assets from relative paths, we need to expand the paths before contatenating the CSS together and losing the location of the original stylesheet path.
- - (Object) embeddable?(asset_path, variant) private An asset is valid for embedding if it exists, is less than 32K, and is stored somewhere inside of a folder named “embed”.
- - (Object) encoded_contents(asset_path) private Return the Base64-encoded contents of an asset on a single line.
- - (Object) mime_type(asset_path) private Grab the mime-type of an asset, by filename.
- - (Object) relative_path(absolute_path) private CSS assets that are referenced by relative paths, and are not being embedded, must be rewritten relative to the newly-merged stylesheet path.
- - (Object) rewrite_asset_path(asset_path, css_path, variant) private Return a rewritten asset URL for a new stylesheet — the asset should be tagged for embedding if embeddable, and referenced at the correct level if relative.
- - (Object) with_data_uris(css) private Re-write all enabled asset URLs in a stylesheet with their corresponding Data-URI Base-64 encoded asset contents.
- - (Object) with_mhtml(css, asset_url) private Re-write all enabled asset URLs in a stylesheet with the MHTML equivalent.
Constructor Details
- (Compressor) initialize
Creating a compressor initializes the internal YUI Compressor from the “yui-compressor” gem, or the internal Closure Compiler from the “closure-compiler” gem.
56 57 58 59 60 61 |
# File 'lib/jammit/compressor.rb', line 56 def initialize @css_compressor = YUI::CssCompressor.new(Jammit. || {}) flavor = Jammit.javascript_compressor || Jammit::DEFAULT_COMPRESSOR = DEFAULT_OPTIONS[flavor].merge(Jammit. || {}) @js_compressor = COMPRESSORS[flavor].new() end |
Method Details
- (Object) compile_jst(paths)
Compiles a single JST file by writing out a javascript that adds template properties to a top-level template namespace object. Adds a JST-compilation function to the top of the package, unless you’ve specified your own preferred function, or turned it off. JST templates are named with the basename of their file.
93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/jammit/compressor.rb', line 93 def compile_jst(paths) namespace = Jammit.template_namespace compiled = paths.grep(JST_EXT).map do |path| template_name = File.basename(path, File.extname(path)) contents = File.read(path).gsub(/\n/, '').gsub("'", '\\\\\'') "#{namespace}.#{template_name} = #{Jammit.template_function}('#{contents}');" end compiler = Jammit.include_jst_script ? File.read(DEFAULT_JST_SCRIPT) : ''; setup_namespace = "#{namespace} = #{namespace} || {};" [JST_START, setup_namespace, compiler, compiled, JST_END].flatten.join("\n") end |
- (Object) compress_css(paths, variant = nil, asset_url = nil)
Concatenate and compress a list of CSS stylesheets. When compressing a :datauri or :mhtml variant, post-processes the result to embed referenced assets.
77 78 79 80 81 82 83 84 85 86 |
# File 'lib/jammit/compressor.rb', line 77 def compress_css(paths, variant=nil, asset_url=nil) css = concatenate_and_tag_assets(paths, variant) css = @css_compressor.compress(css) if Jammit.compress_assets case variant when nil then return css when :datauri then return with_data_uris(css) when :mhtml then return with_mhtml(css, asset_url) else raise PackageNotFound, "\"#{variant}\" is not a valid stylesheet variant" end end |
- (Object) compress_js(paths)
Concatenate together a list of JavaScript paths, and pass them through the YUI Compressor (with munging enabled). JST can optionally be included.
65 66 67 68 69 70 71 72 |
# File 'lib/jammit/compressor.rb', line 65 def compress_js(paths) if (jst_paths = paths.grep(JST_EXT)).empty? js = concatenate(paths) else js = concatenate(paths - jst_paths) + compile_jst(jst_paths) end Jammit.compress_assets ? @js_compressor.compress(js) : js end |
- (Object) absolute_path(asset_pathname, css_pathname) (private)
Get the site-absolute public path for an asset file path that may or may not be relative, given the path of the stylesheet that contains it.
158 159 160 161 162 |
# File 'lib/jammit/compressor.rb', line 158 def absolute_path(asset_pathname, css_pathname) (asset_pathname.absolute? ? Pathname.new(File.join(PUBLIC_ROOT, asset_pathname)) : css_pathname.dirname + asset_pathname).cleanpath end |
- (Object) concatenate(paths) (private)
Concatenate together a list of asset files.
195 196 197 |
# File 'lib/jammit/compressor.rb', line 195 def concatenate(paths) [paths].flatten.map {|p| File.read(p) }.join("\n") end |
- (Object) concatenate_and_tag_assets(paths, variant = nil) (private)
In order to support embedded assets from relative paths, we need to expand the paths before contatenating the CSS together and losing the location of the original stylesheet path. Validate the assets while we’re at it.
112 113 114 115 116 117 118 119 120 121 |
# File 'lib/jammit/compressor.rb', line 112 def concatenate_and_tag_assets(paths, variant=nil) stylesheets = [paths].flatten.map do |css_path| File.read(css_path).gsub(EMBED_DETECTOR) do |url| ipath, cpath = Pathname.new($1), Pathname.new(File.(css_path)) is_url = URI.parse($1).absolute? is_url ? url : "url(#{rewrite_asset_path(ipath, cpath, variant)})" end end stylesheets.join("\n") end |
- (Object) embeddable?(asset_path, variant) (private)
An asset is valid for embedding if it exists, is less than 32K, and is stored somewhere inside of a folder named “embed”. IE does not support Data-URIs larger than 32K, and you probably shouldn’t be embedding assets that large in any case.
174 175 176 177 178 179 180 181 182 |
# File 'lib/jammit/compressor.rb', line 174 def (asset_path, variant) font = EMBED_FONTS.include?(asset_path.extname) return false unless variant return false unless asset_path.to_s.match(EMBEDDABLE) && asset_path.exist? return false unless EMBED_EXTS.include?(asset_path.extname) return false unless font || asset_path.size < MAX_IMAGE_SIZE return false if font && variant == :mhtml true end |
- (Object) encoded_contents(asset_path) (private)
Return the Base64-encoded contents of an asset on a single line.
185 186 187 |
# File 'lib/jammit/compressor.rb', line 185 def encoded_contents(asset_path) Base64.encode64(File.read(asset_path)).gsub(/\n/, '') end |
- (Object) mime_type(asset_path) (private)
Grab the mime-type of an asset, by filename.
190 191 192 |
# File 'lib/jammit/compressor.rb', line 190 def mime_type(asset_path) EMBED_MIME_TYPES[File.extname(asset_path)] end |
- (Object) relative_path(absolute_path) (private)
CSS assets that are referenced by relative paths, and are not being embedded, must be rewritten relative to the newly-merged stylesheet path.
166 167 168 |
# File 'lib/jammit/compressor.rb', line 166 def relative_path(absolute_path) File.join('../', absolute_path.sub(PUBLIC_ROOT, '')) end |
- (Object) rewrite_asset_path(asset_path, css_path, variant) (private)
Return a rewritten asset URL for a new stylesheet — the asset should be tagged for embedding if embeddable, and referenced at the correct level if relative.
150 151 152 153 154 |
# File 'lib/jammit/compressor.rb', line 150 def rewrite_asset_path(asset_path, css_path, variant) public_path = absolute_path(asset_path, css_path) return "__EMBED__#{public_path}" if (public_path, variant) asset_path.absolute? ? asset_path.to_s : relative_path(public_path) end |
- (Object) with_data_uris(css) (private)
Re-write all enabled asset URLs in a stylesheet with their corresponding Data-URI Base-64 encoded asset contents.
125 126 127 128 129 |
# File 'lib/jammit/compressor.rb', line 125 def with_data_uris(css) css.gsub(EMBED_REPLACER) do |url| "url(\"data:#{mime_type($1)};charset=utf-8;base64,#{encoded_contents($1)}\")" end end |
- (Object) with_mhtml(css, asset_url) (private)
Re-write all enabled asset URLs in a stylesheet with the MHTML equivalent. The newlines (“\r\n”) in the following method are critical. Without them your MHTML will look identical, but won’t work.
134 135 136 137 138 139 140 141 142 143 144 145 |
# File 'lib/jammit/compressor.rb', line 134 def with_mhtml(css, asset_url) paths, index = {}, 0 css = css.gsub(EMBED_REPLACER) do |url| i = paths[$1] ||= "#{index += 1}-#{File.basename($1)}" "url(mhtml:#{asset_url}!#{i})" end mhtml = paths.map do |path, identifier| mime, contents = mime_type(path), encoded_contents(path) [MHTML_SEPARATOR, "Content-Location: #{identifier}\r\n", "Content-Type: #{mime}\r\n", "Content-Transfer-Encoding: base64\r\n\r\n", contents, "\r\n"] end [MHTML_START, mhtml, MHTML_END, css].flatten.join('') end |