edouard-metric_fu 1.0.3.2 → 1.0.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/lib/base/base_template.rb +134 -0
  2. data/lib/base/configuration.rb +187 -0
  3. data/lib/base/generator.rb +147 -0
  4. data/lib/base/md5_tracker.rb +52 -0
  5. data/lib/base/report.rb +100 -0
  6. data/lib/generators/churn.rb +86 -0
  7. data/lib/generators/flay.rb +29 -0
  8. data/lib/generators/flog.rb +127 -0
  9. data/lib/generators/rcov.rb +77 -0
  10. data/lib/generators/reek.rb +32 -0
  11. data/lib/generators/roodi.rb +24 -0
  12. data/lib/generators/saikuro.rb +211 -0
  13. data/lib/generators/stats.rb +43 -0
  14. data/lib/metric_fu.rb +21 -0
  15. data/lib/templates/awesome/awesome_template.rb +30 -0
  16. data/lib/templates/awesome/churn.html.erb +19 -0
  17. data/lib/templates/awesome/default.css +62 -0
  18. data/lib/templates/awesome/flay.html.erb +22 -0
  19. data/lib/templates/awesome/flog.html.erb +42 -0
  20. data/lib/templates/awesome/index.html.erb +28 -0
  21. data/lib/templates/awesome/rcov.html.erb +32 -0
  22. data/lib/templates/awesome/reek.html.erb +30 -0
  23. data/lib/templates/awesome/roodi.html.erb +17 -0
  24. data/lib/templates/awesome/saikuro.html.erb +71 -0
  25. data/lib/templates/awesome/stats.html.erb +41 -0
  26. data/lib/templates/standard/churn.html.erb +30 -0
  27. data/lib/templates/standard/default.css +64 -0
  28. data/lib/templates/standard/flay.html.erb +33 -0
  29. data/lib/templates/standard/flog.html.erb +52 -0
  30. data/lib/templates/standard/index.html.erb +38 -0
  31. data/lib/templates/standard/rcov.html.erb +42 -0
  32. data/lib/templates/standard/reek.html.erb +41 -0
  33. data/lib/templates/standard/roodi.html.erb +28 -0
  34. data/lib/templates/standard/saikuro.html.erb +83 -0
  35. data/lib/templates/standard/standard_template.rb +26 -0
  36. data/lib/templates/standard/stats.html.erb +54 -0
  37. data/tasks/metric_fu.rake +15 -0
  38. data/tasks/railroad.rake +39 -0
  39. data/vendor/saikuro/saikuro.rb +1214 -0
  40. metadata +40 -1
@@ -0,0 +1,29 @@
1
+ require 'generator'
2
+ module MetricFu
3
+
4
+ class Flay < Generator
5
+
6
+ def emit
7
+ files_to_flay = MetricFu.flay[:dirs_to_flay].map{|dir| Dir[File.join(dir, "**/*.rb")] }
8
+ @output = `flay #{files_to_flay.join(" ")}`
9
+ end
10
+
11
+ def analyze
12
+ @matches = @output.chomp.split("\n\n").map{|m| m.split("\n ") }
13
+ end
14
+
15
+ def to_h
16
+ target = []
17
+ total_score = @matches.shift.first.split('=').last.strip
18
+ @matches.each do |problem|
19
+ reason = problem.shift.strip
20
+ lines_info = problem.map do |full_line|
21
+ name, line = full_line.split(":")
22
+ {:name => name.strip, :line => line.strip}
23
+ end
24
+ target << [:reason => reason, :matches => lines_info]
25
+ end
26
+ {:flay => {:total_score => total_score, :matches => target.flatten}}
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,127 @@
1
+ module MetricFu
2
+
3
+ class Flog < Generator
4
+ attr_reader :pages
5
+
6
+ SCORE_FORMAT = "%0.2f"
7
+ METHOD_LINE_REGEX = /(\d+\.\d+):\s+([A-Za-z:]+#.*)/
8
+ OPERATOR_LINE_REGEX = /\s*(\d+\.\d+):\s(.*)$/
9
+
10
+ def emit
11
+ metric_dir = MetricFu::Flog.metric_directory
12
+ MetricFu.flog[:dirs_to_flog].each do |directory|
13
+ Dir.glob("#{directory}/**/*.rb").each do |filename|
14
+ output_dir = "#{metric_dir}/#{filename.split("/")[0..-2].join("/")}"
15
+ mkdir_p(output_dir, :verbose => false) unless File.directory?(output_dir)
16
+ if MetricFu::MD5Tracker.file_changed?(filename, metric_dir)
17
+ `flog -ad #{filename} > #{metric_dir}/#{filename.split('.')[0]}.txt`
18
+ end
19
+ end
20
+ end
21
+ rescue LoadError
22
+ if RUBY_PLATFORM =~ /java/
23
+ puts 'running in jruby - flog tasks not available'
24
+ else
25
+ puts 'sudo gem install flog # if you want the flog tasks'
26
+ end
27
+ end
28
+
29
+ def parse(text)
30
+ summary, methods_summary = text.split "\n\n"
31
+ score, average = summary.split("\n").map {|line| line[OPERATOR_LINE_REGEX, 1]}
32
+ return nil unless score && methods_summary
33
+ page = Flog::Page.new(score, average)
34
+ methods_summary.each_line do |method_line|
35
+ if match = method_line.match(METHOD_LINE_REGEX)
36
+ page.scanned_methods << ScannedMethod.new(match[2], match[1])
37
+ elsif match = method_line.match(OPERATOR_LINE_REGEX)
38
+ return if page.scanned_methods.empty?
39
+ page.scanned_methods.last.operators << Operator.new(match[1], match[2])
40
+ end
41
+ end
42
+ page
43
+ end
44
+
45
+ def analyze
46
+ @pages = []
47
+ flog_results.each do |path|
48
+ page = parse(open(path, "r") { |f| f.read })
49
+ if page
50
+ page.path = path.sub(metric_directory, "").sub(".txt", ".rb")
51
+ @pages << page
52
+ end
53
+ end
54
+ end
55
+
56
+ def to_h
57
+ number_of_methods = @pages.inject(0) {|count, page| count += page.scanned_methods.size}
58
+ total_flog_score = @pages.inject(0) {|total, page| total += page.score}
59
+ sorted_pages = @pages.sort_by {|page| page.score }.reverse
60
+ {:flog => { :total => total_flog_score,
61
+ :average => round_to_tenths(total_flog_score/number_of_methods),
62
+ :pages => sorted_pages.map {|page| page.to_h}}}
63
+ end
64
+
65
+ def flog_results
66
+ Dir.glob("#{metric_directory}/**/*.txt")
67
+ end
68
+
69
+ class Operator
70
+ attr_accessor :score, :operator
71
+
72
+ def initialize(score, operator)
73
+ @score = score.to_f
74
+ @operator = operator
75
+ end
76
+
77
+ def to_h
78
+ {:score => @score, :operator => @operator}
79
+ end
80
+ end
81
+
82
+ class ScannedMethod
83
+ attr_accessor :name, :score, :operators
84
+
85
+ def initialize(name, score, operators = [])
86
+ @name = name
87
+ @score = score.to_f
88
+ @operators = operators
89
+ end
90
+
91
+ def to_h
92
+ {:name => @name,
93
+ :score => @score,
94
+ :operators => @operators.map {|o| o.to_h}}
95
+ end
96
+ end
97
+
98
+ end
99
+
100
+ class Flog::Page < MetricFu::Generator
101
+ attr_accessor :path, :score, :scanned_methods, :average_score
102
+
103
+ def initialize(score, average_score, scanned_methods = [])
104
+ @score = score.to_f
105
+ @scanned_methods = scanned_methods
106
+ @average_score = average_score.to_f
107
+ end
108
+
109
+ def filename
110
+ File.basename(path, ".txt")
111
+ end
112
+
113
+ def to_h
114
+ {:score => @score,
115
+ :scanned_methods => @scanned_methods.map {|sm| sm.to_h},
116
+ :highest_score => highest_score,
117
+ :average_score => average_score,
118
+ :path => path}
119
+ end
120
+
121
+ def highest_score
122
+ scanned_methods.inject(0) do |highest, m|
123
+ m.score > highest ? m.score : highest
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,77 @@
1
+ require 'enumerator'
2
+
3
+ module MetricFu
4
+
5
+ class Rcov < Generator
6
+ NEW_FILE_MARKER = ("=" * 80) + "\n"
7
+
8
+
9
+ class Line
10
+ attr_accessor :content, :was_run
11
+
12
+ def initialize(content, was_run)
13
+ @content = content
14
+ @was_run = was_run
15
+ end
16
+
17
+ def to_h
18
+ {:content => @content, :was_run => @was_run}
19
+ end
20
+ end
21
+
22
+ def emit
23
+ begin
24
+ FileUtils.rm_rf(MetricFu::Rcov.metric_directory, :verbose => false)
25
+ Dir.mkdir(MetricFu::Rcov.metric_directory)
26
+ test_files = FileList[*MetricFu.rcov[:test_files]].join(' ')
27
+ rcov_opts = MetricFu.rcov[:rcov_opts].join(' ')
28
+ output = ">> #{MetricFu::Rcov.metric_directory}/rcov.txt"
29
+ `rcov --include-file #{test_files} #{rcov_opts} #{output}`
30
+ rescue LoadError
31
+ if RUBY_PLATFORM =~ /java/
32
+ puts 'running in jruby - rcov tasks not available'
33
+ else
34
+ puts 'sudo gem install rcov # if you want the rcov tasks'
35
+ end
36
+ end
37
+ end
38
+
39
+
40
+ def analyze
41
+ output = File.open(MetricFu::Rcov.metric_directory + '/rcov.txt').read
42
+ output = output.split(NEW_FILE_MARKER)
43
+ # Throw away the first entry - it's the execution time etc.
44
+ output.shift
45
+ files = {}
46
+ output.each_slice(2) {|out| files[out.first.strip] = out.last}
47
+ files.each_pair {|fname, content| files[fname] = content.split("\n") }
48
+ files.each_pair do |fname, content|
49
+ content.map! do |raw_line|
50
+ if raw_line.match(/^!!/)
51
+ line = Line.new(raw_line.gsub('!!', ' '), false).to_h
52
+ else
53
+ line = Line.new(raw_line, true).to_h
54
+ end
55
+ end
56
+ files[fname] = {:lines => content}
57
+ end
58
+
59
+ # Calculate the percentage of lines run in each file
60
+ @global_total_lines = 0
61
+ @global_total_lines_run = 0
62
+ files.each_pair do |fname, content|
63
+ lines = content[:lines]
64
+ @global_total_lines_run += lines_run = lines.find_all {|line| line[:was_run] == true }.length
65
+ @global_total_lines += total_lines = lines.length
66
+ percent_run = ((lines_run.to_f / total_lines.to_f) * 100).round
67
+ files[fname][:percent_run] = percent_run
68
+ end
69
+ @rcov = files
70
+ end
71
+
72
+ def to_h
73
+ global_percent_run = ((@global_total_lines_run.to_f / @global_total_lines.to_f) * 100)
74
+ {:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,32 @@
1
+ module MetricFu
2
+
3
+ class Reek < Generator
4
+ REEK_REGEX = /^(\S+) (.*) \((.*)\)$/
5
+
6
+ def emit
7
+ files_to_reek = MetricFu.reek[:dirs_to_reek].map{|dir| Dir[File.join(dir, "**/*.rb")] }
8
+ @output = `reek #{files_to_reek.join(" ")}`
9
+ end
10
+
11
+ def analyze
12
+ @matches = @output.chomp.split("\n\n").map{|m| m.split("\n") }
13
+ @matches = @matches.map do |match|
14
+ file_path = match.shift.split('--').first
15
+ file_path = file_path.gsub('"', ' ').strip
16
+ code_smells = match.map do |smell|
17
+ match_object = smell.match(REEK_REGEX)
18
+ next unless match_object
19
+ {:method => match_object[1].strip,
20
+ :message => match_object[2].strip,
21
+ :type => match_object[3].strip}
22
+ end.compact
23
+ {:file_path => file_path, :code_smells => code_smells}
24
+ end
25
+ end
26
+
27
+ def to_h
28
+ {:reek => {:matches => @matches}}
29
+ end
30
+
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ module MetricFu
2
+ class Roodi < Generator
3
+ def emit
4
+ files_to_analyze = MetricFu.roodi[:dirs_to_roodi].map{|dir| Dir[File.join(dir, "**/*.rb")] }
5
+ @output = `roodi #{files_to_analyze.join(" ")}`
6
+ end
7
+
8
+ def analyze
9
+ @matches = @output.chomp.split("\n").map{|m| m.split(" - ") }
10
+ total = @matches.pop
11
+ @matches.reject! {|array| array.empty? }
12
+ @matches.map! do |match|
13
+ file, line = match[0].split(':')
14
+ problem = match[1]
15
+ {:file => file, :line => line, :problem => problem}
16
+ end
17
+ @roodi_results = {:total => total, :problems => @matches}
18
+ end
19
+
20
+ def to_h
21
+ {:roodi => @roodi_results}
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,211 @@
1
+ module MetricFu
2
+
3
+ class Saikuro < Generator
4
+
5
+
6
+ def emit
7
+ relative_path = [File.dirname(__FILE__), '..', '..',
8
+ 'vendor', 'saikuro', 'saikuro.rb']
9
+ saikuro = File.expand_path(File.join(relative_path))
10
+
11
+ format_directories
12
+
13
+ options_string = MetricFu.saikuro.inject("") do |options, option|
14
+ options + "--#{option.join(' ')} "
15
+ end
16
+
17
+ sh %{ruby "#{saikuro}" #{options_string}} do |ok, response|
18
+ unless ok
19
+ puts "Saikuro failed with exit status: #{response.exitstatus}"
20
+ exit 1
21
+ end
22
+ end
23
+ end
24
+
25
+ def format_directories
26
+ dirs = MetricFu.saikuro[:input_directory]#.join(" | ")
27
+ dirs = "\"#{dirs}\""
28
+ MetricFu.saikuro[:input_directory] = dirs
29
+ end
30
+
31
+ def analyze
32
+ @files = []
33
+ saikuro_results.each do |path|
34
+ if Saikuro::SFile.is_valid_text_file?(path)
35
+ file = Saikuro::SFile.new(path)
36
+ if file
37
+ @files << file
38
+ end
39
+ end
40
+ end
41
+ @files = @files.sort_by do |file|
42
+ file.elements.
43
+ max {|a,b| a.complexity.to_i <=> b.complexity.to_i}.
44
+ complexity.to_i
45
+ end
46
+ @files.reverse!
47
+ klasses = []
48
+ @files.each {|f| klasses << f.elements}
49
+ klasses.flatten!
50
+ @classes = klasses.sort_by {|k| k.complexity.to_i}
51
+ @classes.reverse!
52
+ meths = []
53
+ @files.each {|f|
54
+ f.elements.each {|el|
55
+ el.defs.each {|defn|
56
+ defn.name = "#{el.name}##{defn.name}"
57
+ meths << defn}
58
+ }
59
+ }
60
+ meths = meths.sort_by {|meth| meth.complexity.to_i}
61
+ @meths = meths.reverse
62
+
63
+ end
64
+
65
+ def to_h
66
+ files = @files.map do |file|
67
+ my_file = file.to_h
68
+ my_file[:filename] = file.filename
69
+ my_file
70
+ end
71
+ {:saikuro => {:files => files,
72
+ :classes => @classes.map {|c| c.to_h},
73
+ :methods => @meths.map {|m| m.to_h}
74
+ }
75
+ }
76
+ end
77
+
78
+ def saikuro_results
79
+ Dir.glob("#{metric_directory}/**/*.html")
80
+ end
81
+
82
+ private
83
+
84
+
85
+ end
86
+
87
+ class Saikuro::SFile
88
+
89
+ attr_reader :elements
90
+
91
+ def initialize(path)
92
+ @path = path
93
+ @file_handle = File.open(@path, "r")
94
+ @elements = []
95
+ get_elements
96
+ end
97
+
98
+ def self.is_valid_text_file?(path)
99
+ File.open(path, "r") do |f|
100
+ unless f.readline.match /--/
101
+ return false
102
+ else
103
+ return true
104
+ end
105
+ end
106
+ end
107
+
108
+ def filename
109
+ File.basename(@path, '_cyclo.html')
110
+ end
111
+
112
+ def to_h
113
+ merge_classes
114
+ {:classes => @elements}
115
+ end
116
+
117
+ def get_elements
118
+ begin
119
+ while ( line = @file_handle.readline) do
120
+ if line.match /START/
121
+ line = @file_handle.readline
122
+ element = Saikuro::ParsingElement.new(line)
123
+ elsif line.match /END/
124
+ @elements << element
125
+ element = nil
126
+ else
127
+ element << line
128
+ end
129
+ end
130
+ rescue EOFError
131
+ nil
132
+ end
133
+ end
134
+
135
+
136
+ def merge_classes
137
+ new_elements = []
138
+ get_class_names.each do |target_class|
139
+ elements = @elements.find_all {|el| el.name == target_class }
140
+ complexity = 0
141
+ lines = 0
142
+ defns = []
143
+ elements.each do |el|
144
+ complexity += el.complexity.to_i
145
+ lines += el.lines.to_i
146
+ defns << el.defs
147
+ end
148
+
149
+ new_element = {:class_name => target_class,
150
+ :complexity => complexity,
151
+ :lines => lines,
152
+ :methods => defns.flatten.map {|d| d.to_h}}
153
+ new_element[:methods] = new_element[:methods].
154
+ sort_by {|x| x[:complexity] }.
155
+ reverse
156
+
157
+ new_elements << new_element
158
+ end
159
+ @elements = new_elements if new_elements
160
+ end
161
+
162
+ def get_class_names
163
+ class_names = []
164
+ @elements.each do |element|
165
+ unless class_names.include?(element.name)
166
+ class_names << element.name
167
+ end
168
+ end
169
+ class_names
170
+ end
171
+
172
+ end
173
+
174
+ class Saikuro::ParsingElement
175
+ TYPE_REGEX=/Type:(.*) Name/
176
+ NAME_REGEX=/Name:(.*) Complexity/
177
+ COMPLEXITY_REGEX=/Complexity:(.*) Lines/
178
+ LINES_REGEX=/Lines:(.*)/
179
+
180
+ attr_reader :complexity, :lines, :defs, :element_type
181
+ attr_accessor :name
182
+
183
+ def initialize(line)
184
+ @line = line
185
+ @element_type = line.match(TYPE_REGEX)[1].strip
186
+ @name = line.match(NAME_REGEX)[1].strip
187
+ @complexity = line.match(COMPLEXITY_REGEX)[1].strip
188
+ @lines = line.match(LINES_REGEX)[1].strip
189
+ @defs = []
190
+ end
191
+
192
+ def <<(line)
193
+ @defs << Saikuro::ParsingElement.new(line)
194
+ end
195
+
196
+ def to_h
197
+ base = {:name => @name, :complexity => @complexity.to_i, :lines => @lines.to_i}
198
+ unless @defs.empty?
199
+ defs = @defs.map do |my_def|
200
+ my_def = my_def.to_h
201
+ my_def.delete(:defs)
202
+ my_def
203
+ end
204
+ base[:defs] = defs
205
+ end
206
+ return base
207
+ end
208
+ end
209
+
210
+
211
+ end
@@ -0,0 +1,43 @@
1
+ module MetricFu
2
+
3
+ class Stats < Generator
4
+
5
+ def emit
6
+ `rake stats > #{metric_directory + '/stats.txt'}`
7
+ end
8
+
9
+ def analyze
10
+ output = File.open(metric_directory + '/stats.txt').read
11
+ output = output.split("\n")
12
+ output = output.find_all {|line| line[0].chr != "+" }
13
+ output = output.find_all {|line| line[0].chr != "(" }
14
+ output.shift
15
+ totals = output.pop
16
+ totals = totals.split(" ").find_all {|el| ! el.empty? }
17
+ @stats = {}
18
+ @stats[:codeLOC] = totals[0].match(/\d.*/)[0].to_i
19
+ @stats[:testLOC] = totals[1].match(/\d.*/)[0].to_i
20
+ @stats[:code_to_test_ratio] = totals[2].match(/1\:(\d.*)/)[1].to_f
21
+
22
+ @stats[:lines] = output.map do |line|
23
+ elements = line.split("|")
24
+ elements.map! {|el| el.strip }
25
+ elements = elements.find_all {|el| ! el.empty? }
26
+ info_line = {}
27
+ info_line[:name] = elements.shift
28
+ elements.map! {|el| el.to_i }
29
+ [:lines, :loc, :classes, :methods,
30
+ :methods_per_class, :loc_per_method].each do |sym|
31
+ info_line[sym] = elements.shift
32
+ end
33
+ info_line
34
+ end
35
+ @stats
36
+ end
37
+
38
+ def to_h
39
+ {:stats => @stats}
40
+ end
41
+
42
+ end
43
+ end
data/lib/metric_fu.rb ADDED
@@ -0,0 +1,21 @@
1
+ # Load a few things to make our lives easier elsewhere.
2
+ module MetricFu
3
+ LIB_ROOT = File.dirname(__FILE__)
4
+ end
5
+ base_dir = File.join(MetricFu::LIB_ROOT, 'base')
6
+ generator_dir = File.join(MetricFu::LIB_ROOT, 'generators')
7
+ template_dir = File.join(MetricFu::LIB_ROOT, 'templates')
8
+
9
+ # We need to require these two things first because our other classes
10
+ # depend on them.
11
+ require File.join(base_dir, 'report')
12
+ require File.join(base_dir, 'generator')
13
+
14
+ # Load the rakefile so users of the gem get the default metric_fu task
15
+ load File.join(MetricFu::LIB_ROOT, '..', 'tasks', 'metric_fu.rake')
16
+
17
+ # Now load everything else that's in the directory
18
+ Dir[File.join(base_dir, '*.rb')].each{|l| require l }
19
+ Dir[File.join(generator_dir, '*.rb')].each {|l| require l }
20
+ Dir[File.join(template_dir, 'standard/*.rb')].each {|l| require l}
21
+ Dir[File.join(template_dir, 'awesome/*.rb')].each {|l| require l}
@@ -0,0 +1,30 @@
1
+ class AwesomeTemplate < MetricFu::Template
2
+
3
+ def write
4
+ # Getting rid of the crap before and after the project name from integrity
5
+ @name = File.basename(Dir.pwd).gsub(/^\w+-|-\w+$/, "")
6
+
7
+ report.each_pair do |section, contents|
8
+ if template_exists?(section)
9
+ create_instance_var(section, contents)
10
+ @html = erbify(section)
11
+ html = erbify('layout')
12
+ fn = output_filename(section)
13
+ MetricFu.report.save_output(html, MetricFu.output_directory, fn)
14
+ end
15
+ end
16
+
17
+ # Instance variables we need should already be created from above
18
+ if template_exists?('index')
19
+ @html = erbify('index')
20
+ html = erbify('layout')
21
+ fn = output_filename('index')
22
+ MetricFu.report.save_output(html, MetricFu.output_directory, fn)
23
+ end
24
+ end
25
+
26
+ def this_directory
27
+ File.dirname(__FILE__)
28
+ end
29
+ end
30
+
@@ -0,0 +1,19 @@
1
+ <h3>Source Control Churn Results</h3>
2
+ <p>Files that change a lot in your project may be bad a sign.
3
+ This task uses your source control log to identify those files.
4
+ </p>
5
+ <table>
6
+ <tr>
7
+ <th>File Path</th>
8
+ <th>Times Changed</th>
9
+ </tr>
10
+ <% count = 0 %>
11
+ <% @churn[:changes].each do |change| %>
12
+ <tr>
13
+ <td><%= change[:file_path] %></td>
14
+ <td><%= change[:times_changed] %></td>
15
+ </tr>
16
+ <% count += 1 %>
17
+ <% end %>
18
+ </table>
19
+ <p>Generated on <%= Time.now.localtime %></p>
@@ -0,0 +1,62 @@
1
+ table {
2
+ margin-top: 20px;
3
+ border-collapse: collapse;
4
+ border: 1px solid #666;
5
+ background: #fff;
6
+ margin-bottom: 20px;
7
+ }
8
+
9
+ table tr.light {
10
+ background: #fff;
11
+ }
12
+
13
+ table tr.dark {
14
+ background: #f9f9f9;
15
+ }
16
+
17
+ table td, table th {
18
+ padding: 4px;
19
+ font-size: 11px;
20
+ }
21
+ table th {
22
+ text-align: center;
23
+ color: #337022;
24
+ background: #DDFFCC;
25
+ font-weight: bold;
26
+ border: #99D688 1px solid;
27
+ }
28
+
29
+ table td {
30
+ border: #d0d0d0 1px solid;
31
+ }
32
+
33
+ table td.score {
34
+ text-align: right;
35
+ }
36
+
37
+ .warning {
38
+ background: yellow;
39
+ }
40
+ .rcov_code td {
41
+ border-bottom: 1px solid #ddd ;
42
+ padding: 0;
43
+ margin: 0;
44
+ }
45
+ .rcov_code tr {
46
+ border: 0px;
47
+ padding:0px;
48
+ margin: 0px;
49
+ }
50
+ .rcov_code pre {
51
+ border: 0px;
52
+ padding: 0px;
53
+ margin: 0px;
54
+ }
55
+ .rcov_run {}
56
+ .rcov_not_run {
57
+ background-color: #d88;
58
+ }
59
+ .rcov_overflow {
60
+ overflow: auto;
61
+ font-size: 50%;
62
+ }
@@ -0,0 +1,22 @@
1
+ <h3>Flay Results</h3>
2
+ <h4>Total Score (lower is better): <%= @flay[:total_score] %></h4>
3
+ <p><a href='http://ruby.sadi.st/Flay.html'>Flay</a> analyzes ruby code for structural similarities.</p>
4
+ <table>
5
+ <tr>
6
+ <th>Files</th>
7
+ <th>Matches</th>
8
+ </tr>
9
+ <% count = 0 %>
10
+ <% @flay[:matches].each do |match| %>
11
+ <tr class='<%= cycle("light", "dark", count) %>'>
12
+ <td>
13
+ <% match[:matches].each do |file| %>
14
+ <%= file[:name] %>:<%= file[:line] %><br />
15
+ <% end %>
16
+ </td>
17
+ <td><%= match[:reason] %></td>
18
+ </tr>
19
+ <% count += 1 %>
20
+ <% end %>
21
+ </table>
22
+ <p>Generated on <%= Time.now.localtime %></p>