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
@@ -0,0 +1,101 @@
1
+ module Fukuzatsu
2
+
3
+ class Summary
4
+
5
+ attr_reader :entity, :source_file, :container
6
+ attr_accessor :edges, :nodes, :exits, :summaries
7
+
8
+ BRANCHES = [
9
+ :if,
10
+ :or_asgn,
11
+ :and_sgn,
12
+ :or,
13
+ :and
14
+ ]
15
+
16
+ def self.from(content:, source_file:nil)
17
+ parser = Analyst.for_source(content)
18
+
19
+ containers = parser.top_level_entities.select{|e| e.respond_to? :all_methods}
20
+ containers << containers.map(&:classes)
21
+ containers.flatten!.reject!{ |container| container.all_methods.empty? }
22
+
23
+ containers.map do |container|
24
+ summary = Fukuzatsu::Summary.new(
25
+ container: container,
26
+ source: container.send(:source),
27
+ entity: container,
28
+ source_file: source_file
29
+ )
30
+ summary.summaries = container.all_methods.map do |method|
31
+ Fukuzatsu::Summary.new(
32
+ container: container,
33
+ source: method.send(:source),
34
+ entity: method,
35
+ source_file: source_file
36
+ )
37
+ end
38
+ summary
39
+ end
40
+ end
41
+
42
+ def initialize(source:, entity:, container:, source_file:nil, summaries:[])
43
+ @source = source
44
+ @entity = entity
45
+ @container = container
46
+ @source_file = source_file
47
+ @summaries = summaries
48
+ @edges, @nodes, @exits = [0, 1, 1]
49
+ end
50
+
51
+ def complexity
52
+ @complexity ||= begin
53
+ traverse(self.entity.ast)
54
+ self.edges - self.nodes + self.exits
55
+ end
56
+ end
57
+
58
+ def container_name
59
+ self.container.full_name
60
+ end
61
+
62
+ def is_class_or_module?
63
+ self.container == self.entity
64
+ end
65
+
66
+ def entity_name
67
+ return "*" if is_class_or_module?
68
+ self.entity.full_name.gsub(self.container.full_name, '')
69
+ end
70
+
71
+ def average_complexity
72
+ return 0 if self.summaries.blank?
73
+ self.summaries.map(&:complexity).reduce(&:+) / self.summaries.count.to_f
74
+ end
75
+
76
+ def raw_source
77
+ self.source_file && "\n#{File.open(self.source_file, "r").read}" || @source
78
+ end
79
+
80
+ private
81
+
82
+ def extend_graph
83
+ self.edges += 2
84
+ self.nodes += 2
85
+ self.exits += 1
86
+ end
87
+
88
+ def traverse(node, accumulator=[])
89
+ accumulator << node.type
90
+ extend_graph if BRANCHES.include?(node.type)
91
+ node.children.each do |child|
92
+ if child.respond_to?(:children) || child.respond_to?(:type)
93
+ accumulator << child.type
94
+ traverse(child, accumulator)
95
+ end
96
+ end
97
+ accumulator
98
+ end
99
+ end
100
+
101
+ end
@@ -1,3 +1,3 @@
1
1
  module Fukuzatsu
2
- VERSION = "1.0.6"
2
+ VERSION = "2.1.1"
3
3
  end
@@ -1,48 +1,15 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Fukuzatsu::CLI" do
3
+ describe Fukuzatsu::CLI do
4
4
 
5
- let(:cli) { Fukuzatsu::CLI.new([
6
- path: "foo/bar.rb",
7
- format: 'text'
8
- ]
9
- )}
5
+ let(:cli) { Fukuzatsu::CLI.new }
10
6
 
11
- let(:summary_1) {
12
- {
13
- results_file: "doc/fukuzatsu/file_1.rb.htm",
14
- path_to_file: "file_1.rb",
15
- class_name: "File_1",
16
- complexity: 13
17
- }
18
- }
19
-
20
- let(:summary_2) {
21
- {
22
- results_file: "doc/fukuzatsu/file_2.rb.htm",
23
- path_to_file: "file_2.rb",
24
- class_name: "File_2",
25
- complexity: 11
26
- }
27
- }
28
-
29
- before do
30
- allow(cli).to receive(:summaries) { [summary_1, summary_2] }
31
- end
32
-
33
- describe "#formatter" do
34
- it "returns a csv formatter" do
35
- allow(cli).to receive(:options) { {'format' => 'csv'} }
36
- expect(cli.send(:formatter)).to eq Formatters::Csv
37
- end
38
- it "returns an html formatter" do
39
- allow(cli).to receive(:options) { {'format' => 'html'} }
40
- expect(cli.send(:formatter)).to eq Formatters::Html
41
- end
42
- it "returns a text formatter" do
43
- allow(cli).to receive(:options) { {'format' => 'text'} }
44
- expect(cli.send(:formatter)).to eq Formatters::Text
7
+ describe "#check" do
8
+ it "invokes Fukuzatsu" do
9
+ expect(Fukuzatsu).to receive(:new) { Struct.new(:report).new("OK") }
10
+ cli.check("./fixtures")
45
11
  end
46
12
  end
47
13
 
48
14
  end
15
+
@@ -0,0 +1,30 @@
1
+ # complexity:5
2
+ class Spaghetti
3
+
4
+ def toppings
5
+
6
+ if rand(10) < 5
7
+ return "meatball"
8
+ end
9
+
10
+ if 1 < 5
11
+ return 'tomato'
12
+ else
13
+ if 4 ==5
14
+ if 2 > 0
15
+ return "parmesan"
16
+ else
17
+ return "romano"
18
+ end
19
+ else
20
+ if 2 > 0
21
+ return "alfredo"
22
+ else
23
+ return "gravy"
24
+ end
25
+ end
26
+ end
27
+
28
+ end
29
+
30
+ end
@@ -0,0 +1,12 @@
1
+ # complexity:1
2
+ module HasToppings
3
+
4
+ def random_topping
5
+ if rand(10) < 5
6
+ return "meatball"
7
+ else
8
+ return "pepperoni"
9
+ end
10
+ end
11
+
12
+ end
@@ -0,0 +1,8 @@
1
+ # complexity:2
2
+ def spaghetti
3
+ if 1 < 5
4
+ p 'hi'
5
+ else
6
+ "hello" || "goodbye"
7
+ end
8
+ end
@@ -1,36 +1,58 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Formatters::Csv" do
3
+ describe Fukuzatsu::Formatters::Csv do
4
4
 
5
- let (:method_1) { ParsedMethod.new(
6
- name: "initialize",
7
- complexity: 13,
8
- type: :instance
5
+ let (:method_summary_1) { Fukuzatsu::Summary.new(
6
+ :source => "",
7
+ :source_file => "foo.rb",
8
+ :entity => "::initialize",
9
+ :container => "Foo",
10
+ :summaries => []
9
11
  )
10
12
  }
11
- let (:method_2) { ParsedMethod.new(
12
- name: "report",
13
- complexity: 11,
14
- type: :instance
13
+
14
+ let (:method_summary_2) { Fukuzatsu::Summary.new(
15
+ :source => "",
16
+ :source_file => "foo.rb",
17
+ :entity => "#print",
18
+ :container => "Foo",
19
+ :summaries => []
15
20
  )
16
21
  }
17
22
 
18
- let(:parsed_file) { Struct.new(:path_to_file, :class_name) }
19
- let(:mock_parsed_file) { parsed_file.new("fred/foo.rb", "Foo") }
20
- let (:formatter) { Formatters::Csv.new(mock_parsed_file) }
23
+ let (:summary) { Fukuzatsu::Summary.new(
24
+ :source => "",
25
+ :source_file => "foo.rb",
26
+ :entity => "Foo",
27
+ :container => "Foo",
28
+ :summaries => [method_summary_1, method_summary_2]
29
+ )
30
+ }
31
+
32
+ let (:formatter) { Fukuzatsu::Formatters::Csv.new(summary: summary) }
21
33
 
22
34
  describe "#rows" do
23
- it "returns comma-separated rows" do
24
- allow(mock_parsed_file).to receive(:methods) { [method_1, method_2] }
25
- expect(formatter.rows).to eq(
26
- "fred/foo.rb,Foo,#initialize,13\r\nfred/foo.rb,Foo,#report,11"
27
- )
35
+
36
+ before do
37
+ allow(summary).to receive(:container_name).and_return("Foo")
38
+ allow(summary).to receive(:entity_name).and_return("*")
39
+ allow(summary).to receive(:complexity).and_return(13)
40
+ allow(summary).to receive(:averge_complexity).and_return(11)
41
+ allow(method_summary_1).to receive(:container_name).and_return("Foo")
42
+ allow(method_summary_1).to receive(:entity_name).and_return("::initialize")
43
+ allow(method_summary_1).to receive(:complexity).and_return(13)
44
+ allow(method_summary_2).to receive(:container_name).and_return("Foo")
45
+ allow(method_summary_2).to receive(:entity_name).and_return("#report")
46
+ allow(method_summary_2).to receive(:complexity).and_return(11)
28
47
  end
29
- end
30
48
 
31
- describe "#file_extension" do
32
- it "returns the proper extension" do
33
- expect(formatter.file_extension).to eq ".csv"
49
+ it "returns formatted rows" do
50
+ expect(formatter.rows).to eq(
51
+ [
52
+ ["foo.rb","Foo","::initialize",13].join(","),
53
+ ["foo.rb","Foo","#report",11].join(","),
54
+ ].join("\r\n")
55
+ )
34
56
  end
35
57
  end
36
58
 
@@ -1,26 +1,32 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe "Formatters::Text" do
4
-
5
- let(:parsed_file) { Struct.new(:path_to_file, :class_name)}
6
- let(:mock_parsed_file) { parsed_file.new("fred/foo.rb", "Foo") }
7
- let (:method_1) { ParsedMethod.new(
8
- name: "initialize",
9
- complexity: 13,
10
- type: :instance
3
+ describe "Fukuzatsu::Formatters::HTML" do
4
+
5
+ let (:method_summary_1) { Fukuzatsu::Summary.new(
6
+ :source => "foo.rb",
7
+ :entity => "::initialize",
8
+ :container => "Foo",
9
+ :summaries => []
11
10
  )
12
11
  }
13
- let (:method_2) { ParsedMethod.new(
14
- name: "report",
15
- complexity: 11,
16
- type: :instance
12
+
13
+ let (:method_summary_2) { Fukuzatsu::Summary.new(
14
+ :source => "foo.rb",
15
+ :entity => "#print",
16
+ :container => "Foo",
17
+ :summaries => []
17
18
  )
18
19
  }
19
- let (:formatter) { Formatters::Html.new(mock_parsed_file) }
20
20
 
21
- before do
22
- allow(mock_parsed_file).to receive(:methods) { [method_1, method_2] }
23
- end
21
+ let (:summary) { Fukuzatsu::Summary.new(
22
+ :source => "foo.rb",
23
+ :entity => "Foo",
24
+ :container => "Foo",
25
+ :summaries => [method_summary_1, method_summary_2]
26
+ )
27
+ }
28
+
29
+ let (:formatter) { Fukuzatsu::Formatters::Html.new(summary: summary) }
24
30
 
25
31
  describe "#header" do
26
32
  it "returns an HTML-formatted header" do
@@ -31,10 +37,23 @@ describe "Formatters::Text" do
31
37
  end
32
38
 
33
39
  describe "#rows" do
40
+
41
+ before do
42
+ allow(summary).to receive(:container_name).and_return("Foo")
43
+ allow(summary).to receive(:entity_name).and_return("*")
44
+ allow(summary).to receive(:complexity).and_return(13)
45
+ allow(summary).to receive(:averge_complexity).and_return(11)
46
+ allow(method_summary_1).to receive(:container_name).and_return("Foo")
47
+ allow(method_summary_1).to receive(:entity_name).and_return("::initialize")
48
+ allow(method_summary_1).to receive(:complexity).and_return(13)
49
+ allow(method_summary_2).to receive(:container_name).and_return("Foo")
50
+ allow(method_summary_2).to receive(:entity_name).and_return("#report")
51
+ allow(method_summary_2).to receive(:complexity).and_return(11)
52
+ end
53
+
34
54
  it "returns HTML-formatted rows" do
35
- expected = "<tr class='even'>\r\n <td>Foo</td>\r\n <td>#initialize</td>\r\n <td>13</td>\r\n</tr>\r\n"
55
+ expected = "<tr class='even'>\r\n <td>Foo</td>\r\n <td>::initialize</td>\r\n <td>13</td>\r\n</tr>\r\n"
36
56
  expected << "<tr class='odd'>\r\n <td>Foo</td>\r\n <td>#report</td>\r\n <td>11</td>\r\n</tr>"
37
- allow(mock_parsed_file).to receive(:methods) { [method_1, method_2] }
38
57
  expect(formatter.rows).to eq(expected)
39
58
  end
40
59
  end
@@ -45,4 +64,4 @@ describe "Formatters::Text" do
45
64
  end
46
65
  end
47
66
 
48
- end
67
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe "Fukuzatsu::Formatters::Json" do
4
+
5
+ let (:method_summary_1) { Fukuzatsu::Summary.new(
6
+ :source => "",
7
+ :source_file => "foo.rb",
8
+ :entity => "::initialize",
9
+ :container => "Foo",
10
+ :summaries => []
11
+ )
12
+ }
13
+
14
+ let (:method_summary_2) { Fukuzatsu::Summary.new(
15
+ :source => "",
16
+ :source_file => "foo.rb",
17
+ :entity => "#print",
18
+ :container => "Foo",
19
+ :summaries => []
20
+ )
21
+ }
22
+
23
+ let (:summary) { Fukuzatsu::Summary.new(
24
+ :source => "",
25
+ :source_file => "foo.rb",
26
+ :entity => "Foo",
27
+ :container => "Foo",
28
+ :summaries => [method_summary_1, method_summary_2]
29
+ )
30
+ }
31
+
32
+ let (:formatter) { Fukuzatsu::Formatters::Json.new(summary: summary) }
33
+
34
+ describe "#to_json" do
35
+
36
+ before do
37
+ allow(summary).to receive(:container_name).and_return("Foo")
38
+ allow(summary).to receive(:entity_name).and_return("*")
39
+ allow(summary).to receive(:complexity).and_return(13)
40
+ allow(summary).to receive(:average_complexity).and_return(11)
41
+ allow(method_summary_1).to receive(:container_name).and_return("Foo")
42
+ allow(method_summary_1).to receive(:entity_name).and_return("::initialize")
43
+ allow(method_summary_1).to receive(:complexity).and_return(13)
44
+ allow(method_summary_2).to receive(:container_name).and_return("Foo")
45
+ allow(method_summary_2).to receive(:entity_name).and_return("#print")
46
+ allow(method_summary_2).to receive(:complexity).and_return(11)
47
+ end
48
+
49
+ it "returns JSON" do
50
+ expected = {
51
+ source_file: "foo.rb",
52
+ object: "Foo",
53
+ name: "*",
54
+ complexity: 13,
55
+ average_complexity: 11,
56
+ methods: [
57
+ {
58
+ source_file: "foo.rb",
59
+ object: "Foo",
60
+ name: "::initialize",
61
+ complexity: 13
62
+ },
63
+ {
64
+ source_file: "foo.rb",
65
+ object: "Foo",
66
+ name: "#print",
67
+ complexity: 11
68
+ },
69
+ ]
70
+ }.to_json
71
+ expect(formatter.content).to eq(expected)
72
+ end
73
+ end
74
+
75
+ describe "#file_extension" do
76
+ it "returns the proper extension" do
77
+ expect(formatter.file_extension).to eq ".json"
78
+ end
79
+ end
80
+
81
+ end