analyst 0.0.1 → 0.13.1

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.
Files changed (72) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.rspec +2 -0
  4. data/CODE_OF_CONDUCT.md +13 -0
  5. data/README.md +1 -0
  6. data/analyst.gemspec +12 -0
  7. data/lib/analyst/analyzer.rb +162 -0
  8. data/lib/analyst/cli.rb +42 -0
  9. data/lib/analyst/entity_parser/association.rb +24 -0
  10. data/lib/analyst/entity_parser/entities/class.rb +92 -0
  11. data/lib/analyst/entity_parser/entities/empty.rb +13 -0
  12. data/lib/analyst/entity_parser/entities/entity.rb +29 -0
  13. data/lib/analyst/entity_parser/entities/method.rb +16 -0
  14. data/lib/analyst/entity_parser/entities/module.rb +31 -0
  15. data/lib/analyst/formatters/base.rb +33 -0
  16. data/lib/analyst/formatters/csv.rb +43 -0
  17. data/lib/analyst/formatters/html.rb +87 -0
  18. data/lib/analyst/formatters/html_index.rb +47 -0
  19. data/lib/analyst/formatters/templates/index.html.haml +92 -0
  20. data/lib/analyst/formatters/templates/output.html.haml +114 -0
  21. data/lib/analyst/formatters/text.rb +56 -0
  22. data/lib/analyst/fukuzatsu/analyzer.rb +162 -0
  23. data/lib/analyst/fukuzatsu/cli.rb +42 -0
  24. data/lib/analyst/fukuzatsu/entity_parser/association.rb +24 -0
  25. data/lib/analyst/fukuzatsu/entity_parser/entities/class.rb +92 -0
  26. data/lib/analyst/fukuzatsu/entity_parser/entities/empty.rb +13 -0
  27. data/lib/analyst/fukuzatsu/entity_parser/entities/entity.rb +29 -0
  28. data/lib/analyst/fukuzatsu/entity_parser/entities/method.rb +16 -0
  29. data/lib/analyst/fukuzatsu/entity_parser/entities/module.rb +31 -0
  30. data/lib/analyst/fukuzatsu/formatters/base.rb +33 -0
  31. data/lib/analyst/fukuzatsu/formatters/csv.rb +43 -0
  32. data/lib/analyst/fukuzatsu/formatters/html.rb +87 -0
  33. data/lib/analyst/fukuzatsu/formatters/html_index.rb +47 -0
  34. data/lib/analyst/fukuzatsu/formatters/templates/index.html.haml +92 -0
  35. data/lib/analyst/fukuzatsu/formatters/templates/output.html.haml +114 -0
  36. data/lib/analyst/fukuzatsu/formatters/text.rb +56 -0
  37. data/lib/analyst/fukuzatsu/line_of_code.rb +19 -0
  38. data/lib/analyst/fukuzatsu/parsed_file.rb +85 -0
  39. data/lib/analyst/fukuzatsu/parsed_method.rb +32 -0
  40. data/lib/analyst/fukuzatsu/parser_original.rb +76 -0
  41. data/lib/analyst/fukuzatsu/rethink/parser.rb +346 -0
  42. data/lib/analyst/fukuzatsu/version.rb +3 -0
  43. data/lib/analyst/line_of_code.rb +19 -0
  44. data/lib/analyst/parsed_file.rb +85 -0
  45. data/lib/analyst/parsed_method.rb +32 -0
  46. data/lib/analyst/parser.rb +76 -0
  47. data/lib/analyst/rethink/parser.rb +346 -0
  48. data/lib/analyst/version.rb +1 -1
  49. data/lib/analyst.rb +17 -2
  50. data/spec/analyzer_spec.rb +122 -0
  51. data/spec/cli_spec.rb +48 -0
  52. data/spec/fixtures/eg_class.rb +8 -0
  53. data/spec/fixtures/eg_mod_class.rb +2 -0
  54. data/spec/fixtures/eg_mod_class_2.rb +5 -0
  55. data/spec/fixtures/eg_module.rb +2 -0
  56. data/spec/fixtures/module_with_class.rb +9 -0
  57. data/spec/fixtures/multiple_methods.rb +7 -0
  58. data/spec/fixtures/nested_methods.rb +8 -0
  59. data/spec/fixtures/program_1.rb +19 -0
  60. data/spec/fixtures/program_2.rb +25 -0
  61. data/spec/fixtures/program_3.rb +66 -0
  62. data/spec/fixtures/program_4.rb +1 -0
  63. data/spec/fixtures/single_class.rb +9 -0
  64. data/spec/fixtures/single_method.rb +3 -0
  65. data/spec/formatters/csv_spec.rb +37 -0
  66. data/spec/formatters/html_index_spec.rb +36 -0
  67. data/spec/formatters/html_spec.rb +48 -0
  68. data/spec/formatters/text_spec.rb +39 -0
  69. data/spec/parsed_file_spec.rb +67 -0
  70. data/spec/parsed_method_spec.rb +34 -0
  71. data/spec/spec_helper.rb +7 -0
  72. metadata +229 -2
@@ -0,0 +1,31 @@
1
+ module Analyst
2
+
3
+ module EntityParser
4
+ module Entities
5
+ class Module < Entity
6
+ def name
7
+ const_node_array(ast.children.first).join('::')
8
+ end
9
+ def full_name
10
+ parent.full_name.empty? ? name : parent.full_name + '::' + name
11
+ end
12
+
13
+ private
14
+
15
+ # takes a (const) node and returns an array specifying the fully-qualified
16
+ # constant name that it represents. ya know, so CoolModule::SubMod::SweetClass
17
+ # would be parsed to:
18
+ # (const
19
+ # (const
20
+ # (const nil :CoolModule) :SubMod) :SweetClass)
21
+ # and passing that node here would return [:CoolModule, :SubMod, :SweetClass]
22
+ def const_node_array(node)
23
+ return [] if node.nil?
24
+ raise "expected (const) node or nil, got (#{node.type})" unless node.type == :const
25
+ const_node_array(node.children.first) << node.children[1]
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ end
@@ -0,0 +1,33 @@
1
+ module Formatters
2
+
3
+ module Base
4
+
5
+ def self.included(klass)
6
+ klass.send(:attr_accessor, :file)
7
+ klass.send(:attr_accessor, :source)
8
+ klass.send(:attr_accessor, :output_directory)
9
+ end
10
+
11
+ def initialize(file, output_directory=nil, source="")
12
+ self.file = file
13
+ self.source = source
14
+ self.output_directory = output_directory
15
+ end
16
+
17
+ def filename
18
+ File.basename(self.file.path_to_file) + file_extension
19
+ end
20
+
21
+ def output_path
22
+ output_path = File.dirname(File.join(self.output_directory, self.file.path_to_file))
23
+ FileUtils.mkpath(output_path)
24
+ output_path
25
+ end
26
+
27
+ def path_to_results
28
+ File.join(output_path, filename)
29
+ end
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,43 @@
1
+ module Formatters
2
+
3
+ class Csv
4
+
5
+ include Formatters::Base
6
+
7
+ def self.has_index?
8
+ false
9
+ end
10
+
11
+ def self.writes_to_file_system?
12
+ true
13
+ end
14
+
15
+ def content
16
+ rows + "\r\n"
17
+ end
18
+
19
+ def export
20
+ begin
21
+ File.open(path_to_results, 'a') {|outfile| outfile.write(content)}
22
+ rescue Exception => e
23
+ puts "Unable to write output: #{e} #{e.backtrace}"
24
+ end
25
+ end
26
+
27
+ def file_extension
28
+ ".csv"
29
+ end
30
+
31
+ def path_to_results
32
+ File.join(output_directory, "results#{file_extension}")
33
+ end
34
+
35
+ def rows
36
+ file.methods.map do |method|
37
+ "#{file.path_to_file},#{file.class_name},#{method.name},#{method.complexity}"
38
+ end.join("\r\n")
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,87 @@
1
+ require 'rouge'
2
+
3
+ module Formatters
4
+
5
+ class Html
6
+
7
+ include Formatters::Base
8
+
9
+ def self.has_index?
10
+ true
11
+ end
12
+
13
+ def self.writes_to_file_system?
14
+ true
15
+ end
16
+
17
+ def self.index_class
18
+ Formatters::HtmlIndex
19
+ end
20
+
21
+ def columns
22
+ ["class", "method", "complexity"]
23
+ end
24
+
25
+ def content
26
+ Haml::Engine.new(output_template).render(
27
+ Object.new, {
28
+ header: header,
29
+ rows: rows,
30
+ source_lines: preprocessed,
31
+ class_name: file.class_name,
32
+ complexity: file.complexity,
33
+ path_to_file: file.path_to_file,
34
+ date: Time.now.strftime("%Y/%m/%d"),
35
+ time: Time.now.strftime("%l:%M %P")
36
+ }
37
+ )
38
+ end
39
+
40
+ def export
41
+ begin
42
+ File.open(path_to_results, 'w') {|outfile| outfile.write(content)}
43
+ rescue Exception => e
44
+ puts "Unable to write output: #{e} #{e.backtrace}"
45
+ end
46
+ end
47
+
48
+ def formatter
49
+ Rouge::Formatters::HTML.new(line_numbers: true)
50
+ end
51
+
52
+ def header
53
+ columns.map{|col| "<th>#{col.titleize}</th>"}.join("\r\n")
54
+ end
55
+
56
+ def lexer
57
+ lexer = Rouge::Lexers::Ruby.new
58
+ end
59
+
60
+ def output_template
61
+ File.read(File.dirname(__FILE__) + "/templates/output.html.haml")
62
+ end
63
+
64
+ def preprocessed
65
+ formatter.format(lexer.lex(source))
66
+ end
67
+
68
+ def rows
69
+ i = 0
70
+ file.methods.inject([]) do |a, method|
71
+ i += 1
72
+ a << "<tr class='#{i % 2 == 1 ? 'even' : 'odd'}'>"
73
+ a << " <td>#{file.class_name}</td>"
74
+ a << " <td>#{method.name}</td>"
75
+ a << " <td>#{method.complexity}</td>"
76
+ a << "</tr>"
77
+ a
78
+ end.join("\r\n")
79
+ end
80
+
81
+ def file_extension
82
+ ".htm"
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,47 @@
1
+ module Formatters
2
+
3
+ class HtmlIndex
4
+
5
+ include Formatters::Base
6
+
7
+ attr_reader :file_summary, :output_directory
8
+
9
+ def initialize(file_summary, output_directory=nil)
10
+ @file_summary = file_summary
11
+ @output_directory = output_directory
12
+ end
13
+
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
23
+
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}"
29
+ end
30
+ end
31
+
32
+ def filename
33
+ "index.htm"
34
+ end
35
+
36
+ def output_path
37
+ FileUtils.mkpath(self.output_directory)
38
+ self.output_directory
39
+ end
40
+
41
+ def output_template
42
+ File.read(File.dirname(__FILE__) + "/templates/index.html.haml")
43
+ end
44
+
45
+ end
46
+
47
+ end
@@ -0,0 +1,92 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title
5
+ Analyst
6
+ %link{href: "http://cdn.datatables.net/1.10.0/css/jquery.dataTables.css", rel: "stylesheet"}
7
+ %script{language: "javascript", src: "http://code.jquery.com/jquery-1.11.0.min.js", type: "text/javascript"}
8
+ %script{language: "javascript", src: "http://code.jquery.com/jquery-migrate-1.2.1.min.js", type: "text/javascript"}
9
+ %script{language: "javascript", src: "http://cdn.datatables.net/1.10.0/js/jquery.dataTables.js", type: "text/javascript"}
10
+
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; }
34
+ div.dataTables_filter { margin-bottom: 2em !important; }
35
+ div.dataTables_filter label { color: #fff; }
36
+ div.dataTables_paginate { display: none !important; }
37
+ div.dataTables_info { display: none !important; }
38
+
39
+ %body
40
+ %table{class: "output-table"}
41
+ %thead
42
+ %tr.header
43
+ %th{colspan: 4}
44
+ .file_meta
45
+ %h1
46
+ Project Overview
47
+ %tr
48
+ %th
49
+ File
50
+ %th
51
+ Module/Class Name
52
+ %th
53
+ Complexity
54
+ %th
55
+ Details
56
+ %tbody
57
+ - file_summary.each do |summary|
58
+ %tr
59
+ %td
60
+ %a{href: "#{summary[:path_to_file]}.htm"}
61
+ = summary[:path_to_file]
62
+ %td
63
+ - if summary[:class_name].size > 25
64
+ = "..."
65
+ = summary[:class_name][-24..-1]
66
+ - else
67
+ = summary[:class_name]
68
+ %td
69
+ = summary[:complexity]
70
+ %td
71
+ %a{href: "#{summary[:path_to_file]}.htm"}
72
+ View Details
73
+ %tfoot
74
+ %tr
75
+ %td.center{colspan: 4}
76
+ %em
77
+ Analyzed on
78
+ = date
79
+ at
80
+ = time
81
+ by
82
+ %a{href: "https://gitlab.com/coraline/Analyst", target: "_new"}
83
+ Analyst
84
+ :javascript
85
+ $(document).ready(function(){
86
+ $('.output-table').dataTable({
87
+ bLengthChange: false,
88
+ iDisplayLength: 25000,
89
+ "order": [[2, "desc"]]
90
+ });
91
+ });
92
+
@@ -0,0 +1,114 @@
1
+ !!!
2
+ %html
3
+ %head
4
+ %title
5
+ Analyst:
6
+ = class_name
7
+ %link{href: "http://cdn.datatables.net/1.10.0/css/jquery.dataTables.css", rel: "stylesheet"}
8
+ %script{language: "javascript", src: "http://code.jquery.com/jquery-1.11.0.min.js", type: "text/javascript"}
9
+ %script{language: "javascript", src: "http://code.jquery.com/jquery-migrate-1.2.1.min.js", type: "text/javascript"}
10
+ %script{language: "javascript", src: "http://cdn.datatables.net/1.10.0/js/jquery.dataTables.js", type: "text/javascript"}
11
+ %script{language: "javascript", src: "http://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.0/highlight.min.js", type: "text/javascript"}
12
+ :css
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
+
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; }
42
+
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; }
59
+
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; }
63
+
64
+ div.dataTables_filter { display: none !important; }
65
+ div.dataTables_paginate { display: none !important; }
66
+ div.dataTables_info { display: none !important; }
67
+
68
+
69
+ %body
70
+ .container
71
+ %table{class: "output-table"}
72
+ %thead
73
+ %tr.header
74
+ %th{colspan: 3}
75
+ .file_meta
76
+ %h1
77
+ = class_name
78
+ %h2
79
+ = path_to_file
80
+ .file_meta_right
81
+ %h3
82
+ = "Overall Complexity: #{complexity}"
83
+ %tr
84
+ = header
85
+ %tbody
86
+ = rows
87
+ %tfoot
88
+ %tr
89
+ %td.center{colspan: 3}
90
+ %em
91
+ Analyzed on
92
+ = date
93
+ at
94
+ = time
95
+ by
96
+ %a{href: "https://gitlab.com/coraline/Analyst", target: "_new"}
97
+ Analyst
98
+ %br
99
+ %input{onclick: "history.back(-1)", type: "button", value: "Back"}
100
+
101
+ %br
102
+
103
+ .file_listing
104
+ = source_lines
105
+
106
+ :javascript
107
+ $(document).ready(function(){
108
+ $('.output-table').dataTable({
109
+ bLengthChange: false,
110
+ iDisplayLength: 25000,
111
+ "order": [[2, "desc"]]
112
+ });
113
+ });
114
+
@@ -0,0 +1,56 @@
1
+ module Formatters
2
+
3
+ require 'terminal-table'
4
+ require 'rainbow/ext/string'
5
+
6
+ class Text
7
+
8
+ include Formatters::Base
9
+
10
+ def self.has_index?
11
+ false
12
+ end
13
+
14
+ def self.writes_to_file_system?
15
+ false
16
+ end
17
+
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
24
+
25
+ def header
26
+ ["Class/Module", "Method", "Complexity"]
27
+ end
28
+
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
40
+
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)]
45
+ end
46
+ end
47
+
48
+ def wrap(string)
49
+ return string if string.length < 25
50
+ string[0..20] << "..."
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
@@ -0,0 +1,19 @@
1
+ class LineOfCode
2
+
3
+ include PoroPlus
4
+ include Ephemeral::Base
5
+
6
+ attr_accessor :line_number, :range, :content
7
+
8
+ def self.containing(locs, start_index, end_index)
9
+ locs.inject([]) do |a, loc|
10
+ a << loc if loc.in_range?(start_index) || loc.in_range?(end_index)
11
+ a
12
+ end.compact
13
+ end
14
+
15
+ def in_range?(index)
16
+ self.range.include?(index)
17
+ end
18
+
19
+ end
@@ -0,0 +1,85 @@
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 average_complexity
23
+ methods.map(&:complexity).reduce(:+) / methods.count.to_f
24
+ end
25
+
26
+ def complexity
27
+ @complexity ||= analyzer.complexity
28
+ end
29
+
30
+ def methods
31
+ @methods ||= analyzer.methods
32
+ end
33
+
34
+ def method_counts
35
+ referenced_methods = methods.map(&:references).flatten
36
+ referenced_methods.inject({}) do |hash, method|
37
+ hash[method] ||= 0
38
+ hash[method] += 1
39
+ hash
40
+ end
41
+ end
42
+
43
+ def source
44
+ return @source if @source
45
+ end_pos = 0
46
+ self.lines_of_code = []
47
+ @source = File.readlines(self.path_to_file).each_with_index do |line, index|
48
+ start_pos = end_pos + 1
49
+ end_pos += line.size
50
+ self.lines_of_code << LineOfCode.new(line_number: index + 1, range: (start_pos..end_pos))
51
+ line
52
+ end.join
53
+ end
54
+
55
+ def summary
56
+ {
57
+ path_to_file: self.path_to_file,
58
+ results_file: self.path_to_results,
59
+ source: source,
60
+ class_name: self.class_name,
61
+ complexity: complexity
62
+ }
63
+ end
64
+
65
+ private
66
+
67
+ def analyzer
68
+ @analyzer ||= Analyzer.new(content)
69
+ end
70
+
71
+ def content
72
+ @content ||= File.open(path_to_file, "r").read
73
+ end
74
+
75
+ def parse!
76
+ end_pos = 0
77
+ File.readlines(self.path_to_file).each_with_index do |line, index|
78
+ start_pos = end_pos + 1
79
+ end_pos += line.size
80
+ self.lines_of_code << LineOfCode.new(line_number: index + 1, range: (start_pos..end_pos))
81
+ line
82
+ end.join
83
+ end
84
+
85
+ end
@@ -0,0 +1,32 @@
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