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,138 @@
1
+ require "optparse"
2
+ module MetricFu
3
+ module Cli
4
+ # see https://github.com/florianpilz/CLI-Option-Parser-Examples
5
+ # https://raw.github.com/florianpilz/micro-optparse/master/lib/micro-optparse/parser.rb
6
+ module MicroOptParse
7
+ class Parser
8
+ attr_accessor :banner, :version
9
+ def initialize
10
+ @options = []
11
+ @used_short = []
12
+ @default_values = nil
13
+ yield self if block_given?
14
+ end
15
+
16
+ def process!(arguments = ARGV)
17
+ parser = build_option_parser
18
+ process_options(parser, arguments)
19
+ end
20
+
21
+ def option(name, desc, settings = {})
22
+ @options << [name, desc, settings]
23
+ end
24
+
25
+ private
26
+
27
+ def build_option_parser
28
+ @result = (@default_values || {}).clone # reset or new
29
+
30
+ @optionparser ||= OptionParser.new do |p| # prepare only once
31
+ configure_options(p)
32
+ end
33
+ end
34
+
35
+ def process_options(parser, arguments)
36
+ begin
37
+ parser.parse!(arguments)
38
+ rescue OptionParser::ParseError => e
39
+ puts e.message
40
+ MetricFu::Cli.immediate_shutdown!
41
+ end
42
+
43
+ @result
44
+ end
45
+
46
+ def configure_options(p)
47
+ add_general_options(@options, p)
48
+
49
+ add_format_option(p)
50
+ add_output_option(p)
51
+
52
+ p.banner = @banner unless @banner.nil?
53
+ p.on_tail("-h", "--help", "Show this message") { puts p; success! }
54
+ short = @used_short.include?("v") ? "-V" : "-v"
55
+ p.on_tail(short, "--version", "Print version") { puts @version; success! } unless @version.nil?
56
+ p.on_tail("--debug-info", "Print debug info") { debug_info; success! }
57
+ @default_values = @result.clone # save default values to reset @result in subsequent calls
58
+ end
59
+
60
+ def add_general_options(options, p)
61
+ options.each do |o|
62
+ add_general_option(p, o)
63
+ end
64
+ end
65
+
66
+ def add_general_option(p, o)
67
+ @used_short << short = o[2][:short] || short_from(o[0])
68
+ @result[o[0]] = o[2][:default] || false # set default
69
+ klass = o[2][:default].class == Integer ? Integer : o[2][:default].class
70
+
71
+ if [TrueClass, FalseClass, NilClass].include?(klass) # boolean switch
72
+ p.on("-" << short, "--[no-]" << o[0].to_s.gsub("_", "-"), o[1]) { |x| @result[o[0]] = x }
73
+ else # argument with parameter
74
+ p.on("-" << short, "--" << o[0].to_s.gsub("_", "-") << " " << o[2][:default].to_s, klass, o[1]) { |x| @result[o[0]] = x }
75
+ end
76
+ end
77
+
78
+ def add_output_option(p)
79
+ p.on("--out FILE|DIR",
80
+ "Specify the file or directory to use for output",
81
+ "This option applies to the previously",
82
+ "specified --format, or the default format",
83
+ "if no format is specified. Paths are relative to",
84
+ "#{MetricFu.run_path.join(MetricFu::Io::FileSystem.directory('base_directory'))}",
85
+ "Check the specific formatter\'s docs to see",
86
+ "whether to pass a file or a dir.") do |o|
87
+ @result[:format] ||= MetricFu::Formatter::DEFAULT
88
+ @result[:format].last << o
89
+ end
90
+ end
91
+
92
+ def add_format_option(p)
93
+ p.on("--format FORMAT",
94
+ "Specify the formatter to use for output.",
95
+ "This option can be specified multiple times.",
96
+ "Specify a built-in formatter from the list below,",
97
+ "or the fully-qualified class name of your own custom formatter.",
98
+ *format_descriptions) do |f|
99
+ @result[:format] ||= []
100
+ @result[:format] << [f]
101
+ end
102
+ end
103
+
104
+ def short_from(name)
105
+ name.to_s.chars.each do |c|
106
+ next if @used_short.include?(c) || c == "_"
107
+ return c # returns from short_from method
108
+ end
109
+ end
110
+
111
+ def debug_info
112
+ extend(MetricFu::Environment)
113
+ require "pp"
114
+ pp debug_info
115
+ end
116
+
117
+ # Build a nicely formatted list of built-in
118
+ # formatter keys and their descriptions
119
+ # @see MetricFu::Formatter::BUILTIN_FORMATS
120
+ # @example
121
+ # format_descriptions #=> [" yaml : Generates the raw output as yaml"]
122
+ # @return [Array<String>] in the form of
123
+ # " <key> : <description>."
124
+ def format_descriptions
125
+ formats = MetricFu::Formatter::BUILTIN_FORMATS
126
+ max = formats.keys.map(&:length).max
127
+ formats.keys.sort.map do |key|
128
+ " #{key}#{' ' * (max - key.length)} : #{formats[key][1]}"
129
+ end
130
+ end
131
+
132
+ def success!
133
+ MetricFu::Cli.complete!
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,150 @@
1
+ MetricFu.lib_require { "logger" }
2
+ module MetricFu
3
+ # Even though the below class methods are defined on the MetricFu module
4
+ # They are included here as they deal with configuration
5
+
6
+ # The @configuration class variable holds a global type configuration
7
+ # object for any parts of the system to use.
8
+ # TODO Configuration should probably be a singleton class
9
+ def self.configuration
10
+ @configuration ||= Configuration.new
11
+ end
12
+
13
+ def self.configure
14
+ configuration.tap(&:configure_metrics)
15
+ end
16
+
17
+ # = Configuration
18
+ #
19
+ # The Configuration class, as it sounds, provides methods for
20
+ # configuring the behaviour of MetricFu.
21
+ #
22
+ # == Customization for CruiseControl.rb
23
+ #
24
+ # The Configuration class checks for the presence of a
25
+ # 'CC_BUILD_ARTIFACTS' environment variable. If it's found
26
+ # it will change the default output directory from the default
27
+ # "tmp/metric_fu to the directory represented by 'CC_BUILD_ARTIFACTS'
28
+ #
29
+ # == Metric Configuration
30
+ #
31
+ # Each metric can be configured by e.g.
32
+ # config.configure_metric(:flog) do |flog|
33
+ # flog.enable
34
+ # flog.dirs_to_flog = %w(app lib spec)
35
+ # ...
36
+ # end
37
+ #
38
+ # or iterate over all metrics to configure by e.g.
39
+ # config.configure_metrics.each do |metric|
40
+ # ...
41
+ # end
42
+ #
43
+ # == Formatter Configuration
44
+ #
45
+ # Formatters can be configured by e.g.
46
+ # config.configure_formatter(:html)
47
+ # config.configure_formatter(:yaml, "customreport.yml")
48
+ # config.configure_formatter(MyCustomFormatter)
49
+ #
50
+ class Configuration
51
+ require_relative "environment"
52
+ require_relative "io"
53
+ require_relative "formatter"
54
+ require_relative "templates/configuration"
55
+
56
+ # TODO: Remove need to include the module
57
+ include MetricFu::Environment
58
+
59
+ def initialize #:nodoc:#
60
+ reset
61
+ end
62
+
63
+ # TODO review if these code is functionally duplicated in the
64
+ # base generator initialize
65
+ attr_reader :formatters
66
+ def reset
67
+ # TODO: Remove calls to self and/or allow querying the
68
+ # template/filesystem/metric/graph/environment, etc settings
69
+ # from the configuration instance
70
+ MetricFu::Io::FileSystem.set_directories
71
+ @templates_configuration = MetricFu::Templates::Configuration.new
72
+ MetricFu::Formatter::Templates.templates_configuration = @templates_configuration
73
+ @formatters = []
74
+ @graph_engine = :bluff
75
+ end
76
+
77
+ # This allows us to have a nice syntax like:
78
+ #
79
+ # MetricFu.run do |config|
80
+ # config.configure_metric(:churn) do
81
+ # ...
82
+ # end
83
+ #
84
+ # config.configure_formatter(MyCustomFormatter)
85
+ # end
86
+ #
87
+ # See the README for more information on configuration options.
88
+ # TODO: Consider breaking compatibility by removing this, now unused method
89
+ def self.run
90
+ yield MetricFu.configuration
91
+ end
92
+
93
+ def self.configure_metric(name)
94
+ yield MetricFu::Metric.get_metric(name)
95
+ end
96
+
97
+ def configure_metric(name, &block)
98
+ self.class.configure_metric(name, &block)
99
+ end
100
+
101
+ def configure_metrics
102
+ MetricFu::Io::FileSystem.set_directories
103
+ MetricFu::Metric.metrics.each do |metric|
104
+ if block_given?
105
+ yield metric
106
+ else
107
+ metric.enabled = false
108
+ metric.enable
109
+ end
110
+ metric.activate if metric.enabled unless metric.activated
111
+ end
112
+ end
113
+
114
+ # TODO: Reconsider method name/behavior, as it really adds a formatter
115
+ def configure_formatter(format, output = nil)
116
+ @formatters << MetricFu::Formatter.class_for(format).new(output: output)
117
+ end
118
+
119
+ # @return [Array<Symbol>] names of enabled metrics with graphs
120
+ def graphed_metrics
121
+ # TODO: This is a common enough need to be pushed into MetricFu::Metric as :enabled_metrics_with_graphs
122
+ MetricFu::Metric.enabled_metrics.select(&:has_graph?).map(&:name)
123
+ end
124
+
125
+ def configure_graph_engine(graph_engine)
126
+ @graph_engine = graph_engine
127
+ end
128
+
129
+ def graph_engine
130
+ @graph_engine
131
+ end
132
+
133
+ # This allows us to configure the templates with:
134
+ #
135
+ # MetricFu.run do |config|
136
+ # config.templates_configuration do |templates_config|
137
+ # templates_config.link_prefix = 'http:/'
138
+ # end
139
+ # end
140
+ def templates_configuration
141
+ yield @templates_configuration
142
+ end
143
+
144
+ # @param option [String, Symbol] the requested template option
145
+ # @return [String] the configured template option
146
+ def templates_option(option)
147
+ @templates_configuration.option(option)
148
+ end
149
+ end
150
+ end
@@ -0,0 +1,57 @@
1
+ module MetricFu
2
+ module Constantize
3
+ # Copied from ActiveSupport and modified so as not to introduce a dependency.
4
+ # https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflector/methods.rb#L220
5
+ def constantize(camel_cased_word)
6
+ tries ||= 2
7
+ names = camel_cased_word.split("::")
8
+ names.shift if names.empty? || names.first.empty?
9
+
10
+ names.inject(Object) do |constant, name|
11
+ if constant == Object
12
+ constant.const_get(name)
13
+ else
14
+ candidate = constant.const_get(name)
15
+ next candidate if constant.const_defined?(name, false)
16
+ next candidate unless Object.const_defined?(name)
17
+
18
+ # Go down the ancestors to check it it's owned
19
+ # directly before we reach Object or the end of ancestors.
20
+ constant = constant.ancestors.inject do |const, ancestor|
21
+ break const if ancestor == Object
22
+ break ancestor if ancestor.const_defined?(name, false)
23
+ const
24
+ end
25
+
26
+ # owner is in Object, so raise
27
+ constant.const_get(name, false)
28
+ end
29
+ end
30
+ rescue NameError
31
+ # May need to attempt to require the file and try again.
32
+ begin
33
+ require underscore(camel_cased_word)
34
+ rescue LoadError => e
35
+ mf_log e.message
36
+ end
37
+
38
+ if (tries -= 1).zero?
39
+ raise
40
+ else
41
+ retry
42
+ end
43
+ end
44
+
45
+ # Copied from active_support
46
+ # https://github.com/rails/rails/blob/51cd6bb829c418c5fbf75de1dfbb177233b1b154/activesupport/lib/active_support/inflector/methods.rb#L88
47
+ def underscore(camel_cased_word)
48
+ word = camel_cased_word.to_s.dup
49
+ word.gsub!(/::/, "/")
50
+ word.gsub!(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
51
+ word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
52
+ word.tr!("-", "_")
53
+ word.downcase!
54
+ word
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,112 @@
1
+ MetricFu.data_structures_require { "sexp_node" }
2
+ module MetricFu
3
+ class LineNumbers
4
+ attr_reader :file_path
5
+
6
+ # Parses ruby code to collect line numbers for class, module, and method definitions.
7
+ # Used by metrics that don't provide line numbers for class, module, or methods problems
8
+ # @param contents [String] a string of ruby code
9
+ # @param file_path [String] the file path for the contents, defaults to empty string
10
+ def initialize(contents, file_path = "")
11
+ @file_path = file_path
12
+ @locations = {}
13
+ if contents.to_s.size.zero?
14
+ mf_log "NON PARSEABLE INPUT: File is empty at path #{file_path.inspect}\n\t#{caller.join("\n\t")}"
15
+ else
16
+ parse_code(contents)
17
+ end
18
+ end
19
+
20
+ # @param line_number [Integer]
21
+ # @return [Boolean] if the given line number is in a method
22
+ def in_method?(line_number)
23
+ not method_at_line(line_number) == :no_method_at_line
24
+ end
25
+
26
+ # @param line_number [Integer]
27
+ # @return [String, nil] the method which includes that line number, if any
28
+ # For all collected locations, find the first location
29
+ # where the line_number_range includes the line_number.
30
+ # If a location is found, return the method name (first element)
31
+ # Else return :no_method_at_line
32
+ def method_at_line(line_number)
33
+ default_proc = -> { [:no_method_at_line] }
34
+ @locations.detect(default_proc) do |_method_name, line_number_range|
35
+ line_number_range.include?(line_number)
36
+ end.first
37
+ end
38
+
39
+ # @param method [String] the method name being queried
40
+ # @return [Integer, nil] the line number at which the given method is defined
41
+ def start_line_for_method(method)
42
+ return nil unless @locations.has_key?(method)
43
+ @locations[method].first
44
+ end
45
+
46
+ private
47
+
48
+ def parse_code(contents)
49
+ file_sexp = MetricFu::SexpNode.parse(contents)
50
+ file_sexp && process_ast(file_sexp)
51
+ rescue => e
52
+ # catch errors for files ruby_parser fails on
53
+ mf_log "RUBY PARSE FAILURE: #{e.class}\t#{e.message}\tFILE:#{file_path}\tSEXP:#{file_sexp.inspect}\n\tCONTENT:#{contents.inspect}\n\t#{e.backtrace}"
54
+ end
55
+
56
+ def process_ast(file_sexp)
57
+ node = MetricFu::SexpNode.new(file_sexp)
58
+ case node.node_type
59
+ when nil
60
+ mf_log "No ruby code found in #{file_path}"
61
+ when :class
62
+ process_class(node)
63
+ when :module
64
+ process_module(node)
65
+ else
66
+ mf_debug "SEXP: Parsing line numbers for classes in sexp type #{node.node_type.inspect}"
67
+ mf_debug " in #{file_path}"
68
+ node.each_module { |child_node| process_class(child_node) }
69
+ node.each_class { |child_node| process_class(child_node) }
70
+ end
71
+ end
72
+
73
+ def process_module(module_node)
74
+ module_name = module_node.name
75
+ module_node.each_class do |class_node|
76
+ process_class(class_node, module_name)
77
+ class_node.hide_methods_from_next_round
78
+ end
79
+ process_class(module_node)
80
+ end
81
+
82
+ def process_class(class_node, module_name = nil)
83
+ class_name = class_node.name
84
+ process_singleton_methods(class_node, class_name)
85
+ process_instance_methods(class_node, class_name, module_name)
86
+ process_class_methods(class_node, class_name, module_name)
87
+ end
88
+
89
+ def process_singleton_methods(class_node, class_name)
90
+ class_node.each_singleton_class do |singleton_node|
91
+ singleton_node.each_singleton_method do |singleton_method_node|
92
+ singleton_method_name = singleton_method_node.full_name(class_name)
93
+ @locations[singleton_method_name] = singleton_method_node.line_range
94
+ end
95
+ end
96
+ end
97
+
98
+ def process_instance_methods(class_node, class_name, module_name)
99
+ class_node.each_instance_method do |instance_method_node|
100
+ instance_method_name = instance_method_node.full_name(module_name, class_name)
101
+ @locations[instance_method_name] = instance_method_node.line_range
102
+ end
103
+ end
104
+
105
+ def process_class_methods(class_node, class_name, module_name)
106
+ class_node.each_class_method do |class_method_node|
107
+ class_method_name = class_method_node.full_name(module_name, class_name)
108
+ @locations[class_method_name] = class_method_node.line_range
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,110 @@
1
+ module MetricFu
2
+ class Location
3
+ include Comparable
4
+
5
+ attr_accessor :file_path, :file_name, :line_number,
6
+ :class_name, :method_name, :simple_method_name, :hash, :hash_key
7
+
8
+ def self.get(file_path, class_name, method_name)
9
+ location = new(file_path, class_name, method_name)
10
+ @@locations ||= {}
11
+ @@locations.fetch(location.hash_key) do
12
+ @@locations[location.hash_key] = location
13
+ location.finalize
14
+ location
15
+ end
16
+ end
17
+
18
+ def initialize(file_path, class_name, method_name)
19
+ @file_path = file_path
20
+ @file_name, @line_number = file_path.to_s.split(/:/)
21
+ @class_name = class_name
22
+ @method_name = method_name
23
+ @simple_method_name = @method_name.to_s.sub(@class_name.to_s, "")
24
+ @hash_key = to_key
25
+ @hash = @hash_key.hash
26
+ end
27
+
28
+ def to_hash
29
+ hash = {
30
+ "class_name" => class_name,
31
+ "method_name" => method_name,
32
+ "file_path" => file_path,
33
+ "file_name" => file_name,
34
+ "line_number" => line_number,
35
+ "hash_key" => hash_key,
36
+ }
37
+
38
+ if method_name.to_s.size > 0
39
+ hash = hash.merge("simple_method_name" => simple_method_name)
40
+ else
41
+ hash
42
+ end
43
+ end
44
+
45
+ # defining :eql? and :hash to use Location as a hash key
46
+ def eql?(other)
47
+ @hash == other.hash
48
+ end
49
+
50
+ def <=>(other)
51
+ hash <=> other.hash
52
+ end
53
+
54
+ # Generates the @hash key
55
+ def to_key
56
+ [@file_path, @class_name, @method_name].inspect
57
+ end
58
+
59
+ def self.for(class_or_method_name)
60
+ class_or_method_name = strip_modules(class_or_method_name)
61
+ if class_or_method_name
62
+ begin
63
+ match = class_or_method_name.match(/(.*)((\.|\#|\:\:[a-z])(.+))/)
64
+ rescue => error
65
+ # new error during port to metric_fu occasionally a unintialized
66
+ # MatchData object shows up here. Not expected.
67
+ mf_debug "ERROR on getting location for #{class_or_method_name} #{error.inspect}"
68
+ match = nil
69
+ end
70
+
71
+ # reek reports the method with :: not # on modules like
72
+ # module ApplicationHelper \n def signed_in?, convert it so it records correctly
73
+ # but classes have to start with a capital letter... HACK for REEK bug, reported underlying issue to REEK
74
+ if match
75
+ class_name = strip_modules(match[1])
76
+ method_name = class_or_method_name.gsub(/\:\:/, "#")
77
+ else
78
+ class_name = strip_modules(class_or_method_name)
79
+ method_name = nil
80
+ end
81
+ else
82
+ class_name = nil
83
+ method_name = nil
84
+ end
85
+ get(nil, class_name, method_name)
86
+ end
87
+
88
+ def finalize
89
+ @file_path &&= @file_path.clone
90
+ @file_name &&= @file_name.clone
91
+ @line_number &&= @line_number.clone
92
+ @class_name &&= @class_name.clone
93
+ @method_name &&= @method_name.clone
94
+ freeze # we cache a lot of method call results, so we want location to be immutable
95
+ end
96
+
97
+ private
98
+
99
+ def self.strip_modules(class_or_method_name)
100
+ # reek reports the method with :: not # on modules like
101
+ # module ApplicationHelper \n def signed_in?, convert it so it records correctly
102
+ # but classes have to start with a capital letter... HACK for REEK bug, reported underlying issue to REEK
103
+ if class_or_method_name =~ /\:\:[A-Z]/
104
+ class_or_method_name.split("::").last
105
+ else
106
+ class_or_method_name
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,107 @@
1
+ require "ruby_parser"
2
+ module MetricFu
3
+ SexpNode = Struct.new(:sexp) do
4
+ # @return file_sexp
5
+ def self.parse(contents)
6
+ rp = RubyParser.new
7
+ rp.parse(contents)
8
+ end
9
+ def nil?
10
+ sexp.nil?
11
+ end
12
+
13
+ def node_type
14
+ sexp[0]
15
+ end
16
+
17
+ def name
18
+ sexp[1]
19
+ end
20
+
21
+ def each_of_type(type, node_class = SexpNode)
22
+ sexp.each_of_type(type) do |child_sexp|
23
+ yield node_class.new(child_sexp)
24
+ end
25
+ end
26
+
27
+ def each_module(&block)
28
+ each_of_type(:module, &block)
29
+ end
30
+
31
+ def each_class(&block)
32
+ each_of_type(:class, &block)
33
+ end
34
+
35
+ def each_singleton_class(&block)
36
+ each_of_type(:sclass, SingletonMethodNode, &block)
37
+ end
38
+
39
+ def each_instance_method(&block)
40
+ each_of_type(:defn, InstanceMethodNode, &block)
41
+ end
42
+
43
+ def each_class_method(&block)
44
+ each_of_type(:defs, ClassMethodNode, &block)
45
+ end
46
+
47
+ def first_line
48
+ sexp.line
49
+ end
50
+
51
+ def last_line
52
+ sexp.last.line
53
+ end
54
+
55
+ def line_range
56
+ (first_line..last_line)
57
+ end
58
+
59
+ def hide_methods_from_next_round
60
+ sexp.find_and_replace_all(:defn, :ignore_me)
61
+ sexp.find_and_replace_all(:defs, :ignore_me)
62
+ end
63
+
64
+ def full_method_name(method_separator, class_name, module_name = nil)
65
+ [module_namespace(module_name), class_name, method_separator, name].join
66
+ end
67
+
68
+ def module_namespace(module_name = nil)
69
+ if module_name.nil?
70
+ nil
71
+ else
72
+ [module_name, class_method_separator].join
73
+ end
74
+ end
75
+
76
+ def instance_method_separator
77
+ "#"
78
+ end
79
+
80
+ def class_method_separator
81
+ "::"
82
+ end
83
+ end
84
+ class ClassMethodNode < SexpNode
85
+ def name
86
+ sexp[2]
87
+ end
88
+
89
+ def full_name(module_name, class_name)
90
+ full_method_name(class_method_separator, class_name, module_name)
91
+ end
92
+ end
93
+ class InstanceMethodNode < SexpNode
94
+ def full_name(module_name, class_name)
95
+ full_method_name(instance_method_separator, class_name, module_name)
96
+ end
97
+ end
98
+ class SingletonMethodNode < SexpNode
99
+ def full_name(class_name)
100
+ full_method_name(class_method_separator, class_name)
101
+ end
102
+
103
+ def each_singleton_method(&block)
104
+ each_of_type(:defn, SingletonMethodNode, &block)
105
+ end
106
+ end
107
+ end