dassets 0.14.2 → 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.
Files changed (43) 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 -27
  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 +15 -9
  15. data/lib/dassets/source_file.rb +103 -82
  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/{public/nested/file3-d41d8cd98f00b204e9800998ecf8427e.txt → 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 +69 -61
  27. data/test/unit/cache_tests.rb +15 -34
  28. data/test/unit/config_tests.rb +59 -52
  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 +44 -31
  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 +80 -73
  36. data/test/unit/source_proxy_tests.rb +84 -90
  37. data/test/unit/source_tests.rb +66 -50
  38. data/tmp/.gitkeep +0 -0
  39. metadata +92 -72
  40. data/.gitignore +0 -19
  41. data/test/support/public/file2-9bbe1047cffbb590f59e0e5aeff46ae4.txt +0 -1
  42. data/test/support/public/grumpy_cat-b0d1f399a916f7a25c4c0f693c619013.jpg +0 -0
  43. data/test/support/public/nested/a-thing.txt-7413d18f2eba9c695a880aff67fde135.no-use +0 -4
@@ -1,25 +1,27 @@
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 = {}
13
15
  end
14
16
 
15
17
  def filter(&block)
16
18
  block.nil? ? @filter : @filter = block
17
19
  end
18
20
 
19
- def engine(input_ext, engine_class, registered_opts=nil)
20
- default_opts = { 'source_path' => @path }
21
+ def engine(input_ext, engine_class, registered_opts = nil)
22
+ default_opts = { "source_path" => @path }
21
23
  engine_opts = default_opts.merge(registered_opts || {})
22
- @engines[input_ext.to_s] = engine_class.new(engine_opts)
24
+ @engines[input_ext.to_s] << engine_class.new(engine_opts)
23
25
  end
24
26
 
25
27
  def files
@@ -28,12 +30,16 @@ class Dassets::Source
28
30
 
29
31
  private
30
32
 
33
+ # Use "**{,/*/**}/*" to glob following symlinks and returning immediate-child
34
+ # matches. See https://stackoverflow.com/a/2724048.
31
35
  def glob_files
32
- Dir.glob(File.join(@path, "**/*")).reject!{ |p| !File.file?(p) }
36
+ Dir
37
+ .glob(File.join(@path, "**{,/*/**}/*"))
38
+ .uniq
39
+ .reject{ |path| !File.file?(path) }
33
40
  end
34
41
 
35
42
  def apply_filter(files)
36
43
  @filter.call(files)
37
44
  end
38
-
39
45
  end
@@ -1,114 +1,135 @@
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
+ [digest_dirname(@file_path), digest_basename].reject(&:empty?),
53
+ )
41
54
  end
42
- end
55
+ end
43
56
 
44
- def compiled
45
- @ext_list.inject(read_file(@file_path)) do |content, ext|
46
- self.source.engines[ext].compile(content)
57
+ def compiled
58
+ @ext_list.reduce(read_file(@file_path)) do |file_acc, ext|
59
+ source.engines[ext].reduce(file_acc) do |ext_acc, engine|
60
+ engine.compile(ext_acc)
47
61
  end
48
62
  end
63
+ end
49
64
 
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
61
-
62
- def ==(other_source_file)
63
- self.file_path == other_source_file.file_path
64
- end
65
+ def exists?
66
+ File.file?(@file_path)
67
+ end
65
68
 
66
- private
69
+ def mtime
70
+ File.mtime(@file_path)
71
+ end
67
72
 
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
73
+ def response_headers
74
+ source.nil? ? {} : source.response_headers
75
+ end
72
76
 
73
- def slash_path(path)
74
- File.join(path, '')
77
+ def ==(other)
78
+ if other.is_a?(self.class)
79
+ file_path == other.file_path
80
+ else
81
+ super
75
82
  end
83
+ end
76
84
 
77
- def read_file(path)
78
- File.send(File.respond_to?(:binread) ? :binread : :read, path.to_s)
79
- end
85
+ private
80
86
 
87
+ # remove the source path from the dirname (if it exists)
88
+ def digest_dirname(file_path)
89
+ slash_path(File.dirname(file_path)).sub(slash_path(source.path), "")
81
90
  end
82
91
 
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)
92
+ def slash_path(path)
93
+ File.join(path, "")
94
+ end
85
95
 
86
- class NullSourceFile < SourceFile
96
+ def read_file(path)
97
+ File.send(File.respond_to?(:binread) ? :binread : :read, path.to_s)
98
+ end
99
+ end
87
100
 
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)
101
+ # A null source file is used to represent source that either doesn't exist
102
+ # or source that is a proxy (ie a combination)
103
+ class Dassets::NullSourceFile < Dassets::SourceFile
104
+ def initialize(digest_path, **options)
105
+ @file_path = ""
106
+ @ext_list = []
107
+ @digest_path = digest_path
108
+ @source_proxy =
109
+ if Dassets.config.combination?(@digest_path)
110
+ Dassets::SourceProxy.new(@digest_path, **options)
93
111
  else
94
- NullSourceProxy.new
112
+ Dassets::NullSourceProxy.new
95
113
  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
114
+ end
101
115
 
102
- def ==(other_source_file)
103
- self.file_path == other_source_file.file_path
104
- end
116
+ def compiled
117
+ @source_proxy.content
118
+ end
105
119
 
106
- class NullSourceProxy
107
- def content; nil; end
108
- def exists?; false; end
109
- def mtime; nil; end
110
- end
120
+ def exists?
121
+ @source_proxy.exists?
122
+ end
111
123
 
124
+ def mtime
125
+ @source_proxy.mtime
112
126
  end
113
127
 
128
+ def ==(other)
129
+ if other.is_a?(self.class)
130
+ file_path == other.file_path
131
+ else
132
+ super
133
+ end
134
+ end
114
135
  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.2"
4
+ VERSION = "0.15.1"
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