dassets 0.15.0 → 0.15.1
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 +4 -4
- data/Gemfile +3 -1
- data/dassets.gemspec +9 -5
- data/lib/dassets.rb +6 -6
- data/lib/dassets/asset_file.rb +15 -14
- data/lib/dassets/config.rb +8 -7
- data/lib/dassets/file_store.rb +4 -4
- data/lib/dassets/server.rb +1 -0
- data/lib/dassets/server/request.rb +2 -1
- data/lib/dassets/server/response.rb +30 -21
- data/lib/dassets/source.rb +2 -1
- data/lib/dassets/source_file.rb +21 -18
- data/lib/dassets/source_proxy.rb +8 -8
- data/lib/dassets/version.rb +1 -1
- data/test/helper.rb +19 -7
- data/test/support/app.rb +2 -0
- data/test/support/empty/{.gitkeep → .keep} +0 -0
- data/test/support/factory.rb +2 -0
- data/test/system/rack_tests.rb +13 -5
- data/test/unit/asset_file_tests.rb +9 -5
- data/test/unit/cache_tests.rb +4 -2
- data/test/unit/config_tests.rb +6 -3
- data/test/unit/dassets_tests.rb +6 -4
- data/test/unit/engine_tests.rb +5 -3
- data/test/unit/file_store_tests.rb +5 -3
- data/test/unit/server/request_tests.rb +7 -4
- data/test/unit/server/response_tests.rb +8 -6
- data/test/unit/server_tests.rb +3 -1
- data/test/unit/source_file_tests.rb +12 -6
- data/test/unit/source_proxy_tests.rb +24 -19
- data/test/unit/source_tests.rb +14 -6
- data/tmp/.gitkeep +0 -0
- metadata +23 -11
- data/.gitignore +0 -19
- data/.ruby-version +0 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d2158827f5cdbb5f779c9c3647a0f358bec9bffa62f5fefbf8b2b83f654d21a
|
4
|
+
data.tar.gz: 84e0a6194cb0e506431750155dea71e3cc7cc0c62ac68a1ce8155454a5348f84
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b05667686c621b41c5cd0bd80242a58b74f58da3867180b039130e1898b7fcd8f49ee010c3007ad49e19d3cc748aa363c4f90170be6132797ddfb7c522e8a05b
|
7
|
+
data.tar.gz: 1827b4f16b9f9d786306c8aef467a63b040830931b3481affe7af2954e7df11ebe5557cabe016b4d1ed0375923e18b9aa8c7906d8e55dae859f205d9e9bb2c34
|
data/Gemfile
CHANGED
data/dassets.gemspec
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
2
4
|
lib = File.expand_path("../lib", __FILE__)
|
3
5
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
6
|
require "dassets/version"
|
@@ -8,20 +10,22 @@ Gem::Specification.new do |gem|
|
|
8
10
|
gem.version = Dassets::VERSION
|
9
11
|
gem.authors = ["Kelly Redding", "Collin Redding"]
|
10
12
|
gem.email = ["kelly@kellyredding.com", "collin.redding@me.com"]
|
11
|
-
gem.summary =
|
12
|
-
gem.description =
|
13
|
+
gem.summary = "Digested asset files"
|
14
|
+
gem.description = "Digest and serve HTML asset files"
|
13
15
|
gem.homepage = "http://github.com/redding/dassets"
|
14
16
|
gem.license = "MIT"
|
15
17
|
|
16
|
-
gem.files
|
18
|
+
gem.files = `git ls-files | grep "^[^.]"`.split($INPUT_RECORD_SEPARATOR)
|
19
|
+
|
17
20
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
21
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
19
22
|
gem.require_paths = ["lib"]
|
20
23
|
|
21
24
|
gem.required_ruby_version = "~> 2.5"
|
22
25
|
|
23
|
-
gem.add_development_dependency("
|
24
|
-
gem.add_development_dependency("assert
|
26
|
+
gem.add_development_dependency("much-style-guide", ["~> 0.6.0"])
|
27
|
+
gem.add_development_dependency("assert", ["~> 2.19.3"])
|
28
|
+
gem.add_development_dependency("assert-rack-test", ["~> 1.1.1"])
|
25
29
|
gem.add_development_dependency("sinatra", ["~> 2.1"])
|
26
30
|
|
27
31
|
gem.add_dependency("rack", ["~> 2.1"])
|
data/lib/dassets.rb
CHANGED
@@ -13,17 +13,17 @@ module Dassets
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def self.configure(&block)
|
16
|
-
block.call(
|
16
|
+
block.call(config)
|
17
17
|
end
|
18
18
|
|
19
19
|
def self.init
|
20
20
|
@asset_files ||= {}
|
21
|
-
@source_files = SourceFiles.new(
|
21
|
+
@source_files = SourceFiles.new(config.sources)
|
22
22
|
end
|
23
23
|
|
24
24
|
def self.reset
|
25
25
|
@asset_files = {}
|
26
|
-
|
26
|
+
config.reset
|
27
27
|
end
|
28
28
|
|
29
29
|
def self.asset_file(digest_path)
|
@@ -31,7 +31,7 @@ module Dassets
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def self.[](digest_path)
|
34
|
-
|
34
|
+
asset_file(digest_path).tap do |af|
|
35
35
|
if af.fingerprint.nil?
|
36
36
|
msg =
|
37
37
|
+"error digesting `#{digest_path}`.\n\nMake sure Dassets has " \
|
@@ -47,7 +47,7 @@ module Dassets
|
|
47
47
|
values = Dassets.combinations[key].sort
|
48
48
|
msg << (
|
49
49
|
["#{bullet}#{values.first}"] +
|
50
|
-
(values[1..-1] || []).map{ |v| "#{" "*bullet.size}#{v}" }
|
50
|
+
(values[1..-1] || []).map{ |v| "#{" " * bullet.size}#{v}" }
|
51
51
|
).join("\n")
|
52
52
|
msg << "\n\n"
|
53
53
|
end
|
@@ -66,7 +66,7 @@ module Dassets
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def self.combinations
|
69
|
-
|
69
|
+
config.combinations
|
70
70
|
end
|
71
71
|
|
72
72
|
module SourceFiles
|
data/lib/dassets/asset_file.rb
CHANGED
@@ -5,6 +5,7 @@ require "rack/utils"
|
|
5
5
|
require "rack/mime"
|
6
6
|
|
7
7
|
module Dassets; end
|
8
|
+
|
8
9
|
class Dassets::AssetFile
|
9
10
|
attr_reader :digest_path, :dirname, :extname, :basename, :source_proxy
|
10
11
|
|
@@ -22,40 +23,40 @@ class Dassets::AssetFile
|
|
22
23
|
end
|
23
24
|
|
24
25
|
def digest!
|
25
|
-
return
|
26
|
-
Dassets.config.file_store.save(
|
26
|
+
return unless exists?
|
27
|
+
Dassets.config.file_store.save(url){ content }
|
27
28
|
end
|
28
29
|
|
29
30
|
def url
|
30
|
-
path_basename = "#{@basename}-#{
|
31
|
+
path_basename = "#{@basename}-#{fingerprint}#{@extname}"
|
31
32
|
path =
|
32
|
-
File.join(@dirname, path_basename).sub(
|
33
|
+
File.join(@dirname, path_basename).sub(%r{^\./}, "").sub(%r{^/}, "")
|
33
34
|
"#{dassets_base_url}/#{path}"
|
34
35
|
end
|
35
36
|
alias_method :href, :url
|
36
37
|
|
37
38
|
def fingerprint
|
38
|
-
return nil
|
39
|
+
return nil unless exists?
|
39
40
|
@source_proxy.fingerprint
|
40
41
|
end
|
41
42
|
|
42
43
|
def content
|
43
|
-
return nil
|
44
|
+
return nil unless exists?
|
44
45
|
@source_proxy.content
|
45
46
|
end
|
46
47
|
|
47
48
|
def mtime
|
48
|
-
return nil
|
49
|
+
return nil unless exists?
|
49
50
|
@source_proxy.mtime.httpdate
|
50
51
|
end
|
51
52
|
|
52
53
|
def size
|
53
|
-
return nil
|
54
|
-
|
54
|
+
return nil unless exists?
|
55
|
+
content.bytesize
|
55
56
|
end
|
56
57
|
|
57
58
|
def mime_type
|
58
|
-
return nil
|
59
|
+
return nil unless exists?
|
59
60
|
Rack::Mime.mime_type(@extname)
|
60
61
|
end
|
61
62
|
|
@@ -67,10 +68,10 @@ class Dassets::AssetFile
|
|
67
68
|
@source_proxy.exists?
|
68
69
|
end
|
69
70
|
|
70
|
-
def ==(
|
71
|
-
|
72
|
-
|
73
|
-
|
71
|
+
def ==(other)
|
72
|
+
other.is_a?(Dassets::AssetFile) &&
|
73
|
+
digest_path == other.digest_path &&
|
74
|
+
fingerprint == other.fingerprint
|
74
75
|
end
|
75
76
|
|
76
77
|
private
|
data/lib/dassets/config.rb
CHANGED
@@ -6,12 +6,13 @@ require "dassets/file_store"
|
|
6
6
|
require "dassets/source"
|
7
7
|
|
8
8
|
module Dassets; end
|
9
|
+
|
9
10
|
class Dassets::Config
|
10
11
|
attr_reader :sources, :combinations
|
11
12
|
|
12
13
|
def initialize
|
13
14
|
super
|
14
|
-
|
15
|
+
reset
|
15
16
|
|
16
17
|
@content_cache = Dassets::NoCache.new
|
17
18
|
@fingerprint_cache = Dassets::NoCache.new
|
@@ -20,12 +21,12 @@ class Dassets::Config
|
|
20
21
|
|
21
22
|
def reset
|
22
23
|
@sources = []
|
23
|
-
@combinations = Hash.new
|
24
|
+
@combinations = Hash.new{ |_h, k| [k] } # digest pass-thru if none defined
|
24
25
|
@file_store = Dassets::NullFileStore.new
|
25
26
|
end
|
26
27
|
|
27
28
|
def base_url(value = nil)
|
28
|
-
set_base_url(value)
|
29
|
+
set_base_url(value) unless value.nil?
|
29
30
|
@base_url
|
30
31
|
end
|
31
32
|
|
@@ -34,9 +35,9 @@ class Dassets::Config
|
|
34
35
|
end
|
35
36
|
|
36
37
|
def file_store(value = nil)
|
37
|
-
|
38
|
+
unless value.nil?
|
38
39
|
@file_store =
|
39
|
-
if value.
|
40
|
+
if value.is_a?(Dassets::FileStore)
|
40
41
|
value
|
41
42
|
else
|
42
43
|
Dassets::FileStore.new(value)
|
@@ -46,12 +47,12 @@ class Dassets::Config
|
|
46
47
|
end
|
47
48
|
|
48
49
|
def content_cache(cache = nil)
|
49
|
-
@content_cache = cache
|
50
|
+
@content_cache = cache unless cache.nil?
|
50
51
|
@content_cache
|
51
52
|
end
|
52
53
|
|
53
54
|
def fingerprint_cache(cache = nil)
|
54
|
-
@fingerprint_cache = cache
|
55
|
+
@fingerprint_cache = cache unless cache.nil?
|
55
56
|
@fingerprint_cache
|
56
57
|
end
|
57
58
|
|
data/lib/dassets/file_store.rb
CHANGED
@@ -14,10 +14,10 @@ class Dassets::FileStore
|
|
14
14
|
|
15
15
|
def save(url_path, &block)
|
16
16
|
@save_mutex.synchronize do
|
17
|
-
store_path(url_path).tap
|
17
|
+
store_path(url_path).tap do |path|
|
18
18
|
FileUtils.mkdir_p(File.dirname(path))
|
19
|
-
File.open(path, "w")
|
20
|
-
|
19
|
+
File.open(path, "w"){ |f| f.write(block.call) }
|
20
|
+
end
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -31,7 +31,7 @@ class Dassets::NullFileStore < Dassets::FileStore
|
|
31
31
|
super("")
|
32
32
|
end
|
33
33
|
|
34
|
-
def save(url_path
|
34
|
+
def save(url_path)
|
35
35
|
# No-op, just return the store path like the base does.
|
36
36
|
store_path(url_path)
|
37
37
|
end
|
data/lib/dassets/server.rb
CHANGED
@@ -4,6 +4,7 @@ require "rack"
|
|
4
4
|
|
5
5
|
module Dassets; end
|
6
6
|
class Dassets::Server; end
|
7
|
+
|
7
8
|
class Dassets::Server::Request < Rack::Request
|
8
9
|
# The HTTP request method. This is the standard implementation of this
|
9
10
|
# method but is respecified here due to libraries that attempt to modify
|
@@ -46,7 +47,7 @@ class Dassets::Server::Request < Rack::Request
|
|
46
47
|
|
47
48
|
def path_digest_match
|
48
49
|
@path_digest_match ||= begin
|
49
|
-
path_info.match(
|
50
|
+
path_info.match(%r{/(.+)-[a-f0-9]{32}(\..+|)$}i) || NullDigestMatch.new
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
@@ -6,6 +6,7 @@ require "rack/mime"
|
|
6
6
|
|
7
7
|
module Dassets; end
|
8
8
|
class Dassets::Server; end
|
9
|
+
|
9
10
|
class Dassets::Server::Response
|
10
11
|
attr_reader :asset_file, :status, :headers, :body
|
11
12
|
|
@@ -30,13 +31,15 @@ class Dassets::Server::Response
|
|
30
31
|
body = Body.new(env, @asset_file)
|
31
32
|
[
|
32
33
|
body.partial? ? 206 : 200,
|
33
|
-
Rack::Utils::HeaderHash
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
34
|
+
Rack::Utils::HeaderHash
|
35
|
+
.new
|
36
|
+
.merge(@asset_file.response_headers).tap do |h|
|
37
|
+
h["Last-Modified"] = mtime.to_s
|
38
|
+
h["Content-Type"] = @asset_file.mime_type.to_s
|
39
|
+
h["Content-Length"] = body.size.to_s
|
40
|
+
h["Content-Range"] = body.content_range if body.partial?
|
41
|
+
end,
|
42
|
+
env["REQUEST_METHOD"] == "HEAD" ? [] : body,
|
40
43
|
]
|
41
44
|
end
|
42
45
|
end
|
@@ -48,14 +51,14 @@ class Dassets::Server::Response
|
|
48
51
|
# This class borrows from the body range handling in Rack::File and adapts
|
49
52
|
# it for use with Dasset's asset files and their generic string content.
|
50
53
|
class Body
|
51
|
-
CHUNK_SIZE = (8*1024)
|
54
|
+
CHUNK_SIZE = (8 * 1024) # 8k
|
52
55
|
|
53
56
|
attr_reader :asset_file, :size, :content_range
|
54
57
|
|
55
58
|
def initialize(env, asset_file)
|
56
59
|
@asset_file = asset_file
|
57
60
|
@range, @content_range = get_range_info(env, @asset_file)
|
58
|
-
@size =
|
61
|
+
@size = range_end - range_begin + 1
|
59
62
|
end
|
60
63
|
|
61
64
|
def partial?
|
@@ -73,7 +76,7 @@ class Dassets::Server::Response
|
|
73
76
|
def each
|
74
77
|
StringIO.open(@asset_file.content, "rb") do |io|
|
75
78
|
io.seek(@range.begin)
|
76
|
-
remaining_len =
|
79
|
+
remaining_len = size
|
77
80
|
while remaining_len > 0
|
78
81
|
part = io.read([CHUNK_SIZE, remaining_len].min)
|
79
82
|
break if part.nil?
|
@@ -85,16 +88,16 @@ class Dassets::Server::Response
|
|
85
88
|
end
|
86
89
|
|
87
90
|
def inspect
|
88
|
-
"#<#{self.class}:#{"0x0%x" % (
|
89
|
-
"digest_path=#{
|
90
|
-
"range_begin=#{
|
91
|
+
"#<#{self.class}:#{"0x0%x" % (object_id << 1)} " \
|
92
|
+
"digest_path=#{asset_file.digest_path} " \
|
93
|
+
"range_begin=#{range_begin} range_end=#{range_end}>"
|
91
94
|
end
|
92
95
|
|
93
|
-
def ==(
|
94
|
-
if
|
95
|
-
|
96
|
-
|
97
|
-
|
96
|
+
def ==(other)
|
97
|
+
if other.is_a?(self.class)
|
98
|
+
asset_file == other.asset_file &&
|
99
|
+
range_begin == other.range_begin &&
|
100
|
+
range_end == other.range_end
|
98
101
|
else
|
99
102
|
super
|
100
103
|
end
|
@@ -105,16 +108,22 @@ class Dassets::Server::Response
|
|
105
108
|
def get_range_info(env, asset_file)
|
106
109
|
content_size = asset_file.size
|
107
110
|
# legacy rack version, just return full size
|
108
|
-
|
111
|
+
unless Rack::Utils.respond_to?(:byte_ranges)
|
112
|
+
return full_size_range_info(content_size)
|
113
|
+
end
|
114
|
+
|
109
115
|
ranges = Rack::Utils.byte_ranges(env, content_size)
|
110
116
|
# No ranges or multiple ranges are not supported, just return full size
|
111
|
-
|
117
|
+
if ranges.nil? || ranges.empty? || ranges.length > 1
|
118
|
+
return full_size_range_info(content_size)
|
119
|
+
end
|
120
|
+
|
112
121
|
# single range
|
113
122
|
[ranges[0], "bytes #{ranges[0].begin}-#{ranges[0].end}/#{content_size}"]
|
114
123
|
end
|
115
124
|
|
116
125
|
def full_size_range_info(content_size)
|
117
|
-
[(0..content_size-1), nil]
|
126
|
+
[(0..content_size - 1), nil]
|
118
127
|
end
|
119
128
|
end
|
120
129
|
end
|
data/lib/dassets/source.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require "dassets/engine"
|
4
4
|
|
5
5
|
module Dassets; end
|
6
|
+
|
6
7
|
class Dassets::Source
|
7
8
|
attr_reader :path, :engines, :response_headers
|
8
9
|
|
@@ -10,7 +11,7 @@ class Dassets::Source
|
|
10
11
|
@path = path.to_s
|
11
12
|
@filter = proc{ |paths| paths }
|
12
13
|
@engines = Hash.new{ |hash, key| hash[key] = [] }
|
13
|
-
@response_headers =
|
14
|
+
@response_headers = {}
|
14
15
|
end
|
15
16
|
|
16
17
|
def filter(&block)
|
data/lib/dassets/source_file.rb
CHANGED
@@ -6,6 +6,7 @@ require "dassets/asset_file"
|
|
6
6
|
require "dassets/source_proxy"
|
7
7
|
|
8
8
|
module Dassets; end
|
9
|
+
|
9
10
|
class Dassets::SourceFile
|
10
11
|
def self.find_by_digest_path(path, **options)
|
11
12
|
Dassets.source_files[path] || Dassets::NullSourceFile.new(path, **options)
|
@@ -23,13 +24,13 @@ class Dassets::SourceFile
|
|
23
24
|
# configured source) in `find_by_digest_path` above.
|
24
25
|
def source
|
25
26
|
@source ||=
|
26
|
-
Dassets.config.sources.select
|
27
|
+
Dassets.config.sources.select{ |source|
|
27
28
|
@file_path =~ /^#{slash_path(source.path)}/
|
28
29
|
}.last
|
29
30
|
end
|
30
31
|
|
31
32
|
def asset_file
|
32
|
-
@asset_file ||= Dassets::AssetFile.new(
|
33
|
+
@asset_file ||= Dassets::AssetFile.new(digest_path)
|
33
34
|
end
|
34
35
|
|
35
36
|
def digest_path
|
@@ -37,26 +38,28 @@ class Dassets::SourceFile
|
|
37
38
|
begin
|
38
39
|
digest_basename =
|
39
40
|
@ext_list
|
40
|
-
.reduce([])
|
41
|
+
.reduce([]){ |digest_ext_list, ext|
|
41
42
|
digest_ext_list <<
|
42
|
-
|
43
|
+
source.engines[ext].reduce(ext)do |ext_acc, engine|
|
43
44
|
engine.ext(ext_acc)
|
44
|
-
|
45
|
+
end
|
45
46
|
}
|
46
47
|
.reject(&:empty?)
|
47
48
|
.reverse
|
48
49
|
.join(".")
|
49
50
|
|
50
|
-
File.join(
|
51
|
+
File.join(
|
52
|
+
[digest_dirname(@file_path), digest_basename].reject(&:empty?),
|
53
|
+
)
|
51
54
|
end
|
52
55
|
end
|
53
56
|
|
54
57
|
def compiled
|
55
|
-
@ext_list.reduce(read_file(@file_path))
|
56
|
-
|
58
|
+
@ext_list.reduce(read_file(@file_path)) do |file_acc, ext|
|
59
|
+
source.engines[ext].reduce(file_acc) do |ext_acc, engine|
|
57
60
|
engine.compile(ext_acc)
|
58
|
-
|
59
|
-
|
61
|
+
end
|
62
|
+
end
|
60
63
|
end
|
61
64
|
|
62
65
|
def exists?
|
@@ -68,12 +71,12 @@ class Dassets::SourceFile
|
|
68
71
|
end
|
69
72
|
|
70
73
|
def response_headers
|
71
|
-
|
74
|
+
source.nil? ? {} : source.response_headers
|
72
75
|
end
|
73
76
|
|
74
|
-
def ==(
|
75
|
-
if
|
76
|
-
|
77
|
+
def ==(other)
|
78
|
+
if other.is_a?(self.class)
|
79
|
+
file_path == other.file_path
|
77
80
|
else
|
78
81
|
super
|
79
82
|
end
|
@@ -83,7 +86,7 @@ class Dassets::SourceFile
|
|
83
86
|
|
84
87
|
# remove the source path from the dirname (if it exists)
|
85
88
|
def digest_dirname(file_path)
|
86
|
-
slash_path(File.dirname(file_path)).sub(slash_path(
|
89
|
+
slash_path(File.dirname(file_path)).sub(slash_path(source.path), "")
|
87
90
|
end
|
88
91
|
|
89
92
|
def slash_path(path)
|
@@ -122,9 +125,9 @@ class Dassets::NullSourceFile < Dassets::SourceFile
|
|
122
125
|
@source_proxy.mtime
|
123
126
|
end
|
124
127
|
|
125
|
-
def ==(
|
126
|
-
if
|
127
|
-
|
128
|
+
def ==(other)
|
129
|
+
if other.is_a?(self.class)
|
130
|
+
file_path == other.file_path
|
128
131
|
else
|
129
132
|
super
|
130
133
|
end
|