dassets 0.15.0 → 0.15.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|