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.
- checksums.yaml +7 -0
- data/.gitignore +28 -0
- data/.metrics +3 -0
- data/.rspec +2 -0
- data/.rubocop.yml +15 -0
- data/.rubocop_todo.yml +69 -0
- data/.simplecov +74 -0
- data/.travis.yml +22 -0
- data/.yardopts +4 -0
- data/AUTHORS +12 -0
- data/CONTRIBUTING.md +47 -0
- data/CONTRIBUTORS +76 -0
- data/DEV.md +76 -0
- data/Gemfile +74 -0
- data/Guardfile +30 -0
- data/HISTORY.md +705 -0
- data/MIT-LICENSE +22 -0
- data/README.md +299 -0
- data/Rakefile +27 -0
- data/TODO.md +118 -0
- data/appveyor.yml +31 -0
- data/bin/metric_fu +9 -0
- data/bin/mf-cane +10 -0
- data/bin/mf-churn +10 -0
- data/bin/mf-flay +10 -0
- data/bin/mf-reek +10 -0
- data/bin/mf-roodi +10 -0
- data/bin/mf-saikuro +10 -0
- data/certs/bf4.pem +22 -0
- data/checksum/.gitkeep +0 -0
- data/checksum/metric_fu-4.10.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.11.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.11.1.gem.sha512 +1 -0
- data/checksum/metric_fu-4.11.2.gem.sha512 +1 -0
- data/checksum/metric_fu-4.11.3.gem.sha512 +1 -0
- data/checksum/metric_fu-4.11.4.gem.sha512 +1 -0
- data/checksum/metric_fu-4.12.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.2.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.2.1.gem.sha512 +1 -0
- data/checksum/metric_fu-4.3.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.3.1.gem.sha512 +1 -0
- data/checksum/metric_fu-4.4.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.4.1.gem.sha512 +1 -0
- data/checksum/metric_fu-4.4.2.gem.sha512 +1 -0
- data/checksum/metric_fu-4.4.3.gem.sha512 +1 -0
- data/checksum/metric_fu-4.4.4.gem.sha512 +1 -0
- data/checksum/metric_fu-4.5.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.5.1.gem.sha512 +1 -0
- data/checksum/metric_fu-4.5.2.gem.sha512 +1 -0
- data/checksum/metric_fu-4.6.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.7.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.7.1.gem.sha512 +1 -0
- data/checksum/metric_fu-4.7.2.gem.sha512 +1 -0
- data/checksum/metric_fu-4.7.3.gem.sha512 +1 -0
- data/checksum/metric_fu-4.7.4.gem.sha512 +1 -0
- data/checksum/metric_fu-4.8.0.gem.sha512 +1 -0
- data/checksum/metric_fu-4.9.0.gem.sha512 +1 -0
- data/config/roodi_config.yml +22 -0
- data/config/rubocop.yml +269 -0
- data/gem_tasks/build.rake +197 -0
- data/gem_tasks/rubocop.rake +10 -0
- data/gem_tasks/usage_test.rake +19 -0
- data/gem_tasks/yard.rake +24 -0
- data/lib/metric_fu/calculate.rb +10 -0
- data/lib/metric_fu/cli/client.rb +26 -0
- data/lib/metric_fu/cli/helper.rb +80 -0
- data/lib/metric_fu/cli/parser.rb +138 -0
- data/lib/metric_fu/configuration.rb +150 -0
- data/lib/metric_fu/constantize.rb +57 -0
- data/lib/metric_fu/data_structures/line_numbers.rb +112 -0
- data/lib/metric_fu/data_structures/location.rb +110 -0
- data/lib/metric_fu/data_structures/sexp_node.rb +107 -0
- data/lib/metric_fu/environment.rb +129 -0
- data/lib/metric_fu/errors/analysis_error.rb +4 -0
- data/lib/metric_fu/formatter/html.rb +96 -0
- data/lib/metric_fu/formatter/syntax.rb +45 -0
- data/lib/metric_fu/formatter/yaml.rb +18 -0
- data/lib/metric_fu/formatter.rb +40 -0
- data/lib/metric_fu/gem_run.rb +70 -0
- data/lib/metric_fu/gem_version.rb +92 -0
- data/lib/metric_fu/generator.rb +135 -0
- data/lib/metric_fu/io.rb +132 -0
- data/lib/metric_fu/loader.rb +105 -0
- data/lib/metric_fu/logger.rb +62 -0
- data/lib/metric_fu/logging/mf_debugger.rb +23 -0
- data/lib/metric_fu/metric.rb +143 -0
- data/lib/metric_fu/metrics/cane/generator.rb +95 -0
- data/lib/metric_fu/metrics/cane/grapher.rb +37 -0
- data/lib/metric_fu/metrics/cane/metric.rb +34 -0
- data/lib/metric_fu/metrics/cane/report.html.erb +87 -0
- data/lib/metric_fu/metrics/cane/violations.rb +46 -0
- data/lib/metric_fu/metrics/churn/generator.rb +37 -0
- data/lib/metric_fu/metrics/churn/hotspot.rb +43 -0
- data/lib/metric_fu/metrics/churn/metric.rb +29 -0
- data/lib/metric_fu/metrics/churn/report.html.erb +58 -0
- data/lib/metric_fu/metrics/flay/generator.rb +51 -0
- data/lib/metric_fu/metrics/flay/grapher.rb +37 -0
- data/lib/metric_fu/metrics/flay/hotspot.rb +52 -0
- data/lib/metric_fu/metrics/flay/metric.rb +28 -0
- data/lib/metric_fu/metrics/flay/report.html.erb +29 -0
- data/lib/metric_fu/metrics/flog/generator.rb +113 -0
- data/lib/metric_fu/metrics/flog/grapher.rb +77 -0
- data/lib/metric_fu/metrics/flog/hotspot.rb +46 -0
- data/lib/metric_fu/metrics/flog/metric.rb +29 -0
- data/lib/metric_fu/metrics/flog/report.html.erb +50 -0
- data/lib/metric_fu/metrics/hotspots/analysis/analyzed_problems.rb +34 -0
- data/lib/metric_fu/metrics/hotspots/analysis/analyzer_tables.rb +114 -0
- data/lib/metric_fu/metrics/hotspots/analysis/grouping.rb +23 -0
- data/lib/metric_fu/metrics/hotspots/analysis/groupings.rb +12 -0
- data/lib/metric_fu/metrics/hotspots/analysis/problems.rb +20 -0
- data/lib/metric_fu/metrics/hotspots/analysis/ranked_problem_location.rb +70 -0
- data/lib/metric_fu/metrics/hotspots/analysis/ranking.rb +29 -0
- data/lib/metric_fu/metrics/hotspots/analysis/rankings.rb +91 -0
- data/lib/metric_fu/metrics/hotspots/analysis/record.rb +32 -0
- data/lib/metric_fu/metrics/hotspots/analysis/scoring_strategies.rb +24 -0
- data/lib/metric_fu/metrics/hotspots/analysis/table.rb +67 -0
- data/lib/metric_fu/metrics/hotspots/generator.rb +40 -0
- data/lib/metric_fu/metrics/hotspots/hotspot.rb +87 -0
- data/lib/metric_fu/metrics/hotspots/hotspot_analyzer.rb +61 -0
- data/lib/metric_fu/metrics/hotspots/metric.rb +20 -0
- data/lib/metric_fu/metrics/hotspots/report.html.erb +60 -0
- data/lib/metric_fu/metrics/rails_best_practices/generator.rb +47 -0
- data/lib/metric_fu/metrics/rails_best_practices/grapher.rb +38 -0
- data/lib/metric_fu/metrics/rails_best_practices/metric.rb +31 -0
- data/lib/metric_fu/metrics/rails_best_practices/report.html.erb +22 -0
- data/lib/metric_fu/metrics/rcov/external_client.rb +22 -0
- data/lib/metric_fu/metrics/rcov/generator.rb +75 -0
- data/lib/metric_fu/metrics/rcov/grapher.rb +37 -0
- data/lib/metric_fu/metrics/rcov/hotspot.rb +46 -0
- data/lib/metric_fu/metrics/rcov/metric.rb +61 -0
- data/lib/metric_fu/metrics/rcov/rcov_format_coverage.rb +149 -0
- data/lib/metric_fu/metrics/rcov/rcov_line.rb +48 -0
- data/lib/metric_fu/metrics/rcov/report.html.erb +40 -0
- data/lib/metric_fu/metrics/rcov/simplecov_formatter.rb +74 -0
- data/lib/metric_fu/metrics/reek/generator.rb +97 -0
- data/lib/metric_fu/metrics/reek/grapher.rb +55 -0
- data/lib/metric_fu/metrics/reek/hotspot.rb +95 -0
- data/lib/metric_fu/metrics/reek/metric.rb +26 -0
- data/lib/metric_fu/metrics/reek/report.html.erb +35 -0
- data/lib/metric_fu/metrics/roodi/generator.rb +41 -0
- data/lib/metric_fu/metrics/roodi/grapher.rb +37 -0
- data/lib/metric_fu/metrics/roodi/hotspot.rb +39 -0
- data/lib/metric_fu/metrics/roodi/metric.rb +24 -0
- data/lib/metric_fu/metrics/roodi/report.html.erb +22 -0
- data/lib/metric_fu/metrics/saikuro/generator.rb +145 -0
- data/lib/metric_fu/metrics/saikuro/hotspot.rb +51 -0
- data/lib/metric_fu/metrics/saikuro/metric.rb +31 -0
- data/lib/metric_fu/metrics/saikuro/parsing_element.rb +37 -0
- data/lib/metric_fu/metrics/saikuro/report.html.erb +71 -0
- data/lib/metric_fu/metrics/saikuro/scratch_file.rb +108 -0
- data/lib/metric_fu/metrics/stats/generator.rb +82 -0
- data/lib/metric_fu/metrics/stats/grapher.rb +40 -0
- data/lib/metric_fu/metrics/stats/hotspot.rb +35 -0
- data/lib/metric_fu/metrics/stats/metric.rb +28 -0
- data/lib/metric_fu/metrics/stats/report.html.erb +44 -0
- data/lib/metric_fu/reporter.rb +37 -0
- data/lib/metric_fu/reporting/graphs/graph.rb +69 -0
- data/lib/metric_fu/reporting/graphs/grapher.rb +66 -0
- data/lib/metric_fu/reporting/result.rb +59 -0
- data/lib/metric_fu/run.rb +82 -0
- data/lib/metric_fu/tasks/metric_fu.rake +54 -0
- data/lib/metric_fu/templates/_gem_info.html.erb +8 -0
- data/lib/metric_fu/templates/_graph.html.erb +2 -0
- data/lib/metric_fu/templates/_report_footer.html.erb +1 -0
- data/lib/metric_fu/templates/configuration.rb +25 -0
- data/lib/metric_fu/templates/css/bluff.css +15 -0
- data/lib/metric_fu/templates/css/buttons.css +82 -0
- data/lib/metric_fu/templates/css/default.css +43 -0
- data/lib/metric_fu/templates/css/integrity.css +337 -0
- data/lib/metric_fu/templates/css/rcov.css +32 -0
- data/lib/metric_fu/templates/css/reset.css +7 -0
- data/lib/metric_fu/templates/css/syntax.css +19 -0
- data/lib/metric_fu/templates/index.html.erb +13 -0
- data/lib/metric_fu/templates/javascripts/bluff-min.js +1 -0
- data/lib/metric_fu/templates/javascripts/bluff_graph.js +15 -0
- data/lib/metric_fu/templates/javascripts/excanvas.js +35 -0
- data/lib/metric_fu/templates/javascripts/highcharts.js +294 -0
- data/lib/metric_fu/templates/javascripts/highcharts_graph.js +38 -0
- data/lib/metric_fu/templates/javascripts/js-class.js +1 -0
- data/lib/metric_fu/templates/javascripts/standalone-framework.js +17 -0
- data/lib/metric_fu/templates/javascripts/utils.js +9 -0
- data/lib/metric_fu/templates/layout.html.erb +41 -0
- data/lib/metric_fu/templates/metrics_template.rb +86 -0
- data/lib/metric_fu/templates/report.html.erb +31 -0
- data/lib/metric_fu/templates/report.rb +41 -0
- data/lib/metric_fu/templates/template.rb +247 -0
- data/lib/metric_fu/utility.rb +79 -0
- data/lib/metric_fu/version.rb +9 -0
- data/lib/metric_fu.rb +143 -0
- data/metric_fu.gemspec +72 -0
- data/spec/capture_warnings.rb +55 -0
- data/spec/cli/helper_spec.rb +165 -0
- data/spec/dummy/.gitignore +1 -0
- data/spec/dummy/.gitkeep +0 -0
- data/spec/dummy/.metrics +4 -0
- data/spec/dummy/lib/.gitkeep +0 -0
- data/spec/dummy/lib/bad_encoding.rb +6 -0
- data/spec/dummy/spec/.gitkeep +0 -0
- data/spec/fixtures/20090630.yml +7922 -0
- data/spec/fixtures/coverage-153.rb +11 -0
- data/spec/fixtures/coverage.rb +13 -0
- data/spec/fixtures/exit0.sh +3 -0
- data/spec/fixtures/exit1.sh +3 -0
- data/spec/fixtures/hotspots/flog.yml +86 -0
- data/spec/fixtures/hotspots/generator.yml +47 -0
- data/spec/fixtures/hotspots/generator_analysis.yml +53 -0
- data/spec/fixtures/hotspots/reek.yml +14 -0
- data/spec/fixtures/hotspots/roodi.yml +13 -0
- data/spec/fixtures/hotspots/saikuro.yml +27 -0
- data/spec/fixtures/hotspots/several_metrics.yml +47 -0
- data/spec/fixtures/hotspots/stats.yml +4 -0
- data/spec/fixtures/hotspots/three_metrics_on_same_file.yml +36 -0
- data/spec/fixtures/line_numbers/foo.rb +33 -0
- data/spec/fixtures/line_numbers/module.rb +11 -0
- data/spec/fixtures/line_numbers/module_surrounds_class.rb +15 -0
- data/spec/fixtures/line_numbers/two_classes.rb +11 -0
- data/spec/fixtures/metric_missing.yml +1 -0
- data/spec/fixtures/rcov_output.txt +135 -0
- data/spec/fixtures/saikuro/app/controllers/sessions_controller.rb_cyclo.html +10 -0
- data/spec/fixtures/saikuro/app/controllers/users_controller.rb_cyclo.html +16 -0
- data/spec/fixtures/saikuro/index_cyclo.html +155 -0
- data/spec/fixtures/saikuro_sfiles/thing.rb_cyclo.html +11 -0
- data/spec/metric_fu/calculate_spec.rb +21 -0
- data/spec/metric_fu/configuration_spec.rb +90 -0
- data/spec/metric_fu/data_structures/line_numbers_spec.rb +63 -0
- data/spec/metric_fu/data_structures/location_spec.rb +110 -0
- data/spec/metric_fu/formatter/configuration_spec.rb +44 -0
- data/spec/metric_fu/formatter/html_spec.rb +138 -0
- data/spec/metric_fu/formatter/yaml_spec.rb +61 -0
- data/spec/metric_fu/formatter_spec.rb +49 -0
- data/spec/metric_fu/gem_version_spec.rb +12 -0
- data/spec/metric_fu/generator_spec.rb +130 -0
- data/spec/metric_fu/loader_spec.rb +10 -0
- data/spec/metric_fu/metric_spec.rb +46 -0
- data/spec/metric_fu/metrics/cane/configuration_spec.rb +22 -0
- data/spec/metric_fu/metrics/cane/generator_spec.rb +184 -0
- data/spec/metric_fu/metrics/churn/configuration_spec.rb +13 -0
- data/spec/metric_fu/metrics/churn/generator_spec.rb +64 -0
- data/spec/metric_fu/metrics/flay/configuration_spec.rb +13 -0
- data/spec/metric_fu/metrics/flay/generator_spec.rb +105 -0
- data/spec/metric_fu/metrics/flay/grapher_spec.rb +57 -0
- data/spec/metric_fu/metrics/flog/configuration_spec.rb +18 -0
- data/spec/metric_fu/metrics/flog/generator_spec.rb +77 -0
- data/spec/metric_fu/metrics/flog/grapher_spec.rb +107 -0
- data/spec/metric_fu/metrics/hotspots/analysis/analyzed_problems_spec.rb +104 -0
- data/spec/metric_fu/metrics/hotspots/analysis/analyzer_tables_spec.rb +71 -0
- data/spec/metric_fu/metrics/hotspots/analysis/ranking_spec.rb +30 -0
- data/spec/metric_fu/metrics/hotspots/analysis/rankings_spec.rb +97 -0
- data/spec/metric_fu/metrics/hotspots/analysis/table_spec.rb +6 -0
- data/spec/metric_fu/metrics/hotspots/generator_spec.rb +46 -0
- data/spec/metric_fu/metrics/hotspots/hotspot_analyzer_spec.rb +10 -0
- data/spec/metric_fu/metrics/hotspots/hotspot_spec.rb +16 -0
- data/spec/metric_fu/metrics/rails_best_practices/configuration_spec.rb +55 -0
- data/spec/metric_fu/metrics/rails_best_practices/generator_spec.rb +33 -0
- data/spec/metric_fu/metrics/rails_best_practices/grapher_spec.rb +62 -0
- data/spec/metric_fu/metrics/rcov/configuration_spec.rb +28 -0
- data/spec/metric_fu/metrics/rcov/generator_spec.rb +22 -0
- data/spec/metric_fu/metrics/rcov/grapher_spec.rb +57 -0
- data/spec/metric_fu/metrics/rcov/hotspot_spec.rb +20 -0
- data/spec/metric_fu/metrics/rcov/rcov_line_spec.rb +89 -0
- data/spec/metric_fu/metrics/rcov/simplecov_formatter_spec.rb +67 -0
- data/spec/metric_fu/metrics/reek/configuration_spec.rb +13 -0
- data/spec/metric_fu/metrics/reek/generator_spec.rb +169 -0
- data/spec/metric_fu/metrics/reek/grapher_spec.rb +66 -0
- data/spec/metric_fu/metrics/roodi/configuration_spec.rb +14 -0
- data/spec/metric_fu/metrics/roodi/generator_spec.rb +82 -0
- data/spec/metric_fu/metrics/roodi/grapher_spec.rb +57 -0
- data/spec/metric_fu/metrics/saikuro/configuration_spec.rb +25 -0
- data/spec/metric_fu/metrics/saikuro/generator_spec.rb +71 -0
- data/spec/metric_fu/metrics/stats/generator_spec.rb +96 -0
- data/spec/metric_fu/metrics/stats/grapher_spec.rb +69 -0
- data/spec/metric_fu/reporter_spec.rb +41 -0
- data/spec/metric_fu/reporting/graphs/graph_spec.rb +44 -0
- data/spec/metric_fu/reporting/graphs/grapher_spec.rb +24 -0
- data/spec/metric_fu/reporting/result_spec.rb +50 -0
- data/spec/metric_fu/run_spec.rb +197 -0
- data/spec/metric_fu/templates/configuration_spec.rb +51 -0
- data/spec/metric_fu/templates/metrics_template_spec.rb +11 -0
- data/spec/metric_fu/templates/report_spec.rb +15 -0
- data/spec/metric_fu/templates/template_spec.rb +233 -0
- data/spec/metric_fu/utility_spec.rb +12 -0
- data/spec/metric_fu_spec.rb +33 -0
- data/spec/quality_spec.rb +114 -0
- data/spec/shared/configured.rb +45 -0
- data/spec/shared/test_coverage.rb +95 -0
- data/spec/spec_helper.rb +54 -0
- data/spec/support/deferred_garbaged_collection.rb +33 -0
- data/spec/support/helper_methods.rb +32 -0
- data/spec/support/matcher_create_file.rb +37 -0
- data/spec/support/matcher_create_files.rb +43 -0
- data/spec/support/suite.rb +26 -0
- data/spec/support/test_fixtures.rb +37 -0
- data/spec/support/timeout.rb +7 -0
- data/spec/support/usage_test.rb +150 -0
- data/spec/usage_test_spec.rb +93 -0
- 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
|