class Middleman::NavTree::NavTreeExtension

Extension namespace @todo: Test the extension against a vanilla Middleman install. @todo: Test the extension against a middleman-blog install.

Public Class Methods

new(app, options_hash={}, &block) click to toggle source
Calls superclass method
# File lib/middleman-navtree/extension.rb, line 23
def initialize(app, options_hash={}, &block)
  # Call super to build options from the options_hash
  super

  # Require libraries only when activated
  require 'yaml'
  require 'titleize'

  @existing_promotes = []

end

Public Instance Methods

after_configuration() click to toggle source
# File lib/middleman-navtree/extension.rb, line 35
def after_configuration
  # Add the user's config directories to the "ignore_dir" option because
  # these are all things we won't need printed in a NavTree.
  options.ignore_dir << app.config[:js_dir]
  options.ignore_dir << app.config[:css_dir]
  options.ignore_dir << app.config[:fonts_dir]
  options.ignore_dir << app.config[:images_dir]
  options.ignore_dir << app.config[:helpers_dir]
  options.ignore_dir << app.config[:layouts_dir]
  options.ignore_dir << app.config[:partials_dir]

  # Build a hash out of our directory information
  tree_hash = scan_directory(app.config[:source], options)

  # Promote any promoted files to the beginning of our hash.
  tree_hash = promote_files(tree_hash, options)

  # Write our directory tree to file as YAML.
  # @todo: This step doesn't rebuild during live-reload, which causes errors if you move files
  #        around during development. It may not be that hard to set up. Low priority though.
  if options.automatic_tree_updates
    FileUtils.mkdir_p(app.config[:data_dir])

    data_path = app.config[:data_dir] + '/' + options.data_file
    IO.write(data_path, YAML::dump(tree_hash))
  end
end
promote_files(tree_hash, options) click to toggle source

Method for appending promoted files to the front of our source tree. @todo: Currently, options.promote_files only expects a filename, which means that

if multiple files in different directories have the same filename, they
will both be promoted, and one will not appear (due to the 'no-two-identical
-indices-in-a-hash' rule).

@todo: This system also assumes filenames only have a single extension,

which may not be the case (like index.html.erb)

@todo: Basically, this is not elegent at all.

# File lib/middleman-navtree/extension.rb, line 116
def promote_files(tree_hash, options)

  if @existing_promotes.any?
    ordered_matches = []

    # The purpose of this loop is to get my list of existing promotes
    # in the order specified in the options array, so it can be promoted
    # properly.
    options.promote_files.each do |filename|
      # Get filename without extension (index.md => index)
      filename_without_ext = filename.chomp(File.extname(filename))
      # Test against each existing_promote, and store matches
      @existing_promotes.each do |pathname|
        # Get another filename without extension from the pathname (/book/index.html => index)
        pathname_without_ext = File.basename(pathname, ".*")
        # Add matches to our ordered matches array.
        if filename_without_ext == pathname_without_ext
          ordered_matches << [filename, pathname]
        end
      end
    end
    # Promote all files found in both the promotes list and the file structure. This is an array
    # of arrays
    ordered_matches.reverse.each do |match|
      tree_hash = Hash[match[0], match[1]].merge!(tree_hash)
    end
  end

  return tree_hash
end
scan_directory(path, options, name=nil) click to toggle source

Method for storing the directory structure in an ordered hash. See more on ordered hashes at www.igvita.com/2009/02/04/ruby-19-internals-ordered-hash/

# File lib/middleman-navtree/extension.rb, line 66
def scan_directory(path, options, name=nil)
  data = {}
  Dir.foreach(path) do |filename|

    # Check to see if we should skip this file. We skip invisible files
    # (starts with "."), ignored files, and promoted files (which are
    # handled later in the process).
    next if (filename[0] == '.')
    next if (filename == '..' || filename == '.')
    next if options.ignore_files.include? filename

    if options.promote_files.include? filename
      original_path = path.sub(/^#{app.config[:source]}/, '') + '/' + filename
      @existing_promotes << original_path
      next
    end

    full_path = File.join(path, filename)
    if File.directory?(full_path)
      # This item is a directory.
      # Check to see if we should ignore this directory.
      next if options.ignore_dir.include? filename

      # Loop through the method again.
      data.store(filename.gsub(' ', '%20'), scan_directory(full_path, options, filename))
    else

      # This item is a file.
      if !options.ext_whitelist.empty?
        # Skip any whitelisted extensions.
        next unless options.ext_whitelist.include? File.extname(filename)
      end

      original_path = path.sub(/^#{app.config[:source]}/, '') + '/' + filename
      data.store(filename.gsub(' ', '%20'), original_path.gsub(' ', '%20'))
    end
  end

  # Return this level's data as a hash sorted by keys.
  return Hash[data.sort]
end