Ruby Create Recursive Directory Tree

Ruby Create Recursive Directory Tree

You probably want something like this (untested):

def directory_hash(path, name=nil)
data = {:data => (name || path)}
data[:children] = children = []
Dir.foreach(path) do |entry|
next if (entry == '..' || entry == '.')
full_path = File.join(path, entry)
if File.directory?(full_path)
children << directory_hash(full_path, entry)
else
children << entry
end
end
return data
end

Recursively walk down the tree, building up a hash. Turn it into json with your favourite serialisation library.

How to create directories recursively in ruby?

Use mkdir_p:

FileUtils.mkdir_p '/a/b/c'

The _p is a unix holdover for parent/path you can also use the alias mkpath if that makes more sense for you.

FileUtils.mkpath '/a/b/c'

In Ruby 1.9 FileUtils was removed from the core, so you'll have to require 'fileutils'.

Elegant way to create recursive directories

FileUtils.mkdir_p is a good choice, and it is part of the standard library.

You can use Array#product to produce the tree.

levels = ['pro', 'non_pro'].product(
['bio', 'mental', 'physical'],
['a', 'b', 'c']
)

Then you can make your block work with any number of levels with Array#join.

base = ["pennsylvania","bucks","medicine"]
levels.each do |level|
FileUtils.mkdir_p( (base + level).join("/") )
end

Note that while this is very elegant, it is not the most efficient way to do it. The problem is every call to FileUtils.mkdir_p will try to make each subdirectory, and if it gets an error, check whether it already exists. For a small number of directories on a fast filesystem this is fine. But for a large tree or slow filesystem, such as a network filesystem, this can hurt performance.

For more efficient filesystem usage you'd do something like this recursion.

levels = [
['pennsylvania'],
['bucks'],
['medicine'],
['pro', 'non_pro'],
['bio', 'mental', 'physical'],
['a', 'b', 'c']
]

def make_subdirs(levels, base = [])
return if levels.empty?

levels[0].each { |dir|
new_base = [*base, dir]
mkdir_ignore_if_exists(new_base)
make_subdirs(levels[1..-1], new_base)
}
end

private def mkdir_ignore_if_exists(dirs)
Dir.mkdir(dirs.join("/"))
rescue Errno::EEXIST
end

make_subdirs(levels)

Recursively building json tree using ruby

Do build something recursively, you need to recurse. That involves calling a function inside itself and having a termination condition.

While you might turn it into JSON later, you're not building JSON. You're building a Hash of a directory tree. dir_tree.

Finally, we'll use Pathname instead of File and Dir. Pathname does everything File and Dir do, but represents them with objects. This makes them much easier to work with. Critically, a Pathname object knows its absolute path. This will be important when we start recursing into subdirectories.

require 'pathname'

# Start with a directory and an empty tree.
def dir_tree(directory)
tree={}

# Pathname#children skips . and .., else we'd go in circles.
Pathname.new(directory).children.each do |path|
# Get the permissions with Ruby, not a program. Faster, simpler.
info = { permissions: path.stat.mode }

# If it's a directory, recurse. Instead of passing in the whole
# tree, we start fresh. Assign the result to be its children.
# Because we're using Pathnames, `path` knows its absolute path
# and everything still works.
if path.directory?
children = dir_tree(path)
info[:children] = children unless children.empty?
end

# Stick just the filename into the tree. Stringify it else we get
# a Pathname object.
tree[path.basename.to_s] = info
end

# And here is the termination of the recursion once a directory with
# no children is reached.
return tree
end

p dir_tree(ARGV[0])

One-liner to recursively list directories in Ruby?

Dir.glob("**/*/") # for directories
Dir.glob("**/*") # for all files

Instead of Dir.glob(foo) you can also write Dir[foo] (however Dir.glob can also take a block, in which case it will yield each path instead of creating an array).

Ruby Glob Docs

What's a good way to require a whole directory tree in Rails?

autoload_paths and friends work only if the given files, or files in the given directories, are named according to the rails naming conventiion.

e.g. if a file some_class.rb is given to autoload_paths, it expcects the file to declare a class SomeClass, and sets up some magic to make any reference to SomeClass load that file on-the-fly.

So if you want to have it only load each of your files as they are needed, then you will have to name your files accordingly, and have one file per class.

If you are happy to load all of the files in a directory tree when rails starts, you can do this:

Dir.glob("/path/to/my/lib/**/*.rb").each { |f| require f }

The above will read every .rb file in /path/to/my/lib, and any directories under it.



Related Topics



Leave a reply



Submit