sprockets 1.0.2 → 2.0.0
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.
- data/LICENSE +21 -0
- data/README.md +356 -0
- data/lib/sprockets.rb +26 -37
- data/lib/sprockets/asset.rb +212 -0
- data/lib/sprockets/asset_attributes.rb +158 -0
- data/lib/sprockets/base.rb +163 -0
- data/lib/sprockets/bundled_asset.rb +258 -0
- data/lib/sprockets/cache/file_store.rb +41 -0
- data/lib/sprockets/caching.rb +123 -0
- data/lib/sprockets/charset_normalizer.rb +41 -0
- data/lib/sprockets/context.rb +217 -0
- data/lib/sprockets/digest.rb +67 -0
- data/lib/sprockets/directive_processor.rb +380 -0
- data/lib/sprockets/eco_template.rb +38 -0
- data/lib/sprockets/ejs_template.rb +37 -0
- data/lib/sprockets/engines.rb +98 -0
- data/lib/sprockets/environment.rb +81 -40
- data/lib/sprockets/errors.rb +17 -0
- data/lib/sprockets/index.rb +79 -0
- data/lib/sprockets/jst_processor.rb +26 -0
- data/lib/sprockets/mime.rb +38 -0
- data/lib/sprockets/processing.rb +280 -0
- data/lib/sprockets/processor.rb +32 -0
- data/lib/sprockets/safety_colons.rb +28 -0
- data/lib/sprockets/server.rb +272 -0
- data/lib/sprockets/static_asset.rb +86 -0
- data/lib/sprockets/trail.rb +114 -0
- data/lib/sprockets/utils.rb +67 -0
- data/lib/sprockets/version.rb +1 -7
- metadata +212 -64
- data/Rakefile +0 -19
- data/bin/sprocketize +0 -54
- data/ext/nph-sprockets.cgi +0 -127
- data/lib/sprockets/concatenation.rb +0 -36
- data/lib/sprockets/error.rb +0 -5
- data/lib/sprockets/pathname.rb +0 -37
- data/lib/sprockets/preprocessor.rb +0 -91
- data/lib/sprockets/secretary.rb +0 -106
- data/lib/sprockets/source_file.rb +0 -54
- data/lib/sprockets/source_line.rb +0 -82
- data/test/fixtures/assets/images/script_with_assets/one.png +0 -1
- data/test/fixtures/assets/images/script_with_assets/two.png +0 -1
- data/test/fixtures/assets/stylesheets/script_with_assets.css +0 -1
- data/test/fixtures/constants.yml +0 -1
- data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_ignored_when_strip_comments_is_false.js +0 -8
- data/test/fixtures/double_slash_comments_that_are_not_requires_should_be_removed_by_default.js +0 -2
- data/test/fixtures/multiline_comments_should_be_removed_by_default.js +0 -4
- data/test/fixtures/requiring_a_file_after_it_has_already_been_required_should_do_nothing.js +0 -5
- data/test/fixtures/requiring_a_file_that_does_not_exist_should_raise_an_error.js +0 -1
- data/test/fixtures/requiring_a_single_file_should_replace_the_require_comment_with_the_file_contents.js +0 -3
- data/test/fixtures/requiring_the_current_file_should_do_nothing.js +0 -1
- data/test/fixtures/src/constants.yml +0 -3
- data/test/fixtures/src/foo.js +0 -1
- data/test/fixtures/src/foo/bar.js +0 -4
- data/test/fixtures/src/foo/foo.js +0 -1
- data/test/fixtures/src/script_with_assets.js +0 -3
- data/test/test_concatenation.rb +0 -28
- data/test/test_environment.rb +0 -64
- data/test/test_helper.rb +0 -55
- data/test/test_pathname.rb +0 -43
- data/test/test_preprocessor.rb +0 -107
- data/test/test_secretary.rb +0 -83
- data/test/test_source_file.rb +0 -34
- data/test/test_source_line.rb +0 -89
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module Sprockets
|
6
|
+
module Cache
|
7
|
+
# A simple file system cache store.
|
8
|
+
#
|
9
|
+
# environment.cache = Sprockets::Cache::FileStore.new("tmp/sprockets")
|
10
|
+
#
|
11
|
+
class FileStore
|
12
|
+
def initialize(root)
|
13
|
+
@root = Pathname.new(root)
|
14
|
+
|
15
|
+
# Ensure directory exists
|
16
|
+
FileUtils.mkdir_p @root
|
17
|
+
end
|
18
|
+
|
19
|
+
# Lookup value in cache
|
20
|
+
def [](key)
|
21
|
+
pathname = path_for(key)
|
22
|
+
pathname.exist? ? pathname.open('rb') { |f| Marshal.load(f) } : nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# Save value to cache
|
26
|
+
def []=(key, value)
|
27
|
+
path_for(key).open('w') { |f| Marshal.dump(value, f)}
|
28
|
+
value
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
# Returns path for cache key.
|
33
|
+
#
|
34
|
+
# The key may include some funky characters so hash it into
|
35
|
+
# safe hex.
|
36
|
+
def path_for(key)
|
37
|
+
@root.join(::Digest::MD5.hexdigest(key))
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'sprockets/bundled_asset'
|
2
|
+
require 'sprockets/static_asset'
|
3
|
+
|
4
|
+
module Sprockets
|
5
|
+
# `Caching` is an internal mixin whose public methods are exposed on
|
6
|
+
# the `Environment` and `Index` classes.
|
7
|
+
module Caching
|
8
|
+
# Return `Asset` instance for serialized `Hash`.
|
9
|
+
def asset_from_hash(hash)
|
10
|
+
return unless hash.is_a?(Hash)
|
11
|
+
case hash['class']
|
12
|
+
when 'BundledAsset'
|
13
|
+
BundledAsset.from_hash(self, hash)
|
14
|
+
when 'StaticAsset'
|
15
|
+
StaticAsset.from_hash(self, hash)
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def cache_hash(key, version)
|
22
|
+
if cache.nil?
|
23
|
+
yield
|
24
|
+
elsif hash = cache_get_hash(key, version)
|
25
|
+
hash
|
26
|
+
elsif hash = yield
|
27
|
+
cache_set_hash(key, version, hash)
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
# Cache helper method. Takes a `path` argument which maybe a
|
34
|
+
# logical path or fully expanded path. The `&block` is passed
|
35
|
+
# for finding and building the asset if its not in cache.
|
36
|
+
def cache_asset(path)
|
37
|
+
# If `cache` is not set, return fast
|
38
|
+
if cache.nil?
|
39
|
+
yield
|
40
|
+
|
41
|
+
# Check cache for `path`
|
42
|
+
elsif (asset = asset_from_hash(cache_get_hash(path.to_s, digest.hexdigest))) && asset.fresh?
|
43
|
+
asset
|
44
|
+
|
45
|
+
# Otherwise yield block that slowly finds and builds the asset
|
46
|
+
elsif asset = yield
|
47
|
+
hash = {}
|
48
|
+
asset.encode_with(hash)
|
49
|
+
|
50
|
+
# Save the asset to at its path
|
51
|
+
cache_set_hash(path.to_s, digest.hexdigest, hash)
|
52
|
+
|
53
|
+
# Since path maybe a logical or full pathname, save the
|
54
|
+
# asset its its full path too
|
55
|
+
if path.to_s != asset.pathname.to_s
|
56
|
+
cache_set_hash(asset.pathname.to_s, digest.hexdigest, hash)
|
57
|
+
end
|
58
|
+
|
59
|
+
asset
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
# Strips `Environment#root` from key to make the key work
|
65
|
+
# consisently across different servers. The key is also hashed
|
66
|
+
# so it does not exceed 250 characters.
|
67
|
+
def cache_key_for(key)
|
68
|
+
File.join('sprockets', digest.hexdigest(key.sub(root, '')))
|
69
|
+
end
|
70
|
+
|
71
|
+
def cache_get_hash(key, version)
|
72
|
+
hash = cache_get(cache_key_for(key))
|
73
|
+
if hash.is_a?(Hash) && version == hash['_version']
|
74
|
+
hash
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def cache_set_hash(key, version, hash)
|
79
|
+
hash['_version'] = version
|
80
|
+
cache_set(cache_key_for(key), hash)
|
81
|
+
hash
|
82
|
+
end
|
83
|
+
|
84
|
+
# Low level cache getter for `key`. Checks a number of supported
|
85
|
+
# cache interfaces.
|
86
|
+
def cache_get(key)
|
87
|
+
# `Cache#get(key)` for Memcache
|
88
|
+
if cache.respond_to?(:get)
|
89
|
+
cache.get(key)
|
90
|
+
|
91
|
+
# `Cache#[key]` so `Hash` can be used
|
92
|
+
elsif cache.respond_to?(:[])
|
93
|
+
cache[key]
|
94
|
+
|
95
|
+
# `Cache#read(key)` for `ActiveSupport::Cache` support
|
96
|
+
elsif cache.respond_to?(:read)
|
97
|
+
cache.read(key)
|
98
|
+
|
99
|
+
else
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Low level cache setter for `key`. Checks a number of supported
|
105
|
+
# cache interfaces.
|
106
|
+
def cache_set(key, value)
|
107
|
+
# `Cache#set(key, value)` for Memcache
|
108
|
+
if cache.respond_to?(:set)
|
109
|
+
cache.set(key, value)
|
110
|
+
|
111
|
+
# `Cache#[key]=value` so `Hash` can be used
|
112
|
+
elsif cache.respond_to?(:[]=)
|
113
|
+
cache[key] = value
|
114
|
+
|
115
|
+
# `Cache#write(key, value)` for `ActiveSupport::Cache` support
|
116
|
+
elsif cache.respond_to?(:write)
|
117
|
+
cache.write(key, value)
|
118
|
+
end
|
119
|
+
|
120
|
+
value
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'tilt'
|
2
|
+
|
3
|
+
module Sprockets
|
4
|
+
# Some browsers have issues with stylesheets that contain multiple
|
5
|
+
# `@charset` definitions. The issue surfaces while using Sass since
|
6
|
+
# it inserts a `@charset` at the top of each file. Then Sprockets
|
7
|
+
# concatenates them together.
|
8
|
+
#
|
9
|
+
# The `CharsetNormalizer` processor strips out multiple `@charset`
|
10
|
+
# definitions.
|
11
|
+
#
|
12
|
+
# The current implementation is naive. It picks the first `@charset`
|
13
|
+
# it sees and strips the others. This works for most people because
|
14
|
+
# the other definitions are usually `UTF-8`. A more sophisticated
|
15
|
+
# approach would be to re-encode stylesheets with mixed encodings.
|
16
|
+
#
|
17
|
+
# This behavior can be disabled with:
|
18
|
+
#
|
19
|
+
# environment.unregister_bundle_processor 'text/css', Sprockets::CharsetNormalizer
|
20
|
+
#
|
21
|
+
class CharsetNormalizer < Tilt::Template
|
22
|
+
def prepare
|
23
|
+
end
|
24
|
+
|
25
|
+
def evaluate(context, locals, &block)
|
26
|
+
charset = nil
|
27
|
+
|
28
|
+
# Find and strip out any `@charset` definitions
|
29
|
+
filtered_data = data.gsub(/^@charset "([^"]+)";$/) {
|
30
|
+
charset ||= $1; ""
|
31
|
+
}
|
32
|
+
|
33
|
+
if charset
|
34
|
+
# If there was a charset, move it to the top
|
35
|
+
"@charset \"#{charset}\";#{filtered_data}"
|
36
|
+
else
|
37
|
+
data
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,217 @@
|
|
1
|
+
require 'base64'
|
2
|
+
require 'rack/utils'
|
3
|
+
require 'sprockets/errors'
|
4
|
+
require 'sprockets/utils'
|
5
|
+
require 'pathname'
|
6
|
+
require 'set'
|
7
|
+
|
8
|
+
module Sprockets
|
9
|
+
# `Context` provides helper methods to all `Tilt` processors. They
|
10
|
+
# are typically accessed by ERB templates. You can mix in custom
|
11
|
+
# helpers by injecting them into `Environment#context_class`. Do not
|
12
|
+
# mix them into `Context` directly.
|
13
|
+
#
|
14
|
+
# environment.instance_eval do
|
15
|
+
# include MyHelper
|
16
|
+
# def asset_url; end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# <%= asset_url "foo.png" %>
|
20
|
+
#
|
21
|
+
# The `Context` also collects dependencies declared by
|
22
|
+
# assets. See `DirectiveProcessor` for an example of this.
|
23
|
+
class Context
|
24
|
+
attr_reader :environment, :pathname
|
25
|
+
attr_reader :_required_paths, :_dependency_paths, :_dependency_assets
|
26
|
+
attr_writer :__LINE__
|
27
|
+
|
28
|
+
def initialize(environment, logical_path, pathname)
|
29
|
+
@environment = environment
|
30
|
+
@logical_path = logical_path
|
31
|
+
@pathname = pathname
|
32
|
+
@__LINE__ = nil
|
33
|
+
|
34
|
+
@_required_paths = []
|
35
|
+
@_dependency_paths = Set.new([pathname.to_s])
|
36
|
+
@_dependency_assets = Set.new
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the environment path that contains the file.
|
40
|
+
#
|
41
|
+
# If `app/javascripts` and `app/stylesheets` are in your path, and
|
42
|
+
# current file is `app/javascripts/foo/bar.js`, `root_path` would
|
43
|
+
# return `app/javascripts`.
|
44
|
+
def root_path
|
45
|
+
environment.paths.detect { |path| pathname.to_s[path] }
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns logical path without any file extensions.
|
49
|
+
#
|
50
|
+
# 'app/javascripts/application.js'
|
51
|
+
# # => 'application'
|
52
|
+
#
|
53
|
+
def logical_path
|
54
|
+
@logical_path[/^([^.]+)/, 0]
|
55
|
+
end
|
56
|
+
|
57
|
+
# Returns content type of file
|
58
|
+
#
|
59
|
+
# 'application/javascript'
|
60
|
+
# 'text/css'
|
61
|
+
#
|
62
|
+
def content_type
|
63
|
+
environment.content_type_of(pathname)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Given a logical path, `resolve` will find and return the fully
|
67
|
+
# expanded path. Relative paths will also be resolved. An optional
|
68
|
+
# `:content_type` restriction can be supplied to restrict the
|
69
|
+
# search.
|
70
|
+
#
|
71
|
+
# resolve("foo.js")
|
72
|
+
# # => "/path/to/app/javascripts/foo.js"
|
73
|
+
#
|
74
|
+
# resolve("./bar.js")
|
75
|
+
# # => "/path/to/app/javascripts/bar.js"
|
76
|
+
#
|
77
|
+
def resolve(path, options = {}, &block)
|
78
|
+
pathname = Pathname.new(path)
|
79
|
+
attributes = environment.attributes_for(pathname)
|
80
|
+
|
81
|
+
if pathname.absolute?
|
82
|
+
pathname
|
83
|
+
|
84
|
+
elsif content_type = options[:content_type]
|
85
|
+
content_type = self.content_type if content_type == :self
|
86
|
+
|
87
|
+
if attributes.format_extension
|
88
|
+
if content_type != attributes.content_type
|
89
|
+
raise ContentTypeMismatch, "#{path} is " +
|
90
|
+
"'#{attributes.content_type}', not '#{content_type}'"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
resolve(path) do |candidate|
|
95
|
+
if self.content_type == environment.content_type_of(candidate)
|
96
|
+
return candidate
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
raise FileNotFound, "couldn't find file '#{path}'"
|
101
|
+
else
|
102
|
+
environment.resolve(path, :base_path => self.pathname.dirname, &block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# `depend_on` allows you to state a dependency on a file without
|
107
|
+
# including it.
|
108
|
+
#
|
109
|
+
# This is used for caching purposes. Any changes made to
|
110
|
+
# the dependency file with invalidate the cache of the
|
111
|
+
# source file.
|
112
|
+
def depend_on(path)
|
113
|
+
@_dependency_paths << resolve(path).to_s
|
114
|
+
nil
|
115
|
+
end
|
116
|
+
|
117
|
+
# `depend_on_asset` allows you to state an asset dependency
|
118
|
+
# without including it.
|
119
|
+
#
|
120
|
+
# This is used for caching purposes. Any changes that would
|
121
|
+
# invalidate the dependency asset will invalidate the source
|
122
|
+
# file. Unlike `depend_on`, this will include recursively include
|
123
|
+
# the target asset's dependencies.
|
124
|
+
def depend_on_asset(path)
|
125
|
+
filename = resolve(path).to_s
|
126
|
+
@_dependency_assets << filename
|
127
|
+
nil
|
128
|
+
end
|
129
|
+
|
130
|
+
# `require_asset` declares `path` as a dependency of the file. The
|
131
|
+
# dependency will be inserted before the file and will only be
|
132
|
+
# included once.
|
133
|
+
#
|
134
|
+
# If ERB processing is enabled, you can use it to dynamically
|
135
|
+
# require assets.
|
136
|
+
#
|
137
|
+
# <%= require_asset "#{framework}.js" %>
|
138
|
+
#
|
139
|
+
def require_asset(path)
|
140
|
+
pathname = resolve(path, :content_type => :self)
|
141
|
+
depend_on_asset(pathname)
|
142
|
+
@_required_paths << pathname.to_s
|
143
|
+
nil
|
144
|
+
end
|
145
|
+
|
146
|
+
# Tests if target path is able to be safely required into the
|
147
|
+
# current concatenation.
|
148
|
+
def asset_requirable?(path)
|
149
|
+
pathname = resolve(path)
|
150
|
+
content_type = environment.content_type_of(pathname)
|
151
|
+
pathname.file? && (self.content_type.nil? || self.content_type == content_type)
|
152
|
+
end
|
153
|
+
|
154
|
+
# Reads `path` and runs processors on the file.
|
155
|
+
#
|
156
|
+
# This allows you to capture the result of an asset and include it
|
157
|
+
# directly in another.
|
158
|
+
#
|
159
|
+
# <%= evaluate "bar.js" %>
|
160
|
+
#
|
161
|
+
def evaluate(path, options = {})
|
162
|
+
pathname = resolve(path)
|
163
|
+
attributes = environment.attributes_for(pathname)
|
164
|
+
processors = options[:processors] || attributes.processors
|
165
|
+
|
166
|
+
if options[:data]
|
167
|
+
result = options[:data]
|
168
|
+
else
|
169
|
+
result = Sprockets::Utils.read_unicode(pathname)
|
170
|
+
end
|
171
|
+
|
172
|
+
processors.each do |processor|
|
173
|
+
begin
|
174
|
+
template = processor.new(pathname.to_s) { result }
|
175
|
+
result = template.render(self, {})
|
176
|
+
rescue Exception => e
|
177
|
+
annotate_exception! e
|
178
|
+
raise
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
result
|
183
|
+
end
|
184
|
+
|
185
|
+
# Returns a Base64-encoded `data:` URI with the contents of the
|
186
|
+
# asset at the specified path, and marks that path as a dependency
|
187
|
+
# of the current file.
|
188
|
+
#
|
189
|
+
# Use `asset_data_uri` from ERB with CSS or JavaScript assets:
|
190
|
+
#
|
191
|
+
# #logo { background: url(<%= asset_data_uri 'logo.png' %>) }
|
192
|
+
#
|
193
|
+
# $('<img>').attr('src', '<%= asset_data_uri 'avatar.jpg' %>')
|
194
|
+
#
|
195
|
+
def asset_data_uri(path)
|
196
|
+
depend_on(path)
|
197
|
+
asset = environment.find_asset(path)
|
198
|
+
base64 = Base64.encode64(asset.to_s).gsub(/\s+/, "")
|
199
|
+
"data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
|
200
|
+
end
|
201
|
+
|
202
|
+
private
|
203
|
+
# Annotates exception backtrace with the original template that
|
204
|
+
# the exception was raised in.
|
205
|
+
def annotate_exception!(exception)
|
206
|
+
location = pathname.to_s
|
207
|
+
location << ":#{@__LINE__}" if @__LINE__
|
208
|
+
|
209
|
+
exception.extend(Sprockets::EngineError)
|
210
|
+
exception.sprockets_annotation = " (in #{location})"
|
211
|
+
end
|
212
|
+
|
213
|
+
def logger
|
214
|
+
environment.logger
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Sprockets
|
2
|
+
# `Digest` is an internal mixin whose public methods are exposed on
|
3
|
+
# the `Environment` and `Index` classes.
|
4
|
+
module Digest
|
5
|
+
# Returns a `Digest` implementation class.
|
6
|
+
#
|
7
|
+
# Defaults to `Digest::MD5`.
|
8
|
+
def digest_class
|
9
|
+
@digest_class
|
10
|
+
end
|
11
|
+
|
12
|
+
# Assign a `Digest` implementation class. This maybe any Ruby
|
13
|
+
# `Digest::` implementation such as `Digest::MD5` or
|
14
|
+
# `Digest::SHA1`.
|
15
|
+
#
|
16
|
+
# environment.digest_class = Digest::SHA1
|
17
|
+
#
|
18
|
+
def digest_class=(klass)
|
19
|
+
expire_index!
|
20
|
+
@digest_class = klass
|
21
|
+
end
|
22
|
+
|
23
|
+
# The `Environment#version` is a custom value used for manually
|
24
|
+
# expiring all asset caches.
|
25
|
+
#
|
26
|
+
# Sprockets is able to track most file and directory changes and
|
27
|
+
# will take care of expiring the cache for you. However, its
|
28
|
+
# impossible to know when any custom helpers change that you mix
|
29
|
+
# into the `Context`.
|
30
|
+
#
|
31
|
+
# It would be wise to increment this value anytime you make a
|
32
|
+
# configuration change to the `Environment` object.
|
33
|
+
def version
|
34
|
+
@version
|
35
|
+
end
|
36
|
+
|
37
|
+
# Assign an environment version.
|
38
|
+
#
|
39
|
+
# environment.version = '2.0'
|
40
|
+
#
|
41
|
+
def version=(version)
|
42
|
+
expire_index!
|
43
|
+
@version = version
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns a `Digest` instance for the `Environment`.
|
47
|
+
#
|
48
|
+
# This value serves two purposes. If two `Environment`s have the
|
49
|
+
# same digest value they can be treated as equal. This is more
|
50
|
+
# useful for comparing environment states between processes rather
|
51
|
+
# than in the same. Two equal `Environment`s can share the same
|
52
|
+
# cached assets.
|
53
|
+
#
|
54
|
+
# The value also provides a seed digest for all `Asset`
|
55
|
+
# digests. Any change in the environment digest will affect all of
|
56
|
+
# its assets.
|
57
|
+
def digest
|
58
|
+
# Compute the initial digest using the implementation class. The
|
59
|
+
# Sprockets release version and custom environment version are
|
60
|
+
# mixed in. So any new releases will affect all your assets.
|
61
|
+
@digest ||= digest_class.new.update(VERSION).update(version.to_s)
|
62
|
+
|
63
|
+
# Returned a dupped copy so the caller can safely mutate it with `.update`
|
64
|
+
@digest.dup
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|