F1e3ab214a976a39cfd713bc93deb10f

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!

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 !

F1e3ab214a976a39cfd713bc93deb10f

Tj Holowaychuk

September 26, 2008, September 26, 2008 03:05, permalink

No rating. Login to rate!

Yikes, not sure what is up with the intense tab-age

Avatar

Spreelanka

September 26, 2008, September 26, 2008 18:49, permalink

No rating. Login to rate!
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
F1e3ab214a976a39cfd713bc93deb10f

Tj Holowaychuk

September 26, 2008, September 26, 2008 22:58, permalink

No rating. Login to rate!

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

Your refactoring





Format Copy from initial code

or Cancel