module Middleman::Util
def all_files_under(path, &ignore)
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
def asset_path(app, kind, source, options_hash = ::Middleman::EMPTY_HASH)
def asset_path(app, kind, source, options_hash = ::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 << ".#{kind}" unless ignore_extension || source.end_with?(".#{kind}") asset_folder = '' if source.start_with?('/') # absolute path asset_url(app, source, asset_folder, options_hash) end
def asset_url(app, path, prefix = '', options_hash = ::Middleman::EMPTY_HASH)
def asset_url(app, path, prefix = '', options_hash = ::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 options_hash[:relative] && !options_hash[: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, options_hash.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( options_hash[:current_resource], result, options_hash[:relative] ) ) result_uri = ::Middleman::Util.parse_uri(final_result) result_uri.query = uri.query result_uri.fragment = uri.fragment result_uri.to_s end
def binary?(filename)
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
def collect_extensions(path)
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
def contains_frontmatter?(path, frontmatter_delims)
def contains_frontmatter?(path, frontmatter_delims) file = ::File.open(path) first_line = file.gets first_line = file.gets if first_line =~ /\A(?:[^\r\n]*coding:[^\r\n]*\r?\n)/ file.close possible_openers = possible_delim_openers(frontmatter_delims) !first_line.nil? && !first_line.match(possible_openers).nil? rescue EOFError, IOError, ::Errno::ENOENT false end
def current_directory
-
(Array
-)
Parameters:
-
path
(String
) -- The glob path.
def current_directory result = ::Dir.pwd return result unless RUBY_PLATFORM =~ /darwin/ result.encode('UTF-8', 'UTF-8-MAC') end
def extract_response_text(response)
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 end
def file_contents_include_binary_bytes?(filename)
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
def find_related_files(app, files)
def find_related_files(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.without_ignored.to_a.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
def full_path(path, app)
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
def glob_directory(path)
-
(Array
-)
Parameters:
-
path
(String
) -- The glob path.
def glob_directory(path) results = ::Dir[path] return results unless RUBY_PLATFORM =~ /darwin/ results.map { |r| r.encode('UTF-8', 'UTF-8-MAC') } end
def hash_file(path)
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
def hash_string(data)
def hash_string(data) ::Digest::SHA1.hexdigest(data) end
def instrument(name, payload = {}, &block)
def instrument(name, payload = {}, &block) suffixed_name = /\.middleman$/.match?(name) ? name.dup : "#{name}.middleman" ::ActiveSupport::Notifications.instrument(suffixed_name, payload, &block) end
def nonbinary_mime?(mime)
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
def normalize_path(path)
def normalize_path(path) return path unless path.is_a?(String) # The tr call works around a bug in Ruby's Unicode handling ::URI.decode(path).sub(%r{^/}, '').tr('', '') end
def parse_uri(uri)
def parse_uri(uri) ::Addressable::URI.parse(uri) end
def path_match(matcher, path)
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
def possible_delim_openers(frontmatter_delims)
def possible_delim_openers(frontmatter_delims) all_possible = frontmatter_delims .values .flatten(1) .map(&:first) .uniq /\A#{::Regexp.union(all_possible)}/ end
def read_file(path, bytes = nil)
def read_file(path, bytes = nil) # puts "Read: #{path}" File.read(path, bytes) end
def recursively_enhance(obj)
def recursively_enhance(obj) if obj.is_a? ::Array obj.map { |e| recursively_enhance(e) } elsif obj.is_a? ::Hash EnhancedHash.new(obj) else obj end end
def relative_path_from_resource(curr_resource, resource_url, relative)
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 << '/' if resource_url.end_with?('/') && !relative_path.end_with?('/') relative_path else resource_url end end
def remove_templating_extensions(path)
def remove_templating_extensions(path) step_through_extensions(path) end
def rewrite_paths(body, path, exts, app, &_block)
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 << 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 !~ /^[^\/].*[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
def should_ignore?(validator, value)
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
def static_file?(path, frontmatter_delims)
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
def step_through_extensions(path)
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
def strip_leading_slash(path)
def strip_leading_slash(path) path.sub(%r{^/}, '') end
def tilt_class(path)
def tilt_class(path) ::Tilt[path] end
def url_for(app, path_or_resource, options_hash = ::Middleman::EMPTY_HASH)
def url_for(app, path_or_resource, options_hash = ::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 = options_hash[: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 = options_hash[: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 options_hash[: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 = options_hash[: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 = options_hash[:anchor] || options_hash[:fragment] uri.fragment = fragment.to_s if fragment # Finally make the URL back into a string uri.to_s end