revtree 0.1.0 → 0.1.1
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
- data/README.md +2 -2
- data/lib/revtree.rb +125 -12
- metadata +21 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3cfacc4513023ac62b90be3f9eed6fe2ab45af739aa6676413c947a214b7595f
|
4
|
+
data.tar.gz: cd7763aed7ca500ef2aaa1103d82e33667b9ad6d732839768b41100f3af435bb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b723f8f00a74e68176bb3b9c60cfd09458cd0b67029ad903c76aee21a411394de498f04285fff7ae07436b35d2b55701e16a6f68fea1645c80acb1ebc6d1111d
|
7
|
+
data.tar.gz: c6d506353323eb333c849460132257f93abe1426c8fb8b3e6724c16499942409d99db0311c2e7ccd36da8bd401b8396e1689928ac3951b1aa9cc3fcff70a06fa
|
data/README.md
CHANGED
@@ -39,7 +39,7 @@ sudo make install
|
|
39
39
|
git clone https://github.com/juliankahlert/revtree.git
|
40
40
|
cd revtree
|
41
41
|
gem build revtree.gemspec
|
42
|
-
sudo gem install --local revtree-0.1.
|
42
|
+
sudo gem install --local revtree-0.1.1.gem
|
43
43
|
```
|
44
44
|
|
45
45
|
## API Documentation
|
@@ -107,7 +107,7 @@ end
|
|
107
107
|
|
108
108
|
## Encouragement for Contribution
|
109
109
|
|
110
|
-
|
110
|
+
Contributions from the community are welcome!
|
111
111
|
If you find any issues or have ideas for new features, please feel free to submit a pull request or open an issue.
|
112
112
|
Your input helps make RevTree better for everyone.
|
113
113
|
|
data/lib/revtree.rb
CHANGED
@@ -26,9 +26,31 @@ require 'digest'
|
|
26
26
|
require 'pathname'
|
27
27
|
require 'json'
|
28
28
|
|
29
|
+
# The `RevTree` class provides a tree structure representing file directories and
|
30
|
+
# files, allowing for version tracking based on MD5 hashes.
|
31
|
+
#
|
32
|
+
# This class can traverse directories, compare versions of trees, and serialize/deserialize
|
33
|
+
# itself to/from JSON.
|
29
34
|
class RevTree
|
30
|
-
|
35
|
+
# @return [Array<RevTree>] the list of children in the tree (empty for files)
|
36
|
+
attr_reader :children
|
37
|
+
|
38
|
+
# @return [Symbol] the type of the node (:folder or :file)
|
39
|
+
attr_reader :type
|
40
|
+
|
41
|
+
# @return [String] the name of the file or directory
|
42
|
+
attr_reader :name
|
31
43
|
|
44
|
+
# @return [String] the revision (MD5 hash) of the file or directory
|
45
|
+
attr_reader :rev
|
46
|
+
|
47
|
+
# @return [Symbol] the status of the file or directory (:unmodified, :modified, :added, :removed)
|
48
|
+
attr_reader :status
|
49
|
+
|
50
|
+
# Initializes a new `RevTree` object representing a directory or file.
|
51
|
+
#
|
52
|
+
# @param path [String, Pathname] the path to the file or directory
|
53
|
+
# @param whitelist [Array<String>, nil] a list of file patterns to include (optional)
|
32
54
|
def initialize(path, whitelist = nil)
|
33
55
|
@path = Pathname.new(path)
|
34
56
|
@name = @path.basename.to_s
|
@@ -42,21 +64,19 @@ class RevTree
|
|
42
64
|
end
|
43
65
|
end
|
44
66
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
def calculate_directory_rev
|
50
|
-
Digest::MD5.hexdigest(@children.map(&:rev).join)
|
51
|
-
end
|
52
|
-
|
67
|
+
# Prints the tree structure, including file names and statuses, to the console.
|
68
|
+
#
|
69
|
+
# @param indent [Integer] the indentation level (default: 0)
|
70
|
+
# @return [void]
|
53
71
|
def print_tree(indent = 0)
|
54
72
|
status_str = @status ? " (status: #{@status})" : ''
|
55
73
|
puts "#{' ' * indent}#{@type == :folder ? '[Folder]' : '[File]'} #{@name} (rev: #{@rev})#{status_str}"
|
56
74
|
@children.each { |child| child.print_tree(indent + 1) }
|
57
75
|
end
|
58
76
|
|
59
|
-
#
|
77
|
+
# Serializes the `RevTree` object to a hash.
|
78
|
+
#
|
79
|
+
# @return [Hash] a hash representing the object
|
60
80
|
def to_h
|
61
81
|
{
|
62
82
|
type: @type,
|
@@ -67,14 +87,25 @@ class RevTree
|
|
67
87
|
}
|
68
88
|
end
|
69
89
|
|
70
|
-
|
90
|
+
# Converts the `RevTree` object to JSON format.
|
91
|
+
#
|
92
|
+
# @return [String] a JSON string representing the object
|
93
|
+
def to_json
|
71
94
|
JSON.pretty_generate(self.to_h)
|
72
95
|
end
|
73
96
|
|
97
|
+
# Reconstructs a `RevTree` object from a hash.
|
98
|
+
#
|
99
|
+
# @param h [Hash] the hash to deserialize
|
100
|
+
# @return [RevTree] the reconstructed `RevTree` object
|
74
101
|
def self.from_h(h)
|
75
102
|
new_tree(h[:name], h[:type].to_sym, h[:rev], h[:children], h[:status])
|
76
103
|
end
|
77
104
|
|
105
|
+
# Reconstructs a `RevTree` object from a JSON string.
|
106
|
+
#
|
107
|
+
# @param json_str [String] the JSON string to deserialize
|
108
|
+
# @return [RevTree] the reconstructed `RevTree` object
|
78
109
|
def self.from_json(json_str)
|
79
110
|
data = JSON.parse(json_str, symbolize_names: true)
|
80
111
|
file_tree = from_h(data)
|
@@ -82,7 +113,14 @@ class RevTree
|
|
82
113
|
file_tree
|
83
114
|
end
|
84
115
|
|
85
|
-
|
116
|
+
# Executes a block of code for each file matching the provided status whitelist.
|
117
|
+
#
|
118
|
+
# @param status_whitelist [Array<Symbol>] the list of statuses to match (:added, :modified, etc.)
|
119
|
+
# @yield [node, full_path] the block to be executed for each matching file
|
120
|
+
# @yieldparam node [RevTree] the current node being traversed
|
121
|
+
# @yieldparam full_path [String] the full path of the current node
|
122
|
+
# @return [void]
|
123
|
+
def for_each(status_whitelist = [:unmodified, :modified, :added, :removed], &block)
|
86
124
|
return unless block_given?
|
87
125
|
|
88
126
|
RevTree.traverse_tree(self, status_whitelist, @path, &block)
|
@@ -90,6 +128,23 @@ class RevTree
|
|
90
128
|
|
91
129
|
private
|
92
130
|
|
131
|
+
# Calculates the MD5 hash for the file.
|
132
|
+
#
|
133
|
+
# @return [String] the MD5 hash of the file
|
134
|
+
def calculate_file_rev
|
135
|
+
Digest::MD5.file(@path).hexdigest
|
136
|
+
end
|
137
|
+
|
138
|
+
# Calculates the MD5 hash for the directory based on its children.
|
139
|
+
#
|
140
|
+
# @return [String] the MD5 hash of the directory
|
141
|
+
def calculate_directory_rev
|
142
|
+
Digest::MD5.hexdigest(@children.map(&:rev).join)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Initializes the directory node by traversing its children.
|
146
|
+
#
|
147
|
+
# @return [void]
|
93
148
|
def init_dir
|
94
149
|
@type = :folder
|
95
150
|
@children = @path.children
|
@@ -98,12 +153,23 @@ class RevTree
|
|
98
153
|
@rev = calculate_directory_rev
|
99
154
|
end
|
100
155
|
|
156
|
+
# Initializes the file node by calculating its revision.
|
157
|
+
#
|
158
|
+
# @return [void]
|
101
159
|
def init_file
|
102
160
|
@type = :file
|
103
161
|
@children = []
|
104
162
|
@rev = calculate_file_rev
|
105
163
|
end
|
106
164
|
|
165
|
+
# Rebuilds a `RevTree` from its serialized components.
|
166
|
+
#
|
167
|
+
# @param name [String] the name of the node
|
168
|
+
# @param type [Symbol] the type of the node (:folder or :file)
|
169
|
+
# @param rev [String] the revision hash of the node
|
170
|
+
# @param children [Array<Hash>] the child nodes (if any)
|
171
|
+
# @param status [Symbol] the status of the node (:unmodified, :modified, etc.)
|
172
|
+
# @return [RevTree] the reconstructed tree
|
107
173
|
def self.new_tree(name, type, rev, children, status = :unmodified)
|
108
174
|
tree = allocate
|
109
175
|
tree.instance_variable_set(:@name, name)
|
@@ -118,6 +184,10 @@ class RevTree
|
|
118
184
|
tree
|
119
185
|
end
|
120
186
|
|
187
|
+
# Determines whether a file or directory should be included in the tree.
|
188
|
+
#
|
189
|
+
# @param path [Pathname] the path to the file or directory
|
190
|
+
# @return [Boolean] `true` if the path should be included, `false` otherwise
|
121
191
|
def include_in_tree?(path)
|
122
192
|
return false if path.directory? && path.basename.to_s.start_with?('.')
|
123
193
|
|
@@ -127,6 +197,11 @@ class RevTree
|
|
127
197
|
@whitelist.any? { |p| File.fnmatch?(p, path.basename.to_s) }
|
128
198
|
end
|
129
199
|
|
200
|
+
# Compares two `RevTree` nodes (old and new) and returns a tree with appropriate status.
|
201
|
+
#
|
202
|
+
# @param old [RevTree, nil] the old version of the tree
|
203
|
+
# @param new [RevTree, nil] the new version of the tree
|
204
|
+
# @return [RevTree, nil] the resulting tree with status updates or `nil`
|
130
205
|
def self.compare(old, new)
|
131
206
|
return nil if old.nil? && new.nil?
|
132
207
|
|
@@ -140,18 +215,31 @@ class RevTree
|
|
140
215
|
end
|
141
216
|
end
|
142
217
|
|
218
|
+
# Handles the addition of a new node.
|
219
|
+
#
|
220
|
+
# @param new [RevTree] the new node
|
221
|
+
# @return [RevTree] the node with the status set to `:added`
|
143
222
|
def self.handle_addition(new)
|
144
223
|
with_status = new.dup
|
145
224
|
with_status.instance_variable_set(:@status, :added)
|
146
225
|
with_status
|
147
226
|
end
|
148
227
|
|
228
|
+
# Handles the removal of an old node.
|
229
|
+
#
|
230
|
+
# @param old [RevTree] the old node
|
231
|
+
# @return [RevTree] the node with the status set to `:removed`
|
149
232
|
def self.handle_removal(old)
|
150
233
|
with_status = old.dup
|
151
234
|
with_status.instance_variable_set(:@status, :removed)
|
152
235
|
with_status
|
153
236
|
end
|
154
237
|
|
238
|
+
# Handles the modification of a node.
|
239
|
+
#
|
240
|
+
# @param old [RevTree] the old node
|
241
|
+
# @param new [RevTree] the new node
|
242
|
+
# @return [RevTree] the node with the status set to `:modified`
|
155
243
|
def self.handle_modification(old, new)
|
156
244
|
if old.type == :folder && new.type == :folder
|
157
245
|
compare_folders(old, new, :modified)
|
@@ -162,6 +250,12 @@ class RevTree
|
|
162
250
|
end
|
163
251
|
end
|
164
252
|
|
253
|
+
# Compares two folder nodes and returns a merged node with status updates.
|
254
|
+
#
|
255
|
+
# @param old [RevTree] the old folder node
|
256
|
+
# @param new [RevTree] the new folder node
|
257
|
+
# @param status [Symbol] the status to apply (:modified or :unmodified)
|
258
|
+
# @return [RevTree] the resulting folder node with status updates
|
165
259
|
def self.compare_folders(old, new, status)
|
166
260
|
combined_children = merge_children(old.children, new.children)
|
167
261
|
with_status = new.dup
|
@@ -174,6 +268,11 @@ class RevTree
|
|
174
268
|
with_status
|
175
269
|
end
|
176
270
|
|
271
|
+
# Handles the unmodified status of a node.
|
272
|
+
#
|
273
|
+
# @param old [RevTree] the old node
|
274
|
+
# @param new [RevTree] the new node
|
275
|
+
# @return [RevTree] the node with the status set to `:unmodified`
|
177
276
|
def self.handle_unmodified(old, new)
|
178
277
|
if old.type == :folder && new.type == :folder
|
179
278
|
compare_folders(old, new, :unmodified)
|
@@ -184,6 +283,11 @@ class RevTree
|
|
184
283
|
end
|
185
284
|
end
|
186
285
|
|
286
|
+
# Merges the children of two nodes.
|
287
|
+
#
|
288
|
+
# @param old_children [Array<RevTree>] the children of the old node
|
289
|
+
# @param new_children [Array<RevTree>] the children of the new node
|
290
|
+
# @return [Array<Array<RevTree, RevTree>>] an array of paired old and new children
|
187
291
|
def self.merge_children(old_children, new_children)
|
188
292
|
all_names = (old_children.map(&:name) + new_children.map(&:name)).uniq
|
189
293
|
all_names.map do |name|
|
@@ -193,6 +297,15 @@ class RevTree
|
|
193
297
|
end
|
194
298
|
end
|
195
299
|
|
300
|
+
# Traverses the tree and executes a block for each file matching the provided status whitelist.
|
301
|
+
#
|
302
|
+
# @param node [RevTree] the current node being traversed
|
303
|
+
# @param status_whitelist [Array<Symbol>] the list of statuses to match
|
304
|
+
# @param current_path [Pathname] the current path
|
305
|
+
# @yield [node, full_path] the block to be executed for each matching file
|
306
|
+
# @yieldparam node [RevTree] the current node being traversed
|
307
|
+
# @yieldparam full_path [String] the full path of the current node
|
308
|
+
# @return [void]
|
196
309
|
def self.traverse_tree(node, status_whitelist, current_path, &block)
|
197
310
|
if node.type == :file && status_whitelist.include?(node.status)
|
198
311
|
block.call(node, File.expand_path(current_path.to_s))
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: revtree
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Julian Kahlert
|
@@ -10,6 +10,26 @@ bindir: bin
|
|
10
10
|
cert_chain: []
|
11
11
|
date: 2024-09-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: yard
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.9'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.9.37
|
23
|
+
type: :development
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "~>"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.9'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 0.9.37
|
13
33
|
- !ruby/object:Gem::Dependency
|
14
34
|
name: rspec
|
15
35
|
requirement: !ruby/object:Gem::Requirement
|