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
Understanding Method_Added for Class Methods
How to Replace the Last Occurrence of a Substring in Ruby
How to Instruct Capistrano 3 to Load My Shell Environment Variables Set at Remote Host
Get Title, Content via Link in Rails
How to Convert a Ruby String Range to a Range Object
Counting Days Excluding Weekends
Gem Not Found in Ruby Cron Job in Rvm Env
How Does Ruby Serialization (Marshaling) Work
Rails 4 User Roles and Permissions
Missing a Template for This Request Format and Variant
Rails: Switch Connection on Each Request But Keep a Connection Pool
Using Soap and Other Standard Libraries in Ruby 1.9.2
Clarification on the Ruby << Operator