Skip to content

Commit 4f8196d

Browse files
committed
Abstract manifest into own class
1 parent 4f54250 commit 4f8196d

File tree

9 files changed

+157
-22
lines changed

9 files changed

+157
-22
lines changed

lib/propshaft/assembly.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require "propshaft/manifest"
12
require "propshaft/load_path"
23
require "propshaft/resolver/dynamic"
34
require "propshaft/resolver/static"

lib/propshaft/asset.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def integrity(hash_algorithm:)
4646
when "sha512"
4747
512
4848
else
49-
raise(Error.new("Subresource Integrity hash algorithm must be one of SHA2 family (sha256, sha384, sha512)"))
49+
raise(StandardError.new("Subresource Integrity hash algorithm must be one of SHA2 family (sha256, sha384, sha512)"))
5050
end
5151

5252
[hash_algorithm, Digest::SHA2.new(bitlen).base64digest(content)].join("-")

lib/propshaft/load_path.rb

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require "propshaft/manifest"
12
require "propshaft/asset"
23

34
class Propshaft::LoadPath
@@ -11,10 +12,10 @@ def execute_if_updated
1112
end
1213
end
1314

14-
attr_reader :paths, :compilers, :version
15+
attr_reader :paths, :compilers, :version, :integrity_hash_algorithm
1516

1617
def initialize(paths = [], compilers:, version: nil, file_watcher: nil, integrity_hash_algorithm: nil)
17-
@paths, @compilers, @version = dedup(paths), compilers, version, integrity_hash_algorithm
18+
@paths, @compilers, @version, @integrity_hash_algorithm = dedup(paths), compilers, version, integrity_hash_algorithm
1819
@file_watcher = file_watcher || NullFileWatcher
1920
end
2021

@@ -41,17 +42,8 @@ def asset_paths_by_glob(glob)
4142
end
4243

4344
def manifest
44-
Hash.new.tap do |manifest|
45-
assets.each do |asset|
46-
manifest[asset.logical_path.to_s] = if @integrity_hash_algorithm.nil?
47-
asset.digested_path.to_s
48-
else
49-
{
50-
digested_path: asset.digested_path.to_s,
51-
integrity: asset.integrity(hash_algorithm: @integrity_hash_algorithm)
52-
}
53-
end
54-
end
45+
Propshaft::Manifest.new(integrity_hash_algorithm:).tap do |manifest|
46+
assets.each { |asset| manifest.push_asset(asset) }
5547
end
5648
end
5749

lib/propshaft/manifest.rb

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
module Propshaft
2+
class Manifest
3+
class ManifestEntry
4+
attr_reader :logical_path, :digested_path, :integrity
5+
def initialize(logical_path:, digested_path:, integrity:)
6+
@logical_path = logical_path
7+
@digested_path = digested_path
8+
@integrity = integrity
9+
end
10+
11+
def to_h
12+
{ digested_path:, integrity: }
13+
end
14+
end
15+
16+
def initialize(integrity_hash_algorithm: nil)
17+
@integrity_hash_algorithm = integrity_hash_algorithm
18+
@entries = {}
19+
end
20+
21+
def push_asset(asset)
22+
entry = ManifestEntry.new(
23+
logical_path: asset.logical_path.to_s,
24+
digested_path: asset.digested_path.to_s,
25+
integrity: @integrity_hash_algorithm && asset.integrity(hash_algorithm: @integrity_hash_algorithm)
26+
)
27+
28+
push(entry)
29+
end
30+
31+
def push(entry)
32+
@entries[entry.logical_path] = entry
33+
end
34+
35+
def <<(asset)
36+
push(asset)
37+
end
38+
39+
def [](logical_path)
40+
@entries[logical_path]
41+
end
42+
43+
def to_json
44+
Hash.new.tap do |serialized_manifest|
45+
@entries.values.each do |manifest_entry|
46+
serialized_manifest[manifest_entry.logical_path] = manifest_entry.to_h
47+
end
48+
end.to_json
49+
end
50+
51+
class << self
52+
def from_path(manifest_path)
53+
JSON.parse(manifest_path.read, symbolize_names: false).tap do |serialized_manifest|
54+
Manifest.new.tap do |manifest|
55+
serialized_manifest.each_pair do |key, value|
56+
# Compatibility mode to be able to
57+
# read the old "simple manifest" format
58+
digested_path, integrity = if value.is_a?(String)
59+
[value, nil]
60+
else
61+
[value["digested_path"], value["integrity"]]
62+
end
63+
64+
entry = ManifestEntry.new(
65+
logical_path: key, digested_path:, integrity:
66+
)
67+
68+
manifest.push(entry)
69+
end
70+
end
71+
end
72+
end
73+
end
74+
end
75+
end

lib/propshaft/resolver/static.rb

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ def read(logical_path, encoding: "ASCII-8BIT")
1919
end
2020

2121
private
22-
def parsed_manifest
23-
@parsed_manifest ||= JSON.parse(manifest_path.read, symbolize_names: false)
22+
def manifest
23+
@manifest ||= Propshaft::Manifest.from_path(manifest_path)
2424
end
2525

2626
def digested_path(logical_path)
27-
entry = parsed_manifest[logical_path]
27+
entry = manifest[logical_path]
2828

2929
if entry.is_a?(String)
3030
return entry

test/propshaft/asset_test.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,23 @@ class Propshaft::AssetTest < ActiveSupport::TestCase
5151
find_asset("file-is-a-sourcemap.js.map").digested_path.to_s
5252
end
5353

54+
test "integrity" do
55+
assert_equal "sha256-+C/K/0dPvIdSC8rl/NDS8zqPp08R0VH+hKMM4D8tNJs=",
56+
find_asset("one.txt").integrity(hash_algorithm: "sha256").to_s
57+
58+
assert_equal "sha384-LdS8l2QTAF8bD8WPb8QSQv0skTWHhmcnS2XU5LBkVQneGzqIqnDRskQtJvi7ADMe",
59+
find_asset("one.txt").integrity(hash_algorithm: "sha384").to_s
60+
61+
assert_equal "sha512-wzPP7om24750PjHXRlgiDOhILPd4V2AbLRxomBudQaTDI1eYZkM5j8pSH/ylSSUxiGqXR3F6lgVCbsmXkqKrEg==",
62+
find_asset("one.txt").integrity(hash_algorithm: "sha512").to_s
63+
64+
exception = assert_raises StandardError do
65+
find_asset("one.txt").integrity(hash_algorithm: "md5")
66+
end
67+
68+
assert_equal "Subresource Integrity hash algorithm must be one of SHA2 family (sha256, sha384, sha512)", exception.message
69+
end
70+
5471
test "value object equality" do
5572
assert_equal find_asset("one.txt"), find_asset("one.txt")
5673
end

test/propshaft/load_path_test.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,16 @@ class Propshaft::LoadPathTest < ActiveSupport::TestCase
3131

3232
test "manifest" do
3333
@load_path.manifest.tap do |manifest|
34-
assert_equal "one-f2e1ec14.txt", manifest["one.txt"]
35-
assert_equal "nested/three-6c2b86a0.txt", manifest["nested/three.txt"]
34+
assert_equal "one-f2e1ec14.txt", manifest["one.txt"].digested_path.to_s
35+
assert_equal "nested/three-6c2b86a0.txt", manifest["nested/three.txt"].digested_path.to_s
3636
end
3737
end
3838

3939
test "manifest with version" do
4040
@load_path = Propshaft::LoadPath.new(@load_path.paths, version: "1", compilers: Propshaft::Compilers.new(nil))
4141
@load_path.manifest.tap do |manifest|
42-
assert_equal "one-c9373b68.txt", manifest["one.txt"]
43-
assert_equal "nested/three-a41a5d38.txt", manifest["nested/three.txt"]
42+
assert_equal "one-c9373b68.txt", manifest["one.txt"].digested_path.to_s
43+
assert_equal "nested/three-a41a5d38.txt", manifest["nested/three.txt"].digested_path.to_s
4444
end
4545
end
4646

test/propshaft/manifest_test.rb

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
require "test_helper"
2+
require "propshaft/manifest"
3+
4+
class Propshaft::ManifestTest < ActiveSupport::TestCase
5+
test "serializes to the extensible manifest format with integrity hash value" do
6+
manifest = create_manifest("sha384")
7+
parsed_manifest = JSON.parse(manifest.to_json)
8+
9+
manifest_entry = parsed_manifest["one.txt"]
10+
assert_equal "one-f2e1ec14.txt", manifest_entry["digested_path"]
11+
assert_equal "sha384-LdS8l2QTAF8bD8WPb8QSQv0skTWHhmcnS2XU5LBkVQneGzqIqnDRskQtJvi7ADMe", manifest_entry["integrity"]
12+
13+
manifest_entry = parsed_manifest["another.css"]
14+
assert_equal "another-c464b1ee.css", manifest_entry["digested_path"]
15+
assert_equal "sha384-RZLbo+FZ8rnE9ct6dNqDcgIYo7DBk/GaB4nCMnNsj6HWp0ePV8q8qky9Qemdpuwl", manifest_entry["integrity"]
16+
end
17+
18+
test "serializes to the extensible manifest format without integrity hash algorithm" do
19+
manifest = create_manifest
20+
parsed_manifest = JSON.parse(manifest.to_json)
21+
22+
manifest_entry = parsed_manifest["one.txt"]
23+
assert_equal "one-f2e1ec14.txt", manifest_entry["digested_path"]
24+
assert_nil manifest_entry["integrity"]
25+
26+
manifest_entry = parsed_manifest["another.css"]
27+
assert_equal "another-c464b1ee.css", manifest_entry["digested_path"]
28+
assert_nil manifest_entry["integrity"]
29+
end
30+
31+
private
32+
def create_manifest(integrity_hash_algorithm = nil)
33+
Propshaft::Manifest.new(integrity_hash_algorithm:).tap do |manifest|
34+
manifest.push_asset(find_asset("one.txt"))
35+
manifest.push_asset(find_asset("another.css"))
36+
end
37+
end
38+
39+
def find_asset(logical_path)
40+
root_path = Pathname.new("#{__dir__}/../fixtures/assets/first_path")
41+
path = root_path.join(logical_path)
42+
43+
assembly = Propshaft::Assembly.new(ActiveSupport::OrderedOptions.new.tap { |config|
44+
config.paths = [ root_path ]
45+
config.compilers = [[ "text/css", Propshaft::Compiler::CssAssetUrls ]]
46+
})
47+
48+
Propshaft::Asset.new(path, logical_path: logical_path, load_path: assembly.load_path)
49+
end
50+
end

test/propshaft/processor_test.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class Propshaft::ProcessorTest < ActiveSupport::TestCase
1717
test "manifest is written" do
1818
processed do |processor|
1919
assert_equal "one-f2e1ec14.txt",
20-
JSON.parse(processor.output_path.join(".manifest.json").read)["one.txt"]
20+
JSON.parse(processor.output_path.join(".manifest.json").read)["one.txt"]["digested_path"]
2121
end
2222
end
2323

0 commit comments

Comments
 (0)