code_metric_fu 4.14.0

Sign up to get free protection for your applications and to get access to all the features.
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,132 @@
1
+ MetricFu.lib_require { "utility" }
2
+ module MetricFu
3
+ module Io
4
+ # TODO: Move this module / functionality elsewhere and make less verbose
5
+ module FileSystem
6
+ # TODO: Use a better environmental variable name for the output / artiface dir. Set to a different default in tests.
7
+ @default_artifact_dir = "tmp/metric_fu"
8
+ def self.default_artifact_dir
9
+ @default_artifact_dir
10
+ end
11
+ def self.artifact_dir
12
+ (ENV["CC_BUILD_ARTIFACTS"] || @artifact_dir)
13
+ end
14
+ def self.artifact_dir=(artifact_dir)
15
+ @artifact_dir = artifact_dir
16
+ end
17
+ self.artifact_dir = default_artifact_dir
18
+
19
+ module_function
20
+
21
+ def directories
22
+ @directories ||= {}
23
+ end
24
+
25
+ def directory(name)
26
+ directories.fetch(name) { raise "No such directory configured: #{name}" }
27
+ end
28
+
29
+ def scratch_directory(name)
30
+ File.join(directory("scratch_directory"), name.to_s)
31
+ end
32
+
33
+ def file_globs_to_ignore
34
+ @file_globs_to_ignore ||= []
35
+ end
36
+
37
+ def set_directories
38
+ @directories = {}
39
+ @directories["base_directory"] = MetricFu.artifact_dir
40
+ @directories["scratch_directory"] = MetricFu.scratch_dir
41
+ @directories["output_directory"] = MetricFu.output_dir
42
+ @directories["data_directory"] = MetricFu.data_dir
43
+ create_directories @directories.values
44
+
45
+ @directories["root_directory"] = MetricFu.root_dir
46
+ # TODO Though this is true of the general AwesomeTemplate, it is not necessarily true of templates within each Metric. Each metric should probably know how to use AwesomeTemplate (or whatever)
47
+ @directories["template_directory"] = File.join(@directories.fetch("root_directory"), "lib", "templates")
48
+ @file_globs_to_ignore = []
49
+ set_code_dirs
50
+ end
51
+
52
+ def create_directories(*dirs)
53
+ # due to behavior differences between ruby 1.8.7 and 1.9.3
54
+ # this is good enough for now
55
+ Array(*dirs).each do |dir|
56
+ MetricFu::Utility.mkdir_p dir
57
+ end
58
+ end
59
+
60
+ # Add the 'app' directory if we're running within rails.
61
+ def set_code_dirs
62
+ @directories["code_dirs"] = %w(app lib).select { |dir| Dir.exist?(dir) }
63
+ end
64
+ end
65
+
66
+ # Writes the output to a file or io stream.
67
+ # @param output [String, #to_s] the content to write.
68
+ # @param path_or_io [String, #to_s, IO, #write] a file path
69
+ # or an io stream that responds to write.
70
+ # @return [nil]
71
+ def write_output(output, path_or_io)
72
+ io_for(path_or_io) do |io|
73
+ io.write(output)
74
+ end
75
+ end
76
+
77
+ # Yields an io object for writing output.
78
+ # @example
79
+ # io_for('path/to/file') do |io|
80
+ # io.write(output)
81
+ # end
82
+ #
83
+ # io_for(STDOUT) do |io|
84
+ # io.write(output)
85
+ # end
86
+ #
87
+ # stream = StringIO.new
88
+ # io_for(stream) do |io|
89
+ # io.write(output)
90
+ # end
91
+ #
92
+ # @param path_or_io [String, #to_s, IO, #write] a file path
93
+ # or an io stream that responds to write.
94
+ #
95
+ # @yield [IO] an open stream for writing.
96
+ #
97
+ # @note Given a path to a file, an open file will
98
+ # be yielded and closed after the block completes.
99
+ # Given an existing io stream, the stream will not
100
+ # be automatically closed. Cleanup, if necessary, is
101
+ # the responsibility of the caller.
102
+ def io_for(path_or_io, &block)
103
+ raise ArgumentError, "No path or io provided." if path_or_io.nil?
104
+ raise ArgumentError, "No block given. Cannot yield io stream." unless block_given?
105
+
106
+ if path_or_io.respond_to?(:write)
107
+ # We have an existing open stream...
108
+ block.call(path_or_io)
109
+ else # Otherwise, we assume its a file path...
110
+ file_for(path_or_io, &block)
111
+ end
112
+ end
113
+
114
+ def file_for(path, &block)
115
+ File.open(path_relative_to_base(path), "w") do |file|
116
+ block.call(file)
117
+ end
118
+ end
119
+
120
+ def dir_for(path)
121
+ return nil if path.nil?
122
+ pathname = path_relative_to_base(path)
123
+ MetricFu::Utility.mkdir_p(pathname) unless File.directory?(pathname)
124
+ pathname
125
+ end
126
+
127
+ def path_relative_to_base(path)
128
+ pathname = MetricFu.run_path.join(MetricFu::Io::FileSystem.directory("base_directory")) # make full path relative to base directory
129
+ pathname.join(path)
130
+ end
131
+ end
132
+ end
@@ -0,0 +1,105 @@
1
+ module MetricFu
2
+ class Loader
3
+ # TODO: This class mostly serves to clean up the base MetricFu module,
4
+ # but needs further work
5
+
6
+ attr_reader :loaded_files
7
+ def initialize(lib_root)
8
+ @lib_root = lib_root
9
+ @loaded_files = []
10
+ end
11
+
12
+ def lib_require(base = "", &_block)
13
+ paths = []
14
+ base_path = File.join(@lib_root, base)
15
+ Array((yield paths, base_path)).each do |path|
16
+ file = File.join(base_path, *Array(path))
17
+ require file
18
+ if @loaded_files.include?(file)
19
+ puts "!!!\tAlready loaded #{file}" if !!(ENV["MF_DEBUG"] =~ /true/i)
20
+ else
21
+ @loaded_files << file
22
+ end
23
+ end
24
+ end
25
+
26
+ # TODO: Reduce duplication of directory logic
27
+ # Adds methods x_dir and _x_require for the directory x,
28
+ # relative to the metric_fu lib, to the given klass
29
+ # @param klass [Class] the klass to add methods for the specified directories
30
+ # @yieldreturn [Array<String>] Takes a list of directories and adds readers for each
31
+ # @example For the directory 'metrics', which is relative to the metric_fu lib directory,
32
+ # creates on the klass two methods:
33
+ # ::metrics_dir which returns the full path
34
+ # ::metrics_require which takes a block of files to require relative to the metrics_dir
35
+ def create_dirs(klass)
36
+ class << klass
37
+ Array(yield).each do |dir|
38
+ define_method("#{dir}_dir") do
39
+ File.join(lib_dir, dir)
40
+ end
41
+ module_eval(%(def #{dir}_require(&block); lib_require('#{dir}', &block); end), __FILE__, __LINE__)
42
+ end
43
+ end
44
+ end
45
+
46
+ # Adds method x_dir relative to the metric_fu artifact directory to the given klass
47
+ # And strips any leading non-alphanumerical character from the directory name
48
+ # @param klass [Class] the klass to add methods for the specified artifact sub-directories
49
+ # @yieldreturn [Array<String>] Takes a list of directories and adds readers for each
50
+ # @example For the artifact sub-directory '_scratch', creates on the klass one method:
51
+ # ::scratch_dir (which notably has the leading underscore removed)
52
+ def create_artifact_subdirs(klass)
53
+ class << klass
54
+ Array(yield).each do |dir|
55
+ define_method("#{dir.gsub(/[^A-Za-z0-9]/, '')}_dir") do
56
+ File.join(artifact_dir, dir)
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def setup
63
+ MetricFu.lib_require { "logger" }
64
+ MetricFu.logger.debug_on = !!(ENV["MF_DEBUG"] =~ /true/i)
65
+
66
+ load_metric_configuration
67
+
68
+ MetricFu.lib_require { "reporter" }
69
+ MetricFu.reporting_require { "result" }
70
+
71
+ MetricFu.load_tasks("metric_fu.rake", task_name: "metrics:all")
72
+ end
73
+
74
+ def load_metric_configuration
75
+ MetricFu.lib_require { "configuration" }
76
+ load_installed_metrics
77
+ MetricFu.configuration.configure_metrics
78
+ load_user_configuration
79
+ end
80
+
81
+ def load_installed_metrics
82
+ MetricFu.lib_require { "metric" }
83
+ Dir.glob(File.join(MetricFu.metrics_dir, "**/metric.rb")).each do |metric_config|
84
+ require metric_config
85
+ end
86
+ end
87
+
88
+ def load_user_configuration
89
+ file = File.join(MetricFu.run_dir, ".metrics")
90
+ load file if File.exist?(file)
91
+ end
92
+
93
+ # Load specified task task only once
94
+ # if and only if rake is required and the task is not yet defined
95
+ # to prevent the task from being loaded multiple times
96
+ # @param tasks_relative_path [String] 'metric_fu.rake' by default
97
+ # @param options [Hash] optional task_name to check if loaded
98
+ # @option options [String] :task_name The task_name to load, if not yet loaded
99
+ def load_tasks(tasks_relative_path, options = { task_name: "" })
100
+ if defined?(Rake::Task) and not Rake::Task.task_defined?(options[:task_name])
101
+ load File.join(@lib_root, "tasks", *Array(tasks_relative_path))
102
+ end
103
+ end
104
+ end
105
+ end
@@ -0,0 +1,62 @@
1
+ require "logger"
2
+ require "forwardable"
3
+ module MetricFu
4
+ def self.logger
5
+ @logger ||= ::MetricFu::Logger.new($stdout)
6
+ end
7
+
8
+ class Logger
9
+ extend Forwardable
10
+ MfLogger = ::Logger
11
+
12
+ def initialize(stdout)
13
+ @logger = MfLogger.new(stdout)
14
+ self.debug_on = false
15
+ self.formatter = ->(_severity, _time, _progname, msg) { "#{msg}\n" }
16
+ self.level = "info"
17
+ end
18
+
19
+ def debug_on=(bool)
20
+ self.level = bool ? "debug" : "info"
21
+ end
22
+
23
+ def debug_on
24
+ @logger.level == MfLogger::DEBUG
25
+ end
26
+
27
+ def_delegators :@logger, :info, :warn, :error, :fatal, :unknown
28
+
29
+ LEVELS = {
30
+ "debug" => MfLogger::DEBUG,
31
+ "info" => MfLogger::INFO,
32
+ "warn" => MfLogger::WARN,
33
+ "error" => MfLogger::ERROR,
34
+ "fatal" => MfLogger::FATAL,
35
+ "unknown" => MfLogger::UNKNOWN,
36
+ }
37
+
38
+ def level=(level)
39
+ @logger.level = LEVELS.fetch(level.to_s.downcase) { level }
40
+ end
41
+
42
+ def formatter=(formatter)
43
+ @logger.formatter = formatter
44
+ end
45
+
46
+ def log(msg)
47
+ @logger.info "*" * 5 + msg.to_s
48
+ end
49
+
50
+ def debug(msg)
51
+ @logger.debug "*" * 5 + msg.to_s
52
+ end
53
+ end
54
+ end
55
+ # For backward compatibility
56
+ def mf_debug(msg, &block)
57
+ MetricFu.logger.debug(msg, &block)
58
+ end
59
+
60
+ def mf_log(msg, &block)
61
+ MetricFu.logger.log(msg, &block)
62
+ end
@@ -0,0 +1,23 @@
1
+ warn "MfDebugger if deprecated. Please use MetricFu.logger"
2
+ MetricFu.lib_require { "logger" }
3
+ module MfDebugger
4
+ extend self
5
+ class Logger
6
+ def self.debug_on
7
+ warn "MfDebugger if deprecated. Please use MetricFu.logger"
8
+ MetricFu.logger.debug_on
9
+ end
10
+ def self.debug_on=(bool)
11
+ warn "MfDebugger if deprecated. Please use MetricFu.logger"
12
+ MetricFu.logger.level = bool ? "debug" : "info"
13
+ end
14
+ def self.log(msg, &_block)
15
+ warn "MfDebugger if deprecated. Please use MetricFu.logger"
16
+ MetricFu.logger.info msg
17
+ end
18
+ def self.debug(msg, &_block)
19
+ warn "MfDebugger if deprecated. Please use MetricFu.logger"
20
+ MetricFu.logger.debug msg
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,143 @@
1
+ require "set"
2
+ MetricFu.lib_require { "gem_run" }
3
+ MetricFu.lib_require { "generator" }
4
+ # Encapsulates the configuration options for each metric
5
+ module MetricFu
6
+ class Metric
7
+ attr_accessor :enabled, :activated
8
+
9
+ def initialize
10
+ self.enabled = false
11
+ @libraries = Set.new
12
+ @configured_run_options = {}
13
+ end
14
+
15
+ def enable
16
+ self.enabled = true
17
+ end
18
+
19
+ # TODO: Confirm this catches load errors from requires in subclasses, such as for flog
20
+ def activate
21
+ MetricFu.metrics_require { default_metric_library_paths }
22
+ @libraries.each { |library| require(library) }
23
+ self.activated = true
24
+ rescue LoadError => e
25
+ mf_log "#{name} metric not activated, #{e.message}"
26
+ end
27
+
28
+ # @return metric name [Symbol]
29
+ def name
30
+ not_implemented
31
+ end
32
+
33
+ def gem_name
34
+ name
35
+ end
36
+
37
+ # @return metric run options [Hash]
38
+ def run_options
39
+ default_run_options.merge(configured_run_options)
40
+ end
41
+
42
+ def default_run_args
43
+ run_options.map { |k, v| "--#{k} #{v}" }.join(" ")
44
+ end
45
+
46
+ def run
47
+ not_implemented
48
+ end
49
+
50
+ def run_external(args = default_run_args)
51
+ runner = GemRun.new(
52
+ gem_name: gem_name.to_s,
53
+ metric_name: name.to_s,
54
+ # version: ,
55
+ args: args,
56
+ )
57
+ stdout, stderr, status = runner.run
58
+ # TODO: do something with the stderr
59
+ # for now, just acknowledge we got it
60
+ unless stderr.empty?
61
+ STDERR.puts "STDERR from #{gem_name}:\n#{stderr}"
62
+ end
63
+ # TODO: status.success? is not reliable for distinguishing
64
+ # between a successful run of the metric and problems
65
+ # found by the metric. Talk to other metrics about this.
66
+ MetricFu.logger.debug "#{gem_name} ran with #{status.success? ? 'success' : 'failure'} code #{status.exitstatus}"
67
+ stdout
68
+ end
69
+
70
+ def configured_run_options
71
+ @configured_run_options
72
+ end
73
+
74
+ # @return [Hash] default metric run options
75
+ def default_run_options
76
+ not_implemented
77
+ end
78
+
79
+ # @return [Hash] metric_options
80
+ def has_graph?
81
+ not_implemented
82
+ end
83
+
84
+ @metrics = []
85
+ # @return all subclassed metrics [Array<MetricFu::Metric>]
86
+ # ensure :hotspots runs last
87
+ def self.metrics
88
+ @metrics
89
+ end
90
+
91
+ def self.enabled_metrics
92
+ metrics.select { |metric| metric.enabled && metric.activated }.sort_by { |metric| metric.name == :hotspots ? 1 : 0 }
93
+ end
94
+
95
+ def self.get_metric(name)
96
+ metrics.find { |metric|metric.name.to_s == name.to_s }
97
+ end
98
+
99
+ def self.inherited(subclass)
100
+ @metrics << subclass.new
101
+ end
102
+
103
+ protected
104
+
105
+ # Enable using a syntax such as metric.foo = 'foo'
106
+ # by catching the missing method here,
107
+ # checking if :foo is a key in the default_run_options, and
108
+ # setting the key/value in the @configured_run_options hash
109
+ # TODO: See if we can do this without a method_missing
110
+ def method_missing(method, *args)
111
+ key = method_to_attr(method)
112
+ if default_run_options.has_key?(key)
113
+ configured_run_options[key] = args.first
114
+ else
115
+ raise "#{key} is not a valid configuration option"
116
+ end
117
+ end
118
+
119
+ # Used above to identify the stem of a setter method
120
+ def method_to_attr(method)
121
+ method.to_s.sub(/=$/, "").to_sym
122
+ end
123
+
124
+ private
125
+
126
+ def not_implemented
127
+ raise "Required method #{caller[0]} not implemented in #{__FILE__}"
128
+ end
129
+
130
+ def activate_library(file)
131
+ @libraries << file.strip
132
+ end
133
+
134
+ def default_metric_library_paths
135
+ paths = []
136
+ paths << generator_path = "#{name}/generator"
137
+ if has_graph?
138
+ paths << grapher_path = "#{name}/grapher"
139
+ end
140
+ paths
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,95 @@
1
+ MetricFu.metrics_require { "cane/violations" }
2
+ module MetricFu
3
+ class CaneGenerator < Generator
4
+ attr_reader :violations, :total_violations
5
+
6
+ def self.metric
7
+ :cane
8
+ end
9
+
10
+ def emit
11
+ args = [
12
+ abc_max_param,
13
+ style_measure_param,
14
+ no_doc_param,
15
+ no_readme_param
16
+ ].join
17
+ @output = run!(args)
18
+ end
19
+
20
+ def analyze
21
+ @violations = violations_by_category
22
+ extract_total_violations
23
+ end
24
+
25
+ def to_h
26
+ { cane: { total_violations: @total_violations, violations: @violations } }
27
+ end
28
+
29
+ private
30
+
31
+ def abc_max_param
32
+ options[:abc_max] ? " --abc-max #{options[:abc_max]}" : ""
33
+ end
34
+
35
+ def style_measure_param
36
+ options[:line_length] ? " --style-measure #{options[:line_length]}" : ""
37
+ end
38
+
39
+ def no_doc_param
40
+ options[:no_doc] == "y" ? " --no-doc" : ""
41
+ end
42
+
43
+ def no_readme_param
44
+ options[:no_readme] == "y" ? " --no-readme" : ""
45
+ end
46
+
47
+ def violations_by_category
48
+ violations_output = @output.scan(/(.*?)\n\n(.*?)\n\n/m)
49
+ violations_output.each_with_object({}) do |(category_desc, violation_list), violations|
50
+ category = category_from(category_desc) || :others
51
+ violations[category] ||= []
52
+ violations[category] += violations_for(category, violation_list)
53
+ end
54
+ end
55
+
56
+ def category_from(description)
57
+ category_descriptions = {
58
+ abc_complexity: /ABC complexity/,
59
+ line_style: /style requirements/,
60
+ comment: /comment/,
61
+ documentation: /documentation/
62
+ }
63
+ category, desc_matcher = category_descriptions.find { |_k, v| description =~ v }
64
+ mf_debug desc_matcher.inspect
65
+ category
66
+ end
67
+
68
+ def violations_for(category, violation_list)
69
+ violation_type_for(category).parse(violation_list)
70
+ end
71
+
72
+ def violation_type_for(category)
73
+ case category
74
+ when :abc_complexity
75
+ CaneViolations::AbcComplexity
76
+ when :line_style
77
+ CaneViolations::LineStyle
78
+ when :comment
79
+ CaneViolations::Comment
80
+ when :documentation
81
+ CaneViolations::Documentation
82
+ else
83
+ CaneViolations::Others
84
+ end
85
+ end
86
+
87
+ def extract_total_violations
88
+ if @output =~ /Total Violations: (\d+)/
89
+ @total_violations = $1.to_i
90
+ else
91
+ @total_violations = 0
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,37 @@
1
+ MetricFu.reporting_require { "graphs/grapher" }
2
+ module MetricFu
3
+ class CaneGrapher < Grapher
4
+ attr_accessor :cane_violations, :labels
5
+
6
+ def self.metric
7
+ :cane
8
+ end
9
+
10
+ def initialize
11
+ super
12
+ @cane_violations = []
13
+ @labels = {}
14
+ end
15
+
16
+ def get_metrics(metrics, date)
17
+ if metrics && metrics[:cane]
18
+ @cane_violations.push(metrics[:cane][:total_violations].to_i)
19
+ @labels.update(@labels.size => date)
20
+ end
21
+ end
22
+
23
+ def title
24
+ "Cane: code quality threshold violations"
25
+ end
26
+
27
+ def data
28
+ [
29
+ ["cane", @cane_violations.join(",")]
30
+ ]
31
+ end
32
+
33
+ def output_filename
34
+ "cane.js"
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,34 @@
1
+ module MetricFu
2
+ class MetricCane < Metric
3
+ def name
4
+ :cane
5
+ end
6
+
7
+ def default_run_options
8
+ {
9
+ dirs_to_cane: MetricFu::Io::FileSystem.directory("code_dirs"),
10
+ abc_max: 15,
11
+ line_length: 80,
12
+ no_doc: "n",
13
+ no_readme: "n",
14
+ filetypes: ["rb"]
15
+ }
16
+ end
17
+
18
+ def has_graph?
19
+ true
20
+ end
21
+
22
+ def enable
23
+ if MetricFu.configuration.supports_ripper? && !MetricFu.configuration.ruby18?
24
+ super
25
+ else
26
+ MetricFu.logger.debug("Cane is only available in MRI. It requires ripper and 1.9 hash syntax support")
27
+ end
28
+ end
29
+
30
+ def activate
31
+ super
32
+ end
33
+ end
34
+ end