fukuzatsu 1.0.6 → 2.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -1
  3. data/README.md +11 -9
  4. data/doc/images/details.png +0 -0
  5. data/doc/images/overview.png +0 -0
  6. data/fukuzatsu.gemspec +5 -2
  7. data/lib/fukuzatsu.rb +18 -4
  8. data/lib/fukuzatsu/cli.rb +2 -15
  9. data/lib/fukuzatsu/file_reader.rb +38 -0
  10. data/lib/fukuzatsu/formatters/base.rb +61 -22
  11. data/lib/fukuzatsu/formatters/csv.rb +28 -29
  12. data/lib/fukuzatsu/formatters/html.rb +61 -66
  13. data/lib/fukuzatsu/formatters/html_index.rb +40 -33
  14. data/lib/fukuzatsu/formatters/json.rb +37 -0
  15. data/lib/fukuzatsu/formatters/json_index.rb +31 -0
  16. data/lib/fukuzatsu/formatters/templates/index.html.haml +21 -32
  17. data/lib/fukuzatsu/formatters/templates/output.html.haml +19 -50
  18. data/lib/fukuzatsu/formatters/text.rb +53 -41
  19. data/lib/fukuzatsu/parser.rb +31 -49
  20. data/lib/fukuzatsu/summary.rb +101 -0
  21. data/lib/fukuzatsu/version.rb +1 -1
  22. data/spec/cli_spec.rb +7 -40
  23. data/spec/fixtures/class.rb +30 -0
  24. data/spec/fixtures/module.rb +12 -0
  25. data/spec/fixtures/procedural.rb +8 -0
  26. data/spec/formatters/csv_spec.rb +43 -21
  27. data/spec/formatters/html_spec.rb +38 -19
  28. data/spec/formatters/json_spec.rb +81 -0
  29. data/spec/formatters/text_spec.rb +18 -18
  30. data/spec/parser_spec.rb +39 -0
  31. data/spec/summary_spec.rb +28 -0
  32. metadata +51 -43
  33. data/doc/details.png +0 -0
  34. data/doc/overview.png +0 -0
  35. data/lib/fukuzatsu/analyzer.rb +0 -162
  36. data/lib/fukuzatsu/line_of_code.rb +0 -19
  37. data/lib/fukuzatsu/parsed_file.rb +0 -89
  38. data/lib/fukuzatsu/parsed_method.rb +0 -32
  39. data/spec/analyzer_spec.rb +0 -122
  40. data/spec/fixtures/eg_class.rb +0 -8
  41. data/spec/fixtures/eg_mod_class.rb +0 -2
  42. data/spec/fixtures/eg_mod_class_2.rb +0 -5
  43. data/spec/fixtures/eg_module.rb +0 -2
  44. data/spec/fixtures/module_with_class.rb +0 -9
  45. data/spec/fixtures/multiple_methods.rb +0 -7
  46. data/spec/fixtures/nested_methods.rb +0 -8
  47. data/spec/fixtures/program_1.rb +0 -19
  48. data/spec/fixtures/program_2.rb +0 -25
  49. data/spec/fixtures/program_3.rb +0 -66
  50. data/spec/fixtures/program_4.rb +0 -1
  51. data/spec/fixtures/single_class.rb +0 -9
  52. data/spec/fixtures/single_method.rb +0 -3
  53. data/spec/formatters/html_index_spec.rb +0 -36
  54. data/spec/parsed_file_spec.rb +0 -67
  55. 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
@@ -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
@@ -1,8 +0,0 @@
1
- require 'fukuzatsu'
2
-
3
- class Foo
4
- def say_hello
5
- "Oh hi there."
6
- end
7
- end
8
-
@@ -1,2 +0,0 @@
1
- class Foo::Bar
2
- end
@@ -1,5 +0,0 @@
1
- module Extracted
2
- class Thing
3
- end
4
- end
5
-
@@ -1,2 +0,0 @@
1
- module Something
2
- end
@@ -1,9 +0,0 @@
1
- module Symbolics
2
- module Insects
3
- class Bee
4
- def correspond(letter)
5
- @letter = letter
6
- end
7
- end
8
- end
9
- end
@@ -1,7 +0,0 @@
1
- def bake_treats
2
- return :a_basket_of_cookies
3
- end
4
-
5
- def lower_from_window(window)
6
- window.insert(bake_treats)
7
- end
@@ -1,8 +0,0 @@
1
- def grow_flowers(seed, water, earth)
2
- def water_earth(water, earth)
3
- earth.insert(water)
4
- end
5
-
6
- earth.insert.seed
7
- 50.times(water_earth(water, earth))
8
- end
@@ -1,19 +0,0 @@
1
- def spaghetti
2
- if 1 < 5
3
- p 'hi'
4
- else
5
- if 4 ==5
6
- if 2 > 0
7
- p 'bye'
8
- else
9
- p 'see ya'
10
- end
11
- else
12
- if 2 > 0
13
- p 'bye'
14
- else
15
- p 'see ya'
16
- end
17
- end
18
- end
19
- end
@@ -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
@@ -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
-
@@ -1 +0,0 @@
1
- program_4.rb
@@ -1,9 +0,0 @@
1
- class Gown
2
- def initialize
3
- @color = "White"
4
- end
5
-
6
- def color
7
- return @color
8
- end
9
- end
@@ -1,3 +0,0 @@
1
- def read_poem
2
- return "I meant to find her when I came/Death had the same design"
3
- end
@@ -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