dassets 0.14.3 → 0.15.2

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.
Files changed (40) hide show
  1. checksums.yaml +7 -7
  2. data/Gemfile +5 -1
  3. data/README.md +15 -17
  4. data/dassets.gemspec +14 -9
  5. data/lib/dassets.rb +51 -13
  6. data/lib/dassets/asset_file.rb +27 -24
  7. data/lib/dassets/cache.rb +27 -33
  8. data/lib/dassets/config.rb +55 -50
  9. data/lib/dassets/engine.rb +11 -23
  10. data/lib/dassets/file_store.rb +27 -28
  11. data/lib/dassets/server.rb +27 -27
  12. data/lib/dassets/server/request.rb +44 -41
  13. data/lib/dassets/server/response.rb +101 -80
  14. data/lib/dassets/source.rb +24 -9
  15. data/lib/dassets/source_file.rb +110 -81
  16. data/lib/dassets/source_proxy.rb +36 -20
  17. data/lib/dassets/version.rb +3 -1
  18. data/test/helper.rb +31 -25
  19. data/test/support/app.rb +5 -5
  20. data/test/support/empty/{.gitkeep → .keep} +0 -0
  21. data/test/support/factory.rb +3 -2
  22. data/test/support/linked_source_files/linked_file.txt +0 -0
  23. data/test/support/source_files/linked +1 -0
  24. data/test/support/source_files/linked_file2.txt +1 -0
  25. data/test/system/rack_tests.rb +65 -61
  26. data/test/unit/asset_file_tests.rb +68 -60
  27. data/test/unit/cache_tests.rb +15 -34
  28. data/test/unit/config_tests.rb +58 -51
  29. data/test/unit/dassets_tests.rb +31 -24
  30. data/test/unit/engine_tests.rb +9 -43
  31. data/test/unit/file_store_tests.rb +34 -24
  32. data/test/unit/server/request_tests.rb +57 -59
  33. data/test/unit/server/response_tests.rb +82 -82
  34. data/test/unit/server_tests.rb +5 -9
  35. data/test/unit/source_file_tests.rb +86 -74
  36. data/test/unit/source_proxy_tests.rb +84 -90
  37. data/test/unit/source_tests.rb +89 -50
  38. data/tmp/.gitkeep +0 -0
  39. metadata +92 -64
  40. data/.gitignore +0 -19
@@ -1,25 +1,36 @@
1
- require 'dassets/engine'
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{ |h,k| Dassets::NullEngine.new }
12
- @response_headers = Hash.new
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 = { 'source_path' => @path }
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] = engine_class.new(engine_opts)
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.glob(File.join(@path, "**/*")).reject!{ |p| !File.file?(p) }
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
@@ -1,114 +1,143 @@
1
- require 'fileutils'
2
- require 'dassets'
3
- require 'dassets/asset_file'
4
- require 'dassets/source_proxy'
1
+ # frozen_string_literal: true
5
2
 
6
- module Dassets
3
+ require "fileutils"
4
+ require "dassets"
5
+ require "dassets/asset_file"
6
+ require "dassets/source_proxy"
7
7
 
8
- class SourceFile
8
+ module Dassets; end
9
9
 
10
- def self.find_by_digest_path(path, options = nil)
11
- Dassets.source_files[path] || NullSourceFile.new(path, options)
12
- end
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
- attr_reader :file_path
15
+ attr_reader :file_path
15
16
 
16
- def initialize(file_path)
17
- @file_path = file_path.to_s
18
- @ext_list = File.basename(@file_path).split('.').reverse
19
- end
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
- # get the last matching one (in the case two sources with the same path are
22
- # configured) since we select the last matching source file (from the last
23
- # configured source) in `find_by_digest_path` above.
24
- def source
25
- @source ||= Dassets.config.sources.select do |source|
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
- end.last
28
- end
29
-
30
- def asset_file
31
- @asset_file ||= Dassets::AssetFile.new(self.digest_path)
32
- end
29
+ }.last
30
+ end
33
31
 
34
- def digest_path
35
- @digest_path ||= begin
36
- digest_basename = @ext_list.inject([]) do |digest_ext_list, ext|
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
- File.join([digest_dirname(@file_path), digest_basename].reject(&:empty?))
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
- end
59
+ end
43
60
 
44
- def compiled
45
- @ext_list.inject(read_file(@file_path)) do |content, ext|
46
- self.source.engines[ext].compile(content)
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
- def exists?
51
- File.file?(@file_path)
52
- end
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
- def ==(other_source_file)
63
- self.file_path == other_source_file.file_path
64
- end
73
+ def mtime
74
+ File.mtime(@file_path)
75
+ end
65
76
 
66
- private
77
+ def base_path
78
+ source&.base_path.to_s
79
+ end
67
80
 
68
- # remove the source path from the dirname (if it exists)
69
- def digest_dirname(file_path)
70
- slash_path(File.dirname(file_path)).sub(slash_path(self.source.path), '')
71
- end
81
+ def response_headers
82
+ source.nil? ? {} : source.response_headers
83
+ end
72
84
 
73
- def slash_path(path)
74
- File.join(path, '')
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
- def read_file(path)
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
- # A null source file is used to represent source that either doesn't exist
84
- # or source that is a proxy (ie a combination)
100
+ def slash_path(path)
101
+ File.join(path, "")
102
+ end
85
103
 
86
- class NullSourceFile < SourceFile
104
+ def read_file(path)
105
+ File.send(File.respond_to?(:binread) ? :binread : :read, path.to_s)
106
+ end
107
+ end
87
108
 
88
- def initialize(digest_path, options = nil)
89
- @file_path, @ext_list = '', []
90
- @digest_path = digest_path
91
- @source_proxy = if Dassets.config.combination?(@digest_path)
92
- SourceProxy.new(@digest_path, options)
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
- end
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
- def ==(other_source_file)
103
- self.file_path == other_source_file.file_path
104
- end
124
+ def compiled
125
+ @source_proxy.content
126
+ end
105
127
 
106
- class NullSourceProxy
107
- def content; nil; end
108
- def exists?; false; end
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
@@ -1,42 +1,45 @@
1
- require 'digest/md5'
2
- require 'dassets/cache'
3
- require 'dassets/source_file'
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 = nil)
12
- options ||= {}
13
+ def initialize(digest_path, **options)
13
14
  @digest_path = digest_path
14
- @content_cache = options[:content_cache] || Dassets::Cache::NoCache.new
15
- @fingerprint_cache = options[:fingerprint_cache] || Dassets::Cache::NoCache.new
16
- @source_files = get_source_files(@digest_path, {
17
- :content_cache => @content_cache,
18
- :fingerprint_cache => @fingerprint_cache
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
- "#{self.digest_path} -- #{self.mtime}"
26
+ "#{digest_path} -- #{mtime}"
24
27
  end
25
28
 
26
29
  def content
27
- @content_cache[self.key] ||= source_content
30
+ @content_cache[key] ||= source_content
28
31
  end
29
32
 
30
33
  def fingerprint
31
- @fingerprint_cache[self.key] ||= source_fingerprint
34
+ @fingerprint_cache[key] ||= source_fingerprint
32
35
  end
33
36
 
34
37
  def mtime
35
- @source_files.map{ |f| f.mtime }.compact.max
38
+ @source_files.map(&:mtime).compact.max
36
39
  end
37
40
 
38
41
  def response_headers
39
- @source_files.inject(Hash.new){ |hash, f| hash.merge!(f.response_headers) }
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{ |f| f.compiled }.join("\n")
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
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dassets
2
- VERSION = "0.14.3"
4
+ VERSION = "0.15.2"
3
5
  end
@@ -1,43 +1,49 @@
1
- # this file is automatically required when you run `assert`
2
- # put any test helpers here
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 'pry'
10
+ require "pry"
9
11
 
10
- require 'test/support/factory'
12
+ require "test/support/factory"
11
13
 
12
- # 1.8.7 backfills
14
+ require "pathname"
15
+ TEST_SUPPORT_PATH = Pathname.new(File.expand_path("../support", __FILE__))
13
16
 
14
- # Array#sample
15
- if !(a = Array.new).respond_to?(:sample) && a.respond_to?(:choice)
16
- class Array
17
- alias_method :sample, :choice
18
- end
19
- end
17
+ ENV["DASSETS_TEST_MODE"] = "yes"
18
+
19
+ require "dassets"
20
20
 
21
- require 'pathname'
22
- TEST_SUPPORT_PATH = Pathname.new(File.expand_path('../support', __FILE__))
21
+ @dumb_engine =
22
+ Class.new(Dassets::Engine) do
23
+ def ext(_in_ext)
24
+ ""
25
+ end
23
26
 
24
- ENV['DASSETS_TEST_MODE'] = 'yes'
27
+ def compile(input)
28
+ "#{input}\nDUMB"
29
+ end
30
+ end
25
31
 
26
- require 'dassets'
32
+ @useless_engine =
33
+ Class.new(Dassets::Engine) do
34
+ def ext(_in_ext)
35
+ "no-use"
36
+ end
27
37
 
28
- @dumb_engine = Class.new(Dassets::Engine) do
29
- def ext(in_ext); ''; end
30
- def compile(input); "#{input}\nDUMB"; end
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 'dumb', @dumb_engine
40
- s.engine 'useless', @useless_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