metric_fu-Saikuro 1.1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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