sprockets 2.1.4 → 2.2.3

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b51e06b67ce29e2f81d65f3ab796288e530749f2
4
- data.tar.gz: bf24d81285d0f2e2c69b6669bba9213f7c038e57
3
+ metadata.gz: be373494a4e64c06e55a9ad11e2c9efae09f1492
4
+ data.tar.gz: b683ce4b0ed64e683ea98d0b5af268bdeca3f584
5
5
  SHA512:
6
- metadata.gz: fabae91920a5939f2bc995f144917bf962646f9ae11fc030a1e953fbe5ca44fe7bf9f1ee6994424a726400f9cbae2d8e279b961f2c9a4867bed92bca70461c55
7
- data.tar.gz: f1926792b20cccc35f1af2c635bea28d55d5f9893b3efa5da36e854184f82f84292d03ad26a82fc70b0219629012993b1b260c2bbac233dab7490eea17bc1174
6
+ metadata.gz: fa430704b4ef87304b8a128cb83fbb64c66e24d0bdae3d9e9e343bbebd56bf8823f65cba4a0ad1eb33204308c814910ebdd9c2b570c33fbbf89a2fadd2383b33
7
+ data.tar.gz: 785eb7a9f36afa38e6f010a573240768a9620ed216f4b20614835ffe9564d258a0e5340c9e8dbf3b538e8b960d9f7fd22342581b5e597dbbfc4e892bac7f7d3a
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
@@ -6,6 +6,7 @@ module Sprockets
6
6
  autoload :Engines, "sprockets/engines"
7
7
  autoload :Environment, "sprockets/environment"
8
8
  autoload :Index, "sprockets/index"
9
+ autoload :Manifest, "sprockets/manifest"
9
10
 
10
11
  # Assets
11
12
  autoload :Asset, "sprockets/asset"
@@ -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`
@@ -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 = attributes_for(filename).logical_path
212
- yield logical_path unless files[logical_path]
213
- files[logical_path] = true
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
@@ -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.instance_eval do
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, :_dependency_paths, :_dependency_assets
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
- result = Sprockets::Utils.read_unicode(pathname)
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|
@@ -126,7 +126,7 @@ module Sprockets
126
126
  @directives ||= header.lines.each_with_index.map { |line, index|
127
127
  if directive = line[DIRECTIVE_PATTERN, 1]
128
128
  name, *args = Shellwords.shellwords(directive)
129
- if respond_to?("process_#{name}_directive")
129
+ if respond_to?("process_#{name}_directive", true)
130
130
  [index + 1, name, *args]
131
131
  end
132
132
  end
@@ -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, "require_tree argument must be a directory"
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
 
@@ -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
@@ -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
- required_assets_cache = {}
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
- (context._required_paths + [pathname.to_s]).each do |path|
105
+ paths.each do |path|
101
106
  if path == self.pathname.to_s
102
- unless required_assets_cache[self]
103
- required_assets_cache[self] = true
104
- @required_assets << self
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 required_assets_cache[asset_dependency]
109
- required_assets_cache[asset_dependency] = true
110
- @required_assets << asset_dependency
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
- required_assets_cache.clear
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|
@@ -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.read.tap do |data|
13
- # Eager validate the file's encoding. In most cases we
14
- # expect it to be UTF-8 unless `default_external` is set to
15
- # something else. An error is usually raised if the file is
16
- # saved as UTF-16 when we expected UTF-8.
17
- if !data.valid_encoding?
18
- raise EncodingError, "#{pathname} has a invalid " +
19
- "#{data.encoding} byte sequence"
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
- # If the file is UTF-8 and theres a BOM, strip it for safe concatenation.
22
- elsif data.encoding.name == "UTF-8" && data =~ UTF8_BOM_PATTERN
23
- data.sub!(UTF8_BOM_PATTERN, "")
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
@@ -1,3 +1,3 @@
1
1
  module Sprockets
2
- VERSION = "2.1.4"
2
+ VERSION = "2.2.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sprockets
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.4
4
+ version: 2.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Stephenson
@@ -25,6 +25,20 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '1.2'
28
+ - !ruby/object:Gem::Dependency
29
+ name: multi_json
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '1.0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '1.0'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: rack
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -73,6 +87,20 @@ dependencies:
73
87
  - - "~>"
74
88
  - !ruby/object:Gem::Version
75
89
  version: '2.0'
90
+ - !ruby/object:Gem::Dependency
91
+ name: coffee-script-source
92
+ requirement: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: 1.2.0
97
+ type: :development
98
+ prerelease: false
99
+ version_requirements: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 1.2.0
76
104
  - !ruby/object:Gem::Dependency
77
105
  name: eco
78
106
  requirement: !ruby/object:Gem::Requirement
@@ -162,12 +190,15 @@ description: Sprockets is a Rack-based asset packaging system that concatenates
162
190
  email:
163
191
  - sstephenson@gmail.com
164
192
  - josh@joshpeek.com
165
- executables: []
193
+ executables:
194
+ - sprockets
166
195
  extensions: []
167
196
  extra_rdoc_files: []
168
197
  files:
169
198
  - LICENSE
170
199
  - README.md
200
+ - bin/sprockets
201
+ - lib/rake/sprocketstask.rb
171
202
  - lib/sprockets.rb
172
203
  - lib/sprockets/asset.rb
173
204
  - lib/sprockets/asset_attributes.rb
@@ -185,6 +216,7 @@ files:
185
216
  - lib/sprockets/errors.rb
186
217
  - lib/sprockets/index.rb
187
218
  - lib/sprockets/jst_processor.rb
219
+ - lib/sprockets/manifest.rb
188
220
  - lib/sprockets/mime.rb
189
221
  - lib/sprockets/processed_asset.rb
190
222
  - lib/sprockets/processing.rb