jammit-ebtd 0.6.6

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/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2009-2011 Jeremy Ashkenas, DocumentCloud
2
+
3
+ Permission is hereby granted, free of charge, to any person
4
+ obtaining a copy of this software and associated documentation
5
+ files (the "Software"), to deal in the Software without
6
+ restriction, including without limitation the rights to use,
7
+ copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the
9
+ Software is furnished to do so, subject to the following
10
+ conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
17
+ OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
19
+ HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
20
+ WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
+ OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,24 @@
1
+ ==
2
+ _ _ __ __ __ __ ___ _____
3
+ _ | |/_\ | \/ | \/ |_ _|_ _|
4
+ | || / _ \| |\/| | |\/| || | | |
5
+ \__/_/ \_\_| |_|_| |_|___| |_|
6
+
7
+
8
+ Jammit is an industrial strength asset packaging library for Rails,
9
+ providing both the CSS and JavaScript concatenation and compression
10
+ that you'd expect, as well as ahead-of-time gzipping, built-in JavaScript
11
+ template support, and optional Data-URI / MHTML image embedding.
12
+
13
+ Installation:
14
+ gem install jammit
15
+
16
+ For documentation, usage, and examples, see:
17
+ http://documentcloud.github.com/jammit/
18
+
19
+ To suggest a feature or report a bug:
20
+ http://github.com/documentcloud/jammit/issues/
21
+
22
+ For internal source docs, see:
23
+ http://documentcloud.github.com/jammit/doc/
24
+
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby -rrubygems
2
+ require 'pathname'
3
+
4
+ APP_ROOT = File.dirname(Pathname.new(__FILE__).realpath)
5
+ require File.join(APP_ROOT, '../lib/jammit/command_line.rb')
6
+
7
+ Jammit::CommandLine.new
@@ -0,0 +1,27 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'jammit-ebtd'
3
+ s.date = '2012-09-04'
4
+ s.version = '0.6.6'
5
+
6
+ s.homepage = "https://github.com/m16a1/jammit-ebtd"
7
+ s.summary = "Industrial Strength Asset Packaging for Rails"
8
+ s.description = <<-EOS
9
+ Jammit-Ebtd is a fork of the Jammit asset packager, modified for ebaytoday.ru
10
+ EOS
11
+
12
+ s.authors = ['Jeremy Ashkenas']
13
+ s.email = 'jeremy@documentcloud.org'
14
+
15
+ s.require_paths = ['lib']
16
+
17
+ s.extra_rdoc_files = ['README']
18
+ s.rdoc_options << '--title' << 'Jammit' <<
19
+ '--exclude' << 'test' <<
20
+ '--main' << 'README' <<
21
+ '--all'
22
+
23
+ s.add_dependency 'cssmin', ['>= 1.0.2']
24
+ s.add_dependency 'jsmin', ['>= 1.0.1']
25
+
26
+ s.files = Dir['lib/**/*', 'bin/*', 'jammit-ebtd.gemspec', 'LICENSE', 'README']
27
+ end
@@ -0,0 +1,241 @@
1
+ $LOAD_PATH.push File.expand_path(File.dirname(__FILE__))
2
+
3
+ # @Jammit@ is the central namespace for all Jammit classes, and provides access
4
+ # to all of the configuration options.
5
+ module Jammit
6
+
7
+ VERSION = "0.6.6"
8
+
9
+ ROOT = File.expand_path(File.dirname(__FILE__) + '/..')
10
+
11
+ ASSET_ROOT = File.expand_path((defined?(Rails) && Rails.root.to_s.length > 0) ? Rails.root : ENV['RAILS_ROOT'] || ".") unless defined?(ASSET_ROOT)
12
+
13
+ DEFAULT_PUBLIC_ROOT = (defined?(Rails) && Rails.public_path.to_s.length > 0) ? Rails.public_path : File.join(ASSET_ROOT, 'public') unless defined?(PUBLIC_ROOT)
14
+
15
+ DEFAULT_CONFIG_PATH = File.join(ASSET_ROOT, 'config', 'assets.yml')
16
+
17
+ DEFAULT_PACKAGE_PATH = "assets"
18
+
19
+ DEFAULT_JST_SCRIPT = File.join(ROOT, 'lib/jammit/jst.js')
20
+
21
+ DEFAULT_JST_COMPILER = "template"
22
+
23
+ DEFAULT_JST_NAMESPACE = "window.JST"
24
+
25
+ JAVASCRIPT_COMPRESSORS = [:jsmin, :yui, :closure, :uglifier]
26
+
27
+ DEFAULT_JAVASCRIPT_COMPRESSOR = :jsmin
28
+
29
+ CSS_COMPRESSORS = [:cssmin, :yui, :sass]
30
+
31
+ DEFAULT_CSS_COMPRESSOR = :cssmin
32
+
33
+ # Extension matchers for JavaScript and JST, which need to be disambiguated.
34
+ JS_EXTENSION = /\.js\Z/
35
+ DEFAULT_JST_EXTENSION = "jst"
36
+
37
+ # Jammit raises a @PackageNotFound@ exception when a non-existent package is
38
+ # requested by a browser -- rendering a 404.
39
+ class PackageNotFound < NameError; end
40
+
41
+ # Jammit raises a MissingConfiguration exception when you try to load the
42
+ # configuration of an assets.yml file that doesn't exist, or are missing
43
+ # a piece of required configuration.
44
+ class MissingConfiguration < NameError; end
45
+
46
+ # Jammit raises an OutputNotWritable exception if the output directory for
47
+ # cached packages is locked.
48
+ class OutputNotWritable < StandardError; end
49
+
50
+ # Jammit raises a DeprecationError if you try to use an outdated feature.
51
+ class DeprecationError < StandardError; end
52
+
53
+ class << self
54
+ attr_reader :configuration, :template_function, :template_namespace,
55
+ :embed_assets, :package_assets, :compress_assets, :gzip_assets,
56
+ :package_path, :mhtml_enabled, :include_jst_script, :config_path,
57
+ :javascript_compressor, :compressor_options, :css_compressor,
58
+ :css_compressor_options, :template_extension,
59
+ :template_extension_matcher, :allow_debugging,
60
+ :rewrite_relative_paths, :public_root
61
+ attr_accessor :javascript_compressors, :css_compressors
62
+ end
63
+
64
+ # The minimal required configuration.
65
+ @configuration = {}
66
+ @public_root = DEFAULT_PUBLIC_ROOT
67
+ @package_path = DEFAULT_PACKAGE_PATH
68
+
69
+ @javascript_compressors = JAVASCRIPT_COMPRESSORS
70
+ @css_compressors = CSS_COMPRESSORS
71
+
72
+ # Load the complete asset configuration from the specified @config_path@.
73
+ # If we're loading softly, don't let missing configuration error out.
74
+ def self.load_configuration(config_path, soft=false)
75
+ exists = config_path && File.exists?(config_path)
76
+ return false if soft && !exists
77
+ raise MissingConfiguration, "could not find the \"#{config_path}\" configuration file" unless exists
78
+ conf = YAML.load(ERB.new(File.read(config_path)).result)
79
+
80
+ # Optionally overwrite configuration based on the environment.
81
+ rails_env = (defined?(Rails) ? ::Rails.env : ENV['RAILS_ENV'] || "development")
82
+ conf.merge! conf.delete rails_env if conf.has_key? rails_env
83
+
84
+ @config_path = config_path
85
+ @configuration = symbolize_keys(conf)
86
+ @package_path = conf[:package_path] || DEFAULT_PACKAGE_PATH
87
+ @embed_assets = conf[:embed_assets] || conf[:embed_images]
88
+ @compress_assets = !(conf[:compress_assets] == false)
89
+ @rewrite_relative_paths = !(conf[:rewrite_relative_paths] == false)
90
+ @gzip_assets = !(conf[:gzip_assets] == false)
91
+ @allow_debugging = !(conf[:allow_debugging] == false)
92
+ @mhtml_enabled = @embed_assets && @embed_assets != "datauri"
93
+ @compressor_options = symbolize_keys(conf[:compressor_options] || {})
94
+ @css_compressor_options = symbolize_keys(conf[:css_compressor_options] || {})
95
+ set_javascript_compressor(conf[:javascript_compressor])
96
+ set_css_compressor(conf[:css_compressor])
97
+ set_package_assets(conf[:package_assets])
98
+ set_template_function(conf[:template_function])
99
+ set_template_namespace(conf[:template_namespace])
100
+ set_template_extension(conf[:template_extension])
101
+ set_public_root(conf[:public_root]) if conf[:public_root]
102
+ symbolize_keys(conf[:stylesheets]) if conf[:stylesheets]
103
+ symbolize_keys(conf[:javascripts]) if conf[:javascripts]
104
+ check_for_deprecations
105
+ self
106
+ end
107
+
108
+ def self.set_conf_dir(dir)
109
+ remove_const 'DEFAULT_CONFIG_PATH'
110
+ const_set 'DEFAULT_CONFIG_PATH', File.join(ASSET_ROOT, dir, 'assets.yml')
111
+ end
112
+
113
+ # Force a reload by resetting the Packager and reloading the configuration.
114
+ # In development, this will be called as a before_filter before every request.
115
+ def self.reload!
116
+ Thread.current[:jammit_packager] = nil
117
+ load_configuration(@config_path)
118
+ end
119
+
120
+ # Keep a global (thread-local) reference to a @Jammit::Packager@, to avoid
121
+ # recomputing asset lists unnecessarily.
122
+ def self.packager
123
+ Thread.current[:jammit_packager] ||= Packager.new
124
+ end
125
+
126
+ # Generate the base filename for a version of a given package.
127
+ def self.filename(package, extension, suffix=nil)
128
+ suffix_part = suffix ? "-#{suffix}" : ''
129
+ "#{package}#{suffix_part}.#{extension}"
130
+ end
131
+
132
+ # Generates the server-absolute URL to an asset package.
133
+ def self.asset_url(package, extension, suffix=nil, mtime=nil)
134
+ timestamp = mtime ? "?#{mtime.to_i}" : ''
135
+ "/#{package_path}/#{filename(package, extension, suffix)}#{timestamp}"
136
+ end
137
+
138
+ # Convenience method for packaging up Jammit, using the default options.
139
+ def self.package!(options={})
140
+ options = {
141
+ :config_path => Jammit::DEFAULT_CONFIG_PATH,
142
+ :output_folder => nil,
143
+ :base_url => nil,
144
+ :public_root => nil,
145
+ :force => false
146
+ }.merge(options)
147
+ load_configuration(options[:config_path])
148
+ set_public_root(options[:public_root]) if options[:public_root]
149
+ packager.force = options[:force]
150
+ packager.package_names = options[:package_names]
151
+ packager.precache_all(options[:output_folder], options[:base_url])
152
+ end
153
+
154
+ private
155
+
156
+ # Allows command-line definition of `PUBLIC_ROOT`, for those using Jammit
157
+ # outside of Rails.
158
+ def self.set_public_root(public_root=nil)
159
+ @public_root = public_root if public_root
160
+ end
161
+
162
+ # Ensure that the JavaScript compressor is a valid choice.
163
+ def self.set_javascript_compressor(value)
164
+ value = value && value.to_sym
165
+ @javascript_compressor = javascript_compressors.include?(value) ? value : DEFAULT_JAVASCRIPT_COMPRESSOR
166
+ end
167
+
168
+ # Ensure that the CSS compressor is a valid choice.
169
+ def self.set_css_compressor(value)
170
+ value = value && value.to_sym
171
+ @css_compressor = css_compressors.include?(value) ? value : DEFAULT_CSS_COMPRESSOR
172
+ end
173
+
174
+ # Turn asset packaging on or off, depending on configuration and environment.
175
+ def self.set_package_assets(value)
176
+ package_env = !defined?(Rails) || (!Rails.env.development? && !Rails.env.test?)
177
+ @package_assets = value == true || value.nil? ? package_env :
178
+ value == 'always' ? true : false
179
+ end
180
+
181
+ # Assign the JST template function, unless explicitly turned off.
182
+ def self.set_template_function(value)
183
+ @template_function = value == true || value.nil? ? DEFAULT_JST_COMPILER :
184
+ value == false ? '' : value
185
+ @include_jst_script = @template_function == DEFAULT_JST_COMPILER
186
+ end
187
+
188
+ # Set the root JS object in which to stash all compiled JST.
189
+ def self.set_template_namespace(value)
190
+ @template_namespace = value == true || value.nil? ? DEFAULT_JST_NAMESPACE : value.to_s
191
+ end
192
+
193
+ # Set the extension for JS templates.
194
+ def self.set_template_extension(value)
195
+ @template_extension = (value == true || value.nil? ? DEFAULT_JST_EXTENSION : value.to_s).gsub(/\A\.?(.*)\Z/, '\1')
196
+ @template_extension_matcher = /\.#{Regexp.escape(@template_extension)}\Z/
197
+ end
198
+
199
+ # The YUI Compressor requires Java > 1.4, and Closure requires Java > 1.6.
200
+ def self.check_java_version
201
+ return true if @checked_java_version
202
+ java = @compressor_options[:java] || 'java'
203
+ @css_compressor_options[:java] ||= java if @compressor_options[:java]
204
+ version = (`#{java} -version 2>&1`)[/\d+\.\d+/]
205
+ disable_compression if !version ||
206
+ (@javascript_compressor == :closure && version < '1.6') ||
207
+ (@javascript_compressor == :yui && version < '1.4')
208
+ @checked_java_version = true
209
+ end
210
+
211
+ # If we don't have a working Java VM, then disable asset compression and
212
+ # complain loudly.
213
+ def self.disable_compression
214
+ @compress_assets = false
215
+ warn("Asset compression disabled -- Java unavailable.")
216
+ end
217
+
218
+ # Jammit 0.5+ no longer supports separate template packages.
219
+ def self.check_for_deprecations
220
+ if @configuration[:templates]
221
+ raise DeprecationError, "Jammit 0.5+ no longer supports separate packages for templates.\nPlease fold your templates into the appropriate 'javascripts' package instead."
222
+ end
223
+ end
224
+
225
+ def self.warn(message)
226
+ message = "Jammit Warning: #{message}"
227
+ $stderr.puts message
228
+ end
229
+
230
+ # Clone of active_support's symbolize_keys, so that we don't have to depend
231
+ # on active_support in any fashion. Converts a hash's keys to all symbols.
232
+ def self.symbolize_keys(hash)
233
+ hash.keys.each do |key|
234
+ hash[(key.to_sym rescue key) || key] = hash.delete(key)
235
+ end
236
+ hash
237
+ end
238
+
239
+ end
240
+
241
+ require 'jammit/dependencies'
@@ -0,0 +1,84 @@
1
+ require 'optparse'
2
+ require File.expand_path(File.dirname(__FILE__) + '/../jammit')
3
+
4
+ module Jammit
5
+
6
+ # The @CommandLine@ is able to compress, pre-package, and pre-gzip all the
7
+ # assets specified in the configuration file, in order to avoid an initial
8
+ # round of slow requests after a fresh deployment.
9
+ class CommandLine
10
+
11
+ BANNER = <<-EOS
12
+
13
+ Usage: jammit OPTIONS
14
+
15
+ Run jammit inside a Rails application to compresses all JS, CSS,
16
+ and JST according to config/assets.yml, saving the packaged
17
+ files and corresponding gzipped versions.
18
+
19
+ If you're using "embed_assets", and you wish to precompile the
20
+ MHTML stylesheet variants, you must specify the "base-url".
21
+
22
+ Options:
23
+ EOS
24
+
25
+ # The @Jammit::CommandLine@ runs from the contents of @ARGV@.
26
+ def initialize
27
+ parse_options
28
+ ensure_configuration_file
29
+ Jammit.package!(@options)
30
+ end
31
+
32
+
33
+ private
34
+
35
+ # Make sure that we have a readable configuration file. The @jammit@
36
+ # command can't run without one.
37
+ def ensure_configuration_file
38
+ config = @options[:config_path]
39
+ return true if File.exists?(config) && File.readable?(config)
40
+ puts "Could not find the asset configuration file \"#{config}\""
41
+ exit(1)
42
+ end
43
+
44
+ # Uses @OptionParser@ to grab the options: *--output*, *--config*, and
45
+ # *--base-url*...
46
+ def parse_options
47
+ @options = {
48
+ :config_path => Jammit::DEFAULT_CONFIG_PATH,
49
+ :output_folder => nil,
50
+ :base_url => nil,
51
+ :force => false
52
+ }
53
+ @option_parser = OptionParser.new do |opts|
54
+ opts.on('-o', '--output PATH', 'output folder for packages (default: "public/assets")') do |output_folder|
55
+ @options[:output_folder] = output_folder
56
+ end
57
+ opts.on('-c', '--config PATH', 'path to assets.yml (default: "config/assets.yml")') do |config_path|
58
+ @options[:config_path] = config_path
59
+ end
60
+ opts.on('-u', '--base-url URL', 'base URL for MHTML (ex: "http://example.com")') do |base_url|
61
+ @options[:base_url] = base_url
62
+ end
63
+ opts.on('-f', '--force', 'force a rebuild of all assets') do |force|
64
+ @options[:force] = force
65
+ end
66
+ opts.on('-p', '--packages LIST', 'list of packages to build (ex: "core,ui", default: all)') do |package_names|
67
+ @options[:package_names] = package_names.split(/,\s*/).map {|n| n.to_sym }
68
+ end
69
+ opts.on('-P', '--public-root PATH', 'path to public assets (default: "public")') do |public_root|
70
+ puts "Option for PUBLIC_ROOT"
71
+ @options[:public_root] = public_root
72
+ end
73
+ opts.on_tail('-v', '--version', 'display Jammit version') do
74
+ puts "Jammit version #{Jammit::VERSION}"
75
+ exit
76
+ end
77
+ end
78
+ @option_parser.banner = BANNER
79
+ @option_parser.parse!(ARGV)
80
+ end
81
+
82
+ end
83
+
84
+ end
@@ -0,0 +1,253 @@
1
+ module Jammit
2
+
3
+ # Uses the YUI Compressor or Closure Compiler to compress JavaScript.
4
+ # Always uses YUI to compress CSS (Which means that Java must be installed.)
5
+ # Also knows how to create a concatenated JST file.
6
+ # If "embed_assets" is turned on, creates "mhtml" and "datauri" versions of
7
+ # all stylesheets, with all enabled assets inlined into the css.
8
+ class Compressor
9
+
10
+ # Mapping from extension to mime-type of all embeddable assets.
11
+ EMBED_MIME_TYPES = {
12
+ '.png' => 'image/png',
13
+ '.jpg' => 'image/jpeg',
14
+ '.jpeg' => 'image/jpeg',
15
+ '.gif' => 'image/gif',
16
+ '.tif' => 'image/tiff',
17
+ '.tiff' => 'image/tiff',
18
+ '.ttf' => 'application/x-font-ttf',
19
+ '.otf' => 'font/opentype',
20
+ '.woff' => 'application/x-font-woff'
21
+ }
22
+
23
+ # Font extensions for which we allow embedding:
24
+ EMBED_EXTS = EMBED_MIME_TYPES.keys
25
+ EMBED_FONTS = ['.ttf', '.otf', '.woff']
26
+
27
+ # (32k - padding) maximum length for data-uri assets (an IE8 limitation).
28
+ MAX_IMAGE_SIZE = 32700
29
+
30
+ # CSS asset-embedding regexes for URL rewriting.
31
+ EMBED_DETECTOR = /url\(['"]?([^\s)]+\.[a-z]+)(\?\d+)?['"]?\)/
32
+ EMBEDDABLE = /[\A\/]embed\//
33
+ EMBED_REPLACER = /url\(__EMBED__(.+?)(\?\d+)?\)/
34
+
35
+ # MHTML file constants.
36
+ MHTML_START = "/*\r\nContent-Type: multipart/related; boundary=\"MHTML_MARK\"\r\n\r\n"
37
+ MHTML_SEPARATOR = "--MHTML_MARK\r\n"
38
+ MHTML_END = "\r\n--MHTML_MARK--\r\n*/\r\n"
39
+
40
+ # JST file constants.
41
+ JST_START = "(function(){"
42
+ JST_END = "})();"
43
+
44
+ JAVASCRIPT_COMPRESSORS = {
45
+ :jsmin => Jammit.javascript_compressors.include?(:jsmin) ? Jammit::JsminCompressor : nil,
46
+ :yui => Jammit.javascript_compressors.include?(:yui) ? YUI::JavaScriptCompressor : nil,
47
+ :closure => Jammit.javascript_compressors.include?(:closure) ? Closure::Compiler : nil,
48
+ :uglifier => Jammit.javascript_compressors.include?(:uglifier) ? Jammit::Uglifier : nil
49
+ }
50
+
51
+ CSS_COMPRESSORS = {
52
+ :cssmin => Jammit.css_compressors.include?(:cssmin) ? Jammit::CssminCompressor : nil,
53
+ :yui => Jammit.css_compressors.include?(:yui) ? YUI::CssCompressor : nil,
54
+ :sass => Jammit.css_compressors.include?(:sass) ? Jammit::SassCompressor : nil
55
+ }
56
+
57
+ JAVASCRIPT_DEFAULT_OPTIONS = {
58
+ :jsmin => {},
59
+ :yui => {:munge => true},
60
+ :closure => {},
61
+ :uglifier => {:copyright => false}
62
+ }
63
+
64
+ # CSS compression can be provided with YUI Compressor or sass. JS
65
+ # compression can be provided with YUI Compressor, Google Closure
66
+ # Compiler or UglifyJS.
67
+ def initialize
68
+ if Jammit.javascript_compressors.include?(:yui) || Jammit.javascript_compressors.include?(:closure) || Jammit.css_compressors.include?(:yui)
69
+ Jammit.check_java_version
70
+ end
71
+
72
+ css_flavor = Jammit.css_compressor || Jammit::DEFAULT_CSS_COMPRESSOR
73
+ @css_compressor = CSS_COMPRESSORS[css_flavor].new(Jammit.css_compressor_options || {})
74
+ js_flavor = Jammit.javascript_compressor || Jammit::DEFAULT_JAVASCRIPT_COMPRESSOR
75
+ @options = JAVASCRIPT_DEFAULT_OPTIONS[js_flavor].merge(Jammit.compressor_options || {})
76
+ @js_compressor = JAVASCRIPT_COMPRESSORS[js_flavor].new(@options)
77
+ end
78
+
79
+ # Concatenate together a list of JavaScript paths, and pass them through the
80
+ # YUI Compressor (with munging enabled). JST can optionally be included.
81
+ def compress_js(paths)
82
+ if (jst_paths = paths.grep(Jammit.template_extension_matcher)).empty?
83
+ js = concatenate(paths)
84
+ else
85
+ js = concatenate(paths - jst_paths) + compile_jst(jst_paths)
86
+ end
87
+ Jammit.compress_assets ? @js_compressor.compress(js) : js
88
+ end
89
+
90
+ # Concatenate and compress a list of CSS stylesheets. When compressing a
91
+ # :datauri or :mhtml variant, post-processes the result to embed
92
+ # referenced assets.
93
+ def compress_css(paths, variant=nil, asset_url=nil)
94
+ @asset_contents = {}
95
+ css = concatenate_and_tag_assets(paths, variant)
96
+ css = @css_compressor.compress(css) if Jammit.compress_assets
97
+ case variant
98
+ when nil then return css
99
+ when :datauri then return with_data_uris(css)
100
+ when :mhtml then return with_mhtml(css, asset_url)
101
+ else raise PackageNotFound, "\"#{variant}\" is not a valid stylesheet variant"
102
+ end
103
+ end
104
+
105
+ # Compiles a single JST file by writing out a javascript that adds
106
+ # template properties to a top-level template namespace object. Adds a
107
+ # JST-compilation function to the top of the package, unless you've
108
+ # specified your own preferred function, or turned it off.
109
+ # JST templates are named with the basename of their file.
110
+ def compile_jst(paths)
111
+ namespace = Jammit.template_namespace
112
+ paths = paths.grep(Jammit.template_extension_matcher).sort
113
+ base_path = find_base_path(paths)
114
+ compiled = paths.map do |path|
115
+ contents = read_binary_file(path)
116
+ contents = contents.gsub(/\r?\n/, "\\n").gsub("'", '\\\\\'')
117
+ name = template_name(path, base_path)
118
+ "#{namespace}['#{name}'] = #{Jammit.template_function}('#{contents}');"
119
+ end
120
+ compiler = Jammit.include_jst_script ? read_binary_file(DEFAULT_JST_SCRIPT) : '';
121
+ setup_namespace = "#{namespace} = #{namespace} || {};"
122
+ [JST_START, setup_namespace, compiler, compiled, JST_END].flatten.join("\n")
123
+ end
124
+
125
+
126
+ private
127
+
128
+ # Given a set of paths, find a common prefix path.
129
+ def find_base_path(paths)
130
+ return nil if paths.length <= 1
131
+ paths.sort!
132
+ first = paths.first.split('/')
133
+ last = paths.last.split('/')
134
+ i = 0
135
+ while first[i] == last[i] && i <= first.length
136
+ i += 1
137
+ end
138
+ res = first.slice(0, i).join('/')
139
+ res.empty? ? nil : res
140
+ end
141
+
142
+ # Determine the name of a JS template. If there's a common base path, use
143
+ # the namespaced prefix. Otherwise, simply use the filename.
144
+ def template_name(path, base_path)
145
+ return File.basename(path, ".#{Jammit.template_extension}") unless base_path
146
+ path.gsub(/\A#{Regexp.escape(base_path)}\/(.*)\.#{Jammit.template_extension}\Z/, '\1')
147
+ end
148
+
149
+ # In order to support embedded assets from relative paths, we need to
150
+ # expand the paths before contatenating the CSS together and losing the
151
+ # location of the original stylesheet path. Validate the assets while we're
152
+ # at it.
153
+ def concatenate_and_tag_assets(paths, variant=nil)
154
+ stylesheets = [paths].flatten.map do |css_path|
155
+ contents = read_binary_file(css_path)
156
+ end
157
+ stylesheets.join("\n")
158
+ end
159
+
160
+ # Re-write all enabled asset URLs in a stylesheet with their corresponding
161
+ # Data-URI Base-64 encoded asset contents.
162
+ def with_data_uris(css)
163
+ css.gsub(EMBED_REPLACER) do |url|
164
+ "url(\"data:#{mime_type($1)};charset=utf-8;base64,#{encoded_contents($1)}\")"
165
+ end
166
+ end
167
+
168
+ # Re-write all enabled asset URLs in a stylesheet with the MHTML equivalent.
169
+ # The newlines ("\r\n") in the following method are critical. Without them
170
+ # your MHTML will look identical, but won't work.
171
+ def with_mhtml(css, asset_url)
172
+ paths, index = {}, 0
173
+ css = css.gsub(EMBED_REPLACER) do |url|
174
+ i = paths[$1] ||= "#{index += 1}-#{File.basename($1)}"
175
+ "url(mhtml:#{asset_url}!#{i})"
176
+ end
177
+ mhtml = paths.sort.map do |path, identifier|
178
+ mime, contents = mime_type(path), encoded_contents(path)
179
+ [MHTML_SEPARATOR, "Content-Location: #{identifier}\r\n", "Content-Type: #{mime}\r\n", "Content-Transfer-Encoding: base64\r\n\r\n", contents, "\r\n"]
180
+ end
181
+ [MHTML_START, mhtml, MHTML_END, css].flatten.join('')
182
+ end
183
+
184
+ # Return a rewritten asset URL for a new stylesheet -- the asset should
185
+ # be tagged for embedding if embeddable, and referenced at the correct level
186
+ # if relative.
187
+ def construct_asset_path(asset_path, css_path, variant)
188
+ public_path = absolute_path(asset_path, css_path)
189
+ return "__EMBED__#{public_path}" if embeddable?(public_path, variant)
190
+ source = asset_path.absolute? || ! Jammit.rewrite_relative_paths ? asset_path.to_s : relative_path(public_path)
191
+ end
192
+
193
+ # Get the site-absolute public path for an asset file path that may or may
194
+ # not be relative, given the path of the stylesheet that contains it.
195
+ def absolute_path(asset_pathname, css_pathname)
196
+ (asset_pathname.absolute? ?
197
+ Pathname.new(File.join(Jammit.public_root, asset_pathname)) :
198
+ css_pathname.dirname + asset_pathname).cleanpath
199
+ end
200
+
201
+ # CSS assets that are referenced by relative paths, and are *not* being
202
+ # embedded, must be rewritten relative to the newly-merged stylesheet path.
203
+ def relative_path(absolute_path)
204
+ File.join('../', absolute_path.sub(Jammit.public_root, ''))
205
+ end
206
+
207
+ # Similar to the AssetTagHelper's method of the same name, this will
208
+ # determine the correct asset id for a file.
209
+ def rails_asset_id(path)
210
+ asset_id = ENV["RAILS_ASSET_ID"]
211
+ return asset_id if asset_id
212
+ File.exists?(path) ? File.mtime(path).to_i.to_s : ''
213
+ end
214
+
215
+ # An asset is valid for embedding if it exists, is less than 32K, and is
216
+ # stored somewhere inside of a folder named "embed". IE does not support
217
+ # Data-URIs larger than 32K, and you probably shouldn't be embedding assets
218
+ # that large in any case. Because we need to check the base64 length here,
219
+ # save it so that we don't have to compute it again later.
220
+ def embeddable?(asset_path, variant)
221
+ font = EMBED_FONTS.include?(asset_path.extname)
222
+ return false unless variant
223
+ return false unless asset_path.to_s.match(EMBEDDABLE) && asset_path.exist?
224
+ return false unless EMBED_EXTS.include?(asset_path.extname)
225
+ return false unless font || encoded_contents(asset_path).length < MAX_IMAGE_SIZE
226
+ return false if font && variant == :mhtml
227
+ return true
228
+ end
229
+
230
+ # Return the Base64-encoded contents of an asset on a single line.
231
+ def encoded_contents(asset_path)
232
+ return @asset_contents[asset_path] if @asset_contents[asset_path]
233
+ data = read_binary_file(asset_path)
234
+ @asset_contents[asset_path] = Base64.encode64(data).gsub(/\n/, '')
235
+ end
236
+
237
+ # Grab the mime-type of an asset, by filename.
238
+ def mime_type(asset_path)
239
+ EMBED_MIME_TYPES[File.extname(asset_path)]
240
+ end
241
+
242
+ # Concatenate together a list of asset files.
243
+ def concatenate(paths)
244
+ [paths].flatten.map {|p| read_binary_file(p) }.join("\n")
245
+ end
246
+
247
+ # `File.read`, but in "binary" mode.
248
+ def read_binary_file(path)
249
+ File.open(path, 'rb:UTF-8') {|f| f.read }
250
+ end
251
+ end
252
+
253
+ end
@@ -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,10 @@
1
+ # Wraps CSSMin compressor to use the same API as the rest of
2
+ # Jammit's compressors.
3
+ class Jammit::CssminCompressor
4
+ def initialize(options = {})
5
+ end
6
+
7
+ def compress(css)
8
+ CSSMin.minify(css)
9
+ end
10
+ end
@@ -0,0 +1,59 @@
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
+
52
+ # Jammit Rails Integration:
53
+ if defined?(Rails)
54
+ require 'jammit/controller'
55
+ require 'jammit/helper'
56
+ require 'jammit/railtie'
57
+ require 'jammit/routes'
58
+ end
59
+
@@ -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)
@@ -0,0 +1,10 @@
1
+ # Wraps JSMin compressor to use the same API as the rest of
2
+ # Jammit's compressors.
3
+ class Jammit::JsminCompressor
4
+ def initialize(options = {})
5
+ end
6
+
7
+ def compress(js)
8
+ JSMin.minify(js)
9
+ end
10
+ end
@@ -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,201 @@
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)]
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 = get_file_list(globs.flatten.uniq).uniq.sort
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
+ puts name
169
+ end
170
+ packages
171
+ end
172
+
173
+ # Raise a PackageNotFound exception for missing packages...
174
+ def not_found(package, extension)
175
+ raise PackageNotFound, "assets.yml does not contain a \"#{package}\" #{extension.to_s.upcase} package"
176
+ end
177
+
178
+ private
179
+ def get_file_list(globs)
180
+ paths = []
181
+ globs.each do |glob|
182
+ if String === glob
183
+ paths.concat glob_files(glob)
184
+ elsif Hash === glob
185
+ paths = exclude_paths(paths, glob['exclude']) if glob['exclude']
186
+ #puts exclude_paths(paths, glob['exclude']).to_s
187
+ end
188
+ end
189
+ paths
190
+ end
191
+
192
+ def exclude_paths(paths, globs)
193
+ globs.each do |glob|
194
+ excluded_files = glob_files(glob)
195
+ paths.reject! { |path| excluded_files.include?(path) }
196
+ end
197
+ paths
198
+ end
199
+
200
+ end
201
+ end
@@ -0,0 +1,14 @@
1
+ # Rails 3 configuration via Railtie
2
+
3
+ if defined?(Rails::Railtie)
4
+ module Jammit
5
+ class Railtie < Rails::Railtie
6
+
7
+ initializer :jammit_routes do |app|
8
+ # Add a Jammit route for the reloader.
9
+ app.routes_reloader.paths << File.join(File.dirname(__FILE__), "..", "..", "rails", "routes.rb")
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,23 @@
1
+ module Jammit
2
+
3
+ # Rails 2.x routing module. Rails 3.x routes are in rails/routes.rb.
4
+ module Routes
5
+
6
+ # Jammit uses a single route in order to slow down Rails' routing speed
7
+ # by the absolute minimum. In your config/routes.rb file, call:
8
+ # Jammit::Routes.draw(map)
9
+ # Passing in the routing "map" object.
10
+ def self.draw(map)
11
+ map.jammit "/#{Jammit.package_path}/:package.:extension", {
12
+ :controller => 'jammit',
13
+ :action => 'package',
14
+ :requirements => {
15
+ # A hack to allow extension to include "."
16
+ :extension => /.+/
17
+ }
18
+ }
19
+ end
20
+
21
+ end
22
+
23
+ end
@@ -0,0 +1,17 @@
1
+ # Wraps sass' css compressor to use the same API as the rest of
2
+ # Jammit's compressors.
3
+ class Jammit::SassCompressor
4
+ # Creates a new sass compressor. Jammit::SassCompressor doesn't use
5
+ # any options, the +options+ parameter is there for API
6
+ # compatibility.
7
+ def initialize(options = {})
8
+ end
9
+
10
+ # Compresses +css+ using sass' CSS parser, and returns the
11
+ # compressed css.
12
+ def compress(css)
13
+ root_node = ::Sass::SCSS::CssParser.new(css, 'test.sass').parse
14
+ root_node.options = {:style => :compressed}
15
+ root_node.render.strip
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ class Jammit::Uglifier < ::Uglifier
2
+ alias :compress :compile
3
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jammit-ebtd
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.6
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Jeremy Ashkenas
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-04 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: cssmin
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.0.2
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.0.2
30
+ - !ruby/object:Gem::Dependency
31
+ name: jsmin
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.0.1
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.0.1
46
+ description: ! ' Jammit-Ebtd is a fork of the Jammit asset packager, modified for
47
+ ebaytoday.ru
48
+
49
+ '
50
+ email: jeremy@documentcloud.org
51
+ executables: []
52
+ extensions: []
53
+ extra_rdoc_files:
54
+ - README
55
+ files:
56
+ - lib/jammit/uglifier.rb
57
+ - lib/jammit/routes.rb
58
+ - lib/jammit/compressor.rb
59
+ - lib/jammit/jsmin_compressor.rb
60
+ - lib/jammit/packager.rb
61
+ - lib/jammit/railtie.rb
62
+ - lib/jammit/dependencies.rb
63
+ - lib/jammit/command_line.rb
64
+ - lib/jammit/jst.js
65
+ - lib/jammit/controller.rb
66
+ - lib/jammit/sass_compressor.rb
67
+ - lib/jammit/cssmin_compressor.rb
68
+ - lib/jammit/helper.rb
69
+ - lib/jammit-ebtd.rb
70
+ - bin/jammit
71
+ - jammit-ebtd.gemspec
72
+ - LICENSE
73
+ - README
74
+ homepage: https://github.com/m16a1/jammit-ebtd
75
+ licenses: []
76
+ post_install_message:
77
+ rdoc_options:
78
+ - --title
79
+ - Jammit
80
+ - --exclude
81
+ - test
82
+ - --main
83
+ - README
84
+ - --all
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ! '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 1.8.24
102
+ signing_key:
103
+ specification_version: 3
104
+ summary: Industrial Strength Asset Packaging for Rails
105
+ test_files: []