flyerhzm-metric_fu 1.0.0

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 (92) hide show
  1. data/HISTORY +164 -0
  2. data/MIT-LICENSE +22 -0
  3. data/Manifest.txt +25 -0
  4. data/README.textile +27 -0
  5. data/Rakefile +18 -0
  6. data/TODO +9 -0
  7. data/lib/base/base_template.rb +145 -0
  8. data/lib/base/configuration.rb +180 -0
  9. data/lib/base/generator.rb +167 -0
  10. data/lib/base/graph.rb +39 -0
  11. data/lib/base/md5_tracker.rb +52 -0
  12. data/lib/base/report.rb +100 -0
  13. data/lib/generators/churn.rb +90 -0
  14. data/lib/generators/flay.rb +34 -0
  15. data/lib/generators/flog.rb +167 -0
  16. data/lib/generators/rails_best_practices.rb +31 -0
  17. data/lib/generators/rcov.rb +87 -0
  18. data/lib/generators/reek.rb +64 -0
  19. data/lib/generators/roodi.rb +32 -0
  20. data/lib/generators/saikuro.rb +208 -0
  21. data/lib/generators/stats.rb +43 -0
  22. data/lib/graphs/engines/bluff.rb +98 -0
  23. data/lib/graphs/engines/gchart.rb +119 -0
  24. data/lib/graphs/flay_grapher.rb +20 -0
  25. data/lib/graphs/flog_grapher.rb +40 -0
  26. data/lib/graphs/grapher.rb +11 -0
  27. data/lib/graphs/rails_best_practices_grapher.rb +20 -0
  28. data/lib/graphs/rcov_grapher.rb +20 -0
  29. data/lib/graphs/reek_grapher.rb +32 -0
  30. data/lib/graphs/roodi_grapher.rb +20 -0
  31. data/lib/metric_fu.rb +31 -0
  32. data/lib/templates/awesome/awesome_template.rb +37 -0
  33. data/lib/templates/awesome/churn.html.erb +19 -0
  34. data/lib/templates/awesome/css/buttons.css +82 -0
  35. data/lib/templates/awesome/css/default.css +75 -0
  36. data/lib/templates/awesome/css/integrity.css +335 -0
  37. data/lib/templates/awesome/css/reset.css +7 -0
  38. data/lib/templates/awesome/flay.html.erb +33 -0
  39. data/lib/templates/awesome/flog.html.erb +53 -0
  40. data/lib/templates/awesome/index.html.erb +31 -0
  41. data/lib/templates/awesome/layout.html.erb +30 -0
  42. data/lib/templates/awesome/rails_best_practices.html.erb +27 -0
  43. data/lib/templates/awesome/rcov.html.erb +42 -0
  44. data/lib/templates/awesome/reek.html.erb +40 -0
  45. data/lib/templates/awesome/roodi.html.erb +27 -0
  46. data/lib/templates/awesome/saikuro.html.erb +71 -0
  47. data/lib/templates/awesome/stats.html.erb +41 -0
  48. data/lib/templates/javascripts/bluff-min.js +1 -0
  49. data/lib/templates/javascripts/excanvas.js +19 -0
  50. data/lib/templates/javascripts/js-class.js +1 -0
  51. data/lib/templates/standard/churn.html.erb +31 -0
  52. data/lib/templates/standard/default.css +64 -0
  53. data/lib/templates/standard/flay.html.erb +34 -0
  54. data/lib/templates/standard/flog.html.erb +53 -0
  55. data/lib/templates/standard/index.html.erb +41 -0
  56. data/lib/templates/standard/rails_best_practices.html.erb +29 -0
  57. data/lib/templates/standard/rcov.html.erb +43 -0
  58. data/lib/templates/standard/reek.html.erb +42 -0
  59. data/lib/templates/standard/roodi.html.erb +29 -0
  60. data/lib/templates/standard/saikuro.html.erb +84 -0
  61. data/lib/templates/standard/standard_template.rb +26 -0
  62. data/lib/templates/standard/stats.html.erb +55 -0
  63. data/spec/base/base_template_spec.rb +161 -0
  64. data/spec/base/configuration_spec.rb +269 -0
  65. data/spec/base/generator_spec.rb +244 -0
  66. data/spec/base/graph_spec.rb +24 -0
  67. data/spec/base/md5_tracker_spec.rb +57 -0
  68. data/spec/base/report_spec.rb +139 -0
  69. data/spec/generators/churn_spec.rb +152 -0
  70. data/spec/generators/flay_spec.rb +104 -0
  71. data/spec/generators/flog_spec.rb +238 -0
  72. data/spec/generators/reek_spec.rb +126 -0
  73. data/spec/generators/saikuro_spec.rb +58 -0
  74. data/spec/generators/stats_spec.rb +74 -0
  75. data/spec/graphs/engines/bluff_spec.rb +15 -0
  76. data/spec/graphs/engines/gchart_spec.rb +15 -0
  77. data/spec/graphs/flay_grapher_spec.rb +37 -0
  78. data/spec/graphs/flog_grapher_spec.rb +45 -0
  79. data/spec/graphs/rcov_grapher_spec.rb +37 -0
  80. data/spec/graphs/reek_grapher_spec.rb +46 -0
  81. data/spec/graphs/roodi_grapher_spec.rb +37 -0
  82. data/spec/resources/saikuro/app/controllers/sessions_controller.rb_cyclo.html +10 -0
  83. data/spec/resources/saikuro/app/controllers/users_controller.rb_cyclo.html +16 -0
  84. data/spec/resources/saikuro/index_cyclo.html +155 -0
  85. data/spec/resources/saikuro_sfiles/thing.rb_cyclo.html +11 -0
  86. data/spec/resources/yml/20090630.yml +7844 -0
  87. data/spec/spec.opts +8 -0
  88. data/spec/spec_helper.rb +7 -0
  89. data/tasks/metric_fu.rake +22 -0
  90. data/vendor/_fonts/monaco.ttf +0 -0
  91. data/vendor/saikuro/saikuro.rb +1219 -0
  92. metadata +234 -0
@@ -0,0 +1,34 @@
1
+ module MetricFu
2
+
3
+ class Flay < Generator
4
+
5
+ def self.verify_dependencies!
6
+ `flay --help`
7
+ raise 'sudo gem install flay # if you want the flay tasks' unless $?.success?
8
+ end
9
+
10
+ def emit
11
+ files_to_flay = MetricFu.flay[:dirs_to_flay].map{|dir| Dir[File.join(dir, "**/*.rb")] }
12
+ files = remove_excluded_files(files_to_flay.flatten)
13
+ @output = `flay #{files.join(" ")}`
14
+ end
15
+
16
+ def analyze
17
+ @matches = @output.chomp.split("\n\n").map{|m| m.split("\n ") }
18
+ end
19
+
20
+ def to_h
21
+ target = []
22
+ total_score = @matches.shift.first.split('=').last.strip
23
+ @matches.each do |problem|
24
+ reason = problem.shift.strip
25
+ lines_info = problem.map do |full_line|
26
+ name, line = full_line.split(":")
27
+ {:name => name.strip, :line => line.strip}
28
+ end
29
+ target << [:reason => reason, :matches => lines_info]
30
+ end
31
+ {:flay => {:total_score => total_score, :matches => target.flatten}}
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,167 @@
1
+ require 'pathname'
2
+
3
+ module MetricFu
4
+
5
+ class Flog < Generator
6
+ attr_reader :pages
7
+
8
+ def self.verify_dependencies!
9
+ `flog --help`
10
+ raise 'sudo gem install flog # if you want the flog tasks' unless $?.success?
11
+ end
12
+
13
+ SCORE_FORMAT = "%0.2f"
14
+ METHOD_LINE_REGEX = /(\d+\.\d+):\s+([A-Za-z:]+#.*)/
15
+ OPERATOR_LINE_REGEX = /\s*(\d+\.\d+):\s(.*)$/
16
+
17
+ def emit
18
+ metric_dir = MetricFu::Flog.metric_directory
19
+ MetricFu.flog[:dirs_to_flog].each do |directory|
20
+ directory = "." if directory=='./'
21
+ files = Dir.glob("#{directory}/**/*.rb")
22
+ files = remove_excluded_files(files)
23
+ files.each do |filename|
24
+ output_dir = "#{metric_dir}/#{filename.split("/")[0..-2].join("/")}"
25
+ mkdir_p(output_dir, :verbose => false) unless File.directory?(output_dir)
26
+ pathname = Pathname.new(filename)
27
+ if MetricFu::MD5Tracker.file_changed?(filename, metric_dir)
28
+ base_name = pathname.basename.to_s.gsub(/\..*$/,'.txt')
29
+ editted_filename = File.join(pathname.dirname.to_s, base_name)
30
+ `flog -ad #{filename} > #{metric_dir}/#{editted_filename}`
31
+ end
32
+ end
33
+ end
34
+ rescue LoadError
35
+ if RUBY_PLATFORM =~ /java/
36
+ puts 'running in jruby - flog tasks not available'
37
+ else
38
+ puts 'sudo gem install flog # if you want the flog tasks'
39
+ end
40
+ end
41
+
42
+ def parse(text)
43
+ summary, methods_summary = text.split "\n\n"
44
+ return unless summary
45
+ score, average = summary.split("\n").map {|line| line[OPERATOR_LINE_REGEX, 1]}
46
+ return nil unless score && methods_summary
47
+ page = Flog::Page.new(score, average)
48
+ methods_summary.each_line do |method_line|
49
+ if match = method_line.match(METHOD_LINE_REGEX)
50
+ page.scanned_methods << ScannedMethod.new(match[2], match[1])
51
+ elsif match = method_line.match(OPERATOR_LINE_REGEX)
52
+ return if page.scanned_methods.empty?
53
+ page.scanned_methods.last.operators << Operator.new(match[1], match[2])
54
+ end
55
+ end
56
+ page
57
+ end
58
+
59
+ def analyze
60
+ @pages = []
61
+ flog_results.each do |path|
62
+ page = parse(open(path, "r") { |f| f.read })
63
+ if page
64
+ page.path = path.sub(metric_directory, "").sub(".txt", ".rb")
65
+ #don't include old cached flog results for files that no longer exist.
66
+ if is_file_current?(page.path.to_s)
67
+ @pages << page
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ def to_h
74
+ number_of_methods = @pages.inject(0) {|count, page| count += page.scanned_methods.size}
75
+ total_flog_score = @pages.inject(0) {|total, page| total += page.score}
76
+ sorted_pages = @pages.sort_by {|page| page.highest_score }.reverse
77
+ {:flog => { :total => total_flog_score,
78
+ :average => average_score(total_flog_score, number_of_methods),
79
+ :pages => sorted_pages.map {|page| page.to_h}}}
80
+ end
81
+
82
+ private
83
+
84
+ def is_file_current?(pathname)
85
+ pathname = pathname.gsub(/^\//,'')
86
+ local_pathname = "./#{pathname}"
87
+ exists = false
88
+
89
+ MetricFu.flog[:dirs_to_flog].each do |directory|
90
+ directory = "." if directory=='./'
91
+ files = Dir.glob("#{directory}/**/*.rb")
92
+ if files.include?(pathname) || files.include?(local_pathname)
93
+ exists = true
94
+ break
95
+ end
96
+ end
97
+ exists
98
+ end
99
+
100
+ def average_score(total_flog_score, number_of_methods)
101
+ return 0 if total_flog_score == 0
102
+ round_to_tenths(total_flog_score/number_of_methods)
103
+ end
104
+
105
+ def flog_results
106
+ Dir.glob("#{metric_directory}/**/*.txt")
107
+ end
108
+
109
+ class Operator
110
+ attr_accessor :score, :operator
111
+
112
+ def initialize(score, operator)
113
+ @score = score.to_f
114
+ @operator = operator
115
+ end
116
+
117
+ def to_h
118
+ {:score => @score, :operator => @operator}
119
+ end
120
+ end
121
+
122
+ class ScannedMethod
123
+ attr_accessor :name, :score, :operators
124
+
125
+ def initialize(name, score, operators = [])
126
+ @name = name
127
+ @score = score.to_f
128
+ @operators = operators
129
+ end
130
+
131
+ def to_h
132
+ {:name => @name,
133
+ :score => @score,
134
+ :operators => @operators.map {|o| o.to_h}}
135
+ end
136
+ end
137
+
138
+ end
139
+
140
+ class Flog::Page < MetricFu::Generator
141
+ attr_accessor :path, :score, :scanned_methods, :average_score
142
+
143
+ def initialize(score, average_score, scanned_methods = [])
144
+ @score = score.to_f
145
+ @scanned_methods = scanned_methods
146
+ @average_score = average_score.to_f
147
+ end
148
+
149
+ def filename
150
+ File.basename(path, ".txt")
151
+ end
152
+
153
+ def to_h
154
+ {:score => @score,
155
+ :scanned_methods => @scanned_methods.map {|sm| sm.to_h},
156
+ :highest_score => highest_score,
157
+ :average_score => average_score,
158
+ :path => path}
159
+ end
160
+
161
+ def highest_score
162
+ scanned_methods.inject(0) do |highest, m|
163
+ m.score > highest ? m.score : highest
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,31 @@
1
+ module MetricFu
2
+ class RailsBestPractices < Generator
3
+
4
+ def self.verify_dependencies!
5
+ `rails_best_practices --help`
6
+ raise 'sudo gem install rails_best_practices # if you want the rails_best_practices tasks' unless $?.success?
7
+ end
8
+
9
+
10
+ def emit
11
+ @output = `rails_best_practices .`
12
+ end
13
+
14
+ def analyze
15
+ @matches = @output.chomp.split("\n").map{|m| m.split(" - ") }
16
+ total = @matches.pop
17
+ 2.times { @matches.pop } # ignore wiki link
18
+ @matches.reject! {|array| array.empty? }
19
+ @matches.map! do |match|
20
+ file, line = match[0].split(':')
21
+ problem = match[1]
22
+ {:file => file, :line => line, :problem => problem}
23
+ end
24
+ @rails_best_practices_results = {:total => total, :problems => @matches}
25
+ end
26
+
27
+ def to_h
28
+ {:rails_best_practices => @rails_best_practices_results}
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,87 @@
1
+ require 'enumerator'
2
+
3
+ module MetricFu
4
+
5
+ class Rcov < Generator
6
+ NEW_FILE_MARKER = ("=" * 80) + "\n"
7
+
8
+ def self.verify_dependencies!
9
+ `rcov --help`
10
+ unless $?.success?
11
+ if RUBY_PLATFORM =~ /java/
12
+ raise 'running in jruby - rcov tasks not available'
13
+ else
14
+ raise 'sudo gem install rcov # if you want the rcov tasks'
15
+ end
16
+ end
17
+ end
18
+
19
+ class Line
20
+ attr_accessor :content, :was_run
21
+
22
+ def initialize(content, was_run)
23
+ @content = content
24
+ @was_run = was_run
25
+ end
26
+
27
+ def to_h
28
+ {:content => @content, :was_run => @was_run}
29
+ end
30
+ end
31
+
32
+ def emit
33
+ begin
34
+ FileUtils.rm_rf(MetricFu::Rcov.metric_directory, :verbose => false)
35
+ Dir.mkdir(MetricFu::Rcov.metric_directory)
36
+ test_files = FileList[*MetricFu.rcov[:test_files]].join(' ')
37
+ rcov_opts = MetricFu.rcov[:rcov_opts].join(' ')
38
+ output = ">> #{MetricFu::Rcov.metric_directory}/rcov.txt"
39
+ `rcov #{test_files} #{rcov_opts} #{output}`
40
+ rescue LoadError
41
+ if RUBY_PLATFORM =~ /java/
42
+ puts 'running in jruby - rcov tasks not available'
43
+ else
44
+ puts 'sudo gem install rcov # if you want the rcov tasks'
45
+ end
46
+ end
47
+ end
48
+
49
+
50
+ def analyze
51
+ output = File.open(MetricFu::Rcov.metric_directory + '/rcov.txt').read
52
+ output = output.split(NEW_FILE_MARKER)
53
+ # Throw away the first entry - it's the execution time etc.
54
+ output.shift
55
+ files = {}
56
+ output.each_slice(2) {|out| files[out.first.strip] = out.last}
57
+ files.each_pair {|fname, content| files[fname] = content.split("\n") }
58
+ files.each_pair do |fname, content|
59
+ content.map! do |raw_line|
60
+ if raw_line.match(/^!!/)
61
+ line = Line.new(raw_line.gsub('!!', ' '), false).to_h
62
+ else
63
+ line = Line.new(raw_line, true).to_h
64
+ end
65
+ end
66
+ files[fname] = {:lines => content}
67
+ end
68
+
69
+ # Calculate the percentage of lines run in each file
70
+ @global_total_lines = 0
71
+ @global_total_lines_run = 0
72
+ files.each_pair do |fname, content|
73
+ lines = content[:lines]
74
+ @global_total_lines_run += lines_run = lines.find_all {|line| line[:was_run] == true }.length
75
+ @global_total_lines += total_lines = lines.length
76
+ percent_run = ((lines_run.to_f / total_lines.to_f) * 100).round
77
+ files[fname][:percent_run] = percent_run
78
+ end
79
+ @rcov = files
80
+ end
81
+
82
+ def to_h
83
+ global_percent_run = ((@global_total_lines_run.to_f / @global_total_lines.to_f) * 100)
84
+ {:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,64 @@
1
+ module MetricFu
2
+
3
+ class Reek < Generator
4
+ REEK_REGEX = /^(\S+) (.*) \((.*)\)$/
5
+
6
+ def self.verify_dependencies!
7
+ `reek --help`
8
+ raise 'sudo gem install reek # if you want the reek tasks' unless $?.success?
9
+ end
10
+
11
+ def emit
12
+ files_to_reek = MetricFu.reek[:dirs_to_reek].map{|dir| Dir[File.join(dir, "**/*.rb")] }
13
+ files = remove_excluded_files(files_to_reek.flatten)
14
+ @output = `reek #{files.join(" ")}`
15
+ @output = massage_for_reek_12 if reek_12?
16
+ end
17
+
18
+ def reek_12?
19
+ return false if @output.length == 0
20
+ (@output =~ /^"/) != 0
21
+ end
22
+
23
+ def massage_for_reek_12
24
+ section_break = ''
25
+ @output.split("\n").map do |line|
26
+ case line
27
+ when /^ /
28
+ "#{line.gsub(/^ /, '')}\n"
29
+ else
30
+ parts = line.split(" -- ")
31
+ if parts[1].nil?
32
+ "#{line}\n"
33
+ else
34
+ warnings = parts[1].gsub(/ \(.*\):/, ':')
35
+ result = "#{section_break}\"#{parts[0]}\" -- #{warnings}\n"
36
+ section_break = "\n"
37
+ result
38
+ end
39
+ end
40
+ end.join
41
+ end
42
+
43
+ def analyze
44
+ @matches = @output.chomp.split("\n\n").map{|m| m.split("\n") }
45
+ @matches = @matches.map do |match|
46
+ file_path = match.shift.split('--').first
47
+ file_path = file_path.gsub('"', ' ').strip
48
+ code_smells = match.map do |smell|
49
+ match_object = smell.match(REEK_REGEX)
50
+ next unless match_object
51
+ {:method => match_object[1].strip,
52
+ :message => match_object[2].strip,
53
+ :type => match_object[3].strip}
54
+ end.compact
55
+ {:file_path => file_path, :code_smells => code_smells}
56
+ end
57
+ end
58
+
59
+ def to_h
60
+ {:reek => {:matches => @matches}}
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,32 @@
1
+ module MetricFu
2
+ class Roodi < Generator
3
+
4
+ def self.verify_dependencies!
5
+ `roodi --help`
6
+ raise 'sudo gem install roodi # if you want the roodi tasks' unless $?.success?
7
+ end
8
+
9
+
10
+ def emit
11
+ files_to_analyze = MetricFu.roodi[:dirs_to_roodi].map{|dir| Dir[File.join(dir, "**/*.rb")] }
12
+ files = remove_excluded_files(files_to_analyze.flatten)
13
+ @output = `roodi #{files.join(" ")}`
14
+ end
15
+
16
+ def analyze
17
+ @matches = @output.chomp.split("\n").map{|m| m.split(" - ") }
18
+ total = @matches.pop
19
+ @matches.reject! {|array| array.empty? }
20
+ @matches.map! do |match|
21
+ file, line = match[0].split(':')
22
+ problem = match[1]
23
+ {:file => file, :line => line, :problem => problem}
24
+ end
25
+ @roodi_results = {:total => total, :problems => @matches}
26
+ end
27
+
28
+ def to_h
29
+ {:roodi => @roodi_results}
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,208 @@
1
+ module MetricFu
2
+
3
+ class Saikuro < Generator
4
+
5
+ def emit
6
+ relative_path = [File.dirname(__FILE__), '..', '..',
7
+ 'vendor', 'saikuro', 'saikuro.rb']
8
+ saikuro = File.expand_path(File.join(relative_path))
9
+
10
+ MetricFu.saikuro[:input_directory] = format_directories
11
+
12
+ options_string = MetricFu.saikuro.inject("") do |options, option|
13
+ options + "--#{option.join(' ')} "
14
+ end
15
+
16
+ sh %{ruby "#{saikuro}" #{options_string}} do |ok, response|
17
+ unless ok
18
+ puts "Saikuro failed with exit status: #{response.exitstatus}"
19
+ exit 1
20
+ end
21
+ end
22
+ end
23
+
24
+ def format_directories
25
+ dirs = MetricFu.saikuro[:input_directory].join(" | ")
26
+ "\"#{dirs}\""
27
+ end
28
+
29
+ def analyze
30
+ @files = []
31
+ saikuro_results.each do |path|
32
+ if Saikuro::SFile.is_valid_text_file?(path)
33
+ file = Saikuro::SFile.new(path)
34
+ if file
35
+ @files << file
36
+ end
37
+ end
38
+ end
39
+ @files = @files.sort_by do |file|
40
+ file.elements.
41
+ max {|a,b| a.complexity.to_i <=> b.complexity.to_i}.
42
+ complexity.to_i
43
+ end
44
+ @files.reverse!
45
+ klasses = []
46
+ @files.each {|f| klasses << f.elements}
47
+ klasses.flatten!
48
+ @classes = klasses.sort_by {|k| k.complexity.to_i}
49
+ @classes.reverse!
50
+ meths = []
51
+ @files.each {|f|
52
+ f.elements.each {|el|
53
+ el.defs.each {|defn|
54
+ defn.name = "#{el.name}##{defn.name}"
55
+ meths << defn}
56
+ }
57
+ }
58
+ meths = meths.sort_by {|meth| meth.complexity.to_i}
59
+ @meths = meths.reverse
60
+ end
61
+
62
+ def to_h
63
+ files = @files.map do |file|
64
+ my_file = file.to_h
65
+ my_file[:filename] = file.filename
66
+ my_file
67
+ end
68
+ {:saikuro => {:files => files,
69
+ :classes => @classes.map {|c| c.to_h},
70
+ :methods => @meths.map {|m| m.to_h}
71
+ }
72
+ }
73
+ end
74
+
75
+ def saikuro_results
76
+ Dir.glob("#{metric_directory}/**/*.html")
77
+ end
78
+ end
79
+
80
+ class Saikuro::SFile
81
+
82
+ attr_reader :elements
83
+
84
+ def initialize(path)
85
+ @path = path
86
+ @file_handle = File.open(@path, "r")
87
+ @elements = []
88
+ get_elements
89
+ end
90
+
91
+ def self.is_valid_text_file?(path)
92
+ File.open(path, "r") do |f|
93
+ if f.eof? || !f.readline.match(/--/)
94
+ return false
95
+ else
96
+ return true
97
+ end
98
+ end
99
+ end
100
+
101
+ def filename
102
+ File.basename(@path, '_cyclo.html')
103
+ end
104
+
105
+ def to_h
106
+ merge_classes
107
+ {:classes => @elements}
108
+ end
109
+
110
+ def get_elements
111
+ begin
112
+ while (line = @file_handle.readline) do
113
+ return [] if line.nil? || line !~ /\S/
114
+ element ||= nil
115
+ if line.match /START/
116
+ unless element.nil?
117
+ @elements << element
118
+ element = nil
119
+ end
120
+ line = @file_handle.readline
121
+ element = Saikuro::ParsingElement.new(line)
122
+ elsif line.match /END/
123
+ @elements << element if element
124
+ element = nil
125
+ else
126
+ element << line if element
127
+ end
128
+ end
129
+ rescue EOFError
130
+ nil
131
+ end
132
+ end
133
+
134
+
135
+ def merge_classes
136
+ new_elements = []
137
+ get_class_names.each do |target_class|
138
+ elements = @elements.find_all {|el| el.name == target_class }
139
+ complexity = 0
140
+ lines = 0
141
+ defns = []
142
+ elements.each do |el|
143
+ complexity += el.complexity.to_i
144
+ lines += el.lines.to_i
145
+ defns << el.defs
146
+ end
147
+
148
+ new_element = {:class_name => target_class,
149
+ :complexity => complexity,
150
+ :lines => lines,
151
+ :methods => defns.flatten.map {|d| d.to_h}}
152
+ new_element[:methods] = new_element[:methods].
153
+ sort_by {|x| x[:complexity] }.
154
+ reverse
155
+
156
+ new_elements << new_element
157
+ end
158
+ @elements = new_elements if new_elements
159
+ end
160
+
161
+ def get_class_names
162
+ class_names = []
163
+ @elements.each do |element|
164
+ unless class_names.include?(element.name)
165
+ class_names << element.name
166
+ end
167
+ end
168
+ class_names
169
+ end
170
+
171
+ end
172
+
173
+ class Saikuro::ParsingElement
174
+ TYPE_REGEX=/Type:(.*) Name/
175
+ NAME_REGEX=/Name:(.*) Complexity/
176
+ COMPLEXITY_REGEX=/Complexity:(.*) Lines/
177
+ LINES_REGEX=/Lines:(.*)/
178
+
179
+ attr_reader :complexity, :lines, :defs, :element_type
180
+ attr_accessor :name
181
+
182
+ def initialize(line)
183
+ @line = line
184
+ @element_type = line.match(TYPE_REGEX)[1].strip
185
+ @name = line.match(NAME_REGEX)[1].strip
186
+ @complexity = line.match(COMPLEXITY_REGEX)[1].strip
187
+ @lines = line.match(LINES_REGEX)[1].strip
188
+ @defs = []
189
+ end
190
+
191
+ def <<(line)
192
+ @defs << Saikuro::ParsingElement.new(line)
193
+ end
194
+
195
+ def to_h
196
+ base = {:name => @name, :complexity => @complexity.to_i, :lines => @lines.to_i}
197
+ unless @defs.empty?
198
+ defs = @defs.map do |my_def|
199
+ my_def = my_def.to_h
200
+ my_def.delete(:defs)
201
+ my_def
202
+ end
203
+ base[:defs] = defs
204
+ end
205
+ return base
206
+ end
207
+ end
208
+ end