user3871995
user3871995

Reputation: 1233

Recursively building json tree using ruby

I want to recursively build a json tree of directories and permissions beginning with one input directory. For example, having input "/initial_directory", I expect the following output:

{
  "initial_directory": {
   "permissions": "755",
   "children": {
     "file_a": {
       "permissions": "755"
     },
     "directory_two": {
       "permissions": "600",
       "children": {
         "file_b": {
           "permissions": "777"
         }
       } 
     }
    }
  }
}

This is my code so far:

def build_json(directory)
  json = {}
  Dir.foreach(directory) do |file|
    perm, file = (`stat -f '%A %N'  #{file} `).split(' ')
    json[file] = perm
    json
  end
end

I want to update this to recursively get all the child directories. My problem is that I need to know the json path upfront for this. Is there a better approach to achieve this?

Upvotes: 0

Views: 128

Answers (1)

Schwern
Schwern

Reputation: 165396

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])

Upvotes: 2

Related Questions