1010require 'sprockets/resolve'
1111require 'sprockets/transformers'
1212require 'sprockets/uri_utils'
13+ require 'sprockets/unloaded_asset'
1314
1415module Sprockets
1516
16- # Internal: Used to parse and store the URI to an unloaded asset
17- # Generates keys used to store and retrieve items from cache
18- class UnloadedAsset
19-
20- # Internal: Initialize object for generating cache keys
21- #
22- # uri - A String containing complete URI to a file including schema
23- # and full path such as
24- # "file:///Path/app/assets/js/app.js?type=application/javascript"
25- # env - The current "environment" that assets are being loaded into.
26- # We need it so we know where the +root+ (directory where sprockets
27- # is being invoked). We also need for the `file_digest` method,
28- # since, for some strange reason, memoization is provided by
29- # overriding methods such as `stat` in the `PathUtils` module.
30- #
31- # Returns UnloadedAsset.
32- def initialize ( uri , env )
33- @uri = uri
34- @env = env
35- @root = env . root
36- @relative_path = get_relative_path_from_uri
37- @params = nil # lazy loaded
38- @filename = nil # lazy loaded
39- end
40- attr_reader :relative_path , :root , :uri
41-
42-
43- # Internal: Full file path without schema
44- #
45- # This returns a string containing the full path to the asset without the schema.
46- # Information is loaded lazilly since we want `UnloadedAsset.new(dep, self).relative_path`
47- # to be fast. Calling this method the first time allocates an array and a hash.
48- #
49- # Example
50- #
51- # If the URI is `file:///Full/path/app/assets/javascripts/application.js"` then the
52- # filename would be `"/Full/path/app/assets/javascripts/application.js"`
53- #
54- # Returns a String.
55- def filename
56- unless @filename
57- load_file_params
58- end
59- @filename
60- end
61-
62- # Internal: Hash of param values
63- #
64- # This information is generated and used internally by sprockets.
65- # Known keys include `:type` which store the asset's mime-type, `:id` which is a fully resolved
66- # digest for the asset (includes dependency digest as opposed to a digest of only file contents)
67- # and `:pipeline`. Hash may be empty.
68- #
69- # Example
70- #
71- # If the URI is `file:///Full/path/app/assets/javascripts/application.js"type=application/javascript`
72- # Then the params would be `{type: "application/javascript"}`
73- #
74- # Returns a Hash.
75- def params
76- unless @params
77- load_file_params
78- end
79- @params
80- end
81-
82- # Internal: Key of asset
83- #
84- # Used to retrieve an asset from the cache based on relative path to asset
85- #
86- # Returns a String.
87- def asset_key
88- "asset-uri:#{ relative_path } "
89- end
90-
91- # Public: Dependency History key
92- #
93- # Used to retrieve an array of "histories" each of which contain a set of stored dependencies
94- # for a given asset path and filename digest.
95- #
96- # A dependency can refer to either an asset i.e. index.js
97- # may rely on jquery.js (so jquery.js is a dependency), or other factors that may affect
98- # compilation, such as the VERSION of sprockets (i.e. the environment) and what "processors"
99- # are used.
100- #
101- # For example a history array with one Set of dependencies may look like:
102- #
103- # [["environment-version", "environment-paths", "processors:type=text/css&file_type=text/css",
104- # "file-digest:///Full/path/app/assets/stylesheets/application.css",
105- # "processors:type=text/css&file_type=text/css&pipeline=self",
106- # "file-digest:///Full/path/app/assets/stylesheets"]]
107- #
108- # This method of asset lookup is used to ensure that none of the dependencies have been modified
109- # since last lookup. If one of them has, the key will be different and a new entry must be stored.
110- #
111- # URI depndencies are later converted to relative paths
112- #
113- # Returns a String.
114- def dependency_history_key
115- "asset-uri-cache-dependencies:#{ relative_path } :#{ @env . file_digest ( filename ) } "
116- end
117-
118- # Internal: Digest key
119- #
120- # Used to retrieve a string containing the relative path to an asset based on
121- # a digest. The digest is generated from dependencies stored via information stored in
122- # the `dependency_history_key` after each of the "dependencies" is "resolved" for example
123- # "environment-version" may be resolved to "environment-1.0-3.2.0" for version "3.2.0" of sprockets
124- #
125- # Returns a String.
126- def digest_key ( digest )
127- "asset-uri-digest:#{ relative_path } :#{ digest } "
128- end
129-
130- # Internal: File digest key
131- #
132- # The digest for a given file won't change if the path and the stat time hasn't changed
133- # We can save time by not re-computing this information and storing it in the cache
134- #
135- # Returns a String.
136- def file_digest_key ( stat )
137- "file_digest:#{ relative_path } :#{ stat } "
138- end
139-
140- private
141- # Internal: Parses uri into filename and params hash
142- #
143- # Returns Array with filename and params hash
144- def load_file_params
145- @filename , @params = URIUtils . parse_asset_uri ( uri )
146- end
147-
148- # Internal: Converts uri to a relative path
149- #
150- # Returns a relative path if given URI is in the `@env.root` of where sprockets
151- # is running. Otherwise it returns a string of the absolute path
152- #
153- # Returns a String.
154- def get_relative_path_from_uri
155- path = uri . sub ( /\A file:\/ \/ / , "" . freeze )
156- if relative_path = PathUtils . split_subpath ( root , path )
157- relative_path
158- else
159- path
160- end
161- end
162- end
16317 # The loader phase takes a asset URI location and returns a constructed Asset
16418 # object.
16519 module Loader
@@ -178,7 +32,7 @@ module Loader
17832 def load ( uri )
17933 unloaded = UnloadedAsset . new ( uri , self )
18034 if unloaded . params . key? ( :id )
181- unless asset = cache . get ( unloaded . asset_key , true )
35+ unless asset = asset_from_cache ( unloaded . asset_key )
18236 id = unloaded . params . delete ( :id )
18337 uri_without_id = build_asset_uri ( unloaded . filename , unloaded . params )
18438 asset = load_from_unloaded ( UnloadedAsset . new ( uri_without_id , self ) )
@@ -200,7 +54,7 @@ def load(uri)
20054 if paths
20155 digest = DigestUtils . digest ( resolve_dependencies ( paths ) )
20256 if uri_from_cache = cache . get ( unloaded . digest_key ( digest ) , true )
203- cache . get ( UnloadedAsset . new ( uri_from_cache , self ) . asset_key , true )
57+ asset_from_cache ( UnloadedAsset . new ( uri_from_cache , self ) . asset_key )
20458 end
20559 else
20660 load_from_unloaded ( unloaded )
@@ -212,6 +66,24 @@ def load(uri)
21266
21367 private
21468
69+ # Internal: Load asset hash from cache
70+ #
71+ # key - A String containing lookup information for an asset
72+ #
73+ # This method converts all "compressed" paths to absolute paths.
74+ # Returns a hash of values representing an asset
75+ def asset_from_cache ( key )
76+ asset = cache . get ( key , true )
77+ if asset
78+ asset [ :uri ] = expand_from_root ( asset [ :uri ] )
79+ asset [ :load_path ] = expand_from_root ( asset [ :load_path ] )
80+ asset [ :filename ] = expand_from_root ( asset [ :filename ] )
81+ asset [ :metadata ] [ :included ] . map! { |uri | expand_from_root ( uri ) } if asset [ :metadata ] [ :included ]
82+ asset [ :metadata ] [ :dependencies ] . map! { |uri | uri . start_with? ( "file-digest://" ) ? expand_from_root ( uri ) : uri } if asset [ :metadata ] [ :dependencies ]
83+ end
84+ asset
85+ end
86+
21587 # Internal: Loads an asset and saves it to cache
21688 #
21789 # unloaded - An UnloadedAsset
@@ -303,16 +175,46 @@ def load_from_unloaded(unloaded)
303175 } . compact . max
304176 asset [ :mtime ] ||= self . stat ( unloaded . filename ) . mtime . to_i
305177
306- # Unloaded asset and stored_asset now have a different URI
307- stored_asset = UnloadedAsset . new ( asset [ :uri ] , self )
178+ store_asset ( asset , unloaded )
179+ asset
180+ end
308181
182+ # Internal: Save a given asset to the cache
183+ #
184+ # asset - A hash containing values of loaded asset
185+ # unloaded - The UnloadedAsset used to lookup the `asset`
186+ #
187+ # This method converts all absolute paths to "compressed" paths
188+ # which are relative if they're in the root.
189+ def store_asset ( asset , unloaded )
309190 # Save the asset in the cache under the new URI
310- cache . set ( stored_asset . asset_key , asset , true )
191+ cached_asset = asset . dup
192+ cached_asset [ :uri ] = compress_from_root ( asset [ :uri ] )
193+ cached_asset [ :filename ] = compress_from_root ( asset [ :filename ] )
194+ cached_asset [ :load_path ] = compress_from_root ( asset [ :load_path ] )
195+
196+ if cached_asset [ :metadata ]
197+ # Deep dup to avoid modifying `asset`
198+ cached_asset [ :metadata ] = cached_asset [ :metadata ] . dup
199+ if cached_asset [ :metadata ] [ :included ]
200+ cached_asset [ :metadata ] [ :included ] = cached_asset [ :metadata ] [ :included ] . dup
201+ cached_asset [ :metadata ] [ :included ] = cached_asset [ :metadata ] [ :included ] . map { |uri | compress_from_root ( uri ) }
202+ end
311203
312- # Save the new relative path for the digest key of the unloaded asset
313- cache . set ( unloaded . digest_key ( asset [ :dependencies_digest ] ) , stored_asset . relative_path , true ) # wat
204+ if cached_asset [ :metadata ] [ :dependencies ]
205+ cached_asset [ :metadata ] [ :dependencies ] = cached_asset [ :metadata ] [ :dependencies ] . dup
206+ cached_asset [ :metadata ] [ :dependencies ] . map! do |uri |
207+ uri . start_with? ( "file-digest://" . freeze ) ? compress_from_root ( uri ) : uri
208+ end
209+ end
210+ end
314211
315- asset
212+ # Unloaded asset and stored_asset now have a different URI
213+ stored_asset = UnloadedAsset . new ( asset [ :uri ] , self )
214+ cache . set ( stored_asset . asset_key , cached_asset , true )
215+
216+ # Save the new relative path for the digest key of the unloaded asset
217+ cache . set ( unloaded . digest_key ( asset [ :dependencies_digest ] ) , stored_asset . compressed_path , true )
316218 end
317219
318220
@@ -334,21 +236,7 @@ def load_from_unloaded(unloaded)
334236 #
335237 # Returns array of resolved dependencies
336238 def resolve_dependencies ( uris )
337- uris . map do |uri |
338- dependency = resolve_dependency ( uri )
339- case dependency
340- when Array
341- dependency . map do |dep |
342- if dep && dep . is_a? ( String )
343- UnloadedAsset . new ( dep , self ) . relative_path
344- else
345- dep
346- end
347- end
348- else
349- dependency
350- end
351- end
239+ uris . map { |uri | resolve_dependency ( uri ) }
352240 end
353241
354242 # Internal: Retrieves an asset based on its digest
@@ -388,14 +276,17 @@ def fetch_asset_from_dependency_cache(unloaded, limit = 3)
388276
389277 history = cache . get ( key ) || [ ]
390278 history . each_with_index do |deps , index |
279+ deps = deps . map { |path | path . start_with? ( "file-digest://" ) ? expand_from_root ( path ) : path }
391280 if asset = yield ( deps )
392281 cache . set ( key , history . rotate! ( index ) ) if index > 0
393282 return asset
394283 end
395284 end
396285
397286 asset = yield
398- deps = asset [ :metadata ] [ :dependencies ]
287+ deps = asset [ :metadata ] [ :dependencies ] . map do |uri |
288+ uri . start_with? ( "file-digest://" ) ? compress_from_root ( uri ) : uri
289+ end
399290 cache . set ( key , history . unshift ( deps ) . take ( limit ) )
400291 asset
401292 end
0 commit comments