class Nanoc::Core::Pruner

Responsible for finding and deleting files in the site’s output directory that are not managed by Nanoc.

Public Class Methods

new(config, reps, dry_run: false, exclude: []) click to toggle source
# File lib/nanoc/core/pruner.rb, line 11
def initialize(config, reps, dry_run: false, exclude: [])
  @config  = config
  @reps    = reps
  @dry_run = dry_run
  @exclude = Set.new(exclude)
end

Public Instance Methods

filename_excluded?(filename) click to toggle source
# File lib/nanoc/core/pruner.rb, line 29
def filename_excluded?(filename)
  pathname = Pathname.new(strip_output_dir(filename))
  @exclude.any? { |e| pathname_components(pathname).include?(e) }
end
files_and_dirs_in(dir) click to toggle source

@api private

# File lib/nanoc/core/pruner.rb, line 79
def files_and_dirs_in(dir)
  present_files = []
  present_dirs = []

  expanded_dir = File.expand_path(dir)

  Find.find(dir) do |f|
    case File.ftype(f)
    when 'file'
      unless filename_excluded?(f)
        present_files << f
      end
    when 'directory'
      if filename_excluded?(f)
        Find.prune
      elsif expanded_dir != File.expand_path(f)
        present_dirs << f
      end
    end
  end

  [present_files, present_dirs]
end
pathname_components(pathname) click to toggle source
# File lib/nanoc/core/pruner.rb, line 44
def pathname_components(pathname)
  components = []
  tmp = pathname
  loop do
    old = tmp
    components << File.basename(tmp)
    tmp = File.dirname(tmp)
    break if old == tmp
  end
  components.reverse
end
remove_empty_directories(present_dirs) click to toggle source

@api private

# File lib/nanoc/core/pruner.rb, line 67
def remove_empty_directories(present_dirs)
  present_dirs.reverse_each do |dir|
    next if Dir.foreach(dir) { |n| break true if n !~ /\A\.\.?\z/ }
    next if filename_excluded?(dir)

    delete_dir(dir)
  end
  self
end
remove_stray_files(present_files, compiled_files) click to toggle source

@api private

# File lib/nanoc/core/pruner.rb, line 58
def remove_stray_files(present_files, compiled_files)
  (present_files - compiled_files).each do |f|
    delete_file(f) unless filename_excluded?(f)
  end
  self
end
run() click to toggle source
# File lib/nanoc/core/pruner.rb, line 18
def run
  return unless File.directory?(@config.output_dir)

  compiled_files = @reps.flat_map { |r| r.raw_paths.values.flatten }.compact
  present_files, present_dirs = files_and_dirs_in(@config.output_dir + '/')

  remove_stray_files(present_files, compiled_files)
  remove_empty_directories(present_dirs)
end
strip_output_dir(filename) click to toggle source
# File lib/nanoc/core/pruner.rb, line 35
def strip_output_dir(filename)
  if filename.start_with?(@config.output_dir)
    filename[@config.output_dir.size..-1]
  else
    filename
  end
end

Protected Instance Methods

delete_dir(dir) click to toggle source
# File lib/nanoc/core/pruner.rb, line 109
def delete_dir(dir)
  log_delete_and_run(dir) { Dir.rmdir(dir) }
end
delete_file(file) click to toggle source
# File lib/nanoc/core/pruner.rb, line 105
def delete_file(file)
  log_delete_and_run(file) { FileUtils.rm(file) }
end
log_delete_and_run(thing) { || ... } click to toggle source
# File lib/nanoc/core/pruner.rb, line 113
def log_delete_and_run(thing)
  if @dry_run
    Nanoc::Core::NotificationCenter.post(:file_listed_for_pruning, thing)
  else
    Nanoc::Core::NotificationCenter.post(:file_pruned, thing)
    yield
  end
end