Skip to content

Commit 4996be3

Browse files
authored
Better relative path simplification. (#51)
1 parent 55a1254 commit 4996be3

File tree

3 files changed

+81
-9
lines changed

3 files changed

+81
-9
lines changed

lib/utopia/path.rb

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -215,19 +215,34 @@ def -(other)
215215
end
216216

217217
def simplify
218-
result = absolute? ? [""] : []
218+
components = []
219219

220-
@components.each do |bit|
221-
if bit == ".."
222-
result.pop
223-
elsif bit != "." && bit != ""
224-
result << bit
225-
end
220+
index = 0
221+
222+
if @components[0] == ""
223+
components << ""
224+
index += 1
226225
end
227226

228-
result << "" if directory?
227+
while index < @components.size
228+
bit = @components[index]
229+
if bit == "."
230+
# No-op (ignore current directory)
231+
elsif bit == "" && index != @components.size - 1
232+
# No-op (ignore multiple slashes)
233+
elsif bit == ".." && components.last && components.last != ".."
234+
if components.last != ""
235+
# We can go up one level:
236+
components.pop
237+
end
238+
else
239+
components << bit
240+
end
241+
242+
index += 1
243+
end
229244

230-
return self.class.new(result)
245+
return self.class.new(components)
231246
end
232247

233248
# Returns the first path component.

releases.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
## Unreleased
44

55
- Add agent context.
6+
- Better simplification of relative paths, e.g. `../../foo` is not modified to `foo`.
67

78
## v2.30.1
89

test/utopia/path.rb

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@
195195
expect(directory.to_directory).to be == directory
196196
end
197197
end
198+
198199
it "should start with the given path" do
199200
path = Utopia::Path["/a/b/c/d/e"]
200201

@@ -263,4 +264,59 @@
263264

264265
expect((output + short).simplify).to be == input
265266
end
267+
268+
with "#simplify" do
269+
it "doesn't remove leading .. from relative paths" do
270+
path = Utopia::Path["../foo/bar"]
271+
simplified = path.simplify
272+
273+
expect(simplified.components).to be == ["..", "foo", "bar"]
274+
end
275+
276+
it "removes redundant .. from absolute paths" do
277+
path = Utopia::Path["/foo/../../bar"]
278+
simplified = path.simplify
279+
280+
expect(simplified.components).to be == ["", "bar"]
281+
end
282+
283+
it "preserves excess .. for purely relative paths" do
284+
# ../../foo cannot be reduced without a base; keep leading .. components
285+
path = Utopia::Path["../../foo"]
286+
simplified = path.simplify
287+
288+
expect(simplified.components).to be == ["..", "..", "foo"]
289+
end
290+
291+
it "reduces relative paths only as far as components allow" do
292+
# foo/../../bar -> ../bar (pop foo for first .., then keep remaining ..)
293+
path = Utopia::Path["foo/../../bar"]
294+
simplified = path.simplify
295+
296+
expect(simplified.components).to be == ["..", "bar"]
297+
end
298+
299+
it "does not traverse above root for absolute paths" do
300+
# /a/b/../../../c -> /c (ignore .. beyond root)
301+
path = Utopia::Path["/a/b/../../../c"]
302+
simplified = path.simplify
303+
304+
expect(simplified.components).to be == ["", "c"]
305+
end
306+
307+
it "ignores leading .. at absolute root" do
308+
# /../../c -> /c
309+
path = Utopia::Path["/../../c"]
310+
simplified = path.simplify
311+
312+
expect(simplified.components).to be == ["", "c"]
313+
end
314+
315+
it "preserves trailing slash for directories after simplify" do
316+
path = Utopia::Path["/a/./b/../c/"]
317+
simplified = path.simplify
318+
319+
expect(simplified.components).to be == ["", "a", "c", ""]
320+
end
321+
end
266322
end

0 commit comments

Comments
 (0)