Module: Middleman::Util
- Extended by:
- Memoist
- Includes:
- Contracts
- Defined in:
- middleman-core/lib/middleman-core/util.rb,
middleman-core/lib/middleman-core/util/data.rb,
middleman-core/lib/middleman-core/util/rack.rb,
middleman-core/lib/middleman-core/util/files.rb,
middleman-core/lib/middleman-core/util/paths.rb,
middleman-core/lib/middleman-core/util/binary.rb,
middleman-core/lib/middleman-core/util/uri_templates.rb
Defined Under Namespace
Modules: Data, UriTemplates Classes: BlogTemplateProcessor, EnhancedHash
Constant Summary collapse
- IGNORE_DESCRIPTOR =
Or[Regexp, RespondTo[:call], String]
- IGNORED_ASSET_EXTENSIONS =
Set.new %i[images fonts]
Constants included from Contracts
Class Method Summary collapse
- .all_files_under(path, &ignore) ⇒ Object
- .asset_path(app, kind, source, options_hash = ::Middleman::EMPTY_HASH) ⇒ Object
- .asset_url(app, path, prefix = '', options_hash = ::Middleman::EMPTY_HASH) ⇒ Object
- .binary?(filename) ⇒ Boolean
- .collect_extensions(path) ⇒ Object
- .contains_frontmatter?(path, frontmatter_delims) ⇒ Boolean
-
.current_directory ⇒ Array<String>
Get the PWD and try to keep path encoding consistent.
-
.each ⇒ String
Extract the text of a Rack response as a string.
- .extract_response_text(response) ⇒ Object
- .file_contents_include_binary_bytes?(filename) ⇒ Boolean
- .find_related_files(app, files) ⇒ Object
- .full_path(path, app) ⇒ Object
-
.glob_directory(path) ⇒ Array<String>
Glob a directory and try to keep path encoding consistent.
- .hash_file(path) ⇒ Object
- .hash_string(data) ⇒ Object
-
.instrument(name, payload = {}, &block) ⇒ Object
Facade for ActiveSupport/Notification.
- .nonbinary_mime?(mime) ⇒ Boolean
- .normalize_path(path) ⇒ Object
- .parse_uri(uri) ⇒ Object
- .path_match(matcher, path) ⇒ Object
-
.PATH_MATCHER ⇒ Boolean
Takes a matcher, which can be a literal string or a string containing glob expressions, or a regexp, or a proc, or anything else that responds to #match or #call, and returns whether or not the given path matches that matcher.
- .possible_delim_openers(frontmatter_delims) ⇒ Object
- .read_file(path, bytes = nil) ⇒ Object
- .recursively_enhance(obj) ⇒ Object
- .relative_path_from_resource(curr_resource, resource_url, relative) ⇒ Object
- .remove_templating_extensions(path) ⇒ Object
- .rewrite_paths(body, path, exts, app, &_block) ⇒ Object
- .should_ignore?(validator, value) ⇒ Boolean
- .static_file?(path, frontmatter_delims) ⇒ Boolean
- .step_through_extensions(path) {|::File.extname(path)| ... } ⇒ Object
-
.String ⇒ String
Expand a path to include the index file if it's a directory.
- .strip_leading_slash(path) ⇒ Object
- .tilt_class(path) ⇒ Object
- .url_for(app, path_or_resource, options_hash = ::Middleman::EMPTY_HASH) ⇒ Object
Methods included from Contracts
Class Method Details
.all_files_under(path, &ignore) ⇒ Object
22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 22 def all_files_under(path, &ignore) path = Pathname(path) if ignore && yield(path) [] elsif path.directory? path.children.flat_map do |child| all_files_under(child, &ignore) end.compact elsif path.file? [path] else [] end end |
.asset_path(app, kind, source, options_hash = ::Middleman::EMPTY_HASH) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 81 def asset_path(app, kind, source, = ::Middleman::EMPTY_HASH) return source if source.to_s.include?('//') || source.to_s.start_with?('data:') asset_folder = case kind when :css app.config[:css_dir] when :js app.config[:js_dir] when :images app.config[:images_dir] when :fonts app.config[:fonts_dir] else kind.to_s end source = source.to_s.tr(' ', '') ignore_extension = IGNORED_ASSET_EXTENSIONS.include? kind # don't append extension source = "#{source}.#{kind}" unless ignore_extension || source.end_with?(".#{kind}") asset_folder = '' if source.start_with?('/') # absolute path asset_url(app, source, asset_folder, ) end |
.asset_url(app, path, prefix = '', options_hash = ::Middleman::EMPTY_HASH) ⇒ Object
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 112 def asset_url(app, path, prefix = '', = ::Middleman::EMPTY_HASH) # Don't touch assets which already have a full path return path if path.include?('//') || path.start_with?('data:') raise ArgumentError, '#asset_url must be run in a context with current_resource if relative: true' if [:relative] && ![:current_resource] uri = ::Middleman::Util.parse_uri(path) path = uri.path # Ensure the url we pass into by_destination_path is not a # relative path, since it only takes absolute url paths. dest_path = url_for(app, path, .merge(relative: false)) resource = app.sitemap.by_path(dest_path) || app.sitemap.by_destination_path(dest_path) result = if resource resource.url else path = ::File.join(prefix, path) resource = app.sitemap.by_path(path) if resource resource.url else ::File.join(app.config[:http_prefix], path) end end final_result = ::Addressable::URI.encode( relative_path_from_resource( [:current_resource], result, [:relative] ) ) result_uri = ::Middleman::Util.parse_uri(final_result) result_uri.query = uri.query result_uri.fragment = uri.fragment result_uri.to_s end |
.binary?(filename) ⇒ Boolean
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'middleman-core/lib/middleman-core/util/binary.rb', line 282 def binary?(filename) @binary_cache ||= {} return @binary_cache[filename] if @binary_cache.key?(filename) @binary_cache[filename] = begin path = Pathname(filename) ext = path.extname without_dot = ext.sub('.', '') # We hardcode detecting of gzipped SVG files if KNOWN_BINARY_FILE_EXTENSIONS.include?(without_dot) true elsif ::Tilt.registered?(without_dot) false else dot_ext = ext.to_s[0] == '.' ? ext.dup : ".#{ext}" mime = ::Rack::Mime.mime_type(dot_ext, nil) if mime !nonbinary_mime?(mime) else file_contents_include_binary_bytes?(path.to_s) end end end end |
.collect_extensions(path) ⇒ Object
90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 90 def collect_extensions(path) @@extensions_cache ||= {} base_name = ::File.basename(path) @@extensions_cache[base_name] ||= begin result = [] step_through_extensions(base_name) { |e| result << e } unless base_name.start_with?('.') result end end |
.contains_frontmatter?(path, frontmatter_delims) ⇒ Boolean
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 |
# File 'middleman-core/lib/middleman-core/util/binary.rb', line 359 def contains_frontmatter?(path, frontmatter_delims) file = ::File.open(path) first_three_lines = [file.gets, file.gets, file.gets] file.close first_two_lines = if /\A(?:[^\r\n]*coding:[^\r\n]*\r?\n)/.match?(first_three_lines.first) [first_three_lines[1], first_three_lines[2]] else [first_three_lines[0], first_three_lines[1]] end possible_openers = possible_delim_openers(frontmatter_delims) !first_two_lines[0].nil? && possible_openers.any? do |delim| !first_two_lines.join('').match(/\A#{delim}/).nil? end rescue EOFError, IOError, ::Errno::ENOENT false end |
.current_directory ⇒ Array<String>
Get the PWD and try to keep path encoding consistent.
54 55 56 57 58 59 60 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 54 def current_directory result = ::Dir.pwd return result unless RUBY_PLATFORM.match?(/darwin/) result.encode('UTF-8', 'UTF-8-MAC') end |
.each ⇒ String
Extract the text of a Rack response as a string. Useful for extensions implemented as Rack middleware.
15 |
# File 'middleman-core/lib/middleman-core/util/rack.rb', line 15 Contract RespondTo[:each] => String |
.extract_response_text(response) ⇒ Object
16 17 18 19 20 21 22 23 |
# File 'middleman-core/lib/middleman-core/util/rack.rb', line 16 def extract_response_text(response) # The rack spec states all response bodies must respond to each result = [] response.each do |part, _| result << part end result.join end |
.file_contents_include_binary_bytes?(filename) ⇒ Boolean
335 336 337 338 339 340 341 342 343 |
# File 'middleman-core/lib/middleman-core/util/binary.rb', line 335 def file_contents_include_binary_bytes?(filename) binary_bytes = [0, 1, 2, 3, 4, 5, 6, 11, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 28, 29, 30, 31] s = ::File.read(filename, 4096) || '' s.each_byte do |c| return true if binary_bytes.include?(c) end false end |
.find_related_files(app, files) ⇒ Object
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 110 def (app, files) return [] if files.empty? file_set = ::Set.new(files) all_extensions = files.flat_map { |f| collect_extensions(f.to_s) } sass_type_aliasing = ['.scss', '.sass'] erb_type_aliasing = ['.erb', '.haml', '.slim'] all_extensions |= sass_type_aliasing unless (all_extensions & sass_type_aliasing).empty? all_extensions |= erb_type_aliasing unless (all_extensions & erb_type_aliasing).empty? all_extensions.uniq! app.sitemap.by_priority.select do |r| if r.file_descriptor local_extensions = collect_extensions(r.file_descriptor[:full_path].to_s) local_extensions |= sass_type_aliasing unless (local_extensions & sass_type_aliasing).empty? local_extensions |= erb_type_aliasing unless (local_extensions & erb_type_aliasing).empty? local_extensions.uniq! !(all_extensions & local_extensions).empty? && !file_set.include?(r.file_descriptor[:full_path]) else false end end.map(&:file_descriptor) end |
.full_path(path, app) ⇒ Object
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 251 def full_path(path, app) resource = app.sitemap.by_destination_path(path) unless resource # Try it with /index.html at the end indexed_path = ::File.join(path.sub(%r{/$}, ''), app.config[:index_file]) resource = app.sitemap.by_destination_path(indexed_path) end if resource "/#{resource.destination_path}" else "/#{normalize_path(path)}" end end |
.glob_directory(path) ⇒ Array<String>
Glob a directory and try to keep path encoding consistent.
42 43 44 45 46 47 48 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 42 def glob_directory(path) results = ::Dir[path] return results unless RUBY_PLATFORM.match?(/darwin/) results.map { |r| r.encode('UTF-8', 'UTF-8-MAC') } end |
.hash_file(path) ⇒ Object
146 147 148 149 150 151 152 153 154 155 156 157 158 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 146 def hash_file(path) # puts "Read (hash): #{path}" if ENV['MIDDLEMAN_SHELL_OUT_TO_GIT_HASH'] == 'true' output, status = ::Open3.capture2e("git hash-object #{::Shellwords.escape(path)}") raise "Failed to get hash for '#{path}' from git." if status.exitstatus != 0 || output.empty? output.strip else ::Digest::SHA1.file(path).hexdigest end end |
.hash_string(data) ⇒ Object
161 162 163 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 161 def hash_string(data) ::Digest::SHA1.hexdigest(data) end |
.instrument(name, payload = {}, &block) ⇒ Object
Facade for ActiveSupport/Notification
18 19 20 21 |
# File 'middleman-core/lib/middleman-core/util.rb', line 18 def instrument(name, payload = {}, &block) suffixed_name = /\.middleman$/.match?(name) ? name.dup : "#{name}.middleman" ::ActiveSupport::Notifications.instrument(suffixed_name, payload, &block) end |
.nonbinary_mime?(mime) ⇒ Boolean
316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'middleman-core/lib/middleman-core/util/binary.rb', line 316 def nonbinary_mime?(mime) if mime.start_with?('text/') true elsif mime.include?('xml') && !mime.include?('officedocument') true elsif mime.include?('json') true elsif mime.include?('javascript') true else false end end |
.normalize_path(path) ⇒ Object
35 36 37 38 39 40 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 35 def normalize_path(path) return path unless path.is_a?(String) # The tr call works around a bug in Ruby's Unicode handling Addressable::URI.unencode(path).sub(%r{^/}, '').tr('', '') end |
.parse_uri(uri) ⇒ Object
20 21 22 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 20 def parse_uri(uri) ::Addressable::URI.parse(uri) end |
.path_match(matcher, path) ⇒ Object
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 301 def path_match(matcher, path) if matcher.is_a?(String) if matcher.include? '*' ::File.fnmatch(matcher, path) else path == matcher end elsif matcher.respond_to?(:match) !matcher.match(path).nil? elsif matcher.respond_to?(:call) matcher.call(path) else ::File.fnmatch(matcher.to_s, path) end end |
.PATH_MATCHER ⇒ Boolean
Takes a matcher, which can be a literal string or a string containing glob expressions, or a regexp, or a proc, or anything else that responds to #match or #call, and returns whether or not the given path matches that matcher.
300 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 300 Contract PATH_MATCHER, String => Bool |
.possible_delim_openers(frontmatter_delims) ⇒ Object
381 382 383 384 385 386 387 388 |
# File 'middleman-core/lib/middleman-core/util/binary.rb', line 381 def possible_delim_openers(frontmatter_delims) frontmatter_delims .values .flatten(1) .map(&:first) .uniq .map { |d| Regexp.escape(d) } end |
.read_file(path, bytes = nil) ⇒ Object
140 141 142 143 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 140 def read_file(path, bytes = nil) # puts "Read: #{path}" File.read(path, bytes) end |
.recursively_enhance(obj) ⇒ Object
32 33 34 35 36 37 38 39 40 41 |
# File 'middleman-core/lib/middleman-core/util/data.rb', line 32 def recursively_enhance(obj) case obj when ::Array obj.map { |e| recursively_enhance(e) } when ::Hash EnhancedHash.new(obj) else obj end end |
.relative_path_from_resource(curr_resource, resource_url, relative) ⇒ Object
274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 274 def relative_path_from_resource(curr_resource, resource_url, relative) # Switch to the relative path between resource and the given resource # if we've been asked to. if relative # Output urls relative to the destination path, not the source path current_dir = Pathname("/#{curr_resource.destination_path}").dirname relative_path = Pathname(resource_url).relative_path_from(current_dir).to_s # Put back the trailing slash to avoid unnecessary Apache redirects relative_path = "#{relative_path}/" if resource_url.end_with?('/') && !relative_path.end_with?('/') relative_path else resource_url end end |
.remove_templating_extensions(path) ⇒ Object
82 83 84 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 82 def remove_templating_extensions(path) step_through_extensions(path) end |
.rewrite_paths(body, path, exts, app, &_block) ⇒ Object
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 |
# File 'middleman-core/lib/middleman-core/util/rack.rb', line 26 def rewrite_paths(body, path, exts, app, &_block) sorted_exts = exts.to_a.sort_by { |ext| -ext.size } matcher = /([\'\"\(,]\s*|# sourceMappingURL=)([^\s\'\"\)\(>]+(#{::Regexp.union(sorted_exts)}))/ url_fn_prefix = 'url(' body.dup.gsub(matcher) do |match| opening_character = Regexp.last_match(1) asset_path = Regexp.last_match(2) if asset_path.start_with?(url_fn_prefix) opening_character = "#{opening_character}#{url_fn_prefix}" asset_path = asset_path[url_fn_prefix.length..-1] end current_resource = app.sitemap.by_destination_path(path) begin uri = ::Middleman::Util.parse_uri(asset_path) if uri.relative? && uri.host.nil? && asset_path !~ %r{^[^/].*[a-z]+\.[a-z]+/.*} dest_path = ::Middleman::Util.url_for(app, asset_path, relative: false, current_resource: current_resource) resource = app.sitemap.by_destination_path(dest_path) if resource && (result = yield(asset_path)) "#{opening_character}#{result}" else match end else match end rescue ::Addressable::URI::InvalidURIError match end end end |
.should_ignore?(validator, value) ⇒ Boolean
54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 54 def should_ignore?(validator, value) if validator.is_a? Regexp # Treat as Regexp !validator.match(value).nil? elsif validator.respond_to? :call # Treat as proc validator.call(value) elsif validator.is_a? String # Treat as glob File.fnmatch(value, validator) else # If some unknown thing, don't ignore false end end |
.static_file?(path, frontmatter_delims) ⇒ Boolean
346 347 348 349 350 351 352 353 354 355 356 |
# File 'middleman-core/lib/middleman-core/util/binary.rb', line 346 def static_file?(path, frontmatter_delims) path = Pathname(path) ext = path.extname without_dot = ext.sub('.', '') if KNOWN_NON_STATIC_FILE_EXTENSIONS.include?(without_dot) || contains_frontmatter?(path, frontmatter_delims) false else !::Tilt.registered?(without_dot) end end |
.step_through_extensions(path) {|::File.extname(path)| ... } ⇒ Object
63 64 65 66 67 68 69 70 71 72 73 74 75 76 |
# File 'middleman-core/lib/middleman-core/util/files.rb', line 63 def step_through_extensions(path) while (ext = File.extname(path)) break if ext.empty? || !::Middleman::Util.tilt_class(ext) yield ext if block_given? # Strip templating extensions as long as Tilt knows them path = path[0..-(ext.length + 1)] end yield ::File.extname(path) if block_given? path end |
.String ⇒ String
Expand a path to include the index file if it's a directory
250 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 250 Contract String, ::Middleman::Application => String |
.strip_leading_slash(path) ⇒ Object
46 47 48 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 46 def strip_leading_slash(path) path.sub(%r{^/}, '') end |
.tilt_class(path) ⇒ Object
26 27 28 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 26 def tilt_class(path) ::Tilt[path] end |
.url_for(app, path_or_resource, options_hash = ::Middleman::EMPTY_HASH) ⇒ Object
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 |
# File 'middleman-core/lib/middleman-core/util/paths.rb', line 158 def url_for(app, path_or_resource, = ::Middleman::EMPTY_HASH) if path_or_resource.is_a?(String) || path_or_resource.is_a?(Symbol) r = app.sitemap.by_page_id(path_or_resource) path_or_resource = r || path_or_resource.to_s end # Handle Resources and other things which define their own url method url = if path_or_resource.respond_to?(:url) path_or_resource.url else path_or_resource.dup end # Try to parse URL begin uri = ::Middleman::Util.parse_uri(url) rescue ::Addressable::URI::InvalidURIError # Nothing we can do with it, it's not really a URI return url end relative = [:relative] raise "Can't use the relative option with an external URL" if relative && uri.host # Allow people to turn on relative paths for all links with # set :relative_links, true # but still override on a case by case basis with the :relative parameter. effective_relative = relative || false effective_relative = true if relative.nil? && app.config[:relative_links] # Try to find a sitemap resource corresponding to the desired path this_resource = [:current_resource] if path_or_resource.is_a?(::Middleman::Sitemap::Resource) resource = path_or_resource resource_url = url elsif this_resource && uri.path && !uri.host # Handle relative urls url_path = Pathname(uri.path) current_source_dir = Pathname("/#{this_resource.path}").dirname url_path = current_source_dir.join(url_path) if url_path.relative? resource = app.sitemap.by_path(url_path.to_s) if resource resource_url = resource.url else # Try to find a resource relative to destination paths url_path = Pathname(uri.path) current_source_dir = Pathname("/#{this_resource.destination_path}").dirname url_path = current_source_dir.join(url_path) if url_path.relative? resource = app.sitemap.by_destination_path(url_path.to_s) resource_url = resource.url if resource end elsif [:find_resource] && uri.path && !uri.host resource = app.sitemap.by_path(uri.path) resource_url = resource.url if resource end if resource uri.path = if this_resource ::Addressable::URI.encode( relative_path_from_resource( this_resource, resource_url, effective_relative ) ) else resource_url end end # Support a :query option that can be a string or hash query = [:query] if query uri.query = query.respond_to?(:to_param) ? query.to_param : query.to_s end # Support a :fragment or :anchor option just like Padrino fragment = [:anchor] || [:fragment] uri.fragment = fragment.to_s if fragment # Finally make the URL back into a string uri.to_s end |