Created
November 17, 2015 09:45
-
-
Save shibulijack-fd/c6243a444f5e798e7c68 to your computer and use it in GitHub Desktop.
Middleman Helper Method Utility [UPDATED]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Our custom logger | |
require "middleman-core/logger" | |
# For instrumenting | |
require "active_support/notifications" | |
# Using Thor's indifferent hash access | |
require "thor" | |
# Core Pathname library used for traversal | |
require "pathname" | |
require "tilt" | |
require "rack/mime" | |
module Middleman | |
module Util | |
# Whether the source file is binary. | |
# | |
# @param [String] filename The file to check. | |
# @return [Boolean] | |
def self.binary?(filename) | |
ext = File.extname(filename) | |
return true if ext == '.svgz' | |
return false if Tilt.registered?(ext.sub('.','')) | |
ext = ".#{ext}" unless ext.to_s[0] == ?. | |
mime = ::Rack::Mime.mime_type(ext, nil) | |
unless mime | |
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 | |
return false | |
end | |
return false if mime.start_with?('text/') | |
return false if mime.include?('xml') | |
return false if mime.include?('json') | |
return false if mime.include?('javascript') | |
true | |
end | |
# The logger | |
# | |
# @return [Middleman::Logger] The logger | |
def self.logger(*args) | |
if !@_logger || args.length > 0 | |
@_logger = ::Middleman::Logger.new(*args) | |
end | |
@_logger | |
end | |
# Facade for ActiveSupport/Notification | |
def self.instrument(name, payload={}, &block) | |
name << ".middleman" unless name =~ /\.middleman$/ | |
::ActiveSupport::Notifications.instrument(name, payload, &block) | |
end | |
# Recursively convert a normal Hash into a HashWithIndifferentAccess | |
# | |
# @private | |
# @param [Hash] data Normal hash | |
# @return [Thor::CoreExt::HashWithIndifferentAccess] | |
def self.recursively_enhance(data) | |
if data.is_a? Hash | |
data = ::Thor::CoreExt::HashWithIndifferentAccess.new(data) | |
data.each do |key, val| | |
data[key] = recursively_enhance(val) | |
end | |
data | |
elsif data.is_a? Array | |
data.each_with_index do |val, i| | |
data[i] = recursively_enhance(val) | |
end | |
data | |
else | |
data | |
end | |
end | |
# Normalize a path to not include a leading slash | |
# @param [String] path | |
# @return [String] | |
def self.normalize_path(path) | |
# The tr call works around a bug in Ruby's Unicode handling | |
path.sub(%r{^/}, "").tr('','') | |
end | |
# This is a separate method from normalize_path in case we | |
# change how we normalize paths | |
def self.strip_leading_slash(path) | |
path.sub(%r{^/}, "") | |
end | |
# Extract the text of a Rack response as a string. | |
# Useful for extensions implemented as Rack middleware. | |
# @param response The response from #call | |
# @return [String] The whole response as a string. | |
def self.extract_response_text(response) | |
# The rack spec states all response bodies must respond to each | |
result = '' | |
response.each do |part, s| | |
result << part | |
end | |
result | |
end | |
# 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. | |
# | |
# @param matcher A matcher string/regexp/proc/etc | |
# @param path A path as a string | |
# @return [Boolean] Whether the path matches the matcher | |
def self.path_match(matcher, path) | |
if matcher.is_a? String | |
path.match matcher | |
elsif matcher.respond_to? :match | |
matcher.match path | |
elsif matcher.respond_to? :call | |
matcher.call path | |
else | |
File.fnmatch(matcher.to_s, path) | |
end | |
end | |
# Get a recusive list of files inside a set of paths. | |
# Works with symlinks. | |
# | |
# @param paths Some paths string or Pathname | |
# @return [Array] An array of filenames | |
def self.all_files_under(*paths) | |
# when we drop 1.8, replace this with flat_map | |
paths.map do |p| | |
path = Pathname(p) | |
if path.directory? | |
all_files_under(*path.children) | |
elsif path.file? | |
path | |
end | |
end.flatten.compact | |
end | |
# Given a source path (referenced either absolutely or relatively) | |
# or a Resource, this will produce the nice URL configured for that | |
# path, respecting :relative_links, directory indexes, etc. | |
def self.url_for(app, path_or_resource, options={}) | |
# Handle Resources and other things which define their own url method | |
url = path_or_resource.respond_to?(:url) ? path_or_resource.url : path_or_resource | |
begin | |
uri = URI(url) | |
rescue URI::InvalidURIError | |
# Nothing we can do with it, it's not really a URI | |
return url | |
end | |
relative = options.delete(: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 = app.current_resource # store in a local var to save work | |
if path_or_resource.is_a?(::Middleman::Sitemap::Resource) | |
resource = path_or_resource | |
resource_url = url | |
elsif this_resource && uri.path | |
# Handle relative urls | |
url_path = Pathname(uri.path) | |
puts "Resource path: #{this_resource.path}" | |
current_source_dir_split = this_resource.path.split('/') | |
if current_source_dir_split.length > 1 | |
current_source_dir = Pathname.new('/'+current_source_dir_split[0]+'/') | |
else | |
current_source_dir = Pathname('/' + this_resource.path).dirname | |
end | |
url_path = current_source_dir.join(url_path) if url_path.relative? | |
puts "URL : #{url_path}" | |
resource = app.sitemap.find_resource_by_path(url_path.to_s) | |
resource_url = resource.url if resource | |
elsif options[:find_resource] && uri.path | |
resource = app.sitemap.find_resource_by_path(uri.path) | |
resource_url = resource.url if resource | |
end | |
if resource | |
# Switch to the relative path between this_resource and the given resource | |
# if we've been asked to. | |
if effective_relative | |
# Output urls relative to the destination path, not the source path | |
current_dir = Pathname('/' + this_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 | |
if resource_url.end_with?('/') && !relative_path.end_with?('/') | |
relative_path << '/' | |
end | |
uri.path = relative_path | |
else | |
uri.path = resource_url | |
end | |
else | |
# If they explicitly asked for relative links but we can't find a resource... | |
raise "No resource exists at #{url}" if relative | |
end | |
# Support a :query option that can be a string or hash | |
if query = options.delete(: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.delete(:anchor) || options.delete(:fragment) | |
uri.fragment = fragment.to_s if fragment | |
# Finally make the URL back into a string | |
uri.to_s | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment