metric_adapter 0.0.1
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/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
|
+
|