metric_abc 0.0.2
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/.gitignore +1 -0
- data/README +29 -0
- data/Rakefile +13 -0
- data/VERSION +1 -0
- data/bin/metric_abc +15 -0
- data/lib/metric_abc.rb +51 -0
- data/metric_abc.gemspec +55 -0
- data/spec/fixtures/example.rb +49 -0
- data/spec/fixtures/not_valid.rb +3 -0
- data/spec/functional/metric_abc_spec.rb +37 -0
- data/spec/spec_helper.rb +12 -0
- metadata +77 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
pkg/*
|
data/README
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
Metric ABC
|
2
|
+
==========
|
3
|
+
|
4
|
+
This little tool calculates ABC metric for your Ruby code. Requires Ruby 1.9 but should work more or less with sources compatible with 1.9 and 1.8.
|
5
|
+
|
6
|
+
Installation
|
7
|
+
============
|
8
|
+
|
9
|
+
$ gem install metric_abc
|
10
|
+
|
11
|
+
Usage
|
12
|
+
=====
|
13
|
+
|
14
|
+
$ metric_abc filename1 [filename2] ...
|
15
|
+
|
16
|
+
Understanding output
|
17
|
+
====================
|
18
|
+
|
19
|
+
Output from metric_abc is in form of:
|
20
|
+
|
21
|
+
/path/to/file.rb#Module1#Module2#ClassName#method_name: SCORE
|
22
|
+
|
23
|
+
where SCORE is a number. The higher SCORE is, the more crazy code is. If a method scores above 20, it very likely needs some refactoring because it's complete shit.
|
24
|
+
|
25
|
+
TODO:
|
26
|
+
=====
|
27
|
+
|
28
|
+
- method is not always properly recognized, sometimes it's full of shit like #@const#1#5#@const etc. Need to look at AST to figure out what's wrong
|
29
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
begin
|
2
|
+
require 'jeweler'
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
4
|
+
gemspec.name = "metric_abc"
|
5
|
+
gemspec.summary = "Quick and *DIRTY* ABC metric for Ruby 1.9 code"
|
6
|
+
gemspec.description = "Calculates ABC metric for Ruby 1.9. Just like Flog but much more dull."
|
7
|
+
gemspec.email = "hubert.lepick@amberbit.com"
|
8
|
+
gemspec.homepage = "http://github.com/hubertlepicki/metric_abc"
|
9
|
+
gemspec.authors = ["Hubert Lepicki"]
|
10
|
+
end
|
11
|
+
rescue LoadError
|
12
|
+
puts "Jeweler not available. Install it with: gem install jeweler"
|
13
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.2
|
data/bin/metric_abc
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "..", "lib", 'metric_abc')
|
4
|
+
|
5
|
+
if ARGV.empty?
|
6
|
+
puts "USAGE:\nmetric_abc filename [filename] ..."
|
7
|
+
end
|
8
|
+
|
9
|
+
ARGV.collect do |filename|
|
10
|
+
MetricABC.new(filename).complexity
|
11
|
+
end.inject({}) do |merged, current|
|
12
|
+
merged.merge(current)
|
13
|
+
end.sort{|a,b| b[1]<=>a[1]}.each do |function, score|
|
14
|
+
puts "#{function}: #{score}"
|
15
|
+
end
|
data/lib/metric_abc.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'ripper'
|
2
|
+
require 'pp' if ENV["DEBUG"]
|
3
|
+
|
4
|
+
class MetricABC
|
5
|
+
attr_accessor :ast, :complexity
|
6
|
+
|
7
|
+
def initialize(file_name)
|
8
|
+
File.open(file_name, "r") do |f|
|
9
|
+
@ast = Ripper::SexpBuilder.new(f.read).parse
|
10
|
+
end
|
11
|
+
return if @ast.empty?
|
12
|
+
@complexity = {}
|
13
|
+
@nesting = [file_name]
|
14
|
+
process_ast(@ast)
|
15
|
+
pp @ast if ENV["DEBUG"]
|
16
|
+
end
|
17
|
+
|
18
|
+
def process_ast(node)
|
19
|
+
backup_nesting = @nesting.clone
|
20
|
+
|
21
|
+
if node[0] == :def
|
22
|
+
@nesting << node[1][1]
|
23
|
+
@complexity[@nesting.join("#")] = calculate_abc(node)
|
24
|
+
elsif node[0] == :class || node[0] == :const
|
25
|
+
@nesting << node[1][1][1]
|
26
|
+
end
|
27
|
+
|
28
|
+
node[1..-1].each { |n| process_ast(n) if n } if node.is_a? Array
|
29
|
+
@nesting = backup_nesting
|
30
|
+
end
|
31
|
+
|
32
|
+
def calculate_abc(method_node)
|
33
|
+
a = calculate_assignments(method_node)
|
34
|
+
b = calculate_branches(method_node)
|
35
|
+
c = calculate_conditions(method_node)
|
36
|
+
abc = Math.sqrt(a**2 + b**2 + c**2).round
|
37
|
+
abc
|
38
|
+
end
|
39
|
+
|
40
|
+
def calculate_assignments(node)
|
41
|
+
node.flatten.select{|n| [:assign, :opassign].include?(n)}.size.to_f
|
42
|
+
end
|
43
|
+
|
44
|
+
def calculate_branches(node)
|
45
|
+
node.flatten.select{|n| [:call, :fcall, :brace_block, :do_block].include?(n)}.size.to_f + 1.0
|
46
|
+
end
|
47
|
+
|
48
|
+
def calculate_conditions(node, sum=0)
|
49
|
+
node.flatten.select{|n| [:==, :===, :"<>", :"<=", :">=", :"=~", :>, :<, :else, :"<=>"].include?(n)}.size.to_f
|
50
|
+
end
|
51
|
+
end
|
data/metric_abc.gemspec
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{metric_abc}
|
8
|
+
s.version = "0.0.2"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Hubert Lepicki"]
|
12
|
+
s.date = %q{2010-08-24}
|
13
|
+
s.default_executable = %q{metric_abc}
|
14
|
+
s.description = %q{Calculates ABC metric for Ruby 1.9. Just like Flog but much more dull.}
|
15
|
+
s.email = %q{hubert.lepick@amberbit.com}
|
16
|
+
s.executables = ["metric_abc"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"README"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
".gitignore",
|
22
|
+
"README",
|
23
|
+
"Rakefile",
|
24
|
+
"VERSION",
|
25
|
+
"bin/metric_abc",
|
26
|
+
"lib/metric_abc.rb",
|
27
|
+
"metric_abc.gemspec",
|
28
|
+
"spec/fixtures/example.rb",
|
29
|
+
"spec/fixtures/not_valid.rb",
|
30
|
+
"spec/functional/metric_abc_spec.rb",
|
31
|
+
"spec/spec_helper.rb"
|
32
|
+
]
|
33
|
+
s.homepage = %q{http://github.com/hubertlepicki/metric_abc}
|
34
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
s.rubygems_version = %q{1.3.7}
|
37
|
+
s.summary = %q{Quick and *DIRTY* ABC metric for Ruby 1.9 code}
|
38
|
+
s.test_files = [
|
39
|
+
"spec/fixtures/example.rb",
|
40
|
+
"spec/fixtures/not_valid.rb",
|
41
|
+
"spec/spec_helper.rb",
|
42
|
+
"spec/functional/metric_abc_spec.rb"
|
43
|
+
]
|
44
|
+
|
45
|
+
if s.respond_to? :specification_version then
|
46
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
47
|
+
s.specification_version = 3
|
48
|
+
|
49
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
50
|
+
else
|
51
|
+
end
|
52
|
+
else
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
def outside_function
|
2
|
+
a = 5
|
3
|
+
end
|
4
|
+
|
5
|
+
module Test
|
6
|
+
class Test2
|
7
|
+
def class_in_module_function
|
8
|
+
a=1
|
9
|
+
b=2
|
10
|
+
a += 1
|
11
|
+
b *= 2
|
12
|
+
5
|
13
|
+
[1,2,3].each do |element|
|
14
|
+
puts element
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
class Example
|
20
|
+
attr_accessor :something
|
21
|
+
def simple_function
|
22
|
+
return 0
|
23
|
+
end
|
24
|
+
def medium_function(param1, param2=whatever)
|
25
|
+
a = simple_function()
|
26
|
+
if param1 == nil
|
27
|
+
a = asdfaf()
|
28
|
+
elsif param1 < 4
|
29
|
+
elsif param1 > 3
|
30
|
+
elsif param2 <=2
|
31
|
+
elsif param3() >=3
|
32
|
+
else
|
33
|
+
puts 'test'
|
34
|
+
end
|
35
|
+
a
|
36
|
+
end
|
37
|
+
def complex_function(param1, param2=whatever)
|
38
|
+
while param1 > 0
|
39
|
+
for i=0; i<=7; i++
|
40
|
+
param2.collect{|c| c+2}.inject("+")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
if something == param1 && tewww != asdf || a === Object
|
44
|
+
Array.new(a + b)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe MetricABC, "initialization" do
|
4
|
+
|
5
|
+
it "should read passed file and generate AST for it on initialization" do
|
6
|
+
metric = MetricABC.new(File.join(File.dirname(__FILE__), "..", "fixtures", "example.rb"))
|
7
|
+
metric.ast.should be_instance_of(Array)
|
8
|
+
metric.ast.should_not be_empty
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should throw exception when file is not found" do
|
12
|
+
lambda{ MetricABC.new(File.join(File.dirname(__FILE__), "..", "fixtures", "not_found.rb"))}.should raise_exception
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
describe MetricABC, "calculating" do
|
18
|
+
before :each do
|
19
|
+
@metric = MetricABC.new(File.join(File.dirname(__FILE__), "..", "fixtures", "example.rb"))
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should have 1 complexity for simple function" do
|
23
|
+
@metric.complexity["Example#simple_function"].should eql(1)
|
24
|
+
end
|
25
|
+
|
26
|
+
it "should recognize top level functions" do
|
27
|
+
@metric.complexity["outside_function"].should_not be_nil
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should recognize modules functions" do
|
31
|
+
@metric.complexity["Test#Test2#class_in_module_function"].should_not be_nil
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should have complexity of 7 for medium method" do
|
35
|
+
@metric.complexity["Example#medium_function"].should eql(7)
|
36
|
+
end
|
37
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: metric_abc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 2
|
9
|
+
version: 0.0.2
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Hubert Lepicki
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2010-08-24 00:00:00 +02:00
|
18
|
+
default_executable: metric_abc
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: Calculates ABC metric for Ruby 1.9. Just like Flog but much more dull.
|
22
|
+
email: hubert.lepick@amberbit.com
|
23
|
+
executables:
|
24
|
+
- metric_abc
|
25
|
+
extensions: []
|
26
|
+
|
27
|
+
extra_rdoc_files:
|
28
|
+
- README
|
29
|
+
files:
|
30
|
+
- .gitignore
|
31
|
+
- README
|
32
|
+
- Rakefile
|
33
|
+
- VERSION
|
34
|
+
- bin/metric_abc
|
35
|
+
- lib/metric_abc.rb
|
36
|
+
- metric_abc.gemspec
|
37
|
+
- spec/fixtures/example.rb
|
38
|
+
- spec/fixtures/not_valid.rb
|
39
|
+
- spec/functional/metric_abc_spec.rb
|
40
|
+
- spec/spec_helper.rb
|
41
|
+
has_rdoc: true
|
42
|
+
homepage: http://github.com/hubertlepicki/metric_abc
|
43
|
+
licenses: []
|
44
|
+
|
45
|
+
post_install_message:
|
46
|
+
rdoc_options:
|
47
|
+
- --charset=UTF-8
|
48
|
+
require_paths:
|
49
|
+
- lib
|
50
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
51
|
+
none: false
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
segments:
|
56
|
+
- 0
|
57
|
+
version: "0"
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
segments:
|
64
|
+
- 0
|
65
|
+
version: "0"
|
66
|
+
requirements: []
|
67
|
+
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.3.7
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Quick and *DIRTY* ABC metric for Ruby 1.9 code
|
73
|
+
test_files:
|
74
|
+
- spec/fixtures/example.rb
|
75
|
+
- spec/fixtures/not_valid.rb
|
76
|
+
- spec/spec_helper.rb
|
77
|
+
- spec/functional/metric_abc_spec.rb
|