From 75cf45867284c925fb20006e2d3ee0bf7c3cefe7 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 22 Aug 2020 15:35:23 -0400 Subject: [PATCH] Destroy method from #8 --- src/lib/file_tree.cr | 21 ++++++++++ src/lib/renderer.cr | 85 ++++++++++++++++++++++++++++++++++++++ src/lib/rendering_entry.cr | 17 ++++++++ 3 files changed, 123 insertions(+) diff --git a/src/lib/file_tree.cr b/src/lib/file_tree.cr index a4bf0c7..8f7be54 100644 --- a/src/lib/file_tree.cr +++ b/src/lib/file_tree.cr @@ -1,6 +1,9 @@ module Teeplate # Collects template files from a local directory. abstract class FileTree + # Array of paths to skip when performing destroy + @skip_on_destroy = [] of String + # Collects and embeds template files. # # It runs another macro process that collects template files and embeds the files as code. @@ -8,6 +11,11 @@ module Teeplate {{ run(__DIR__ + "/file_tree/macros/directory", dir.id) }} end + # Skip directory/file at the given path. The path is relative to the out_dir. + def skip_on_destroy(path) + @skip_on_destroy.push(path) + end + @file_entries : Array(AsDataEntry)? # Returns collected file entries. def file_entries : Array(AsDataEntry) @@ -28,6 +36,14 @@ module Teeplate renderer end + # Destroy the rendered files. + def destroy(out_dir, force : Bool = false, interactive : Bool = false, interact : Bool = false, list : Bool = false, color : Bool = false, per_entry : Bool = false, quit : Bool = true) + renderer = Renderer.new(out_dir, force: force, interact: interactive || interact, list: list, color: color, per_entry: per_entry, quit: quit) + renderer << destroy_file_entries + renderer.destroy(@skip_on_destroy) + renderer + end + # Returns file entries to be rendered. # # This method just returns the `#file_entries` method's result. To filter entries, override this method. @@ -35,6 +51,11 @@ module Teeplate file_entries end + # :nodoc: + def destroy_file_entries + file_entries + end + # :nodoc: def ____collect_files(files) end diff --git a/src/lib/renderer.cr b/src/lib/renderer.cr index 2f69e5f..d075c59 100644 --- a/src/lib/renderer.cr +++ b/src/lib/renderer.cr @@ -41,6 +41,9 @@ module Teeplate # :nodoc: getter data_entries = [] of AsDataEntry + # :nodoc: + getter? pending_destroy = false + # :nodoc: getter entries = [] of RenderingEntry @@ -97,6 +100,88 @@ module Teeplate rescue ex : Quit @quitted = true end + end + + # Destroy templates. + # + # If passing paths as skip, these paths will be skipped in the destroy process, and thus will remain on the + # file system + def destroy(skip : Array(String)?) + @pending_destroy = true + begin + if @interactive + @entries.each do |entry| + entry.destroy(should_destroy?(entry)) + end + elsif should_destroy_all?(@entries) + @entries.each do |entry| + entry.destroy(should_skip_on_destroy?(entry, skip)) + end + end + rescue ex : Quit + @quitted = true + end + end + + # Confirm whether the user wants to destroy a singe file. + def should_destroy?(entry : RenderingEntry) + STDOUT.puts "Destroy #{entry.out_path}? (y/n)" + + loop do + case input = ::STDIN.gets.to_s.strip.downcase + when "y" + return true + when "n" + return false + end + end + end + + # Confirm whether or not the user wishes to destroy multiple files. + def should_destroy_all?(entries : Array(RenderingEntry)) + return should_destroy?(entries.first) if entries.size == 1 + + STDOUT.puts "Destroy all the following files? (y/n)" + + entries.each do |entry| + STDOUT.puts entry.out_path + end + + loop do + case input = ::STDIN.gets.to_s.strip.downcase + when "y" + return true + when "n" + return false + end + end + end + + # Determine whether a file should be skipped upon performing #destroy, based on the + # provided array of paths to skip. + def should_skip_on_destroy?(file : RenderingEntry, skip : Array(String)?) : Bool + skip_file = false + + skip_path_parts : Array(String)? + entry_path_parts : Array(String)? + entry_path_parts = file.out_path.split("/") + + skip.each do |skip_path| + skip_path_parts = skip_path.split("/") + + skip_path_parts.unshift(entry_path_parts.first) + skip_path_parts.each_with_index do |part, i| + if i + 1 > entry_path_parts.size + skip_file = false + break + end + skip_file = part.downcase == entry_path_parts[i].downcase + end + + break if skip_file + end + + skip_file end end end diff --git a/src/lib/rendering_entry.cr b/src/lib/rendering_entry.cr index b4b9cd5..1052039 100644 --- a/src/lib/rendering_entry.cr +++ b/src/lib/rendering_entry.cr @@ -97,6 +97,20 @@ module Teeplate end end + # :nodoc: + def destroy(skip? = false) + unless skip? + begin + File.delete out_path + list_if_any "destroyed ", :red + rescue + list_if_any "skipped ", :yellow + end + else + list_if_any "skipped ", :yellow + end + end + # :nodoc: def set_perm if perm = @data.perm? && File.file?(out_path) @@ -132,6 +146,7 @@ module Teeplate return :keep if !@renderer.interactive? || @renderer.keeps_all? return modifies?("#{local_path} is a symlink...", diff: false) if File.symlink?(out_path) return :modify if appends? + return :destroy if @renderer.pending_destroy? return :none if identical? modifies?("#{local_path} already exists...", diff: true) end @@ -223,8 +238,10 @@ module Teeplate begin if !GIT.empty? Process.new(GIT, ["diff", "--no-index", "--", out_path, "-"], shell: true, input: r, output: w2, error: Process::Redirect::Inherit).wait + # Process.new(GIT, ["diff", "--no-index", "--", out_path, "-"], shell: true, input: r, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit).wait elsif !DIFF.empty? Process.new(DIFF, ["-u", out_path, "-"], shell: true, input: r, output: w2, error: Process::Redirect::Inherit).wait + # Process.new(DIFF, ["-u", out_path, "-"], shell: true, input: r, output: Process::Redirect::Inherit, error: Process::Redirect::Inherit).wait else w2.puts "No diff command is installed." end