jammit-ebtd 0.6.6

Sign up to get free protection for your applications and to get access to all the features.
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: []