Skip to content

Instantly share code, notes, and snippets.

@cldwalker
Created July 20, 2011 06:47
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cldwalker/1094474 to your computer and use it in GitHub Desktop.
Save cldwalker/1094474 to your computer and use it in GitHub Desktop.
Prints out a tree of classes and their methods for a rails project
#!/usr/bin/env ruby
# == Description
# Prints out a tree of classes and their methods for a rails project. Can be run from the
# root directory of a rails project or with a specified rails directory. By default
# this script only looks in the models, controllers and helpers directories for ruby classes but that can be
# configured.
#
# This was useful back in the day when Rails projects were small and I wanted to a quick overview of
# the project.
##### RDOC file parser #####
#Known to work with rdoc for ruby 1.8.2, 1.8.4 and 1.8.6
require 'rdoc/rdoc.rb'
#to avoid issues in rdoc/parsers/parse_rb.rb
Options.instance.instance_eval "@tab_width = 8"
def index_class(cls,hash)
class_name = cls.full_name
hash[class_name] = cls.method_list.map {|x| x.name } if ! hash[class_name]
#indexes subclasses of a class
cls.each_classmodule { |x| index_class(x,hash) }
end
# Correctly parses methods with explicit classes.
# Todo: Doesn't correctly parse global methods ie methods without a class.
def rdoc_parse_file(file)
class_hash = {}
content = File.open(file, "r") {|f| f.read}
capital_p = RDoc::ParserFactory.parser_for(RDoc::TopLevel.new(file),file,content,Options.instance,RDoc::Stats.new)
capital_p.scan
rclasses = RDoc::TopLevel.all_classes_and_modules
rclasses.each {|rc| index_class(rc,class_hash) }
RDoc::TopLevel::reset
class_hash
end
##### END of RDOC file parser #####
require "yaml"
require 'optparse'
options = {:nosort=>false,:delete_empty=>false }
OptionParser.new do |opt|
opt.banner = "Usage: #{File.basename($0)} [OPTIONS] [Directories under app/]"
opt.on('-s', '--nosort', 'Displays original order of methods for each class') { |options[:nosort]| }
opt.on('-d', '--delete_empty', 'Deletes classes that don\'t have any methods') { |options[:delete_empty]| }
opt.on('-r', '--rails_root==DIR',String, 'Specify rails root directory') { |options[:rails_root]| }
opt.parse!(ARGV)
end
rails_root = Dir.pwd
if options.has_key?(:rails_root)
RAILS_ROOT = options[:rails_root]
elsif File.exists?(File.join(rails_root,'config','environment.rb'))
RAILS_ROOT = rails_root
else
raise "No rails root detected. Add a default rails root."
end
directories = (! ARGV.empty?) ? ARGV : %w{models helpers controllers}
# Returns hash of class to methods pairs
def parse_methods_from_file(filename)
#other parsers could be used here
rdoc_parse_file(filename)
end
def yaml_format(dir_info)
dir_info.to_yaml + "\n"
end
def outline_format(dir_info)
body = ''
indent = "\t"
dir_info.each {|dir,info|
body += dir + "\n"
body += info.to_yaml.gsub!("\n","\n#{indent}").sub!(/^-+.*?\n/,'') + "\n"
}
body
end
dir_info = {}
directories.each { |e|
full_dir = File.join(RAILS_ROOT,'app',e)
unless File.exists?(full_dir)
puts "nonexistant directory '#{full_dir}' skipped"
next
end
files = Dir.glob(full_dir +"/**/*")
dir_info[e] = files.grep(/\.rb$/).map {|f| parse_methods_from_file(f) }.inject({}) {|t,s| t.update(s) }
if options[:delete_empty]
dir_info[e].each { |k,s|
dir_info[e].delete(k) if dir_info[e][k].empty?
}
end
unless options[:nosort]
dir_info[e].each { |k,sub|
dir_info[e][k] = sub.sort
}
end
}
#could also use yaml_format()
puts "\n",outline_format(dir_info)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment