# From rails (http://rubyonrails.com) # # Improved by murphy class CodeStatistics TEST_TYPES = /\btest/i # Create a new Code Statistic. # # Rakefile Example: # # desc 'Report code statistics (LOC) from the application' # task :stats => :copy_files do # require 'rake_helpers/code_statistics' # CodeStatistics.new( # ["Main", "lib"], # ["Tests", "test"], # ["Demos", "demo"] # ).to_s # end def initialize(*pairs) @pairs = pairs @statistics = calculate_statistics @total = if pairs.empty? then nil else calculate_total end end # Print a textual table viewing the stats # # Intended for console output. def print print_header @pairs.each { |name, path| print_line name, @statistics[name] } print_splitter if @total print_line 'Total', @total print_splitter end print_code_test_stats end private DEFAULT_FILE_PATTERN = /\.rb$/ def calculate_statistics @pairs.inject({}) do |stats, (name, path, pattern, is_ruby_code)| pattern ||= DEFAULT_FILE_PATTERN path = File.join path, '*.rb' stats[name] = calculate_directory_statistics path, pattern, is_ruby_code stats end end def calculate_directory_statistics directory, pattern = DEFAULT_FILE_PATTERN, is_ruby_code = true is_ruby_code = true if is_ruby_code.nil? stats = Hash.new 0 Dir[directory].each do |file_name| p "Scanning #{file_name}..." if $VERBOSE next unless file_name =~ pattern lines = codelines = classes = modules = methods = 0 empty_lines = comment_lines = 0 in_comment_block = false File.readlines(file_name).each do |line| lines += 1 if line[/^\s*$/] empty_lines += 1 elsif is_ruby_code case line when /^=end\b/ comment_lines += 1 in_comment_block = false when in_comment_block comment_lines += 1 when /^\s*class\b/ classes += 1 when /^\s*module\b/ modules += 1 when /^\s*def\b/ methods += 1 when /^\s*#/ comment_lines += 1 when /^=begin\b/ in_comment_block = false comment_lines += 1 when /^__END__$/ in_comment_block = true end end end codelines = lines - comment_lines - empty_lines stats[:lines] += lines stats[:comments] += comment_lines stats[:codelines] += codelines stats[:classes] += classes stats[:modules] += modules stats[:methods] += methods stats[:files] += 1 end stats end def calculate_total total = Hash.new 0 @statistics.each_value { |pair| pair.each { |k, v| total[k] += v } } total end def calculate_code code_loc = 0 @statistics.each { |k, v| code_loc += v[:codelines] unless k[TEST_TYPES] } code_loc end def calculate_tests test_loc = 0 @statistics.each { |k, v| test_loc += v[:codelines] if k[TEST_TYPES] } test_loc end def print_header print_splitter puts "| T=Test Name | Files | Lines | LOC | Comments | Classes | Modules | Methods | M/C | LOC/M |" print_splitter end def print_splitter puts "+---------------------------+-------+-------+-------+----------+---------+---------+---------+-----+-------+" end def print_line name, statistics m_over_c = (statistics[:methods] / (statistics[:classes] + statistics[:modules])) rescue m_over_c = 0 loc_over_m = (statistics[:codelines] / statistics[:methods]) - 2 rescue loc_over_m = 0 if name[TEST_TYPES] name = "T #{name}" else name = " #{name}" end line = "| %-25s | %5d | %5d | %5d | %8d | %7d | %7d | %7d | %3d | %5d |" % ( [name, *statistics.values_at(:files, :lines, :codelines, :comments, :classes, :modules, :methods)] + [m_over_c, loc_over_m] ) puts line end def print_code_test_stats code = calculate_code tests = calculate_tests puts " Code LOC = #{code} Test LOC = #{tests} Code:Test Ratio = [1 : #{sprintf("%.2f", tests.to_f / code)}]" puts "" end end # Run a test script. if $0 == __FILE__ $VERBOSE = true CodeStatistics.new( ['This dir', File.dirname(__FILE__)] ).print end