1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234
#!/usr/bin/env ruby # == Synopsis # lc is an open source Ruby program for analysing the contents # of source code, such as line counts and comment ratios. # # == Usage # lc [-hVr] [ dir | file ... ] # # == Examples # Analyse current directory # lc # # Analyse current directory recursively # lc --recursive # # Analyse specific files and a directory recursively # lc -r ./index.php ./cron.php ./sites/all/modules/gui # # == Options # -h, --help Display this help information. # -V, --version Display version of lc. # -r, --recurse Scan directories recursively. # # == Author # TJ Holowaychuk # # == Copyright # Copyright (c) 2008 TJ Holowaychuk. Licensed under the MIT License: # http://www.opensource.org/licenses/mit-license.php require 'optparse' require 'rdoc/usage' $syntax = { 'rb' => { :name => 'Ruby', :blank => /^\s*$/, :comment => /^[\s]*#/, :comment_open => /[\s]*=begin/, :comment_close => /[\s]*=end/, :function => /def[\s]+[\w]+/, :class => /class[\s]+[\w]+/, :todo => /@?todo/, :associations => ['rb', 'erb'] }, 'php' => { :name => 'PHP', :blank => /^\s*$/, :comment => /^[\s]*\/\//, :comment_open => /^[\s]*\/\*/, :comment_close => /[\s]*\*\//, :function => /^[\s]*function[\s]+[\w]+[\s]*\(/, :class => /class[\s]+[\w]+/, :todo => /@?todo/, :associations => ['php', 'inc', 'module', 'install'] }, 'js' => { :name => 'JavaScript', :blank => /^\s*$/, :comment => /^[\s]*\/\//, :comment_open => /^[\s]*\/\*/, :comment_close => /[\s]*\*\//, :function => /^[\s]*(?:function[\s][\w]+|(var)?[\s]*[\w\.]+[\s]+=[\s]+function)[\s]*\(/, :todo => /@?todo/, :associations => ['js'] }, 'css' => { :name => 'CSS', :blank => /^\s*$/, :comment => /^[\s]*\/\//, :comment_open => /^[\s]*\/\*/, :comment_close => /[\s]*\*\//, :todo => /@?todo/, :associations => ['css'] } } class LC VERSION = '0.0.1' attr_reader :files, :reports attr_accessor :options, :arguments # Initialize def initialize() self.initialize_options self.initialize_reports end # Initialize option defaults def initialize_options @options = {} @options[:recursive] = false end # Initialize reports, create defaults def initialize_reports @reports = {} @reports[:comment_ratio] = 0 @reports[:totals] = { 'files' => 0, 'lines' => 0, 'lines blank' => 0, 'lines comments' => 0, 'lines todo' => 0, 'declared functions' => 0, 'declared classes' => 0, } $syntax.each do |lang| lang[1][:associations].each do |association| @reports[:totals]['files ' << association] = 0 end end end # Start analysis. def run # Parse options self.parse_options # Default files to cwd @files = @arguments.empty? ? ['.'] : @arguments # Parse files and directories file_pattern = $syntax.collect{|lang| lang[1][:associations].join(',')}.join(',') @files.each do |file| if File.directory?(file) files = Dir[(@options[:recursive] ? '**/' : '') << file << '/*.{' + file_pattern + '}'] files.each do |file| self.parse_script(file) end elsif File.file?(file) self.parse_script(file) end end # Report self.prep_report self.output_report exit end # Parse options def parse_options opts = OptionParser.new opts.on('-h', '--help') { RDoc.usage(0) } opts.on('-V', '--version') { self.output_version; exit 0 } opts.on('-r', '--recursive') { @options[:recursive] = true } begin opts.parse!(@arguments) rescue => e puts e exit 1 end end # Get extension of filepath. def get_extension(filepath) File.extname(filepath).reverse.chop.reverse end # Get syntax based on a filenames extension. def get_syntax(filepath) extension = get_extension(filepath) $syntax.each_pair do |lang, info| return $syntax[lang] if info[:associations].include?(extension) end end # Parse a script and report on findings. def parse_script(filepath) lang = get_syntax(filepath) comment_open = false extension = get_extension(filepath) # Ensure syntax was found if lang.kind_of? NilClass return end File.open(filepath) do |file| @reports[:totals]['files'] += 1 @reports[:totals]['files ' << extension] += 1 file.each_line do |line| @reports[:totals]['lines'] += 1 @reports[:totals]['lines blank'] += 1 if line.match(lang[:blank]) if !line.match(lang[:blank]) @reports[:totals]['lines todo'] += 1 if line.match(lang[:todo]) case when line.match(lang[:comment]); @reports[:totals]['lines comments'] += 1 when line.match(lang[:comment_open]); @reports[:totals]['lines comments'] += 1; comment_open = true when line.match(lang[:comment_close]); @reports[:totals]['lines comments'] += 1; comment_open = false else if comment_open @reports[:totals]['lines comments'] += 1 else case when lang[:function] && line.match(lang[:function]); @reports[:totals]['declared functions'] += 1 when lang[:class] && line.match(lang[:class]); @reports[:totals]['declared classes'] += 1 end end end end end end end # Prepare output of report. def prep_report if @reports[:totals]['lines comments'] > 0 and @reports[:totals]['lines'] > 0 @reports[:comment_ratio] = @reports[:totals]['lines comments'].to_f / @reports[:totals]['lines'].to_f end end # Output report. def output_report @reports[:totals].each_pair do |k, v| puts k + ' ' + v.to_s unless v == 0 end puts 'comment ratio ' << '%.2f' % @reports[:comment_ratio] unless @reports[:comment_ratio] == 0 end # Output version information. def output_version puts "Version #{LC::VERSION}" end end counter = LC.new counter.arguments = ARGV counter.run
Refactorings
No refactoring yet !
Tj Holowaychuk
September 26, 2008, September 26, 2008 03:05, permalink
Yikes, not sure what is up with the intense tab-age
Spreelanka
September 26, 2008, September 26, 2008 18:49, permalink
1 2 3 4 5 6 7 8 9 10 11
for k in data_hash.keys.sort action_to_perform data_hash[k] end #or alternatively keys_in_desired_order=[:k1,:k2,:k3] for k in keys_in_desired_order action_to_perform data_hash[k] end #even better, rails has a rake task that does this, my guess is there's a module to do this
Tj Holowaychuk
September 26, 2008, September 26, 2008 22:58, permalink
I am skipping rails for now just trying to learn what I can without a framework first :)
your first example looks like what I am looking for I will give it a go
Small class to analyze scripts, outputs similar to below:
files js 5
declared classes 146
lines todo 27
lines blank 3171
files 69
lines 28263
files module 3
files inc 36
files install 1
files css 5
files php 19
declared functions 1004
lines comments 6313
comment ratio 0.22
I dont really like the order of which I am outputting but you cannot sort Hashes correct? it looks fine
when piped through sort $ lc -r . | sort -n, but I would hardly call that ideal. Anyways feel free to comment
or refactor any portion, I am eager to learn Ruby and seeing your examples helps alot!