dassets 0.14.3 → 0.15.2

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