ee_jammit 0.6.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/LICENSE +22 -0
- data/README +24 -0
- data/bin/jammit +7 -0
- data/jammit.gemspec +34 -0
- data/lib/jammit.rb +236 -0
- data/lib/jammit/command_line.rb +84 -0
- data/lib/jammit/compressor.rb +266 -0
- data/lib/jammit/controller.rb +97 -0
- data/lib/jammit/cssmin_compressor.rb +10 -0
- data/lib/jammit/dependencies.rb +61 -0
- data/lib/jammit/helper.rb +88 -0
- data/lib/jammit/jsmin_compressor.rb +10 -0
- data/lib/jammit/jst.js +1 -0
- data/lib/jammit/middleware.rb +115 -0
- data/lib/jammit/packager.rb +179 -0
- data/lib/jammit/railtie.rb +14 -0
- data/lib/jammit/routes.rb +23 -0
- data/lib/jammit/sass_compressor.rb +17 -0
- data/lib/jammit/uglifier.rb +3 -0
- data/rails/routes.rb +10 -0
- metadata +104 -0
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'action_controller'
|
2
|
+
|
3
|
+
module Jammit
|
4
|
+
|
5
|
+
# The JammitController is added to your Rails application when the Gem is
|
6
|
+
# loaded. It takes responsibility for /assets, and dynamically packages any
|
7
|
+
# missing or uncached asset packages.
|
8
|
+
class Controller < ActionController::Base
|
9
|
+
|
10
|
+
VALID_FORMATS = [:css, :js]
|
11
|
+
|
12
|
+
SUFFIX_STRIPPER = /-(datauri|mhtml)\Z/
|
13
|
+
|
14
|
+
NOT_FOUND_PATH = "#{Jammit.public_root}/404.html"
|
15
|
+
|
16
|
+
# The "package" action receives all requests for asset packages that haven't
|
17
|
+
# yet been cached. The package will be built, cached, and gzipped.
|
18
|
+
def package
|
19
|
+
parse_request
|
20
|
+
template_ext = Jammit.template_extension.to_sym
|
21
|
+
case @extension
|
22
|
+
when :js
|
23
|
+
render :js => (@contents = Jammit.packager.pack_javascripts(@package))
|
24
|
+
when template_ext
|
25
|
+
render :js => (@contents = Jammit.packager.pack_templates(@package))
|
26
|
+
when :css
|
27
|
+
render :text => generate_stylesheets, :content_type => 'text/css'
|
28
|
+
end
|
29
|
+
cache_package if perform_caching && (@extension != template_ext)
|
30
|
+
rescue Jammit::PackageNotFound
|
31
|
+
package_not_found
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Tells the Jammit::Packager to cache and gzip an asset package. We can't
|
38
|
+
# just use the built-in "cache_page" because we need to ensure that
|
39
|
+
# the timestamp that ends up in the MHTML is also on the cached file.
|
40
|
+
def cache_package
|
41
|
+
dir = File.join(page_cache_directory, Jammit.package_path)
|
42
|
+
Jammit.packager.cache(@package, @extension, @contents, dir, @variant, @mtime)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Generate the complete, timestamped, MHTML url -- if we're rendering a
|
46
|
+
# dynamic MHTML package, we'll need to put one URL in the response, and a
|
47
|
+
# different one into the cached package.
|
48
|
+
def prefix_url(path)
|
49
|
+
host = request.port == 80 ? request.host : request.host_with_port
|
50
|
+
"#{request.protocol}#{host}#{path}"
|
51
|
+
end
|
52
|
+
|
53
|
+
# If we're generating MHTML/CSS, return a stylesheet with the absolute
|
54
|
+
# request URL to the client, and cache a version with the timestamped cache
|
55
|
+
# URL swapped in.
|
56
|
+
def generate_stylesheets
|
57
|
+
return @contents = Jammit.packager.pack_stylesheets(@package, @variant) unless @variant == :mhtml
|
58
|
+
@mtime = Time.now
|
59
|
+
request_url = prefix_url(request.fullpath)
|
60
|
+
cached_url = prefix_url(Jammit.asset_url(@package, @extension, @variant, @mtime))
|
61
|
+
css = Jammit.packager.pack_stylesheets(@package, @variant, request_url)
|
62
|
+
@contents = css.gsub(request_url, cached_url) if perform_caching
|
63
|
+
css
|
64
|
+
end
|
65
|
+
|
66
|
+
# Extracts the package name, extension (:css, :js), and variant (:datauri,
|
67
|
+
# :mhtml) from the incoming URL.
|
68
|
+
def parse_request
|
69
|
+
pack = params[:package]
|
70
|
+
@extension = params[:extension].to_sym
|
71
|
+
raise PackageNotFound unless (VALID_FORMATS + [Jammit.template_extension.to_sym]).include?(@extension)
|
72
|
+
if Jammit.embed_assets
|
73
|
+
suffix_match = pack.match(SUFFIX_STRIPPER)
|
74
|
+
@variant = Jammit.embed_assets && suffix_match && suffix_match[1].to_sym
|
75
|
+
pack.sub!(SUFFIX_STRIPPER, '')
|
76
|
+
end
|
77
|
+
@package = pack.to_sym
|
78
|
+
end
|
79
|
+
|
80
|
+
# Render the 404 page, if one exists, for any packages that don't.
|
81
|
+
def package_not_found
|
82
|
+
return render(:file => NOT_FOUND_PATH, :status => 404) if File.exists?(NOT_FOUND_PATH)
|
83
|
+
render :text => "<h1>404: \"#{@package}\" asset package not found.</h1>", :status => 404
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
# Make the Jammit::Controller available to Rails as a top-level controller.
|
91
|
+
::JammitController = Jammit::Controller
|
92
|
+
|
93
|
+
if defined?(Rails) && Rails.env.development?
|
94
|
+
ActionController::Base.class_eval do
|
95
|
+
append_before_filter { Jammit.reload! }
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# Standard Library Dependencies:
|
2
|
+
require 'uri'
|
3
|
+
require 'erb'
|
4
|
+
require 'zlib'
|
5
|
+
require 'yaml'
|
6
|
+
require 'base64'
|
7
|
+
require 'pathname'
|
8
|
+
require 'fileutils'
|
9
|
+
|
10
|
+
# Try Uglifier.
|
11
|
+
begin
|
12
|
+
require 'uglifier'
|
13
|
+
require 'jammit/uglifier'
|
14
|
+
rescue LoadError
|
15
|
+
Jammit.javascript_compressors.delete :uglifier
|
16
|
+
end
|
17
|
+
|
18
|
+
# Try YUI
|
19
|
+
begin
|
20
|
+
require 'yui/compressor'
|
21
|
+
rescue LoadError
|
22
|
+
Jammit.javascript_compressors.delete :yui
|
23
|
+
Jammit.css_compressors.delete :yui
|
24
|
+
end
|
25
|
+
|
26
|
+
# Try Closure.
|
27
|
+
begin
|
28
|
+
require 'closure-compiler'
|
29
|
+
rescue LoadError
|
30
|
+
Jammit.javascript_compressors.delete :closure
|
31
|
+
end
|
32
|
+
|
33
|
+
# Try Sass
|
34
|
+
begin
|
35
|
+
require 'sass'
|
36
|
+
require 'jammit/sass_compressor'
|
37
|
+
rescue LoadError
|
38
|
+
Jammit.css_compressors.delete :sass
|
39
|
+
end
|
40
|
+
|
41
|
+
# Load initial configuration before the rest of Jammit.
|
42
|
+
Jammit.load_configuration(Jammit::DEFAULT_CONFIG_PATH, true) if defined?(Rails)
|
43
|
+
|
44
|
+
# Jammit Core:
|
45
|
+
require 'jsmin'
|
46
|
+
require 'cssmin'
|
47
|
+
require 'jammit/jsmin_compressor'
|
48
|
+
require 'jammit/cssmin_compressor'
|
49
|
+
require 'jammit/compressor'
|
50
|
+
require 'jammit/packager'
|
51
|
+
require 'jammit/helper'
|
52
|
+
require 'jammit/middleware'
|
53
|
+
|
54
|
+
# Jammit Rails Integration:
|
55
|
+
if defined?(Rails)
|
56
|
+
require 'jammit/controller'
|
57
|
+
require 'jammit/helper'
|
58
|
+
require 'jammit/railtie'
|
59
|
+
require 'jammit/routes'
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module Jammit
|
2
|
+
|
3
|
+
# The Jammit::Helper module, which is made available to every view, provides
|
4
|
+
# helpers for writing out HTML tags for asset packages. In development you
|
5
|
+
# get the ordered list of source files -- in any other environment, a link
|
6
|
+
# to the cached packages.
|
7
|
+
module Helper
|
8
|
+
|
9
|
+
DATA_URI_START = "<!--[if (!IE)|(gte IE 8)]><!-->" unless defined?(DATA_URI_START)
|
10
|
+
DATA_URI_END = "<!--<![endif]-->" unless defined?(DATA_URI_END)
|
11
|
+
MHTML_START = "<!--[if lte IE 7]>" unless defined?(MHTML_START)
|
12
|
+
MHTML_END = "<![endif]-->" unless defined?(MHTML_END)
|
13
|
+
|
14
|
+
# If embed_assets is turned on, writes out links to the Data-URI and MHTML
|
15
|
+
# versions of the stylesheet package, otherwise the package is regular
|
16
|
+
# compressed CSS, and in development the stylesheet URLs are passed verbatim.
|
17
|
+
def include_stylesheets(*packages)
|
18
|
+
options = packages.extract_options!
|
19
|
+
return html_safe(individual_stylesheets(packages, options)) unless should_package?
|
20
|
+
disabled = (options.delete(:embed_assets) == false) || (options.delete(:embed_images) == false)
|
21
|
+
return html_safe(packaged_stylesheets(packages, options)) if disabled || !Jammit.embed_assets
|
22
|
+
return html_safe(embedded_image_stylesheets(packages, options))
|
23
|
+
end
|
24
|
+
|
25
|
+
# Writes out the URL to the bundled and compressed javascript package,
|
26
|
+
# except in development, where it references the individual scripts.
|
27
|
+
def include_javascripts(*packages)
|
28
|
+
options = packages.extract_options!
|
29
|
+
html_safe packages.map {|pack|
|
30
|
+
should_package? ? Jammit.asset_url(pack, :js) : Jammit.packager.individual_urls(pack.to_sym, :js)
|
31
|
+
}.flatten.map {|pack|
|
32
|
+
javascript_include_tag pack, options
|
33
|
+
}.join("\n")
|
34
|
+
end
|
35
|
+
|
36
|
+
# Writes out the URL to the concatenated and compiled JST file -- we always
|
37
|
+
# have to pre-process it, even in development.
|
38
|
+
def include_templates(*packages)
|
39
|
+
raise DeprecationError, "Jammit 0.5+ no longer supports separate packages for templates.\nYou can include your JST alongside your JS, and use include_javascripts."
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def should_package?
|
46
|
+
Jammit.package_assets && !(Jammit.allow_debugging && params[:debug_assets])
|
47
|
+
end
|
48
|
+
|
49
|
+
def html_safe(string)
|
50
|
+
string.respond_to?(:html_safe) ? string.html_safe : string
|
51
|
+
end
|
52
|
+
|
53
|
+
# HTML tags, in order, for all of the individual stylesheets.
|
54
|
+
def individual_stylesheets(packages, options)
|
55
|
+
tags_with_options(packages, options) {|p| Jammit.packager.individual_urls(p.to_sym, :css) }
|
56
|
+
end
|
57
|
+
|
58
|
+
# HTML tags for the stylesheet packages.
|
59
|
+
def packaged_stylesheets(packages, options)
|
60
|
+
tags_with_options(packages, options) {|p| Jammit.asset_url(p, :css) }
|
61
|
+
end
|
62
|
+
|
63
|
+
# HTML tags for the 'datauri', and 'mhtml' versions of the packaged
|
64
|
+
# stylesheets, using conditional comments to load the correct variant.
|
65
|
+
def embedded_image_stylesheets(packages, options)
|
66
|
+
datauri_tags = tags_with_options(packages, options) {|p| Jammit.asset_url(p, :css, :datauri) }
|
67
|
+
ie_tags = Jammit.mhtml_enabled ?
|
68
|
+
tags_with_options(packages, options) {|p| Jammit.asset_url(p, :css, :mhtml) } :
|
69
|
+
packaged_stylesheets(packages, options)
|
70
|
+
[DATA_URI_START, datauri_tags, DATA_URI_END, MHTML_START, ie_tags, MHTML_END].join("\n")
|
71
|
+
end
|
72
|
+
|
73
|
+
# Generate the stylesheet tags for a batch of packages, with options, by
|
74
|
+
# yielding each package to a block.
|
75
|
+
def tags_with_options(packages, options)
|
76
|
+
packages.dup.map {|package|
|
77
|
+
yield package
|
78
|
+
}.flatten.map {|package|
|
79
|
+
stylesheet_link_tag package, options
|
80
|
+
}.join("\n")
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
# Include the Jammit asset helpers in all views, a-la ApplicationHelper.
|
88
|
+
::ActionView::Base.send(:include, Jammit::Helper) if defined?(::ActionView)
|
data/lib/jammit/jst.js
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
var template = function(str){var fn = new Function('obj', 'var __p=[],print=function(){__p.push.apply(__p,arguments);};with(obj||{}){__p.push(\''+str.replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/<%=([\s\S]+?)%>/g,function(match,code){return "',"+code.replace(/\\'/g, "'")+",'";}).replace(/<%([\s\S]+?)%>/g,function(match,code){return "');"+code.replace(/\\'/g, "'").replace(/[\r\n\t]/g,' ')+"__p.push('";}).replace(/\r/g,'\\r').replace(/\n/g,'\\n').replace(/\t/g,'\\t')+"');}return __p.join('');");return fn;};
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Jammit
|
2
|
+
|
3
|
+
# Rack Middle that allows Jammit to integrate with any Rack compatible web
|
4
|
+
# framework. It takes responsibility for /assets, and dynamically packages
|
5
|
+
# any missing or uncached asset packages.
|
6
|
+
|
7
|
+
class Middleware
|
8
|
+
|
9
|
+
VALID_FORMATS = [:css, :js]
|
10
|
+
|
11
|
+
SUFFIX_STRIPPER = /-(datauri|mhtml)\Z/
|
12
|
+
|
13
|
+
PUBLIC_ROOT = defined?(Jammit.public_root) ? Jammit.public_root : PUBLIC_ROOT
|
14
|
+
NOT_FOUND_PATH = "#{PUBLIC_ROOT}/404.html"
|
15
|
+
|
16
|
+
def initialize(app)
|
17
|
+
@app = app
|
18
|
+
end
|
19
|
+
|
20
|
+
def call(env)
|
21
|
+
dup._call(env)
|
22
|
+
end
|
23
|
+
|
24
|
+
def _call(env)
|
25
|
+
if matches = %r(^/#{Jammit.package_path}/(.*)\.(.*)).match(env['PATH_INFO'])
|
26
|
+
package(matches[1].to_s, matches[2] || "none")
|
27
|
+
else
|
28
|
+
@app.call(env)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
# The "package" action receives all requests for asset packages that haven't
|
35
|
+
# yet been cached. The package will be built, cached, and gzipped.
|
36
|
+
def package(package, extension)
|
37
|
+
parse_request(package, extension)
|
38
|
+
template_ext = Jammit.template_extension.to_sym
|
39
|
+
result = []
|
40
|
+
|
41
|
+
headers = {
|
42
|
+
'Cache-Control' => "public, max-age=#{(60*60*24*365.25*10).to_i}",
|
43
|
+
'Expires' => (Time.now + 60*60*24*365.25*10).httpdate
|
44
|
+
}
|
45
|
+
|
46
|
+
case @extension
|
47
|
+
when :js
|
48
|
+
@contents = Jammit.packager.pack_javascripts(@package)
|
49
|
+
@contents = @contents.to_js if @contents.respond_to?(:to_js)
|
50
|
+
result = [
|
51
|
+
200,
|
52
|
+
headers.merge({
|
53
|
+
'Content-Type' => Rack::Mime.mime_type(".js")
|
54
|
+
}),
|
55
|
+
[@contents]
|
56
|
+
]
|
57
|
+
when template_ext
|
58
|
+
@contents = Jammit.packager.pack_templates(@package)
|
59
|
+
@contents = @contents.to_js if @contents.respond_to?(:to_js)
|
60
|
+
[
|
61
|
+
200,
|
62
|
+
headers.merge({
|
63
|
+
'Content-Type' => Rack::Mime.mime_type(".js")
|
64
|
+
}),
|
65
|
+
[@contents]
|
66
|
+
]
|
67
|
+
when :css
|
68
|
+
[
|
69
|
+
200,
|
70
|
+
headers.merge({
|
71
|
+
'Content-Type' => Rack::Mime.mime_type(".css")
|
72
|
+
}),
|
73
|
+
[generate_stylesheets]
|
74
|
+
]
|
75
|
+
end
|
76
|
+
rescue Jammit::PackageNotFound
|
77
|
+
[404, {'Content-Type' => 'text/plain'}, ['Not found']]
|
78
|
+
end
|
79
|
+
|
80
|
+
# Generate the complete, timestamped, MHTML url -- if we're rendering a
|
81
|
+
# dynamic MHTML package, we'll need to put one URL in the response, and a
|
82
|
+
# different one into the cached package.
|
83
|
+
def prefix_url(path)
|
84
|
+
host = request.port == 80 ? request.host : request.host_with_port
|
85
|
+
"#{request.protocol}#{host}#{path}"
|
86
|
+
end
|
87
|
+
|
88
|
+
# If we're generating MHTML/CSS, return a stylesheet with the absolute
|
89
|
+
# request URL to the client, and cache a version with the timestamped cache
|
90
|
+
# URL swapped in.
|
91
|
+
def generate_stylesheets
|
92
|
+
return @contents = Jammit.packager.pack_stylesheets(@package, @variant) unless @variant == :mhtml
|
93
|
+
@mtime = Time.now
|
94
|
+
request_url = prefix_url(request.fullpath)
|
95
|
+
cached_url = prefix_url(Jammit.asset_url(@package, @extension, @variant, @mtime))
|
96
|
+
css = Jammit.packager.pack_stylesheets(@package, @variant, request_url)
|
97
|
+
# @contents = css.gsub(request_url, cached_url) if perform_caching
|
98
|
+
css
|
99
|
+
end
|
100
|
+
|
101
|
+
# Extracts the package name, extension (:css, :js), and variant (:datauri,
|
102
|
+
# :mhtml) from the incoming URL.
|
103
|
+
def parse_request(pack, extension)
|
104
|
+
@extension = extension.to_sym
|
105
|
+
raise PackageNotFound unless (VALID_FORMATS + [Jammit.template_extension.to_sym]).include?(@extension)
|
106
|
+
if Jammit.embed_assets
|
107
|
+
suffix_match = pack.match(SUFFIX_STRIPPER)
|
108
|
+
@variant = Jammit.embed_assets && suffix_match && suffix_match[1].to_sym
|
109
|
+
pack.sub!(SUFFIX_STRIPPER, '')
|
110
|
+
end
|
111
|
+
@package = pack.to_sym
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
module Jammit
|
2
|
+
|
3
|
+
# The Jammit::Packager resolves the configuration file into lists of real
|
4
|
+
# assets that get merged into individual asset packages. Given the compiled
|
5
|
+
# contents of an asset package, the Packager knows how to cache that package
|
6
|
+
# with the correct timestamps.
|
7
|
+
class Packager
|
8
|
+
|
9
|
+
# Set force to false to allow packages to only be rebuilt when their source
|
10
|
+
# files have changed since the last time their package was built.
|
11
|
+
attr_accessor :force, :package_names
|
12
|
+
|
13
|
+
# Creating a new Packager will rebuild the list of assets from the
|
14
|
+
# Jammit.configuration. When assets.yml is being changed on the fly,
|
15
|
+
# create a new Packager.
|
16
|
+
def initialize
|
17
|
+
@force = false
|
18
|
+
@package_names = nil
|
19
|
+
@config = {
|
20
|
+
:css => (Jammit.configuration[:stylesheets] || {}),
|
21
|
+
:js => (Jammit.configuration[:javascripts] || {})
|
22
|
+
}
|
23
|
+
@packages = {
|
24
|
+
:css => create_packages(@config[:css]),
|
25
|
+
:js => create_packages(@config[:js])
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
# Ask the packager to precache all defined assets, along with their gzip'd
|
30
|
+
# versions. In order to prebuild the MHTML stylesheets, we need to know the
|
31
|
+
# base_url, because IE only supports MHTML with absolute references.
|
32
|
+
# Unless forced, will only rebuild assets whose source files have been
|
33
|
+
# changed since their last package build.
|
34
|
+
def precache_all(output_dir=nil, base_url=nil)
|
35
|
+
output_dir ||= File.join(Jammit.public_root, Jammit.package_path)
|
36
|
+
cacheable(:js, output_dir).each {|p| cache(p, 'js', pack_javascripts(p), output_dir) }
|
37
|
+
cacheable(:css, output_dir).each do |p|
|
38
|
+
cache(p, 'css', pack_stylesheets(p), output_dir)
|
39
|
+
if Jammit.embed_assets
|
40
|
+
cache(p, 'css', pack_stylesheets(p, :datauri), output_dir, :datauri)
|
41
|
+
if Jammit.mhtml_enabled
|
42
|
+
raise MissingConfiguration, "A --base-url option is required in order to generate MHTML." unless base_url
|
43
|
+
mtime = latest_mtime package_for(p, :css)[:paths]
|
44
|
+
asset_url = "#{base_url}#{Jammit.asset_url(p, :css, :mhtml, mtime)}"
|
45
|
+
cache(p, 'css', pack_stylesheets(p, :mhtml, asset_url), output_dir, :mhtml, mtime)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Caches a single prebuilt asset package and gzips it at the highest
|
52
|
+
# compression level. Ensures that the modification time of both both
|
53
|
+
# variants is identical, for web server caching modules, as well as MHTML.
|
54
|
+
def cache(package, extension, contents, output_dir, suffix=nil, mtime=nil)
|
55
|
+
FileUtils.mkdir_p(output_dir) unless File.exists?(output_dir)
|
56
|
+
raise OutputNotWritable, "Jammit doesn't have permission to write to \"#{output_dir}\"" unless File.writable?(output_dir)
|
57
|
+
mtime ||= latest_mtime package_for(package, extension.to_sym)[:paths]
|
58
|
+
files = []
|
59
|
+
files << file_name = File.join(output_dir, Jammit.filename(package, extension, suffix))
|
60
|
+
File.open(file_name, 'wb+') {|f| f.write(contents) }
|
61
|
+
if Jammit.gzip_assets
|
62
|
+
files << zip_name = "#{file_name}.gz"
|
63
|
+
Zlib::GzipWriter.open(zip_name, Zlib::BEST_COMPRESSION) {|f| f.write(contents) }
|
64
|
+
end
|
65
|
+
File.utime(mtime, mtime, *files)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Get the list of individual assets for a package.
|
69
|
+
def individual_urls(package, extension)
|
70
|
+
package_for(package, extension)[:urls]
|
71
|
+
end
|
72
|
+
|
73
|
+
def compressor
|
74
|
+
@compressor ||= Compressor.new
|
75
|
+
end
|
76
|
+
|
77
|
+
# Return the compressed contents of a stylesheet package.
|
78
|
+
def pack_stylesheets(package, variant=nil, asset_url=nil)
|
79
|
+
compressor.compress_css(package_for(package, :css)[:paths], variant, asset_url)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Return the compressed contents of a javascript package.
|
83
|
+
def pack_javascripts(package)
|
84
|
+
compressor.compress_js(package_for(package, :js)[:paths])
|
85
|
+
end
|
86
|
+
|
87
|
+
# Return the compiled contents of a JST package.
|
88
|
+
def pack_templates(package)
|
89
|
+
compressor.compile_jst(package_for(package, :js)[:paths])
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# Look up a package asset list by name, raising an exception if the
|
96
|
+
# package has gone missing.
|
97
|
+
def package_for(package, extension)
|
98
|
+
pack = @packages[extension] && @packages[extension][package]
|
99
|
+
pack || not_found(package, extension)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Absolute globs are absolute -- relative globs are relative to ASSET_ROOT.
|
103
|
+
# Print a warning if no files were found that match the glob.
|
104
|
+
def glob_files(glob)
|
105
|
+
absolute = Pathname.new(glob).absolute?
|
106
|
+
paths = Dir[absolute ? glob : File.join(ASSET_ROOT, glob)].sort
|
107
|
+
Jammit.warn("No assets match '#{glob}'") if paths.empty?
|
108
|
+
paths
|
109
|
+
end
|
110
|
+
|
111
|
+
# In Rails, the difference between a path and an asset URL is "public".
|
112
|
+
def path_to_url
|
113
|
+
@path_to_url ||= /\A#{Regexp.escape(ASSET_ROOT)}(\/?#{Regexp.escape(Jammit.public_root.sub(ASSET_ROOT, ''))})?/
|
114
|
+
end
|
115
|
+
|
116
|
+
# Get the latest mtime of a list of files (plus the config path).
|
117
|
+
def latest_mtime(paths)
|
118
|
+
paths += [Jammit.config_path]
|
119
|
+
paths.map {|p| File.mtime(p) }.max || Time.now
|
120
|
+
end
|
121
|
+
|
122
|
+
# Return a list of all of the packages that should be cached. If "force" is
|
123
|
+
# true, this is all of them -- otherwise only the packages that are missing
|
124
|
+
# or whose source files have changed since the last package build.
|
125
|
+
def cacheable(extension, output_dir)
|
126
|
+
names = @packages[extension].keys
|
127
|
+
names = names.select {|n| @package_names.include? n } if @package_names
|
128
|
+
config_mtime = File.mtime(Jammit.config_path)
|
129
|
+
return names if @force
|
130
|
+
return names.select do |name|
|
131
|
+
pack = package_for(name, extension)
|
132
|
+
cached = [Jammit.filename(name, extension)]
|
133
|
+
if extension == :css
|
134
|
+
cached.push Jammit.filename(name, extension, :datauri) if Jammit.embed_assets
|
135
|
+
cached.push Jammit.filename(name, extension, :mhtml) if Jammit.mhtml_enabled
|
136
|
+
end
|
137
|
+
cached.map! {|file| File.join(output_dir, file) }
|
138
|
+
if cached.any? {|file| !File.exists?(file) }
|
139
|
+
true
|
140
|
+
else
|
141
|
+
since = cached.map {|file| File.mtime(file) }.min
|
142
|
+
config_mtime > since || pack[:paths].any? {|src| File.mtime(src) > since }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Compiles the list of assets that goes into each package. Runs an
|
148
|
+
# ordered list of Dir.globs, taking the merged unique result.
|
149
|
+
# If there are JST files in this package we need to add an extra
|
150
|
+
# path for when package_assets is off (e.g. in a dev environment).
|
151
|
+
# This package (e.g. /assets/package-name.jst) will never exist as
|
152
|
+
# an actual file but will be dynamically generated by Jammit on
|
153
|
+
# every request.
|
154
|
+
def create_packages(config)
|
155
|
+
packages = {}
|
156
|
+
return packages if !config
|
157
|
+
config.each do |name, globs|
|
158
|
+
globs ||= []
|
159
|
+
packages[name] = {}
|
160
|
+
paths = globs.flatten.uniq.map {|glob| glob_files(glob) }.flatten.uniq
|
161
|
+
packages[name][:paths] = paths
|
162
|
+
if !paths.grep(Jammit.template_extension_matcher).empty?
|
163
|
+
packages[name][:urls] = paths.grep(JS_EXTENSION).map {|path| path.sub(path_to_url, '') }
|
164
|
+
packages[name][:urls] += [Jammit.asset_url(name, Jammit.template_extension)]
|
165
|
+
else
|
166
|
+
packages[name][:urls] = paths.map {|path| path.sub(path_to_url, '') }
|
167
|
+
end
|
168
|
+
end
|
169
|
+
packages
|
170
|
+
end
|
171
|
+
|
172
|
+
# Raise a PackageNotFound exception for missing packages...
|
173
|
+
def not_found(package, extension)
|
174
|
+
raise PackageNotFound, "assets.yml does not contain a \"#{package}\" #{extension.to_s.upcase} package"
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|