metric_adapter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +58 -0
- data/examples/analyzer_mixin.rb +34 -0
- data/examples/annotate.rb +75 -0
- data/examples/report.rb +37 -0
- data/lib/adapters/flay_adapter.rb +51 -0
- data/lib/adapters/flog_adapter.rb +47 -0
- data/lib/adapters/reek_adapter.rb +32 -0
- data/lib/location.rb +36 -0
- data/lib/metric.rb +29 -0
- data/lib/metric_adapter.rb +8 -0
- data/test/flay_adapter_test.rb +53 -0
- data/test/flog_adapter_test.rb +69 -0
- data/test/location_test.rb +49 -0
- data/test/reek_adapter_test.rb +55 -0
- metadata +78 -0
data/README.markdown
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
MetricAdapter
|
2
|
+
==============
|
3
|
+
|
4
|
+
At [LiquidPlanner](http://liquidplanner.com), we wanted to build some tools to help us analyze our code. There are a [ton of static analyzers for ruby](http://xkcd.com/927/), each providing different insightful metrics, so there's no need to write a new analyzer. But what do you do when you want to use more than one of them?
|
5
|
+
|
6
|
+
Each analyzer takes a different configuration, and provides results in different formats. MetricAdapter solves the latter problem by converting the results of each analyzer into a common format.
|
7
|
+
|
8
|
+
Now you can take all of these wonderful libraries and combine their results without worrying about how they internally represent their metrics.
|
9
|
+
|
10
|
+
Metrics
|
11
|
+
-------
|
12
|
+
Each metric represents a single statement about one line of code. Flay for instance, generates reports that looks like this:
|
13
|
+
|
14
|
+
1) IDENTICAL code found in :lasgn (mass*2 = 24)
|
15
|
+
lib/adapters/flay_adapter.rb:26
|
16
|
+
lib/adapters/flog_adapter.rb:23
|
17
|
+
|
18
|
+
In this case, two metrics would be generated, one per line. This makes it easier to annotate source files, or integrate these libraries with your favorite editor (`sed`, I kid, I know we all use `notepad` these days).
|
19
|
+
|
20
|
+
Each metric contains:
|
21
|
+
|
22
|
+
* Metric#location – An object representing a file path and line number
|
23
|
+
* Metric#signature – The associated method signature (_not all metrics currently have this_)
|
24
|
+
* Metric#message – A message explaining the metric, for instance "Has no descriptive comment"
|
25
|
+
* Metric#score – When applicable, a numeric score or rating indicating the severity
|
26
|
+
|
27
|
+
Examples
|
28
|
+
--------
|
29
|
+
To get normalized metrics, instantiate the static analysis library of your choice, and pass it to the appropriate adapter:
|
30
|
+
|
31
|
+
# Instantiate and configure your analyzer
|
32
|
+
flay = Flay.new :mass => 4
|
33
|
+
flay.process(*files)
|
34
|
+
flay.analyze
|
35
|
+
|
36
|
+
# Report on the adapted metrics
|
37
|
+
adapter = MetricAdapter::FlayAdapter.new(flay)
|
38
|
+
adapter.metrics.each do |m|
|
39
|
+
puts "#{m.location} - #{m.message}"
|
40
|
+
end
|
41
|
+
|
42
|
+
For a full example, see `examples/report.rb` and `examples/annotate.rb`. You can use these examples to build tools that are appropriate for your team.
|
43
|
+
|
44
|
+
Supported Libraries
|
45
|
+
-------------------
|
46
|
+
|
47
|
+
* Flay – Code duplication
|
48
|
+
* Flog – Code complexity
|
49
|
+
* Reek – Code smells
|
50
|
+
|
51
|
+
|
52
|
+
Contributing
|
53
|
+
------------
|
54
|
+
Please do! Take a look at the existing adapters and tests (`lib/adapters` and `test/*_adapter_test.rb`) for examples.
|
55
|
+
|
56
|
+
---
|
57
|
+
|
58
|
+
[Adam Sanderson](http://monkeyandcrow.com) for [LiquidPlanner](http://liquidplanner.com)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'flay'
|
2
|
+
require 'flog'
|
3
|
+
require 'reek'
|
4
|
+
|
5
|
+
# In your actual application, you would configure each of these static
|
6
|
+
# analyzers to match your own exacting standards. These mostly use the
|
7
|
+
# defaults for each library.
|
8
|
+
#
|
9
|
+
module AnalyzerMixin
|
10
|
+
# Generate metrics for Flay.
|
11
|
+
#
|
12
|
+
# In this sample, we are setting the `mass` option low so that there are more
|
13
|
+
# results.
|
14
|
+
#
|
15
|
+
def flay(files, mass = 4)
|
16
|
+
flay = Flay.new :mass => mass
|
17
|
+
flay.process(*files)
|
18
|
+
flay.analyze
|
19
|
+
MetricAdapter::FlayAdapter.new(flay).metrics
|
20
|
+
end
|
21
|
+
|
22
|
+
# Generate flog metrics.
|
23
|
+
def flog(files)
|
24
|
+
flog = Flog.new
|
25
|
+
flog.flog(*files)
|
26
|
+
MetricAdapter::FlogAdapter.new(flog).metrics
|
27
|
+
end
|
28
|
+
|
29
|
+
# Generate Reek metrics.
|
30
|
+
def reek(files)
|
31
|
+
examiner = Reek::Examiner.new(files)
|
32
|
+
MetricAdapter::ReekAdapter.new(examiner).metrics
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
DIR_NAME = File.dirname(__FILE__)
|
2
|
+
$:.unshift(DIR_NAME + '/../lib')
|
3
|
+
|
4
|
+
require "metric_adapter"
|
5
|
+
require "./#{DIR_NAME}/analyzer_mixin"
|
6
|
+
|
7
|
+
#
|
8
|
+
# This is an example of using MetricAdapter to provide a common
|
9
|
+
# interface for Flay, Flog, and Reek's metrics to annotate a file.
|
10
|
+
#
|
11
|
+
|
12
|
+
include AnalyzerMixin
|
13
|
+
|
14
|
+
COLORS = {
|
15
|
+
:normal => "\033[39m",
|
16
|
+
:alternate => "\033[38;5;99m",
|
17
|
+
:faded => "\033[90m"
|
18
|
+
}
|
19
|
+
|
20
|
+
files = ARGV.empty? ? [__FILE__] : ARGV
|
21
|
+
|
22
|
+
def report_on_file(path, metrics)
|
23
|
+
puts "\n#{path}"
|
24
|
+
return if !metrics || metrics.empty?
|
25
|
+
|
26
|
+
metrics_by_line = metrics.group_by{|m| m.location.line }
|
27
|
+
|
28
|
+
source = IO.read(path)
|
29
|
+
source.lines.each_with_index do |src_line, i|
|
30
|
+
line_number = i + 1 # Logical line numbers, not indices
|
31
|
+
src_line = src_line.chomp # Strip newline
|
32
|
+
indent = src_line.scan(/\A\s+/).first
|
33
|
+
line_metrics = metrics_by_line[line_number]
|
34
|
+
|
35
|
+
print_metrics(line_number, line_metrics, indent)
|
36
|
+
print_src(line_number, src_line)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def print_metrics(line_number, metrics, indent = "")
|
41
|
+
return unless metrics
|
42
|
+
|
43
|
+
metrics.each do |m|
|
44
|
+
print_line line_number, "#{indent}#{m.message}", :alternate
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def print_src(line_number, src_line)
|
49
|
+
if src_line =~ /^\s*#/
|
50
|
+
# Print a comment
|
51
|
+
print_line line_number, src_line, :faded
|
52
|
+
else
|
53
|
+
# Print a normal line
|
54
|
+
print_line line_number, src_line, :normal
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def print_line(line_number, text, name)
|
59
|
+
formatted_number = '%5d.' % line_number
|
60
|
+
puts "#{color formatted_number, :faded} #{color text, name}"
|
61
|
+
end
|
62
|
+
|
63
|
+
def color(text, name)
|
64
|
+
"#{COLORS[name]}#{text}\033[0m"
|
65
|
+
end
|
66
|
+
|
67
|
+
# Combine metrics from all our sources (See AnalyzerMixin):
|
68
|
+
metrics = flay(files, 16) + flog(files) + reek(files)
|
69
|
+
|
70
|
+
metrics_by_path = metrics.group_by{|m| m.location.path }
|
71
|
+
|
72
|
+
files.each do |path|
|
73
|
+
metrics = metrics_by_path[path]
|
74
|
+
report_on_file(path, metrics)
|
75
|
+
end
|
data/examples/report.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
DIR_NAME = File.dirname(__FILE__)
|
2
|
+
$:.unshift(DIR_NAME + '/../lib')
|
3
|
+
|
4
|
+
require "metric_adapter"
|
5
|
+
require "./#{DIR_NAME}/analyzer_mixin"
|
6
|
+
|
7
|
+
include AnalyzerMixin
|
8
|
+
|
9
|
+
#
|
10
|
+
# This is a quick and dirty example of using MetricAdapter to provide a common
|
11
|
+
# interface for Flay, Flog, and Reek's metrics.
|
12
|
+
#
|
13
|
+
|
14
|
+
files = ARGV.empty? ? Dir[DIR_NAME + '/../lib/**/*.rb'] : ARGV
|
15
|
+
|
16
|
+
# Helper for displaying an ascii divider
|
17
|
+
def divider(char="-")
|
18
|
+
char * 80
|
19
|
+
end
|
20
|
+
|
21
|
+
# Combine metrics from all our sources (See AnalyzerMixin):
|
22
|
+
metrics = flay(files) + flog(files) + reek(files)
|
23
|
+
|
24
|
+
# Group those metrics by path
|
25
|
+
metrics_by_path = metrics.group_by{|m| m.location.path }
|
26
|
+
|
27
|
+
# For each path, print out a little report.
|
28
|
+
metrics_by_path.each do |path, metrics|
|
29
|
+
puts divider
|
30
|
+
puts path
|
31
|
+
puts divider
|
32
|
+
|
33
|
+
puts " line: message"
|
34
|
+
metrics.sort_by{|m| m.location }.each do |m|
|
35
|
+
puts "%6d: %s" % [m.location.line, m.message]
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module MetricAdapter
|
2
|
+
class FlayAdapter
|
3
|
+
attr_reader :flay
|
4
|
+
|
5
|
+
def initialize(flay)
|
6
|
+
@flay = flay
|
7
|
+
end
|
8
|
+
|
9
|
+
def metrics
|
10
|
+
metrics = flay.hashes.map do |hash_key, nodes|
|
11
|
+
nodes.permutation(2).map do |node_a, node_b|
|
12
|
+
create_metric(hash_key, node_a, node_b)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
metrics.flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def metrics_for_hash(hash_key, score)
|
22
|
+
nodes = flay.hashes[hash_key]
|
23
|
+
is_identical =
|
24
|
+
|
25
|
+
location = method_location(signature)
|
26
|
+
message = "Flog: #{score.round(2)}"
|
27
|
+
|
28
|
+
metric = Metric.new(location, signature, message)
|
29
|
+
metric.score = score
|
30
|
+
|
31
|
+
metric
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_metric(hash_key, node_a, node_b)
|
35
|
+
is_identical = flay.identical[hash_key]
|
36
|
+
similarity = is_identical ? "identical" : "similar"
|
37
|
+
score = flay.masses[hash_key]
|
38
|
+
|
39
|
+
message = "Flay: #{score}, #{similarity} to #{node_location(node_b)}"
|
40
|
+
# TODO: generate signatures from locations
|
41
|
+
signature = ""
|
42
|
+
|
43
|
+
metric = Metric.new(node_location(node_a), signature, message)
|
44
|
+
end
|
45
|
+
|
46
|
+
def node_location(node)
|
47
|
+
Location.new(node.file, node.line)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module MetricAdapter
|
2
|
+
class FlogAdapter
|
3
|
+
attr_reader :flog
|
4
|
+
|
5
|
+
def initialize(flog)
|
6
|
+
@flog = flog
|
7
|
+
end
|
8
|
+
|
9
|
+
def metrics
|
10
|
+
metrics = []
|
11
|
+
|
12
|
+
flog.each_by_score do |signature, score|
|
13
|
+
if locatable?(signature)
|
14
|
+
metrics << create_metric(signature, score)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
metrics
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def create_metric(signature, score)
|
24
|
+
location = method_location(signature)
|
25
|
+
message = "Flog: #{score.round(2)}"
|
26
|
+
|
27
|
+
metric = Metric.new(location, signature, message)
|
28
|
+
metric.score = score
|
29
|
+
|
30
|
+
metric
|
31
|
+
end
|
32
|
+
|
33
|
+
def method_location(signature)
|
34
|
+
path = flog.method_locations[signature]
|
35
|
+
Location.new(path)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Flog will report on code that is not in a class or method,
|
39
|
+
# all of this top level code may be spread out across multiple
|
40
|
+
# files, so we can're report on that code's location.
|
41
|
+
def locatable?(signature)
|
42
|
+
!!flog.method_locations[signature]
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module MetricAdapter
|
2
|
+
class ReekAdapter
|
3
|
+
attr_reader :examiner
|
4
|
+
|
5
|
+
def initialize(examiner)
|
6
|
+
@examiner = examiner
|
7
|
+
end
|
8
|
+
|
9
|
+
def metrics
|
10
|
+
metrics = examiner.smells.map do |smell|
|
11
|
+
line_numbers = Array(smell.lines).uniq
|
12
|
+
line_numbers.map do |line|
|
13
|
+
create_metric(smell, line)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
metrics.flatten
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def create_metric(smell, line)
|
23
|
+
location = Location.new(smell.source, line)
|
24
|
+
message = "#{smell.message.capitalize} (#{smell.subclass})"
|
25
|
+
signature = smell.context
|
26
|
+
|
27
|
+
Metric.new(location, signature, message)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/lib/location.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module MetricAdapter
|
2
|
+
class Location
|
3
|
+
attr_reader :path, :line
|
4
|
+
|
5
|
+
def initialize(combined_path, line = 0)
|
6
|
+
@path, @line = parse_path(combined_path || '')
|
7
|
+
@line ||= line
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
"#{path}:#{line}"
|
12
|
+
end
|
13
|
+
|
14
|
+
def <=>(other)
|
15
|
+
[path,line] <=> [other.path, other.line]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def parse_path(path)
|
21
|
+
path = path.strip
|
22
|
+
|
23
|
+
if has_line_number?(path)
|
24
|
+
*path_chunks, line = path.split(':')
|
25
|
+
[path_chunks.join(':'), line.to_i]
|
26
|
+
else
|
27
|
+
[path,nil]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_line_number?(path)
|
32
|
+
!!(path =~ /\:\d+\s*$/)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
data/lib/metric.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
module MetricAdapter
|
2
|
+
|
3
|
+
# A normalized representation of a code metric.
|
4
|
+
class Metric
|
5
|
+
|
6
|
+
# Location where this metric applies
|
7
|
+
attr_accessor :location
|
8
|
+
|
9
|
+
# Associated class and method signature
|
10
|
+
attr_accessor :signature
|
11
|
+
|
12
|
+
# Message indicating the issue being reported
|
13
|
+
attr_accessor :message
|
14
|
+
|
15
|
+
# Optional score for for the metric indicating severity
|
16
|
+
# A score is not normalized across analyzers
|
17
|
+
attr_accessor :score
|
18
|
+
|
19
|
+
# Create an instance of Metric.
|
20
|
+
# `location` is expected to be a `Location` instance
|
21
|
+
def initialize(location, signature, message)
|
22
|
+
@location = location
|
23
|
+
@signature = signature
|
24
|
+
@message = message
|
25
|
+
@score = 0
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "minitest/autorun"
|
2
|
+
require "metric_adapter"
|
3
|
+
|
4
|
+
require "flay"
|
5
|
+
|
6
|
+
class FlayAdapterTest < MiniTest::Unit::TestCase
|
7
|
+
include MetricAdapter
|
8
|
+
|
9
|
+
def test_empty_flay
|
10
|
+
flay = Flay.new
|
11
|
+
adapter = FlayAdapter.new(flay)
|
12
|
+
|
13
|
+
assert_equal [], adapter.metrics
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_flay_adapts_locations
|
17
|
+
adapter = flay_self
|
18
|
+
flay = adapter.flay
|
19
|
+
# For each set of similar nodes, it should generate all the permutations (nodes choose 2)
|
20
|
+
expected_count = flay.hashes.map{|h, nodes| nodes ** 2}.inject{|n,m| n+m }
|
21
|
+
|
22
|
+
assert_equal expected_count, adapter.metrics.length, "Each set of similar nodes should generate n^2 metrics"
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_flay_adapts_locations
|
26
|
+
adapter = flay_self
|
27
|
+
metric = adapter.metrics.first
|
28
|
+
location = metric.location
|
29
|
+
|
30
|
+
assert_equal __FILE__, location.path, "Should reference this file"
|
31
|
+
assert location.line != 0, "Line numbers should be captured (line:0 should not show up in flog)"
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_flay_messages
|
35
|
+
adapter = flay_self
|
36
|
+
metric = adapter.metrics.first
|
37
|
+
message = metric.message
|
38
|
+
|
39
|
+
assert message =~ /flay/i, "Should reference flay: #{message.inspect}"
|
40
|
+
assert message =~ /#{Regexp.escape __FILE__}:\d+/, "Should reference the a path: #{message.inspect}"
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def flay_self
|
46
|
+
flay = Flay.new :mass => 2
|
47
|
+
flay.process(__FILE__)
|
48
|
+
flay.analyze
|
49
|
+
|
50
|
+
FlayAdapter.new(flay)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require "minitest/autorun"
|
2
|
+
require "metric_adapter"
|
3
|
+
|
4
|
+
require "flog"
|
5
|
+
|
6
|
+
class FlogAdapterTest < MiniTest::Unit::TestCase
|
7
|
+
include MetricAdapter
|
8
|
+
|
9
|
+
def test_empty_flog
|
10
|
+
flog = Flog.new
|
11
|
+
adapter = FlogAdapter.new(flog)
|
12
|
+
|
13
|
+
assert_equal [], adapter.metrics
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_flogging_adapts_locations
|
17
|
+
adapter = flog_self
|
18
|
+
metric = adapter.metrics.first
|
19
|
+
location = metric.location
|
20
|
+
|
21
|
+
assert_equal __FILE__, location.path, "Should reference this file"
|
22
|
+
assert location.line != 0, "Line numbers should be captured (line:0 should not show up in flog)"
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_flogging_messages
|
26
|
+
adapter = flog_self
|
27
|
+
metric = adapter.metrics.first
|
28
|
+
message = metric.message
|
29
|
+
|
30
|
+
assert message =~ /flog/i, "Should reference flog: #{message.inspect}"
|
31
|
+
assert message =~ /\d+/, "Should reference the score: #{message.inspect}"
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_ignoring_unlocatable_metrics
|
35
|
+
flog = Flog.new
|
36
|
+
flog.flog_ruby top_level_sample, "sample.rb"
|
37
|
+
flog.calculate_total_scores
|
38
|
+
|
39
|
+
adapter = FlogAdapter.new(flog)
|
40
|
+
metrics = adapter.metrics
|
41
|
+
|
42
|
+
assert metrics.empty?, "Flog should not attach location information to top level source"
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
def flog_self
|
47
|
+
flog = Flog.new
|
48
|
+
flog.flog(__FILE__)
|
49
|
+
|
50
|
+
FlogAdapter.new(flog)
|
51
|
+
end
|
52
|
+
|
53
|
+
def top_level_sample
|
54
|
+
<<-RUBY
|
55
|
+
# Some good meaningless floggable source outside the scope of a method
|
56
|
+
if xs.any?{|x| x ? true : false} && x.length > 3 && x.length < 9
|
57
|
+
xs.each do |x|
|
58
|
+
(x+1).times{|xp| report(xp / 2)}
|
59
|
+
yield x-1
|
60
|
+
end
|
61
|
+
if xs.length % 2 == 0
|
62
|
+
puts xs.length % 4 == 0 ? 1 : 0
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
puts xs.join(", ")
|
67
|
+
RUBY
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require "minitest/autorun"
|
2
|
+
require "metric_adapter"
|
3
|
+
|
4
|
+
class LocationTest < MiniTest::Unit::TestCase
|
5
|
+
include MetricAdapter
|
6
|
+
|
7
|
+
def test_creating_a_location_with_line_number
|
8
|
+
path = "./test"
|
9
|
+
line = 7
|
10
|
+
location = Location.new(path, 7)
|
11
|
+
|
12
|
+
assert_equal path, location.path
|
13
|
+
assert_equal line, location.line
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_creating_a_location_with_no_number
|
17
|
+
path = "./test"
|
18
|
+
location = Location.new(path)
|
19
|
+
|
20
|
+
assert_equal path, location.path
|
21
|
+
assert_equal 0, location.line
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_creating_a_location_with_embedded_line_number
|
25
|
+
path = "./test"
|
26
|
+
line = 17
|
27
|
+
location = Location.new("#{path}:#{line}")
|
28
|
+
|
29
|
+
assert_equal path, location.path
|
30
|
+
assert_equal line, location.line
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_retains_colons
|
34
|
+
path = "yes:we_have_no_banannas"
|
35
|
+
line = 17
|
36
|
+
location = Location.new("#{path}:#{line}")
|
37
|
+
|
38
|
+
assert_equal path, location.path
|
39
|
+
assert_equal line, location.line
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_nil_path
|
43
|
+
location = Location.new(nil)
|
44
|
+
|
45
|
+
assert_equal "", location.path
|
46
|
+
assert_equal 0, location.line
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "minitest/autorun"
|
2
|
+
require "metric_adapter"
|
3
|
+
|
4
|
+
require "reek"
|
5
|
+
|
6
|
+
class ReekAdapterTest < MiniTest::Unit::TestCase
|
7
|
+
SAMPLE_PATH = 'dirty.rb'
|
8
|
+
|
9
|
+
include MetricAdapter
|
10
|
+
|
11
|
+
def test_empty_examiner
|
12
|
+
examiner = Reek::Examiner.new([])
|
13
|
+
adapter = ReekAdapter.new(examiner)
|
14
|
+
|
15
|
+
assert_equal [], adapter.metrics
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_examiner_adapts_locations
|
19
|
+
adapter = examine_sample
|
20
|
+
metric = adapter.metrics.first
|
21
|
+
location = metric.location
|
22
|
+
|
23
|
+
assert_equal SAMPLE_PATH, location.path, "Should have included the path"
|
24
|
+
assert location.line > 0, "Should have a non zero line number"
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_examiner_returns_metrics_for_each_line_of_each_smell
|
28
|
+
adapter = examine_sample
|
29
|
+
metrics = adapter.metrics
|
30
|
+
total_lines = adapter.examiner.smells.inject(0){|sum, s| sum + s.lines.length}
|
31
|
+
|
32
|
+
assert_equal total_lines, metrics.length, "Should have included a metric per line, per smell"
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def examine_sample
|
38
|
+
examiner = Reek::Examiner.new(sample)
|
39
|
+
ReekAdapter.new(examiner)
|
40
|
+
end
|
41
|
+
|
42
|
+
def sample
|
43
|
+
Reek::Source::SourceCode.new(<<-SAMPLE, SAMPLE_PATH)
|
44
|
+
# Reek Sample Code
|
45
|
+
class Dirty
|
46
|
+
def a
|
47
|
+
puts @s.title
|
48
|
+
@s = fred.map {|x| x.each {|key| key += 3}}
|
49
|
+
puts @s.title
|
50
|
+
end
|
51
|
+
end
|
52
|
+
SAMPLE
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: metric_adapter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Adam Sanderson
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2013-06-25 00:00:00 -07:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Provides a common interface for using metrics generated by tools such as Flog, Flay, and Reek.
|
22
|
+
email: netghost@gmail.com
|
23
|
+
executables: []
|
24
|
+
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README.markdown
|
29
|
+
files:
|
30
|
+
- lib/adapters/flay_adapter.rb
|
31
|
+
- lib/adapters/flog_adapter.rb
|
32
|
+
- lib/adapters/reek_adapter.rb
|
33
|
+
- lib/location.rb
|
34
|
+
- lib/metric.rb
|
35
|
+
- lib/metric_adapter.rb
|
36
|
+
- examples/analyzer_mixin.rb
|
37
|
+
- examples/annotate.rb
|
38
|
+
- examples/report.rb
|
39
|
+
- test/flay_adapter_test.rb
|
40
|
+
- test/flog_adapter_test.rb
|
41
|
+
- test/location_test.rb
|
42
|
+
- test/reek_adapter_test.rb
|
43
|
+
- README.markdown
|
44
|
+
has_rdoc: true
|
45
|
+
homepage: https://github.com/adamsanderson/metric_adapter
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options:
|
50
|
+
- --main
|
51
|
+
- README.markdown
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
none: false
|
56
|
+
requirements:
|
57
|
+
- - ">="
|
58
|
+
- !ruby/object:Gem::Version
|
59
|
+
segments:
|
60
|
+
- 0
|
61
|
+
version: "0"
|
62
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
segments:
|
68
|
+
- 0
|
69
|
+
version: "0"
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
rubyforge_project:
|
73
|
+
rubygems_version: 1.3.7
|
74
|
+
signing_key:
|
75
|
+
specification_version: 3
|
76
|
+
summary: Common interface for multiple static analyzers metrics.
|
77
|
+
test_files: []
|
78
|
+
|