fastruby-metric_fu 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +18 -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 +58 -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 +29 -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.rb +151 -0
  65. data/lib/metric_fu/calculate.rb +10 -0
  66. data/lib/metric_fu/cli/client.rb +26 -0
  67. data/lib/metric_fu/cli/helper.rb +80 -0
  68. data/lib/metric_fu/cli/parser.rb +138 -0
  69. data/lib/metric_fu/configuration.rb +150 -0
  70. data/lib/metric_fu/constantize.rb +57 -0
  71. data/lib/metric_fu/data_structures/line_numbers.rb +112 -0
  72. data/lib/metric_fu/data_structures/location.rb +110 -0
  73. data/lib/metric_fu/data_structures/sexp_node.rb +107 -0
  74. data/lib/metric_fu/environment.rb +129 -0
  75. data/lib/metric_fu/errors/analysis_error.rb +4 -0
  76. data/lib/metric_fu/formatter.rb +40 -0
  77. data/lib/metric_fu/formatter/html.rb +96 -0
  78. data/lib/metric_fu/formatter/syntax.rb +45 -0
  79. data/lib/metric_fu/formatter/yaml.rb +18 -0
  80. data/lib/metric_fu/gem_run.rb +70 -0
  81. data/lib/metric_fu/gem_version.rb +92 -0
  82. data/lib/metric_fu/generator.rb +135 -0
  83. data/lib/metric_fu/io.rb +132 -0
  84. data/lib/metric_fu/loader.rb +105 -0
  85. data/lib/metric_fu/logger.rb +62 -0
  86. data/lib/metric_fu/logging/mf_debugger.rb +23 -0
  87. data/lib/metric_fu/metric.rb +143 -0
  88. data/lib/metric_fu/metrics/cane/generator.rb +95 -0
  89. data/lib/metric_fu/metrics/cane/grapher.rb +37 -0
  90. data/lib/metric_fu/metrics/cane/metric.rb +34 -0
  91. data/lib/metric_fu/metrics/cane/report.html.erb +87 -0
  92. data/lib/metric_fu/metrics/cane/violations.rb +46 -0
  93. data/lib/metric_fu/metrics/churn/generator.rb +37 -0
  94. data/lib/metric_fu/metrics/churn/hotspot.rb +43 -0
  95. data/lib/metric_fu/metrics/churn/metric.rb +29 -0
  96. data/lib/metric_fu/metrics/churn/report.html.erb +58 -0
  97. data/lib/metric_fu/metrics/flay/generator.rb +51 -0
  98. data/lib/metric_fu/metrics/flay/grapher.rb +37 -0
  99. data/lib/metric_fu/metrics/flay/hotspot.rb +52 -0
  100. data/lib/metric_fu/metrics/flay/metric.rb +28 -0
  101. data/lib/metric_fu/metrics/flay/report.html.erb +29 -0
  102. data/lib/metric_fu/metrics/flog/generator.rb +113 -0
  103. data/lib/metric_fu/metrics/flog/grapher.rb +77 -0
  104. data/lib/metric_fu/metrics/flog/hotspot.rb +46 -0
  105. data/lib/metric_fu/metrics/flog/metric.rb +29 -0
  106. data/lib/metric_fu/metrics/flog/report.html.erb +50 -0
  107. data/lib/metric_fu/metrics/hotspots/analysis/analyzed_problems.rb +34 -0
  108. data/lib/metric_fu/metrics/hotspots/analysis/analyzer_tables.rb +114 -0
  109. data/lib/metric_fu/metrics/hotspots/analysis/grouping.rb +23 -0
  110. data/lib/metric_fu/metrics/hotspots/analysis/groupings.rb +12 -0
  111. data/lib/metric_fu/metrics/hotspots/analysis/problems.rb +20 -0
  112. data/lib/metric_fu/metrics/hotspots/analysis/ranked_problem_location.rb +70 -0
  113. data/lib/metric_fu/metrics/hotspots/analysis/ranking.rb +29 -0
  114. data/lib/metric_fu/metrics/hotspots/analysis/rankings.rb +91 -0
  115. data/lib/metric_fu/metrics/hotspots/analysis/record.rb +32 -0
  116. data/lib/metric_fu/metrics/hotspots/analysis/scoring_strategies.rb +24 -0
  117. data/lib/metric_fu/metrics/hotspots/analysis/table.rb +67 -0
  118. data/lib/metric_fu/metrics/hotspots/generator.rb +40 -0
  119. data/lib/metric_fu/metrics/hotspots/hotspot.rb +87 -0
  120. data/lib/metric_fu/metrics/hotspots/hotspot_analyzer.rb +61 -0
  121. data/lib/metric_fu/metrics/hotspots/metric.rb +20 -0
  122. data/lib/metric_fu/metrics/hotspots/report.html.erb +60 -0
  123. data/lib/metric_fu/metrics/rails_best_practices/generator.rb +47 -0
  124. data/lib/metric_fu/metrics/rails_best_practices/grapher.rb +38 -0
  125. data/lib/metric_fu/metrics/rails_best_practices/metric.rb +31 -0
  126. data/lib/metric_fu/metrics/rails_best_practices/report.html.erb +22 -0
  127. data/lib/metric_fu/metrics/rcov/external_client.rb +22 -0
  128. data/lib/metric_fu/metrics/rcov/generator.rb +75 -0
  129. data/lib/metric_fu/metrics/rcov/grapher.rb +37 -0
  130. data/lib/metric_fu/metrics/rcov/hotspot.rb +46 -0
  131. data/lib/metric_fu/metrics/rcov/metric.rb +61 -0
  132. data/lib/metric_fu/metrics/rcov/rcov_format_coverage.rb +149 -0
  133. data/lib/metric_fu/metrics/rcov/rcov_line.rb +48 -0
  134. data/lib/metric_fu/metrics/rcov/report.html.erb +40 -0
  135. data/lib/metric_fu/metrics/rcov/simplecov_formatter.rb +74 -0
  136. data/lib/metric_fu/metrics/reek/generator.rb +94 -0
  137. data/lib/metric_fu/metrics/reek/grapher.rb +55 -0
  138. data/lib/metric_fu/metrics/reek/hotspot.rb +95 -0
  139. data/lib/metric_fu/metrics/reek/metric.rb +26 -0
  140. data/lib/metric_fu/metrics/reek/report.html.erb +35 -0
  141. data/lib/metric_fu/metrics/roodi/generator.rb +40 -0
  142. data/lib/metric_fu/metrics/roodi/grapher.rb +37 -0
  143. data/lib/metric_fu/metrics/roodi/hotspot.rb +39 -0
  144. data/lib/metric_fu/metrics/roodi/metric.rb +24 -0
  145. data/lib/metric_fu/metrics/roodi/report.html.erb +22 -0
  146. data/lib/metric_fu/metrics/saikuro/generator.rb +145 -0
  147. data/lib/metric_fu/metrics/saikuro/hotspot.rb +51 -0
  148. data/lib/metric_fu/metrics/saikuro/metric.rb +31 -0
  149. data/lib/metric_fu/metrics/saikuro/parsing_element.rb +37 -0
  150. data/lib/metric_fu/metrics/saikuro/report.html.erb +71 -0
  151. data/lib/metric_fu/metrics/saikuro/scratch_file.rb +108 -0
  152. data/lib/metric_fu/metrics/stats/generator.rb +82 -0
  153. data/lib/metric_fu/metrics/stats/grapher.rb +40 -0
  154. data/lib/metric_fu/metrics/stats/hotspot.rb +35 -0
  155. data/lib/metric_fu/metrics/stats/metric.rb +28 -0
  156. data/lib/metric_fu/metrics/stats/report.html.erb +44 -0
  157. data/lib/metric_fu/reporter.rb +37 -0
  158. data/lib/metric_fu/reporting/graphs/graph.rb +69 -0
  159. data/lib/metric_fu/reporting/graphs/grapher.rb +66 -0
  160. data/lib/metric_fu/reporting/result.rb +59 -0
  161. data/lib/metric_fu/run.rb +82 -0
  162. data/lib/metric_fu/tasks/metric_fu.rake +54 -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 +10 -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 +37 -0
  183. data/lib/metric_fu/templates/metrics_template.rb +80 -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 +244 -0
  187. data/lib/metric_fu/utility.rb +79 -0
  188. data/lib/metric_fu/version.rb +9 -0
  189. data/metric_fu.gemspec +71 -0
  190. data/spec/capture_warnings.rb +55 -0
  191. data/spec/cli/helper_spec.rb +165 -0
  192. data/spec/dummy/.gitignore +1 -0
  193. data/spec/dummy/.gitkeep +0 -0
  194. data/spec/dummy/lib/.gitkeep +0 -0
  195. data/spec/dummy/lib/bad_encoding.rb +6 -0
  196. data/spec/dummy/spec/.gitkeep +0 -0
  197. data/spec/fixtures/20090630.yml +7922 -0
  198. data/spec/fixtures/coverage-153.rb +11 -0
  199. data/spec/fixtures/coverage.rb +13 -0
  200. data/spec/fixtures/exit0.sh +3 -0
  201. data/spec/fixtures/exit1.sh +3 -0
  202. data/spec/fixtures/hotspots/flog.yml +86 -0
  203. data/spec/fixtures/hotspots/generator.yml +47 -0
  204. data/spec/fixtures/hotspots/generator_analysis.yml +53 -0
  205. data/spec/fixtures/hotspots/reek.yml +14 -0
  206. data/spec/fixtures/hotspots/roodi.yml +13 -0
  207. data/spec/fixtures/hotspots/saikuro.yml +27 -0
  208. data/spec/fixtures/hotspots/several_metrics.yml +47 -0
  209. data/spec/fixtures/hotspots/stats.yml +4 -0
  210. data/spec/fixtures/hotspots/three_metrics_on_same_file.yml +36 -0
  211. data/spec/fixtures/line_numbers/foo.rb +33 -0
  212. data/spec/fixtures/line_numbers/module.rb +11 -0
  213. data/spec/fixtures/line_numbers/module_surrounds_class.rb +15 -0
  214. data/spec/fixtures/line_numbers/two_classes.rb +11 -0
  215. data/spec/fixtures/metric_missing.yml +1 -0
  216. data/spec/fixtures/rcov_output.txt +135 -0
  217. data/spec/fixtures/saikuro/app/controllers/sessions_controller.rb_cyclo.html +10 -0
  218. data/spec/fixtures/saikuro/app/controllers/users_controller.rb_cyclo.html +16 -0
  219. data/spec/fixtures/saikuro/index_cyclo.html +155 -0
  220. data/spec/fixtures/saikuro_sfiles/thing.rb_cyclo.html +11 -0
  221. data/spec/metric_fu/calculate_spec.rb +21 -0
  222. data/spec/metric_fu/configuration_spec.rb +90 -0
  223. data/spec/metric_fu/data_structures/line_numbers_spec.rb +63 -0
  224. data/spec/metric_fu/data_structures/location_spec.rb +110 -0
  225. data/spec/metric_fu/formatter/configuration_spec.rb +44 -0
  226. data/spec/metric_fu/formatter/html_spec.rb +138 -0
  227. data/spec/metric_fu/formatter/yaml_spec.rb +61 -0
  228. data/spec/metric_fu/formatter_spec.rb +49 -0
  229. data/spec/metric_fu/gem_version_spec.rb +12 -0
  230. data/spec/metric_fu/generator_spec.rb +130 -0
  231. data/spec/metric_fu/loader_spec.rb +13 -0
  232. data/spec/metric_fu/metric_spec.rb +46 -0
  233. data/spec/metric_fu/metrics/cane/configuration_spec.rb +22 -0
  234. data/spec/metric_fu/metrics/cane/generator_spec.rb +184 -0
  235. data/spec/metric_fu/metrics/churn/configuration_spec.rb +13 -0
  236. data/spec/metric_fu/metrics/churn/generator_spec.rb +64 -0
  237. data/spec/metric_fu/metrics/flay/configuration_spec.rb +13 -0
  238. data/spec/metric_fu/metrics/flay/generator_spec.rb +105 -0
  239. data/spec/metric_fu/metrics/flay/grapher_spec.rb +57 -0
  240. data/spec/metric_fu/metrics/flog/configuration_spec.rb +18 -0
  241. data/spec/metric_fu/metrics/flog/generator_spec.rb +77 -0
  242. data/spec/metric_fu/metrics/flog/grapher_spec.rb +107 -0
  243. data/spec/metric_fu/metrics/hotspots/analysis/analyzed_problems_spec.rb +104 -0
  244. data/spec/metric_fu/metrics/hotspots/analysis/analyzer_tables_spec.rb +71 -0
  245. data/spec/metric_fu/metrics/hotspots/analysis/ranking_spec.rb +30 -0
  246. data/spec/metric_fu/metrics/hotspots/analysis/rankings_spec.rb +97 -0
  247. data/spec/metric_fu/metrics/hotspots/analysis/table_spec.rb +6 -0
  248. data/spec/metric_fu/metrics/hotspots/generator_spec.rb +46 -0
  249. data/spec/metric_fu/metrics/hotspots/hotspot_analyzer_spec.rb +10 -0
  250. data/spec/metric_fu/metrics/hotspots/hotspot_spec.rb +16 -0
  251. data/spec/metric_fu/metrics/rails_best_practices/configuration_spec.rb +55 -0
  252. data/spec/metric_fu/metrics/rails_best_practices/generator_spec.rb +33 -0
  253. data/spec/metric_fu/metrics/rails_best_practices/grapher_spec.rb +62 -0
  254. data/spec/metric_fu/metrics/rcov/configuration_spec.rb +28 -0
  255. data/spec/metric_fu/metrics/rcov/generator_spec.rb +22 -0
  256. data/spec/metric_fu/metrics/rcov/grapher_spec.rb +57 -0
  257. data/spec/metric_fu/metrics/rcov/hotspot_spec.rb +20 -0
  258. data/spec/metric_fu/metrics/rcov/rcov_line_spec.rb +89 -0
  259. data/spec/metric_fu/metrics/rcov/simplecov_formatter_spec.rb +67 -0
  260. data/spec/metric_fu/metrics/reek/configuration_spec.rb +13 -0
  261. data/spec/metric_fu/metrics/reek/generator_spec.rb +203 -0
  262. data/spec/metric_fu/metrics/reek/grapher_spec.rb +66 -0
  263. data/spec/metric_fu/metrics/roodi/configuration_spec.rb +14 -0
  264. data/spec/metric_fu/metrics/roodi/generator_spec.rb +82 -0
  265. data/spec/metric_fu/metrics/roodi/grapher_spec.rb +57 -0
  266. data/spec/metric_fu/metrics/saikuro/configuration_spec.rb +25 -0
  267. data/spec/metric_fu/metrics/saikuro/generator_spec.rb +71 -0
  268. data/spec/metric_fu/metrics/stats/generator_spec.rb +96 -0
  269. data/spec/metric_fu/metrics/stats/grapher_spec.rb +69 -0
  270. data/spec/metric_fu/reporter_spec.rb +41 -0
  271. data/spec/metric_fu/reporting/graphs/graph_spec.rb +44 -0
  272. data/spec/metric_fu/reporting/graphs/grapher_spec.rb +24 -0
  273. data/spec/metric_fu/reporting/result_spec.rb +50 -0
  274. data/spec/metric_fu/run_spec.rb +197 -0
  275. data/spec/metric_fu/templates/configuration_spec.rb +51 -0
  276. data/spec/metric_fu/templates/metrics_template_spec.rb +11 -0
  277. data/spec/metric_fu/templates/report_spec.rb +15 -0
  278. data/spec/metric_fu/templates/template_spec.rb +233 -0
  279. data/spec/metric_fu/utility_spec.rb +12 -0
  280. data/spec/metric_fu_spec.rb +52 -0
  281. data/spec/quality_spec.rb +114 -0
  282. data/spec/shared/configured.rb +45 -0
  283. data/spec/shared/test_coverage.rb +95 -0
  284. data/spec/spec_helper.rb +46 -0
  285. data/spec/support/.metrics +4 -0
  286. data/spec/support/deferred_garbaged_collection.rb +33 -0
  287. data/spec/support/helper_methods.rb +32 -0
  288. data/spec/support/matcher_create_file.rb +37 -0
  289. data/spec/support/matcher_create_files.rb +43 -0
  290. data/spec/support/samples/reek/alfa.rb +1 -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 +735 -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 == Fixnum ? 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