diff --git a/lib/net/sftp/operations/dir.rb b/lib/net/sftp/operations/dir.rb index 614c781..9c8c4cc 100644 --- a/lib/net/sftp/operations/dir.rb +++ b/lib/net/sftp/operations/dir.rb @@ -64,12 +64,7 @@ def glob(path, pattern, flags=0) while queue.any? entry = queue.shift - if entry.directory? && !%w(. ..).include?(::File.basename(entry.name)) - queue += entries("#{path}/#{entry.name}").map do |e| - e.name.replace("#{entry.name}/#{e.name}") - e - end - end + queue = enqueue_subtree_if_dir(queue, entry, path) if ::File.fnmatch(pattern, entry.name, flags) if block_given? @@ -88,6 +83,45 @@ def glob(path, pattern, flags=0) def [](path, pattern) glob(path, pattern, 0) end + + # Finds paths matching recursively all directory entries under +path+ + # against Regexp +regexp+. If a block is given, matches will be yielded + # to the block as they are found; otherwise, they will be returned in + # an array when the method finishes. + def match(path, regexp) + path = path.chop if path[-1,1] == "/" + + results = [] unless block_given? + queue = entries(path).reject { |e| e.name == "." || e.name == ".." } + while queue.any? + entry = queue.shift + + queue = enqueue_subtree_if_dir(queue, entry, path) + + if regexp.match(entry.name) + if block_given? + yield entry + else + results << entry + end + end + end + + return results unless block_given? + end + + private + + # Returns given queue with found subtree entries (Only one level deeper). + def enqueue_subtree_if_dir(queue, entry, path) + if entry.directory? && !%w(. ..).include?(::File.basename(entry.name)) + queue += entries("#{path}/#{entry.name}").map do |e| + e.name.replace("#{entry.name}/#{e.name}") + e + end + end + queue + end end end; end; end \ No newline at end of file diff --git a/test/test_dir.rb b/test/test_dir.rb index 1612bae..03a39b8 100644 --- a/test/test_dir.rb +++ b/test/test_dir.rb @@ -25,21 +25,53 @@ def test_entries_should_return_all_entries_in_a_single_array end def test_glob_should_search_under_path_for_matching_entries - @sftp.expects(:opendir!).with("/path/to/remote").returns("handle") - @sftp.expects(:opendir!).with("/path/to/remote/e3").returns("handle-e3") - @sftp.expects(:opendir!).with("/path/to/remote/e5").returns("handle-e5") + stub_folder_structure @sftp.expects(:readdir!).with("handle").returns([n(".", true), n("..", true), n("e1"), n("e2"), n("e3", true)], [n("e4"), n("e5", true)], nil).times(3) @sftp.expects(:readdir!).with("handle-e3").returns([n(".", true), n("..", true), n("e3e1"), n("e3e2")], nil).times(2) @sftp.expects(:readdir!).with("handle-e5").returns([n(".", true), n("..", true), n("e5e1"), n("e5e2"), n("e5e3")], nil).times(2) - @sftp.expects(:close!).with("handle") - @sftp.expects(:close!).with("handle-e3") - @sftp.expects(:close!).with("handle-e5") - assert_equal %w(e3/e3e2 e5/e5e2), @dir.glob("/path/to/remote", "**/e?e2").map { |e| e.name } + assert_equal %w(e3/e3e2 e5/e5e2), + @dir.glob("/path/to/remote", "**/e?e2").map { |e| e.name } + end + + def test_match_should_search_a_path_matching_regexp + stub_folder_structure + + assert_equal %w(e3/e3e2), + @dir.match("/path/to/remote", /e3e2/).map { |e| e.name } + end + + def test_match_should_search_a_path_matching_a_complex_regexp + stub_folder_structure + + assert_equal %w(e3 e5/e5e1 e5/e5e2 e5/e5e3), + @dir.match("/path/to/remote", /^(e3|e5\/e5.*)$/).map { |e| e.name } end private + def stub_folder_structure + # /path/to/remote + # |- e1 + # |- e2 + # |- e3 + # |- e3e1 + # |- e3e2 + # |- e4 + # |- e5 + # |- e5e1 + # |- e5e2 + @sftp.stubs(:opendir!).with("/path/to/remote").returns("handle") + @sftp.stubs(:opendir!).with("/path/to/remote/e3").returns("handle-e3") + @sftp.stubs(:opendir!).with("/path/to/remote/e5").returns("handle-e5") + @sftp.stubs(:readdir!).with("handle").returns([n(".", true), n("..", true), n("e1"), n("e2"), n("e3", true)], [n("e4"), n("e5", true)], nil) + @sftp.stubs(:readdir!).with("handle-e3").returns([n(".", true), n("..", true), n("e3e1"), n("e3e2")], nil) + @sftp.stubs(:readdir!).with("handle-e5").returns([n(".", true), n("..", true), n("e5e1"), n("e5e2"), n("e5e3")], nil) + @sftp.stubs(:close!).with("handle") + @sftp.stubs(:close!).with("handle-e3") + @sftp.stubs(:close!).with("handle-e5") + end + def n(name, directory=false) Net::SFTP::Protocol::V01::Name.new(name.to_s, "longname for #{name}", Net::SFTP::Protocol::V01::Attributes.new(:permissions => directory ? 040755 : 0100644))