sprockets 2.1.2 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sprockets might be problematic. Click here for more details.
- data/README.md +8 -0
- data/bin/sprockets +80 -0
- data/lib/rake/sprocketstask.rb +140 -0
- data/lib/sprockets.rb +1 -0
- data/lib/sprockets/asset.rb +2 -0
- data/lib/sprockets/base.rb +45 -5
- data/lib/sprockets/context.rb +19 -3
- data/lib/sprockets/directive_processor.rb +13 -1
- data/lib/sprockets/environment.rb +4 -0
- data/lib/sprockets/index.rb +4 -0
- data/lib/sprockets/manifest.rb +203 -0
- data/lib/sprockets/mime.rb +10 -0
- data/lib/sprockets/processed_asset.rb +15 -11
- data/lib/sprockets/static_asset.rb +2 -0
- data/lib/sprockets/utils.rb +14 -12
- data/lib/sprockets/version.rb +1 -1
- metadata +57 -23
data/README.md
CHANGED
@@ -354,6 +354,14 @@ submit a pull request.
|
|
354
354
|
|
355
355
|
## Version History ##
|
356
356
|
|
357
|
+
**2.2.0** (Unreleased)
|
358
|
+
|
359
|
+
* Added `sprockets` command line utility.
|
360
|
+
* Added rake/sprocketstask.
|
361
|
+
* Added json manifest log of compiled assets.
|
362
|
+
* Added `stub` directive that allows you to exclude files from the bundle.
|
363
|
+
* Added per environment external encoding (Environment#default_external_encoding). Defaults to UTF-8. Fixes issues where LANG is not set correctly and Rubys default external is set to ASCII.
|
364
|
+
|
357
365
|
**2.1.2** (November 20, 2011)
|
358
366
|
|
359
367
|
* Disabled If-Modified-Since server checks. Fixes some browser caching issues when serving the asset body only. If-None-Match caching is sufficent.
|
data/bin/sprockets
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'sprockets'
|
4
|
+
require 'optparse'
|
5
|
+
require 'shellwords'
|
6
|
+
|
7
|
+
unless ARGV.delete("--noenv")
|
8
|
+
if File.exist?(path = "./.sprocketsrc")
|
9
|
+
rcflags = Shellwords.split(File.read(path))
|
10
|
+
ARGV.unshift(*rcflags)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
filenames = []
|
15
|
+
environment = Sprockets::Environment.new(Dir.pwd)
|
16
|
+
manifest = nil
|
17
|
+
|
18
|
+
(ENV['SPROCKETS_PATH'] || "").split(File::PATH_SEPARATOR).each do |path|
|
19
|
+
environment.append_path path
|
20
|
+
end
|
21
|
+
|
22
|
+
OptionParser.new do |opts|
|
23
|
+
opts.summary_width = 28
|
24
|
+
opts.banner = "Usage: sprockets [options] filename [filename ...]"
|
25
|
+
|
26
|
+
def opts.show_usage
|
27
|
+
puts self
|
28
|
+
exit 1
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-r", "--require LIBRARY", "Require the LIBRARY before doing anything") do |lib|
|
32
|
+
require lib
|
33
|
+
end
|
34
|
+
|
35
|
+
opts.on("-I DIRECTORY", "--include=DIRECTORY", "Adds the directory to the Sprockets load path") do |directory|
|
36
|
+
environment.append_path directory
|
37
|
+
end
|
38
|
+
|
39
|
+
opts.on("-o DIRECTORY", "--output=DIRECTORY", "Copy provided assets into DIRECTORY") do |directory|
|
40
|
+
manifest = Sprockets::Manifest.new(environment, directory)
|
41
|
+
end
|
42
|
+
|
43
|
+
opts.on("--noenv", "Disables .sprocketsrc file") do
|
44
|
+
end
|
45
|
+
|
46
|
+
opts.on_tail("-h", "--help", "Shows this help message") do
|
47
|
+
opts.show_usage
|
48
|
+
end
|
49
|
+
|
50
|
+
opts.on_tail("-v", "--version", "Shows version") do
|
51
|
+
puts Sprockets::VERSION
|
52
|
+
exit
|
53
|
+
end
|
54
|
+
|
55
|
+
opts.show_usage if ARGV.empty?
|
56
|
+
|
57
|
+
begin
|
58
|
+
opts.order(ARGV) do |filename|
|
59
|
+
filenames << File.expand_path(filename)
|
60
|
+
end
|
61
|
+
rescue OptionParser::ParseError => e
|
62
|
+
opts.warn e.message
|
63
|
+
opts.show_usage
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
if environment.paths.empty?
|
68
|
+
warn "No load paths given"
|
69
|
+
warn "Usage: sprockets -Ijavascripts/ filename"
|
70
|
+
exit 1
|
71
|
+
end
|
72
|
+
|
73
|
+
if manifest
|
74
|
+
manifest.compile(filenames)
|
75
|
+
elsif filenames.length == 1
|
76
|
+
puts environment.find_asset(filenames.first).to_s
|
77
|
+
else
|
78
|
+
warn "Only one file can be compiled to stdout at a time"
|
79
|
+
exit 1
|
80
|
+
end
|
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/tasklib'
|
3
|
+
|
4
|
+
require 'sprockets'
|
5
|
+
require 'logger'
|
6
|
+
|
7
|
+
module Rake
|
8
|
+
# Simple Sprockets compilation Rake task macro.
|
9
|
+
#
|
10
|
+
# Rake::SprocketsTask.new do |t|
|
11
|
+
# t.environment = Sprockets::Environment.new
|
12
|
+
# t.output = "./public/assets"
|
13
|
+
# t.assets = %w( application.js application.css )
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
class SprocketsTask < Rake::TaskLib
|
17
|
+
# Name of the task. Defaults to "assets".
|
18
|
+
#
|
19
|
+
# The name will also be used to suffix the clean and clobber
|
20
|
+
# tasks, "clean_assets" and "clobber_assets".
|
21
|
+
attr_accessor :name
|
22
|
+
|
23
|
+
# `Environment` instance used for finding assets.
|
24
|
+
#
|
25
|
+
# You'll most likely want to reassign `environment` to your own.
|
26
|
+
#
|
27
|
+
# Rake::SprocketsTask.new do |t|
|
28
|
+
# t.environment = Foo::Assets
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
def environment
|
32
|
+
if !@environment.is_a?(Sprockets::Base) && @environment.respond_to?(:call)
|
33
|
+
@environment = @environment.call
|
34
|
+
else
|
35
|
+
@environment
|
36
|
+
end
|
37
|
+
end
|
38
|
+
attr_writer :environment
|
39
|
+
|
40
|
+
# Directory to write compiled assets too. As well as the manifest file.
|
41
|
+
#
|
42
|
+
# t.output = "./public/assets"
|
43
|
+
#
|
44
|
+
attr_accessor :output
|
45
|
+
|
46
|
+
# Array of asset logical paths to compile.
|
47
|
+
#
|
48
|
+
# t.assets = %w( application.js jquery.js application.css )
|
49
|
+
#
|
50
|
+
attr_accessor :assets
|
51
|
+
|
52
|
+
# Number of old assets to keep.
|
53
|
+
attr_accessor :keep
|
54
|
+
|
55
|
+
# Logger to use during rake tasks. Defaults to using stderr.
|
56
|
+
#
|
57
|
+
# t.logger = Logger.new($stdout)
|
58
|
+
#
|
59
|
+
attr_accessor :logger
|
60
|
+
|
61
|
+
# Returns logger level Integer.
|
62
|
+
def log_level
|
63
|
+
@logger.level
|
64
|
+
end
|
65
|
+
|
66
|
+
# Set logger level with constant or symbol.
|
67
|
+
#
|
68
|
+
# t.log_level = Logger::INFO
|
69
|
+
# t.log_level = :debug
|
70
|
+
#
|
71
|
+
def log_level=(level)
|
72
|
+
if level.is_a?(Integer)
|
73
|
+
@logger.level = level
|
74
|
+
else
|
75
|
+
@logger.level = Logger.const_get(level.to_s.upcase)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def initialize(name = :assets)
|
80
|
+
@name = name
|
81
|
+
@environment = lambda { Sprockets::Environment.new(Dir.pwd) }
|
82
|
+
@logger = Logger.new($stderr)
|
83
|
+
@logger.level = Logger::INFO
|
84
|
+
@keep = 2
|
85
|
+
|
86
|
+
yield self if block_given?
|
87
|
+
|
88
|
+
define
|
89
|
+
end
|
90
|
+
|
91
|
+
# Define tasks
|
92
|
+
def define
|
93
|
+
desc name == :assets ? "Compile assets" : "Compile #{name} assets"
|
94
|
+
task name do
|
95
|
+
with_logger do
|
96
|
+
manifest.compile(assets)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
desc name == :assets ? "Remove all assets" : "Remove all #{name} assets"
|
101
|
+
task "clobber_#{name}" do
|
102
|
+
with_logger do
|
103
|
+
manifest.clobber
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
task :clobber => ["clobber_#{name}"]
|
108
|
+
|
109
|
+
desc name == :assets ? "Clean old assets" : "Clean old #{name} assets"
|
110
|
+
task "clean_#{name}" do
|
111
|
+
with_logger do
|
112
|
+
manifest.clean(keep)
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
task :clean => ["clean_#{name}"]
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
# Returns cached indexed environment
|
121
|
+
def index
|
122
|
+
@index ||= environment.index
|
123
|
+
end
|
124
|
+
|
125
|
+
# Returns manifest for tasks
|
126
|
+
def manifest
|
127
|
+
@manifest ||= Sprockets::Manifest.new(index, output)
|
128
|
+
end
|
129
|
+
|
130
|
+
# Sub out environment logger with our rake task logger that
|
131
|
+
# writes to stderr.
|
132
|
+
def with_logger
|
133
|
+
old_logger = index.logger
|
134
|
+
index.logger = @logger
|
135
|
+
yield
|
136
|
+
ensure
|
137
|
+
index.logger = old_logger
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
data/lib/sprockets.rb
CHANGED
data/lib/sprockets/asset.rb
CHANGED
@@ -138,6 +138,8 @@ module Sprockets
|
|
138
138
|
# Gzip contents if filename has '.gz'
|
139
139
|
options[:compress] ||= File.extname(filename) == '.gz'
|
140
140
|
|
141
|
+
FileUtils.mkdir_p File.dirname(filename)
|
142
|
+
|
141
143
|
File.open("#{filename}+", 'wb') do |f|
|
142
144
|
if options[:compress]
|
143
145
|
# Run contents through `Zlib`
|
data/lib/sprockets/base.rb
CHANGED
@@ -103,6 +103,12 @@ module Sprockets
|
|
103
103
|
raise NotImplementedError
|
104
104
|
end
|
105
105
|
|
106
|
+
if defined? Encoding.default_external
|
107
|
+
# Define `default_external_encoding` accessor on 1.9.
|
108
|
+
# Defaults to UTF-8.
|
109
|
+
attr_accessor :default_external_encoding
|
110
|
+
end
|
111
|
+
|
106
112
|
# Works like `Dir.entries`.
|
107
113
|
#
|
108
114
|
# Subclasses may cache this method.
|
@@ -204,13 +210,15 @@ module Sprockets
|
|
204
210
|
nil
|
205
211
|
end
|
206
212
|
|
207
|
-
def each_logical_path
|
208
|
-
return to_enum(__method__) unless block_given?
|
213
|
+
def each_logical_path(*args)
|
214
|
+
return to_enum(__method__, *args) unless block_given?
|
215
|
+
filters = args.flatten
|
209
216
|
files = {}
|
210
217
|
each_file do |filename|
|
211
|
-
logical_path =
|
212
|
-
|
213
|
-
|
218
|
+
if logical_path = logical_path_for_filename(filename, filters)
|
219
|
+
yield logical_path unless files[logical_path]
|
220
|
+
files[logical_path] = true
|
221
|
+
end
|
214
222
|
end
|
215
223
|
nil
|
216
224
|
end
|
@@ -263,5 +271,37 @@ module Sprockets
|
|
263
271
|
ensure
|
264
272
|
Thread.current[:sprockets_circular_calls] = nil if reset
|
265
273
|
end
|
274
|
+
|
275
|
+
def logical_path_for_filename(filename, filters)
|
276
|
+
logical_path = attributes_for(filename).logical_path.to_s
|
277
|
+
|
278
|
+
if matches_filter(filters, logical_path)
|
279
|
+
return logical_path
|
280
|
+
end
|
281
|
+
|
282
|
+
# If filename is an index file, retest with alias
|
283
|
+
if File.basename(logical_path)[/[^\.]+/, 0] == 'index'
|
284
|
+
path = logical_path.sub(/\/index\./, '.')
|
285
|
+
if matches_filter(filters, path)
|
286
|
+
return path
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
nil
|
291
|
+
end
|
292
|
+
|
293
|
+
def matches_filter(filters, filename)
|
294
|
+
return true if filters.empty?
|
295
|
+
|
296
|
+
filters.any? do |filter|
|
297
|
+
if filter.is_a?(Regexp)
|
298
|
+
filter.match(filename)
|
299
|
+
elsif filter.respond_to?(:call)
|
300
|
+
filter.call(filename)
|
301
|
+
else
|
302
|
+
File.fnmatch(filter.to_s, filename)
|
303
|
+
end
|
304
|
+
end
|
305
|
+
end
|
266
306
|
end
|
267
307
|
end
|
data/lib/sprockets/context.rb
CHANGED
@@ -11,7 +11,7 @@ module Sprockets
|
|
11
11
|
# helpers by injecting them into `Environment#context_class`. Do not
|
12
12
|
# mix them into `Context` directly.
|
13
13
|
#
|
14
|
-
# environment.
|
14
|
+
# environment.context_class.class_eval do
|
15
15
|
# include MyHelper
|
16
16
|
# def asset_url; end
|
17
17
|
# end
|
@@ -22,7 +22,8 @@ module Sprockets
|
|
22
22
|
# assets. See `DirectiveProcessor` for an example of this.
|
23
23
|
class Context
|
24
24
|
attr_reader :environment, :pathname
|
25
|
-
attr_reader :_required_paths, :
|
25
|
+
attr_reader :_required_paths, :_stubbed_assets
|
26
|
+
attr_reader :_dependency_paths, :_dependency_assets
|
26
27
|
attr_writer :__LINE__
|
27
28
|
|
28
29
|
def initialize(environment, logical_path, pathname)
|
@@ -32,6 +33,7 @@ module Sprockets
|
|
32
33
|
@__LINE__ = nil
|
33
34
|
|
34
35
|
@_required_paths = []
|
36
|
+
@_stubbed_assets = Set.new
|
35
37
|
@_dependency_paths = Set.new
|
36
38
|
@_dependency_assets = Set.new([pathname.to_s])
|
37
39
|
end
|
@@ -143,6 +145,14 @@ module Sprockets
|
|
143
145
|
nil
|
144
146
|
end
|
145
147
|
|
148
|
+
# `stub_asset` blacklists `path` from being included in the bundle.
|
149
|
+
# `path` must be an asset which may or may not already be included
|
150
|
+
# in the bundle.
|
151
|
+
def stub_asset(path)
|
152
|
+
@_stubbed_assets << resolve(path, :content_type => :self).to_s
|
153
|
+
nil
|
154
|
+
end
|
155
|
+
|
146
156
|
# Tests if target path is able to be safely required into the
|
147
157
|
# current concatenation.
|
148
158
|
def asset_requirable?(path)
|
@@ -168,7 +178,13 @@ module Sprockets
|
|
168
178
|
if options[:data]
|
169
179
|
result = options[:data]
|
170
180
|
else
|
171
|
-
|
181
|
+
if environment.respond_to?(:default_external_encoding)
|
182
|
+
mime_type = environment.mime_types(pathname.extname)
|
183
|
+
encoding = environment.encoding_for_mime_type(mime_type)
|
184
|
+
result = Sprockets::Utils.read_unicode(pathname, encoding)
|
185
|
+
else
|
186
|
+
result = Sprockets::Utils.read_unicode(pathname)
|
187
|
+
end
|
172
188
|
end
|
173
189
|
|
174
190
|
processors.each do |processor|
|
@@ -260,7 +260,7 @@ module Sprockets
|
|
260
260
|
root = pathname.dirname.join(path).expand_path
|
261
261
|
|
262
262
|
unless (stats = stat(root)) && stats.directory?
|
263
|
-
raise ArgumentError, "
|
263
|
+
raise ArgumentError, "require_directory argument must be a directory"
|
264
264
|
end
|
265
265
|
|
266
266
|
context.depend_on(root)
|
@@ -340,6 +340,18 @@ module Sprockets
|
|
340
340
|
context.depend_on_asset(path)
|
341
341
|
end
|
342
342
|
|
343
|
+
# Allows dependency to be excluded from the asset bundle.
|
344
|
+
#
|
345
|
+
# The `path` must be a valid asset and may or may not already
|
346
|
+
# be part of the bundle. Once stubbed, it is blacklisted and
|
347
|
+
# can't be brought back by any other `require`.
|
348
|
+
#
|
349
|
+
# //= stub "jquery"
|
350
|
+
#
|
351
|
+
def process_stub_directive(path)
|
352
|
+
context.stub_asset(path)
|
353
|
+
end
|
354
|
+
|
343
355
|
# Enable Sprockets 1.x compat mode.
|
344
356
|
#
|
345
357
|
# Makes it possible to use the same JavaScript source
|
@@ -23,6 +23,10 @@ module Sprockets
|
|
23
23
|
self.logger = Logger.new($stderr)
|
24
24
|
self.logger.level = Logger::FATAL
|
25
25
|
|
26
|
+
if respond_to?(:default_external_encoding)
|
27
|
+
self.default_external_encoding = Encoding::UTF_8
|
28
|
+
end
|
29
|
+
|
26
30
|
# Create a safe `Context` subclass to mutate
|
27
31
|
@context_class = Class.new(Context)
|
28
32
|
|
data/lib/sprockets/index.rb
CHANGED
@@ -14,6 +14,10 @@ module Sprockets
|
|
14
14
|
def initialize(environment)
|
15
15
|
@environment = environment
|
16
16
|
|
17
|
+
if environment.respond_to?(:default_external_encoding)
|
18
|
+
@default_external_encoding = environment.default_external_encoding
|
19
|
+
end
|
20
|
+
|
17
21
|
# Copy environment attributes
|
18
22
|
@logger = environment.logger
|
19
23
|
@context_class = environment.context_class
|
@@ -0,0 +1,203 @@
|
|
1
|
+
require 'multi_json'
|
2
|
+
require 'time'
|
3
|
+
|
4
|
+
module Sprockets
|
5
|
+
# The Manifest logs the contents of assets compiled to a single
|
6
|
+
# directory. It records basic attributes about the asset for fast
|
7
|
+
# lookup without having to compile. A pointer from each logical path
|
8
|
+
# indicates with fingerprinted asset is the current one.
|
9
|
+
#
|
10
|
+
# The JSON is part of the public API and should be considered
|
11
|
+
# stable. This should make it easy to read from other programming
|
12
|
+
# languages and processes that don't have sprockets loaded. See
|
13
|
+
# `#assets` and `#files` for more infomation about the structure.
|
14
|
+
class Manifest
|
15
|
+
attr_reader :environment, :path, :dir
|
16
|
+
|
17
|
+
# Create new Manifest associated with an `environment`. `path` is
|
18
|
+
# a full path to the manifest json file. The file may or may not
|
19
|
+
# already exist. The dirname of the `path` will be used to write
|
20
|
+
# compiled assets to. Otherwise, if the path is a directory, the
|
21
|
+
# filename will default to "manifest.json" in that directory.
|
22
|
+
#
|
23
|
+
# Manifest.new(environment, "./public/assets/manifest.json")
|
24
|
+
#
|
25
|
+
def initialize(environment, path)
|
26
|
+
@environment = environment
|
27
|
+
|
28
|
+
if File.extname(path) == ""
|
29
|
+
@dir = File.expand_path(path)
|
30
|
+
@path = File.join(@dir, 'manifest.json')
|
31
|
+
else
|
32
|
+
@path = File.expand_path(path)
|
33
|
+
@dir = File.dirname(path)
|
34
|
+
end
|
35
|
+
|
36
|
+
data = nil
|
37
|
+
|
38
|
+
begin
|
39
|
+
if File.exist?(@path)
|
40
|
+
data = MultiJson.decode(File.read(@path))
|
41
|
+
end
|
42
|
+
rescue MultiJson::DecodeError => e
|
43
|
+
logger.error "#{@path} is invalid: #{e.class} #{e.message}"
|
44
|
+
end
|
45
|
+
|
46
|
+
@data = data.is_a?(Hash) ? data : {}
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns internal assets mapping. Keys are logical paths which
|
50
|
+
# map to the latest fingerprinted filename.
|
51
|
+
#
|
52
|
+
# Logical path (String): Fingerprint path (String)
|
53
|
+
#
|
54
|
+
# { "application.js" => "application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js",
|
55
|
+
# "jquery.js" => "jquery-ae0908555a245f8266f77df5a8edca2e.js" }
|
56
|
+
#
|
57
|
+
def assets
|
58
|
+
@data['assets'] ||= {}
|
59
|
+
end
|
60
|
+
|
61
|
+
# Returns internal file directory listing. Keys are filenames
|
62
|
+
# which map to an attributes array.
|
63
|
+
#
|
64
|
+
# Fingerprint path (String):
|
65
|
+
# logical_path: Logical path (String)
|
66
|
+
# mtime: ISO8601 mtime (String)
|
67
|
+
# digest: Base64 hex digest (String)
|
68
|
+
#
|
69
|
+
# { "application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js" =>
|
70
|
+
# { 'logical_path' => "application.js",
|
71
|
+
# 'mtime' => "2011-12-13T21:47:08-06:00",
|
72
|
+
# 'digest' => "2e8e9a7c6b0aafa0c9bdeec90ea30213" } }
|
73
|
+
#
|
74
|
+
def files
|
75
|
+
@data['files'] ||= {}
|
76
|
+
end
|
77
|
+
|
78
|
+
# Compile and write asset to directory. The asset is written to a
|
79
|
+
# fingerprinted filename like
|
80
|
+
# `application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js`. An entry is
|
81
|
+
# also inserted into the manifest file.
|
82
|
+
#
|
83
|
+
# compile("application.js")
|
84
|
+
#
|
85
|
+
def compile(*args)
|
86
|
+
paths = environment.each_logical_path(*args).to_a +
|
87
|
+
args.flatten.select { |fn| Pathname.new(fn).absolute? }
|
88
|
+
|
89
|
+
paths.each do |path|
|
90
|
+
if asset = find_asset(path)
|
91
|
+
files[asset.digest_path] = {
|
92
|
+
'logical_path' => asset.logical_path,
|
93
|
+
'mtime' => asset.mtime.iso8601,
|
94
|
+
'digest' => asset.digest
|
95
|
+
}
|
96
|
+
assets[asset.logical_path] = asset.digest_path
|
97
|
+
|
98
|
+
target = File.join(dir, asset.digest_path)
|
99
|
+
|
100
|
+
if File.exist?(target)
|
101
|
+
logger.debug "Skipping #{target}, already exists"
|
102
|
+
else
|
103
|
+
logger.info "Writing #{target}"
|
104
|
+
asset.write_to target
|
105
|
+
end
|
106
|
+
|
107
|
+
save
|
108
|
+
asset
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Removes file from directory and from manifest. `filename` must
|
114
|
+
# be the name with any directory path.
|
115
|
+
#
|
116
|
+
# manifest.remove("application-2e8e9a7c6b0aafa0c9bdeec90ea30213.js")
|
117
|
+
#
|
118
|
+
def remove(filename)
|
119
|
+
path = File.join(dir, filename)
|
120
|
+
logical_path = files[filename]['logical_path']
|
121
|
+
|
122
|
+
if assets[logical_path] == filename
|
123
|
+
assets.delete(logical_path)
|
124
|
+
end
|
125
|
+
|
126
|
+
files.delete(filename)
|
127
|
+
FileUtils.rm(path) if File.exist?(path)
|
128
|
+
|
129
|
+
save
|
130
|
+
|
131
|
+
logger.warn "Removed #{filename}"
|
132
|
+
|
133
|
+
nil
|
134
|
+
end
|
135
|
+
|
136
|
+
# Cleanup old assets in the compile directory. By default it will
|
137
|
+
# keep the latest version plus 2 backups.
|
138
|
+
def clean(keep = 2)
|
139
|
+
self.assets.keys.each do |logical_path|
|
140
|
+
# Get assets sorted by ctime, newest first
|
141
|
+
assets = backups_for(logical_path)
|
142
|
+
|
143
|
+
# Keep the last N backups
|
144
|
+
assets = assets[keep..-1] || []
|
145
|
+
|
146
|
+
# Remove old assets
|
147
|
+
assets.each { |path, _| remove(path) }
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
# Wipe directive
|
152
|
+
def clobber
|
153
|
+
FileUtils.rm_r(@dir) if File.exist?(@dir)
|
154
|
+
logger.warn "Removed #{@dir}"
|
155
|
+
nil
|
156
|
+
end
|
157
|
+
|
158
|
+
protected
|
159
|
+
# Finds all the backup assets for a logical path. The latest
|
160
|
+
# version is always excluded. The return array is sorted by the
|
161
|
+
# assets mtime in descending order (Newest to oldest).
|
162
|
+
def backups_for(logical_path)
|
163
|
+
files.select { |filename, attrs|
|
164
|
+
# Matching logical paths
|
165
|
+
attrs['logical_path'] == logical_path &&
|
166
|
+
# Excluding whatever asset is the current
|
167
|
+
assets[logical_path] != filename
|
168
|
+
}.sort_by { |filename, attrs|
|
169
|
+
# Sort by timestamp
|
170
|
+
Time.parse(attrs['mtime'])
|
171
|
+
}.reverse
|
172
|
+
end
|
173
|
+
|
174
|
+
# Basic wrapper around Environment#find_asset. Logs compile time.
|
175
|
+
def find_asset(logical_path)
|
176
|
+
asset = nil
|
177
|
+
ms = benchmark do
|
178
|
+
asset = environment.find_asset(logical_path)
|
179
|
+
end
|
180
|
+
logger.warn "Compiled #{logical_path} (#{ms}ms)"
|
181
|
+
asset
|
182
|
+
end
|
183
|
+
|
184
|
+
# Persist manfiest back to FS
|
185
|
+
def save
|
186
|
+
FileUtils.mkdir_p dir
|
187
|
+
File.open(path, 'w') do |f|
|
188
|
+
f.write MultiJson.encode(@data)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
private
|
193
|
+
def logger
|
194
|
+
environment.logger
|
195
|
+
end
|
196
|
+
|
197
|
+
def benchmark
|
198
|
+
start_time = Time.now.to_f
|
199
|
+
yield
|
200
|
+
((Time.now.to_f - start_time) * 1000).to_i
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
data/lib/sprockets/mime.rb
CHANGED
@@ -30,6 +30,16 @@ module Sprockets
|
|
30
30
|
ext = Sprockets::Utils.normalize_extension(ext)
|
31
31
|
@mime_types[ext] = mime_type
|
32
32
|
end
|
33
|
+
|
34
|
+
if defined? Encoding
|
35
|
+
# Returns the correct encoding for a given mime type, while falling
|
36
|
+
# back on the default external encoding, if it exists.
|
37
|
+
def encoding_for_mime_type(type)
|
38
|
+
encoding = Encoding::BINARY if type =~ %r{^(image|audio|video)/}
|
39
|
+
encoding ||= default_external_encoding if respond_to?(:default_external_encoding)
|
40
|
+
encoding
|
41
|
+
end
|
42
|
+
end
|
33
43
|
end
|
34
44
|
|
35
45
|
# Extend Sprockets module to provide global registry
|
@@ -94,27 +94,31 @@ module Sprockets
|
|
94
94
|
|
95
95
|
private
|
96
96
|
def build_required_assets(environment, context)
|
97
|
-
@required_assets = []
|
98
|
-
|
97
|
+
@required_assets = resolve_dependencies(environment, context._required_paths + [pathname.to_s]) -
|
98
|
+
resolve_dependencies(environment, context._stubbed_assets.to_a)
|
99
|
+
end
|
100
|
+
|
101
|
+
def resolve_dependencies(environment, paths)
|
102
|
+
assets = []
|
103
|
+
cache = {}
|
99
104
|
|
100
|
-
|
105
|
+
paths.each do |path|
|
101
106
|
if path == self.pathname.to_s
|
102
|
-
unless
|
103
|
-
|
104
|
-
|
107
|
+
unless cache[self]
|
108
|
+
cache[self] = true
|
109
|
+
assets << self
|
105
110
|
end
|
106
111
|
elsif asset = environment.find_asset(path, :bundle => false)
|
107
112
|
asset.required_assets.each do |asset_dependency|
|
108
|
-
unless
|
109
|
-
|
110
|
-
|
113
|
+
unless cache[asset_dependency]
|
114
|
+
cache[asset_dependency] = true
|
115
|
+
assets << asset_dependency
|
111
116
|
end
|
112
117
|
end
|
113
118
|
end
|
114
119
|
end
|
115
120
|
|
116
|
-
|
117
|
-
required_assets_cache = nil
|
121
|
+
assets
|
118
122
|
end
|
119
123
|
|
120
124
|
def build_dependency_paths(environment, context)
|
@@ -23,6 +23,8 @@ module Sprockets
|
|
23
23
|
# Gzip contents if filename has '.gz'
|
24
24
|
options[:compress] ||= File.extname(filename) == '.gz'
|
25
25
|
|
26
|
+
FileUtils.mkdir_p File.dirname(filename)
|
27
|
+
|
26
28
|
if options[:compress]
|
27
29
|
# Open file and run it through `Zlib`
|
28
30
|
pathname.open('rb') do |rd|
|
data/lib/sprockets/utils.rb
CHANGED
@@ -8,19 +8,21 @@ module Sprockets
|
|
8
8
|
# encoding and we want to avoid syntax errors in other interpreters.
|
9
9
|
UTF8_BOM_PATTERN = Regexp.new("\\A\uFEFF".encode('utf-8'))
|
10
10
|
|
11
|
-
def self.read_unicode(pathname)
|
12
|
-
pathname.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
"#{
|
11
|
+
def self.read_unicode(pathname, external_encoding = Encoding.default_external)
|
12
|
+
pathname.open("r:#{external_encoding}") do |f|
|
13
|
+
f.read.tap do |data|
|
14
|
+
# Eager validate the file's encoding. In most cases we
|
15
|
+
# expect it to be UTF-8 unless `default_external` is set to
|
16
|
+
# something else. An error is usually raised if the file is
|
17
|
+
# saved as UTF-16 when we expected UTF-8.
|
18
|
+
if !data.valid_encoding?
|
19
|
+
raise EncodingError, "#{pathname} has a invalid " +
|
20
|
+
"#{data.encoding} byte sequence"
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
22
|
+
# If the file is UTF-8 and theres a BOM, strip it for safe concatenation.
|
23
|
+
elsif data.encoding.name == "UTF-8" && data =~ UTF8_BOM_PATTERN
|
24
|
+
data.sub!(UTF8_BOM_PATTERN, "")
|
25
|
+
end
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
data/lib/sprockets/version.rb
CHANGED
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sprockets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 7
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 2
|
8
|
-
- 1
|
9
8
|
- 2
|
10
|
-
|
9
|
+
- 0
|
10
|
+
version: 2.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Sam Stephenson
|
@@ -16,7 +16,7 @@ autorequire:
|
|
16
16
|
bindir: bin
|
17
17
|
cert_chain: []
|
18
18
|
|
19
|
-
date:
|
19
|
+
date: 2012-01-10 00:00:00 -06:00
|
20
20
|
default_executable:
|
21
21
|
dependencies:
|
22
22
|
- !ruby/object:Gem::Dependency
|
@@ -35,7 +35,7 @@ dependencies:
|
|
35
35
|
type: :runtime
|
36
36
|
version_requirements: *id001
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
|
-
name:
|
38
|
+
name: multi_json
|
39
39
|
prerelease: false
|
40
40
|
requirement: &id002 !ruby/object:Gem::Requirement
|
41
41
|
none: false
|
@@ -50,9 +50,24 @@ dependencies:
|
|
50
50
|
type: :runtime
|
51
51
|
version_requirements: *id002
|
52
52
|
- !ruby/object:Gem::Dependency
|
53
|
-
name:
|
53
|
+
name: rack
|
54
54
|
prerelease: false
|
55
55
|
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 15
|
61
|
+
segments:
|
62
|
+
- 1
|
63
|
+
- 0
|
64
|
+
version: "1.0"
|
65
|
+
type: :runtime
|
66
|
+
version_requirements: *id003
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: tilt
|
69
|
+
prerelease: false
|
70
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
56
71
|
none: false
|
57
72
|
requirements:
|
58
73
|
- - ~>
|
@@ -71,11 +86,11 @@ dependencies:
|
|
71
86
|
- 0
|
72
87
|
version: 1.3.0
|
73
88
|
type: :runtime
|
74
|
-
version_requirements: *
|
89
|
+
version_requirements: *id004
|
75
90
|
- !ruby/object:Gem::Dependency
|
76
91
|
name: coffee-script
|
77
92
|
prerelease: false
|
78
|
-
requirement: &
|
93
|
+
requirement: &id005 !ruby/object:Gem::Requirement
|
79
94
|
none: false
|
80
95
|
requirements:
|
81
96
|
- - ~>
|
@@ -86,11 +101,27 @@ dependencies:
|
|
86
101
|
- 0
|
87
102
|
version: "2.0"
|
88
103
|
type: :development
|
89
|
-
version_requirements: *
|
104
|
+
version_requirements: *id005
|
105
|
+
- !ruby/object:Gem::Dependency
|
106
|
+
name: coffee-script-source
|
107
|
+
prerelease: false
|
108
|
+
requirement: &id006 !ruby/object:Gem::Requirement
|
109
|
+
none: false
|
110
|
+
requirements:
|
111
|
+
- - ~>
|
112
|
+
- !ruby/object:Gem::Version
|
113
|
+
hash: 31
|
114
|
+
segments:
|
115
|
+
- 1
|
116
|
+
- 2
|
117
|
+
- 0
|
118
|
+
version: 1.2.0
|
119
|
+
type: :development
|
120
|
+
version_requirements: *id006
|
90
121
|
- !ruby/object:Gem::Dependency
|
91
122
|
name: eco
|
92
123
|
prerelease: false
|
93
|
-
requirement: &
|
124
|
+
requirement: &id007 !ruby/object:Gem::Requirement
|
94
125
|
none: false
|
95
126
|
requirements:
|
96
127
|
- - ~>
|
@@ -101,11 +132,11 @@ dependencies:
|
|
101
132
|
- 0
|
102
133
|
version: "1.0"
|
103
134
|
type: :development
|
104
|
-
version_requirements: *
|
135
|
+
version_requirements: *id007
|
105
136
|
- !ruby/object:Gem::Dependency
|
106
137
|
name: ejs
|
107
138
|
prerelease: false
|
108
|
-
requirement: &
|
139
|
+
requirement: &id008 !ruby/object:Gem::Requirement
|
109
140
|
none: false
|
110
141
|
requirements:
|
111
142
|
- - ~>
|
@@ -116,11 +147,11 @@ dependencies:
|
|
116
147
|
- 0
|
117
148
|
version: "1.0"
|
118
149
|
type: :development
|
119
|
-
version_requirements: *
|
150
|
+
version_requirements: *id008
|
120
151
|
- !ruby/object:Gem::Dependency
|
121
152
|
name: execjs
|
122
153
|
prerelease: false
|
123
|
-
requirement: &
|
154
|
+
requirement: &id009 !ruby/object:Gem::Requirement
|
124
155
|
none: false
|
125
156
|
requirements:
|
126
157
|
- - ~>
|
@@ -131,11 +162,11 @@ dependencies:
|
|
131
162
|
- 0
|
132
163
|
version: "1.0"
|
133
164
|
type: :development
|
134
|
-
version_requirements: *
|
165
|
+
version_requirements: *id009
|
135
166
|
- !ruby/object:Gem::Dependency
|
136
167
|
name: json
|
137
168
|
prerelease: false
|
138
|
-
requirement: &
|
169
|
+
requirement: &id010 !ruby/object:Gem::Requirement
|
139
170
|
none: false
|
140
171
|
requirements:
|
141
172
|
- - ">="
|
@@ -145,11 +176,11 @@ dependencies:
|
|
145
176
|
- 0
|
146
177
|
version: "0"
|
147
178
|
type: :development
|
148
|
-
version_requirements: *
|
179
|
+
version_requirements: *id010
|
149
180
|
- !ruby/object:Gem::Dependency
|
150
181
|
name: rack-test
|
151
182
|
prerelease: false
|
152
|
-
requirement: &
|
183
|
+
requirement: &id011 !ruby/object:Gem::Requirement
|
153
184
|
none: false
|
154
185
|
requirements:
|
155
186
|
- - ">="
|
@@ -159,11 +190,11 @@ dependencies:
|
|
159
190
|
- 0
|
160
191
|
version: "0"
|
161
192
|
type: :development
|
162
|
-
version_requirements: *
|
193
|
+
version_requirements: *id011
|
163
194
|
- !ruby/object:Gem::Dependency
|
164
195
|
name: rake
|
165
196
|
prerelease: false
|
166
|
-
requirement: &
|
197
|
+
requirement: &id012 !ruby/object:Gem::Requirement
|
167
198
|
none: false
|
168
199
|
requirements:
|
169
200
|
- - ">="
|
@@ -173,13 +204,13 @@ dependencies:
|
|
173
204
|
- 0
|
174
205
|
version: "0"
|
175
206
|
type: :development
|
176
|
-
version_requirements: *
|
207
|
+
version_requirements: *id012
|
177
208
|
description: Sprockets is a Rack-based asset packaging system that concatenates and serves JavaScript, CoffeeScript, CSS, LESS, Sass, and SCSS.
|
178
209
|
email:
|
179
210
|
- sstephenson@gmail.com
|
180
211
|
- josh@joshpeek.com
|
181
|
-
executables:
|
182
|
-
|
212
|
+
executables:
|
213
|
+
- sprockets
|
183
214
|
extensions: []
|
184
215
|
|
185
216
|
extra_rdoc_files: []
|
@@ -187,6 +218,7 @@ extra_rdoc_files: []
|
|
187
218
|
files:
|
188
219
|
- README.md
|
189
220
|
- LICENSE
|
221
|
+
- lib/rake/sprocketstask.rb
|
190
222
|
- lib/sprockets/asset.rb
|
191
223
|
- lib/sprockets/asset_attributes.rb
|
192
224
|
- lib/sprockets/base.rb
|
@@ -203,6 +235,7 @@ files:
|
|
203
235
|
- lib/sprockets/errors.rb
|
204
236
|
- lib/sprockets/index.rb
|
205
237
|
- lib/sprockets/jst_processor.rb
|
238
|
+
- lib/sprockets/manifest.rb
|
206
239
|
- lib/sprockets/mime.rb
|
207
240
|
- lib/sprockets/processed_asset.rb
|
208
241
|
- lib/sprockets/processing.rb
|
@@ -214,6 +247,7 @@ files:
|
|
214
247
|
- lib/sprockets/utils.rb
|
215
248
|
- lib/sprockets/version.rb
|
216
249
|
- lib/sprockets.rb
|
250
|
+
- bin/sprockets
|
217
251
|
has_rdoc: true
|
218
252
|
homepage: http://getsprockets.org/
|
219
253
|
licenses: []
|