lib/middleman-core/util.rb



# 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"

module Middleman

  module Util

    # 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(/^\//, "").tr('','')
    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)
      case(response)
      when String
        response
      when Array
        response.join
      when Rack::Response
        response.body.join
      when Rack::File
        File.read(response.path)
      else
        response.to_s
      end
    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.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 path A path string or Pathname
    # @return [Array] An array of filenames
    def self.all_files_under(*paths)
      paths.flatten!
      paths.map! { |p| Pathname(p) }
      files = paths.select { |p| p.file? }
      paths.select {|p| p.directory? }.each do |dir|
        files << all_files_under(dir.children)
      end
      files.flatten
    end

    # Simple shared cache implementation
    class Cache
      # Initialize
      def initialize
        self.clear
      end

      # Either get the cached key or save the contents of the block
      #
      # @param key Anything Hash can use as a key
      def fetch(*key)
        @cache[key] ||= yield
      end

      # Whether the key is in the cache
      #
      # @param key Anything Hash can use as a key
      # @return [Boolean]
      def has_key?(key)
        @cache.has_key?(key)
      end

      # Get a specific key
      #
      # @param key Anything Hash can use as a key
      def get(key)
        @cache[key]
      end

      # Array of keys
      # @return [Array]
      def keys
        @cache.keys
      end

      # Clear the entire cache
      # @return [void]
      def clear
        @cache = {}
      end

      # Set a specific key
      #
      # @param key Anything Hash can use as a key
      # @param value Cached value
      # @return [void]
      def set(key, value)
        @cache[key] = value
      end

      # Remove a specific key
      # @param key Anything Hash can use as a key
      def remove(*key)
        @cache.delete(key)
      end
    end
  end
end