fukuzatsu 1.0.6 → 2.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/README.md +11 -9
- data/doc/images/details.png +0 -0
- data/doc/images/overview.png +0 -0
- data/fukuzatsu.gemspec +5 -2
- data/lib/fukuzatsu.rb +18 -4
- data/lib/fukuzatsu/cli.rb +2 -15
- data/lib/fukuzatsu/file_reader.rb +38 -0
- data/lib/fukuzatsu/formatters/base.rb +61 -22
- data/lib/fukuzatsu/formatters/csv.rb +28 -29
- data/lib/fukuzatsu/formatters/html.rb +61 -66
- data/lib/fukuzatsu/formatters/html_index.rb +40 -33
- data/lib/fukuzatsu/formatters/json.rb +37 -0
- data/lib/fukuzatsu/formatters/json_index.rb +31 -0
- data/lib/fukuzatsu/formatters/templates/index.html.haml +21 -32
- data/lib/fukuzatsu/formatters/templates/output.html.haml +19 -50
- data/lib/fukuzatsu/formatters/text.rb +53 -41
- data/lib/fukuzatsu/parser.rb +31 -49
- data/lib/fukuzatsu/summary.rb +101 -0
- data/lib/fukuzatsu/version.rb +1 -1
- data/spec/cli_spec.rb +7 -40
- data/spec/fixtures/class.rb +30 -0
- data/spec/fixtures/module.rb +12 -0
- data/spec/fixtures/procedural.rb +8 -0
- data/spec/formatters/csv_spec.rb +43 -21
- data/spec/formatters/html_spec.rb +38 -19
- data/spec/formatters/json_spec.rb +81 -0
- data/spec/formatters/text_spec.rb +18 -18
- data/spec/parser_spec.rb +39 -0
- data/spec/summary_spec.rb +28 -0
- metadata +51 -43
- data/doc/details.png +0 -0
- data/doc/overview.png +0 -0
- data/lib/fukuzatsu/analyzer.rb +0 -162
- data/lib/fukuzatsu/line_of_code.rb +0 -19
- data/lib/fukuzatsu/parsed_file.rb +0 -89
- data/lib/fukuzatsu/parsed_method.rb +0 -32
- data/spec/analyzer_spec.rb +0 -122
- data/spec/fixtures/eg_class.rb +0 -8
- data/spec/fixtures/eg_mod_class.rb +0 -2
- data/spec/fixtures/eg_mod_class_2.rb +0 -5
- data/spec/fixtures/eg_module.rb +0 -2
- data/spec/fixtures/module_with_class.rb +0 -9
- data/spec/fixtures/multiple_methods.rb +0 -7
- data/spec/fixtures/nested_methods.rb +0 -8
- data/spec/fixtures/program_1.rb +0 -19
- data/spec/fixtures/program_2.rb +0 -25
- data/spec/fixtures/program_3.rb +0 -66
- data/spec/fixtures/program_4.rb +0 -1
- data/spec/fixtures/single_class.rb +0 -9
- data/spec/fixtures/single_method.rb +0 -3
- data/spec/formatters/html_index_spec.rb +0 -36
- data/spec/parsed_file_spec.rb +0 -67
- data/spec/parsed_method_spec.rb +0 -34
@@ -1,89 +0,0 @@
|
|
1
|
-
class ParsedFile
|
2
|
-
|
3
|
-
attr_accessor :lines_of_code, :source
|
4
|
-
attr_accessor :complexity, :path_to_file, :class_name, :path_to_results
|
5
|
-
|
6
|
-
def initialize(path_to_file: path_to_file, class_name: class_name=nil, complexity: complexity)
|
7
|
-
@path_to_file = path_to_file
|
8
|
-
@class_name = class_name
|
9
|
-
@lines_of_code = []
|
10
|
-
@complexity = complexity
|
11
|
-
@source = parse!
|
12
|
-
end
|
13
|
-
|
14
|
-
def class_name
|
15
|
-
@class_name ||= analyzer.class_name
|
16
|
-
end
|
17
|
-
|
18
|
-
def class_references
|
19
|
-
@class_references ||= analyzer.constants
|
20
|
-
end
|
21
|
-
|
22
|
-
def content
|
23
|
-
@content ||= File.open(path_to_file, "r").read
|
24
|
-
end
|
25
|
-
|
26
|
-
def average_complexity
|
27
|
-
methods.map(&:complexity).reduce(:+) / methods.count.to_f
|
28
|
-
end
|
29
|
-
|
30
|
-
def complexity
|
31
|
-
@complexity ||= analyzer.complexity
|
32
|
-
end
|
33
|
-
|
34
|
-
def methods
|
35
|
-
@methods ||= analyzer.methods
|
36
|
-
end
|
37
|
-
|
38
|
-
def method_counts
|
39
|
-
referenced_methods = methods.map(&:references).flatten
|
40
|
-
referenced_methods.inject({}) do |hash, method|
|
41
|
-
hash[method] ||= 0
|
42
|
-
hash[method] += 1
|
43
|
-
hash
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def source
|
48
|
-
return @source if @source
|
49
|
-
end_pos = 0
|
50
|
-
self.lines_of_code = []
|
51
|
-
@source = File.readlines(self.path_to_file).each_with_index do |line, index|
|
52
|
-
start_pos = end_pos + 1
|
53
|
-
end_pos += line.size
|
54
|
-
self.lines_of_code << LineOfCode.new(line_number: index + 1, range: (start_pos..end_pos))
|
55
|
-
line
|
56
|
-
end.join
|
57
|
-
end
|
58
|
-
|
59
|
-
def summary
|
60
|
-
{
|
61
|
-
path_to_file: self.path_to_file,
|
62
|
-
results_file: self.path_to_results,
|
63
|
-
source: source,
|
64
|
-
class_name: self.class_name,
|
65
|
-
complexity: complexity
|
66
|
-
}
|
67
|
-
end
|
68
|
-
|
69
|
-
private
|
70
|
-
|
71
|
-
def analyzer
|
72
|
-
@analyzer ||= Analyzer.new(content)
|
73
|
-
end
|
74
|
-
|
75
|
-
def content
|
76
|
-
@content ||= File.open(path_to_file, "r").read
|
77
|
-
end
|
78
|
-
|
79
|
-
def parse!
|
80
|
-
end_pos = 0
|
81
|
-
File.readlines(self.path_to_file).each_with_index do |line, index|
|
82
|
-
start_pos = end_pos + 1
|
83
|
-
end_pos += line.size
|
84
|
-
self.lines_of_code << LineOfCode.new(line_number: index + 1, range: (start_pos..end_pos))
|
85
|
-
line
|
86
|
-
end.join
|
87
|
-
end
|
88
|
-
|
89
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
class ParsedMethod
|
2
|
-
|
3
|
-
attr_reader :name, :content, :type, :complexity, :references
|
4
|
-
|
5
|
-
def initialize(name: name, content: content, type: type, refs: refs=[], complexity: complexity)
|
6
|
-
@name = name
|
7
|
-
@content = content
|
8
|
-
@type = type
|
9
|
-
@references = refs
|
10
|
-
@complexity = complexity
|
11
|
-
end
|
12
|
-
|
13
|
-
def complexity
|
14
|
-
@complexity ||= analyzer.complexity
|
15
|
-
end
|
16
|
-
|
17
|
-
def name
|
18
|
-
return "" if self.type == :none
|
19
|
-
"#{prefix}#{@name}"
|
20
|
-
end
|
21
|
-
|
22
|
-
def prefix
|
23
|
-
return "." if self.type == :class
|
24
|
-
return "#" if self.type == :instance
|
25
|
-
return "*"
|
26
|
-
end
|
27
|
-
|
28
|
-
def analyzer
|
29
|
-
Analyzer.new(self.content)
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
data/spec/analyzer_spec.rb
DELETED
@@ -1,122 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe Analyzer do
|
4
|
-
|
5
|
-
let(:content_1) { File.read("spec/fixtures/program_1.rb") }
|
6
|
-
let(:content_2) { File.read("spec/fixtures/program_2.rb") }
|
7
|
-
let(:content_3) { File.read("spec/fixtures/eg_class.rb") }
|
8
|
-
let(:content_4) { File.read("spec/fixtures/eg_mod_class.rb") }
|
9
|
-
let(:content_5) { File.read("spec/fixtures/eg_module.rb") }
|
10
|
-
let(:content_6) { File.read("spec/fixtures/eg_mod_class_2.rb") }
|
11
|
-
|
12
|
-
context "#extract_class_name" do
|
13
|
-
|
14
|
-
context "from Class Foo" do
|
15
|
-
it "returns Foo" do
|
16
|
-
expect(Analyzer.new(content_3).extract_class_name).to eq("Foo")
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
context "from Module::Class Foo" do
|
21
|
-
it "returns Foo::Bar" do
|
22
|
-
expect(Analyzer.new(content_4).extract_class_name).to eq("Foo::Bar")
|
23
|
-
end
|
24
|
-
end
|
25
|
-
|
26
|
-
context "from Module; Class" do
|
27
|
-
it "returns Extracted::Thing" do
|
28
|
-
expect(Analyzer.new(content_6).extract_class_name).to eq("Extracted::Thing")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context "from Module" do
|
33
|
-
it "returns Something" do
|
34
|
-
expect(Analyzer.new(content_5).extract_class_name).to eq("Something")
|
35
|
-
end
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
describe "#complexity" do
|
40
|
-
context "program_1" do
|
41
|
-
|
42
|
-
let(:analyzer) { Analyzer.new(content_1) }
|
43
|
-
|
44
|
-
it "matches the manual calculation of complexity of 4" do
|
45
|
-
expect(analyzer.complexity).to eq(4)
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
|
50
|
-
context "program_2" do
|
51
|
-
|
52
|
-
let(:analyzer) { Analyzer.new(content_2) }
|
53
|
-
|
54
|
-
it "matches the manual calculation of complexity of 5" do
|
55
|
-
expect(analyzer.complexity).to eq(5)
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
describe "extract_class_name" do
|
62
|
-
context "from a file with a class in it" do
|
63
|
-
let(:analyzer) { Analyzer.new(File.read("spec/fixtures/single_class.rb")) }
|
64
|
-
it "should return the name of the class" do
|
65
|
-
expect(analyzer.extract_class_name).to eq "Gown"
|
66
|
-
end
|
67
|
-
end
|
68
|
-
context "from a file with a class inside a module" do
|
69
|
-
let(:analyzer) { Analyzer.new(File.read("spec/fixtures/module_with_class.rb")) }
|
70
|
-
it "should return the name of the class" do
|
71
|
-
expect(analyzer.extract_class_name).to eq "Symbolics::Insects::Bee"
|
72
|
-
end
|
73
|
-
end
|
74
|
-
context "from a file with no class in it" do
|
75
|
-
let(:analyzer) { Analyzer.new(File.read("spec/fixtures/single_method.rb")) }
|
76
|
-
it "should return 'Unknown'" do
|
77
|
-
expect(analyzer.extract_class_name).to eq "Unknown"
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
describe "extract_methods" do
|
84
|
-
# Note: should implicitly trust private method #methods_from
|
85
|
-
context "from a file with a single method" do
|
86
|
-
let(:analyzer) { Analyzer.new(File.read("spec/fixtures/single_method.rb")) }
|
87
|
-
it "should return a single method" do
|
88
|
-
expect(analyzer.extract_methods.count).to eq 1
|
89
|
-
end
|
90
|
-
it "should extract the method name" do
|
91
|
-
expect(analyzer.extract_methods[0].name).to eq "#read_poem"
|
92
|
-
end
|
93
|
-
it "should extract the method content" do
|
94
|
-
expect(analyzer.extract_methods[0].content).to eq 'def read_poem
|
95
|
-
return "I meant to find her when I came/Death had the same design"
|
96
|
-
end'
|
97
|
-
end
|
98
|
-
it "should set type to :instance" do
|
99
|
-
expect(analyzer.extract_methods[0].type).to eq :instance
|
100
|
-
end
|
101
|
-
end
|
102
|
-
context "from a file with multiple methods" do
|
103
|
-
let(:analyzer) { Analyzer.new(File.read("spec/fixtures/multiple_methods.rb")) }
|
104
|
-
it "should return multiple methods" do
|
105
|
-
expect(analyzer.extract_methods.map { |m| m.name }).to eq ["#bake_treats", "#lower_from_window"]
|
106
|
-
end
|
107
|
-
end
|
108
|
-
context "from a file with nested methods" do
|
109
|
-
let(:analyzer) { Analyzer.new(File.read("spec/fixtures/nested_methods.rb")) }
|
110
|
-
it "should return the root method, and its child" do
|
111
|
-
expect(analyzer.extract_methods.map { |m| m.name }).to eq ["#grow_flowers", "#water_earth"]
|
112
|
-
end
|
113
|
-
end
|
114
|
-
context "from a file with a class" do
|
115
|
-
let(:analyzer) { Analyzer.new(File.read("spec/fixtures/single_class.rb")) }
|
116
|
-
it "should return the class and its methods" do
|
117
|
-
expect(analyzer.extract_methods.map { |m| m.name }).to eq ["#initialize", "#color"]
|
118
|
-
end
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
end
|
data/spec/fixtures/eg_class.rb
DELETED
data/spec/fixtures/eg_module.rb
DELETED
data/spec/fixtures/program_1.rb
DELETED
data/spec/fixtures/program_2.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
def toppings
|
2
|
-
|
3
|
-
if rand(10) < 5
|
4
|
-
return "meatball"
|
5
|
-
end
|
6
|
-
|
7
|
-
if 1 < 5
|
8
|
-
return 'tomato'
|
9
|
-
else
|
10
|
-
if 4 ==5
|
11
|
-
if 2 > 0
|
12
|
-
return "parmesan"
|
13
|
-
else
|
14
|
-
return "romano"
|
15
|
-
end
|
16
|
-
else
|
17
|
-
if 2 > 0
|
18
|
-
return "alfredo"
|
19
|
-
else
|
20
|
-
return "gravy"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
end
|
data/spec/fixtures/program_3.rb
DELETED
@@ -1,66 +0,0 @@
|
|
1
|
-
require 'fukuzatsu'
|
2
|
-
|
3
|
-
class Breathalizer
|
4
|
-
|
5
|
-
INDICATORS = [
|
6
|
-
:if,
|
7
|
-
:def,
|
8
|
-
]
|
9
|
-
|
10
|
-
attr_accessor :path_to_file, :edges, :nodes, :exits, :accumulator
|
11
|
-
|
12
|
-
def self.parse!(path_to_file)
|
13
|
-
new(path_to_file).parse
|
14
|
-
end
|
15
|
-
|
16
|
-
def initialize(path_to_file)
|
17
|
-
self.accumulator = []
|
18
|
-
self.edges = 0
|
19
|
-
self.nodes = 1
|
20
|
-
self.exits = 1
|
21
|
-
self.path_to_file = path_to_file
|
22
|
-
end
|
23
|
-
|
24
|
-
def file_contents
|
25
|
-
contents ||= File.open(path_to_file, "r").read
|
26
|
-
end
|
27
|
-
|
28
|
-
def parsed
|
29
|
-
@parsed ||= Parser::CurrentRuby.parse(file_contents)
|
30
|
-
end
|
31
|
-
|
32
|
-
def parse!
|
33
|
-
traverse(parsed)
|
34
|
-
complexity
|
35
|
-
end
|
36
|
-
|
37
|
-
def traverse(node)#, accumulator=[])
|
38
|
-
|
39
|
-
accumulator << node.type
|
40
|
-
|
41
|
-
if node.type == :if || node.type == :begin
|
42
|
-
self.edges += 2
|
43
|
-
self.nodes += 2
|
44
|
-
self.exits += 1
|
45
|
-
elsif node.type == :def
|
46
|
-
self.edges += 1
|
47
|
-
self.nodes += 1
|
48
|
-
self.exits += 1
|
49
|
-
end
|
50
|
-
|
51
|
-
node.children.each do |child|
|
52
|
-
if child.respond_to?(:type) || child.respond_to?(:children)
|
53
|
-
accumulator << child.type
|
54
|
-
traverse(child, accumulator)
|
55
|
-
else
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
def complexity
|
61
|
-
p "edges = #{self.edges}, nodes = #{self.nodes}, exits = #{self.exits}"
|
62
|
-
self.edges - self.nodes + exits
|
63
|
-
end
|
64
|
-
|
65
|
-
end
|
66
|
-
|
data/spec/fixtures/program_4.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
program_4.rb
|
@@ -1,36 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
describe "Formatters::HtmlIndex" do
|
4
|
-
|
5
|
-
let(:file_summary) {
|
6
|
-
[
|
7
|
-
{
|
8
|
-
results_file: "doc/fukuzatsu/file_1.rb.htm",
|
9
|
-
path_to_file: "file_1.rb",
|
10
|
-
class_name: "File_1",
|
11
|
-
complexity: 13
|
12
|
-
},
|
13
|
-
{
|
14
|
-
results_file: "doc/fukuzatsu/file_2.rb.htm",
|
15
|
-
path_to_file: "file_2.rb",
|
16
|
-
class_name: "File_2",
|
17
|
-
complexity: 11
|
18
|
-
}
|
19
|
-
]
|
20
|
-
}
|
21
|
-
|
22
|
-
let (:formatter) { Formatters::HtmlIndex.new(file_summary) }
|
23
|
-
|
24
|
-
describe "#initialize" do
|
25
|
-
it "initializes with a file summary" do
|
26
|
-
expect(formatter.file_summary).to eq file_summary
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe "#filename" do
|
31
|
-
it "returns the expected filename" do
|
32
|
-
expect(formatter.filename).to eq "index.htm"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|