slight util reorg

This commit is contained in:
Thomas Reynolds 2014-07-14 09:50:44 -07:00
parent c74d03777a
commit 332ce2bebc
2 changed files with 98 additions and 93 deletions

View file

@ -1,8 +1,8 @@
# For instrumenting # For instrumenting
require 'active_support/notifications' require 'active_support/notifications'
# Using Thor's indifferent hash access # Indifferent hash access
require 'thor' require 'middleman-core/util/hash_with_indifferent_access'
# Core Pathname library used for traversal # Core Pathname library used for traversal
require 'pathname' require 'pathname'
@ -15,90 +15,17 @@ require 'rack/mime'
require 'middleman-core/contracts' require 'middleman-core/contracts'
module Middleman module Middleman
module Util module Util
extend self
include Contracts include Contracts
# A hash with indifferent access and magic predicates.
# Copied from Thor
#
# hash = Middleman::Util::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
#
# hash[:foo] #=> 'bar'
# hash['foo'] #=> 'bar'
# hash.foo? #=> true
#
class HashWithIndifferentAccess < ::Hash #:nodoc:
def initialize(hash={})
super()
hash.each do |key, value|
self[convert_key(key)] = value
end
end
def [](key)
super(convert_key(key))
end
def []=(key, value)
super(convert_key(key), value)
end
def delete(key)
super(convert_key(key))
end
def values_at(*indices)
indices.map { |key| self[convert_key(key)] }
end
def merge(other)
dup.merge!(other)
end
def merge!(other)
other.each do |key, value|
self[convert_key(key)] = value
end
self
end
# Convert to a Hash with String keys.
def to_hash
Hash.new(default).merge!(self)
end
protected
def convert_key(key)
key.is_a?(Symbol) ? key.to_s : key
end
# Magic predicates. For instance:
#
# options.force? # => !!options['force']
# options.shebang # => "/usr/lib/local/ruby"
# options.test_framework?(:rspec) # => options[:test_framework] == :rspec
# rubocop:disable DoubleNegation
def method_missing(method, *args)
method = method.to_s
if method =~ /^(\w+)\?$/
if args.empty?
!!self[$1]
else
self[$1] == args.first
end
else
self[method]
end
end
end
# Whether the source file is binary. # Whether the source file is binary.
# #
# @param [String] filename The file to check. # @param [String] filename The file to check.
# @return [Boolean] # @return [Boolean]
Contract String => Bool Contract String => Bool
def self.binary?(filename) def binary?(filename)
ext = File.extname(filename) ext = File.extname(filename)
# We hardcode detecting of gzipped SVG files # We hardcode detecting of gzipped SVG files
@ -125,7 +52,7 @@ module Middleman
# @param [String] path A path as a string # @param [String] path A path as a string
# @return [Boolean] Whether the path matches the matcher # @return [Boolean] Whether the path matches the matcher
Contract PATH_MATCHER, String => Bool Contract PATH_MATCHER, String => Bool
def self.path_match(matcher, path) def path_match(matcher, path)
case case
when matcher.is_a?(String) when matcher.is_a?(String)
if matcher.include? '*' if matcher.include? '*'
@ -148,7 +75,7 @@ module Middleman
# @param [Hash] data Normal hash # @param [Hash] data Normal hash
# @return [Middleman::Util::HashWithIndifferentAccess] # @return [Middleman::Util::HashWithIndifferentAccess]
Contract Or[Hash, Array] => Or[HashWithIndifferentAccess, Array] Contract Or[Hash, Array] => Or[HashWithIndifferentAccess, Array]
def self.recursively_enhance(data) def recursively_enhance(data)
if data.is_a? Hash if data.is_a? Hash
enhanced = ::Middleman::Util::HashWithIndifferentAccess.new(data) enhanced = ::Middleman::Util::HashWithIndifferentAccess.new(data)
@ -180,7 +107,7 @@ module Middleman
# @param [String] path # @param [String] path
# @return [String] # @return [String]
Contract String => String Contract String => String
def self.normalize_path(path) def normalize_path(path)
# The tr call works around a bug in Ruby's Unicode handling # The tr call works around a bug in Ruby's Unicode handling
path.sub(%r{^/}, '').tr('', '') path.sub(%r{^/}, '').tr('', '')
end end
@ -188,12 +115,12 @@ module Middleman
# This is a separate method from normalize_path in case we # This is a separate method from normalize_path in case we
# change how we normalize paths # change how we normalize paths
Contract String => String Contract String => String
def self.strip_leading_slash(path) def strip_leading_slash(path)
path.sub(%r{^/}, '') path.sub(%r{^/}, '')
end end
# Facade for ActiveSupport/Notification # Facade for ActiveSupport/Notification
def self.instrument(name, payload={}, &block) def instrument(name, payload={}, &block)
suffixed_name = (name =~ /\.middleman$/) ? name.dup : "#{name}.middleman" suffixed_name = (name =~ /\.middleman$/) ? name.dup : "#{name}.middleman"
::ActiveSupport::Notifications.instrument(suffixed_name, payload, &block) ::ActiveSupport::Notifications.instrument(suffixed_name, payload, &block)
end end
@ -203,7 +130,7 @@ module Middleman
# @param response The response from #call # @param response The response from #call
# @return [String] The whole response as a string. # @return [String] The whole response as a string.
Contract RespondTo[:each] => String Contract RespondTo[:each] => String
def self.extract_response_text(response) def extract_response_text(response)
# The rack spec states all response bodies must respond to each # The rack spec states all response bodies must respond to each
result = '' result = ''
response.each do |part, _| response.each do |part, _|
@ -220,7 +147,7 @@ module Middleman
# is ignored, nothing below it will be searched either. # is ignored, nothing below it will be searched either.
# @return [Array<Pathname>] An array of Pathnames for each file (no directories) # @return [Array<Pathname>] An array of Pathnames for each file (no directories)
Contract Or[String, Pathname], Proc => ArrayOf[Pathname] Contract Or[String, Pathname], Proc => ArrayOf[Pathname]
def self.all_files_under(path, &ignore) def all_files_under(path, &ignore)
path = Pathname(path) path = Pathname(path)
if path.directory? if path.directory?
@ -246,7 +173,7 @@ module Middleman
# @param [Hash] options Data to pass through. # @param [Hash] options Data to pass through.
# @return [String] # @return [String]
Contract IsA['Middleman::Application'], Symbol, Or[String, Symbol], Hash => String Contract IsA['Middleman::Application'], Symbol, Or[String, Symbol], Hash => String
def self.asset_path(app, kind, source, options={}) def asset_path(app, kind, source, options={})
return source if source.to_s.include?('//') || source.to_s.start_with?('data:') return source if source.to_s.include?('//') || source.to_s.start_with?('data:')
asset_folder = case kind asset_folder = case kind
@ -277,7 +204,7 @@ module Middleman
# @param [Hash] options Data to pass through. # @param [Hash] options Data to pass through.
# @return [String] The fully qualified asset url # @return [String] The fully qualified asset url
Contract IsA['Middleman::Application'], String, String, Hash => String Contract IsA['Middleman::Application'], String, String, Hash => String
def self.asset_url(app, path, prefix='', _options={}) def asset_url(app, path, prefix='', _options={})
# Don't touch assets which already have a full path # Don't touch assets which already have a full path
if path.include?('//') || path.start_with?('data:') if path.include?('//') || path.start_with?('data:')
path path
@ -299,7 +226,7 @@ module Middleman
# or a Resource, this will produce the nice URL configured for that # or a Resource, this will produce the nice URL configured for that
# path, respecting :relative_links, directory indexes, etc. # path, respecting :relative_links, directory indexes, etc.
Contract IsA['Middleman::Application'], Or[String, IsA['Middleman::Sitemap::Resource']], Hash => String Contract IsA['Middleman::Application'], Or[String, IsA['Middleman::Sitemap::Resource']], Hash => String
def self.url_for(app, path_or_resource, options={}) def url_for(app, path_or_resource, options={})
# Handle Resources and other things which define their own url method # Handle Resources and other things which define their own url method
url = if path_or_resource.respond_to?(:url) url = if path_or_resource.respond_to?(:url)
path_or_resource.url path_or_resource.url
@ -372,7 +299,7 @@ module Middleman
# @param [Middleman::Application] app The requesting app. # @param [Middleman::Application] app The requesting app.
# @return [String] Path with index file if necessary. # @return [String] Path with index file if necessary.
Contract String, IsA['Middleman::Application'] => String Contract String, IsA['Middleman::Application'] => String
def self.full_path(path, app) def full_path(path, app)
resource = app.sitemap.find_resource_by_destination_path(path) resource = app.sitemap.find_resource_by_destination_path(path)
unless resource unless resource
@ -389,7 +316,7 @@ module Middleman
end end
Contract String, String, ArrayOf[String], Proc => String Contract String, String, ArrayOf[String], Proc => String
def self.rewrite_paths(body, _path, exts, &_block) def rewrite_paths(body, _path, exts, &_block)
body.dup.gsub(/([=\'\"\(]\s*)([^\s\'\"\)]+(#{Regexp.union(exts)}))/) do |match| body.dup.gsub(/([=\'\"\(]\s*)([^\s\'\"\)]+(#{Regexp.union(exts)}))/) do |match|
opening_character = $1 opening_character = $1
asset_path = $2 asset_path = $2
@ -407,7 +334,7 @@ module Middleman
# @param [String] mime The mimetype to check. # @param [String] mime The mimetype to check.
# @return [Boolean] # @return [Boolean]
Contract String => Bool Contract String => Bool
def self.nonbinary_mime?(mime) def nonbinary_mime?(mime)
case case
when mime.start_with?('text/') when mime.start_with?('text/')
true true
@ -427,7 +354,7 @@ module Middleman
# @param [String] filename The file to check. # @param [String] filename The file to check.
# @return [Boolean] # @return [Boolean]
Contract String => Bool Contract String => Bool
def self.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] 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 = File.read(filename, 4096) || ''
s.each_byte do |c| s.each_byte do |c|
@ -444,7 +371,7 @@ module Middleman
# @param [Boolean] relative If the path should be relative. # @param [Boolean] relative If the path should be relative.
# @return [String] # @return [String]
Contract IsA['Middleman::Sitemap::Resource'], String, Bool => String Contract IsA['Middleman::Sitemap::Resource'], String, Bool => String
def self.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 # Switch to the relative path between resource and the given resource
# if we've been asked to. # if we've been asked to.
if relative if relative

View file

@ -0,0 +1,78 @@
module Middleman
module Util
# A hash with indifferent access and magic predicates.
# Copied from Thor
#
# hash = Middleman::Util::HashWithIndifferentAccess.new 'foo' => 'bar', 'baz' => 'bee', 'force' => true
#
# hash[:foo] #=> 'bar'
# hash['foo'] #=> 'bar'
# hash.foo? #=> true
#
class HashWithIndifferentAccess < ::Hash #:nodoc:
def initialize(hash={})
super()
hash.each do |key, value|
self[convert_key(key)] = value
end
end
def [](key)
super(convert_key(key))
end
def []=(key, value)
super(convert_key(key), value)
end
def delete(key)
super(convert_key(key))
end
def values_at(*indices)
indices.map { |key| self[convert_key(key)] }
end
def merge(other)
dup.merge!(other)
end
def merge!(other)
other.each do |key, value|
self[convert_key(key)] = value
end
self
end
# Convert to a Hash with String keys.
def to_hash
Hash.new(default).merge!(self)
end
protected
def convert_key(key)
key.is_a?(Symbol) ? key.to_s : key
end
# Magic predicates. For instance:
#
# options.force? # => !!options['force']
# options.shebang # => "/usr/lib/local/ruby"
# options.test_framework?(:rspec) # => options[:test_framework] == :rspec
# rubocop:disable DoubleNegation
def method_missing(method, *args)
method = method.to_s
if method =~ /^(\w+)\?$/
if args.empty?
!!self[$1]
else
self[$1] == args.first
end
else
self[method]
end
end
end
end
end