metric_fu 4.6.0 → 4.7.0
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.
- data.tar.gz.sig +0 -0
- data/.metrics +6 -0
- data/.travis.yml +6 -5
- data/CONTRIBUTING.md +15 -3
- data/Gemfile +3 -8
- data/Gemfile.devtools +3 -3
- data/HISTORY.md +23 -2
- data/README.md +42 -38
- data/checksum/metric_fu-4.6.0.gem.sha512 +1 -0
- data/gemfiles/Gemfile.travis +7 -0
- data/lib/metric_fu/cli/helper.rb +2 -1
- data/lib/metric_fu/configuration.rb +0 -2
- data/lib/metric_fu/formatter/html.rb +2 -0
- data/lib/metric_fu/formatter/syntax.rb +47 -0
- data/lib/metric_fu/gem_version.rb +44 -11
- data/lib/metric_fu/io.rb +5 -5
- data/lib/metric_fu/loader.rb +12 -5
- data/lib/metric_fu/metric.rb +10 -0
- data/lib/metric_fu/metrics/base_template.rb +2 -2
- data/lib/metric_fu/metrics/cane/cane.rb +1 -0
- data/lib/metric_fu/metrics/churn/churn.rb +16 -42
- data/lib/metric_fu/metrics/churn/init.rb +7 -1
- data/lib/metric_fu/metrics/hotspots/analysis/groupings.rb +1 -0
- data/lib/metric_fu/metrics/hotspots/analysis/problems.rb +1 -0
- data/lib/metric_fu/metrics/hotspots/analysis/ranked_problem_location.rb +2 -0
- data/lib/metric_fu/metrics/hotspots/analysis/rankings.rb +5 -0
- data/lib/metric_fu/metrics/hotspots/hotspot.rb +3 -0
- data/lib/metric_fu/metrics/hotspots/hotspot_analyzer.rb +8 -4
- data/lib/metric_fu/metrics/hotspots/hotspots.rb +11 -0
- data/lib/metric_fu/metrics/reek/reek.rb +1 -1
- data/lib/metric_fu/metrics/reek/reek_hotspot.rb +0 -1
- data/lib/metric_fu/metrics/saikuro/saikuro.rb +1 -0
- data/lib/metric_fu/reporting/graphs/grapher.rb +0 -1
- data/lib/metric_fu/reporting/templates/awesome/awesome_template.rb +8 -59
- data/lib/metric_fu/run.rb +1 -1
- data/lib/metric_fu/{reporting/templates/awesome → templates}/css/bluff.css +0 -0
- data/lib/metric_fu/{reporting/templates/awesome → templates}/css/buttons.css +0 -0
- data/lib/metric_fu/{reporting/templates/awesome → templates}/css/default.css +0 -0
- data/lib/metric_fu/{reporting/templates/awesome → templates}/css/integrity.css +0 -0
- data/lib/metric_fu/{reporting/templates/awesome → templates}/css/rcov.css +0 -0
- data/lib/metric_fu/{reporting/templates/awesome → templates}/css/reset.css +0 -0
- data/lib/metric_fu/{reporting/templates/awesome → templates}/css/syntax.css +0 -0
- data/lib/metric_fu/templates/report.html.erb +32 -0
- data/lib/metric_fu/templates/report.rb +36 -0
- data/lib/metric_fu/utility.rb +4 -0
- data/lib/metric_fu/version.rb +1 -1
- data/metric_fu.gemspec +11 -2
- data/spec/cli/helper_spec.rb +33 -33
- data/spec/metric_fu/configuration_spec.rb +20 -20
- data/spec/metric_fu/data_structures/line_numbers_spec.rb +12 -11
- data/spec/metric_fu/data_structures/location_spec.rb +27 -26
- data/spec/metric_fu/formatter/html_spec.rb +18 -8
- data/spec/metric_fu/formatter/yaml_spec.rb +14 -3
- data/spec/metric_fu/formatter_spec.rb +5 -5
- data/spec/metric_fu/loader_spec.rb +1 -1
- data/spec/metric_fu/metric_spec.rb +2 -2
- data/spec/metric_fu/metrics/base_template_spec.rb +50 -50
- data/spec/metric_fu/metrics/cane/cane_spec.rb +29 -28
- data/spec/metric_fu/metrics/churn/churn_spec.rb +10 -33
- data/spec/metric_fu/metrics/flay/flay_grapher_spec.rb +9 -8
- data/spec/metric_fu/metrics/flay/flay_spec.rb +14 -13
- data/spec/metric_fu/metrics/flog/flog_grapher_spec.rb +16 -15
- data/spec/metric_fu/metrics/flog/flog_spec.rb +10 -9
- data/spec/metric_fu/metrics/generator_spec.rb +19 -19
- data/spec/metric_fu/metrics/graph_spec.rb +11 -9
- data/spec/metric_fu/metrics/hotspots/analysis/analyzed_problems_spec.rb +5 -0
- data/spec/metric_fu/metrics/hotspots/analysis/analyzer_tables_spec.rb +11 -6
- data/spec/metric_fu/metrics/hotspots/analysis/ranking_spec.rb +4 -3
- data/spec/metric_fu/metrics/hotspots/analysis/rankings_spec.rb +16 -10
- data/spec/metric_fu/metrics/hotspots/analysis/table_spec.rb +2 -1
- data/spec/metric_fu/metrics/hotspots/hotspot_analyzer_spec.rb +2 -1
- data/spec/metric_fu/metrics/hotspots/hotspot_spec.rb +8 -2
- data/spec/metric_fu/metrics/hotspots/hotspots_spec.rb +4 -8
- data/spec/metric_fu/metrics/rails_best_practices/rails_best_practices_grapher_spec.rb +10 -9
- data/spec/metric_fu/metrics/rails_best_practices/rails_best_practices_spec.rb +8 -7
- data/spec/metric_fu/metrics/rcov/rcov_grapher_spec.rb +8 -8
- data/spec/metric_fu/metrics/rcov/rcov_hotspot_spec.rb +7 -4
- data/spec/metric_fu/metrics/rcov/rcov_spec.rb +15 -14
- data/spec/metric_fu/metrics/reek/reek_grapher_spec.rb +10 -9
- data/spec/metric_fu/metrics/reek/reek_spec.rb +17 -15
- data/spec/metric_fu/metrics/roodi/roodi_grapher_spec.rb +9 -8
- data/spec/metric_fu/metrics/roodi/roodi_spec.rb +4 -3
- data/spec/metric_fu/metrics/saikuro/saikuro_spec.rb +13 -10
- data/spec/metric_fu/metrics/stats/stats_grapher_spec.rb +13 -12
- data/spec/metric_fu/metrics/stats/stats_spec.rb +16 -14
- data/spec/metric_fu/reporter_spec.rb +8 -7
- data/spec/metric_fu/reporting/graphs/grapher_spec.rb +1 -1
- data/spec/metric_fu/reporting/result_spec.rb +12 -11
- data/spec/metric_fu/run_spec.rb +24 -8
- data/spec/spec_helper.rb +1 -2
- data/spec/support/helper_methods.rb +14 -1
- metadata +124 -57
- metadata.gz.sig +0 -0
- checksums.yaml +0 -7
- checksums.yaml.gz.sig +0 -0
- data/lib/metric_fu/initial_requires.rb +0 -13
- data/lib/metric_fu/load_files.rb +0 -34
data/lib/metric_fu/io.rb
CHANGED
|
@@ -101,21 +101,21 @@ module MetricFu
|
|
|
101
101
|
# Given an existing io stream, the stream will not
|
|
102
102
|
# be automatically closed. Cleanup, if necessary, is
|
|
103
103
|
# the responsibility of the caller.
|
|
104
|
-
def io_for(path_or_io)
|
|
104
|
+
def io_for(path_or_io, &block)
|
|
105
105
|
raise ArgumentError, "No path or io provided." if path_or_io.nil?
|
|
106
106
|
raise ArgumentError, "No block given. Cannot yield io stream." unless block_given?
|
|
107
107
|
|
|
108
108
|
if path_or_io.respond_to?(:write)
|
|
109
109
|
# We have an existing open stream...
|
|
110
|
-
|
|
110
|
+
block.call(path_or_io)
|
|
111
111
|
else # Otherwise, we assume its a file path...
|
|
112
|
-
file_for(path_or_io)
|
|
112
|
+
file_for(path_or_io, &block)
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
|
|
116
|
-
def file_for(path)
|
|
116
|
+
def file_for(path, &block)
|
|
117
117
|
File.open(path_relative_to_base(path), 'w') do |file|
|
|
118
|
-
|
|
118
|
+
block.call(file)
|
|
119
119
|
end
|
|
120
120
|
end
|
|
121
121
|
|
data/lib/metric_fu/loader.rb
CHANGED
|
@@ -72,13 +72,21 @@ module MetricFu
|
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
def setup
|
|
75
|
+
MetricFu.logging_require { 'mf_debugger' }
|
|
76
|
+
Object.send :include, MfDebugger
|
|
77
|
+
MfDebugger::Logger.debug_on = !!(ENV['MF_DEBUG'] =~ /true/i)
|
|
78
|
+
|
|
75
79
|
MetricFu.lib_require { 'configuration' }
|
|
76
80
|
MetricFu.lib_require { 'metric' }
|
|
77
|
-
|
|
78
|
-
MetricFu.
|
|
79
|
-
|
|
80
|
-
MetricFu.lib_require { 'load_files' }
|
|
81
|
+
|
|
82
|
+
Dir.glob(File.join(MetricFu.metrics_dir, '**/init.rb')).each{|init_file|require(init_file)}
|
|
83
|
+
|
|
81
84
|
load_user_configuration
|
|
85
|
+
|
|
86
|
+
MetricFu.lib_require { 'reporter' }
|
|
87
|
+
MetricFu.reporting_require { 'result' }
|
|
88
|
+
|
|
89
|
+
MetricFu.load_tasks('metric_fu.rake', task_name: 'metrics:all')
|
|
82
90
|
end
|
|
83
91
|
|
|
84
92
|
def load_user_configuration
|
|
@@ -88,4 +96,3 @@ module MetricFu
|
|
|
88
96
|
|
|
89
97
|
end
|
|
90
98
|
end
|
|
91
|
-
|
data/lib/metric_fu/metric.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'set'
|
|
2
2
|
MetricFu.lib_require { 'gem_run' }
|
|
3
|
+
MetricFu.metrics_require { 'generator' }
|
|
3
4
|
# Encapsulates the configuration options for each metric
|
|
4
5
|
module MetricFu
|
|
5
6
|
class Metric
|
|
@@ -18,6 +19,7 @@ module MetricFu
|
|
|
18
19
|
|
|
19
20
|
# TODO: Confirm this catches load errors from requires in subclasses, such as for flog
|
|
20
21
|
def activate
|
|
22
|
+
MetricFu.metrics_require { default_metric_library_paths }
|
|
21
23
|
@libraries.each {|library| require(library) }
|
|
22
24
|
self.activated = true
|
|
23
25
|
rescue LoadError => e
|
|
@@ -120,5 +122,13 @@ module MetricFu
|
|
|
120
122
|
@libraries << file.strip
|
|
121
123
|
end
|
|
122
124
|
|
|
125
|
+
def default_metric_library_paths
|
|
126
|
+
paths = []
|
|
127
|
+
paths << generator_path = "#{name}/#{name}"
|
|
128
|
+
if has_graph?
|
|
129
|
+
paths << grapher_path = "#{name}/#{name}_grapher"
|
|
130
|
+
end
|
|
131
|
+
paths
|
|
132
|
+
end
|
|
123
133
|
end
|
|
124
134
|
end
|
|
@@ -120,8 +120,8 @@ module MetricFu
|
|
|
120
120
|
# @return String
|
|
121
121
|
# The contents of the css file
|
|
122
122
|
def inline_css(css)
|
|
123
|
-
css_file = File.join(
|
|
124
|
-
|
|
123
|
+
css_file = File.join(MetricFu.lib_dir,'templates', css)
|
|
124
|
+
MetricFu::Utility.binread(css_file)
|
|
125
125
|
end
|
|
126
126
|
|
|
127
127
|
# Provides a link to open a file through the textmate protocol
|
|
@@ -6,61 +6,35 @@ module MetricFu
|
|
|
6
6
|
:churn
|
|
7
7
|
end
|
|
8
8
|
|
|
9
|
+
###
|
|
10
|
+
# options available are what can be passed to churn_calculator
|
|
11
|
+
# https://github.com/danmayer/churn#library-options
|
|
12
|
+
###
|
|
9
13
|
def emit
|
|
10
|
-
@output =
|
|
14
|
+
@output = run(options)
|
|
11
15
|
end
|
|
12
16
|
|
|
13
17
|
def analyze
|
|
14
|
-
if @output.nil? || @output.
|
|
18
|
+
if @output.nil? || @output.size.zero?
|
|
15
19
|
@churn = {:churn => {}}
|
|
16
20
|
else
|
|
17
|
-
@churn =
|
|
21
|
+
@churn = @output
|
|
18
22
|
end
|
|
23
|
+
@churn
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
# ensure hash only has the :churn key
|
|
22
27
|
def to_h
|
|
23
28
|
{:churn => @churn[:churn]}
|
|
24
29
|
end
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
def churn_code
|
|
34
|
-
run!(build_churn_options)
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def ensure_output_is_valid_yaml(output)
|
|
38
|
-
yaml_start = output.index("---")
|
|
39
|
-
if yaml_start
|
|
40
|
-
output[yaml_start...output.length]
|
|
41
|
-
else
|
|
42
|
-
nil
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def build_churn_options
|
|
47
|
-
opts = ["--yaml"]
|
|
48
|
-
churn_options.each do |churn_option, command_flag|
|
|
49
|
-
if has_option?(churn_option)
|
|
50
|
-
opts << "#{command_flag}=#{options[churn_option]}"
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
opts.join(" ")
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def has_option?(churn_option)
|
|
57
|
-
options.include?(churn_option)
|
|
58
|
-
end
|
|
59
|
-
def churn_options
|
|
60
|
-
{
|
|
61
|
-
:minimum_churn_count => '--minimum_churn_count',
|
|
62
|
-
:start_date => '--start_date'
|
|
63
|
-
}
|
|
30
|
+
|
|
31
|
+
# @param args [Hash] churn metric run options
|
|
32
|
+
# @return [Hash] churn results
|
|
33
|
+
# @example {something}
|
|
34
|
+
def run(args)
|
|
35
|
+
# @note passing in false to report will return a hash
|
|
36
|
+
# instead of the default String
|
|
37
|
+
::Churn::ChurnCalculator.new(args).report(false)
|
|
64
38
|
end
|
|
65
39
|
|
|
66
40
|
end
|
|
@@ -6,7 +6,12 @@ module MetricFu
|
|
|
6
6
|
end
|
|
7
7
|
|
|
8
8
|
def default_run_options
|
|
9
|
-
{
|
|
9
|
+
{
|
|
10
|
+
:start_date => %q("1 year ago"),
|
|
11
|
+
:minimum_churn_count => 10,
|
|
12
|
+
:ignore_files => [],
|
|
13
|
+
:data_directory => MetricFu::Io::FileSystem.scratch_directory(name)
|
|
14
|
+
}
|
|
10
15
|
end
|
|
11
16
|
|
|
12
17
|
def has_graph?
|
|
@@ -18,6 +23,7 @@ module MetricFu
|
|
|
18
23
|
end
|
|
19
24
|
|
|
20
25
|
def activate
|
|
26
|
+
activate_library('churn/churn_calculator')
|
|
21
27
|
super
|
|
22
28
|
end
|
|
23
29
|
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
-
require
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
require 'yaml'
|
|
2
|
+
MetricFu.metrics_require do
|
|
3
|
+
[
|
|
4
|
+
'hotspots/hotspot',
|
|
5
|
+
'hotspots/analysis/analyzer_tables',
|
|
6
|
+
'hotspots/analysis/analyzed_problems',
|
|
7
|
+
'hotspots/analysis/rankings'
|
|
8
|
+
]
|
|
4
9
|
end
|
|
5
|
-
MetricFu.metrics_require { 'hotspots/hotspot' }
|
|
6
10
|
|
|
7
11
|
module MetricFu
|
|
8
12
|
class HotspotAnalyzer
|
|
@@ -8,6 +8,9 @@ module MetricFu
|
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def initialize(options={})
|
|
11
|
+
MetricFu::Metric.enabled_metrics.each do |metric|
|
|
12
|
+
require_hotspot(metric.name)
|
|
13
|
+
end
|
|
11
14
|
super
|
|
12
15
|
end
|
|
13
16
|
|
|
@@ -27,6 +30,14 @@ module MetricFu
|
|
|
27
30
|
end
|
|
28
31
|
result
|
|
29
32
|
end
|
|
33
|
+
|
|
34
|
+
private
|
|
35
|
+
|
|
36
|
+
def require_hotspot(metric_name)
|
|
37
|
+
require "metric_fu/metrics/#{metric_name}/#{metric_name}_hotspot"
|
|
38
|
+
rescue LoadError
|
|
39
|
+
mf_debug "*** No hotspot for #{metric_name}"
|
|
40
|
+
end
|
|
30
41
|
end
|
|
31
42
|
|
|
32
43
|
end
|
|
@@ -23,7 +23,7 @@ module MetricFu
|
|
|
23
23
|
def analyze
|
|
24
24
|
@matches = @output.chomp.split("\n\n").map{|m| m.split("\n") }
|
|
25
25
|
@matches = @matches.map do |match|
|
|
26
|
-
file_path = match.shift.split('--').first
|
|
26
|
+
file_path = match.shift.split(' -- ').first
|
|
27
27
|
file_path = file_path.gsub('"', ' ').strip
|
|
28
28
|
code_smells = match.map do |smell|
|
|
29
29
|
match_object = smell.match(REEK_REGEX)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
require 'fileutils'
|
|
2
|
-
require 'coderay'
|
|
3
2
|
MetricFu.metrics_require { 'base_template' }
|
|
4
|
-
MetricFu.lib_require { '
|
|
3
|
+
MetricFu.lib_require { 'templates/report' }
|
|
5
4
|
|
|
6
5
|
class AwesomeTemplate < MetricFu::Template
|
|
7
6
|
|
|
@@ -44,70 +43,20 @@ class AwesomeTemplate < MetricFu::Template
|
|
|
44
43
|
write_file_data
|
|
45
44
|
end
|
|
46
45
|
|
|
47
|
-
def convert_ruby_to_html(ruby_text, line_number)
|
|
48
|
-
tokens = CodeRay.scan(MetricFu::Utility.clean_ascii_text(ruby_text), :ruby)
|
|
49
|
-
options = { :css => :class, :style => :alpha }
|
|
50
|
-
if line_number.to_i > 0
|
|
51
|
-
options = options.merge({:line_numbers => :inline, :line_number_start => line_number.to_i })
|
|
52
|
-
end
|
|
53
|
-
tokens.div(options)
|
|
54
|
-
# CodeRay options
|
|
55
|
-
# used to analyze source code, because object Tokens is a list of tokens with specified types.
|
|
56
|
-
# :tab_width – tabulation width in spaces. Default: 8
|
|
57
|
-
# :css – how to include the styles (:class и :style). Default: :class)
|
|
58
|
-
#
|
|
59
|
-
# :wrap – wrap result in html tag :page, :div, :span or not to wrap (nil)
|
|
60
|
-
#
|
|
61
|
-
# :line_numbers – how render line numbers (:table, :inline, :list or nil)
|
|
62
|
-
#
|
|
63
|
-
# :line_number_start – first line number
|
|
64
|
-
#
|
|
65
|
-
# :bold_every – make every n-th line number bold. Default: 10
|
|
66
|
-
end
|
|
67
46
|
def write_file_data
|
|
68
|
-
|
|
69
47
|
per_file_data.each_pair do |file, lines|
|
|
70
48
|
next if file.to_s.empty?
|
|
71
49
|
next unless File.file?(file)
|
|
50
|
+
report = MetricFu::Templates::Report.new(file, lines).render(@metrics)
|
|
72
51
|
|
|
73
|
-
|
|
74
|
-
fn = "#{file.gsub(%r{/}, '_')}.html"
|
|
75
|
-
|
|
76
|
-
out = <<-HTML
|
|
77
|
-
<html><head><style>
|
|
78
|
-
#{inline_css('css/syntax.css')}
|
|
79
|
-
#{inline_css('css/bluff.css') if MetricFu.configuration.graph_engine == :bluff}
|
|
80
|
-
#{inline_css('css/rcov.css') if @metrics.has_key?(:rcov)}
|
|
81
|
-
</style></head><body>
|
|
82
|
-
HTML
|
|
83
|
-
out << "<table cellpadding='0' cellspacing='0' class='ruby'>"
|
|
84
|
-
data.each_with_index do |line, idx|
|
|
85
|
-
line_number = (idx + 1).to_s
|
|
86
|
-
out << "<tr>"
|
|
87
|
-
out << "<td valign='top'>"
|
|
88
|
-
if lines.has_key?(line_number)
|
|
89
|
-
out << "<ul>"
|
|
90
|
-
lines[line_number].each do |problem|
|
|
91
|
-
out << "<li>#{problem[:description]} » #{problem[:type]}</li>"
|
|
92
|
-
end
|
|
93
|
-
out << "</ul>"
|
|
94
|
-
else
|
|
95
|
-
out << " "
|
|
96
|
-
end
|
|
97
|
-
out << "</td>"
|
|
98
|
-
if MetricFu::Formatter::Templates.option('syntax_highlighting')
|
|
99
|
-
line_for_display = convert_ruby_to_html(line, line_number)
|
|
100
|
-
else
|
|
101
|
-
line_for_display = "<a name='n#{line_number}' href='n#{line_number}'>#{line_number}</a>#{line}"
|
|
102
|
-
end
|
|
103
|
-
out << "<td valign='top'>#{line_for_display}</td>"
|
|
104
|
-
out << "</tr>"
|
|
105
|
-
end
|
|
106
|
-
out << "<table></body></html>"
|
|
107
|
-
|
|
108
|
-
formatter.write_template(out, fn)
|
|
52
|
+
formatter.write_template(report, html_filename(file))
|
|
109
53
|
end
|
|
110
54
|
end
|
|
55
|
+
|
|
56
|
+
def html_filename(file)
|
|
57
|
+
"#{file.gsub(%r{/}, '_')}.html"
|
|
58
|
+
end
|
|
59
|
+
|
|
111
60
|
def template_directory
|
|
112
61
|
File.dirname(__FILE__)
|
|
113
62
|
end
|
data/lib/metric_fu/run.rb
CHANGED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<html>
|
|
2
|
+
<head>
|
|
3
|
+
<style>
|
|
4
|
+
<%= inline_css('css/syntax.css') %>
|
|
5
|
+
<%= inline_css('css/bluff.css') if MetricFu.configuration.graph_engine == :bluff %>
|
|
6
|
+
<%= inline_css('css/rcov.css') if @metrics.has_key?(:rcov) %>
|
|
7
|
+
</style>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<table cellpadding='0' cellspacing='0' class='ruby'>
|
|
11
|
+
<% @data.each_with_index do |line, idx| %>
|
|
12
|
+
<% line_number = (idx + 1).to_s %>
|
|
13
|
+
<tr>
|
|
14
|
+
<td valign='top'>
|
|
15
|
+
<% if @lines.has_key?(line_number) %>
|
|
16
|
+
<ul>
|
|
17
|
+
<% @lines[line_number].each do |problem| %>
|
|
18
|
+
<li><%= "#{problem[:description]} » #{problem[:type]}" %></li>
|
|
19
|
+
<% end %>
|
|
20
|
+
</ul>
|
|
21
|
+
<% else %>
|
|
22
|
+
|
|
23
|
+
<% end %>
|
|
24
|
+
</td>
|
|
25
|
+
<td valign='top'>
|
|
26
|
+
<%= line_for_display(line, line_number) %>
|
|
27
|
+
</td>
|
|
28
|
+
</tr>
|
|
29
|
+
<% end %>
|
|
30
|
+
<table>
|
|
31
|
+
</body>
|
|
32
|
+
</html>
|