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,47 +1,54 @@
1
- module Formatters
1
+ module Fukuzatsu
2
2
 
3
- class HtmlIndex
3
+ module Formatters
4
4
 
5
- include Formatters::Base
5
+ class HtmlIndex
6
6
 
7
- attr_reader :file_summary, :output_directory
7
+ include Formatters::Base
8
8
 
9
- def initialize(file_summary, output_directory=nil)
10
- @file_summary = file_summary
11
- @output_directory = output_directory
12
- end
9
+ attr_reader :summaries
13
10
 
14
- def content
15
- Haml::Engine.new(output_template).render(
16
- Object.new, {
17
- file_summary: file_summary,
18
- date: Time.now.strftime("%Y/%m/%d"),
19
- time: Time.now.strftime("%l:%M %P")
20
- }
21
- )
22
- end
11
+ def initialize(summaries)
12
+ @summaries = summaries
13
+ end
23
14
 
24
- def export
25
- begin
26
- File.open(path_to_results, 'w') {|outfile| outfile.write(content)}
27
- rescue Exception => e
28
- puts "Unable to write output: #{e} #{e.backtrace}"
15
+ def content
16
+ Haml::Engine.new(output_template).render(
17
+ Object.new, {
18
+ summaries: summaries,
19
+ date: Time.now.strftime("%Y/%m/%d"),
20
+ time: Time.now.strftime("%l:%M %P")
21
+ }
22
+ )
29
23
  end
30
- end
31
24
 
32
- def filename
33
- "index.htm"
34
- end
25
+ def export
26
+ begin
27
+ File.open(path_to_results, 'w') {|outfile| outfile.write(content)}
28
+ rescue Exception => e
29
+ puts "Unable to write output: #{e} #{e.backtrace}"
30
+ end
31
+ end
35
32
 
36
- def output_path
37
- FileUtils.mkpath(self.output_directory)
38
- self.output_directory
39
- end
33
+ def filename
34
+ "index.htm"
35
+ end
36
+
37
+ def file_extension
38
+ ".htm"
39
+ end
40
+
41
+ def output_path
42
+ FileUtils.mkpath(self.output_directory)
43
+ self.output_directory
44
+ end
45
+
46
+ def output_template
47
+ File.read(File.dirname(__FILE__) + "/templates/index.html.haml")
48
+ end
40
49
 
41
- def output_template
42
- File.read(File.dirname(__FILE__) + "/templates/index.html.haml")
43
50
  end
44
51
 
45
52
  end
46
53
 
47
- end
54
+ end
@@ -0,0 +1,37 @@
1
+ module Fukuzatsu
2
+
3
+ module Formatters
4
+
5
+ class Json
6
+
7
+ include Formatters::Base
8
+
9
+ def self.index(summaries)
10
+ Fukuzatsu::Formatters::JsonIndex.new(summaries).export
11
+ end
12
+
13
+ def as_json
14
+ result = {
15
+ source_file: summary.source_file,
16
+ object: summary.container_name,
17
+ name: summary.entity_name,
18
+ complexity: summary.complexity
19
+ }
20
+ if summary.is_class_or_module?
21
+ result[:average_complexity] = summary.average_complexity
22
+ result[:methods] = summary.summaries.map { |s| Json.new(summary: s).as_json }
23
+ end
24
+ result
25
+ end
26
+
27
+ def content
28
+ as_json.to_json
29
+ end
30
+
31
+ def file_extension
32
+ ".json"
33
+ end
34
+ end
35
+
36
+ end
37
+ end
@@ -0,0 +1,31 @@
1
+ module Fukuzatsu
2
+
3
+ module Formatters
4
+
5
+ class JsonIndex
6
+
7
+ include Formatters::Base
8
+
9
+ attr_reader :summaries
10
+
11
+ def initialize(summaries)
12
+ @summaries = summaries
13
+ end
14
+
15
+ def content
16
+ summaries.map { |summary| Json.new(summary: summary).as_json }.to_json
17
+ end
18
+
19
+ def filename
20
+ "json/results.json"
21
+ end
22
+
23
+ def file_extension
24
+ ".json"
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -9,30 +9,19 @@
9
9
  %script{language: "javascript", src: "http://cdn.datatables.net/1.10.0/js/jquery.dataTables.js", type: "text/javascript"}
10
10
 
11
11
  %style{media: "screen", type: "text/css"}
12
- body { background: #593232; color: #fff; font-family: arial, sans-serif; padding: 2em; }
13
- table { box-shadow: 0 5px 0 rgba(0,0,0,.8); background: #444; border-spacing: 0; border: 5px solid #000; border-radius: 25px; border-collapse: collapse; min-width: 50%; }
14
- th.sorting_asc, th.sorting_desc { text-transform: uppercase !important; font-size: .8em !important; background-image: none !important; background: rgba(64, 41, 41, .5) !important;}
15
- th.sorting { background-position: left !important; border-right: 1px solid #222; text-transform: uppercase !important; font-size: .8em !important}
16
- tr.header th:first-child { border-radius: 6px 0 0 0; }
17
- tr.header th:last-child { border-radius: 0 6px 0 0; }
18
- tr.header th:only-child { border-radius: 6px 6px 0 0; }
19
- tr.header { background-color: #222; }
20
- tr.even { background: rgba(128, 128, 128, 0.5) !important;}
21
- tr.odd { background: rgba(128, 128, 128, 0.25) !important}
22
- tr.even:hover, tr.odd:hover { background: rgba(128, 128, 128, 0.75) !important;}
23
- th { background: #000; text-align: left; padding: .5em; }
24
- td { text-align: left; padding: .5em; padding-left: 1.25em !important;}
25
- td.center { text-align: center; }
26
- td.sorting_1 { background: none !important; padding-left: 1.25em !important; }
27
- tfoot { background: #000; border-top: 10px solid #000; font-family: courier; margin-top: 4em; font-size: .75em; }
28
- a:link, a:visited { color: #aaa }
29
- div.file_meta { float: left; height: 3em; width: 30%; }
30
- h1 { color:#fff; font-size: 1.25em; margin-top: .25em; }
31
- h2 { color:#fff; font-size: .75em; margin-top: -1em; }
32
- h3 { color:#fff; font-size: 1em; float: right; margin-top: 1em; }
33
- td.sorting_1 { background: none !important; padding-left: 1.25em !important; }
12
+ body { font-family: Arial, sans-serif; font-size: 13px; padding: 2em; }
13
+
14
+ a { color: #900; }
15
+ table { border: 4px solid #444; width: 100%; }
16
+
17
+ tr.sort-controls th { background-color: #999 !important; font-size: .75em; text-transform: uppercase; text-align: left; }
18
+
19
+ tr.header th, tfoot { background-color: #444 !important; color: #fff; }
20
+ tfoot td { font-size: .75em; }
21
+ tr.odd td { background-color: #eee; }
22
+
34
23
  div.dataTables_filter { margin-bottom: 2em !important; }
35
- div.dataTables_filter label { color: #fff; }
24
+ div.dataTables_filter label { color: ::fff; }
36
25
  div.dataTables_paginate { display: none !important; }
37
26
  div.dataTables_info { display: none !important; }
38
27
 
@@ -44,7 +33,7 @@
44
33
  .file_meta
45
34
  %h1
46
35
  Project Overview
47
- %tr
36
+ %tr.sort-controls
48
37
  %th
49
38
  File
50
39
  %th
@@ -54,21 +43,21 @@
54
43
  %th
55
44
  Details
56
45
  %tbody
57
- - file_summary.each do |summary|
46
+ - summaries.each do |summary|
58
47
  %tr
59
48
  %td
60
- %a{href: "#{summary[:path_to_file]}.htm"}
61
- = summary[:path_to_file]
49
+ %a{href: "#{summary.source_file}.htm"}
50
+ = summary.source_file
62
51
  %td
63
- - if summary[:class_name].size > 25
52
+ - if summary.entity.full_name.size > 50
64
53
  = "..."
65
- = summary[:class_name][-24..-1]
54
+ = summary.entity.full_name[-49..-1]
66
55
  - else
67
- = summary[:class_name]
56
+ = summary.entity.full_name
68
57
  %td
69
- = summary[:complexity]
58
+ = sprintf("%0.2f", summary.average_complexity)
70
59
  %td
71
- %a{href: "#{summary[:path_to_file]}.htm"}
60
+ %a{href: "#{summary.source_file}.htm"}
72
61
  View Details
73
62
  %tfoot
74
63
  %tr
@@ -11,61 +11,30 @@
11
11
  %script{language: "javascript", src: "http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.0/highlight.min.js", type: "text/javascript"}
12
12
  :css
13
13
  #{Rouge::Theme.find('thankful_eyes').render(scope: '.highlight')}
14
- body { background: #593232; color: #fff; font-family: arial, sans-serif; padding: 2em; }
15
- div.column { float: left; width: 45%; margin-left: 4%; }
16
- div.file_listing { padding: .1em; border-radius: 5px; background: #000; width: 100%; border: 1px solid #000;}
17
- div.file_meta { float: left; height: 3em; width: 30%; }
18
- h1 { color:#fff; font-size: 1.25em; margin-top: .25em; }
19
- h2 { color:#fff; font-size: .75em; margin-top: -1em; }
20
- h3 { color:#fff; font-size: 1.1em;margin-top: 1em; }
21
- h3.highlighted { background: rgba(170, 161, 57, .6); border-radius: 100px; padding: .25em; padding-left: 1em; color: #000;}
22
- h3.highlighted-method { background: rgba(153, 51, 80, .6); border-radius: 100px; padding-left: 1em; }
23
- li { margin-bottom: 1em;}
24
- pre { line-height: 1.75em;}
25
- pre.lineno { margin-top: -1.4em !important;}
26
- span.highlighted { padding-left: 1em; display: inline-block; position: absolute; left: 0px; padding-right: 90%}
27
- a:link, a:visited { color: #aaa }
28
14
 
29
- .file_listing table { width: 100%; box-shadow: 0 5px 0 rgba(0,0,0,.8); border-spacing: 0; border: 5px solid #000; border-radius: 5px; border-collapse: collapse; min-width: 50%; }
30
- .file_listing td { text-align: left; padding: .5em; padding-left: 1.25em !important;}
31
- .file_listing td.gutter { background-color: rgb(109, 109, 109) !important; }
32
- .file_listing tfoot { background: #000; border-top: 10px solid #000; font-family: courier; margin-top: 4em; font-size: .75em; }
33
- .file_listing th { background: #000; text-align: left; padding: .5em; }
34
- .file_listing tr.faint td { opacity: 0.5; font-style: italic; }
35
- .file_listing tr.header { background-color: #222; }
36
- .file_listing tr { background: rgba(0, 0, 0, 0.25) !important; border-bottom: 1px solid #000;}
37
- .file_listing th { background: #000; text-align: left; padding: .5em;}
38
- .file_listing td { text-align: left; padding: .5em; padding-left: 1.25em !important;}
39
- .file_listing td.center { text-align: center; }
40
- .file_listing td.sorting_1 { background: none !important; padding-left: 1.25em !important; }
41
- .file_listing tfoot { background: #000; font-family: courier; margin-top: 4em; font-size: .75em; }
15
+ body {
16
+ font-family: Arial, sans-serif;
17
+ font-size: 13px;
18
+ padding: 2em;
19
+ }
42
20
 
43
- table { box-shadow: 0 5px 0 rgba(0,0,0,.8); background: #444; border-spacing: 0; border: 5px solid #000; border-radius: 25px; border-collapse: collapse; min-width: 50%; }
44
- th.sorting_asc, th.sorting_desc { text-transform: uppercase !important; font-size: .8em !important; background-image: none !important; background: rgba(64, 41, 41, .5) !important;}
45
- th.sorting { background-position: left !important; border-right: 1px solid #222; text-transform: uppercase !important; font-size: .8em !important}
46
- tr.header th:first-child { border-radius: 6px 0 0 0; }
47
- tr.header th:last-child { border-radius: 0 6px 0 0; }
48
- tr.header th:only-child { border-radius: 6px 6px 0 0; }
49
- tfoot tr { border-radius: 6px 6px 6px 6px; }
50
- tr.header { background-color: #222; }
51
- tr.even { background: rgba(128, 128, 128, 0.5) !important;}
52
- tr.odd { background: rgba(128, 128, 128, 0.25) !important}
53
- tr.even:hover, tr.odd:hover { background: rgba(128, 128, 128, 0.75) !important;}
54
- th { background: #000; text-align: left; padding: .5em;}
55
- td { text-align: left; padding: .5em; padding-left: 1.25em !important;}
56
- td.center { text-align: center; }
57
- td.sorting_1 { background: none !important; padding-left: 1.25em !important; }
58
- tfoot { background: #000; font-family: courier; margin-top: 4em; font-size: .75em; }
21
+ a { color: #fff; }
22
+ table { border: 4px solid #444; width: 100%; }
59
23
 
60
- h1 { color:#fff; font-size: 1.25em; margin-top: .25em; }
61
- h2 { color:#fff; font-size: .75em; margin-top: -1em; }
62
- h3 { color:#fff; font-size: 1em; float: right; margin-top: 1em; }
24
+ tr.sort-controls th {
25
+ background-color: #999 !important;
26
+ font-size: .75em;
27
+ text-transform: uppercase;
28
+ text-align: left;
29
+ }
63
30
 
31
+ tr.header th, tfoot { background-color: #444 !important; color: #fff; }
32
+ tfoot td { font-size: .75em; }
33
+ tr.odd td { background-color: #eee; }
64
34
  div.dataTables_filter { display: none !important; }
65
35
  div.dataTables_paginate { display: none !important; }
66
36
  div.dataTables_info { display: none !important; }
67
37
 
68
-
69
38
  %body
70
39
  .container
71
40
  %table{class: "output-table"}
@@ -79,8 +48,8 @@
79
48
  = path_to_file
80
49
  .file_meta_right
81
50
  %h3
82
- = "Overall Complexity: #{complexity}"
83
- %tr
51
+ = "Average Complexity: #{average_complexity}"
52
+ %tr.sort-controls
84
53
  = header
85
54
  %tbody
86
55
  = rows
@@ -97,7 +66,7 @@
97
66
  Fukuzatsu
98
67
  %br
99
68
  %input{onclick: "history.back(-1)", type: "button", value: "Back"}
100
-
69
+ %br
101
70
  %br
102
71
 
103
72
  .file_listing
@@ -1,56 +1,68 @@
1
- module Formatters
1
+ module Fukuzatsu
2
2
 
3
- require 'terminal-table'
4
- require 'rainbow/ext/string'
3
+ module Formatters
5
4
 
6
- class Text
5
+ require 'terminal-table'
6
+ require 'rainbow/ext/string'
7
7
 
8
- include Formatters::Base
8
+ class Text
9
9
 
10
- def self.has_index?
11
- false
12
- end
10
+ include Formatters::Base
13
11
 
14
- def self.writes_to_file_system?
15
- false
16
- end
12
+ def self.explain(count); end
17
13
 
18
- def color_for(row)
19
- return :green if row.complexity == 0
20
- return :yellow if row.complexity <= file.average_complexity
21
- return :red if row.complexity > file.average_complexity
22
- return :white
23
- end
14
+ def self.reset_output_directory
15
+ end
24
16
 
25
- def header
26
- ["Class/Module", "Method", "Complexity"]
27
- end
17
+ def self.writes_to_file_system?
18
+ false
19
+ end
28
20
 
29
- def export
30
- return if rows.empty?
31
- table = Terminal::Table.new(
32
- title: file.path_to_file.color(:white),
33
- headings: header,
34
- rows: rows,
35
- style: {width: 90}
36
- )
37
- table.align_column(3, :right)
38
- puts table
39
- end
21
+ def color_for(entity, average_complexity)
22
+ return :green if entity.complexity == 0
23
+ return :yellow if entity.complexity <= average_complexity
24
+ return :red if entity.complexity > average_complexity
25
+ return :white
26
+ end
40
27
 
41
- def rows
42
- file.methods.map do |method|
43
- color = color_for(method)
44
- [wrap("#{file.class_name}").color(color), wrap("#{method.name}".color(color)), "#{method.complexity}".color(color)]
28
+ def header
29
+ ["Class/Module", "Method", "Complexity"]
30
+ end
31
+
32
+ def export
33
+ table = Terminal::Table.new(
34
+ title: "#{summary.source_file}".color(:white),
35
+ headings: header,
36
+ rows: rows,
37
+ )
38
+ table.align_column(3, :right)
39
+ puts table
40
+ end
41
+
42
+ def rows
43
+ rows_for(
44
+ [self.summary, self.summary.summaries].flatten,
45
+ self.summary.average_complexity
46
+ )
47
+ end
48
+
49
+ def rows_for(summaries, average_complexity)
50
+ summaries.map do |summary|
51
+ color = color_for(summary, average_complexity)
52
+ [
53
+ wrap("#{summary.container_name}").color(color),
54
+ wrap("#{summary.entity_name}".color(color)),
55
+ "#{summary.complexity}".color(color)
56
+ ]
57
+ end.compact
58
+ end
59
+
60
+ def wrap(string)
61
+ return string if string.length < 50
62
+ string[0..49] << "..."
45
63
  end
46
- end
47
64
 
48
- def wrap(string)
49
- return string if string.length < 25
50
- string[0..20] << "..."
51
65
  end
52
66
 
53
67
  end
54
-
55
68
  end
56
-
@@ -1,76 +1,58 @@
1
1
  require 'fileutils'
2
+ require 'analyst'
2
3
 
3
4
  module Fukuzatsu
4
5
 
5
6
  class Parser
6
7
 
7
- attr_reader :start_path, :parsed_files
8
- attr_reader :threshold, :formatter
9
- attr_reader :start_time
8
+ attr_reader :path_to_files, :formatter, :threshold
10
9
 
11
- OUTPUT_DIRECTORY = "doc/fukuzatsu"
12
-
13
- def initialize(path, formatter, threshold=0)
14
- @start_path = path
15
- @formatter = formatter
16
- @threshold = threshold
17
- @start_time = Time.now
18
- reset_output_directory
19
- end
20
-
21
- def parse_files
22
- @parsed_files = source_files.map do |path_to_file|
23
- parse_source_file(path_to_file)
24
- end
10
+ def initialize(path_to_files, formatter, threshold)
11
+ @path_to_files = path_to_files
12
+ @formatter = formatter
13
+ @threshold = threshold
25
14
  end
26
15
 
27
16
  def report
28
- self.parsed_files.each do |file|
29
- print "."
30
- formatter.new(file, OUTPUT_DIRECTORY, file.source).export
17
+ self.formatter.reset_output_directory
18
+ self.formatter.index(summaries)
19
+ summaries.uniq(&:container_name).each do |summary|
20
+ self.formatter.new(summary: summary).export
31
21
  end
32
- puts
33
- write_report_index
34
- report_complexity
22
+ self.formatter.explain(summaries.count)
23
+ check_complexity
35
24
  end
36
25
 
37
26
  private
38
27
 
39
- def reset_output_directory
40
- begin
41
- FileUtils.remove_dir(OUTPUT_DIRECTORY)
42
- rescue Errno::ENOENT
43
- end
44
- FileUtils.mkpath(OUTPUT_DIRECTORY)
28
+ def check_complexity
29
+ return if self.threshold == 0
30
+ return unless complexity_exceeds_threshold?
31
+ puts "Maximum average complexity of #{average_complexities.max} exceeds #{self.threshold.to_f} threshold!"
32
+ exit 1
45
33
  end
46
34
 
47
- def source_files
48
- if File.directory?(start_path)
49
- return Dir.glob(File.join(start_path, "**", "*.rb"))
50
- else
51
- return [start_path]
52
- end
35
+ def average_complexities
36
+ average_complexities ||= summaries.map(&:average_complexity)
53
37
  end
54
38
 
55
- def parse_source_file(path_to_file, options={})
56
- ParsedFile.new(path_to_file: path_to_file)
39
+ def complexity_exceeds_threshold?
40
+ average_complexities.max.to_f > self.threshold.to_f
57
41
  end
58
42
 
59
- def report_complexity
60
- return if self.threshold == 0
61
- complexities = self.parsed_files.map(&:complexity)
62
- return if complexities.max.to_i <= self.threshold
63
- puts "Maximum complexity of #{complexities.max} exceeds #{options['threshold']} threshold!"
64
- exit 1
43
+ def summaries
44
+ @summaries ||= file_reader.source_files.map do |source_file|
45
+ Fukuzatsu::Summary.from(
46
+ content: source_file.contents,
47
+ source_file: source_file.filename
48
+ )
49
+ end.flatten
65
50
  end
66
51
 
67
- def write_report_index
68
- return unless self.formatter.writes_to_file_system?
69
- puts "Results written to #{OUTPUT_DIRECTORY} "
70
- return unless self.formatter.has_index?
71
- formatter.index_class.new(parsed_files.map(&:summary), OUTPUT_DIRECTORY).export
52
+ def file_reader
53
+ @file_reader ||= Fukuzatsu::FileReader.new(self.path_to_files)
72
54
  end
73
55
 
74
56
  end
75
57
 
76
- end
58
+ end