sprockets 2.6.0 → 4.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +118 -0
- data/{LICENSE → MIT-LICENSE} +2 -2
- data/README.md +541 -289
- data/bin/sprockets +20 -7
- data/lib/rake/sprocketstask.rb +34 -17
- data/lib/sprockets/add_source_map_comment_to_asset_processor.rb +60 -0
- data/lib/sprockets/asset.rb +158 -210
- data/lib/sprockets/autoload/babel.rb +8 -0
- data/lib/sprockets/autoload/closure.rb +8 -0
- data/lib/sprockets/autoload/coffee_script.rb +8 -0
- data/lib/sprockets/autoload/eco.rb +8 -0
- data/lib/sprockets/autoload/ejs.rb +8 -0
- data/lib/sprockets/autoload/jsminc.rb +8 -0
- data/lib/sprockets/autoload/sass.rb +8 -0
- data/lib/sprockets/autoload/sassc.rb +8 -0
- data/lib/sprockets/autoload/uglifier.rb +8 -0
- data/lib/sprockets/autoload/yui.rb +8 -0
- data/lib/sprockets/autoload/zopfli.rb +7 -0
- data/lib/sprockets/autoload.rb +16 -0
- data/lib/sprockets/babel_processor.rb +66 -0
- data/lib/sprockets/base.rb +89 -378
- data/lib/sprockets/bower.rb +61 -0
- data/lib/sprockets/bundle.rb +105 -0
- data/lib/sprockets/cache/file_store.rb +190 -14
- data/lib/sprockets/cache/memory_store.rb +84 -0
- data/lib/sprockets/cache/null_store.rb +54 -0
- data/lib/sprockets/cache.rb +271 -0
- data/lib/sprockets/cached_environment.rb +64 -0
- data/lib/sprockets/closure_compressor.rb +48 -0
- data/lib/sprockets/coffee_script_processor.rb +39 -0
- data/lib/sprockets/compressing.rb +134 -0
- data/lib/sprockets/configuration.rb +79 -0
- data/lib/sprockets/context.rb +166 -150
- data/lib/sprockets/dependencies.rb +74 -0
- data/lib/sprockets/digest_utils.rb +197 -0
- data/lib/sprockets/directive_processor.rb +241 -215
- data/lib/sprockets/eco_processor.rb +33 -0
- data/lib/sprockets/ejs_processor.rb +32 -0
- data/lib/sprockets/encoding_utils.rb +261 -0
- data/lib/sprockets/environment.rb +23 -64
- data/lib/sprockets/erb_processor.rb +43 -0
- data/lib/sprockets/errors.rb +5 -13
- data/lib/sprockets/exporters/base.rb +71 -0
- data/lib/sprockets/exporters/file_exporter.rb +24 -0
- data/lib/sprockets/exporters/zlib_exporter.rb +33 -0
- data/lib/sprockets/exporters/zopfli_exporter.rb +14 -0
- data/lib/sprockets/exporting.rb +73 -0
- data/lib/sprockets/file_reader.rb +16 -0
- data/lib/sprockets/http_utils.rb +135 -0
- data/lib/sprockets/jsminc_compressor.rb +32 -0
- data/lib/sprockets/jst_processor.rb +36 -19
- data/lib/sprockets/loader.rb +347 -0
- data/lib/sprockets/manifest.rb +228 -112
- data/lib/sprockets/manifest_utils.rb +48 -0
- data/lib/sprockets/mime.rb +78 -31
- data/lib/sprockets/npm.rb +52 -0
- data/lib/sprockets/path_dependency_utils.rb +77 -0
- data/lib/sprockets/path_digest_utils.rb +48 -0
- data/lib/sprockets/path_utils.rb +367 -0
- data/lib/sprockets/paths.rb +43 -19
- data/lib/sprockets/preprocessors/default_source_map.rb +49 -0
- data/lib/sprockets/processing.rb +146 -164
- data/lib/sprockets/processor_utils.rb +170 -0
- data/lib/sprockets/resolve.rb +295 -0
- data/lib/sprockets/sass_cache_store.rb +20 -15
- data/lib/sprockets/sass_compressor.rb +55 -10
- data/lib/sprockets/sass_functions.rb +3 -70
- data/lib/sprockets/sass_importer.rb +3 -29
- data/lib/sprockets/sass_processor.rb +313 -0
- data/lib/sprockets/sassc_compressor.rb +56 -0
- data/lib/sprockets/sassc_processor.rb +297 -0
- data/lib/sprockets/server.rb +159 -91
- data/lib/sprockets/source_map_processor.rb +66 -0
- data/lib/sprockets/source_map_utils.rb +483 -0
- data/lib/sprockets/transformers.rb +173 -0
- data/lib/sprockets/uglifier_compressor.rb +66 -0
- data/lib/sprockets/unloaded_asset.rb +139 -0
- data/lib/sprockets/uri_tar.rb +99 -0
- data/lib/sprockets/uri_utils.rb +194 -0
- data/lib/sprockets/utils/gzip.rb +99 -0
- data/lib/sprockets/utils.rb +193 -52
- data/lib/sprockets/version.rb +2 -1
- data/lib/sprockets/yui_compressor.rb +56 -0
- data/lib/sprockets.rb +217 -75
- metadata +272 -117
- data/lib/sprockets/asset_attributes.rb +0 -131
- data/lib/sprockets/bundled_asset.rb +0 -80
- data/lib/sprockets/caching.rb +0 -96
- data/lib/sprockets/charset_normalizer.rb +0 -41
- data/lib/sprockets/eco_template.rb +0 -38
- data/lib/sprockets/ejs_template.rb +0 -37
- data/lib/sprockets/engines.rb +0 -74
- data/lib/sprockets/index.rb +0 -99
- data/lib/sprockets/processed_asset.rb +0 -152
- data/lib/sprockets/processor.rb +0 -32
- data/lib/sprockets/safety_colons.rb +0 -28
- data/lib/sprockets/sass_template.rb +0 -60
- data/lib/sprockets/scss_template.rb +0 -13
- data/lib/sprockets/static_asset.rb +0 -58
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'json'
|
|
3
|
+
|
|
4
|
+
module Sprockets
|
|
5
|
+
module Bower
|
|
6
|
+
# Internal: All supported bower.json files.
|
|
7
|
+
#
|
|
8
|
+
# https://github.com/bower/json/blob/0.4.0/lib/json.js#L7
|
|
9
|
+
POSSIBLE_BOWER_JSONS = ['bower.json', 'component.json', '.bower.json']
|
|
10
|
+
|
|
11
|
+
# Internal: Override resolve_alternates to install bower.json behavior.
|
|
12
|
+
#
|
|
13
|
+
# load_path - String environment path
|
|
14
|
+
# logical_path - String path relative to base
|
|
15
|
+
#
|
|
16
|
+
# Returns candidate filenames.
|
|
17
|
+
def resolve_alternates(load_path, logical_path)
|
|
18
|
+
candidates, deps = super
|
|
19
|
+
|
|
20
|
+
# bower.json can only be nested one level deep
|
|
21
|
+
if !logical_path.index('/'.freeze)
|
|
22
|
+
dirname = File.join(load_path, logical_path)
|
|
23
|
+
|
|
24
|
+
if directory?(dirname)
|
|
25
|
+
filenames = POSSIBLE_BOWER_JSONS.map { |basename| File.join(dirname, basename) }
|
|
26
|
+
filename = filenames.detect { |fn| self.file?(fn) }
|
|
27
|
+
|
|
28
|
+
if filename
|
|
29
|
+
deps << build_file_digest_uri(filename)
|
|
30
|
+
read_bower_main(dirname, filename) do |path|
|
|
31
|
+
if file?(path)
|
|
32
|
+
candidates << path
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
return candidates, deps
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Internal: Read bower.json's main directive.
|
|
43
|
+
#
|
|
44
|
+
# dirname - String path to component directory.
|
|
45
|
+
# filename - String path to bower.json.
|
|
46
|
+
#
|
|
47
|
+
# Returns nothing.
|
|
48
|
+
def read_bower_main(dirname, filename)
|
|
49
|
+
bower = JSON.parse(File.read(filename), create_additions: false)
|
|
50
|
+
|
|
51
|
+
case bower['main']
|
|
52
|
+
when String
|
|
53
|
+
yield File.expand_path(bower['main'], dirname)
|
|
54
|
+
when Array
|
|
55
|
+
bower['main'].each do |name|
|
|
56
|
+
yield File.expand_path(name, dirname)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require 'set'
|
|
3
|
+
require 'sprockets/utils'
|
|
4
|
+
require 'sprockets/uri_utils'
|
|
5
|
+
|
|
6
|
+
module Sprockets
|
|
7
|
+
# Internal: Bundle processor takes a single file asset and prepends all the
|
|
8
|
+
# `:required` URIs to the contents.
|
|
9
|
+
#
|
|
10
|
+
# Uses pipeline metadata:
|
|
11
|
+
#
|
|
12
|
+
# :required - Ordered Set of asset URIs to prepend
|
|
13
|
+
# :stubbed - Set of asset URIs to subtract from the required set.
|
|
14
|
+
#
|
|
15
|
+
# Also see DirectiveProcessor.
|
|
16
|
+
class Bundle
|
|
17
|
+
def self.call(input)
|
|
18
|
+
env = input[:environment]
|
|
19
|
+
type = input[:content_type]
|
|
20
|
+
input[:links] ||= Set.new
|
|
21
|
+
dependencies = Set.new(input[:metadata][:dependencies])
|
|
22
|
+
|
|
23
|
+
processed_uri, deps = env.resolve(input[:filename], accept: type, pipeline: :self)
|
|
24
|
+
dependencies.merge(deps)
|
|
25
|
+
|
|
26
|
+
# DirectiveProcessor (and any other transformers called here with pipeline=self)
|
|
27
|
+
primary_asset = env.load(processed_uri)
|
|
28
|
+
to_load = primary_asset.metadata.delete(:to_load) || Set.new
|
|
29
|
+
to_link = primary_asset.metadata.delete(:to_link) || Set.new
|
|
30
|
+
|
|
31
|
+
to_load.each do |uri|
|
|
32
|
+
loaded_asset = env.load(uri)
|
|
33
|
+
dependencies.merge(loaded_asset.metadata[:dependencies])
|
|
34
|
+
if to_link.include?(uri)
|
|
35
|
+
primary_metadata = primary_asset.metadata
|
|
36
|
+
input[:links] << loaded_asset.uri
|
|
37
|
+
primary_metadata[:links] << loaded_asset.uri
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
find_required = proc { |uri| env.load(uri).metadata[:required] }
|
|
42
|
+
required = Utils.dfs(processed_uri, &find_required)
|
|
43
|
+
stubbed = Utils.dfs(env.load(processed_uri).metadata[:stubbed], &find_required)
|
|
44
|
+
required.subtract(stubbed)
|
|
45
|
+
dedup(required)
|
|
46
|
+
assets = required.map { |uri| env.load(uri) }
|
|
47
|
+
|
|
48
|
+
(required + stubbed).each do |uri|
|
|
49
|
+
dependencies.merge(env.load(uri).metadata[:dependencies])
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
reducers = Hash[env.match_mime_type_keys(env.config[:bundle_reducers], type).flat_map(&:to_a)]
|
|
53
|
+
process_bundle_reducers(input, assets, reducers).merge(dependencies: dependencies, included: assets.map(&:uri))
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Internal: Removes uri from required if it's already included as an alias.
|
|
57
|
+
#
|
|
58
|
+
# required - Set of required uris
|
|
59
|
+
#
|
|
60
|
+
# Returns deduped set of uris
|
|
61
|
+
def self.dedup(required)
|
|
62
|
+
dupes = required.reduce([]) do |r, uri|
|
|
63
|
+
path, params = URIUtils.parse_asset_uri(uri)
|
|
64
|
+
if (params.delete(:index_alias))
|
|
65
|
+
r << URIUtils.build_asset_uri(path, params)
|
|
66
|
+
end
|
|
67
|
+
r
|
|
68
|
+
end
|
|
69
|
+
required.subtract(dupes)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Internal: Run bundle reducers on set of Assets producing a reduced
|
|
73
|
+
# metadata Hash.
|
|
74
|
+
#
|
|
75
|
+
# filename - String bundle filename
|
|
76
|
+
# assets - Array of Assets
|
|
77
|
+
# reducers - Array of [initial, reducer_proc] pairs
|
|
78
|
+
#
|
|
79
|
+
# Returns reduced asset metadata Hash.
|
|
80
|
+
def self.process_bundle_reducers(input, assets, reducers)
|
|
81
|
+
initial = {}
|
|
82
|
+
reducers.each do |k, (v, _)|
|
|
83
|
+
if v.respond_to?(:call)
|
|
84
|
+
initial[k] = v.call(input)
|
|
85
|
+
elsif !v.nil?
|
|
86
|
+
initial[k] = v
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
assets.reduce(initial) do |h, asset|
|
|
91
|
+
reducers.each do |k, (_, block)|
|
|
92
|
+
value = k == :data ? asset.source : asset.metadata[k]
|
|
93
|
+
if h.key?(k)
|
|
94
|
+
if !value.nil?
|
|
95
|
+
h[k] = block.call(h[k], value)
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
h[k] = value
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
h
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|
|
@@ -1,32 +1,208 @@
|
|
|
1
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
2
|
require 'fileutils'
|
|
3
|
-
require '
|
|
3
|
+
require 'logger'
|
|
4
|
+
require 'sprockets/encoding_utils'
|
|
5
|
+
require 'sprockets/path_utils'
|
|
6
|
+
require 'zlib'
|
|
4
7
|
|
|
5
8
|
module Sprockets
|
|
6
|
-
|
|
7
|
-
# A
|
|
9
|
+
class Cache
|
|
10
|
+
# Public: A file system cache store that automatically cleans up old keys.
|
|
11
|
+
#
|
|
12
|
+
# Assign the instance to the Environment#cache.
|
|
8
13
|
#
|
|
9
14
|
# environment.cache = Sprockets::Cache::FileStore.new("/tmp")
|
|
10
15
|
#
|
|
16
|
+
# See Also
|
|
17
|
+
#
|
|
18
|
+
# ActiveSupport::Cache::FileStore
|
|
19
|
+
#
|
|
11
20
|
class FileStore
|
|
12
|
-
|
|
13
|
-
|
|
21
|
+
# Internal: Default key limit for store.
|
|
22
|
+
DEFAULT_MAX_SIZE = 25 * 1024 * 1024
|
|
23
|
+
EXCLUDED_DIRS = ['.', '..'].freeze
|
|
24
|
+
GITKEEP_FILES = ['.gitkeep', '.keep'].freeze
|
|
25
|
+
|
|
26
|
+
# Internal: Default standard error fatal logger.
|
|
27
|
+
#
|
|
28
|
+
# Returns a Logger.
|
|
29
|
+
def self.default_logger
|
|
30
|
+
logger = Logger.new($stderr)
|
|
31
|
+
logger.level = Logger::FATAL
|
|
32
|
+
logger
|
|
14
33
|
end
|
|
15
34
|
|
|
16
|
-
#
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
35
|
+
# Public: Initialize the cache store.
|
|
36
|
+
#
|
|
37
|
+
# root - A String path to a directory to persist cached values to.
|
|
38
|
+
# max_size - A Integer of the maximum size the store will hold (in bytes).
|
|
39
|
+
# (default: 25MB).
|
|
40
|
+
# logger - The logger to which some info will be printed.
|
|
41
|
+
# (default logger level is FATAL and won't output anything).
|
|
42
|
+
def initialize(root, max_size = DEFAULT_MAX_SIZE, logger = self.class.default_logger)
|
|
43
|
+
@root = root
|
|
44
|
+
@max_size = max_size
|
|
45
|
+
@gc_size = max_size * 0.75
|
|
46
|
+
@logger = logger
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Public: Retrieve value from cache.
|
|
50
|
+
#
|
|
51
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
|
52
|
+
#
|
|
53
|
+
# key - String cache key.
|
|
54
|
+
#
|
|
55
|
+
# Returns Object or nil or the value is not set.
|
|
56
|
+
def get(key)
|
|
57
|
+
path = File.join(@root, "#{key}.cache")
|
|
58
|
+
|
|
59
|
+
value = safe_open(path) do |f|
|
|
60
|
+
begin
|
|
61
|
+
EncodingUtils.unmarshaled_deflated(f.read, Zlib::MAX_WBITS)
|
|
62
|
+
rescue Exception => e
|
|
63
|
+
@logger.error do
|
|
64
|
+
"#{self.class}[#{path}] could not be unmarshaled: " +
|
|
65
|
+
"#{e.class}: #{e.message}"
|
|
66
|
+
end
|
|
67
|
+
nil
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
if value
|
|
72
|
+
FileUtils.touch(path)
|
|
73
|
+
value
|
|
74
|
+
end
|
|
20
75
|
end
|
|
21
76
|
|
|
22
|
-
#
|
|
23
|
-
|
|
77
|
+
# Public: Set a key and value in the cache.
|
|
78
|
+
#
|
|
79
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
|
80
|
+
#
|
|
81
|
+
# key - String cache key.
|
|
82
|
+
# value - Object value.
|
|
83
|
+
#
|
|
84
|
+
# Returns Object value.
|
|
85
|
+
def set(key, value)
|
|
86
|
+
path = File.join(@root, "#{key}.cache")
|
|
87
|
+
|
|
24
88
|
# Ensure directory exists
|
|
25
|
-
FileUtils.mkdir_p
|
|
89
|
+
FileUtils.mkdir_p File.dirname(path)
|
|
90
|
+
|
|
91
|
+
# Check if cache exists before writing
|
|
92
|
+
exists = File.exist?(path)
|
|
93
|
+
|
|
94
|
+
# Serialize value
|
|
95
|
+
marshaled = Marshal.dump(value)
|
|
96
|
+
|
|
97
|
+
# Compress if larger than 4KB
|
|
98
|
+
if marshaled.bytesize > 4 * 1024
|
|
99
|
+
deflater = Zlib::Deflate.new(
|
|
100
|
+
Zlib::BEST_COMPRESSION,
|
|
101
|
+
Zlib::MAX_WBITS,
|
|
102
|
+
Zlib::MAX_MEM_LEVEL,
|
|
103
|
+
Zlib::DEFAULT_STRATEGY
|
|
104
|
+
)
|
|
105
|
+
deflater << marshaled
|
|
106
|
+
raw = deflater.finish
|
|
107
|
+
else
|
|
108
|
+
raw = marshaled
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Write data
|
|
112
|
+
PathUtils.atomic_write(path) do |f|
|
|
113
|
+
f.write(raw)
|
|
114
|
+
@size = size + f.size unless exists
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# GC if necessary
|
|
118
|
+
gc! if size > @max_size
|
|
26
119
|
|
|
27
|
-
@root.join(key).open('w') { |f| Marshal.dump(value, f)}
|
|
28
120
|
value
|
|
29
121
|
end
|
|
122
|
+
|
|
123
|
+
# Public: Pretty inspect
|
|
124
|
+
#
|
|
125
|
+
# Returns String.
|
|
126
|
+
def inspect
|
|
127
|
+
"#<#{self.class} size=#{size}/#{@max_size}>"
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
# Public: Clear the cache
|
|
131
|
+
#
|
|
132
|
+
# adapted from ActiveSupport::Cache::FileStore#clear
|
|
133
|
+
#
|
|
134
|
+
# Deletes all items from the cache. In this case it deletes all the entries in the specified
|
|
135
|
+
# file store directory except for .keep or .gitkeep. Be careful which directory is specified
|
|
136
|
+
# as @root because everything in that directory will be deleted.
|
|
137
|
+
#
|
|
138
|
+
# Returns true
|
|
139
|
+
def clear(options=nil)
|
|
140
|
+
if File.exist?(@root)
|
|
141
|
+
root_dirs = Dir.entries(@root).reject { |f| (EXCLUDED_DIRS + GITKEEP_FILES).include?(f) }
|
|
142
|
+
FileUtils.rm_r(root_dirs.collect{ |f| File.join(@root, f) })
|
|
143
|
+
end
|
|
144
|
+
true
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
private
|
|
148
|
+
# Internal: Get all cache files along with stats.
|
|
149
|
+
#
|
|
150
|
+
# Returns an Array of [String filename, File::Stat] pairs sorted by
|
|
151
|
+
# mtime.
|
|
152
|
+
def find_caches
|
|
153
|
+
Dir.glob(File.join(@root, '**/*.cache')).reduce([]) { |stats, filename|
|
|
154
|
+
stat = safe_stat(filename)
|
|
155
|
+
# stat maybe nil if file was removed between the time we called
|
|
156
|
+
# dir.glob and the next stat
|
|
157
|
+
stats << [filename, stat] if stat
|
|
158
|
+
stats
|
|
159
|
+
}.sort_by { |_, stat| stat.mtime.to_i }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def size
|
|
163
|
+
@size ||= compute_size(find_caches)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def compute_size(caches)
|
|
167
|
+
caches.inject(0) { |sum, (_, stat)| sum + stat.size }
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def safe_stat(fn)
|
|
171
|
+
File.stat(fn)
|
|
172
|
+
rescue Errno::ENOENT
|
|
173
|
+
nil
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def safe_open(path, &block)
|
|
177
|
+
if File.exist?(path)
|
|
178
|
+
File.open(path, 'rb', &block)
|
|
179
|
+
end
|
|
180
|
+
rescue Errno::ENOENT
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def gc!
|
|
184
|
+
start_time = Time.now
|
|
185
|
+
|
|
186
|
+
caches = find_caches
|
|
187
|
+
size = compute_size(caches)
|
|
188
|
+
|
|
189
|
+
delete_caches, keep_caches = caches.partition { |filename, stat|
|
|
190
|
+
deleted = size > @gc_size
|
|
191
|
+
size -= stat.size
|
|
192
|
+
deleted
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return if delete_caches.empty?
|
|
196
|
+
|
|
197
|
+
FileUtils.remove(delete_caches.map(&:first), force: true)
|
|
198
|
+
@size = compute_size(keep_caches)
|
|
199
|
+
|
|
200
|
+
@logger.warn do
|
|
201
|
+
secs = Time.now.to_f - start_time.to_f
|
|
202
|
+
"#{self.class}[#{@root}] garbage collected " +
|
|
203
|
+
"#{delete_caches.size} files (#{(secs * 1000).to_i}ms)"
|
|
204
|
+
end
|
|
205
|
+
end
|
|
30
206
|
end
|
|
31
207
|
end
|
|
32
208
|
end
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Sprockets
|
|
3
|
+
class Cache
|
|
4
|
+
# Public: Basic in memory LRU cache.
|
|
5
|
+
#
|
|
6
|
+
# Assign the instance to the Environment#cache.
|
|
7
|
+
#
|
|
8
|
+
# environment.cache = Sprockets::Cache::MemoryStore.new(1000)
|
|
9
|
+
#
|
|
10
|
+
# See Also
|
|
11
|
+
#
|
|
12
|
+
# ActiveSupport::Cache::MemoryStore
|
|
13
|
+
#
|
|
14
|
+
class MemoryStore
|
|
15
|
+
# Internal: Default key limit for store.
|
|
16
|
+
DEFAULT_MAX_SIZE = 1000
|
|
17
|
+
|
|
18
|
+
# Public: Initialize the cache store.
|
|
19
|
+
#
|
|
20
|
+
# max_size - A Integer of the maximum number of keys the store will hold.
|
|
21
|
+
# (default: 1000).
|
|
22
|
+
def initialize(max_size = DEFAULT_MAX_SIZE)
|
|
23
|
+
@max_size = max_size
|
|
24
|
+
@cache = {}
|
|
25
|
+
@mutex = Mutex.new
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Public: Retrieve value from cache.
|
|
29
|
+
#
|
|
30
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
|
31
|
+
#
|
|
32
|
+
# key - String cache key.
|
|
33
|
+
#
|
|
34
|
+
# Returns Object or nil or the value is not set.
|
|
35
|
+
def get(key)
|
|
36
|
+
@mutex.synchronize do
|
|
37
|
+
exists = true
|
|
38
|
+
value = @cache.delete(key) { exists = false }
|
|
39
|
+
if exists
|
|
40
|
+
@cache[key] = value
|
|
41
|
+
else
|
|
42
|
+
nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Public: Set a key and value in the cache.
|
|
48
|
+
#
|
|
49
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
|
50
|
+
#
|
|
51
|
+
# key - String cache key.
|
|
52
|
+
# value - Object value.
|
|
53
|
+
#
|
|
54
|
+
# Returns Object value.
|
|
55
|
+
def set(key, value)
|
|
56
|
+
@mutex.synchronize do
|
|
57
|
+
@cache.delete(key)
|
|
58
|
+
@cache[key] = value
|
|
59
|
+
@cache.shift if @cache.size > @max_size
|
|
60
|
+
end
|
|
61
|
+
value
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Public: Pretty inspect
|
|
65
|
+
#
|
|
66
|
+
# Returns String.
|
|
67
|
+
def inspect
|
|
68
|
+
@mutex.synchronize do
|
|
69
|
+
"#<#{self.class} size=#{@cache.size}/#{@max_size}>"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Public: Clear the cache
|
|
74
|
+
#
|
|
75
|
+
# Returns true
|
|
76
|
+
def clear(options=nil)
|
|
77
|
+
@mutex.synchronize do
|
|
78
|
+
@cache.clear
|
|
79
|
+
end
|
|
80
|
+
true
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
module Sprockets
|
|
3
|
+
class Cache
|
|
4
|
+
# Public: A compatible cache store that doesn't store anything. Used by
|
|
5
|
+
# default when no Environment#cache is configured.
|
|
6
|
+
#
|
|
7
|
+
# Assign the instance to the Environment#cache.
|
|
8
|
+
#
|
|
9
|
+
# environment.cache = Sprockets::Cache::NullStore.new
|
|
10
|
+
#
|
|
11
|
+
# See Also
|
|
12
|
+
#
|
|
13
|
+
# ActiveSupport::Cache::NullStore
|
|
14
|
+
#
|
|
15
|
+
class NullStore
|
|
16
|
+
# Public: Simulate a cache miss.
|
|
17
|
+
#
|
|
18
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
|
19
|
+
#
|
|
20
|
+
# key - String cache key.
|
|
21
|
+
#
|
|
22
|
+
# Returns nil.
|
|
23
|
+
def get(key)
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Public: Simulate setting a value in the cache.
|
|
28
|
+
#
|
|
29
|
+
# This API should not be used directly, but via the Cache wrapper API.
|
|
30
|
+
#
|
|
31
|
+
# key - String cache key.
|
|
32
|
+
# value - Object value.
|
|
33
|
+
#
|
|
34
|
+
# Returns Object value.
|
|
35
|
+
def set(key, value)
|
|
36
|
+
value
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Public: Pretty inspect
|
|
40
|
+
#
|
|
41
|
+
# Returns String.
|
|
42
|
+
def inspect
|
|
43
|
+
"#<#{self.class}>"
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Public: Simulate clearing the cache
|
|
47
|
+
#
|
|
48
|
+
# Returns true
|
|
49
|
+
def clear(options=nil)
|
|
50
|
+
true
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|