sprockets 3.0.0.beta.1 → 3.0.0.beta.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 221043e6f00af1601780ca6aeb8c68911c1eeab6
4
- data.tar.gz: 480adcfe9d59b1381dc000142ca7c4df6fc69a08
3
+ metadata.gz: 2dcb1bab6dcd806bd08e5f467aa057cd75004be8
4
+ data.tar.gz: 86037ea057923f8b2afd7dbffdea6d72f3596d02
5
5
  SHA512:
6
- metadata.gz: e1260c1e89442a746a8c5bf095cb72fc2fb04115dadec6874147b5a9794bea557255cf429af883e9f1216c6dee84ed2da3019ceb1a5ccd1dcbc14166058ceb24
7
- data.tar.gz: 0eaa3ace51ff6477b1b411fc2b4bfc67f9e3a268b088dc2c6fc1848ed5ffe9939ac481676324107be54e53abe835c0b4b2cb44a60c34083557c5d1458fad480b
6
+ metadata.gz: ce12f8ec442a3d4a3cef5352f12a6c83151ff4dd717ce0fb94d31811ca9f3c80953a3f52e3f582eb5ecb42d0af05d2e5320fe925c3c55e4b8b61fd6280ff4ef8
7
+ data.tar.gz: fbe0b9468d6e81a596517d4b4ecda0faf34ef84027ac7340ea2b603bc7635585ed2f93fa7e215e9c1062df1990c46222aefefd97caa4ba54a2dfcafa68c21811
data/README.md CHANGED
@@ -394,6 +394,10 @@ submit a pull request.
394
394
  idea](https://issues.apache.org/bugzilla/show_bug.cgi?id=39727)
395
395
  * Added linked or referenced assets. When an asset is compiled, any of its links
396
396
  will be compiled as well.
397
+ * Add Asset integrity attribute for Subresource Integrity
398
+ * Default digest changed to SHA256. Configuring digest_class is deprecated.
399
+ * Rename Asset#digest to Asset#hexdigest. Asset#digest is deprecated and will
400
+ return a raw byte String in 4.x.
397
401
 
398
402
  **2.12.2** (September 5, 2014)
399
403
 
@@ -29,6 +29,7 @@ module Sprockets
29
29
  autoload :AssetURI, 'sprockets/asset_uri'
30
30
  autoload :Cache, 'sprockets/cache'
31
31
  autoload :ContentTypeMismatch, 'sprockets/errors'
32
+ autoload :DigestUtils, 'sprockets/digest_utils'
32
33
  autoload :EncodingUtils, 'sprockets/encoding_utils'
33
34
  autoload :Error, 'sprockets/errors'
34
35
  autoload :FileNotFound, 'sprockets/errors'
@@ -58,8 +59,8 @@ module Sprockets
58
59
  @version = ''
59
60
 
60
61
  # Set the default digest
61
- require 'digest/sha1'
62
- @digest_class = Digest::SHA1
62
+ require 'digest/sha2'
63
+ @digest_class = Digest::SHA256
63
64
 
64
65
  require 'logger'
65
66
  @logger = Logger.new($stderr)
@@ -1,3 +1,4 @@
1
+ require 'base64'
1
2
  require 'fileutils'
2
3
  require 'pathname'
3
4
 
@@ -49,7 +50,7 @@ module Sprockets
49
50
 
50
51
  # Internal: Unique asset object ID.
51
52
  #
52
- # Returns String SHA1 String.
53
+ # Returns a String.
53
54
  attr_reader :id
54
55
 
55
56
  # Public: Internal URI to lookup asset by.
@@ -65,7 +66,7 @@ module Sprockets
65
66
  #
66
67
  # Returns String.
67
68
  def digest_path
68
- logical_path.sub(/\.(\w+)$/) { |ext| "-#{digest}#{ext}" }
69
+ logical_path.sub(/\.(\w+)$/) { |ext| "-#{etag}#{ext}" }
69
70
  end
70
71
 
71
72
  # Public: Returns String MIME type of asset. Returns nil if type is unknown.
@@ -165,10 +166,25 @@ module Sprockets
165
166
  end
166
167
 
167
168
  # Public: Returns String hexdigest of source.
168
- attr_reader :digest
169
+ def hexdigest
170
+ DigestUtils.pack_hexdigest(@digest)
171
+ end
172
+
173
+ # Deprecated: Returns String hexdigest of source.
174
+ #
175
+ # In 4.x this will be changed to return a raw Digest byte String.
176
+ alias_method :digest, :hexdigest
169
177
 
170
178
  # Pubic: ETag String of Asset.
171
- alias_method :etag, :digest
179
+ alias_method :etag, :hexdigest
180
+
181
+ # Public: Returns String base64 digest of source.
182
+ def base64digest
183
+ DigestUtils.pack_base64digest(@digest)
184
+ end
185
+
186
+ # Public: A "named information" URL for subresource integrity.
187
+ attr_reader :integrity
172
188
 
173
189
  # Public: Add enumerator to allow `Asset` instances to be used as Rack
174
190
  # compatible body objects.
@@ -8,7 +8,7 @@ require 'sprockets/server'
8
8
  module Sprockets
9
9
  # `Base` class for `Environment` and `Cached`.
10
10
  class Base
11
- include PathUtils, HTTPUtils
11
+ include PathUtils, HTTPUtils, DigestUtils
12
12
  include Configuration
13
13
  include Server
14
14
  include Resolve
@@ -33,39 +33,37 @@ module Sprockets
33
33
  end
34
34
  alias_method :index, :cached
35
35
 
36
- # Internal: Compute hexdigest for path.
36
+ # Internal: Compute digest for path.
37
37
  #
38
38
  # path - String filename or directory path.
39
39
  #
40
- # Returns a String SHA1 hexdigest or nil.
41
- def file_hexdigest(path)
40
+ # Returns a String digest or nil.
41
+ def file_digest(path)
42
42
  if stat = self.stat(path)
43
43
  # Caveat: Digests are cached by the path's current mtime. Its possible
44
44
  # for a files contents to have changed and its mtime to have been
45
45
  # negligently reset thus appearing as if the file hasn't changed on
46
46
  # disk. Also, the mtime is only read to the nearest second. Its
47
47
  # also possible the file was updated more than once in a given second.
48
- cache.fetch(['file_hexdigest', path, stat.mtime.to_i]) do
48
+ cache.fetch(['file_digest', path, stat.mtime.to_i]) do
49
49
  if stat.directory?
50
50
  # If its a directive, digest the list of filenames
51
- Digest::SHA1.hexdigest(self.entries(path).join(','))
51
+ digest_class.digest(self.entries(path).join(','))
52
52
  elsif stat.file?
53
53
  # If its a file, digest the contents
54
- Digest::SHA1.file(path.to_s).hexdigest
54
+ digest_class.file(path.to_s).digest
55
55
  end
56
56
  end
57
57
  end
58
58
  end
59
59
 
60
- # Internal: Compute hexdigest for a set of paths.
60
+ # Internal: Compute digest for a set of paths.
61
61
  #
62
62
  # paths - Array of filename or directory paths.
63
63
  #
64
- # Returns a String SHA1 hexdigest.
65
- def dependencies_hexdigest(paths)
66
- digest = Digest::SHA1.new
67
- paths.each { |path| digest.update(file_hexdigest(path).to_s) }
68
- digest.hexdigest
64
+ # Returns a String digest.
65
+ def files_digest(paths)
66
+ digest(paths.map { |path| file_digest(path) })
69
67
  end
70
68
 
71
69
  # Find asset by logical path or expanded path.
@@ -191,16 +189,18 @@ module Sprockets
191
189
  asset.merge!({
192
190
  encoding: Encoding::BINARY,
193
191
  length: self.stat(asset[:filename]).size,
194
- digest: digest_class.file(asset[:filename]).hexdigest,
192
+ digest: file_digest(asset[:filename]),
195
193
  metadata: {}
196
194
  })
197
195
  end
198
196
 
199
197
  metadata = asset[:metadata]
200
198
  metadata[:dependency_paths] = Set.new(metadata[:dependency_paths]).merge([asset[:filename]])
201
- metadata[:dependency_digest] = dependencies_hexdigest(metadata[:dependency_paths])
199
+ metadata[:dependency_sources_digest] = files_digest(metadata[:dependency_paths])
202
200
 
203
- asset[:id] = Utils.hexdigest(asset)
201
+ asset[:integrity] = integrity_uri(asset[:digest], asset[:content_type])
202
+
203
+ asset[:id] = pack_hexdigest(digest(asset))
204
204
  asset[:uri] = AssetURI.build(filename, params.merge(id: asset[:id]))
205
205
 
206
206
  # TODO: Avoid tracking Asset mtime
@@ -1,4 +1,4 @@
1
- require 'sprockets/utils'
1
+ require 'sprockets/digest_utils'
2
2
  require 'logger'
3
3
 
4
4
  module Sprockets
@@ -142,7 +142,7 @@ module Sprockets
142
142
  #
143
143
  # Returns a String with a length less than 250 characters.
144
144
  def expand_key(key)
145
- "sprockets/v#{VERSION}/#{Utils.hexdigest(key)}"
145
+ "sprockets/v#{VERSION}/#{DigestUtils.pack_urlsafe_base64digest(DigestUtils.digest(key))}"
146
146
  end
147
147
 
148
148
  PEEK_SIZE = 100
@@ -158,7 +158,7 @@ module Sprockets
158
158
  key.each { |k| str << peek_key(k) }
159
159
  str.join(':')[0, PEEK_SIZE]
160
160
  else
161
- peek_key(Utils.hexdigest(key))
161
+ peek_key(DigestUtils.pack_urlsafe_base64digest(DigestUtils.digest(key)))
162
162
  end
163
163
  end
164
164
 
@@ -14,10 +14,10 @@ module Sprockets
14
14
  def initialize(environment)
15
15
  initialize_configuration(environment)
16
16
 
17
- @cache = environment.cache
18
- @stats = Hash.new { |h, k| h[k] = _stat(k) }
19
- @entries = Hash.new { |h, k| h[k] = _entries(k) }
20
- @hexdigests = Hash.new { |h, k| h[k] = _file_hexdigest(k) }
17
+ @cache = environment.cache
18
+ @stats = Hash.new { |h, k| h[k] = _stat(k) }
19
+ @entries = Hash.new { |h, k| h[k] = _entries(k) }
20
+ @digests = Hash.new { |h, k| h[k] = _file_digest(k) }
21
21
  end
22
22
 
23
23
  # No-op return self as cached environment.
@@ -38,10 +38,10 @@ module Sprockets
38
38
  @stats[path]
39
39
  end
40
40
 
41
- # Internal: Cache Environment#file_hexdigest
42
- alias_method :_file_hexdigest, :file_hexdigest
43
- def file_hexdigest(path)
44
- @hexdigests[path]
41
+ # Internal: Cache Environment#file_digest
42
+ alias_method :_file_digest, :file_digest
43
+ def file_digest(path)
44
+ @digests[path]
45
45
  end
46
46
 
47
47
  protected
@@ -53,7 +53,7 @@ module Sprockets
53
53
  self.version,
54
54
  self.paths,
55
55
  uri,
56
- file_hexdigest(filename)
56
+ file_digest(filename)
57
57
  ]
58
58
  end
59
59
 
@@ -75,9 +75,9 @@ module Sprockets
75
75
  def build_asset_by_uri(uri)
76
76
  dep_graph_key = asset_dependency_graph_cache_key(uri)
77
77
 
78
- dependency_paths, dependency_digest, digest_uri = cache._get(dep_graph_key)
79
- if dependency_paths && dependency_digest && digest_uri
80
- if dependencies_hexdigest(dependency_paths) == dependency_digest
78
+ dependency_paths, dependency_sources_digest, digest_uri = cache._get(dep_graph_key)
79
+ if dependency_paths && dependency_sources_digest && digest_uri
80
+ if files_digest(dependency_paths) == dependency_sources_digest
81
81
  if asset = cache._get(asset_digest_uri_cache_key(digest_uri))
82
82
  return asset
83
83
  end
@@ -86,8 +86,8 @@ module Sprockets
86
86
 
87
87
  asset = super
88
88
 
89
- dependency_digest, dependency_paths = asset[:metadata].values_at(:dependency_digest, :dependency_paths)
90
- cache._set(dep_graph_key, [dependency_paths, dependency_digest, asset[:uri]])
89
+ dependency_sources_digest, dependency_paths = asset[:metadata].values_at(:dependency_sources_digest, :dependency_paths)
90
+ cache._set(dep_graph_key, [dependency_paths, dependency_sources_digest, asset[:uri]])
91
91
  cache.fetch(asset_digest_uri_cache_key(asset[:uri])) { asset }
92
92
 
93
93
  asset
@@ -52,13 +52,13 @@ module Sprockets
52
52
  mutate_config(:version) { version.dup }
53
53
  end
54
54
 
55
- # Returns a `Digest` implementation class.
55
+ # Deprecated: Returns a `Digest` implementation class.
56
56
  #
57
- # Defaults to `Digest::SHA1`.
57
+ # Defaults to `Digest::SHA256`.
58
58
  attr_reader :digest_class
59
59
 
60
- # Assign a `Digest` implementation class. This maybe any Ruby
61
- # `Digest::` implementation such as `Digest::SHA1` or
60
+ # Deprecated: Assign a `Digest` implementation class. This maybe any Ruby
61
+ # `Digest::` implementation such as `Digest::SHA256` or
62
62
  # `Digest::MD5`.
63
63
  #
64
64
  # environment.digest_class = Digest::MD5
@@ -0,0 +1,144 @@
1
+ require 'digest/md5'
2
+ require 'digest/sha1'
3
+ require 'digest/sha2'
4
+
5
+ module Sprockets
6
+ module DigestUtils
7
+ extend self
8
+
9
+ # Internal: Default digest class.
10
+ #
11
+ # Returns a Digest::Base subclass.
12
+ def digest_class
13
+ Digest::SHA256
14
+ end
15
+
16
+ # Internal: Maps digest bytesize to the digest class.
17
+ DIGEST_SIZES = {
18
+ 16 => Digest::MD5,
19
+ 20 => Digest::SHA1,
20
+ 32 => Digest::SHA256,
21
+ 48 => Digest::SHA384,
22
+ 64 => Digest::SHA512
23
+ }
24
+
25
+ # Internal: Detect digest class hash algorithm for digest bytes.
26
+ #
27
+ # While not elegant, all the supported digests have a unique bytesize.
28
+ #
29
+ # Returns Digest::Base or nil.
30
+ def detect_digest_class(bytes)
31
+ DIGEST_SIZES[bytes.bytesize]
32
+ end
33
+
34
+ # Internal: Generate a hexdigest for a nested JSON serializable object.
35
+ #
36
+ # obj - A JSON serializable object.
37
+ #
38
+ # Returns a String digest of the object.
39
+ def digest(obj)
40
+ digest = digest_class.new
41
+ queue = [obj]
42
+
43
+ while queue.length > 0
44
+ obj = queue.shift
45
+ klass = obj.class
46
+
47
+ if klass == String
48
+ digest << obj
49
+ elsif klass == Symbol
50
+ digest << 'Symbol'
51
+ digest << obj.to_s
52
+ elsif klass == Fixnum
53
+ digest << 'Fixnum'
54
+ digest << obj.to_s
55
+ elsif klass == TrueClass
56
+ digest << 'TrueClass'
57
+ elsif klass == FalseClass
58
+ digest << 'FalseClass'
59
+ elsif klass == NilClass
60
+ digest << 'NilClass'
61
+ elsif klass == Array
62
+ digest << 'Array'
63
+ queue.concat(obj)
64
+ elsif klass == Hash
65
+ digest << 'Hash'
66
+ queue.concat(obj.sort)
67
+ elsif klass == Set
68
+ digest << 'Set'
69
+ queue.concat(obj.to_a)
70
+ elsif klass == Encoding
71
+ digest << 'Encoding'
72
+ digest << obj.name
73
+ else
74
+ raise TypeError, "couldn't digest #{klass}"
75
+ end
76
+ end
77
+
78
+ digest.digest
79
+ end
80
+
81
+ # Internal: Pack a binary digest to a hex encoded string.
82
+ #
83
+ # bin - String bytes
84
+ #
85
+ # Returns hex String.
86
+ def pack_hexdigest(bin)
87
+ bin.unpack('H*').first
88
+ end
89
+
90
+ # Internal: Pack a binary digest to a base64 encoded string.
91
+ #
92
+ # bin - String bytes
93
+ #
94
+ # Returns base64 String.
95
+ def pack_base64digest(bin)
96
+ [bin].pack('m0')
97
+ end
98
+
99
+ # Internal: Pack a binary digest to a urlsafe base64 encoded string.
100
+ #
101
+ # bin - String bytes
102
+ #
103
+ # Returns urlsafe base64 String.
104
+ def pack_urlsafe_base64digest(bin)
105
+ pack_base64digest(bin).tr('+/', '-_').tr('=', '')
106
+ end
107
+
108
+ # Internal: Maps digest class to the named information hash algorithm name.
109
+ #
110
+ # http://www.iana.org/assignments/named-information/named-information.xhtml
111
+ NI_HASH_ALGORIHMS = {
112
+ Digest::SHA256 => 'sha-256'.freeze,
113
+ Digest::SHA384 => 'sha-384'.freeze,
114
+ Digest::SHA512 => 'sha-512'.freeze
115
+ }
116
+
117
+ # Internal: Generate a "named information" URI for use in the `integrity`
118
+ # attribute of an asset tag as per the subresource integrity specification.
119
+ #
120
+ # digest - The String byte digest of the asset content.
121
+ # content_type - The content-type the asset will be served with. This *must*
122
+ # be accurate if provided. Otherwise, subresource integrity
123
+ # will block the loading of the asset.
124
+ #
125
+ # Returns a String or nil if hash algorithm is incompatible.
126
+ def integrity_uri(digest, content_type = nil)
127
+ case digest
128
+ when Digest::Base
129
+ digest_class = digest.class
130
+ digest = digest.digest
131
+ when String
132
+ digest_class = DIGEST_SIZES[digest.bytesize]
133
+ else
134
+ raise TypeError, "unknown digest: #{digest.inspect}"
135
+ end
136
+
137
+ if hash_name = NI_HASH_ALGORIHMS[digest_class]
138
+ uri = "ni:///#{hash_name};#{pack_urlsafe_base64digest(digest)}"
139
+ uri << "?ct=#{content_type}" if content_type
140
+ uri
141
+ end
142
+ end
143
+ end
144
+ end
@@ -173,7 +173,8 @@ module Sprockets
173
173
  'logical_path' => asset.logical_path,
174
174
  'mtime' => asset.mtime.iso8601,
175
175
  'size' => asset.bytesize,
176
- 'digest' => asset.digest
176
+ 'digest' => asset.hexdigest,
177
+ 'integrity' => asset.integrity
177
178
  }
178
179
  assets[asset.logical_path] = asset.digest_path
179
180
 
@@ -114,6 +114,41 @@ module Sprockets
114
114
  File.basename(path).scan(/\.[^.]+/)
115
115
  end
116
116
 
117
+ # Internal: Returns all parents for path
118
+ #
119
+ # path - String absolute filename or directory
120
+ # root - String path to stop at (default: system root)
121
+ #
122
+ # Returns an Array of String paths.
123
+ def path_parents(path, root = nil)
124
+ root = "#{root}#{File::SEPARATOR}" if root
125
+ parents = []
126
+
127
+ loop do
128
+ parent = File.dirname(path)
129
+ break if parent == path
130
+ break if root && !path.start_with?(root)
131
+ parents << path = parent
132
+ end
133
+
134
+ parents
135
+ end
136
+
137
+ # Internal: Find target basename checking upwards from path.
138
+ #
139
+ # basename - String filename: ".sprocketsrc"
140
+ # path - String path to start search: "app/assets/javascripts/app.js"
141
+ # root - String path to stop at (default: system root)
142
+ #
143
+ # Returns String filename or nil.
144
+ def find_upwards(basename, path, root = nil)
145
+ path_parents(path, root).each do |dir|
146
+ filename = File.join(dir, basename)
147
+ return filename if file?(filename)
148
+ end
149
+ nil
150
+ end
151
+
117
152
  # Internal: Stat all the files under a directory.
118
153
  #
119
154
  # dir - A String directory
@@ -180,7 +180,7 @@ module Sprockets
180
180
  when NilClass
181
181
  # noop
182
182
  when Hash
183
- data = result[:data]
183
+ data = result[:data] if result.key?(:data)
184
184
  metadata = metadata.merge(result)
185
185
  metadata.delete(:data)
186
186
  when String
@@ -195,7 +195,7 @@ module Sprockets
195
195
  source: data,
196
196
  charset: data.encoding.name.downcase,
197
197
  length: data.bytesize,
198
- digest: digest_class.hexdigest(data),
198
+ digest: digest(data),
199
199
  metadata: metadata
200
200
  }
201
201
  end
@@ -67,11 +67,11 @@ module Sprockets
67
67
 
68
68
  if asset.nil?
69
69
  status = :not_found
70
- elsif fingerprint && asset.digest != fingerprint
70
+ elsif fingerprint && asset.etag != fingerprint
71
71
  status = :not_found
72
- elsif if_match && asset.digest != if_match
72
+ elsif if_match && asset.etag != if_match
73
73
  status = :precondition_failed
74
- elsif if_none_match && asset.digest == if_none_match
74
+ elsif if_none_match && asset.etag == if_none_match
75
75
  status = :not_modified
76
76
  else
77
77
  status = :ok
@@ -124,8 +124,8 @@ module Sprockets
124
124
  end
125
125
 
126
126
  # Returns a 304 Not Modified response tuple
127
- def not_modified_response(env, digest)
128
- [ 304, cache_headers(env, digest), [] ]
127
+ def not_modified_response(env, etag)
128
+ [ 304, cache_headers(env, etag), [] ]
129
129
  end
130
130
 
131
131
  # Returns a 403 Forbidden response tuple
@@ -221,12 +221,12 @@ module Sprockets
221
221
  env["QUERY_STRING"].to_s =~ /body=(1|t)/
222
222
  end
223
223
 
224
- def cache_headers(env, digest)
224
+ def cache_headers(env, etag)
225
225
  headers = {}
226
226
 
227
227
  # Set caching headers
228
228
  headers["Cache-Control"] = "public"
229
- headers["ETag"] = %("#{digest}")
229
+ headers["ETag"] = %("#{etag}")
230
230
 
231
231
  # If the request url contains a fingerprint, set a long
232
232
  # expires on the response
@@ -262,16 +262,16 @@ module Sprockets
262
262
  headers["Content-Type"] = type
263
263
  end
264
264
 
265
- headers.merge(cache_headers(env, asset.digest))
265
+ headers.merge(cache_headers(env, asset.etag))
266
266
  end
267
267
 
268
- # Gets digest fingerprint.
268
+ # Gets ETag fingerprint.
269
269
  #
270
270
  # "foo-0aa2105d29558f3eb790d411d7d8fb66.js"
271
271
  # # => "0aa2105d29558f3eb790d411d7d8fb66"
272
272
  #
273
273
  def path_fingerprint(path)
274
- path[/-([0-9a-f]{7,40})\.[^.]+$/, 1]
274
+ path[/-([0-9a-f]{7,128})\.[^.]+$/, 1]
275
275
  end
276
276
  end
277
277
  end
@@ -1,4 +1,3 @@
1
- require 'digest/sha1'
2
1
  require 'set'
3
2
 
4
3
  module Sprockets
@@ -101,54 +100,6 @@ module Sprockets
101
100
  end
102
101
  end
103
102
 
104
- # Internal: Generate a hexdigest for a nested JSON serializable object.
105
- #
106
- # obj - A JSON serializable object.
107
- #
108
- # Returns a String SHA1 digest of the object.
109
- def hexdigest(obj)
110
- digest = Digest::SHA1.new
111
- queue = [obj]
112
-
113
- while queue.length > 0
114
- obj = queue.shift
115
- klass = obj.class
116
-
117
- if klass == String
118
- digest << 'String'
119
- digest << obj
120
- elsif klass == Symbol
121
- digest << 'Symbol'
122
- digest << obj.to_s
123
- elsif klass == Fixnum
124
- digest << 'Fixnum'
125
- digest << obj.to_s
126
- elsif klass == TrueClass
127
- digest << 'TrueClass'
128
- elsif klass == FalseClass
129
- digest << 'FalseClass'
130
- elsif klass == NilClass
131
- digest << 'NilClass'
132
- elsif klass == Array
133
- digest << 'Array'
134
- queue.concat(obj)
135
- elsif klass == Hash
136
- digest << 'Hash'
137
- queue.concat(obj.sort)
138
- elsif klass == Set
139
- digest << 'Set'
140
- queue.concat(obj.to_a)
141
- elsif klass == Encoding
142
- digest << 'Encoding'
143
- digest << obj.name
144
- else
145
- raise TypeError, "couldn't digest #{klass}"
146
- end
147
- end
148
-
149
- digest.hexdigest
150
- end
151
-
152
103
  # Internal: Post-order Depth-First search algorithm.
153
104
  #
154
105
  # Used for resolving asset dependencies.
@@ -1,3 +1,3 @@
1
1
  module Sprockets
2
- VERSION = "3.0.0.beta.1"
2
+ VERSION = "3.0.0.beta.2"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sprockets
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.beta.1
4
+ version: 3.0.0.beta.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sam Stephenson
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-09-28 00:00:00.000000000 Z
12
+ date: 2014-10-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rack
@@ -237,6 +237,7 @@ files:
237
237
  - lib/sprockets/compressing.rb
238
238
  - lib/sprockets/configuration.rb
239
239
  - lib/sprockets/context.rb
240
+ - lib/sprockets/digest_utils.rb
240
241
  - lib/sprockets/directive_processor.rb
241
242
  - lib/sprockets/eco_template.rb
242
243
  - lib/sprockets/ejs_template.rb