sprockets 2.3.2 → 3.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.
- checksums.yaml +7 -0
- data/LICENSE +2 -2
- data/README.md +332 -115
- data/bin/sprockets +8 -0
- data/lib/rake/sprocketstask.rb +25 -13
- data/lib/sprockets/asset.rb +143 -205
- data/lib/sprockets/autoload/closure.rb +7 -0
- data/lib/sprockets/autoload/coffee_script.rb +7 -0
- data/lib/sprockets/autoload/eco.rb +7 -0
- data/lib/sprockets/autoload/ejs.rb +7 -0
- data/lib/sprockets/autoload/sass.rb +7 -0
- data/lib/sprockets/autoload/uglifier.rb +7 -0
- data/lib/sprockets/autoload/yui.rb +7 -0
- data/lib/sprockets/autoload.rb +11 -0
- data/lib/sprockets/base.rb +49 -257
- data/lib/sprockets/bower.rb +58 -0
- data/lib/sprockets/bundle.rb +65 -0
- data/lib/sprockets/cache/file_store.rb +165 -14
- data/lib/sprockets/cache/memory_store.rb +66 -0
- data/lib/sprockets/cache/null_store.rb +46 -0
- data/lib/sprockets/cache.rb +234 -0
- data/lib/sprockets/cached_environment.rb +69 -0
- data/lib/sprockets/closure_compressor.rb +53 -0
- data/lib/sprockets/coffee_script_processor.rb +25 -0
- data/lib/sprockets/coffee_script_template.rb +6 -0
- data/lib/sprockets/compressing.rb +74 -0
- data/lib/sprockets/configuration.rb +83 -0
- data/lib/sprockets/context.rb +125 -131
- data/lib/sprockets/dependencies.rb +73 -0
- data/lib/sprockets/digest_utils.rb +156 -0
- data/lib/sprockets/directive_processor.rb +209 -211
- data/lib/sprockets/eco_processor.rb +32 -0
- data/lib/sprockets/eco_template.rb +3 -35
- data/lib/sprockets/ejs_processor.rb +31 -0
- data/lib/sprockets/ejs_template.rb +3 -34
- data/lib/sprockets/encoding_utils.rb +258 -0
- data/lib/sprockets/engines.rb +45 -38
- data/lib/sprockets/environment.rb +17 -67
- data/lib/sprockets/erb_processor.rb +30 -0
- data/lib/sprockets/erb_template.rb +6 -0
- data/lib/sprockets/errors.rb +6 -13
- data/lib/sprockets/file_reader.rb +15 -0
- data/lib/sprockets/http_utils.rb +115 -0
- data/lib/sprockets/jst_processor.rb +35 -19
- data/lib/sprockets/legacy.rb +314 -0
- data/lib/sprockets/legacy_proc_processor.rb +35 -0
- data/lib/sprockets/legacy_tilt_processor.rb +29 -0
- data/lib/sprockets/loader.rb +176 -0
- data/lib/sprockets/manifest.rb +179 -98
- data/lib/sprockets/manifest_utils.rb +45 -0
- data/lib/sprockets/mime.rb +114 -32
- data/lib/sprockets/path_dependency_utils.rb +85 -0
- data/lib/sprockets/path_digest_utils.rb +47 -0
- data/lib/sprockets/path_utils.rb +282 -0
- data/lib/sprockets/paths.rb +81 -0
- data/lib/sprockets/processing.rb +157 -189
- data/lib/sprockets/processor_utils.rb +103 -0
- data/lib/sprockets/resolve.rb +208 -0
- data/lib/sprockets/sass_cache_store.rb +19 -15
- data/lib/sprockets/sass_compressor.rb +59 -0
- data/lib/sprockets/sass_functions.rb +2 -0
- data/lib/sprockets/sass_importer.rb +2 -29
- data/lib/sprockets/sass_processor.rb +285 -0
- data/lib/sprockets/sass_template.rb +4 -44
- data/lib/sprockets/server.rb +109 -84
- data/lib/sprockets/transformers.rb +145 -0
- data/lib/sprockets/uglifier_compressor.rb +63 -0
- data/lib/sprockets/uri_utils.rb +190 -0
- data/lib/sprockets/utils.rb +193 -44
- data/lib/sprockets/version.rb +1 -1
- data/lib/sprockets/yui_compressor.rb +65 -0
- data/lib/sprockets.rb +144 -53
- metadata +248 -238
- data/lib/sprockets/asset_attributes.rb +0 -126
- data/lib/sprockets/bundled_asset.rb +0 -79
- data/lib/sprockets/caching.rb +0 -96
- data/lib/sprockets/charset_normalizer.rb +0 -41
- 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/scss_template.rb +0 -13
- data/lib/sprockets/static_asset.rb +0 -57
- data/lib/sprockets/trail.rb +0 -90
data/lib/sprockets/context.rb
CHANGED
@@ -1,15 +1,13 @@
|
|
1
|
-
require 'base64'
|
2
|
-
require 'rack/utils'
|
3
|
-
require 'sprockets/errors'
|
4
|
-
require 'sprockets/utils'
|
5
1
|
require 'pathname'
|
2
|
+
require 'rack/utils'
|
6
3
|
require 'set'
|
4
|
+
require 'sprockets/errors'
|
7
5
|
|
8
6
|
module Sprockets
|
9
|
-
# `Context` provides helper methods to all
|
10
|
-
# are typically accessed by ERB templates. You can mix in custom
|
11
|
-
#
|
12
|
-
#
|
7
|
+
# Deprecated: `Context` provides helper methods to all processors.
|
8
|
+
# They are typically accessed by ERB templates. You can mix in custom helpers
|
9
|
+
# by injecting them into `Environment#context_class`. Do not mix them into
|
10
|
+
# `Context` directly.
|
13
11
|
#
|
14
12
|
# environment.context_class.class_eval do
|
15
13
|
# include MyHelper
|
@@ -21,88 +19,86 @@ module Sprockets
|
|
21
19
|
# The `Context` also collects dependencies declared by
|
22
20
|
# assets. See `DirectiveProcessor` for an example of this.
|
23
21
|
class Context
|
24
|
-
attr_reader :environment, :pathname
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
def initialize(
|
30
|
-
@environment = environment
|
31
|
-
@
|
32
|
-
@
|
33
|
-
@
|
22
|
+
attr_reader :environment, :filename, :pathname
|
23
|
+
|
24
|
+
# Deprecated
|
25
|
+
attr_accessor :__LINE__
|
26
|
+
|
27
|
+
def initialize(input)
|
28
|
+
@environment = input[:environment]
|
29
|
+
@metadata = input[:metadata]
|
30
|
+
@load_path = input[:load_path]
|
31
|
+
@logical_path = input[:name]
|
32
|
+
@filename = input[:filename]
|
33
|
+
@dirname = File.dirname(@filename)
|
34
|
+
@pathname = Pathname.new(@filename)
|
35
|
+
@content_type = input[:content_type]
|
36
|
+
|
37
|
+
@required = Set.new(@metadata[:required])
|
38
|
+
@stubbed = Set.new(@metadata[:stubbed])
|
39
|
+
@links = Set.new(@metadata[:links])
|
40
|
+
@dependencies = Set.new(input[:metadata][:dependencies])
|
41
|
+
end
|
34
42
|
|
35
|
-
|
36
|
-
@
|
37
|
-
|
38
|
-
|
43
|
+
def metadata
|
44
|
+
{ required: @required,
|
45
|
+
stubbed: @stubbed,
|
46
|
+
links: @links,
|
47
|
+
dependencies: @dependencies }
|
39
48
|
end
|
40
49
|
|
41
50
|
# Returns the environment path that contains the file.
|
42
51
|
#
|
43
52
|
# If `app/javascripts` and `app/stylesheets` are in your path, and
|
44
|
-
# current file is `app/javascripts/foo/bar.js`, `
|
53
|
+
# current file is `app/javascripts/foo/bar.js`, `load_path` would
|
45
54
|
# return `app/javascripts`.
|
46
|
-
|
47
|
-
|
48
|
-
end
|
55
|
+
attr_reader :load_path
|
56
|
+
alias_method :root_path, :load_path
|
49
57
|
|
50
58
|
# Returns logical path without any file extensions.
|
51
59
|
#
|
52
60
|
# 'app/javascripts/application.js'
|
53
61
|
# # => 'application'
|
54
62
|
#
|
55
|
-
|
56
|
-
@logical_path.chomp(File.extname(@logical_path))
|
57
|
-
end
|
63
|
+
attr_reader :logical_path
|
58
64
|
|
59
65
|
# Returns content type of file
|
60
66
|
#
|
61
67
|
# 'application/javascript'
|
62
68
|
# 'text/css'
|
63
69
|
#
|
64
|
-
|
65
|
-
environment.content_type_of(pathname)
|
66
|
-
end
|
70
|
+
attr_reader :content_type
|
67
71
|
|
68
|
-
# Given a logical path, `resolve` will find and return
|
69
|
-
#
|
70
|
-
#
|
71
|
-
# search.
|
72
|
+
# Public: Given a logical path, `resolve` will find and return an Asset URI.
|
73
|
+
# Relative paths will also be resolved. An accept type maybe given to
|
74
|
+
# restrict the search.
|
72
75
|
#
|
73
76
|
# resolve("foo.js")
|
74
|
-
# # => "
|
77
|
+
# # => "file:///path/to/app/javascripts/foo.js?type=application/javascript"
|
75
78
|
#
|
76
79
|
# resolve("./bar.js")
|
77
|
-
# # => "
|
80
|
+
# # => "file:///path/to/app/javascripts/bar.js?type=application/javascript"
|
78
81
|
#
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
if attributes.format_extension
|
90
|
-
if content_type != attributes.content_type
|
91
|
-
raise ContentTypeMismatch, "#{path} is " +
|
92
|
-
"'#{attributes.content_type}', not '#{content_type}'"
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
resolve(path) do |candidate|
|
97
|
-
if self.content_type == environment.content_type_of(candidate)
|
98
|
-
return candidate
|
99
|
-
end
|
100
|
-
end
|
82
|
+
# path - String logical or absolute path
|
83
|
+
# options
|
84
|
+
# accept - String content accept type
|
85
|
+
#
|
86
|
+
# Returns an Asset URI String.
|
87
|
+
def resolve(path, options = {})
|
88
|
+
uri, deps = environment.resolve!(path, options.merge(base_path: @dirname))
|
89
|
+
@dependencies.merge(deps)
|
90
|
+
uri
|
91
|
+
end
|
101
92
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
93
|
+
# Public: Load Asset by AssetURI and track it as a dependency.
|
94
|
+
#
|
95
|
+
# uri - AssetURI
|
96
|
+
#
|
97
|
+
# Returns Asset.
|
98
|
+
def load(uri)
|
99
|
+
asset = environment.load(uri)
|
100
|
+
@dependencies.merge(asset.metadata[:dependencies])
|
101
|
+
asset
|
106
102
|
end
|
107
103
|
|
108
104
|
# `depend_on` allows you to state a dependency on a file without
|
@@ -112,7 +108,11 @@ module Sprockets
|
|
112
108
|
# the dependency file with invalidate the cache of the
|
113
109
|
# source file.
|
114
110
|
def depend_on(path)
|
115
|
-
|
111
|
+
if environment.absolute_path?(path) && environment.directory?(path)
|
112
|
+
@dependencies << environment.build_file_digest_uri(path)
|
113
|
+
else
|
114
|
+
resolve(path, compat: false)
|
115
|
+
end
|
116
116
|
nil
|
117
117
|
end
|
118
118
|
|
@@ -124,9 +124,7 @@ module Sprockets
|
|
124
124
|
# file. Unlike `depend_on`, this will include recursively include
|
125
125
|
# the target asset's dependencies.
|
126
126
|
def depend_on_asset(path)
|
127
|
-
|
128
|
-
@_dependency_assets << filename
|
129
|
-
nil
|
127
|
+
load(resolve(path, compat: false))
|
130
128
|
end
|
131
129
|
|
132
130
|
# `require_asset` declares `path` as a dependency of the file. The
|
@@ -139,9 +137,7 @@ module Sprockets
|
|
139
137
|
# <%= require_asset "#{framework}.js" %>
|
140
138
|
#
|
141
139
|
def require_asset(path)
|
142
|
-
|
143
|
-
depend_on_asset(pathname)
|
144
|
-
@_required_paths << pathname.to_s
|
140
|
+
@required << resolve(path, accept: @content_type, pipeline: :self, compat: false)
|
145
141
|
nil
|
146
142
|
end
|
147
143
|
|
@@ -149,55 +145,19 @@ module Sprockets
|
|
149
145
|
# `path` must be an asset which may or may not already be included
|
150
146
|
# in the bundle.
|
151
147
|
def stub_asset(path)
|
152
|
-
@
|
148
|
+
@stubbed << resolve(path, accept: @content_type, pipeline: :self, compat: false)
|
153
149
|
nil
|
154
150
|
end
|
155
151
|
|
156
|
-
#
|
157
|
-
#
|
158
|
-
|
159
|
-
pathname = resolve(path)
|
160
|
-
content_type = environment.content_type_of(pathname)
|
161
|
-
stat = environment.stat(path)
|
162
|
-
return false unless stat && stat.file?
|
163
|
-
self.content_type.nil? || self.content_type == content_type
|
164
|
-
end
|
165
|
-
|
166
|
-
# Reads `path` and runs processors on the file.
|
152
|
+
# `link_asset` declares an external dependency on an asset without directly
|
153
|
+
# including it. The target asset is returned from this function making it
|
154
|
+
# easy to construct a link to it.
|
167
155
|
#
|
168
|
-
#
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
def evaluate(path, options = {})
|
174
|
-
pathname = resolve(path)
|
175
|
-
attributes = environment.attributes_for(pathname)
|
176
|
-
processors = options[:processors] || attributes.processors
|
177
|
-
|
178
|
-
if options[:data]
|
179
|
-
result = options[:data]
|
180
|
-
else
|
181
|
-
if environment.respond_to?(:default_external_encoding)
|
182
|
-
mime_type = environment.mime_types(pathname.extname)
|
183
|
-
encoding = environment.encoding_for_mime_type(mime_type)
|
184
|
-
result = Sprockets::Utils.read_unicode(pathname, encoding)
|
185
|
-
else
|
186
|
-
result = Sprockets::Utils.read_unicode(pathname)
|
187
|
-
end
|
188
|
-
end
|
189
|
-
|
190
|
-
processors.each do |processor|
|
191
|
-
begin
|
192
|
-
template = processor.new(pathname.to_s) { result }
|
193
|
-
result = template.render(self, {})
|
194
|
-
rescue Exception => e
|
195
|
-
annotate_exception! e
|
196
|
-
raise
|
197
|
-
end
|
198
|
-
end
|
199
|
-
|
200
|
-
result
|
156
|
+
# Returns an Asset or nil.
|
157
|
+
def link_asset(path)
|
158
|
+
asset = depend_on_asset(path)
|
159
|
+
@links << asset.uri
|
160
|
+
asset
|
201
161
|
end
|
202
162
|
|
203
163
|
# Returns a Base64-encoded `data:` URI with the contents of the
|
@@ -211,25 +171,59 @@ module Sprockets
|
|
211
171
|
# $('<img>').attr('src', '<%= asset_data_uri 'avatar.jpg' %>')
|
212
172
|
#
|
213
173
|
def asset_data_uri(path)
|
214
|
-
depend_on_asset(path)
|
215
|
-
|
216
|
-
base64
|
217
|
-
"data:#{asset.content_type};base64,#{Rack::Utils.escape(base64)}"
|
174
|
+
asset = depend_on_asset(path)
|
175
|
+
data = EncodingUtils.base64(asset.source)
|
176
|
+
"data:#{asset.content_type};base64,#{Rack::Utils.escape(data)}"
|
218
177
|
end
|
219
178
|
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
179
|
+
# Expands logical path to full url to asset.
|
180
|
+
#
|
181
|
+
# NOTE: This helper is currently not implemented and should be
|
182
|
+
# customized by the application. Though, in the future, some
|
183
|
+
# basics implemention may be provided with different methods that
|
184
|
+
# are required to be overridden.
|
185
|
+
def asset_path(path, options = {})
|
186
|
+
message = <<-EOS
|
187
|
+
Custom asset_path helper is not implemented
|
226
188
|
|
227
|
-
|
228
|
-
exception.sprockets_annotation = " (in #{location})"
|
229
|
-
end
|
189
|
+
Extend your environment context with a custom method.
|
230
190
|
|
231
|
-
|
232
|
-
|
191
|
+
environment.context_class.class_eval do
|
192
|
+
def asset_path(path, options = {})
|
233
193
|
end
|
194
|
+
end
|
195
|
+
EOS
|
196
|
+
raise NotImplementedError, message
|
197
|
+
end
|
198
|
+
|
199
|
+
# Expand logical image asset path.
|
200
|
+
def image_path(path)
|
201
|
+
asset_path(path, type: :image)
|
202
|
+
end
|
203
|
+
|
204
|
+
# Expand logical video asset path.
|
205
|
+
def video_path(path)
|
206
|
+
asset_path(path, type: :video)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Expand logical audio asset path.
|
210
|
+
def audio_path(path)
|
211
|
+
asset_path(path, type: :audio)
|
212
|
+
end
|
213
|
+
|
214
|
+
# Expand logical font asset path.
|
215
|
+
def font_path(path)
|
216
|
+
asset_path(path, type: :font)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Expand logical javascript asset path.
|
220
|
+
def javascript_path(path)
|
221
|
+
asset_path(path, type: :javascript)
|
222
|
+
end
|
223
|
+
|
224
|
+
# Expand logical stylesheet asset path.
|
225
|
+
def stylesheet_path(path)
|
226
|
+
asset_path(path, type: :stylesheet)
|
227
|
+
end
|
234
228
|
end
|
235
229
|
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'sprockets/digest_utils'
|
2
|
+
require 'sprockets/path_digest_utils'
|
3
|
+
require 'sprockets/uri_utils'
|
4
|
+
|
5
|
+
module Sprockets
|
6
|
+
# `Dependencies` is an internal mixin whose public methods are exposed on the
|
7
|
+
# `Environment` and `CachedEnvironment` classes.
|
8
|
+
module Dependencies
|
9
|
+
include DigestUtils, PathDigestUtils, URIUtils
|
10
|
+
|
11
|
+
# Public: Mapping dependency schemes to resolver functions.
|
12
|
+
#
|
13
|
+
# key - String scheme
|
14
|
+
# value - Proc.call(Environment, String)
|
15
|
+
#
|
16
|
+
# Returns Hash.
|
17
|
+
def dependency_resolvers
|
18
|
+
config[:dependency_resolvers]
|
19
|
+
end
|
20
|
+
|
21
|
+
# Public: Default set of dependency URIs for assets.
|
22
|
+
#
|
23
|
+
# Returns Set of String URIs.
|
24
|
+
def dependencies
|
25
|
+
config[:dependencies]
|
26
|
+
end
|
27
|
+
|
28
|
+
# Public: Register new dependency URI resolver.
|
29
|
+
#
|
30
|
+
# scheme - String scheme
|
31
|
+
# block -
|
32
|
+
# environment - Environment
|
33
|
+
# uri - String dependency URI
|
34
|
+
#
|
35
|
+
# Returns nothing.
|
36
|
+
def register_dependency_resolver(scheme, &block)
|
37
|
+
self.config = hash_reassoc(config, :dependency_resolvers) do |hash|
|
38
|
+
hash.merge(scheme => block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Public: Add environmental dependency inheirted by all assets.
|
43
|
+
#
|
44
|
+
# uri - String dependency URI
|
45
|
+
#
|
46
|
+
# Returns nothing.
|
47
|
+
def add_dependency(uri)
|
48
|
+
self.config = hash_reassoc(config, :dependencies) do |set|
|
49
|
+
set + Set.new([uri])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
alias_method :depend_on, :add_dependency
|
53
|
+
|
54
|
+
# Internal: Resolve set of dependency URIs.
|
55
|
+
#
|
56
|
+
# Returns Array of resolved Objects.
|
57
|
+
def resolve_dependencies(uris)
|
58
|
+
uris.map { |uri| resolve_dependency(uri) }
|
59
|
+
end
|
60
|
+
|
61
|
+
# Internal: Resolve dependency URIs.
|
62
|
+
#
|
63
|
+
# Returns resolved Object.
|
64
|
+
def resolve_dependency(str)
|
65
|
+
scheme = str[/([^:]+)/, 1]
|
66
|
+
if resolver = config[:dependency_resolvers][scheme]
|
67
|
+
resolver.call(self, str)
|
68
|
+
else
|
69
|
+
nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'digest/sha1'
|
3
|
+
require 'digest/sha2'
|
4
|
+
require 'set'
|
5
|
+
|
6
|
+
module Sprockets
|
7
|
+
# Internal: Hash functions and digest related utilities. Mixed into
|
8
|
+
# Environment.
|
9
|
+
module DigestUtils
|
10
|
+
extend self
|
11
|
+
|
12
|
+
# Internal: Default digest class.
|
13
|
+
#
|
14
|
+
# Returns a Digest::Base subclass.
|
15
|
+
def digest_class
|
16
|
+
Digest::SHA256
|
17
|
+
end
|
18
|
+
|
19
|
+
# Internal: Maps digest bytesize to the digest class.
|
20
|
+
DIGEST_SIZES = {
|
21
|
+
16 => Digest::MD5,
|
22
|
+
20 => Digest::SHA1,
|
23
|
+
32 => Digest::SHA256,
|
24
|
+
48 => Digest::SHA384,
|
25
|
+
64 => Digest::SHA512
|
26
|
+
}
|
27
|
+
|
28
|
+
# Internal: Detect digest class hash algorithm for digest bytes.
|
29
|
+
#
|
30
|
+
# While not elegant, all the supported digests have a unique bytesize.
|
31
|
+
#
|
32
|
+
# Returns Digest::Base or nil.
|
33
|
+
def detect_digest_class(bytes)
|
34
|
+
DIGEST_SIZES[bytes.bytesize]
|
35
|
+
end
|
36
|
+
|
37
|
+
# Internal: Generate a hexdigest for a nested JSON serializable object.
|
38
|
+
#
|
39
|
+
# This is used for generating cache keys, so its pretty important its
|
40
|
+
# wicked fast. Microbenchmarks away!
|
41
|
+
#
|
42
|
+
# obj - A JSON serializable object.
|
43
|
+
#
|
44
|
+
# Returns a String digest of the object.
|
45
|
+
def digest(obj)
|
46
|
+
digest = digest_class.new
|
47
|
+
queue = [obj]
|
48
|
+
|
49
|
+
while queue.length > 0
|
50
|
+
obj = queue.shift
|
51
|
+
klass = obj.class
|
52
|
+
|
53
|
+
if klass == String
|
54
|
+
digest << obj
|
55
|
+
elsif klass == Symbol
|
56
|
+
digest << 'Symbol'
|
57
|
+
digest << obj.to_s
|
58
|
+
elsif klass == Fixnum
|
59
|
+
digest << 'Fixnum'
|
60
|
+
digest << obj.to_s
|
61
|
+
elsif klass == Bignum
|
62
|
+
digest << 'Bignum'
|
63
|
+
digest << obj.to_s
|
64
|
+
elsif klass == TrueClass
|
65
|
+
digest << 'TrueClass'
|
66
|
+
elsif klass == FalseClass
|
67
|
+
digest << 'FalseClass'
|
68
|
+
elsif klass == NilClass
|
69
|
+
digest << 'NilClass'
|
70
|
+
elsif klass == Array
|
71
|
+
digest << 'Array'
|
72
|
+
queue.concat(obj)
|
73
|
+
elsif klass == Hash
|
74
|
+
digest << 'Hash'
|
75
|
+
queue.concat(obj.sort)
|
76
|
+
elsif klass == Set
|
77
|
+
digest << 'Set'
|
78
|
+
queue.concat(obj.to_a)
|
79
|
+
elsif klass == Encoding
|
80
|
+
digest << 'Encoding'
|
81
|
+
digest << obj.name
|
82
|
+
else
|
83
|
+
raise TypeError, "couldn't digest #{klass}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
digest.digest
|
88
|
+
end
|
89
|
+
|
90
|
+
# Internal: Pack a binary digest to a hex encoded string.
|
91
|
+
#
|
92
|
+
# bin - String bytes
|
93
|
+
#
|
94
|
+
# Returns hex String.
|
95
|
+
def pack_hexdigest(bin)
|
96
|
+
bin.unpack('H*').first
|
97
|
+
end
|
98
|
+
|
99
|
+
# Internal: Pack a binary digest to a base64 encoded string.
|
100
|
+
#
|
101
|
+
# bin - String bytes
|
102
|
+
#
|
103
|
+
# Returns base64 String.
|
104
|
+
def pack_base64digest(bin)
|
105
|
+
[bin].pack('m0')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Internal: Pack a binary digest to a urlsafe base64 encoded string.
|
109
|
+
#
|
110
|
+
# bin - String bytes
|
111
|
+
#
|
112
|
+
# Returns urlsafe base64 String.
|
113
|
+
def pack_urlsafe_base64digest(bin)
|
114
|
+
str = pack_base64digest(bin)
|
115
|
+
str.tr!('+/'.freeze, '-_'.freeze)
|
116
|
+
str.tr!('='.freeze, ''.freeze)
|
117
|
+
str
|
118
|
+
end
|
119
|
+
|
120
|
+
# Internal: Maps digest class to the named information hash algorithm name.
|
121
|
+
#
|
122
|
+
# http://www.iana.org/assignments/named-information/named-information.xhtml
|
123
|
+
NI_HASH_ALGORITHMS = {
|
124
|
+
Digest::SHA256 => 'sha-256'.freeze,
|
125
|
+
Digest::SHA384 => 'sha-384'.freeze,
|
126
|
+
Digest::SHA512 => 'sha-512'.freeze
|
127
|
+
}
|
128
|
+
|
129
|
+
# Internal: Generate a "named information" URI for use in the `integrity`
|
130
|
+
# attribute of an asset tag as per the subresource integrity specification.
|
131
|
+
#
|
132
|
+
# digest - The String byte digest of the asset content.
|
133
|
+
# content_type - The content-type the asset will be served with. This *must*
|
134
|
+
# be accurate if provided. Otherwise, subresource integrity
|
135
|
+
# will block the loading of the asset.
|
136
|
+
#
|
137
|
+
# Returns a String or nil if hash algorithm is incompatible.
|
138
|
+
def integrity_uri(digest, content_type = nil)
|
139
|
+
case digest
|
140
|
+
when Digest::Base
|
141
|
+
digest_class = digest.class
|
142
|
+
digest = digest.digest
|
143
|
+
when String
|
144
|
+
digest_class = DIGEST_SIZES[digest.bytesize]
|
145
|
+
else
|
146
|
+
raise TypeError, "unknown digest: #{digest.inspect}"
|
147
|
+
end
|
148
|
+
|
149
|
+
if hash_name = NI_HASH_ALGORITHMS[digest_class]
|
150
|
+
uri = "ni:///#{hash_name};#{pack_urlsafe_base64digest(digest)}"
|
151
|
+
uri << "?ct=#{content_type}" if content_type
|
152
|
+
uri
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|