edouard-metric_fu 1.0.3.2 → 1.0.3.3

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.
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>