metric_fu-Saikuro 1.1.1.0 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,27 @@
1
+ class ParseStateFormater < BaseFormater
2
+
3
+ def start(new_out=nil)
4
+ reset_data
5
+ @out = new_out if new_out
6
+ end
7
+
8
+ def end
9
+ end
10
+
11
+ def start_class_compute_state(type_name,name,complexity,lines)
12
+ @current = name
13
+ @out.puts "-- START #{name} --"
14
+ @out.puts "Type:#{type_name} Name:#{name} Complexity:#{complexity} Lines:#{lines}"
15
+ end
16
+
17
+ def end_class_compute_state(name)
18
+ @out.puts "-- END #{name} --"
19
+ end
20
+
21
+ def def_compute_state(name,complexity,lines)
22
+ return if @filter.ignore?(complexity)
23
+ warn_error?(complexity, name)
24
+ @out.puts "Type:Def Name:#{name} Complexity:#{complexity} Lines:#{lines}"
25
+ end
26
+
27
+ end
@@ -0,0 +1,13 @@
1
+ class ParseSymbol < ParseState
2
+ def initialize(lexer, parent = nil)
3
+ super
4
+ STDOUT.puts "STARTING SYMBOL" if $VERBOSE
5
+ end
6
+
7
+ def parse_token(token)
8
+ STDOUT.puts "Symbol's token is #{token.class}" if $VERBOSE
9
+ # Consume the next token and stop
10
+ @run = false
11
+ nil
12
+ end
13
+ end
@@ -0,0 +1,87 @@
1
+ module ResultIndexGenerator
2
+ def summarize_errors_and_warnings(enw, header)
3
+ return "" if enw.empty?
4
+ f = StringIO.new
5
+ erval = Hash.new { |h,k| h[k] = Array.new }
6
+ wval = Hash.new { |h,k| h[k] = Array.new }
7
+
8
+ enw.each do |fname, warnings, errors|
9
+ errors.each do |c,m,v|
10
+ erval[v] << [fname, c, m]
11
+ end
12
+ warnings.each do |c,m,v|
13
+ wval[v] << [fname, c, m]
14
+ end
15
+ end
16
+
17
+ f.puts "<h2 class=\"class_name\">Errors and Warnings</h2>"
18
+ f.puts "<table width=\"100%\" border=\"1\">"
19
+ f.puts header
20
+
21
+ f.puts print_summary_table_rows(erval, "error")
22
+ f.puts print_summary_table_rows(wval, "warning")
23
+ f.puts "</table>"
24
+
25
+ f.string
26
+ end
27
+
28
+ def print_summary_table_rows(ewvals, klass_type)
29
+ f = StringIO.new
30
+ ewvals.sort { |a,b| b <=> a}.each do |v, vals|
31
+ vals.sort.each do |fname, c, m|
32
+ f.puts "<tr><td><a href=\"./#{fname}\">#{c}</a></td><td>#{m}</td>"
33
+ f.puts "<td class=\"#{klass_type}\">#{v}</td></tr>"
34
+ end
35
+ end
36
+ f.string
37
+ end
38
+
39
+ def list_analyzed_files(files)
40
+ f = StringIO.new
41
+ f.puts "<h2 class=\"class_name\">Analyzed Files</h2>"
42
+ f.puts "<ul>"
43
+ files.each do |fname, warnings, errors|
44
+ readname = fname.split("_")[0...-1].join("_")
45
+ f.puts "<li>"
46
+ f.puts "<p class=\"file_name\"><a href=\"./#{fname}\">#{readname}</a>"
47
+ f.puts "</li>"
48
+ end
49
+ f.puts "</ul>"
50
+ f.string
51
+ end
52
+
53
+ def write_index(files, filename, title, header)
54
+ return if files.empty?
55
+
56
+ File.open(filename,"w") do |f|
57
+ f.puts "<html><head><title>#{title}</title></head>"
58
+ f.puts "#{HTMLStyleSheet.style_sheet}\n<body>"
59
+ f.puts "<h1>#{title}</h1>"
60
+
61
+ enw = files.find_all { |fn,w,e| (!w.empty? || !e.empty?) }
62
+
63
+ f.puts summarize_errors_and_warnings(enw, header)
64
+
65
+ f.puts "<hr/>"
66
+ f.puts list_analyzed_files(files)
67
+ f.puts "</body></html>"
68
+ end
69
+ end
70
+
71
+ def write_cyclo_index(files, output_dir)
72
+ header = "<tr><th>Class</th><th>Method</th><th>Complexity</th></tr>"
73
+ write_index(files,
74
+ "#{output_dir}/index_cyclo.html",
75
+ "Index for cyclomatic complexity",
76
+ header)
77
+ end
78
+
79
+ def write_token_index(files, output_dir)
80
+ header = "<tr><th>File</th><th>Line #</th><th>Tokens</th></tr>"
81
+ write_index(files,
82
+ "#{output_dir}/index_token.html",
83
+ "Index for tokens per line",
84
+ header)
85
+ end
86
+
87
+ end
@@ -0,0 +1,166 @@
1
+ class SaikuroCMDLineRunner
2
+ require 'stringio'
3
+ require 'getoptlong'
4
+ require 'fileutils'
5
+ require 'find'
6
+
7
+ include ResultIndexGenerator
8
+
9
+ attr_accessor :formater, :output_dir, :comp_state, :comp_token,
10
+ :state_formater, :token_count_formater
11
+ def initialize
12
+ @opt = GetoptLong.new(
13
+ ["-o","--output_directory", GetoptLong::REQUIRED_ARGUMENT],
14
+ ["-h","--help", GetoptLong::NO_ARGUMENT],
15
+ ["-f","--formater", GetoptLong::REQUIRED_ARGUMENT],
16
+ ["-c","--cyclo", GetoptLong::NO_ARGUMENT],
17
+ ["-t","--token", GetoptLong::NO_ARGUMENT],
18
+ ["-y","--filter_cyclo", GetoptLong::REQUIRED_ARGUMENT],
19
+ ["-k","--filter_token", GetoptLong::REQUIRED_ARGUMENT],
20
+ ["-w","--warn_cyclo", GetoptLong::REQUIRED_ARGUMENT],
21
+ ["-s","--warn_token", GetoptLong::REQUIRED_ARGUMENT],
22
+ ["-e","--error_cyclo", GetoptLong::REQUIRED_ARGUMENT],
23
+ ["-d","--error_token", GetoptLong::REQUIRED_ARGUMENT],
24
+ ["-p","--parse_file", GetoptLong::REQUIRED_ARGUMENT],
25
+ ["-i","--input_directory", GetoptLong::REQUIRED_ARGUMENT],
26
+ ["-v","--verbose", GetoptLong::NO_ARGUMENT]
27
+ )
28
+ self.output_dir = "./"
29
+ self.formater = "html"
30
+ self.comp_state = self.comp_token = false
31
+ end
32
+
33
+ def get_ruby_files(path)
34
+ files = Array.new
35
+ Find.find(path) do |f|
36
+ if !FileTest.directory?(f)
37
+ if f =~ /rb$/
38
+ files<< f
39
+ end
40
+ end
41
+ end
42
+ files
43
+ end
44
+
45
+ def run
46
+ files = Array.new
47
+ state_filter = Filter.new(5)
48
+ token_filter = Filter.new(10, 25, 50)
49
+
50
+ parse_opts(state_filter, token_filter, files)
51
+ set_formatters(state_filter, token_filter)
52
+
53
+ idx_states, idx_tokens = analyze(files)
54
+ write_results(idx_states, idx_tokens)
55
+ end
56
+
57
+ def analyze(files)
58
+ Saikuro.analyze(files,
59
+ state_formater,
60
+ token_count_formater,
61
+ output_dir)
62
+ end
63
+
64
+ def write_results(idx_states, idx_tokens)
65
+ write_cyclo_index(idx_states, output_dir)
66
+ write_token_index(idx_tokens, output_dir)
67
+ end
68
+
69
+ def parse_opts(state_filter, token_filter, files)
70
+ @opt.each do |arg,val|
71
+ case arg
72
+ when "-o" then self.output_dir = val
73
+ when "-h" then usage('help')
74
+ when "-f" then self.formater = val
75
+ when "-c" then self.comp_state = true
76
+ when "-t" then self.comp_token = true
77
+ when "-k" then token_filter.limit = val.to_i
78
+ when "-s" then token_filter.warn = val.to_i
79
+ when "-d" then token_filter.error = val.to_i
80
+ when "-y" then state_filter.limit = val.to_i
81
+ when "-w" then state_filter.warn = val.to_i
82
+ when "-e" then state_filter.error = val.to_i
83
+ when "-p" then files<< val
84
+ when "-i" then files.concat(get_ruby_files(val))
85
+ when "-v"
86
+ STDOUT.puts "Verbose mode on"
87
+ $VERBOSE = true
88
+ end
89
+ end
90
+ usage('no complexity token or state set') if no_complexity_token_or_state?
91
+ rescue => err
92
+ usage([err.class,err.message,err.backtrace[0..15]].join(', '))
93
+ end
94
+
95
+ def usage(message)
96
+ usage = <<-USAGE
97
+ == Usage
98
+
99
+ saikuro [ -h ] [-o output_directory] [-f type] [ -c, -t ]
100
+ [ -y, -w, -e, -k, -s, -d - number ] ( -p file | -i directory )
101
+
102
+ == Help
103
+
104
+ -o, --output_directory (directory) : A directory to ouput the results in.
105
+ The current directory is used if this option is not passed.
106
+
107
+ -h, --help : This help message.
108
+
109
+ -f, --formater (html | text) : The format to output the results in.
110
+ The default is html
111
+
112
+ -c, --cyclo : Compute the cyclomatic complexity of the input.
113
+
114
+ -t, --token : Count the number of tokens per line of the input.
115
+
116
+ -y, --filter_cyclo (number) : Filter the output to only include methods
117
+ whose cyclomatic complexity are greater than the passed number.
118
+
119
+ -w, --warn_cyclo (number) : Highlight with a warning methods whose
120
+ cyclomatic complexity are greather than or equal to the passed number.
121
+
122
+
123
+ -e, --error_cyclo (number) : Highligh with an error methods whose
124
+ cyclomatic complexity are greather than or equal to the passed number.
125
+
126
+
127
+ -k, --filter_token (number) : Filter the output to only include lines
128
+ whose token count are greater than the passed number.
129
+
130
+
131
+ -s, --warn_token (number) : Highlight with a warning lines whose
132
+ token count are greater than or equal to the passed number.
133
+
134
+
135
+ -d, --error_token (number) : Highlight with an error lines whose
136
+ token count are greater than or equal to the passed number.
137
+
138
+
139
+ -p, --parse_file (file) : A file to use as input.
140
+
141
+ -i, --input_directory (directory) : All ruby files found recursively
142
+ inside the directory are passed as input.
143
+ USAGE
144
+ STDOUT.puts usage
145
+ STDOUT.puts
146
+ STDOUT.puts message
147
+ end
148
+
149
+ def no_complexity_token_or_state?
150
+ !comp_state && !comp_token
151
+ end
152
+
153
+ def set_formatters(state_filter, token_filter)
154
+ if formater =~ /html/i
155
+ self.state_formater = StateHTMLComplexityFormater.new(STDOUT,state_filter)
156
+ self.token_count_formater = HTMLTokenCounterFormater.new(STDOUT,token_filter)
157
+ else
158
+ self.state_formater = ParseStateFormater.new(STDOUT,state_filter)
159
+ self.token_count_formater = TokenCounterFormater.new(STDOUT,token_filter)
160
+ end
161
+
162
+ self.state_formater = nil if !comp_state
163
+ self.token_count_formater = nil if !comp_token
164
+ end
165
+
166
+ end
@@ -0,0 +1,38 @@
1
+ class StateHTMLComplexityFormater < ParseStateFormater
2
+ include HTMLStyleSheet
3
+
4
+ def start(new_out=nil)
5
+ reset_data
6
+ @out = new_out if new_out
7
+ @out.puts "<html><head><title>Cyclometric Complexity</title></head>"
8
+ @out.puts style_sheet
9
+ @out.puts "<body>"
10
+ end
11
+
12
+ def end
13
+ @out.puts "</body>"
14
+ @out.puts "</html>"
15
+ end
16
+
17
+ def start_class_compute_state(type_name,name,complexity,lines)
18
+ @current = name
19
+ @out.puts "<div class=\"class_complexity\">"
20
+ @out.puts "<h2 class=\"class_name\">#{type_name} : #{name}</h2>"
21
+ @out.puts "<div class=\"class_total_complexity\">Total Complexity: #{complexity}</div>"
22
+ @out.puts "<div class=\"class_total_lines\">Total Lines: #{lines}</div>"
23
+ @out.puts "<table width=\"100%\" border=\"1\">"
24
+ @out.puts "<tr><th>Method</th><th>Complexity</th><th># Lines</th></tr>"
25
+ end
26
+
27
+ def end_class_compute_state(name)
28
+ @out.puts "</table>"
29
+ @out.puts "</div>"
30
+ end
31
+
32
+ def def_compute_state(name, complexity, lines)
33
+ return if @filter.ignore?(complexity)
34
+ klass = warn_error?(complexity, name)
35
+ @out.puts "<tr><td>#{name}</td><td#{klass}>#{complexity}</td><td>#{lines}</td></tr>"
36
+ end
37
+
38
+ end
@@ -0,0 +1,51 @@
1
+ # Counts the number of tokens in each line.
2
+ class TokenCounter
3
+ include RubyToken
4
+
5
+ attr_reader :current_file
6
+
7
+ def initialize
8
+ @files = Hash.new
9
+ @tokens_per_line = Hash.new(0)
10
+ @current_file = ""
11
+ end
12
+
13
+ # Mark file to associate with the token count.
14
+ def set_current_file(file)
15
+ @current_file = file
16
+ @tokens_per_line = Hash.new(0)
17
+ @files[@current_file] = @tokens_per_line
18
+ end
19
+
20
+ # Iterate through all tracked files, passing the
21
+ # the provided formater the token counts.
22
+ def list_tokens_per_line(formater)
23
+ formater.start_count(@files.size)
24
+ @files.each do |fname, tok_per_line|
25
+ formater.start_file(fname)
26
+ tok_per_line.sort.each do |line,num|
27
+ formater.line_token_count(line,num)
28
+ end
29
+ formater.end_file
30
+ end
31
+ end
32
+
33
+ # Count the token for the passed line.
34
+ def count_token(line_no,token)
35
+ case token
36
+ when TkSPACE, TkNL, TkRD_COMMENT
37
+ # Do not count these as tokens
38
+ when TkCOMMENT
39
+ # Ignore this only for comments in a statement?
40
+ # Ignore TkCOLON,TkCOLON2 and operators? like "." etc..
41
+ when TkRBRACK, TkRPAREN, TkRBRACE
42
+ # Ignore the closing of an array/index/hash/paren
43
+ # The opening is counted, but no more.
44
+ # Thus [], () {} is counted as 1 token not 2.
45
+ else
46
+ # may want to filter out comments...
47
+ @tokens_per_line[line_no] += 1
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,35 @@
1
+ class TokenCounterFormater < BaseFormater
2
+
3
+ def start(new_out=nil)
4
+ reset_data
5
+ @out = new_out if new_out
6
+ @out.puts "Token Count"
7
+ end
8
+
9
+ def start_count(number_of_files)
10
+ @out.puts "Counting tokens for #{number_of_files} files."
11
+ end
12
+
13
+ def start_file(file_name)
14
+ @current = file_name
15
+ @out.puts "File:#{file_name}"
16
+ end
17
+
18
+ def line_token_count(line_number,number_of_tokens)
19
+ return if @filter.ignore?(number_of_tokens)
20
+ warn_error?(number_of_tokens, line_number)
21
+ @out.puts "Line:#{line_number} ; Tokens : #{number_of_tokens}"
22
+ end
23
+
24
+ def end_file
25
+ @out.puts ""
26
+ end
27
+
28
+ def end_count
29
+ end
30
+
31
+ def end
32
+ end
33
+
34
+ end
35
+
@@ -1,37 +0,0 @@
1
- # This is a patch to RDoc so that when saikuro is installed as a
2
- # RubyGem usage will read the proper file.
3
-
4
- module RDoc
5
-
6
- def RDoc.main_program_file=(file)
7
- @@main_program_file = file
8
- end
9
-
10
- # Display usage
11
- def RDoc.usage_no_exit(*args)
12
- @@main_program_file ||= caller[-1].sub(/:\d+$/, '')
13
- comment = File.open(@@main_program_file) do |file|
14
- find_comment(file)
15
- end
16
-
17
- comment = comment.gsub(/^\s*#/, '')
18
-
19
- markup = SM::SimpleMarkup.new
20
- flow_convertor = SM::ToFlow.new
21
-
22
- flow = markup.convert(comment, flow_convertor)
23
-
24
- format = "plain"
25
-
26
- unless args.empty?
27
- flow = extract_sections(flow, args)
28
- end
29
-
30
- options = RI::Options.instance
31
- if args = ENV["RI"]
32
- options.parse(args.split)
33
- end
34
- formatter = options.formatter.new(options, "")
35
- formatter.display_flow(flow)
36
- end
37
- end