Skip to content

Commit 4ae0bca

Browse files
committed
Merge pull request #101 from rails/schneems/its-all-relative-3.x
[close #96] Remove all absolute paths from cache
2 parents 59b86d9 + 0c44c2f commit 4ae0bca

File tree

6 files changed

+320
-173
lines changed

6 files changed

+320
-173
lines changed

lib/sprockets.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ module Sprockets
144144
env.version
145145
end
146146
register_dependency_resolver 'environment-paths' do |env|
147-
env.paths
147+
env.paths.map {|path| env.compress_from_root(path) }
148148
end
149149
register_dependency_resolver 'file-digest' do |env, str|
150150
env.file_digest(env.parse_file_digest_uri(str))

lib/sprockets/base.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
require 'sprockets/resolve'
1212
require 'sprockets/server'
1313
require 'sprockets/loader'
14+
require 'sprockets/uri_tar'
1415

1516
module Sprockets
1617
# `Base` class for `Environment` and `Cached`.
@@ -97,5 +98,13 @@ def inspect
9798
"root=#{root.to_s.inspect}, " +
9899
"paths=#{paths.inspect}>"
99100
end
101+
102+
def compress_from_root(uri)
103+
URITar.new(uri, self).compress
104+
end
105+
106+
def expand_from_root(uri)
107+
URITar.new(uri, self).expand
108+
end
100109
end
101110
end

lib/sprockets/loader.rb

Lines changed: 62 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -10,156 +10,10 @@
1010
require 'sprockets/resolve'
1111
require 'sprockets/transformers'
1212
require 'sprockets/uri_utils'
13+
require 'sprockets/unloaded_asset'
1314

1415
module 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(/\Afile:\/\//, "".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

Comments
 (0)