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,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