code_metric_fu 4.14.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 (296) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +28 -0
  3. data/.metrics +3 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +15 -0
  6. data/.rubocop_todo.yml +69 -0
  7. data/.simplecov +74 -0
  8. data/.travis.yml +22 -0
  9. data/.yardopts +4 -0
  10. data/AUTHORS +12 -0
  11. data/CONTRIBUTING.md +47 -0
  12. data/CONTRIBUTORS +76 -0
  13. data/DEV.md +76 -0
  14. data/Gemfile +74 -0
  15. data/Guardfile +30 -0
  16. data/HISTORY.md +705 -0
  17. data/MIT-LICENSE +22 -0
  18. data/README.md +299 -0
  19. data/Rakefile +27 -0
  20. data/TODO.md +118 -0
  21. data/appveyor.yml +31 -0
  22. data/bin/metric_fu +9 -0
  23. data/bin/mf-cane +10 -0
  24. data/bin/mf-churn +10 -0
  25. data/bin/mf-flay +10 -0
  26. data/bin/mf-reek +10 -0
  27. data/bin/mf-roodi +10 -0
  28. data/bin/mf-saikuro +10 -0
  29. data/certs/bf4.pem +22 -0
  30. data/checksum/.gitkeep +0 -0
  31. data/checksum/metric_fu-4.10.0.gem.sha512 +1 -0
  32. data/checksum/metric_fu-4.11.0.gem.sha512 +1 -0
  33. data/checksum/metric_fu-4.11.1.gem.sha512 +1 -0
  34. data/checksum/metric_fu-4.11.2.gem.sha512 +1 -0
  35. data/checksum/metric_fu-4.11.3.gem.sha512 +1 -0
  36. data/checksum/metric_fu-4.11.4.gem.sha512 +1 -0
  37. data/checksum/metric_fu-4.12.0.gem.sha512 +1 -0
  38. data/checksum/metric_fu-4.2.0.gem.sha512 +1 -0
  39. data/checksum/metric_fu-4.2.1.gem.sha512 +1 -0
  40. data/checksum/metric_fu-4.3.0.gem.sha512 +1 -0
  41. data/checksum/metric_fu-4.3.1.gem.sha512 +1 -0
  42. data/checksum/metric_fu-4.4.0.gem.sha512 +1 -0
  43. data/checksum/metric_fu-4.4.1.gem.sha512 +1 -0
  44. data/checksum/metric_fu-4.4.2.gem.sha512 +1 -0
  45. data/checksum/metric_fu-4.4.3.gem.sha512 +1 -0
  46. data/checksum/metric_fu-4.4.4.gem.sha512 +1 -0
  47. data/checksum/metric_fu-4.5.0.gem.sha512 +1 -0
  48. data/checksum/metric_fu-4.5.1.gem.sha512 +1 -0
  49. data/checksum/metric_fu-4.5.2.gem.sha512 +1 -0
  50. data/checksum/metric_fu-4.6.0.gem.sha512 +1 -0
  51. data/checksum/metric_fu-4.7.0.gem.sha512 +1 -0
  52. data/checksum/metric_fu-4.7.1.gem.sha512 +1 -0
  53. data/checksum/metric_fu-4.7.2.gem.sha512 +1 -0
  54. data/checksum/metric_fu-4.7.3.gem.sha512 +1 -0
  55. data/checksum/metric_fu-4.7.4.gem.sha512 +1 -0
  56. data/checksum/metric_fu-4.8.0.gem.sha512 +1 -0
  57. data/checksum/metric_fu-4.9.0.gem.sha512 +1 -0
  58. data/config/roodi_config.yml +22 -0
  59. data/config/rubocop.yml +269 -0
  60. data/gem_tasks/build.rake +197 -0
  61. data/gem_tasks/rubocop.rake +10 -0
  62. data/gem_tasks/usage_test.rake +19 -0
  63. data/gem_tasks/yard.rake +24 -0
  64. data/lib/metric_fu/calculate.rb +10 -0
  65. data/lib/metric_fu/cli/client.rb +26 -0
  66. data/lib/metric_fu/cli/helper.rb +80 -0
  67. data/lib/metric_fu/cli/parser.rb +138 -0
  68. data/lib/metric_fu/configuration.rb +150 -0
  69. data/lib/metric_fu/constantize.rb +57 -0
  70. data/lib/metric_fu/data_structures/line_numbers.rb +112 -0
  71. data/lib/metric_fu/data_structures/location.rb +110 -0
  72. data/lib/metric_fu/data_structures/sexp_node.rb +107 -0
  73. data/lib/metric_fu/environment.rb +129 -0
  74. data/lib/metric_fu/errors/analysis_error.rb +4 -0
  75. data/lib/metric_fu/formatter/html.rb +96 -0
  76. data/lib/metric_fu/formatter/syntax.rb +45 -0
  77. data/lib/metric_fu/formatter/yaml.rb +18 -0
  78. data/lib/metric_fu/formatter.rb +40 -0
  79. data/lib/metric_fu/gem_run.rb +70 -0
  80. data/lib/metric_fu/gem_version.rb +92 -0
  81. data/lib/metric_fu/generator.rb +135 -0
  82. data/lib/metric_fu/io.rb +132 -0
  83. data/lib/metric_fu/loader.rb +105 -0
  84. data/lib/metric_fu/logger.rb +62 -0
  85. data/lib/metric_fu/logging/mf_debugger.rb +23 -0
  86. data/lib/metric_fu/metric.rb +143 -0
  87. data/lib/metric_fu/metrics/cane/generator.rb +95 -0
  88. data/lib/metric_fu/metrics/cane/grapher.rb +37 -0
  89. data/lib/metric_fu/metrics/cane/metric.rb +34 -0
  90. data/lib/metric_fu/metrics/cane/report.html.erb +87 -0
  91. data/lib/metric_fu/metrics/cane/violations.rb +46 -0
  92. data/lib/metric_fu/metrics/churn/generator.rb +37 -0
  93. data/lib/metric_fu/metrics/churn/hotspot.rb +43 -0
  94. data/lib/metric_fu/metrics/churn/metric.rb +29 -0
  95. data/lib/metric_fu/metrics/churn/report.html.erb +58 -0
  96. data/lib/metric_fu/metrics/flay/generator.rb +51 -0
  97. data/lib/metric_fu/metrics/flay/grapher.rb +37 -0
  98. data/lib/metric_fu/metrics/flay/hotspot.rb +52 -0
  99. data/lib/metric_fu/metrics/flay/metric.rb +28 -0
  100. data/lib/metric_fu/metrics/flay/report.html.erb +29 -0
  101. data/lib/metric_fu/metrics/flog/generator.rb +113 -0
  102. data/lib/metric_fu/metrics/flog/grapher.rb +77 -0
  103. data/lib/metric_fu/metrics/flog/hotspot.rb +46 -0
  104. data/lib/metric_fu/metrics/flog/metric.rb +29 -0
  105. data/lib/metric_fu/metrics/flog/report.html.erb +50 -0
  106. data/lib/metric_fu/metrics/hotspots/analysis/analyzed_problems.rb +34 -0
  107. data/lib/metric_fu/metrics/hotspots/analysis/analyzer_tables.rb +114 -0
  108. data/lib/metric_fu/metrics/hotspots/analysis/grouping.rb +23 -0
  109. data/lib/metric_fu/metrics/hotspots/analysis/groupings.rb +12 -0
  110. data/lib/metric_fu/metrics/hotspots/analysis/problems.rb +20 -0
  111. data/lib/metric_fu/metrics/hotspots/analysis/ranked_problem_location.rb +70 -0
  112. data/lib/metric_fu/metrics/hotspots/analysis/ranking.rb +29 -0
  113. data/lib/metric_fu/metrics/hotspots/analysis/rankings.rb +91 -0
  114. data/lib/metric_fu/metrics/hotspots/analysis/record.rb +32 -0
  115. data/lib/metric_fu/metrics/hotspots/analysis/scoring_strategies.rb +24 -0
  116. data/lib/metric_fu/metrics/hotspots/analysis/table.rb +67 -0
  117. data/lib/metric_fu/metrics/hotspots/generator.rb +40 -0
  118. data/lib/metric_fu/metrics/hotspots/hotspot.rb +87 -0
  119. data/lib/metric_fu/metrics/hotspots/hotspot_analyzer.rb +61 -0
  120. data/lib/metric_fu/metrics/hotspots/metric.rb +20 -0
  121. data/lib/metric_fu/metrics/hotspots/report.html.erb +60 -0
  122. data/lib/metric_fu/metrics/rails_best_practices/generator.rb +47 -0
  123. data/lib/metric_fu/metrics/rails_best_practices/grapher.rb +38 -0
  124. data/lib/metric_fu/metrics/rails_best_practices/metric.rb +31 -0
  125. data/lib/metric_fu/metrics/rails_best_practices/report.html.erb +22 -0
  126. data/lib/metric_fu/metrics/rcov/external_client.rb +22 -0
  127. data/lib/metric_fu/metrics/rcov/generator.rb +75 -0
  128. data/lib/metric_fu/metrics/rcov/grapher.rb +37 -0
  129. data/lib/metric_fu/metrics/rcov/hotspot.rb +46 -0
  130. data/lib/metric_fu/metrics/rcov/metric.rb +61 -0
  131. data/lib/metric_fu/metrics/rcov/rcov_format_coverage.rb +149 -0
  132. data/lib/metric_fu/metrics/rcov/rcov_line.rb +48 -0
  133. data/lib/metric_fu/metrics/rcov/report.html.erb +40 -0
  134. data/lib/metric_fu/metrics/rcov/simplecov_formatter.rb +74 -0
  135. data/lib/metric_fu/metrics/reek/generator.rb +97 -0
  136. data/lib/metric_fu/metrics/reek/grapher.rb +55 -0
  137. data/lib/metric_fu/metrics/reek/hotspot.rb +95 -0
  138. data/lib/metric_fu/metrics/reek/metric.rb +26 -0
  139. data/lib/metric_fu/metrics/reek/report.html.erb +35 -0
  140. data/lib/metric_fu/metrics/roodi/generator.rb +41 -0
  141. data/lib/metric_fu/metrics/roodi/grapher.rb +37 -0
  142. data/lib/metric_fu/metrics/roodi/hotspot.rb +39 -0
  143. data/lib/metric_fu/metrics/roodi/metric.rb +24 -0
  144. data/lib/metric_fu/metrics/roodi/report.html.erb +22 -0
  145. data/lib/metric_fu/metrics/saikuro/generator.rb +145 -0
  146. data/lib/metric_fu/metrics/saikuro/hotspot.rb +51 -0
  147. data/lib/metric_fu/metrics/saikuro/metric.rb +31 -0
  148. data/lib/metric_fu/metrics/saikuro/parsing_element.rb +37 -0
  149. data/lib/metric_fu/metrics/saikuro/report.html.erb +71 -0
  150. data/lib/metric_fu/metrics/saikuro/scratch_file.rb +108 -0
  151. data/lib/metric_fu/metrics/stats/generator.rb +82 -0
  152. data/lib/metric_fu/metrics/stats/grapher.rb +40 -0
  153. data/lib/metric_fu/metrics/stats/hotspot.rb +35 -0
  154. data/lib/metric_fu/metrics/stats/metric.rb +28 -0
  155. data/lib/metric_fu/metrics/stats/report.html.erb +44 -0
  156. data/lib/metric_fu/reporter.rb +37 -0
  157. data/lib/metric_fu/reporting/graphs/graph.rb +69 -0
  158. data/lib/metric_fu/reporting/graphs/grapher.rb +66 -0
  159. data/lib/metric_fu/reporting/result.rb +59 -0
  160. data/lib/metric_fu/run.rb +82 -0
  161. data/lib/metric_fu/tasks/metric_fu.rake +54 -0
  162. data/lib/metric_fu/templates/_gem_info.html.erb +8 -0
  163. data/lib/metric_fu/templates/_graph.html.erb +2 -0
  164. data/lib/metric_fu/templates/_report_footer.html.erb +1 -0
  165. data/lib/metric_fu/templates/configuration.rb +25 -0
  166. data/lib/metric_fu/templates/css/bluff.css +15 -0
  167. data/lib/metric_fu/templates/css/buttons.css +82 -0
  168. data/lib/metric_fu/templates/css/default.css +43 -0
  169. data/lib/metric_fu/templates/css/integrity.css +337 -0
  170. data/lib/metric_fu/templates/css/rcov.css +32 -0
  171. data/lib/metric_fu/templates/css/reset.css +7 -0
  172. data/lib/metric_fu/templates/css/syntax.css +19 -0
  173. data/lib/metric_fu/templates/index.html.erb +13 -0
  174. data/lib/metric_fu/templates/javascripts/bluff-min.js +1 -0
  175. data/lib/metric_fu/templates/javascripts/bluff_graph.js +15 -0
  176. data/lib/metric_fu/templates/javascripts/excanvas.js +35 -0
  177. data/lib/metric_fu/templates/javascripts/highcharts.js +294 -0
  178. data/lib/metric_fu/templates/javascripts/highcharts_graph.js +38 -0
  179. data/lib/metric_fu/templates/javascripts/js-class.js +1 -0
  180. data/lib/metric_fu/templates/javascripts/standalone-framework.js +17 -0
  181. data/lib/metric_fu/templates/javascripts/utils.js +9 -0
  182. data/lib/metric_fu/templates/layout.html.erb +41 -0
  183. data/lib/metric_fu/templates/metrics_template.rb +86 -0
  184. data/lib/metric_fu/templates/report.html.erb +31 -0
  185. data/lib/metric_fu/templates/report.rb +41 -0
  186. data/lib/metric_fu/templates/template.rb +247 -0
  187. data/lib/metric_fu/utility.rb +79 -0
  188. data/lib/metric_fu/version.rb +9 -0
  189. data/lib/metric_fu.rb +143 -0
  190. data/metric_fu.gemspec +72 -0
  191. data/spec/capture_warnings.rb +55 -0
  192. data/spec/cli/helper_spec.rb +165 -0
  193. data/spec/dummy/.gitignore +1 -0
  194. data/spec/dummy/.gitkeep +0 -0
  195. data/spec/dummy/.metrics +4 -0
  196. data/spec/dummy/lib/.gitkeep +0 -0
  197. data/spec/dummy/lib/bad_encoding.rb +6 -0
  198. data/spec/dummy/spec/.gitkeep +0 -0
  199. data/spec/fixtures/20090630.yml +7922 -0
  200. data/spec/fixtures/coverage-153.rb +11 -0
  201. data/spec/fixtures/coverage.rb +13 -0
  202. data/spec/fixtures/exit0.sh +3 -0
  203. data/spec/fixtures/exit1.sh +3 -0
  204. data/spec/fixtures/hotspots/flog.yml +86 -0
  205. data/spec/fixtures/hotspots/generator.yml +47 -0
  206. data/spec/fixtures/hotspots/generator_analysis.yml +53 -0
  207. data/spec/fixtures/hotspots/reek.yml +14 -0
  208. data/spec/fixtures/hotspots/roodi.yml +13 -0
  209. data/spec/fixtures/hotspots/saikuro.yml +27 -0
  210. data/spec/fixtures/hotspots/several_metrics.yml +47 -0
  211. data/spec/fixtures/hotspots/stats.yml +4 -0
  212. data/spec/fixtures/hotspots/three_metrics_on_same_file.yml +36 -0
  213. data/spec/fixtures/line_numbers/foo.rb +33 -0
  214. data/spec/fixtures/line_numbers/module.rb +11 -0
  215. data/spec/fixtures/line_numbers/module_surrounds_class.rb +15 -0
  216. data/spec/fixtures/line_numbers/two_classes.rb +11 -0
  217. data/spec/fixtures/metric_missing.yml +1 -0
  218. data/spec/fixtures/rcov_output.txt +135 -0
  219. data/spec/fixtures/saikuro/app/controllers/sessions_controller.rb_cyclo.html +10 -0
  220. data/spec/fixtures/saikuro/app/controllers/users_controller.rb_cyclo.html +16 -0
  221. data/spec/fixtures/saikuro/index_cyclo.html +155 -0
  222. data/spec/fixtures/saikuro_sfiles/thing.rb_cyclo.html +11 -0
  223. data/spec/metric_fu/calculate_spec.rb +21 -0
  224. data/spec/metric_fu/configuration_spec.rb +90 -0
  225. data/spec/metric_fu/data_structures/line_numbers_spec.rb +63 -0
  226. data/spec/metric_fu/data_structures/location_spec.rb +110 -0
  227. data/spec/metric_fu/formatter/configuration_spec.rb +44 -0
  228. data/spec/metric_fu/formatter/html_spec.rb +138 -0
  229. data/spec/metric_fu/formatter/yaml_spec.rb +61 -0
  230. data/spec/metric_fu/formatter_spec.rb +49 -0
  231. data/spec/metric_fu/gem_version_spec.rb +12 -0
  232. data/spec/metric_fu/generator_spec.rb +130 -0
  233. data/spec/metric_fu/loader_spec.rb +10 -0
  234. data/spec/metric_fu/metric_spec.rb +46 -0
  235. data/spec/metric_fu/metrics/cane/configuration_spec.rb +22 -0
  236. data/spec/metric_fu/metrics/cane/generator_spec.rb +184 -0
  237. data/spec/metric_fu/metrics/churn/configuration_spec.rb +13 -0
  238. data/spec/metric_fu/metrics/churn/generator_spec.rb +64 -0
  239. data/spec/metric_fu/metrics/flay/configuration_spec.rb +13 -0
  240. data/spec/metric_fu/metrics/flay/generator_spec.rb +105 -0
  241. data/spec/metric_fu/metrics/flay/grapher_spec.rb +57 -0
  242. data/spec/metric_fu/metrics/flog/configuration_spec.rb +18 -0
  243. data/spec/metric_fu/metrics/flog/generator_spec.rb +77 -0
  244. data/spec/metric_fu/metrics/flog/grapher_spec.rb +107 -0
  245. data/spec/metric_fu/metrics/hotspots/analysis/analyzed_problems_spec.rb +104 -0
  246. data/spec/metric_fu/metrics/hotspots/analysis/analyzer_tables_spec.rb +71 -0
  247. data/spec/metric_fu/metrics/hotspots/analysis/ranking_spec.rb +30 -0
  248. data/spec/metric_fu/metrics/hotspots/analysis/rankings_spec.rb +97 -0
  249. data/spec/metric_fu/metrics/hotspots/analysis/table_spec.rb +6 -0
  250. data/spec/metric_fu/metrics/hotspots/generator_spec.rb +46 -0
  251. data/spec/metric_fu/metrics/hotspots/hotspot_analyzer_spec.rb +10 -0
  252. data/spec/metric_fu/metrics/hotspots/hotspot_spec.rb +16 -0
  253. data/spec/metric_fu/metrics/rails_best_practices/configuration_spec.rb +55 -0
  254. data/spec/metric_fu/metrics/rails_best_practices/generator_spec.rb +33 -0
  255. data/spec/metric_fu/metrics/rails_best_practices/grapher_spec.rb +62 -0
  256. data/spec/metric_fu/metrics/rcov/configuration_spec.rb +28 -0
  257. data/spec/metric_fu/metrics/rcov/generator_spec.rb +22 -0
  258. data/spec/metric_fu/metrics/rcov/grapher_spec.rb +57 -0
  259. data/spec/metric_fu/metrics/rcov/hotspot_spec.rb +20 -0
  260. data/spec/metric_fu/metrics/rcov/rcov_line_spec.rb +89 -0
  261. data/spec/metric_fu/metrics/rcov/simplecov_formatter_spec.rb +67 -0
  262. data/spec/metric_fu/metrics/reek/configuration_spec.rb +13 -0
  263. data/spec/metric_fu/metrics/reek/generator_spec.rb +169 -0
  264. data/spec/metric_fu/metrics/reek/grapher_spec.rb +66 -0
  265. data/spec/metric_fu/metrics/roodi/configuration_spec.rb +14 -0
  266. data/spec/metric_fu/metrics/roodi/generator_spec.rb +82 -0
  267. data/spec/metric_fu/metrics/roodi/grapher_spec.rb +57 -0
  268. data/spec/metric_fu/metrics/saikuro/configuration_spec.rb +25 -0
  269. data/spec/metric_fu/metrics/saikuro/generator_spec.rb +71 -0
  270. data/spec/metric_fu/metrics/stats/generator_spec.rb +96 -0
  271. data/spec/metric_fu/metrics/stats/grapher_spec.rb +69 -0
  272. data/spec/metric_fu/reporter_spec.rb +41 -0
  273. data/spec/metric_fu/reporting/graphs/graph_spec.rb +44 -0
  274. data/spec/metric_fu/reporting/graphs/grapher_spec.rb +24 -0
  275. data/spec/metric_fu/reporting/result_spec.rb +50 -0
  276. data/spec/metric_fu/run_spec.rb +197 -0
  277. data/spec/metric_fu/templates/configuration_spec.rb +51 -0
  278. data/spec/metric_fu/templates/metrics_template_spec.rb +11 -0
  279. data/spec/metric_fu/templates/report_spec.rb +15 -0
  280. data/spec/metric_fu/templates/template_spec.rb +233 -0
  281. data/spec/metric_fu/utility_spec.rb +12 -0
  282. data/spec/metric_fu_spec.rb +33 -0
  283. data/spec/quality_spec.rb +114 -0
  284. data/spec/shared/configured.rb +45 -0
  285. data/spec/shared/test_coverage.rb +95 -0
  286. data/spec/spec_helper.rb +54 -0
  287. data/spec/support/deferred_garbaged_collection.rb +33 -0
  288. data/spec/support/helper_methods.rb +32 -0
  289. data/spec/support/matcher_create_file.rb +37 -0
  290. data/spec/support/matcher_create_files.rb +43 -0
  291. data/spec/support/suite.rb +26 -0
  292. data/spec/support/test_fixtures.rb +37 -0
  293. data/spec/support/timeout.rb +7 -0
  294. data/spec/support/usage_test.rb +150 -0
  295. data/spec/usage_test_spec.rb +93 -0
  296. metadata +757 -0
@@ -0,0 +1,46 @@
1
+ class MetricFu::FlogHotspot < MetricFu::Hotspot
2
+ COLUMNS = %w{score}
3
+
4
+ def columns
5
+ COLUMNS
6
+ end
7
+
8
+ def name
9
+ :flog
10
+ end
11
+
12
+ def map_strategy
13
+ :score
14
+ end
15
+
16
+ def reduce_strategy
17
+ :average
18
+ end
19
+
20
+ def score_strategy
21
+ :identity
22
+ end
23
+
24
+ def generate_records(data, table)
25
+ return if data == nil
26
+ Array(data[:method_containers]).each do |method_container|
27
+ Array(method_container[:methods]).each do |entry|
28
+ file_path = entry[1][:path].sub(%r{^/}, "") if entry[1][:path]
29
+ location = MetricFu::Location.for(entry.first)
30
+ table << {
31
+ "metric" => name,
32
+ "score" => entry[1][:score],
33
+ "file_path" => file_path,
34
+ "class_name" => location.class_name,
35
+ "method_name" => location.method_name
36
+ }
37
+ end
38
+ end
39
+ end
40
+
41
+ def present_group(group)
42
+ occurences = group.size
43
+ complexity = get_mean(group.column("score"))
44
+ "#{'average ' if occurences > 1}complexity is %.1f" % complexity
45
+ end
46
+ end
@@ -0,0 +1,29 @@
1
+ module MetricFu
2
+ class MetricFlog < Metric
3
+ def name
4
+ :flog
5
+ end
6
+
7
+ def default_run_options
8
+ { dirs_to_flog: MetricFu::Io::FileSystem.directory("code_dirs"), continue: true, all: true, quiet: true }
9
+ end
10
+
11
+ def has_graph?
12
+ true
13
+ end
14
+
15
+ def enable
16
+ if MetricFu.configuration.mri?
17
+ super
18
+ else
19
+ MetricFu.logger.debug("Flog is only available in MRI due to flog tasks")
20
+ end
21
+ end
22
+
23
+ def activate
24
+ activate_library "flog"
25
+ activate_library "flog_cli"
26
+ super
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ <h3>Flog Results</h3>
2
+ <p><a href='http://ruby.sadi.st/Flog.html'>Flog</a> measures code complexity.</p>
3
+
4
+ <%= render_partial 'graph', {:graph_name => 'flog'} %>
5
+
6
+ <h2>Total Flog score for all methods: <%= round_to_tenths @flog[:total]%></h2>
7
+ <h2>Average Flog score for all methods: <%= round_to_tenths @flog[:average]%></h2>
8
+
9
+ <table>
10
+ <tr>
11
+ <th>File</th>
12
+ <th>Total score</th>
13
+ <th>Methods</th>
14
+ <th>Average score</th>
15
+ <th>Highest score</th>
16
+ </tr>
17
+ <% @flog[:method_containers].each do |method_container| %>
18
+ <tr>
19
+ <td><a href="#<%= method_container[:path].gsub(/[^a-z]+/, '_') %>"><%= method_container[:path] %></a></td>
20
+ <td><%= round_to_tenths method_container[:total_score] %></td>
21
+ <td><%= method_container[:methods].size %></td>
22
+ <td><%= round_to_tenths method_container[:average_score] %></td>
23
+ <td><%= round_to_tenths method_container[:highest_score] %></td>
24
+ </tr>
25
+ <% end %>
26
+ </table>
27
+
28
+ <% @flog[:method_containers].each do |method_container| %>
29
+ <h2 id="<%= method_container[:path].gsub(/[^a-z]+/, '_') %>"><%= link_to_filename(method_container[:path]) %></h2>
30
+
31
+ <% method_container[:methods].each do |full_method_name, method_info| %>
32
+ <% path, line = method_info[:path].split(":") if method_info[:path] %>
33
+ <p><%= link_to_filename(path, line, full_method_name) %></p>
34
+ <p>Total Score: <%= round_to_tenths method_info[:score]%></p>
35
+ <table>
36
+ <tr>
37
+ <th>Score</th>
38
+ <th>Operator</th>
39
+ </tr>
40
+ <% method_info[:operators].each do |operator, score| %>
41
+ <tr>
42
+ <td><%= round_to_tenths score %></td>
43
+ <td><%= operator %></td>
44
+ </tr>
45
+ <% end %>
46
+ </table>
47
+ <% end %>
48
+ <% end %>
49
+
50
+ <%= render_partial 'report_footer' %>
@@ -0,0 +1,34 @@
1
+ module MetricFu
2
+ class HotspotAnalyzedProblems
3
+ MetricFu.metrics_require { "hotspots/analysis/ranked_problem_location" }
4
+
5
+ def initialize(hotspot_rankings, analyzer_tables)
6
+ @hotspot_rankings = hotspot_rankings
7
+ @analyzer_tables = analyzer_tables
8
+ end
9
+
10
+ def worst_items
11
+ worst_items = {}
12
+ worst_items[:files] = worst(@hotspot_rankings.worst_files, :file)
13
+ worst_items[:classes] = worst(@hotspot_rankings.worst_classes, :class)
14
+ worst_items[:methods] = worst(@hotspot_rankings.worst_methods, :method)
15
+ worst_items
16
+ end
17
+
18
+ private
19
+
20
+ # @param rankings [Array<MetricFu::HotspotRankings>]
21
+ # @param granularity [Symbol] one of :class, :method, :file
22
+ def worst(rankings, granularity)
23
+ rankings.map do |ranked_item_name|
24
+ sub_table = get_sub_table(granularity, ranked_item_name)
25
+ MetricFu::HotspotRankedProblemLocation.new(sub_table, granularity)
26
+ end
27
+ end
28
+
29
+ def get_sub_table(granularity, ranked_item_name)
30
+ tables = @analyzer_tables.tables_for(granularity)
31
+ tables[ranked_item_name]
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,114 @@
1
+ module MetricFu
2
+ class AnalyzerTables
3
+ %w(table).each do |path|
4
+ MetricFu.metrics_require { "hotspots/analysis/#{path}" }
5
+ end
6
+
7
+ def initialize(analyzer_columns)
8
+ @columns = analyzer_columns
9
+ end
10
+
11
+ def generate_records
12
+ build_lookups!
13
+ process_rows!
14
+ end
15
+
16
+ def tool_tables
17
+ @tool_tables ||= make_table_hash(@columns)
18
+ end
19
+
20
+ def table
21
+ @table ||= make_table(@columns)
22
+ end
23
+
24
+ def tables_for(item)
25
+ {
26
+ class: @class_tables,
27
+ method: @method_tables,
28
+ file: @file_tables,
29
+ tool: @tool_tables
30
+ }.fetch(item) do
31
+ raise ArgumentError, "Item must be :class, :method, or :file, but was #{item}"
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ def make_table(columns)
38
+ MetricFu::Table.new(column_names: columns)
39
+ end
40
+
41
+ def make_table_hash(columns)
42
+ Hash.new { |hash, key|
43
+ hash[key] = make_table(columns)
44
+ }
45
+ end
46
+
47
+ # COLLECT AND PROCESS RAW HOTSPOT METRICS
48
+ def build_lookups!
49
+ @class_and_method_to_file ||= {}
50
+ # Build a mapping from [class,method] => filename
51
+ # (and make sure the mapping is unique)
52
+ table.each do |row|
53
+ # We know that Saikuro provides the wrong data
54
+ # TODO inject Saikuro reference
55
+ next if row["metric"] == :saikuro
56
+ key = [row["class_name"], row["method_name"]]
57
+ file_path = row["file_path"]
58
+ @class_and_method_to_file[key] ||= file_path
59
+ end
60
+ end
61
+
62
+ # COLLECT AND PROCESS RAW HOTSPOT METRICS
63
+ def process_rows!
64
+ # Correct incorrect rows in the table
65
+ table.each do |row|
66
+ row_metric = row["metric"] # perf optimization
67
+ # TODO inject Saikuro reference
68
+ if row_metric == :saikuro
69
+ fix_row_file_path!(row)
70
+ end
71
+ tool_tables[row_metric] << row
72
+ file_tables[row["file_path"]] << row
73
+ class_tables[row["class_name"]] << row
74
+ method_tables[row["method_name"]] << row
75
+ end
76
+ end
77
+
78
+ # COLLECT AND PROCESS RAW HOTSPOT METRICS
79
+ def fix_row_file_path!(row)
80
+ # We know that Saikuro rows are broken
81
+ # next unless row['metric'] == :saikuro
82
+ key = [row["class_name"], row["method_name"]]
83
+ current_file_path = row["file_path"].to_s
84
+ correct_file_path = @class_and_method_to_file[key]
85
+ if !correct_file_path.nil? && correct_file_path.include?(current_file_path)
86
+ row["file_path"] = correct_file_path
87
+ else
88
+ # There wasn't an exact match, so we can do a substring match
89
+ matching_file_path = file_paths.detect {|file_path|
90
+ !file_path.nil? && file_path.include?(current_file_path)
91
+ }
92
+ if matching_file_path
93
+ row["file_path"] = matching_file_path
94
+ end
95
+ end
96
+ end
97
+
98
+ def file_paths
99
+ @file_paths ||= @table.column("file_path").uniq
100
+ end
101
+
102
+ def file_tables
103
+ @file_tables ||= make_table_hash(@columns)
104
+ end
105
+
106
+ def class_tables
107
+ @class_tables ||= make_table_hash(@columns)
108
+ end
109
+
110
+ def method_tables
111
+ @method_tables ||= make_table_hash(@columns)
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,23 @@
1
+ %w(table).each do |path|
2
+ MetricFu.metrics_require { "hotspots/analysis/#{path}" }
3
+ end
4
+ module MetricFu
5
+ class Grouping
6
+ def initialize(table, opts)
7
+ column_name = opts.fetch(:by)
8
+ hash = {}
9
+ if column_name.to_sym == :metric # special optimized case
10
+ hash = table.group_by_metric
11
+ else
12
+ raise "Unexpected column_name #{column_name}"
13
+ end
14
+ @arr = hash.to_a
15
+ end
16
+
17
+ def each
18
+ @arr.each do |value, rows|
19
+ yield value, rows
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,12 @@
1
+ MetricFu.metrics_require { "hotspots/analysis/grouping" }
2
+ module MetricFu
3
+ class HotspotGroupings
4
+ def initialize(table, opts)
5
+ @table, @opts = table, opts
6
+ end
7
+
8
+ def get_grouping
9
+ MetricFu::Grouping.new(@table, @opts)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ MetricFu.metrics_require { "hotspots/analysis/groupings" }
2
+ module MetricFu
3
+ class HotspotProblems
4
+ def initialize(sub_table)
5
+ @grouping = group_by(sub_table, "metric")
6
+ end
7
+
8
+ def problems
9
+ problems = {}
10
+ @grouping.each do |metric, table|
11
+ problems[metric] = MetricFu::Hotspot.analyzer_for_metric(metric).present_group(table)
12
+ end
13
+ problems
14
+ end
15
+
16
+ def group_by(sub_table, by = "metric")
17
+ MetricFu::HotspotGroupings.new(sub_table, by: by).get_grouping
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,70 @@
1
+ MetricFu.lib_require { "errors/analysis_error" }
2
+ MetricFu.metrics_require { "hotspots/analysis/problems" }
3
+ module MetricFu
4
+ class HotspotRankedProblemLocation
5
+ MetricFu.data_structures_require { "location" }
6
+ attr_reader :sub_table, :granularity
7
+ def initialize(sub_table, granularity)
8
+ @sub_table = sub_table
9
+ @granularity = granularity
10
+ end
11
+
12
+ def to_hash
13
+ {
14
+ "location" => location.to_hash,
15
+ "details" => MetricFu::Utility.stringify_keys(problems),
16
+ }
17
+ end
18
+
19
+ # @todo redo as item,value, options = {}
20
+ # Note that the other option for 'details' is :detailed (this isn't
21
+ # at all clear from this method itself
22
+ def problems
23
+ @problems ||= MetricFu::HotspotProblems.new(sub_table).problems
24
+ end
25
+
26
+ def location
27
+ @location ||= case granularity
28
+ when :class then class_location
29
+ when :method then method_location
30
+ when :file then file_location
31
+ else raise ArgumentError, "Item must be :class, :method, or :file"
32
+ end
33
+ end
34
+
35
+ def file_path
36
+ first_row.file_path
37
+ end
38
+
39
+ def class_name
40
+ first_row.class_name
41
+ end
42
+
43
+ def method_name
44
+ first_row.method_name
45
+ end
46
+
47
+ def file_location
48
+ MetricFu::Location.get(file_path, nil, nil)
49
+ end
50
+
51
+ def method_location
52
+ MetricFu::Location.get(file_path, class_name, method_name)
53
+ end
54
+
55
+ def class_location
56
+ MetricFu::Location.get(file_path, class_name, nil)
57
+ end
58
+
59
+ def first_row
60
+ assert_sub_table_has_data
61
+ @first_row ||= sub_table[0]
62
+ end
63
+
64
+ def assert_sub_table_has_data
65
+ if (sub_table.length == 0)
66
+ raise MetricFu::AnalysisError, "The #{item} '#{value}' does not have any rows in the analysis table"
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,29 @@
1
+ require "forwardable"
2
+ module MetricFu
3
+ class Ranking
4
+ extend Forwardable
5
+
6
+ def initialize
7
+ @items_to_score = {}
8
+ end
9
+
10
+ def top
11
+ sorted_items
12
+ end
13
+
14
+ def percentile(item)
15
+ index = sorted_items.index(item)
16
+ worse_item_count = (length - (index + 1))
17
+ worse_item_count.to_f / length
18
+ end
19
+
20
+ def_delegator :@items_to_score, :has_key?, :scored?
21
+ def_delegators :@items_to_score, :[], :[]=, :length, :each, :delete, :fetch
22
+
23
+ private
24
+
25
+ def sorted_items
26
+ @sorted_items ||= @items_to_score.sort_by { |_item, score| -score }.map { |item, _score| item }
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,91 @@
1
+ MetricFu.metrics_require do
2
+ [
3
+ "hotspots/analysis/ranking"
4
+ ]
5
+ end
6
+ module MetricFu
7
+ class HotspotRankings
8
+ def initialize(tool_tables)
9
+ @tool_tables = tool_tables
10
+ @file_ranking = MetricFu::Ranking.new
11
+ @class_ranking = MetricFu::Ranking.new
12
+ @method_ranking = MetricFu::Ranking.new
13
+ end
14
+
15
+ def calculate_scores(tool_analyzers, granularities)
16
+ tool_analyzers.each do |analyzer|
17
+ calculate_scores_by_granularities(analyzer, granularities)
18
+ end
19
+ end
20
+
21
+ def worst_methods
22
+ @method_ranking.delete(nil)
23
+ @method_ranking.top
24
+ end
25
+
26
+ def worst_classes
27
+ @class_ranking.delete(nil)
28
+ @class_ranking.top
29
+ end
30
+
31
+ def worst_files
32
+ @file_ranking.delete(nil)
33
+ @file_ranking.top
34
+ end
35
+
36
+ private
37
+
38
+ def calculate_scores_by_granularities(analyzer, granularities)
39
+ granularities.each do |granularity|
40
+ calculate_score_for_granularity(analyzer, granularity)
41
+ end
42
+ end
43
+
44
+ def calculate_score_for_granularity(analyzer, granularity)
45
+ metric_ranking = calculate_metric_scores(granularity, analyzer)
46
+
47
+ add_to_master_ranking(
48
+ ranking(granularity),
49
+ metric_ranking,
50
+ analyzer
51
+ )
52
+ end
53
+
54
+ # CALCULATES METRIC HOTSPOT SCORES / RANKINGS PER map/reduce in HOTSPOT subclasses
55
+ def calculate_metric_scores(granularity, analyzer)
56
+ metric_ranking = MetricFu::Ranking.new
57
+ metric_violations = @tool_tables[analyzer.name]
58
+ metric_violations.each do |row|
59
+ location = row[granularity]
60
+ metric_ranking[location] ||= []
61
+ metric_ranking[location] << analyzer.map(row)
62
+ end
63
+
64
+ metric_ranking.each do |item, scores|
65
+ metric_ranking[item] = analyzer.reduce(scores)
66
+ end
67
+
68
+ metric_ranking
69
+ end
70
+
71
+ def ranking(column_name)
72
+ case column_name
73
+ when "file_path"
74
+ @file_ranking
75
+ when "class_name"
76
+ @class_ranking
77
+ when "method_name"
78
+ @method_ranking
79
+ else
80
+ raise ArgumentError, "Invalid column name #{column_name}"
81
+ end
82
+ end
83
+
84
+ def add_to_master_ranking(master_ranking, metric_ranking, analyzer)
85
+ metric_ranking.each do |item, _|
86
+ master_ranking[item] ||= 0
87
+ master_ranking[item] += analyzer.score(metric_ranking, item) # scaling? Do we just add in the raw score?
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,32 @@
1
+ module MetricFu
2
+ class Record
3
+ attr_reader :data
4
+
5
+ def initialize(data, _columns)
6
+ @data = data
7
+ end
8
+
9
+ def method_missing(name, *args, &block)
10
+ key = name.to_s
11
+ if key == "fetch"
12
+ @data.send(name, *args, &block)
13
+ elsif @data.has_key?(key)
14
+ @data[key]
15
+ else
16
+ super(name, *args, &block)
17
+ end
18
+ end
19
+
20
+ def []=(key, value)
21
+ @data[key] = value
22
+ end
23
+
24
+ def [](key)
25
+ @data[key]
26
+ end
27
+
28
+ def has_key?(key)
29
+ @data.has_key?(key)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,24 @@
1
+ module MetricFu
2
+ module HotspotScoringStrategies
3
+ module_function
4
+
5
+ # per project score percentile
6
+ def percentile(ranking, item)
7
+ ranking.percentile(item)
8
+ end
9
+
10
+ # Use the score you got
11
+ # (ex flog score of 20 is not bad even if it is the top one in project)
12
+ def identity(ranking, item)
13
+ ranking.fetch(item)
14
+ end
15
+
16
+ def sum(scores)
17
+ scores.inject(&:+)
18
+ end
19
+
20
+ def average(scores)
21
+ sum(scores).to_f / scores.size.to_f
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,67 @@
1
+ %w(record).each do |path|
2
+ MetricFu.metrics_require { "hotspots/analysis/#{path}" }
3
+ end
4
+
5
+ module MetricFu
6
+ class Table
7
+ include Enumerable
8
+
9
+ def initialize(opts = {})
10
+ @rows = []
11
+ @columns = opts.fetch(:column_names)
12
+
13
+ @make_index = opts.fetch(:make_index) { true }
14
+ @metric_index = {}
15
+ end
16
+
17
+ def <<(row)
18
+ record = nil
19
+ if row.is_a?(MetricFu::Record)
20
+ record = row
21
+ else
22
+ record = MetricFu::Record.new(row, @columns)
23
+ end
24
+ @rows << record
25
+ updated_key_index(record) if @make_index
26
+ end
27
+
28
+ def each
29
+ @rows.each do |row|
30
+ yield row
31
+ end
32
+ end
33
+
34
+ def size
35
+ length
36
+ end
37
+
38
+ def length
39
+ @rows.length
40
+ end
41
+
42
+ def [](index)
43
+ @rows[index]
44
+ end
45
+
46
+ def column(column_name)
47
+ arr = []
48
+ @rows.each do |row|
49
+ arr << row[column_name]
50
+ end
51
+ arr
52
+ end
53
+
54
+ def group_by_metric
55
+ @metric_index.to_a
56
+ end
57
+
58
+ private
59
+
60
+ def updated_key_index(record)
61
+ if record.has_key?("metric")
62
+ @metric_index[record.metric] ||= MetricFu::Table.new(column_names: @columns, make_index: false)
63
+ @metric_index[record.metric] << record
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,40 @@
1
+ MetricFu.metrics_require { "hotspots/hotspot_analyzer" }
2
+ module MetricFu
3
+ class HotspotsGenerator < Generator
4
+ def self.metric
5
+ :hotspots
6
+ end
7
+
8
+ def initialize(options = {})
9
+ MetricFu::Metric.enabled_metrics.each do |metric|
10
+ require_hotspot(metric.name)
11
+ end
12
+ super
13
+ end
14
+
15
+ def emit
16
+ # no-op
17
+ end
18
+
19
+ def analyze
20
+ analyzer = MetricFu::HotspotAnalyzer.new(MetricFu.result.result_hash)
21
+ @hotspots = analyzer.hotspots
22
+ end
23
+
24
+ def to_h
25
+ result = { hotspots: {} }
26
+ @hotspots.each do |granularity, hotspots|
27
+ result[:hotspots][granularity.to_s] = hotspots.map(&:to_hash)
28
+ end
29
+ result
30
+ end
31
+
32
+ private
33
+
34
+ def require_hotspot(metric_name)
35
+ require "metric_fu/metrics/#{metric_name}/hotspot"
36
+ rescue LoadError
37
+ mf_debug "*** No hotspot for #{metric_name}"
38
+ end
39
+ end
40
+ end