sprockets 3.0.0.beta.3 → 3.0.0.beta.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,23 +10,24 @@ module Sprockets
10
10
  include Paths, Mime, Engines, Transformers, Processing, Compressing
11
11
 
12
12
  def initialize_configuration(parent)
13
- @logger = parent.logger
14
- @version = parent.version
15
- @digest_class = parent.digest_class
16
- @context_class = Class.new(parent.context_class)
17
- @root = parent.root
18
- @paths = parent.paths
19
- @mime_types = parent.mime_types
20
- @mime_exts = parent.mime_exts
21
- @encodings = parent.encodings
22
- @engines = parent.engines
23
- @engine_mime_types = parent.engine_mime_types
24
- @transformers = parent.transformers
25
- @preprocessors = parent.preprocessors
26
- @postprocessors = parent.postprocessors
27
- @bundle_reducers = parent.bundle_reducers
28
- @bundle_processors = parent.bundle_processors
29
- @compressors = parent.compressors
13
+ @logger = parent.logger
14
+ @version = parent.version
15
+ @digest_class = parent.digest_class
16
+ @context_class = Class.new(parent.context_class)
17
+ @root = parent.root
18
+ @paths = parent.paths
19
+ @mime_types = parent.mime_types
20
+ @mime_exts = parent.mime_exts
21
+ @encodings = parent.encodings
22
+ @engines = parent.engines
23
+ @engine_mime_types = parent.engine_mime_types
24
+ @transformers = parent.transformers
25
+ @inverted_transformers = parent.inverted_transformers
26
+ @preprocessors = parent.preprocessors
27
+ @postprocessors = parent.postprocessors
28
+ @bundle_reducers = parent.bundle_reducers
29
+ @bundle_processors = parent.bundle_processors
30
+ @compressors = parent.compressors
30
31
  end
31
32
 
32
33
  # Get and set `Logger` instance.
@@ -52,7 +53,7 @@ module Sprockets
52
53
  mutate_config(:version) { version.dup }
53
54
  end
54
55
 
55
- # Deprecated: Returns a `Digest` implementation class.
56
+ # Public: Returns a `Digest` implementation class.
56
57
  #
57
58
  # Defaults to `Digest::SHA256`.
58
59
  attr_reader :digest_class
@@ -66,6 +66,39 @@ module Sprockets
66
66
  #
67
67
  attr_reader :content_type
68
68
 
69
+ # Internal
70
+ def _resolve(method, path, options = {})
71
+ options[:content_type] = self.content_type if options[:content_type] == :self
72
+ options[:accept] = options.delete(:content_type)
73
+
74
+ if environment.absolute_path?(path)
75
+ filename = path
76
+ elsif environment.relative_path?(path)
77
+ path = File.expand_path(path, @dirname)
78
+ if logical_path = @environment.split_subpath(load_path, path)
79
+ if filename = environment.send(method, logical_path, options.merge(load_paths: [load_path]))
80
+ accept = options[:accept]
81
+ message = "couldn't find file '#{logical_path}' under '#{load_path}'"
82
+ message << " with type '#{accept}'" if accept
83
+ raise FileNotFound, message
84
+ end
85
+ else
86
+ raise FileOutsidePaths, "#{path} isn't under path: #{load_path}"
87
+ end
88
+ else
89
+ filename = environment.send(method, path, options)
90
+ end
91
+
92
+ if filename
93
+ filename
94
+ else
95
+ accept = options[:accept]
96
+ message = "couldn't find file '#{path}'"
97
+ message << " with type '#{accept}'" if accept
98
+ raise FileNotFound, message
99
+ end
100
+ end
101
+
69
102
  # Given a logical path, `resolve` will find and return the fully
70
103
  # expanded path. Relative paths will also be resolved. An optional
71
104
  # `:content_type` restriction can be supplied to restrict the
@@ -78,21 +111,11 @@ module Sprockets
78
111
  # # => "/path/to/app/javascripts/bar.js"
79
112
  #
80
113
  def resolve(path, options = {})
81
- options[:content_type] = self.content_type if options[:content_type] == :self
82
- options[:accept] = options.delete(:content_type)
114
+ _resolve(:resolve, path, options)
115
+ end
83
116
 
84
- if environment.absolute_path?(path)
85
- path
86
- elsif environment.relative_path?(path)
87
- path = File.expand_path(path, @dirname)
88
- if logical_path = @environment.split_subpath(load_path, path)
89
- environment.resolve_in_load_path(load_path, logical_path, options)
90
- else
91
- raise FileOutsidePaths, "#{path} isn't under path: #{load_path}"
92
- end
93
- else
94
- environment.resolve(path, options)
95
- end
117
+ def locate(path, options = {})
118
+ _resolve(:locate, path, options)
96
119
  end
97
120
 
98
121
  # `depend_on` allows you to state a dependency on a file without
@@ -114,7 +137,7 @@ module Sprockets
114
137
  # file. Unlike `depend_on`, this will include recursively include
115
138
  # the target asset's dependencies.
116
139
  def depend_on_asset(path)
117
- if asset = @environment.find_asset(resolve(path))
140
+ if asset = @environment.load(locate(path))
118
141
  @dependency_paths.merge(asset.metadata[:dependency_paths])
119
142
  end
120
143
  nil
@@ -130,8 +153,7 @@ module Sprockets
130
153
  # <%= require_asset "#{framework}.js" %>
131
154
  #
132
155
  def require_asset(path)
133
- filename = resolve(path, accept: @content_type)
134
- @required << @environment.resolve_asset_uri(filename, accept: @content_type, bundle: false)
156
+ @required << locate(path, accept: @content_type, bundle: false)
135
157
  nil
136
158
  end
137
159
 
@@ -139,8 +161,7 @@ module Sprockets
139
161
  # `path` must be an asset which may or may not already be included
140
162
  # in the bundle.
141
163
  def stub_asset(path)
142
- filename = resolve(path, accept: @content_type)
143
- @stubbed << @environment.resolve_asset_uri(filename, accept: @content_type, bundle: false)
164
+ @stubbed << @environment.locate(path, accept: @content_type, bundle: false)
144
165
  nil
145
166
  end
146
167
 
@@ -150,7 +171,7 @@ module Sprockets
150
171
  #
151
172
  # Returns an Asset or nil.
152
173
  def link_asset(path)
153
- if asset = @environment.find_asset(resolve(path))
174
+ if asset = @environment.load(locate(path))
154
175
  @dependency_paths.merge(asset.metadata[:dependency_paths])
155
176
  @links << asset.uri
156
177
  end
@@ -196,7 +196,7 @@ module Sprockets
196
196
  # //= require "./bar"
197
197
  #
198
198
  def process_require_directive(path)
199
- @required << resolve_uri(path)
199
+ @required << locate(path, accept: @content_type, bundle: false)
200
200
  end
201
201
 
202
202
  # `require_self` causes the body of the current file to be inserted
@@ -235,8 +235,10 @@ module Sprockets
235
235
  @environment.stat_directory(root).each do |subpath, stat|
236
236
  if subpath == @filename
237
237
  next
238
- elsif @environment.resolve_path_transform_type(subpath, @content_type)
239
- @required << @environment.resolve_asset_uri(subpath, accept: @content_type, bundle: false)
238
+ elsif stat.directory?
239
+ next
240
+ elsif uri = @environment.locate(subpath, accept: @content_type, bundle: false)
241
+ @required << uri
240
242
  end
241
243
  end
242
244
  else
@@ -260,19 +262,15 @@ module Sprockets
260
262
 
261
263
  @dependency_paths << root
262
264
 
263
- required = []
264
- @environment.stat_tree(root).each do |subpath, stat|
265
+ @environment.stat_sorted_tree(root).each do |subpath, stat|
265
266
  if subpath == @filename
266
267
  next
267
268
  elsif stat.directory?
268
269
  @dependency_paths << subpath
269
- elsif @environment.resolve_path_transform_type(subpath, @content_type)
270
- required << subpath
270
+ elsif uri = @environment.locate(subpath, accept: @content_type, bundle: false)
271
+ @required << uri
271
272
  end
272
273
  end
273
- required.sort_by(&:to_s).each do |subpath|
274
- @required << @environment.resolve_asset_uri(subpath, accept: @content_type, bundle: false)
275
- end
276
274
  else
277
275
  # The path must be relative and start with a `./`.
278
276
  raise ArgumentError, "require_tree argument must be a relative path"
@@ -292,7 +290,7 @@ module Sprockets
292
290
  # //= depend_on "foo.png"
293
291
  #
294
292
  def process_depend_on_directive(path)
295
- @dependency_paths << resolve(path, accept: "#{@content_type}, */*")
293
+ @dependency_paths << resolve(path)
296
294
  end
297
295
 
298
296
  # Allows you to state a dependency on an asset without including
@@ -307,7 +305,7 @@ module Sprockets
307
305
  # //= depend_on_asset "bar.js"
308
306
  #
309
307
  def process_depend_on_asset_directive(path)
310
- if asset = @environment.find_asset(resolve(path, accept: "#{@content_type}, */*"))
308
+ if asset = @environment.load(locate(path))
311
309
  @dependency_paths.merge(asset.metadata[:dependency_paths])
312
310
  end
313
311
  end
@@ -321,7 +319,7 @@ module Sprockets
321
319
  # //= stub "jquery"
322
320
  #
323
321
  def process_stub_directive(path)
324
- @stubbed << resolve_uri(path)
322
+ @stubbed << locate(path, accept: @content_type, bundle: false)
325
323
  end
326
324
 
327
325
  # Declares a linked dependency on the target asset.
@@ -333,7 +331,7 @@ module Sprockets
333
331
  # /*= link "logo.png" */
334
332
  #
335
333
  def process_link_directive(path)
336
- if asset = @environment.find_asset(resolve(path, accept: "#{@content_type}, */*"))
334
+ if asset = @environment.load(locate(path))
337
335
  @dependency_paths.merge(asset.metadata[:dependency_paths])
338
336
  @links << asset.uri
339
337
  end
@@ -344,23 +342,42 @@ module Sprockets
344
342
  File.expand_path(path, @dirname)
345
343
  end
346
344
 
347
- def resolve_uri(path)
348
- filename = resolve(path, accept: @content_type)
349
- @environment.resolve_asset_uri(filename, accept: @content_type, bundle: false)
345
+ def locate(path, options = {})
346
+ _resolve(:locate, path, options)
350
347
  end
351
348
 
352
349
  def resolve(path, options = {})
350
+ _resolve(:resolve, path, options)
351
+ end
352
+
353
+ def _resolve(method, path, options = {})
353
354
  if @environment.absolute_path?(path)
354
355
  raise FileOutsidePaths, "can't require absolute file: #{path}"
355
356
  elsif @environment.relative_path?(path)
356
357
  path = expand_relative_path(path)
357
358
  if logical_path = @environment.split_subpath(@load_path, path)
358
- @environment.resolve_in_load_path(@load_path, logical_path, options)
359
+ if filename = @environment.send(method, logical_path, options.merge(load_paths: [@load_path]))
360
+ filename
361
+ else
362
+ accept = options[:accept]
363
+ message = "couldn't find file '#{logical_path}' under '#{@load_path}'"
364
+ message << " with type '#{accept}'" if accept
365
+ raise FileNotFound, message
366
+ end
359
367
  else
360
368
  raise FileOutsidePaths, "#{path} isn't under path: #{@load_path}"
361
369
  end
362
370
  else
363
- @environment.resolve(path, options)
371
+ filename = @environment.send(method, path, options)
372
+ end
373
+
374
+ if filename
375
+ filename
376
+ else
377
+ accept = options[:accept]
378
+ message = "couldn't find file '#{path}'"
379
+ message << " with type '#{accept}'" if accept
380
+ raise FileNotFound, message
364
381
  end
365
382
  end
366
383
  end
@@ -1,4 +1,5 @@
1
1
  require 'sprockets/base'
2
+ require 'sprockets/cache/memory_store'
2
3
  require 'sprockets/cached_environment'
3
4
 
4
5
  module Sprockets
@@ -1,5 +1,58 @@
1
+ require 'sprockets/manifest'
2
+
1
3
  module Sprockets
2
4
  module Legacy
5
+ # Deprecated: Iterate over all logical paths with a matcher.
6
+ #
7
+ # Remove from 4.x.
8
+ #
9
+ # args - List of matcher objects.
10
+ #
11
+ # Returns Enumerator if no block is given.
12
+ def each_logical_path(*args, &block)
13
+ return to_enum(__method__, *args) unless block_given?
14
+
15
+ filters = args.flatten.map { |arg| Manifest.compile_match_filter(arg) }
16
+ logical_paths.each do |a, b|
17
+ if filters.any? { |f| f.call(a, b) }
18
+ if block.arity == 2
19
+ yield a, b
20
+ else
21
+ yield a
22
+ end
23
+ end
24
+ end
25
+
26
+ nil
27
+ end
28
+
29
+ # Deprecated: Enumerate over all logical paths in the environment.
30
+ #
31
+ # Returns an Enumerator of [logical_path, filename].
32
+ def logical_paths
33
+ return to_enum(__method__) unless block_given?
34
+
35
+ seen = Set.new
36
+
37
+ self.paths.each do |load_path|
38
+ stat_tree(load_path).each do |filename, stat|
39
+ next unless stat.file?
40
+
41
+ path = split_subpath(load_path, filename)
42
+ path, mime_type, _ = parse_path_extnames(path)
43
+ path = normalize_logical_path(path)
44
+ path += mime_types[mime_type][:extensions].first if mime_type
45
+
46
+ if !seen.include?(path)
47
+ yield path, filename
48
+ seen << path
49
+ end
50
+ end
51
+ end
52
+
53
+ nil
54
+ end
55
+
3
56
  private
4
57
  # Deprecated: Seriously.
5
58
  def matches_filter(filters, logical_path, filename)
@@ -19,5 +72,19 @@ module Sprockets
19
72
  end
20
73
  end
21
74
  end
75
+
76
+ # URI.unescape is deprecated on 1.9. We need to use URI::Parser
77
+ # if its available.
78
+ if defined? URI::DEFAULT_PARSER
79
+ def unescape(str)
80
+ str = URI::DEFAULT_PARSER.unescape(str)
81
+ str.force_encoding(Encoding.default_internal) if Encoding.default_internal
82
+ str
83
+ end
84
+ else
85
+ def unescape(str)
86
+ URI.unescape(str)
87
+ end
88
+ end
22
89
  end
23
90
  end
@@ -0,0 +1,168 @@
1
+ require 'sprockets/asset_uri'
2
+ require 'sprockets/asset'
3
+ require 'sprockets/digest_utils'
4
+ require 'sprockets/engines'
5
+ require 'sprockets/errors'
6
+ require 'sprockets/mime'
7
+ require 'sprockets/path_utils'
8
+ require 'sprockets/processing'
9
+ require 'sprockets/resolve'
10
+ require 'sprockets/transformers'
11
+
12
+ module Sprockets
13
+ # The loader phase takes a asset URI location and returns a constructed Asset
14
+ # object.
15
+ module Loader
16
+ include DigestUtils, Engines, Mime, PathUtils, Processing, Resolve, Transformers
17
+
18
+ # Public: Load Asset by AssetURI.
19
+ #
20
+ # uri - AssetURI
21
+ #
22
+ # Returns Asset.
23
+ def load(uri)
24
+ _, params = AssetURI.parse(uri)
25
+ asset = params.key?(:id) ?
26
+ load_asset_by_id_uri(uri) :
27
+ load_asset_by_uri(uri)
28
+ Asset.new(self, asset)
29
+ end
30
+
31
+ private
32
+ def load_asset_by_id_uri(uri)
33
+ path, params = AssetURI.parse(uri)
34
+
35
+ # Internal assertion, should be routed through load_asset_by_uri
36
+ unless id = params.delete(:id)
37
+ raise ArgumentError, "expected uri to have an id: #{uri}"
38
+ end
39
+
40
+ asset = load_asset_by_uri(AssetURI.build(path, params))
41
+
42
+ if id && asset[:id] != id
43
+ raise VersionNotFound, "could not find specified id: #{id}"
44
+ end
45
+
46
+ asset
47
+ end
48
+
49
+ def load_asset_by_uri(uri)
50
+ filename, params = AssetURI.parse(uri)
51
+
52
+ # Internal assertion, should be routed through load_asset_by_id_uri
53
+ if params.key?(:id)
54
+ raise ArgumentError, "expected uri to have no id: #{uri}"
55
+ end
56
+
57
+ unless file?(filename)
58
+ raise FileNotFound, "could not find file: #{filename}"
59
+ end
60
+
61
+
62
+ type = params[:type]
63
+ load_path, logical_path = paths_split(self.paths, filename)
64
+
65
+ unless load_path
66
+ raise FileOutsidePaths, "#{filename} is no longer under a load path: #{self.paths.join(', ')}"
67
+ end
68
+
69
+ logical_path, file_type, engine_extnames = parse_path_extnames(logical_path)
70
+ logical_path = normalize_logical_path(logical_path)
71
+
72
+ asset = {
73
+ uri: uri,
74
+ load_path: load_path,
75
+ filename: filename,
76
+ name: logical_path,
77
+ logical_path: logical_path
78
+ }
79
+
80
+ if type
81
+ asset[:content_type] = type
82
+ asset[:logical_path] += mime_types[type][:extensions].first
83
+ end
84
+
85
+ if type != file_type
86
+ transformers = unwrap_transformer(file_type, type)
87
+ unless transformers.any?
88
+ raise ConversionError, "could not convert #{file_type.inspect} to #{type.inspect}"
89
+ end
90
+ else
91
+ transformers = []
92
+ end
93
+
94
+ processed_processors = unwrap_preprocessors(file_type) +
95
+ unwrap_engines(engine_extnames).reverse +
96
+ transformers +
97
+ unwrap_postprocessors(type)
98
+
99
+ bundled_processors = params[:skip_bundle] ? [] : unwrap_bundle_processors(type)
100
+
101
+ processors = bundled_processors.any? ? bundled_processors : processed_processors
102
+ processors += unwrap_encoding_processors(params[:encoding])
103
+
104
+ # Read into memory and process if theres a processor pipeline or the
105
+ # content type is text.
106
+ if processors.any? || mime_type_charset_detecter(type)
107
+ data = read_file(asset[:filename], asset[:content_type])
108
+ metadata = {}
109
+
110
+ input = {
111
+ environment: self,
112
+ cache: self.cache,
113
+ uri: asset[:uri],
114
+ filename: asset[:filename],
115
+ load_path: asset[:load_path],
116
+ name: asset[:name],
117
+ content_type: asset[:content_type],
118
+ metadata: metadata
119
+ }
120
+
121
+ processors.each do |processor|
122
+ begin
123
+ result = processor.call(input.merge(data: data, metadata: metadata))
124
+ case result
125
+ when NilClass
126
+ # noop
127
+ when Hash
128
+ data = result[:data] if result.key?(:data)
129
+ metadata = metadata.merge(result)
130
+ metadata.delete(:data)
131
+ when String
132
+ data = result
133
+ else
134
+ raise Error, "invalid processor return type: #{result.class}"
135
+ end
136
+ end
137
+ end
138
+
139
+ asset[:source] = data
140
+ asset[:metadata] = metadata.merge(
141
+ charset: data.encoding.name.downcase,
142
+ digest: digest(data),
143
+ length: data.bytesize
144
+ )
145
+ else
146
+ asset[:metadata] = {
147
+ encoding: Encoding::BINARY,
148
+ digest: file_digest(asset[:filename]),
149
+ length: self.stat(asset[:filename]).size
150
+ }
151
+ end
152
+
153
+ metadata = asset[:metadata]
154
+ metadata[:dependency_paths] = Set.new(metadata[:dependency_paths]).merge([asset[:filename]])
155
+ metadata[:dependency_sources_digest] = files_digest(metadata[:dependency_paths])
156
+
157
+ asset[:integrity] = integrity_uri(asset[:metadata][:digest], asset[:content_type])
158
+
159
+ asset[:id] = pack_hexdigest(digest(asset))
160
+ asset[:uri] = AssetURI.build(filename, params.merge(id: asset[:id]))
161
+
162
+ # Deprecated: Avoid tracking Asset mtime
163
+ asset[:mtime] = metadata[:dependency_paths].map { |p| stat(p).mtime.to_i }.max
164
+
165
+ asset
166
+ end
167
+ end
168
+ end