dassets 0.14.3 → 0.15.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -7
- data/Gemfile +5 -1
- data/README.md +15 -17
- data/dassets.gemspec +14 -9
- data/lib/dassets.rb +51 -13
- data/lib/dassets/asset_file.rb +27 -24
- data/lib/dassets/cache.rb +27 -33
- data/lib/dassets/config.rb +55 -50
- data/lib/dassets/engine.rb +11 -23
- data/lib/dassets/file_store.rb +27 -28
- data/lib/dassets/server.rb +27 -27
- data/lib/dassets/server/request.rb +44 -41
- data/lib/dassets/server/response.rb +101 -80
- data/lib/dassets/source.rb +24 -9
- data/lib/dassets/source_file.rb +110 -81
- data/lib/dassets/source_proxy.rb +36 -20
- data/lib/dassets/version.rb +3 -1
- data/test/helper.rb +31 -25
- data/test/support/app.rb +5 -5
- data/test/support/empty/{.gitkeep → .keep} +0 -0
- data/test/support/factory.rb +3 -2
- data/test/support/linked_source_files/linked_file.txt +0 -0
- data/test/support/source_files/linked +1 -0
- data/test/support/source_files/linked_file2.txt +1 -0
- data/test/system/rack_tests.rb +65 -61
- data/test/unit/asset_file_tests.rb +68 -60
- data/test/unit/cache_tests.rb +15 -34
- data/test/unit/config_tests.rb +58 -51
- data/test/unit/dassets_tests.rb +31 -24
- data/test/unit/engine_tests.rb +9 -43
- data/test/unit/file_store_tests.rb +34 -24
- data/test/unit/server/request_tests.rb +57 -59
- data/test/unit/server/response_tests.rb +82 -82
- data/test/unit/server_tests.rb +5 -9
- data/test/unit/source_file_tests.rb +86 -74
- data/test/unit/source_proxy_tests.rb +84 -90
- data/test/unit/source_tests.rb +89 -50
- data/tmp/.gitkeep +0 -0
- metadata +92 -64
- data/.gitignore +0 -19
data/lib/dassets/source.rb
CHANGED
@@ -1,25 +1,36 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dassets/engine"
|
2
4
|
|
3
5
|
module Dassets; end
|
4
|
-
class Dassets::Source
|
5
6
|
|
7
|
+
class Dassets::Source
|
6
8
|
attr_reader :path, :engines, :response_headers
|
7
9
|
|
8
10
|
def initialize(path)
|
9
11
|
@path = path.to_s
|
10
12
|
@filter = proc{ |paths| paths }
|
11
|
-
@engines = Hash.new{ |
|
12
|
-
@response_headers =
|
13
|
+
@engines = Hash.new{ |hash, key| hash[key] = [] }
|
14
|
+
@response_headers = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def base_path(value = nil)
|
18
|
+
set_base_path(value) unless value.nil?
|
19
|
+
@base_path
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_base_path(value)
|
23
|
+
@base_path = value
|
13
24
|
end
|
14
25
|
|
15
26
|
def filter(&block)
|
16
27
|
block.nil? ? @filter : @filter = block
|
17
28
|
end
|
18
29
|
|
19
|
-
def engine(input_ext, engine_class, registered_opts=nil)
|
20
|
-
default_opts = {
|
30
|
+
def engine(input_ext, engine_class, registered_opts = nil)
|
31
|
+
default_opts = { "source_path" => @path }
|
21
32
|
engine_opts = default_opts.merge(registered_opts || {})
|
22
|
-
@engines[input_ext.to_s]
|
33
|
+
@engines[input_ext.to_s] << engine_class.new(engine_opts)
|
23
34
|
end
|
24
35
|
|
25
36
|
def files
|
@@ -28,12 +39,16 @@ class Dassets::Source
|
|
28
39
|
|
29
40
|
private
|
30
41
|
|
42
|
+
# Use "**{,/*/**}/*" to glob following symlinks and returning immediate-child
|
43
|
+
# matches. See https://stackoverflow.com/a/2724048.
|
31
44
|
def glob_files
|
32
|
-
Dir
|
45
|
+
Dir
|
46
|
+
.glob(File.join(@path, "**{,/*/**}/*"))
|
47
|
+
.uniq
|
48
|
+
.reject{ |path| !File.file?(path) }
|
33
49
|
end
|
34
50
|
|
35
51
|
def apply_filter(files)
|
36
52
|
@filter.call(files)
|
37
53
|
end
|
38
|
-
|
39
54
|
end
|
data/lib/dassets/source_file.rb
CHANGED
@@ -1,114 +1,143 @@
|
|
1
|
-
|
2
|
-
require 'dassets'
|
3
|
-
require 'dassets/asset_file'
|
4
|
-
require 'dassets/source_proxy'
|
1
|
+
# frozen_string_literal: true
|
5
2
|
|
6
|
-
|
3
|
+
require "fileutils"
|
4
|
+
require "dassets"
|
5
|
+
require "dassets/asset_file"
|
6
|
+
require "dassets/source_proxy"
|
7
7
|
|
8
|
-
|
8
|
+
module Dassets; end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
10
|
+
class Dassets::SourceFile
|
11
|
+
def self.find_by_digest_path(path, **options)
|
12
|
+
Dassets.source_files[path] || Dassets::NullSourceFile.new(path, **options)
|
13
|
+
end
|
13
14
|
|
14
|
-
|
15
|
+
attr_reader :file_path
|
15
16
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
def initialize(file_path)
|
18
|
+
@file_path = file_path.to_s
|
19
|
+
@ext_list = File.basename(@file_path).split(".").reverse
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
22
|
+
# Get the last matching one (in the case two sources with the same path are
|
23
|
+
# configured) since we select the last matching source file (from the last
|
24
|
+
# configured source) in `find_by_digest_path` above.
|
25
|
+
def source
|
26
|
+
@source ||=
|
27
|
+
Dassets.config.sources.select{ |source|
|
26
28
|
@file_path =~ /^#{slash_path(source.path)}/
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
def asset_file
|
31
|
-
@asset_file ||= Dassets::AssetFile.new(self.digest_path)
|
32
|
-
end
|
29
|
+
}.last
|
30
|
+
end
|
33
31
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
digest_ext_list << self.source.engines[ext].ext(ext)
|
38
|
-
end.reject(&:empty?).reverse.join('.')
|
32
|
+
def asset_file
|
33
|
+
@asset_file ||= Dassets::AssetFile.new(digest_path)
|
34
|
+
end
|
39
35
|
|
40
|
-
|
36
|
+
def digest_path
|
37
|
+
@digest_path ||=
|
38
|
+
begin
|
39
|
+
digest_basename =
|
40
|
+
@ext_list
|
41
|
+
.reduce([]){ |digest_ext_list, ext|
|
42
|
+
digest_ext_list <<
|
43
|
+
source.engines[ext].reduce(ext)do |ext_acc, engine|
|
44
|
+
engine.ext(ext_acc)
|
45
|
+
end
|
46
|
+
}
|
47
|
+
.reject(&:empty?)
|
48
|
+
.reverse
|
49
|
+
.join(".")
|
50
|
+
|
51
|
+
File.join(
|
52
|
+
[
|
53
|
+
base_path,
|
54
|
+
digest_dirname(@file_path),
|
55
|
+
digest_basename,
|
56
|
+
].reject(&:empty?),
|
57
|
+
)
|
41
58
|
end
|
42
|
-
|
59
|
+
end
|
43
60
|
|
44
|
-
|
45
|
-
|
46
|
-
|
61
|
+
def compiled
|
62
|
+
@ext_list.reduce(read_file(@file_path)) do |file_acc, ext|
|
63
|
+
source.engines[ext].reduce(file_acc) do |ext_acc, engine|
|
64
|
+
engine.compile(ext_acc)
|
47
65
|
end
|
48
66
|
end
|
67
|
+
end
|
49
68
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
def mtime
|
55
|
-
File.mtime(@file_path)
|
56
|
-
end
|
57
|
-
|
58
|
-
def response_headers
|
59
|
-
self.source.nil? ? Hash.new : self.source.response_headers
|
60
|
-
end
|
69
|
+
def exists?
|
70
|
+
File.file?(@file_path)
|
71
|
+
end
|
61
72
|
|
62
|
-
|
63
|
-
|
64
|
-
|
73
|
+
def mtime
|
74
|
+
File.mtime(@file_path)
|
75
|
+
end
|
65
76
|
|
66
|
-
|
77
|
+
def base_path
|
78
|
+
source&.base_path.to_s
|
79
|
+
end
|
67
80
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
81
|
+
def response_headers
|
82
|
+
source.nil? ? {} : source.response_headers
|
83
|
+
end
|
72
84
|
|
73
|
-
|
74
|
-
|
85
|
+
def ==(other)
|
86
|
+
if other.is_a?(self.class)
|
87
|
+
file_path == other.file_path
|
88
|
+
else
|
89
|
+
super
|
75
90
|
end
|
91
|
+
end
|
76
92
|
|
77
|
-
|
78
|
-
File.send(File.respond_to?(:binread) ? :binread : :read, path.to_s)
|
79
|
-
end
|
93
|
+
private
|
80
94
|
|
95
|
+
# remove the source path from the dirname (if it exists)
|
96
|
+
def digest_dirname(file_path)
|
97
|
+
slash_path(File.dirname(file_path)).sub(slash_path(source.path), "")
|
81
98
|
end
|
82
99
|
|
83
|
-
|
84
|
-
|
100
|
+
def slash_path(path)
|
101
|
+
File.join(path, "")
|
102
|
+
end
|
85
103
|
|
86
|
-
|
104
|
+
def read_file(path)
|
105
|
+
File.send(File.respond_to?(:binread) ? :binread : :read, path.to_s)
|
106
|
+
end
|
107
|
+
end
|
87
108
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
109
|
+
# A null source file is used to represent source that either doesn't exist
|
110
|
+
# or source that is a proxy (ie a combination)
|
111
|
+
class Dassets::NullSourceFile < Dassets::SourceFile
|
112
|
+
def initialize(digest_path, **options)
|
113
|
+
@file_path = ""
|
114
|
+
@ext_list = []
|
115
|
+
@digest_path = digest_path
|
116
|
+
@source_proxy =
|
117
|
+
if Dassets.config.combination?(@digest_path)
|
118
|
+
Dassets::SourceProxy.new(@digest_path, **options)
|
93
119
|
else
|
94
|
-
NullSourceProxy.new
|
120
|
+
Dassets::NullSourceProxy.new
|
95
121
|
end
|
96
|
-
|
97
|
-
|
98
|
-
def compiled; @source_proxy.content; end
|
99
|
-
def exists?; @source_proxy.exists?; end
|
100
|
-
def mtime; @source_proxy.mtime; end
|
122
|
+
end
|
101
123
|
|
102
|
-
|
103
|
-
|
104
|
-
|
124
|
+
def compiled
|
125
|
+
@source_proxy.content
|
126
|
+
end
|
105
127
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
def mtime; nil; end
|
110
|
-
end
|
128
|
+
def exists?
|
129
|
+
@source_proxy.exists?
|
130
|
+
end
|
111
131
|
|
132
|
+
def mtime
|
133
|
+
@source_proxy.mtime
|
112
134
|
end
|
113
135
|
|
136
|
+
def ==(other)
|
137
|
+
if other.is_a?(self.class)
|
138
|
+
file_path == other.file_path
|
139
|
+
else
|
140
|
+
super
|
141
|
+
end
|
142
|
+
end
|
114
143
|
end
|
data/lib/dassets/source_proxy.rb
CHANGED
@@ -1,42 +1,45 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "digest/md5"
|
4
|
+
require "dassets/cache"
|
5
|
+
require "dassets/source_file"
|
4
6
|
|
5
7
|
module Dassets; end
|
6
|
-
class Dassets::SourceProxy
|
7
8
|
|
9
|
+
class Dassets::SourceProxy
|
8
10
|
attr_reader :digest_path, :content_cache, :fingerprint_cache
|
9
11
|
attr_reader :source_files
|
10
12
|
|
11
|
-
def initialize(digest_path, options
|
12
|
-
options ||= {}
|
13
|
+
def initialize(digest_path, **options)
|
13
14
|
@digest_path = digest_path
|
14
|
-
@content_cache = options[:content_cache] || Dassets::
|
15
|
-
@fingerprint_cache = options[:fingerprint_cache] || Dassets::
|
16
|
-
@source_files =
|
17
|
-
|
18
|
-
|
19
|
-
|
15
|
+
@content_cache = options[:content_cache] || Dassets::NoCache.new
|
16
|
+
@fingerprint_cache = options[:fingerprint_cache] || Dassets::NoCache.new
|
17
|
+
@source_files =
|
18
|
+
get_source_files(
|
19
|
+
@digest_path,
|
20
|
+
content_cache: @content_cache,
|
21
|
+
fingerprint_cache: @fingerprint_cache,
|
22
|
+
)
|
20
23
|
end
|
21
24
|
|
22
25
|
def key
|
23
|
-
"#{
|
26
|
+
"#{digest_path} -- #{mtime}"
|
24
27
|
end
|
25
28
|
|
26
29
|
def content
|
27
|
-
@content_cache[
|
30
|
+
@content_cache[key] ||= source_content
|
28
31
|
end
|
29
32
|
|
30
33
|
def fingerprint
|
31
|
-
@fingerprint_cache[
|
34
|
+
@fingerprint_cache[key] ||= source_fingerprint
|
32
35
|
end
|
33
36
|
|
34
37
|
def mtime
|
35
|
-
@source_files.map
|
38
|
+
@source_files.map(&:mtime).compact.max
|
36
39
|
end
|
37
40
|
|
38
41
|
def response_headers
|
39
|
-
@source_files.inject(
|
42
|
+
@source_files.inject({}){ |hash, f| hash.merge!(f.response_headers) }
|
40
43
|
end
|
41
44
|
|
42
45
|
def exists?
|
@@ -46,17 +49,30 @@ class Dassets::SourceProxy
|
|
46
49
|
private
|
47
50
|
|
48
51
|
def source_content
|
49
|
-
@source_files.map
|
52
|
+
@source_files.map(&:compiled).join("\n")
|
50
53
|
end
|
51
54
|
|
52
55
|
def source_fingerprint
|
53
56
|
Digest::MD5.new.hexdigest(source_content)
|
54
57
|
end
|
55
58
|
|
56
|
-
def get_source_files(digest_path, options)
|
59
|
+
def get_source_files(digest_path, **options)
|
57
60
|
Dassets.config.combinations[digest_path.to_s].map do |source_digest_path|
|
58
|
-
Dassets::SourceFile.find_by_digest_path(source_digest_path, options)
|
61
|
+
Dassets::SourceFile.find_by_digest_path(source_digest_path, **options)
|
59
62
|
end
|
60
63
|
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class Dassets::NullSourceProxy
|
67
|
+
def content
|
68
|
+
nil
|
69
|
+
end
|
70
|
+
|
71
|
+
def exists?
|
72
|
+
false
|
73
|
+
end
|
61
74
|
|
75
|
+
def mtime
|
76
|
+
nil
|
77
|
+
end
|
62
78
|
end
|
data/lib/dassets/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -1,43 +1,49 @@
|
|
1
|
-
#
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# This file is automatically required when you run `assert`
|
4
|
+
# put any test helpers here.
|
3
5
|
|
4
6
|
# add the root dir to the load path
|
5
7
|
$LOAD_PATH.unshift(File.expand_path("../..", __FILE__))
|
6
8
|
|
7
9
|
# require pry for debugging (`binding.pry`)
|
8
|
-
require
|
10
|
+
require "pry"
|
9
11
|
|
10
|
-
require
|
12
|
+
require "test/support/factory"
|
11
13
|
|
12
|
-
|
14
|
+
require "pathname"
|
15
|
+
TEST_SUPPORT_PATH = Pathname.new(File.expand_path("../support", __FILE__))
|
13
16
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
alias_method :sample, :choice
|
18
|
-
end
|
19
|
-
end
|
17
|
+
ENV["DASSETS_TEST_MODE"] = "yes"
|
18
|
+
|
19
|
+
require "dassets"
|
20
20
|
|
21
|
-
|
22
|
-
|
21
|
+
@dumb_engine =
|
22
|
+
Class.new(Dassets::Engine) do
|
23
|
+
def ext(_in_ext)
|
24
|
+
""
|
25
|
+
end
|
23
26
|
|
24
|
-
|
27
|
+
def compile(input)
|
28
|
+
"#{input}\nDUMB"
|
29
|
+
end
|
30
|
+
end
|
25
31
|
|
26
|
-
|
32
|
+
@useless_engine =
|
33
|
+
Class.new(Dassets::Engine) do
|
34
|
+
def ext(_in_ext)
|
35
|
+
"no-use"
|
36
|
+
end
|
27
37
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
end
|
32
|
-
@useless_engine = Class.new(Dassets::Engine) do
|
33
|
-
def ext(in_ext); 'no-use'; end
|
34
|
-
def compile(input); "#{input}\nUSELESS"; end
|
35
|
-
end
|
38
|
+
def compile(input)
|
39
|
+
"#{input}\nUSELESS"
|
40
|
+
end
|
41
|
+
end
|
36
42
|
|
37
43
|
Dassets.configure do |c|
|
38
44
|
c.source TEST_SUPPORT_PATH.join("app/assets") do |s|
|
39
|
-
s.engine
|
40
|
-
s.engine
|
45
|
+
s.engine "dumb", @dumb_engine
|
46
|
+
s.engine "useless", @useless_engine
|
41
47
|
s.response_headers[Factory.string] = Factory.string
|
42
48
|
end
|
43
49
|
end
|