1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
|
require 'fileutils'
require_relative 'file'
module RCM
# Manages directories: create, delete/purge, or recursively copy from
# a source directory. Backup is performed before destructive operations.
# Extends BasicFile directly — Directory has no file content or sourcing,
# so it must not inherit content/from from BaseFile (ISP). The source
# directory for recursive copy is stored via the separate #source method.
class Directory < BasicFile
def recursively = @recursively = true
# Set or get the source directory path used for recursive copy.
def source(path = nil) = path.nil? ? @source_path : @source_path = path
def evaluate!
return unless super
case @is
when :present
evaluate_present!
when :absent, :purged
evaluate_absent!
end
ensure
permissions!
end
private
def evaluate_present!
if ::File.directory?(@file_path)
return @recursively ? evaluate_present_recursively! : nil
end
create_parent_directory! if @manage_directory
do? "Creating directory #{@file_path}" do
Dir.mkdir(@file_path)
end
end
# Override BasicFile#evaluate_absent! with directory-specific behaviour:
# optionally recursive removal and backup of the whole directory tree.
def evaluate_absent!
return unless ::File.directory?(@file_path)
backup!(@file_path)
@recursively = true if @is == :purged
what = @is == :purged ? 'Purging' : 'Deleting'
do? "#{what} directory #{@file_path}" do
if ::File.directory?(@file_path)
@recursively ? FileUtils.rm_r(@file_path) : Dir.delete(@file_path)
end
end
cleanup_parent_directory! if @manage_directory
end
def evaluate_present_recursively!
src = source
raise "Source #{src} is not a directory!" unless ::File.directory?(src)
if ::File.exist?(@file_path)
raise "Destination #{@file_path} is not a directory!" unless ::File.directory?(@file_path)
backup_recursively!(src, @file_path) unless @without_backup
end
do? "Copying #{src} -> #{@file_path} recursively" do
if ::File.directory?(@file_path)
Dir["#{src}/*"].each { FileUtils.cp_r(_1, @file_path) }
else
FileUtils.cp_r(src, @file_path)
end
end
end
# TODO: Unit test this
def backup_recursively!(source, dest)
Dir.foreach(source) do |entry|
next if ['.', '..'].include?(entry)
source_path = ::File.join(source, entry)
dest_path = ::File.join(dest, entry)
if ::File.directory?(source_path) && !::File.directory?(dest_path)
raise "Unable to copy directory #{source_path} into non-directory #{dest_path}"
elsif !::File.directory?(source_path) && ::File.directory?(dest_path)
raise "Unable to copy non-directory #{source_path} into directory #{dest_path}"
elsif ::File.directory?(source_path) && ::File.directory?(dest_path)
backup_recursively!(source_path, dest_path)
else
backup!(dest_path)
end
end
end
end
class DSL
def directory(file_path = nil, &block)
register_keyword(Directory, :directory, file_path) { |d| d.source(d.instance_eval(&block)) }
end
end
end
|