Snippet: ls_r (recursively list and process directories)
Here's a little Ruby function I came up with today. I thought it might be useful to other people, so here it is.
# To the extent possible under law, the author (John Croisant) has
# waived all copyright and related or neighboring rights to this work.
# See: https://creativecommons.org/publicdomain/zero/1.0/
# Return a hash of directory contents, recursively.
#
# Given a directory structure like this:
#
# a/
# |-- b.txt
# `-- c/
# |-- d.txt
# `-- e/
#
# `ls_r("a")` will produce:
#
# { <Pathname:a> => {
# <Pathname:a/b.txt> => nil,
# <Pathname:a/c> => {
# <Pathname:a/c/d.txt> => nil,
# <Pathname:a/c/e> => {}
# }
# }
# }
#
# If a block is given, each Pathname will be passed to the block
# and the result used in the hash instead of the Pathname. This
# way, you can process the results as they are built, so you get a
# hash of things besides Pathnames.
#
# If the block returns nil, that entry and its children are discarded
# from the final result.
#
# Symlinks are not followed.
#
def ls_r( path, &block )
require 'pathname'
path = Pathname.new(path) unless path.is_a? Pathname
new_path = path
if block_given?
new_path = yield path
return {} if new_path.nil?
end
children = nil
if path.directory?
# Call ls_r recursively on each child. Makes an array of hashes.
children = path.children.map { |child| ls_r( child, &block ) }
# Combine the array of hashes into a single hash.
# But ignore any children with a nil key.
children = children.reduce({}){ |result, child|
result.merge!( child.reject{ |k,v| k.nil? } )
}
end
{ new_path => children }
end