build-files 1.9.1 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/lib/build/files/composite.rb +33 -5
- data/lib/build/files/difference.rb +23 -4
- data/lib/build/files/directory.rb +32 -3
- data/lib/build/files/glob.rb +24 -0
- data/lib/build/files/list.rb +27 -0
- data/lib/build/files/path.rb +74 -1
- data/lib/build/files/paths.rb +26 -1
- data/lib/build/files/state.rb +26 -3
- data/lib/build/files/system.rb +12 -0
- data/lib/build/files/version.rb +4 -2
- data/readme.md +6 -12
- data/releases.md +3 -0
- data.tar.gz.sig +0 -0
- metadata +5 -4
- metadata.gz.sig +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 555dbd01ff6915b1d7a9c11864d95544929e35006094fb0eda4ca7b8d8227006
|
4
|
+
data.tar.gz: 6c17197d4bacc772f1e9aed6bce37cfab7ea4dcfb172c07a7ad955dfd8dc25ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8078524f809ad559c7c62de94ac98db9e6129e7d97f268fa2440a4732fbd3cba590d37775d9021b5a118c6957b81d87dbfb2321065fcd0eb32e9ab6e099617e8
|
7
|
+
data.tar.gz: 951b008d3c8a395024acfa3d83db9dc86cedadb3fbc6af4964f48cf23cc61f4f460eb63df40c7fd89efffaa3f6d6a818f2641a6c49b629d790643b01304c8e49
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
@@ -7,14 +7,18 @@ require_relative "list"
|
|
7
7
|
|
8
8
|
module Build
|
9
9
|
module Files
|
10
|
+
# Represents a composite list of files from multiple sources.
|
10
11
|
class Composite < List
|
12
|
+
# Initialize a composite list with multiple file lists.
|
13
|
+
# @parameter files [Array] The file lists to combine.
|
14
|
+
# @parameter roots [Array(Path) | Nil] The root paths, if known.
|
11
15
|
def initialize(files, roots = nil)
|
12
16
|
@files = []
|
13
17
|
|
14
18
|
files.each do |list|
|
15
19
|
if list.kind_of? Composite
|
16
20
|
@files += list.files
|
17
|
-
elsif
|
21
|
+
elsif list.kind_of? List
|
18
22
|
@files << list
|
19
23
|
else
|
20
24
|
# Try to convert into a explicit paths list:
|
@@ -28,12 +32,16 @@ module Build
|
|
28
32
|
|
29
33
|
attr :files
|
30
34
|
|
35
|
+
# Freeze the composite list and its dependencies.
|
31
36
|
def freeze
|
32
37
|
self.roots
|
33
38
|
|
34
39
|
super
|
35
40
|
end
|
36
41
|
|
42
|
+
# Iterate over all files in the composite list.
|
43
|
+
# @yields {|path| ...} Each path in all combined lists.
|
44
|
+
# @parameter path [Path] The current file path.
|
37
45
|
def each
|
38
46
|
return to_enum(:each) unless block_given?
|
39
47
|
|
@@ -42,18 +50,28 @@ module Build
|
|
42
50
|
end
|
43
51
|
end
|
44
52
|
|
53
|
+
# Get all root paths for all lists in the composite.
|
54
|
+
# @returns [Array(Path)] The unique root paths.
|
45
55
|
def roots
|
46
56
|
@roots ||= @files.collect(&:roots).flatten.uniq
|
47
57
|
end
|
48
58
|
|
59
|
+
# Check equality with another composite list.
|
60
|
+
# @parameter other [Composite] The other composite to compare.
|
61
|
+
# @returns [Boolean] True if both composites have the same files.
|
49
62
|
def eql?(other)
|
50
63
|
self.class.eql?(other.class) and @files.eql?(other.files)
|
51
64
|
end
|
52
|
-
|
65
|
+
|
66
|
+
# Compute the hash value for this composite.
|
67
|
+
# @returns [Integer] The hash value based on files.
|
53
68
|
def hash
|
54
69
|
@files.hash
|
55
70
|
end
|
56
71
|
|
72
|
+
# Combine this composite with another list.
|
73
|
+
# @parameter list [List] The list to add.
|
74
|
+
# @returns [Composite] A new composite containing both lists.
|
57
75
|
def +(list)
|
58
76
|
if list.kind_of? Composite
|
59
77
|
self.class.new(@files + list.files)
|
@@ -61,19 +79,29 @@ module Build
|
|
61
79
|
self.class.new(@files + [list])
|
62
80
|
end
|
63
81
|
end
|
64
|
-
|
82
|
+
|
83
|
+
# Check if the composite includes a specific path.
|
84
|
+
# @parameter path [Path] The path to check.
|
85
|
+
# @returns [Boolean] True if any list in the composite includes the path.
|
65
86
|
def include?(path)
|
66
87
|
@files.any? {|list| list.include?(path)}
|
67
88
|
end
|
68
|
-
|
89
|
+
|
90
|
+
# Rebase all lists in the composite to a new root.
|
91
|
+
# @parameter root [Path] The new root path.
|
92
|
+
# @returns [Composite] A new composite with rebased lists.
|
69
93
|
def rebase(root)
|
70
94
|
self.class.new(@files.collect{|list| list.rebase(root)}, [root])
|
71
95
|
end
|
72
|
-
|
96
|
+
|
97
|
+
# Convert all lists in the composite to paths.
|
98
|
+
# @returns [Composite] A new composite with all lists converted to paths.
|
73
99
|
def to_paths
|
74
100
|
self.class.new(@files.collect(&:to_paths), roots: @roots)
|
75
101
|
end
|
76
102
|
|
103
|
+
# Generate a string representation for debugging.
|
104
|
+
# @returns [String] A debug string showing the composite structure.
|
77
105
|
def inspect
|
78
106
|
"<Composite #{@files.inspect}>"
|
79
107
|
end
|
@@ -7,7 +7,11 @@ require_relative "list"
|
|
7
7
|
|
8
8
|
module Build
|
9
9
|
module Files
|
10
|
+
# Represents a list of files with exclusions applied.
|
10
11
|
class Difference < List
|
12
|
+
# Initialize a difference list.
|
13
|
+
# @parameter list [List] The base list of files.
|
14
|
+
# @parameter excludes [List] The list of files to exclude.
|
11
15
|
def initialize(list, excludes)
|
12
16
|
@list = list
|
13
17
|
@excludes = excludes
|
@@ -15,6 +19,7 @@ module Build
|
|
15
19
|
|
16
20
|
attr :files
|
17
21
|
|
22
|
+
# Freeze the difference list and its dependencies.
|
18
23
|
def freeze
|
19
24
|
@list.freeze
|
20
25
|
@excludes.freeze
|
@@ -22,6 +27,9 @@ module Build
|
|
22
27
|
super
|
23
28
|
end
|
24
29
|
|
30
|
+
# Iterate over files in the base list, excluding those in the exclusion list.
|
31
|
+
# @yields {|path| ...} Each path not in the exclusion list.
|
32
|
+
# @parameter path [Path] The current file path.
|
25
33
|
def each
|
26
34
|
return to_enum(:each) unless block_given?
|
27
35
|
|
@@ -30,20 +38,31 @@ module Build
|
|
30
38
|
end
|
31
39
|
end
|
32
40
|
|
41
|
+
# Subtract additional files from this difference.
|
42
|
+
# @parameter list [List] Additional files to exclude.
|
43
|
+
# @returns [Difference] A new difference with expanded exclusions.
|
33
44
|
def -(list)
|
34
|
-
self.class.new(@list, Composite.new(@excludes, list))
|
45
|
+
self.class.new(@list, Composite.new([@excludes, list]))
|
35
46
|
end
|
36
47
|
|
48
|
+
# Check if the difference includes a specific path.
|
49
|
+
# @parameter path [Path] The path to check.
|
50
|
+
# @returns [Boolean] True if the path is in the base list but not excluded.
|
37
51
|
def include?(path)
|
38
|
-
@list.
|
52
|
+
@list.include?(path) and !@excludes.include?(path)
|
39
53
|
end
|
40
54
|
|
55
|
+
# Rebase the difference to a new root.
|
56
|
+
# @parameter root [Path] The new root path.
|
57
|
+
# @returns [Difference] A new difference with rebased files.
|
41
58
|
def rebase(root)
|
42
|
-
self.class.new(@
|
59
|
+
self.class.new(@list.rebase(root), @excludes.rebase(root))
|
43
60
|
end
|
44
61
|
|
62
|
+
# Generate a string representation for debugging.
|
63
|
+
# @returns [String] A debug string showing the difference structure.
|
45
64
|
def inspect
|
46
|
-
"<Difference #{@
|
65
|
+
"<Difference #{@list.inspect} - #{@excludes.inspect}>"
|
47
66
|
end
|
48
67
|
end
|
49
68
|
end
|
@@ -7,23 +7,36 @@ require_relative "list"
|
|
7
7
|
|
8
8
|
module Build
|
9
9
|
module Files
|
10
|
+
# Represents a directory of files.
|
10
11
|
class Directory < List
|
12
|
+
# Join path components and create a directory.
|
13
|
+
# @parameter args [Array(String)] The path components to join.
|
14
|
+
# @returns [Directory] A new directory at the joined path.
|
11
15
|
def self.join(*args)
|
12
16
|
self.new(Path.join(*args))
|
13
17
|
end
|
14
18
|
|
19
|
+
# Initialize a directory with a root path.
|
20
|
+
# @parameter root [Path | String] The root path of the directory.
|
15
21
|
def initialize(root)
|
16
22
|
@root = root
|
17
23
|
end
|
18
24
|
|
25
|
+
# Get the root path of the directory.
|
26
|
+
# @returns [Path] The root path.
|
19
27
|
def root
|
20
28
|
@root
|
21
29
|
end
|
22
30
|
|
31
|
+
# Get the root paths as an array.
|
32
|
+
# @returns [Array(Path)] An array containing the single root path.
|
23
33
|
def roots
|
24
34
|
[root]
|
25
35
|
end
|
26
36
|
|
37
|
+
# Iterate over all files in the directory recursively.
|
38
|
+
# @yields {|path| ...} Each file path in the directory.
|
39
|
+
# @parameter path [Path] The current file path.
|
27
40
|
def each
|
28
41
|
return to_enum(:each) unless block_given?
|
29
42
|
|
@@ -33,32 +46,48 @@ module Build
|
|
33
46
|
end
|
34
47
|
end
|
35
48
|
|
49
|
+
# Check equality with another directory.
|
50
|
+
# @parameter other [Directory] The other directory to compare.
|
51
|
+
# @returns [Boolean] True if both directories have the same root.
|
36
52
|
def eql?(other)
|
37
53
|
self.class.eql?(other.class) and @root.eql?(other.root)
|
38
54
|
end
|
39
55
|
|
56
|
+
# Compute the hash value for this directory.
|
57
|
+
# @returns [Integer] The hash value based on the root.
|
40
58
|
def hash
|
41
59
|
@root.hash
|
42
60
|
end
|
43
|
-
|
61
|
+
|
62
|
+
# Check if the directory includes a specific path.
|
63
|
+
# @parameter path [Path] The path to check.
|
64
|
+
# @returns [Boolean] True if the path is within this directory.
|
44
65
|
def include?(path)
|
45
66
|
# Would be true if path is a descendant of full_path.
|
46
67
|
path.start_with?(@root)
|
47
68
|
end
|
48
|
-
|
69
|
+
|
70
|
+
# Rebase the directory to a new root.
|
71
|
+
# @parameter root [Path] The new root path.
|
72
|
+
# @returns [Directory] A new directory with the rebased root.
|
49
73
|
def rebase(root)
|
50
74
|
self.class.new(@root.rebase(root))
|
51
75
|
end
|
52
76
|
|
53
|
-
# Convert
|
77
|
+
# Convert the directory to a string for use as a command argument.
|
78
|
+
# @returns [String] The root path as a string.
|
54
79
|
def to_str
|
55
80
|
@root.to_str
|
56
81
|
end
|
57
82
|
|
83
|
+
# Convert the directory to a string.
|
84
|
+
# @returns [String] The root path as a string.
|
58
85
|
def to_s
|
59
86
|
to_str
|
60
87
|
end
|
61
88
|
|
89
|
+
# Convert the directory to a path object.
|
90
|
+
# @returns [Path] The root path.
|
62
91
|
def to_path
|
63
92
|
@root
|
64
93
|
end
|
data/lib/build/files/glob.rb
CHANGED
@@ -8,12 +8,19 @@ require_relative "list"
|
|
8
8
|
module Build
|
9
9
|
module Files
|
10
10
|
class Path
|
11
|
+
# Create a glob pattern matcher for files under this path.
|
12
|
+
# @parameter pattern [String] The glob pattern to match.
|
13
|
+
# @returns [Glob] A glob matcher for the pattern.
|
11
14
|
def glob(pattern)
|
12
15
|
Glob.new(self, pattern)
|
13
16
|
end
|
14
17
|
end
|
15
18
|
|
19
|
+
# Represents a glob pattern for matching files.
|
16
20
|
class Glob < List
|
21
|
+
# Initialize a glob with a root path and pattern.
|
22
|
+
# @parameter root [Path] The root directory for the glob.
|
23
|
+
# @parameter pattern [String] The glob pattern to match.
|
17
24
|
def initialize(root, pattern)
|
18
25
|
@root = root
|
19
26
|
@pattern = pattern
|
@@ -22,10 +29,14 @@ module Build
|
|
22
29
|
attr :root
|
23
30
|
attr :pattern
|
24
31
|
|
32
|
+
# Get the root paths for this glob.
|
33
|
+
# @returns [Array(Path)] An array containing the root path.
|
25
34
|
def roots
|
26
35
|
[@root]
|
27
36
|
end
|
28
37
|
|
38
|
+
# Get the full pattern including the root path.
|
39
|
+
# @returns [String] The complete glob pattern.
|
29
40
|
def full_pattern
|
30
41
|
Path.join(@root, @pattern)
|
31
42
|
end
|
@@ -42,22 +53,35 @@ module Build
|
|
42
53
|
end
|
43
54
|
end
|
44
55
|
|
56
|
+
# Check equality with another glob.
|
57
|
+
# @parameter other [Glob] The other glob to compare.
|
58
|
+
# @returns [Boolean] True if both globs have the same root and pattern.
|
45
59
|
def eql?(other)
|
46
60
|
self.class.eql?(other.class) and @root.eql?(other.root) and @pattern.eql?(other.pattern)
|
47
61
|
end
|
48
62
|
|
63
|
+
# Compute the hash value for this glob.
|
64
|
+
# @returns [Integer] The hash value based on root and pattern.
|
49
65
|
def hash
|
50
66
|
[@root, @pattern].hash
|
51
67
|
end
|
52
68
|
|
69
|
+
# Check if a path matches this glob pattern.
|
70
|
+
# @parameter path [Path] The path to check.
|
71
|
+
# @returns [Boolean] True if the path matches the pattern.
|
53
72
|
def include?(path)
|
54
73
|
File.fnmatch(full_pattern, path)
|
55
74
|
end
|
56
75
|
|
76
|
+
# Rebase the glob to a new root.
|
77
|
+
# @parameter root [Path] The new root path.
|
78
|
+
# @returns [Glob] A new glob with the same pattern under the new root.
|
57
79
|
def rebase(root)
|
58
80
|
self.class.new(root, @pattern)
|
59
81
|
end
|
60
82
|
|
83
|
+
# Generate a string representation for debugging.
|
84
|
+
# @returns [String] A debug string showing the full pattern.
|
61
85
|
def inspect
|
62
86
|
"<Glob #{full_pattern.inspect}>"
|
63
87
|
end
|
data/lib/build/files/list.rb
CHANGED
@@ -11,6 +11,8 @@ module Build
|
|
11
11
|
class List
|
12
12
|
include Enumerable
|
13
13
|
|
14
|
+
# Get all unique root paths from the list.
|
15
|
+
# @returns [Array(Path)] Sorted unique root paths.
|
14
16
|
def roots
|
15
17
|
collect{|path| path.root}.sort.uniq
|
16
18
|
end
|
@@ -20,6 +22,9 @@ module Build
|
|
20
22
|
Composite.new([self, list])
|
21
23
|
end
|
22
24
|
|
25
|
+
# Subtract a list from this list.
|
26
|
+
# @parameter list [List] The list to subtract.
|
27
|
+
# @returns [Difference] A difference list excluding the given paths.
|
23
28
|
def -(list)
|
24
29
|
Difference.new(self, list)
|
25
30
|
end
|
@@ -40,6 +45,8 @@ module Build
|
|
40
45
|
other.any?{|path| include?(path)}
|
41
46
|
end
|
42
47
|
|
48
|
+
# Check if the list is empty.
|
49
|
+
# @returns [Boolean] True if the list contains no paths.
|
43
50
|
def empty?
|
44
51
|
each do
|
45
52
|
return false
|
@@ -48,6 +55,12 @@ module Build
|
|
48
55
|
return true
|
49
56
|
end
|
50
57
|
|
58
|
+
# Transform paths with modified attributes.
|
59
|
+
# @parameter options [Hash] Options to pass to {Path#with}.
|
60
|
+
# @yields {|path, updated_path| ...} Each original and updated path.
|
61
|
+
# @parameter path [Path] The original path.
|
62
|
+
# @parameter updated_path [Path] The modified path.
|
63
|
+
# @returns [Paths] A new paths list with transformed paths.
|
51
64
|
def with(**options)
|
52
65
|
return to_enum(:with, **options) unless block_given?
|
53
66
|
|
@@ -64,18 +77,30 @@ module Build
|
|
64
77
|
return Paths.new(paths)
|
65
78
|
end
|
66
79
|
|
80
|
+
# Rebase all paths in the list to a new root.
|
81
|
+
# @parameter root [Path] The new root path.
|
82
|
+
# @returns [Paths] A new paths list with rebased paths.
|
67
83
|
def rebase(root)
|
68
84
|
Paths.new(self.collect{|path| path.rebase(root)}, [root])
|
69
85
|
end
|
70
86
|
|
87
|
+
# Convert the list to a Paths instance.
|
88
|
+
# @returns [Paths] A paths list containing all items.
|
71
89
|
def to_paths
|
72
90
|
Paths.new(each.to_a)
|
73
91
|
end
|
74
92
|
|
93
|
+
# Map over the list and return a Paths instance.
|
94
|
+
# @yields {|path| ...} Each path in the list.
|
95
|
+
# @parameter path [Path] The current path.
|
96
|
+
# @returns [Paths] A new paths list with mapped values.
|
75
97
|
def map
|
76
98
|
Paths.new(super)
|
77
99
|
end
|
78
100
|
|
101
|
+
# Coerce an argument to a List instance.
|
102
|
+
# @parameter arg [List | Object] The object to coerce.
|
103
|
+
# @returns [List] A list instance.
|
79
104
|
def self.coerce(arg)
|
80
105
|
if arg.kind_of? self
|
81
106
|
arg
|
@@ -84,6 +109,8 @@ module Build
|
|
84
109
|
end
|
85
110
|
end
|
86
111
|
|
112
|
+
# Convert the list to a string.
|
113
|
+
# @returns [String] The string representation.
|
87
114
|
def to_s
|
88
115
|
inspect
|
89
116
|
end
|
data/lib/build/files/path.rb
CHANGED
@@ -7,10 +7,15 @@ module Build
|
|
7
7
|
module Files
|
8
8
|
# Represents a file path with an absolute root and a relative offset:
|
9
9
|
class Path
|
10
|
+
# Get the current working directory as a path.
|
11
|
+
# @returns [Path] The current directory path.
|
10
12
|
def self.current
|
11
13
|
self.new(::Dir.pwd)
|
12
14
|
end
|
13
15
|
|
16
|
+
# Split a path into directory, filename, and extension components.
|
17
|
+
# @parameter path [String] The path to split.
|
18
|
+
# @returns [Array(String, String, String)] The directory, filename, and extension.
|
14
19
|
def self.split(path)
|
15
20
|
# Effectively dirname and basename:
|
16
21
|
dirname, separator, filename = path.rpartition(File::SEPARATOR)
|
@@ -33,6 +38,9 @@ module Build
|
|
33
38
|
end
|
34
39
|
end
|
35
40
|
|
41
|
+
# Get the root directory of a path.
|
42
|
+
# @parameter path [Path | String] The path to get the root from.
|
43
|
+
# @returns [String] The root directory.
|
36
44
|
def self.root(path)
|
37
45
|
if Path === path
|
38
46
|
path.root
|
@@ -41,7 +49,10 @@ module Build
|
|
41
49
|
end
|
42
50
|
end
|
43
51
|
|
44
|
-
#
|
52
|
+
# Compute the shortest relative path from root to path.
|
53
|
+
# @parameter path [Path | String] The target path.
|
54
|
+
# @parameter root [Path | String] The root directory.
|
55
|
+
# @returns [String] The shortest relative path.
|
45
56
|
def self.shortest_path(path, root)
|
46
57
|
path_components = Path.components(path)
|
47
58
|
root_components = Path.components(root)
|
@@ -61,6 +72,10 @@ module Build
|
|
61
72
|
end
|
62
73
|
end
|
63
74
|
|
75
|
+
# Compute the relative path from root to full path.
|
76
|
+
# @parameter root [String] The root directory.
|
77
|
+
# @parameter full_path [String] The full path.
|
78
|
+
# @returns [String] The relative path.
|
64
79
|
def self.relative_path(root, full_path)
|
65
80
|
relative_offset = root.length
|
66
81
|
|
@@ -70,6 +85,9 @@ module Build
|
|
70
85
|
return full_path.slice(relative_offset..-1)
|
71
86
|
end
|
72
87
|
|
88
|
+
# Convert a path-like object to a Path instance.
|
89
|
+
# @parameter path [Path | String] The path to convert.
|
90
|
+
# @returns [Path] A Path instance.
|
73
91
|
def self.[] path
|
74
92
|
self === path ? path : self.new(path.to_s)
|
75
93
|
end
|
@@ -91,20 +109,28 @@ module Build
|
|
91
109
|
attr :root
|
92
110
|
attr :full_path
|
93
111
|
|
112
|
+
# Get the length of the full path.
|
113
|
+
# @returns [Integer] The number of characters in the path.
|
94
114
|
def length
|
95
115
|
@full_path.length
|
96
116
|
end
|
97
117
|
|
98
118
|
alias size length
|
99
119
|
|
120
|
+
# Get the path components as an array.
|
121
|
+
# @returns [Array(String)] The path split by directory separator.
|
100
122
|
def components
|
101
123
|
@components ||= @full_path.split(File::SEPARATOR).freeze
|
102
124
|
end
|
103
125
|
|
126
|
+
# Get the basename of the path.
|
127
|
+
# @returns [String] The last component of the path.
|
104
128
|
def basename
|
105
129
|
self.parts.last
|
106
130
|
end
|
107
131
|
|
132
|
+
# Get the parent directory path.
|
133
|
+
# @returns [Path] The parent directory.
|
108
134
|
def parent
|
109
135
|
root = @root
|
110
136
|
full_path = File.dirname(@full_path)
|
@@ -120,22 +146,32 @@ module Build
|
|
120
146
|
self.class.new(full_path, root)
|
121
147
|
end
|
122
148
|
|
149
|
+
# Check if the path starts with the given prefix.
|
150
|
+
# @parameter args [Array(String)] The prefix strings to check.
|
151
|
+
# @returns [Boolean] True if the path starts with any of the prefixes.
|
123
152
|
def start_with?(*args)
|
124
153
|
@full_path.start_with?(*args)
|
125
154
|
end
|
126
155
|
|
127
156
|
alias parts components
|
128
157
|
|
158
|
+
# Get the relative path from the root.
|
159
|
+
# @returns [String] The path relative to the root.
|
129
160
|
def relative_path
|
130
161
|
@relative_path ||= Path.relative_path(@root.to_s, @full_path.to_s).freeze
|
131
162
|
end
|
132
163
|
|
164
|
+
# Split the relative path into directory and basename components.
|
165
|
+
# @returns [Array(String, String)] The directory and basename.
|
133
166
|
def relative_parts
|
134
167
|
dirname, _, basename = self.relative_path.rpartition(File::SEPARATOR)
|
135
168
|
|
136
169
|
return dirname, basename
|
137
170
|
end
|
138
171
|
|
172
|
+
# Append an extension to the path.
|
173
|
+
# @parameter extension [String] The extension to append.
|
174
|
+
# @returns [Path] A new path with the extension appended.
|
139
175
|
def append(extension)
|
140
176
|
self.class.new(@full_path + extension, @root)
|
141
177
|
end
|
@@ -160,10 +196,18 @@ module Build
|
|
160
196
|
end
|
161
197
|
end
|
162
198
|
|
199
|
+
# Rebase the path to a new root directory.
|
200
|
+
# @parameter root [Path | String] The new root.
|
201
|
+
# @returns [Path] A new path with the same relative path under the new root.
|
163
202
|
def rebase(root)
|
164
203
|
self.class.new(File.join(root, relative_path), root)
|
165
204
|
end
|
166
205
|
|
206
|
+
# Create a modified path with new root, extension, or basename.
|
207
|
+
# @parameter root [Path | String] The new root directory.
|
208
|
+
# @parameter extension [String | Nil] An extension to add.
|
209
|
+
# @parameter basename [String | Boolean | Nil] A new basename or `true` to keep existing.
|
210
|
+
# @returns [Path] A new path with the specified modifications.
|
167
211
|
def with(root: @root, extension: nil, basename: false)
|
168
212
|
relative_path = self.relative_path
|
169
213
|
|
@@ -183,6 +227,10 @@ module Build
|
|
183
227
|
self.class.new(File.join(root, relative_path), root, relative_path)
|
184
228
|
end
|
185
229
|
|
230
|
+
# Join a root and relative path to create a new Path.
|
231
|
+
# @parameter root [String] The root directory.
|
232
|
+
# @parameter relative_path [String] The relative path.
|
233
|
+
# @returns [Path] A new path combining root and relative path.
|
186
234
|
def self.join(root, relative_path)
|
187
235
|
self.new(File.join(root, relative_path), root)
|
188
236
|
end
|
@@ -196,37 +244,56 @@ module Build
|
|
196
244
|
end
|
197
245
|
end
|
198
246
|
|
247
|
+
# Compute the shortest path from this path to a root.
|
248
|
+
# @parameter root [Path | String] The root directory.
|
249
|
+
# @returns [String] The shortest relative path.
|
199
250
|
def shortest_path(root)
|
200
251
|
self.class.shortest_path(self, root)
|
201
252
|
end
|
202
253
|
|
254
|
+
# Convert the path to a string.
|
255
|
+
# @returns [String] The full path as a string.
|
203
256
|
def to_str
|
204
257
|
@full_path.to_str
|
205
258
|
end
|
206
259
|
|
260
|
+
# Convert the path to a path string.
|
261
|
+
# @returns [String] The full path.
|
207
262
|
def to_path
|
208
263
|
@full_path
|
209
264
|
end
|
210
265
|
|
266
|
+
# Convert the path to a string representation.
|
267
|
+
# @returns [String] The full path as a string.
|
211
268
|
def to_s
|
212
269
|
# It's not guaranteed to be string.
|
213
270
|
@full_path.to_s
|
214
271
|
end
|
215
272
|
|
273
|
+
# Generate a string representation for debugging.
|
274
|
+
# @returns [String] A debug string showing root and relative path.
|
216
275
|
def inspect
|
217
276
|
"#{@root.inspect}/#{relative_path.inspect}"
|
218
277
|
end
|
219
278
|
|
279
|
+
# Compute the hash value for this path.
|
280
|
+
# @returns [Integer] The hash value based on root and full path.
|
220
281
|
def hash
|
221
282
|
[@root, @full_path].hash
|
222
283
|
end
|
223
284
|
|
285
|
+
# Check equality with another path.
|
286
|
+
# @parameter other [Path] The other path to compare.
|
287
|
+
# @returns [Boolean] True if both paths have the same root and full path.
|
224
288
|
def eql?(other)
|
225
289
|
self.class.eql?(other.class) and @root.eql?(other.root) and @full_path.eql?(other.full_path)
|
226
290
|
end
|
227
291
|
|
228
292
|
include Comparable
|
229
293
|
|
294
|
+
# Compare this path with another for sorting.
|
295
|
+
# @parameter other [Path] The other path to compare.
|
296
|
+
# @returns [Integer] -1, 0, or 1 for less than, equal, or greater than.
|
230
297
|
def <=>(other)
|
231
298
|
self.to_s <=> other.to_s
|
232
299
|
end
|
@@ -238,14 +305,20 @@ module Build
|
|
238
305
|
return File.fnmatch(pattern, path, flags)
|
239
306
|
end
|
240
307
|
|
308
|
+
# Get file opening arguments for reading.
|
309
|
+
# @returns [Array] The path and file mode for reading.
|
241
310
|
def for_reading
|
242
311
|
[@full_path, File::RDONLY]
|
243
312
|
end
|
244
313
|
|
314
|
+
# Get file opening arguments for writing.
|
315
|
+
# @returns [Array] The path and file mode for writing.
|
245
316
|
def for_writing
|
246
317
|
[@full_path, File::CREAT|File::TRUNC|File::WRONLY]
|
247
318
|
end
|
248
319
|
|
320
|
+
# Get file opening arguments for appending.
|
321
|
+
# @returns [Array] The path and file mode for appending.
|
249
322
|
def for_appending
|
250
323
|
[@full_path, File::CREAT|File::APPEND|File::WRONLY]
|
251
324
|
end
|
data/lib/build/files/paths.rb
CHANGED
@@ -7,7 +7,11 @@ require_relative "list"
|
|
7
7
|
|
8
8
|
module Build
|
9
9
|
module Files
|
10
|
+
# Represents an explicit list of file paths.
|
10
11
|
class Paths < List
|
12
|
+
# Initialize a paths list.
|
13
|
+
# @parameter list [Array] The array of paths.
|
14
|
+
# @parameter roots [Array(Path) | Nil] The root paths, if known.
|
11
15
|
def initialize(list, roots = nil)
|
12
16
|
@list = Array(list).freeze
|
13
17
|
@roots = roots
|
@@ -20,32 +24,50 @@ module Build
|
|
20
24
|
@roots ||= super
|
21
25
|
end
|
22
26
|
|
27
|
+
# Get the count of paths in the list.
|
28
|
+
# @returns [Integer] The number of paths.
|
23
29
|
def count
|
24
30
|
@list.count
|
25
31
|
end
|
26
32
|
|
33
|
+
# Iterate over all paths in the list.
|
34
|
+
# @yields {|path| ...} Each path in the list.
|
35
|
+
# @parameter path [Path] The current path.
|
27
36
|
def each
|
28
37
|
return to_enum(:each) unless block_given?
|
29
38
|
|
30
39
|
@list.each{|path| yield path}
|
31
40
|
end
|
32
41
|
|
42
|
+
# Check equality with another paths list.
|
43
|
+
# @parameter other [Paths] The other paths list to compare.
|
44
|
+
# @returns [Boolean] True if both have the same paths.
|
33
45
|
def eql?(other)
|
34
46
|
self.class.eql?(other.class) and @list.eql?(other.list)
|
35
47
|
end
|
36
|
-
|
48
|
+
|
49
|
+
# Compute the hash value for this paths list.
|
50
|
+
# @returns [Integer] The hash value based on the list.
|
37
51
|
def hash
|
38
52
|
@list.hash
|
39
53
|
end
|
40
54
|
|
55
|
+
# Return this paths list unchanged.
|
56
|
+
# @returns [Paths] Self.
|
41
57
|
def to_paths
|
42
58
|
self
|
43
59
|
end
|
44
60
|
|
61
|
+
# Generate a string representation for debugging.
|
62
|
+
# @returns [String] A debug string showing the paths.
|
45
63
|
def inspect
|
46
64
|
"<Paths #{@list.inspect}>"
|
47
65
|
end
|
48
66
|
|
67
|
+
# Create a paths list from a directory root and relative paths.
|
68
|
+
# @parameter root [Path] The root directory.
|
69
|
+
# @parameter relative_paths [Array(String)] The relative paths.
|
70
|
+
# @returns [Paths] A new paths list.
|
49
71
|
def self.directory(root, relative_paths)
|
50
72
|
paths = relative_paths.collect do |path|
|
51
73
|
Path.join(root, path)
|
@@ -56,6 +78,9 @@ module Build
|
|
56
78
|
end
|
57
79
|
|
58
80
|
class Path
|
81
|
+
# Create a paths list from relative paths under this path.
|
82
|
+
# @parameter relative_paths [Array(String)] The relative paths.
|
83
|
+
# @returns [Paths] A new paths list.
|
59
84
|
def list(*relative_paths)
|
60
85
|
Paths.directory(self, relative_paths)
|
61
86
|
end
|
data/lib/build/files/state.rb
CHANGED
@@ -17,6 +17,9 @@ module Build
|
|
17
17
|
class FileTime
|
18
18
|
include Comparable
|
19
19
|
|
20
|
+
# Initialize a file time record.
|
21
|
+
# @parameter path [Path] The file path.
|
22
|
+
# @parameter time [Time] The modification time.
|
20
23
|
def initialize(path, time)
|
21
24
|
@path = path
|
22
25
|
@time = time
|
@@ -25,15 +28,23 @@ module Build
|
|
25
28
|
attr :path
|
26
29
|
attr :time
|
27
30
|
|
31
|
+
# Compare file times for ordering.
|
32
|
+
# @parameter other [FileTime] The other file time to compare.
|
33
|
+
# @returns [Integer] -1, 0, or 1 for less than, equal, or greater than.
|
28
34
|
def <=> other
|
29
35
|
@time <=> other.time
|
30
36
|
end
|
31
37
|
|
38
|
+
# Generate a string representation for debugging.
|
39
|
+
# @returns [String] A debug string showing path and time.
|
32
40
|
def inspect
|
33
41
|
"<FileTime #{@path.inspect} #{@time.inspect}>"
|
34
42
|
end
|
35
43
|
end
|
36
44
|
|
45
|
+
# Initialize file state tracking.
|
46
|
+
# @parameter files [List] The list of files to track.
|
47
|
+
# @raises [ArgumentError] If files is not a Files::List.
|
37
48
|
def initialize(files)
|
38
49
|
raise ArgumentError.new("Invalid files list: #{files}") unless Files::List === files
|
39
50
|
|
@@ -55,6 +66,8 @@ module Build
|
|
55
66
|
|
56
67
|
def_delegators :@files, :each, :roots, :count
|
57
68
|
|
69
|
+
# Update the state by checking all files for changes.
|
70
|
+
# @returns [Boolean] True if any files were added, changed, removed, or are missing.
|
58
71
|
def update!
|
59
72
|
last_times = @times
|
60
73
|
@times = {}
|
@@ -72,7 +85,7 @@ module Build
|
|
72
85
|
|
73
86
|
if File.exist?(path)
|
74
87
|
modified_time = File.mtime(path)
|
75
|
-
|
88
|
+
|
76
89
|
if last_time = last_times.delete(path)
|
77
90
|
# Path was valid last update:
|
78
91
|
if modified_time != last_time
|
@@ -86,9 +99,9 @@ module Build
|
|
86
99
|
|
87
100
|
# puts "Added: #{path}"
|
88
101
|
end
|
89
|
-
|
102
|
+
|
90
103
|
@times[path] = modified_time
|
91
|
-
|
104
|
+
|
92
105
|
unless File.directory?(path)
|
93
106
|
file_times << FileTime.new(path, modified_time)
|
94
107
|
end
|
@@ -111,14 +124,20 @@ module Build
|
|
111
124
|
attr :oldest_time
|
112
125
|
attr :newest_time
|
113
126
|
|
127
|
+
# Check if any files are missing.
|
128
|
+
# @returns [Boolean] True if any files do not exist.
|
114
129
|
def missing?
|
115
130
|
!@missing.empty?
|
116
131
|
end
|
117
132
|
|
133
|
+
# Check if the state is empty.
|
134
|
+
# @returns [Boolean] True if no files are being tracked.
|
118
135
|
def empty?
|
119
136
|
@times.empty?
|
120
137
|
end
|
121
138
|
|
139
|
+
# Generate a string representation for debugging.
|
140
|
+
# @returns [String] A debug string showing state changes.
|
122
141
|
def inspect
|
123
142
|
"<State Added:#{@added} Removed:#{@removed} Changed:#{@changed} Missing:#{@missing}>"
|
124
143
|
end
|
@@ -149,6 +168,10 @@ module Build
|
|
149
168
|
return true
|
150
169
|
end
|
151
170
|
|
171
|
+
# Check if outputs are dirty with respect to inputs.
|
172
|
+
# @parameter inputs [State] The input files state.
|
173
|
+
# @parameter outputs [State] The output files state.
|
174
|
+
# @returns [Boolean] True if outputs need to be regenerated.
|
152
175
|
def self.dirty?(inputs, outputs)
|
153
176
|
outputs.dirty?(inputs)
|
154
177
|
end
|
data/lib/build/files/system.rb
CHANGED
@@ -30,6 +30,8 @@ module Build
|
|
30
30
|
end
|
31
31
|
end
|
32
32
|
|
33
|
+
# Copy the path to a destination.
|
34
|
+
# @parameter destination [Path] The destination path.
|
33
35
|
def copy(destination)
|
34
36
|
if directory?
|
35
37
|
destination.create
|
@@ -43,6 +45,8 @@ module Build
|
|
43
45
|
FileUtils.touch(self.to_s)
|
44
46
|
end
|
45
47
|
|
48
|
+
# Get file statistics.
|
49
|
+
# @returns [File::Stat] The file statistics.
|
46
50
|
def stat
|
47
51
|
File.stat(self.to_s)
|
48
52
|
end
|
@@ -57,14 +61,20 @@ module Build
|
|
57
61
|
File.directory?(self.to_s)
|
58
62
|
end
|
59
63
|
|
64
|
+
# Check if the path refers to a regular file.
|
65
|
+
# @returns [Boolean] True if the path is a file.
|
60
66
|
def file?
|
61
67
|
File.file?(self.to_s)
|
62
68
|
end
|
63
69
|
|
70
|
+
# Check if the path is a symbolic link.
|
71
|
+
# @returns [Boolean] True if the path is a symlink.
|
64
72
|
def symlink?
|
65
73
|
File.symlink?(self.to_s)
|
66
74
|
end
|
67
75
|
|
76
|
+
# Check if the file is readable.
|
77
|
+
# @returns [Boolean] True if the file can be read.
|
68
78
|
def readable?
|
69
79
|
File.readable?(self.to_s)
|
70
80
|
end
|
@@ -110,6 +120,8 @@ module Build
|
|
110
120
|
each(&:delete)
|
111
121
|
end
|
112
122
|
|
123
|
+
# Copy all files in the list to a destination.
|
124
|
+
# @parameter destination [Path] The destination root path.
|
113
125
|
def copy(destination)
|
114
126
|
each do |path|
|
115
127
|
path.copy(destination / path.relative_path)
|
data/lib/build/files/version.rb
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2014-
|
4
|
+
# Copyright, 2014-2025, by Samuel Williams.
|
5
5
|
|
6
|
+
# @namespace
|
6
7
|
module Build
|
8
|
+
# @namespace
|
7
9
|
module Files
|
8
|
-
VERSION = "1.
|
10
|
+
VERSION = "1.10.0"
|
9
11
|
end
|
10
12
|
end
|
data/readme.md
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
Build::Files is a set of idiomatic classes for dealing with paths and monitoring directories. File paths are represented with both root and relative parts which makes copying directory structures intuitive.
|
4
4
|
|
5
|
-
[](/actions?workflow=Test)
|
5
|
+
[](https://github.com/ioquatix/build-files/actions?workflow=Test)
|
6
6
|
|
7
7
|
## Installation
|
8
8
|
|
@@ -20,19 +20,13 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
|
23
|
+
Please see the [project documentation](https://github.com/ioquatix/build-files) for more details.
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
path = Build::Files::Path("/foo/bar/baz")
|
28
|
-
=> "/foo/bar"/"baz"
|
29
|
-
|
30
|
-
> path.root
|
31
|
-
=> "/foo/bar"
|
32
|
-
> path.relative_path
|
33
|
-
=> "baz"
|
25
|
+
## Releases
|
34
26
|
|
35
|
-
|
27
|
+
Please see the [project releases](https://github.com/ioquatix/build-filesreleases/index) for all releases.
|
28
|
+
|
29
|
+
### v1.10.0
|
36
30
|
|
37
31
|
## Contributing
|
38
32
|
|
data/releases.md
ADDED
data.tar.gz.sig
CHANGED
Binary file
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: build-files
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.10.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -36,7 +36,7 @@ cert_chain:
|
|
36
36
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
37
37
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
38
38
|
-----END CERTIFICATE-----
|
39
|
-
date:
|
39
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
40
40
|
dependencies: []
|
41
41
|
executables: []
|
42
42
|
extensions: []
|
@@ -55,6 +55,7 @@ files:
|
|
55
55
|
- lib/build/files/version.rb
|
56
56
|
- license.md
|
57
57
|
- readme.md
|
58
|
+
- releases.md
|
58
59
|
homepage: https://github.com/ioquatix/build-files
|
59
60
|
licenses:
|
60
61
|
- MIT
|
@@ -68,14 +69,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
68
69
|
requirements:
|
69
70
|
- - ">="
|
70
71
|
- !ruby/object:Gem::Version
|
71
|
-
version: '3.
|
72
|
+
version: '3.2'
|
72
73
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
74
|
requirements:
|
74
75
|
- - ">="
|
75
76
|
- !ruby/object:Gem::Version
|
76
77
|
version: '0'
|
77
78
|
requirements: []
|
78
|
-
rubygems_version: 3.
|
79
|
+
rubygems_version: 3.7.2
|
79
80
|
specification_version: 4
|
80
81
|
summary: Abstractions for handling and mapping paths.
|
81
82
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|