Module: Condenser::Resolve

Included in:
Condenser
Defined in:
lib/condenser/resolve.rb

Instance Method Summary collapse

Instance Method Details

#[](filename) ⇒ Object



110
111
112
113
114
# File 'lib/condenser/resolve.rb', line 110

def [](filename)
  build do
    find!(filename).export
  end
end

#buildObject



123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/condenser/resolve.rb', line 123

def build
  @build_cc += 1
  if @build_cc == 1
    @build_cache = {}
  end
  yield
ensure
  @build_cc -= 1
  if @build_cc == 0
    @build_cache = nil
  end
end

#decompose_path(path, base = nil) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'lib/condenser/resolve.rb', line 136

def decompose_path(path, base=nil)
  dirname = path.index('/') ? File.dirname(path) : nil
  if base && path&.start_with?('.')
    dirname = File.expand_path(dirname, base)
  end
  
  _, star, basename, extensions = path.match(/(([^\.\/]+)(\.[^\/]+)|\*|[^\/]+)$/).to_a
  if extensions == '.*'
    extensions = nil
  end
  if basename.nil? && extensions.nil?
    basename = star
  end
    
  if extensions.nil? && basename == '*'
    extensions = nil
    mime_types = []
  elsif extensions.nil?
    mime_types = []
  else
    exts = []
    while !extensions.empty?
      matching_extensions = @extensions.keys.select { |e| extensions.end_with?(e) }
      if matching_extensions.empty?
        basename << extensions
        break
        # raise 'unkown mime'
      else
        matching_extensions.sort_by! { |e| -e.length }
        exts.unshift(matching_extensions.first)
        extensions.delete_suffix!(matching_extensions.first)
      end
    end
    extensions = exts
    mime_types = extensions.map { |k| @extensions[k] }
  end
  
  
  [ dirname, basename, extensions, mime_types ]
end

#find(filename, base = nil, accept: nil, ignore: []) ⇒ Object



98
99
100
101
102
# File 'lib/condenser/resolve.rb', line 98

def find(filename, base=nil, accept: nil, ignore: [])
  build do
    @build_cache["find/#{[filename, base, accept].join('-')}"] ||= resolve(filename, base, accept: accept, ignore: ignore).first
  end
end

#find!(filename, base = nil, **kargs) ⇒ Object



104
105
106
107
108
# File 'lib/condenser/resolve.rb', line 104

def find!(filename, base=nil, **kargs)
  build do
    resolve!(filename, base, **kargs).first
  end
end

#find_export(filename, base = nil, **kargs) ⇒ Object



116
117
118
119
120
121
# File 'lib/condenser/resolve.rb', line 116

def find_export(filename, base=nil, **kargs)
  build do
    asset = resolve(filename, base, **kargs).first
    asset&.export
  end
end

#initialize(*args) ⇒ Object



4
5
6
7
# File 'lib/condenser/resolve.rb', line 4

def initialize(*args)
  @reverse_mapping = nil
  super
end

#match_mime_type?(value, matcher) ⇒ Boolean

Public: Test mime type against mime range.

match_mime_type?('text/html', 'text/*') => true
match_mime_type?('text/plain', '*') => true
match_mime_type?('text/html', 'application/json') => false

Returns true if the given value is a mime match for the given mime match specification, false otherwise.

Returns:

  • (Boolean)


229
230
231
232
233
# File 'lib/condenser/resolve.rb', line 229

def match_mime_type?(value, matcher)
  v1, v2 = value.split('/'.freeze, 2)
  m1, m2 = matcher.split('/'.freeze, 2)
  (m1 == '*'.freeze || v1 == m1) && (m2.nil? || m2 == '*'.freeze || m2 == v2)
end

#match_mime_types?(value, matcher) ⇒ Boolean

Returns:

  • (Boolean)


204
205
206
207
208
209
210
211
212
213
# File 'lib/condenser/resolve.rb', line 204

def match_mime_types?(value, matcher)
  matcher = Array(matcher)
  value = Array(value)
  
  if matcher.length == 1 && matcher.last == '*/*'
    true
  else
    value.length == matcher.length && value.zip(matcher).all? { |v, m| match_mime_type?(v, m) }
  end
end

#mime_type_match_accept?(value, accept) ⇒ Boolean

Returns:

  • (Boolean)


215
216
217
218
219
# File 'lib/condenser/resolve.rb', line 215

def mime_type_match_accept?(value, accept)
  accept.any? do |a|
    match_mime_types?(value, Array(a))
  end
end

#resolve(filename, base = nil, accept: nil, ignore: []) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/condenser/resolve.rb', line 9

def resolve(filename, base=nil, accept: nil, ignore: [])
  filename = filename.delete_prefix("/") if path.none? { |p| filename.start_with?(p) }
  
  build do
    dirname, basename, extensions, mime_types = decompose_path(filename, base)

    results = []
  
    accept ||= mime_types.empty? ? ['*/*'] : mime_types
    accept = Array(accept)
  
    paths = if dirname&.start_with?('/')
      if pat = path.find { |pa| dirname.start_with?(pa) }
        dirname.delete_prefix!(pat)
        dirname.delete_prefix!('/')
        [pat]
      else
        []
      end
    else
      path
    end

    paths.each do |path|
      glob = path
      glob = File.join(glob, dirname) if dirname
      glob = File.join(glob, basename)
      glob << '.*' unless glob.end_with?('*')
      
      Dir.glob(glob).sort.each do |f|
        next if !File.file?(f) || ignore.include?(f)
      
        f_dirname, f_basename, f_extensions, f_mime_types = decompose_path(f)
        if (basename == '*' || basename == f_basename)
          if accept == ['*/*'] || mime_type_match_accept?(f_mime_types, accept)
            asset_dir = f_dirname.delete_prefix(path).delete_prefix('/')
            asset_basename = f_basename + f_extensions.join('')
            asset_filename = asset_dir.empty? ? asset_basename : File.join(asset_dir, asset_basename)
            @build_cache[asset_filename] ||= Asset.new(self, {
              filename: asset_filename,
              content_types: f_mime_types,
              source_file: f,
              source_path: path
            })
            results << @build_cache[asset_filename]
          else
            reverse_mapping[f_mime_types]&.each do |derivative_mime_types|
              if accept == ['*/*'] || mime_type_match_accept?(derivative_mime_types, accept)
                asset_dir = f_dirname.delete_prefix(path).delete_prefix('/')
                asset_basename = f_basename + derivative_mime_types.map { |t| @mime_types[t][:extensions].first }.join('')
                asset_filename = asset_dir.empty? ? asset_basename : File.join(asset_dir, asset_basename)
                @build_cache[asset_filename] ||= Asset.new(self, {
                  filename: asset_filename,
                  content_types: derivative_mime_types,
                  source_file: f,
                  source_path: path
                })
                results << @build_cache[asset_filename]
              end
            end
          end

        end
      end
    end
    
    results = results.group_by do |a|
      accept.find_index { |m| match_mime_types?(a.content_types, m) }
    end
    
    results = results.keys.sort.reduce([]) do |c, key|
      c += results[key].sort_by(&:filename)
    end

    results.sort_by(&:filename)
  end
end

#resolve!(filename, base = nil, **kargs) ⇒ Object



87
88
89
90
91
92
93
94
95
96
# File 'lib/condenser/resolve.rb', line 87

def resolve!(filename, base=nil, **kargs)
  build do
    assets = resolve(filename, base, **kargs)
    if assets.empty?
      raise FileNotFound, "couldn't find file '#{filename}'"
    else
      assets
    end
  end
end

#reverse_mappingObject



177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/condenser/resolve.rb', line 177

def reverse_mapping
  return @reverse_mapping if @reverse_mapping
  map = {}
  @mime_types.each_key do |source_mime_type|
    to_mime_type = source_mime_type

    ([nil] + (@transformers[source_mime_type]&.keys || [])).each do |transform_mime_type|
      to_mime_type = transform_mime_type if transform_mime_type
      
      ([nil] + @templates.keys).each do |template_mime_type|
        from_mimes = [source_mime_type, template_mime_type].compact
        to_mime_types = [to_mime_type].compact
        if from_mimes != to_mime_types
          map[from_mimes] ||= Set.new
          map[from_mimes] << to_mime_types
        end
      end

    end
  end
  $map = map
end

#writers_for_mime_type(mime_type) ⇒ Object



200
201
202
# File 'lib/condenser/resolve.rb', line 200

def writers_for_mime_type(mime_type)
  @writers.select { |m, e| match_mime_type?(mime_type, m) }.values.reduce(&:+)
end