metric_fu 4.2.0 → 4.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY.md +8 -0
- data/checksum/metric_fu-4.2.1.gem.sha512 +1 -0
- data/lib/metric_fu.rb +0 -1
- data/lib/metric_fu/cli/helper.rb +1 -1
- data/lib/metric_fu/data_structures/line_numbers.rb +6 -9
- data/lib/metric_fu/initial_requires.rb +1 -0
- data/lib/metric_fu/logging/mf_debugger.rb +20 -10
- data/lib/metric_fu/metrics/cane/cane_bluff_grapher.rb +1 -1
- data/lib/metric_fu/metrics/flay/flay_bluff_grapher.rb +1 -1
- data/lib/metric_fu/metrics/flog/flog.rb +2 -3
- data/lib/metric_fu/metrics/flog/flog_bluff_grapher.rb +1 -1
- data/lib/metric_fu/metrics/graph.rb +2 -2
- data/lib/metric_fu/metrics/rails_best_practices/rails_best_practices_bluff_grapher.rb +1 -1
- data/lib/metric_fu/metrics/rcov/rcov.rb +2 -2
- data/lib/metric_fu/metrics/rcov/rcov_bluff_grapher.rb +1 -1
- data/lib/metric_fu/metrics/reek/reek.rb +1 -1
- data/lib/metric_fu/metrics/reek/reek_bluff_grapher.rb +1 -1
- data/lib/metric_fu/metrics/roodi/roodi_bluff_grapher.rb +1 -1
- data/lib/metric_fu/metrics/saikuro/saikuro.rb +28 -4
- data/lib/metric_fu/metrics/stats/stats_bluff_grapher.rb +1 -1
- data/lib/metric_fu/reporting/graphs/engines/gchart.rb +1 -1
- data/lib/metric_fu/run.rb +5 -2
- data/lib/metric_fu/version.rb +1 -1
- data/metric_fu.gemspec +2 -0
- data/spec/metric_fu/metrics/flog/flog_spec.rb +1 -1
- data/spec/metric_fu/metrics/graph_spec.rb +4 -4
- data/spec/metric_fu/metrics/hotspots/hotspots_spec.rb +5 -4
- data/spec/metric_fu/metrics/rcov/rcov_spec.rb +2 -2
- data/spec/metric_fu/reporting/graphs/engines/gchart_spec.rb +1 -1
- metadata +19 -10
- data/lib/metric_fu/core_ext.rb +0 -2
- data/lib/metric_fu/core_ext/inflector/inflections.rb +0 -214
- data/lib/metric_fu/core_ext/inflector/methods.rb +0 -153
- data/lib/metric_fu/core_ext/object.rb +0 -3
- data/lib/metric_fu/core_ext/object/blank.rb +0 -106
- data/lib/metric_fu/core_ext/object/to_json.rb +0 -12
- data/lib/metric_fu/core_ext/string.rb +0 -2
- data/lib/metric_fu/core_ext/string/inflections.rb +0 -195
data/HISTORY.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
### Master
|
2
2
|
|
3
|
+
### MetricFu 4.2.1 / 2013-05-23
|
4
|
+
|
5
|
+
* Fixes
|
6
|
+
* Remove ActiveSupport dependencies (Benjamin Fleischer #79)
|
7
|
+
* Add MultiJson to ensure JSON support in rbx and jruby (Benjamin Fleischer)
|
8
|
+
* Misc
|
9
|
+
* Improve STDOUT to show which metric is running but hide the details by default
|
10
|
+
|
3
11
|
### MetricFu 4.2.0 / 2013-05-20
|
4
12
|
|
5
13
|
* Features
|
@@ -0,0 +1 @@
|
|
1
|
+
529b99cf3f5fb314e9a7684ad38198e7509ec13926ded779bfef2a4b968b0538fc72248606a3b0b1bdead31163e04641bd6f565d5db62bb168ee80b854e9c580
|
data/lib/metric_fu.rb
CHANGED
@@ -41,7 +41,6 @@ module MetricFu
|
|
41
41
|
(ENV['CC_BUILD_ARTIFACTS'] || 'tmp/metric_fu')
|
42
42
|
end
|
43
43
|
def self.configure
|
44
|
-
MetricFu.lib_require { 'core_ext' }
|
45
44
|
MetricFu.lib_require { 'configuration' }
|
46
45
|
init_files = Dir.glob(File.join(MetricFu.metrics_dir, '**/init.rb')).reject do |file|
|
47
46
|
if file =~ /rcov/o
|
data/lib/metric_fu/cli/helper.rb
CHANGED
@@ -41,7 +41,7 @@ module MetricFu
|
|
41
41
|
p.version = self.version
|
42
42
|
p.option :run, "Run all metrics with defaults", :default => false
|
43
43
|
metrics.each do |metric|
|
44
|
-
p.option metric.to_sym, "Enables or disables #{metric.to_s
|
44
|
+
p.option metric.to_sym, "Enables or disables #{metric.to_s}", :default => true #, :value_in_set => [true, false]
|
45
45
|
end
|
46
46
|
p.option :open, "Open report in browser", :default => true
|
47
47
|
end.process!(argv)
|
@@ -4,7 +4,7 @@ module MetricFu
|
|
4
4
|
|
5
5
|
def initialize(contents,file_path='')
|
6
6
|
if contents.to_s.size.zero?
|
7
|
-
|
7
|
+
mf_log "NON PARSEABLE INPUT: File is empty at path #{file_path.inspect}\n\t#{caller.join("\n\t")}"
|
8
8
|
else
|
9
9
|
rp = RubyParser.new
|
10
10
|
@locations = {}
|
@@ -14,19 +14,16 @@ module MetricFu
|
|
14
14
|
process_class(file_sexp)
|
15
15
|
when :module
|
16
16
|
process_module(file_sexp)
|
17
|
-
when :block
|
18
|
-
file_sexp.each_of_type(:class) { |sexp| process_class(sexp) }
|
19
|
-
when :iter
|
20
|
-
mf_debug "SEXP: Not parsing line number for #{file_sexp.inspect}"
|
21
|
-
when :cdecl
|
22
|
-
mf_debug "SEXP: Not parsing line number for #{file_sexp.inspect}"
|
23
17
|
else
|
24
|
-
|
18
|
+
mf_debug "SEXP: Parsing line numbers for classes in sexp type #{file_sexp[0].inspect}"
|
19
|
+
mf_debug " in #{file_path}"
|
20
|
+
file_sexp.each_of_type(:module) { |sexp| process_class(sexp) }
|
21
|
+
file_sexp.each_of_type(:class) { |sexp| process_class(sexp) }
|
25
22
|
end
|
26
23
|
end
|
27
24
|
rescue Exception => e
|
28
25
|
#catch errors for files ruby_parser fails on
|
29
|
-
|
26
|
+
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}"
|
30
27
|
@locations
|
31
28
|
end
|
32
29
|
|
@@ -1,24 +1,34 @@
|
|
1
1
|
module MfDebugger
|
2
|
+
extend self
|
3
|
+
|
2
4
|
class Logger
|
3
5
|
class << self
|
4
6
|
attr_accessor :debug_on
|
5
7
|
@debug_on = false
|
6
8
|
end
|
7
|
-
|
8
|
-
|
9
|
-
def self.mf_debug(msg,&block)
|
10
|
-
if MfDebugger::Logger.debug_on
|
9
|
+
def self.log(msg, &block)
|
11
10
|
if block_given?
|
12
11
|
block.call
|
13
12
|
end
|
14
|
-
STDOUT.puts msg
|
13
|
+
STDOUT.puts '*'*5 + msg.to_s
|
14
|
+
end
|
15
|
+
def self.debug(msg, &block)
|
16
|
+
if MfDebugger::Logger.debug_on
|
17
|
+
if block_given?
|
18
|
+
log(msg,&block)
|
19
|
+
else
|
20
|
+
log(msg)
|
21
|
+
end
|
22
|
+
end
|
15
23
|
end
|
16
24
|
end
|
25
|
+
|
17
26
|
def mf_debug(msg,&block)
|
18
|
-
|
19
|
-
MfDebugger.mf_debug(msg,&block)
|
20
|
-
else
|
21
|
-
MfDebugger.mf_debug(msg)
|
22
|
-
end
|
27
|
+
MfDebugger::Logger.debug(msg, &block)
|
23
28
|
end
|
29
|
+
|
30
|
+
def mf_log(msg,&block)
|
31
|
+
MfDebugger::Logger.log(msg, &block)
|
32
|
+
end
|
33
|
+
|
24
34
|
end
|
@@ -6,7 +6,7 @@ module MetricFu
|
|
6
6
|
#{BLUFF_DEFAULT_OPTIONS}
|
7
7
|
g.title = 'Cane: code quality threshold violations';
|
8
8
|
g.data('cane', [#{@cane_violations.join(',')}]);
|
9
|
-
g.labels = #{@labels
|
9
|
+
g.labels = #{MultiJson.dump(@labels)};
|
10
10
|
g.draw();
|
11
11
|
EOS
|
12
12
|
File.open(File.join(MetricFu.output_directory, 'cane.js'), 'w') {|f| f << content }
|
@@ -6,7 +6,7 @@ module MetricFu
|
|
6
6
|
#{BLUFF_DEFAULT_OPTIONS}
|
7
7
|
g.title = 'Flay: duplication';
|
8
8
|
g.data('flay', [#{@flay_score.join(',')}]);
|
9
|
-
g.labels = #{@labels
|
9
|
+
g.labels = #{MultiJson.dump(@labels)};
|
10
10
|
g.draw();
|
11
11
|
EOS
|
12
12
|
File.open(File.join(MetricFu.output_directory, 'flay.js'), 'w') {|f| f << content }
|
@@ -27,7 +27,6 @@ module MetricFu
|
|
27
27
|
end
|
28
28
|
options = ::Flog.parse_options [
|
29
29
|
"--all",
|
30
|
-
"--details",
|
31
30
|
MetricFu.flog[:continue] ? "--continue" : nil,
|
32
31
|
].compact
|
33
32
|
|
@@ -37,9 +36,9 @@ module MetricFu
|
|
37
36
|
rescue LoadError => e
|
38
37
|
message = "#{e.class}\t#{e.message}\n\t#{e.backtrace.join('\n\t')}"
|
39
38
|
if MetricFu.configuration.mri?
|
40
|
-
|
39
|
+
mf_log "Flog Error: #{message}"
|
41
40
|
else
|
42
|
-
|
41
|
+
mf_log "Flog tasks only available in MRI: #{message}"
|
43
42
|
end
|
44
43
|
end
|
45
44
|
|
@@ -7,7 +7,7 @@ module MetricFu
|
|
7
7
|
g.title = 'Flog: code complexity';
|
8
8
|
g.data('average', [#{@flog_average.join(',')}]);
|
9
9
|
g.data('top 5% average', [#{@top_five_percent_average.join(',')}])
|
10
|
-
g.labels = #{@labels
|
10
|
+
g.labels = #{MultiJson.dump(@labels)};
|
11
11
|
g.draw();
|
12
12
|
EOS
|
13
13
|
File.open(File.join(MetricFu.output_directory, 'flog.js'), 'w') {|f| f << content }
|
@@ -20,9 +20,9 @@ module MetricFu
|
|
20
20
|
|
21
21
|
def generate
|
22
22
|
return if self.clazz.empty?
|
23
|
-
|
23
|
+
mf_log "Generating graphs"
|
24
24
|
Dir[File.join(MetricFu.data_directory, '*.yml')].sort.each do |metric_file|
|
25
|
-
|
25
|
+
mf_log "Generating graphs for #{metric_file}"
|
26
26
|
date_parts = year_month_day_from_filename(metric_file)
|
27
27
|
metrics = YAML::load(File.open(metric_file))
|
28
28
|
|
@@ -6,7 +6,7 @@ module MetricFu
|
|
6
6
|
#{BLUFF_DEFAULT_OPTIONS}
|
7
7
|
g.title = 'Rails Best Practices: design problems';
|
8
8
|
g.data('rails_best_practices', [#{@rails_best_practices_count.join(',')}]);
|
9
|
-
g.labels = #{@labels
|
9
|
+
g.labels = #{MultiJson.dump(@labels)};
|
10
10
|
g.draw();
|
11
11
|
EOS
|
12
12
|
File.open(File.join(MetricFu.output_directory, 'rails_best_practices.js'), 'w') {|f| f << content }
|
@@ -81,7 +81,7 @@ module MetricFu
|
|
81
81
|
line_numbers = MetricFu::LineNumbers.new(file_contents)
|
82
82
|
rescue StandardError => e
|
83
83
|
raise e unless e.message =~ /you shouldn't be able to get here/
|
84
|
-
|
84
|
+
mf_log "ruby_parser blew up while trying to parse #{file_path}. You won't have method level Rcov information for this file."
|
85
85
|
next
|
86
86
|
end
|
87
87
|
|
@@ -115,7 +115,7 @@ module MetricFu
|
|
115
115
|
content.map! do |raw_line|
|
116
116
|
line = Line.new(raw_line[3..-1], !raw_line.match(/^!!/)).to_h
|
117
117
|
end
|
118
|
-
content.reject! {|line| line[:content].
|
118
|
+
content.reject! {|line| line[:content].to_s == '' }
|
119
119
|
files[fname] = {:lines => content}
|
120
120
|
end
|
121
121
|
files
|
@@ -6,7 +6,7 @@ module MetricFu
|
|
6
6
|
#{BLUFF_DEFAULT_OPTIONS}
|
7
7
|
g.title = 'Rcov: code coverage';
|
8
8
|
g.data('rcov', [#{@rcov_percent.join(',')}]);
|
9
|
-
g.labels = #{@labels
|
9
|
+
g.labels = #{MultiJson.dump(@labels)};
|
10
10
|
g.draw();
|
11
11
|
EOS
|
12
12
|
File.open(File.join(MetricFu.output_directory, 'rcov.js'), 'w') {|f| f << content }
|
@@ -66,7 +66,7 @@ module MetricFu
|
|
66
66
|
line_numbers = MetricFu::LineNumbers.new(File.open(file_path, 'r').read,file_path)
|
67
67
|
rescue StandardError => e
|
68
68
|
raise e unless e.message =~ /you shouldn't be able to get here/
|
69
|
-
|
69
|
+
mf_log "ruby_parser blew up while trying to parse #{file_path}. You won't have method level reek information for this file."
|
70
70
|
next
|
71
71
|
end
|
72
72
|
|
@@ -11,7 +11,7 @@ module MetricFu
|
|
11
11
|
#{BLUFF_DEFAULT_OPTIONS}
|
12
12
|
g.title = 'Reek: code smells';
|
13
13
|
#{data}
|
14
|
-
g.labels = #{@labels
|
14
|
+
g.labels = #{MultiJson.dump(@labels)};
|
15
15
|
g.draw();
|
16
16
|
EOS
|
17
17
|
File.open(File.join(MetricFu.output_directory, 'reek.js'), 'w') {|f| f << content }
|
@@ -6,7 +6,7 @@ module MetricFu
|
|
6
6
|
#{BLUFF_DEFAULT_OPTIONS}
|
7
7
|
g.title = 'Roodi: design problems';
|
8
8
|
g.data('roodi', [#{@roodi_count.join(',')}]);
|
9
|
-
g.labels = #{@labels
|
9
|
+
g.labels = #{MultiJson.dump(@labels)};
|
10
10
|
g.draw();
|
11
11
|
EOS
|
12
12
|
File.open(File.join(MetricFu.output_directory, 'roodi.js'), 'w') {|f| f << content }
|
@@ -13,11 +13,35 @@ module MetricFu
|
|
13
13
|
end
|
14
14
|
|
15
15
|
saikuro_bin= $:.map{|d| d+'/../bin/saikuro'}.select{|f| File.exists? f}.first || 'saikuro'
|
16
|
-
|
17
|
-
|
18
|
-
|
16
|
+
mf_debug(capture_output do
|
17
|
+
sh %{#{saikuro_bin} #{options_string}} do |ok, response|
|
18
|
+
unless ok
|
19
|
+
mf_log "Saikuro failed with exit status: #{response.exitstatus}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end)
|
23
|
+
end
|
24
|
+
|
25
|
+
def capture_output(&block)
|
26
|
+
old_stdout = STDOUT.clone
|
27
|
+
pipe_r, pipe_w = IO.pipe
|
28
|
+
pipe_r.sync = true
|
29
|
+
output = ""
|
30
|
+
reader = Thread.new do
|
31
|
+
begin
|
32
|
+
loop do
|
33
|
+
output << pipe_r.readpartial(1024)
|
34
|
+
end
|
35
|
+
rescue EOFError
|
19
36
|
end
|
20
37
|
end
|
38
|
+
STDOUT.reopen(pipe_w)
|
39
|
+
yield
|
40
|
+
ensure
|
41
|
+
STDOUT.reopen(old_stdout)
|
42
|
+
pipe_w.close
|
43
|
+
reader.join
|
44
|
+
return output
|
21
45
|
end
|
22
46
|
|
23
47
|
def format_directories
|
@@ -56,7 +80,7 @@ module MetricFu
|
|
56
80
|
line_numbers = MetricFu::LineNumbers.new(File.open(file_data[:filename], 'r').read)
|
57
81
|
rescue StandardError => e
|
58
82
|
raise e unless e.message =~ /you shouldn't be able to get here/
|
59
|
-
|
83
|
+
mf_log "ruby_parser blew up while trying to parse #{file_path}. You won't have method level Saikuro information for this file."
|
60
84
|
next
|
61
85
|
end
|
62
86
|
|
@@ -7,7 +7,7 @@ module MetricFu
|
|
7
7
|
g.title = 'Stats: LOC & LOT';
|
8
8
|
g.data('LOC', [#{@loc_counts.join(',')}]);
|
9
9
|
g.data('LOT', [#{@lot_counts.join(',')}])
|
10
|
-
g.labels = #{@labels
|
10
|
+
g.labels = #{MultiJson.dump(@labels)};
|
11
11
|
g.draw();
|
12
12
|
EOS
|
13
13
|
File.open(File.join(MetricFu.output_directory, 'stats.js'), 'w') {|f| f << content }
|
@@ -25,7 +25,7 @@ module MetricFu
|
|
25
25
|
def self.require_graphing_gem
|
26
26
|
require 'gchart' if MetricFu.graph_engine == :gchart
|
27
27
|
rescue LoadError
|
28
|
-
|
28
|
+
mf_log "#"*99 + "\n" +
|
29
29
|
"If you want to use google charts for graphing, you'll need to install the googlecharts rubygem." +
|
30
30
|
"\n" + "#"*99
|
31
31
|
end
|
data/lib/metric_fu/run.rb
CHANGED
@@ -21,12 +21,13 @@ module MetricFu
|
|
21
21
|
end
|
22
22
|
def run_reports
|
23
23
|
report_metrics.each {|metric|
|
24
|
-
|
24
|
+
mf_log "** STARTING METRIC #{metric}"
|
25
25
|
MetricFu.report.add(metric)
|
26
|
-
|
26
|
+
mf_log "** ENDING METRIC #{metric}"
|
27
27
|
}
|
28
28
|
end
|
29
29
|
def save_reports
|
30
|
+
mf_log "** SAVING REPORTS"
|
30
31
|
mf_debug "** SAVING REPORT YAML OUTPUT TO #{MetricFu.base_directory}"
|
31
32
|
MetricFu.report.save_output(MetricFu.report.as_yaml,
|
32
33
|
MetricFu.base_directory,
|
@@ -39,6 +40,7 @@ module MetricFu
|
|
39
40
|
MetricFu.report.save_templatized_report
|
40
41
|
end
|
41
42
|
def save_graphs
|
43
|
+
mf_log "** GENERATING GRAPHS"
|
42
44
|
mf_debug "** PREPARING TO GRAPH"
|
43
45
|
MetricFu.graphs.each {|graph|
|
44
46
|
mf_debug "** Graphing #{graph} with #{MetricFu.graph_engine}"
|
@@ -52,6 +54,7 @@ module MetricFu
|
|
52
54
|
mf_debug "** OPENING IN BROWSER FROM #{MetricFu.output_directory}"
|
53
55
|
MetricFu.report.show_in_browser(MetricFu.output_directory)
|
54
56
|
end
|
57
|
+
mf_log "** COMPLETE"
|
55
58
|
end
|
56
59
|
private
|
57
60
|
def load_user_configuration
|
data/lib/metric_fu/version.rb
CHANGED
data/metric_fu.gemspec
CHANGED
@@ -10,7 +10,7 @@ describe Flog do
|
|
10
10
|
describe "emit method" do
|
11
11
|
it "should look for files and flog them" do
|
12
12
|
Dir.should_receive(:glob).with("lib/**/*.rb").and_return(["found/file.rb"])
|
13
|
-
::Flog.should_receive(:parse_options).with(["--all"
|
13
|
+
::Flog.should_receive(:parse_options).with(["--all"]).and_return("options")
|
14
14
|
::Flog.should_receive(:new).with("options").and_return(flogger = mock('flogger'))
|
15
15
|
flogger.should_receive(:flog).with(["found/file.rb"])
|
16
16
|
@flog.emit
|
@@ -27,11 +27,11 @@ describe MetricFu::Graph do
|
|
27
27
|
@graph.clazz.should_receive(:push).with(an_instance_of(RcovGchartGrapher))
|
28
28
|
@graph.add("rcov", 'gchart')
|
29
29
|
end
|
30
|
-
end
|
30
|
+
end
|
31
31
|
|
32
32
|
describe "setting the date on the graph" do
|
33
33
|
before(:each) do
|
34
|
-
@graph.stub!(:
|
34
|
+
@graph.stub!(:mf_log)
|
35
35
|
end
|
36
36
|
|
37
37
|
it "should set the date once for one data point" do
|
@@ -41,7 +41,7 @@ describe MetricFu::Graph do
|
|
41
41
|
mock_grapher = stub
|
42
42
|
mock_grapher.should_receive(:get_metrics).with("Metrics", "11/5")
|
43
43
|
mock_grapher.should_receive(:graph!)
|
44
|
-
|
44
|
+
|
45
45
|
@graph.clazz = [mock_grapher]
|
46
46
|
@graph.generate
|
47
47
|
end
|
@@ -57,5 +57,5 @@ describe MetricFu::Graph do
|
|
57
57
|
@graph.clazz = [mock_grapher]
|
58
58
|
@graph.generate
|
59
59
|
end
|
60
|
-
end
|
60
|
+
end
|
61
61
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'multi_json'
|
1
2
|
require "spec_helper"
|
2
3
|
|
3
4
|
describe Hotspots do
|
@@ -69,8 +70,8 @@ END
|
|
69
70
|
analyzer = HotspotAnalyzer.new(@yaml)
|
70
71
|
hotspots.instance_variable_set(:@analyzer, analyzer)
|
71
72
|
result = hotspots.analyze
|
72
|
-
expected =
|
73
|
-
compare_hashes(
|
73
|
+
expected = MultiJson.load("{\"methods\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":\"Client#client_requested_sync\",\"file_path\":\"lib/client/client.rb\",\"hash\":7919384682,\"simple_method_name\":\"#client_requested_sync\"},\"details\":{\"reek\":\"found 1 code smells\",\"flog\":\"complexity is 37.9\"}}],\"classes\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":7995629750},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\"}}],\"files\":[{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":-5738801681},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\",\"churn\":\"detected high level of churn (changed 54 times)\"}},{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/foo.rb\",\"hash\":-7081271905},\"details\":{\"churn\":\"detected high level of churn (changed 52 times)\"}}]}")
|
74
|
+
compare_hashes(MultiJson.load(hotspots.to_h[:hotspots].to_json), expected)
|
74
75
|
end
|
75
76
|
|
76
77
|
it "should put the changes into a hash" do
|
@@ -78,8 +79,8 @@ END
|
|
78
79
|
analyzer = HotspotAnalyzer.new(@yaml)
|
79
80
|
hotspots.instance_variable_set(:@analyzer, analyzer)
|
80
81
|
hotspots.analyze
|
81
|
-
expected =
|
82
|
-
compare_hashes(
|
82
|
+
expected = MultiJson.load("{\"methods\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":\"Client#client_requested_sync\",\"file_path\":\"lib/client/client.rb\",\"hash\":7919384682,\"simple_method_name\":\"#client_requested_sync\"},\"details\":{\"reek\":\"found 1 code smells\",\"flog\":\"complexity is 37.9\"}}],\"classes\":[{\"location\":{\"class_name\":\"Client\",\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":7995629750},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\"}}],\"files\":[{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/client.rb\",\"hash\":-5738801681},\"details\":{\"reek\":\"found 2 code smells\",\"flog\":\"complexity is 37.9\",\"churn\":\"detected high level of churn (changed 54 times)\"}},{\"location\":{\"class_name\":null,\"method_name\":null,\"file_path\":\"lib/client/foo.rb\",\"hash\":-7081271905},\"details\":{\"churn\":\"detected high level of churn (changed 52 times)\"}}]}")
|
83
|
+
compare_hashes(MultiJson.load(hotspots.to_h[:hotspots].to_json), expected)
|
83
84
|
end
|
84
85
|
# really testing the output of analyzed_problems#worst_items
|
85
86
|
it "should return the worst item granularities: files, classes, methods" do
|
@@ -11,7 +11,7 @@ describe MetricFu::Rcov do
|
|
11
11
|
|
12
12
|
describe "emit" do
|
13
13
|
before :each do
|
14
|
-
@rcov.stub!(:
|
14
|
+
@rcov.stub!(:mf_log)
|
15
15
|
MetricFu.rcov[:external] = nil
|
16
16
|
end
|
17
17
|
|
@@ -65,7 +65,7 @@ describe MetricFu::Rcov do
|
|
65
65
|
end
|
66
66
|
describe "with external configuration option set" do
|
67
67
|
before :each do
|
68
|
-
@rcov.stub!(:
|
68
|
+
@rcov.stub!(:mf_log)
|
69
69
|
MetricFu.rcov[:external] = "coverage/rcov.txt"
|
70
70
|
end
|
71
71
|
|
@@ -5,7 +5,7 @@ describe MetricFu::Grapher do
|
|
5
5
|
it "should give a warning if trying to use gchart but gem is not installed" do
|
6
6
|
MetricFu::Configuration.run {|config| config.graph_engine = :gchart}
|
7
7
|
MetricFu::Grapher.should_receive(:require).with('gchart').and_raise(LoadError)
|
8
|
-
MetricFu::Grapher.should_receive(:
|
8
|
+
MetricFu::Grapher.should_receive(:mf_log).with(/If you want to use google charts/)
|
9
9
|
MetricFu::Grapher.require_graphing_gem
|
10
10
|
end
|
11
11
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: metric_fu
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.2.
|
4
|
+
version: 4.2.1
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -19,7 +19,7 @@ authors:
|
|
19
19
|
autorequire:
|
20
20
|
bindir: bin
|
21
21
|
cert_chain: []
|
22
|
-
date: 2013-05-
|
22
|
+
date: 2013-05-23 00:00:00.000000000 Z
|
23
23
|
dependencies:
|
24
24
|
- !ruby/object:Gem::Dependency
|
25
25
|
name: flay
|
@@ -321,6 +321,22 @@ dependencies:
|
|
321
321
|
- - ! '>='
|
322
322
|
- !ruby/object:Gem::Version
|
323
323
|
version: '0'
|
324
|
+
- !ruby/object:Gem::Dependency
|
325
|
+
name: multi_json
|
326
|
+
requirement: !ruby/object:Gem::Requirement
|
327
|
+
none: false
|
328
|
+
requirements:
|
329
|
+
- - ! '>='
|
330
|
+
- !ruby/object:Gem::Version
|
331
|
+
version: '0'
|
332
|
+
type: :runtime
|
333
|
+
prerelease: false
|
334
|
+
version_requirements: !ruby/object:Gem::Requirement
|
335
|
+
none: false
|
336
|
+
requirements:
|
337
|
+
- - ! '>='
|
338
|
+
- !ruby/object:Gem::Version
|
339
|
+
version: '0'
|
324
340
|
description: Code metrics from Flog, Flay, Saikuro, Churn, Reek, Roodi, Rails' stats
|
325
341
|
task and Rails Best Practices, and optionally RCov
|
326
342
|
email: github@benjaminfleischer.com
|
@@ -362,6 +378,7 @@ files:
|
|
362
378
|
- bin/mf-stats
|
363
379
|
- checksum/.gitkeep
|
364
380
|
- checksum/metric_fu-4.2.0.gem.sha512
|
381
|
+
- checksum/metric_fu-4.2.1.gem.sha512
|
365
382
|
- config/roodi_config.yml
|
366
383
|
- gem_tasks/build.rake
|
367
384
|
- lib/metric_fu.rb
|
@@ -369,14 +386,6 @@ files:
|
|
369
386
|
- lib/metric_fu/cli/helper.rb
|
370
387
|
- lib/metric_fu/cli/parser.rb
|
371
388
|
- lib/metric_fu/configuration.rb
|
372
|
-
- lib/metric_fu/core_ext.rb
|
373
|
-
- lib/metric_fu/core_ext/inflector/inflections.rb
|
374
|
-
- lib/metric_fu/core_ext/inflector/methods.rb
|
375
|
-
- lib/metric_fu/core_ext/object.rb
|
376
|
-
- lib/metric_fu/core_ext/object/blank.rb
|
377
|
-
- lib/metric_fu/core_ext/object/to_json.rb
|
378
|
-
- lib/metric_fu/core_ext/string.rb
|
379
|
-
- lib/metric_fu/core_ext/string/inflections.rb
|
380
389
|
- lib/metric_fu/data_structures/careful_array.rb
|
381
390
|
- lib/metric_fu/data_structures/line_numbers.rb
|
382
391
|
- lib/metric_fu/data_structures/location.rb
|
data/lib/metric_fu/core_ext.rb
DELETED
@@ -1,214 +0,0 @@
|
|
1
|
-
# https://raw.github.com/rails/rails/20768176292cbcb883ab152b4aa9ed8c664771cd/activesupport/lib/active_support/inflector/inflections.rb
|
2
|
-
# not https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/inflector/inflections.rb
|
3
|
-
MetricFu.lib_require { 'core_ext/inflector/methods' }
|
4
|
-
module ActiveSupport
|
5
|
-
module Inflector
|
6
|
-
# A singleton instance of this class is yielded by Inflector.inflections, which can then be used to specify additional
|
7
|
-
# inflection rules. Examples:
|
8
|
-
#
|
9
|
-
# ActiveSupport::Inflector.inflections do |inflect|
|
10
|
-
# inflect.plural /^(ox)$/i, '\1\2en'
|
11
|
-
# inflect.singular /^(ox)en/i, '\1'
|
12
|
-
#
|
13
|
-
# inflect.irregular 'octopus', 'octopi'
|
14
|
-
#
|
15
|
-
# inflect.uncountable "equipment"
|
16
|
-
# end
|
17
|
-
#
|
18
|
-
# New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
|
19
|
-
# pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
|
20
|
-
# already have been loaded.
|
21
|
-
class Inflections
|
22
|
-
def self.instance
|
23
|
-
@__instance__ ||= new
|
24
|
-
end
|
25
|
-
|
26
|
-
attr_reader :plurals, :singulars, :uncountables, :humans
|
27
|
-
|
28
|
-
def initialize
|
29
|
-
@plurals, @singulars, @uncountables, @humans = [], [], [], []
|
30
|
-
end
|
31
|
-
|
32
|
-
# Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
|
33
|
-
# The replacement should always be a string that may include references to the matched data from the rule.
|
34
|
-
def plural(rule, replacement)
|
35
|
-
@uncountables.delete(rule) if rule.is_a?(String)
|
36
|
-
@uncountables.delete(replacement)
|
37
|
-
@plurals.insert(0, [rule, replacement])
|
38
|
-
end
|
39
|
-
|
40
|
-
# Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
|
41
|
-
# The replacement should always be a string that may include references to the matched data from the rule.
|
42
|
-
def singular(rule, replacement)
|
43
|
-
@uncountables.delete(rule) if rule.is_a?(String)
|
44
|
-
@uncountables.delete(replacement)
|
45
|
-
@singulars.insert(0, [rule, replacement])
|
46
|
-
end
|
47
|
-
|
48
|
-
# Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
|
49
|
-
# for strings, not regular expressions. You simply pass the irregular in singular and plural form.
|
50
|
-
#
|
51
|
-
# Examples:
|
52
|
-
# irregular 'octopus', 'octopi'
|
53
|
-
# irregular 'person', 'people'
|
54
|
-
def irregular(singular, plural)
|
55
|
-
@uncountables.delete(singular)
|
56
|
-
@uncountables.delete(plural)
|
57
|
-
if singular[0,1].upcase == plural[0,1].upcase
|
58
|
-
plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
|
59
|
-
plural(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + plural[1..-1])
|
60
|
-
singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
|
61
|
-
else
|
62
|
-
plural(Regexp.new("#{singular[0,1].upcase}(?i)#{singular[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
|
63
|
-
plural(Regexp.new("#{singular[0,1].downcase}(?i)#{singular[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
|
64
|
-
plural(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), plural[0,1].upcase + plural[1..-1])
|
65
|
-
plural(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), plural[0,1].downcase + plural[1..-1])
|
66
|
-
singular(Regexp.new("#{plural[0,1].upcase}(?i)#{plural[1..-1]}$"), singular[0,1].upcase + singular[1..-1])
|
67
|
-
singular(Regexp.new("#{plural[0,1].downcase}(?i)#{plural[1..-1]}$"), singular[0,1].downcase + singular[1..-1])
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
# Add uncountable words that shouldn't be attempted inflected.
|
72
|
-
#
|
73
|
-
# Examples:
|
74
|
-
# uncountable "money"
|
75
|
-
# uncountable "money", "information"
|
76
|
-
# uncountable %w( money information rice )
|
77
|
-
def uncountable(*words)
|
78
|
-
(@uncountables << words).flatten!
|
79
|
-
end
|
80
|
-
|
81
|
-
# Specifies a humanized form of a string by a regular expression rule or by a string mapping.
|
82
|
-
# When using a regular expression based replacement, the normal humanize formatting is called after the replacement.
|
83
|
-
# When a string is used, the human form should be specified as desired (example: 'The name', not 'the_name')
|
84
|
-
#
|
85
|
-
# Examples:
|
86
|
-
# human /_cnt$/i, '\1_count'
|
87
|
-
# human "legacy_col_person_name", "Name"
|
88
|
-
def human(rule, replacement)
|
89
|
-
@humans.insert(0, [rule, replacement])
|
90
|
-
end
|
91
|
-
|
92
|
-
# Clears the loaded inflections within a given scope (default is <tt>:all</tt>).
|
93
|
-
# Give the scope as a symbol of the inflection type, the options are: <tt>:plurals</tt>,
|
94
|
-
# <tt>:singulars</tt>, <tt>:uncountables</tt>, <tt>:humans</tt>.
|
95
|
-
#
|
96
|
-
# Examples:
|
97
|
-
# clear :all
|
98
|
-
# clear :plurals
|
99
|
-
def clear(scope = :all)
|
100
|
-
case scope
|
101
|
-
when :all
|
102
|
-
@plurals, @singulars, @uncountables = [], [], []
|
103
|
-
else
|
104
|
-
instance_variable_set "@#{scope}", []
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
# Yields a singleton instance of Inflector::Inflections so you can specify additional
|
110
|
-
# inflector rules.
|
111
|
-
#
|
112
|
-
# Example:
|
113
|
-
# ActiveSupport::Inflector.inflections do |inflect|
|
114
|
-
# inflect.uncountable "rails"
|
115
|
-
# end
|
116
|
-
def inflections
|
117
|
-
if block_given?
|
118
|
-
yield Inflections.instance
|
119
|
-
else
|
120
|
-
Inflections.instance
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
# Returns the plural form of the word in the string.
|
125
|
-
#
|
126
|
-
# Examples:
|
127
|
-
# "post".pluralize # => "posts"
|
128
|
-
# "octopus".pluralize # => "octopi"
|
129
|
-
# "sheep".pluralize # => "sheep"
|
130
|
-
# "words".pluralize # => "words"
|
131
|
-
# "CamelOctopus".pluralize # => "CamelOctopi"
|
132
|
-
def pluralize(word)
|
133
|
-
result = word.to_s.dup
|
134
|
-
|
135
|
-
if word.empty? || inflections.uncountables.include?(result.downcase)
|
136
|
-
result
|
137
|
-
else
|
138
|
-
inflections.plurals.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
139
|
-
result
|
140
|
-
end
|
141
|
-
end
|
142
|
-
|
143
|
-
# The reverse of +pluralize+, returns the singular form of a word in a string.
|
144
|
-
#
|
145
|
-
# Examples:
|
146
|
-
# "posts".singularize # => "post"
|
147
|
-
# "octopi".singularize # => "octopus"
|
148
|
-
# "sheep".singularize # => "sheep"
|
149
|
-
# "word".singularize # => "word"
|
150
|
-
# "CamelOctopi".singularize # => "CamelOctopus"
|
151
|
-
def singularize(word)
|
152
|
-
result = word.to_s.dup
|
153
|
-
|
154
|
-
if inflections.uncountables.any? { |inflection| result =~ /\b(#{inflection})\Z/i }
|
155
|
-
result
|
156
|
-
else
|
157
|
-
inflections.singulars.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
158
|
-
result
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
# Capitalizes the first word and turns underscores into spaces and strips a
|
163
|
-
# trailing "_id", if any. Like +titleize+, this is meant for creating pretty output.
|
164
|
-
#
|
165
|
-
# Examples:
|
166
|
-
# "employee_salary" # => "Employee salary"
|
167
|
-
# "author_id" # => "Author"
|
168
|
-
def humanize(lower_case_and_underscored_word)
|
169
|
-
result = lower_case_and_underscored_word.to_s.dup
|
170
|
-
|
171
|
-
inflections.humans.each { |(rule, replacement)| break if result.gsub!(rule, replacement) }
|
172
|
-
result.gsub(/_id$/, "").gsub(/_/, " ").capitalize
|
173
|
-
end
|
174
|
-
|
175
|
-
# Capitalizes all the words and replaces some characters in the string to create
|
176
|
-
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
|
177
|
-
# used in the Rails internals.
|
178
|
-
#
|
179
|
-
# +titleize+ is also aliased as as +titlecase+.
|
180
|
-
#
|
181
|
-
# Examples:
|
182
|
-
# "man from the boondocks".titleize # => "Man From The Boondocks"
|
183
|
-
# "x-men: the last stand".titleize # => "X Men: The Last Stand"
|
184
|
-
def titleize(word)
|
185
|
-
humanize(underscore(word)).gsub(/\b('?[a-z])/) { $1.capitalize }
|
186
|
-
end
|
187
|
-
|
188
|
-
# Create the name of a table like Rails does for models to table names. This method
|
189
|
-
# uses the +pluralize+ method on the last word in the string.
|
190
|
-
#
|
191
|
-
# Examples
|
192
|
-
# "RawScaledScorer".tableize # => "raw_scaled_scorers"
|
193
|
-
# "egg_and_ham".tableize # => "egg_and_hams"
|
194
|
-
# "fancyCategory".tableize # => "fancy_categories"
|
195
|
-
def tableize(class_name)
|
196
|
-
pluralize(underscore(class_name))
|
197
|
-
end
|
198
|
-
|
199
|
-
# Create a class name from a plural table name like Rails does for table names to models.
|
200
|
-
# Note that this returns a string and not a Class. (To convert to an actual class
|
201
|
-
# follow +classify+ with +constantize+.)
|
202
|
-
#
|
203
|
-
# Examples:
|
204
|
-
# "egg_and_hams".classify # => "EggAndHam"
|
205
|
-
# "posts".classify # => "Post"
|
206
|
-
#
|
207
|
-
# Singular names are not handled correctly:
|
208
|
-
# "business".classify # => "Busines"
|
209
|
-
def classify(table_name)
|
210
|
-
# strip out any leading schema name
|
211
|
-
camelize(singularize(table_name.to_s.sub(/.*\./, '')))
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
@@ -1,153 +0,0 @@
|
|
1
|
-
# https://raw.github.com/rails/rails/20768176292cbcb883ab152b4aa9ed8c664771cd/activesupport/lib/active_support/inflector/methods.rb
|
2
|
-
# not https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/inflector/methods.rb
|
3
|
-
module ActiveSupport
|
4
|
-
# The Inflector transforms words from singular to plural, class names to table names, modularized class names to ones without,
|
5
|
-
# and class names to foreign keys. The default inflections for pluralization, singularization, and uncountable words are kept
|
6
|
-
# in inflections.rb.
|
7
|
-
#
|
8
|
-
# The Rails core team has stated patches for the inflections library will not be accepted
|
9
|
-
# in order to avoid breaking legacy applications which may be relying on errant inflections.
|
10
|
-
# If you discover an incorrect inflection and require it for your application, you'll need
|
11
|
-
# to correct it yourself (explained below).
|
12
|
-
module Inflector
|
13
|
-
extend self
|
14
|
-
|
15
|
-
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to +camelize+
|
16
|
-
# is set to <tt>:lower</tt> then +camelize+ produces lowerCamelCase.
|
17
|
-
#
|
18
|
-
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
|
19
|
-
#
|
20
|
-
# Examples:
|
21
|
-
# "active_record".camelize # => "ActiveRecord"
|
22
|
-
# "active_record".camelize(:lower) # => "activeRecord"
|
23
|
-
# "active_record/errors".camelize # => "ActiveRecord::Errors"
|
24
|
-
# "active_record/errors".camelize(:lower) # => "activeRecord::Errors"
|
25
|
-
#
|
26
|
-
# As a rule of thumb you can think of +camelize+ as the inverse of +underscore+,
|
27
|
-
# though there are cases where that does not hold:
|
28
|
-
#
|
29
|
-
# "SSLError".underscore.camelize # => "SslError"
|
30
|
-
def camelize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
31
|
-
if first_letter_in_uppercase
|
32
|
-
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
33
|
-
else
|
34
|
-
lower_case_and_underscored_word.to_s[0].chr.downcase + camelize(lower_case_and_underscored_word)[1..-1]
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
# Makes an underscored, lowercase form from the expression in the string.
|
39
|
-
#
|
40
|
-
# Changes '::' to '/' to convert namespaces to paths.
|
41
|
-
#
|
42
|
-
# Examples:
|
43
|
-
# "ActiveRecord".underscore # => "active_record"
|
44
|
-
# "ActiveRecord::Errors".underscore # => active_record/errors
|
45
|
-
#
|
46
|
-
# As a rule of thumb you can think of +underscore+ as the inverse of +camelize+,
|
47
|
-
# though there are cases where that does not hold:
|
48
|
-
#
|
49
|
-
# "SSLError".underscore.camelize # => "SslError"
|
50
|
-
def underscore(camel_cased_word)
|
51
|
-
word = camel_cased_word.to_s.dup
|
52
|
-
word.gsub!(/::/, '/')
|
53
|
-
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
54
|
-
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
55
|
-
word.tr!("-", "_")
|
56
|
-
word.downcase!
|
57
|
-
word
|
58
|
-
end
|
59
|
-
|
60
|
-
# Replaces underscores with dashes in the string.
|
61
|
-
#
|
62
|
-
# Example:
|
63
|
-
# "puni_puni" # => "puni-puni"
|
64
|
-
def dasherize(underscored_word)
|
65
|
-
underscored_word.gsub(/_/, '-')
|
66
|
-
end
|
67
|
-
|
68
|
-
# Removes the module part from the expression in the string.
|
69
|
-
#
|
70
|
-
# Examples:
|
71
|
-
# "ActiveRecord::CoreExtensions::String::Inflections".demodulize # => "Inflections"
|
72
|
-
# "Inflections".demodulize # => "Inflections"
|
73
|
-
def demodulize(class_name_in_module)
|
74
|
-
class_name_in_module.to_s.gsub(/^.*::/, '')
|
75
|
-
end
|
76
|
-
|
77
|
-
# Creates a foreign key name from a class name.
|
78
|
-
# +separate_class_name_and_id_with_underscore+ sets whether
|
79
|
-
# the method should put '_' between the name and 'id'.
|
80
|
-
#
|
81
|
-
# Examples:
|
82
|
-
# "Message".foreign_key # => "message_id"
|
83
|
-
# "Message".foreign_key(false) # => "messageid"
|
84
|
-
# "Admin::Post".foreign_key # => "post_id"
|
85
|
-
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
|
86
|
-
underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
|
87
|
-
end
|
88
|
-
|
89
|
-
# Ruby 1.9 introduces an inherit argument for Module#const_get and
|
90
|
-
# #const_defined? and changes their default behavior.
|
91
|
-
if Module.method(:const_get).arity == 1
|
92
|
-
# Tries to find a constant with the name specified in the argument string:
|
93
|
-
#
|
94
|
-
# "Module".constantize # => Module
|
95
|
-
# "Test::Unit".constantize # => Test::Unit
|
96
|
-
#
|
97
|
-
# The name is assumed to be the one of a top-level constant, no matter whether
|
98
|
-
# it starts with "::" or not. No lexical context is taken into account:
|
99
|
-
#
|
100
|
-
# C = 'outside'
|
101
|
-
# module M
|
102
|
-
# C = 'inside'
|
103
|
-
# C # => 'inside'
|
104
|
-
# "C".constantize # => 'outside', same as ::C
|
105
|
-
# end
|
106
|
-
#
|
107
|
-
# NameError is raised when the name is not in CamelCase or the constant is
|
108
|
-
# unknown.
|
109
|
-
def constantize(camel_cased_word)
|
110
|
-
names = camel_cased_word.split('::')
|
111
|
-
names.shift if names.empty? || names.first.empty?
|
112
|
-
|
113
|
-
constant = Object
|
114
|
-
names.each do |name|
|
115
|
-
constant = constant.const_defined?(name) ? constant.const_get(name) : constant.const_missing(name)
|
116
|
-
end
|
117
|
-
constant
|
118
|
-
end
|
119
|
-
else
|
120
|
-
def constantize(camel_cased_word) #:nodoc:
|
121
|
-
names = camel_cased_word.split('::')
|
122
|
-
names.shift if names.empty? || names.first.empty?
|
123
|
-
|
124
|
-
constant = Object
|
125
|
-
names.each do |name|
|
126
|
-
constant = constant.const_defined?(name, false) ? constant.const_get(name) : constant.const_missing(name)
|
127
|
-
end
|
128
|
-
constant
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
# Turns a number into an ordinal string used to denote the position in an
|
133
|
-
# ordered sequence such as 1st, 2nd, 3rd, 4th.
|
134
|
-
#
|
135
|
-
# Examples:
|
136
|
-
# ordinalize(1) # => "1st"
|
137
|
-
# ordinalize(2) # => "2nd"
|
138
|
-
# ordinalize(1002) # => "1002nd"
|
139
|
-
# ordinalize(1003) # => "1003rd"
|
140
|
-
def ordinalize(number)
|
141
|
-
if (11..13).include?(number.to_i % 100)
|
142
|
-
"#{number}th"
|
143
|
-
else
|
144
|
-
case number.to_i % 10
|
145
|
-
when 1; "#{number}st"
|
146
|
-
when 2; "#{number}nd"
|
147
|
-
when 3; "#{number}rd"
|
148
|
-
else "#{number}th"
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
@@ -1,106 +0,0 @@
|
|
1
|
-
# https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/core_ext/object/blank.rb
|
2
|
-
# encoding: utf-8
|
3
|
-
|
4
|
-
class Object
|
5
|
-
# An object is blank if it's false, empty, or a whitespace string.
|
6
|
-
# For example, '', ' ', +nil+, [], and {} are all blank.
|
7
|
-
#
|
8
|
-
# This simplifies:
|
9
|
-
#
|
10
|
-
# if address.nil? || address.empty?
|
11
|
-
#
|
12
|
-
# ...to:
|
13
|
-
#
|
14
|
-
# if address.blank?
|
15
|
-
def blank?
|
16
|
-
respond_to?(:empty?) ? empty? : !self
|
17
|
-
end
|
18
|
-
|
19
|
-
# An object is present if it's not <tt>blank?</tt>.
|
20
|
-
def present?
|
21
|
-
!blank?
|
22
|
-
end
|
23
|
-
|
24
|
-
# Returns object if it's <tt>present?</tt> otherwise returns +nil+.
|
25
|
-
# <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
|
26
|
-
#
|
27
|
-
# This is handy for any representation of objects where blank is the same
|
28
|
-
# as not present at all. For example, this simplifies a common check for
|
29
|
-
# HTTP POST/query parameters:
|
30
|
-
#
|
31
|
-
# state = params[:state] if params[:state].present?
|
32
|
-
# country = params[:country] if params[:country].present?
|
33
|
-
# region = state || country || 'US'
|
34
|
-
#
|
35
|
-
# ...becomes:
|
36
|
-
#
|
37
|
-
# region = params[:state].presence || params[:country].presence || 'US'
|
38
|
-
def presence
|
39
|
-
self if present?
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
43
|
-
class NilClass
|
44
|
-
# +nil+ is blank:
|
45
|
-
#
|
46
|
-
# nil.blank? # => true
|
47
|
-
def blank?
|
48
|
-
true
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
class FalseClass
|
53
|
-
# +false+ is blank:
|
54
|
-
#
|
55
|
-
# false.blank? # => true
|
56
|
-
def blank?
|
57
|
-
true
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
class TrueClass
|
62
|
-
# +true+ is not blank:
|
63
|
-
#
|
64
|
-
# true.blank? # => false
|
65
|
-
def blank?
|
66
|
-
false
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
class Array
|
71
|
-
# An array is blank if it's empty:
|
72
|
-
#
|
73
|
-
# [].blank? # => true
|
74
|
-
# [1,2,3].blank? # => false
|
75
|
-
alias_method :blank?, :empty?
|
76
|
-
end
|
77
|
-
|
78
|
-
class Hash
|
79
|
-
# A hash is blank if it's empty:
|
80
|
-
#
|
81
|
-
# {}.blank? # => true
|
82
|
-
# { key: 'value' }.blank? # => false
|
83
|
-
alias_method :blank?, :empty?
|
84
|
-
end
|
85
|
-
|
86
|
-
class String
|
87
|
-
# A string is blank if it's empty or contains whitespaces only:
|
88
|
-
#
|
89
|
-
# ''.blank? # => true
|
90
|
-
# ' '.blank? # => true
|
91
|
-
# ' '.blank? # => true
|
92
|
-
# ' something here '.blank? # => false
|
93
|
-
def blank?
|
94
|
-
self !~ /[^[:space:]]/
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
class Numeric #:nodoc:
|
99
|
-
# No number is blank:
|
100
|
-
#
|
101
|
-
# 1.blank? # => false
|
102
|
-
# 0.blank? # => false
|
103
|
-
def blank?
|
104
|
-
false
|
105
|
-
end
|
106
|
-
end
|
@@ -1,12 +0,0 @@
|
|
1
|
-
# https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/core_ext/object/to_json.rb
|
2
|
-
# Hack to load json gem first so we can overwrite its to_json.
|
3
|
-
begin
|
4
|
-
require 'json'
|
5
|
-
rescue LoadError
|
6
|
-
end
|
7
|
-
|
8
|
-
# The JSON gem adds a few modules to Ruby core classes containing :to_json definition, overwriting
|
9
|
-
# their default behavior. That said, we need to define the basic to_json method in all of them,
|
10
|
-
# otherwise they will always use to_json gem implementation, which is backwards incompatible in
|
11
|
-
# several cases (for instance, the JSON implementation for Hash does not work) with inheritance
|
12
|
-
# and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json.
|
@@ -1,195 +0,0 @@
|
|
1
|
-
# https://github.com/rails/rails/blob/e8727d37fc49d5bf9976c3cb5c46badb92cf4ced/activesupport/lib/active_support/core_ext/string/inflections.rb
|
2
|
-
MetricFu.lib_require { 'core_ext/inflector/inflections' }
|
3
|
-
|
4
|
-
# String inflections define new methods on the String class to transform names for different purposes.
|
5
|
-
# For instance, you can figure out the name of a table from the name of a class.
|
6
|
-
#
|
7
|
-
# 'ScaleScore'.tableize # => "scale_scores"
|
8
|
-
#
|
9
|
-
class String
|
10
|
-
# Returns the plural form of the word in the string.
|
11
|
-
#
|
12
|
-
# If the optional parameter +count+ is specified,
|
13
|
-
# the singular form will be returned if <tt>count == 1</tt>.
|
14
|
-
# For any other value of +count+ the plural will be returned.
|
15
|
-
#
|
16
|
-
# If the optional parameter +locale+ is specified,
|
17
|
-
# the word will be pluralized as a word of that language.
|
18
|
-
# By default, this parameter is set to <tt>:en</tt>.
|
19
|
-
# You must define your own inflection rules for languages other than English.
|
20
|
-
#
|
21
|
-
# 'post'.pluralize # => "posts"
|
22
|
-
# 'octopus'.pluralize # => "octopi"
|
23
|
-
# 'sheep'.pluralize # => "sheep"
|
24
|
-
# 'words'.pluralize # => "words"
|
25
|
-
# 'the blue mailman'.pluralize # => "the blue mailmen"
|
26
|
-
# 'CamelOctopus'.pluralize # => "CamelOctopi"
|
27
|
-
# 'apple'.pluralize(1) # => "apple"
|
28
|
-
# 'apple'.pluralize(2) # => "apples"
|
29
|
-
# 'ley'.pluralize(:es) # => "leyes"
|
30
|
-
# 'ley'.pluralize(1, :es) # => "ley"
|
31
|
-
def pluralize(count = nil, locale = :en)
|
32
|
-
locale = count if count.is_a?(Symbol)
|
33
|
-
if count == 1
|
34
|
-
self
|
35
|
-
else
|
36
|
-
ActiveSupport::Inflector.pluralize(self, locale)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
# The reverse of +pluralize+, returns the singular form of a word in a string.
|
41
|
-
#
|
42
|
-
# If the optional parameter +locale+ is specified,
|
43
|
-
# the word will be singularized as a word of that language.
|
44
|
-
# By default, this parameter is set to <tt>:en</tt>.
|
45
|
-
# You must define your own inflection rules for languages other than English.
|
46
|
-
#
|
47
|
-
# 'posts'.singularize # => "post"
|
48
|
-
# 'octopi'.singularize # => "octopus"
|
49
|
-
# 'sheep'.singularize # => "sheep"
|
50
|
-
# 'word'.singularize # => "word"
|
51
|
-
# 'the blue mailmen'.singularize # => "the blue mailman"
|
52
|
-
# 'CamelOctopi'.singularize # => "CamelOctopus"
|
53
|
-
# 'leyes'.singularize(:es) # => "ley"
|
54
|
-
def singularize(locale = :en)
|
55
|
-
ActiveSupport::Inflector.singularize(self, locale)
|
56
|
-
end
|
57
|
-
|
58
|
-
# +constantize+ tries to find a declared constant with the name specified
|
59
|
-
# in the string. It raises a NameError when the name is not in CamelCase
|
60
|
-
# or is not initialized. See ActiveSupport::Inflector.constantize
|
61
|
-
#
|
62
|
-
# 'Module'.constantize # => Module
|
63
|
-
# 'Class'.constantize # => Class
|
64
|
-
# 'blargle'.constantize # => NameError: wrong constant name blargle
|
65
|
-
def constantize
|
66
|
-
ActiveSupport::Inflector.constantize(self)
|
67
|
-
end
|
68
|
-
|
69
|
-
# +safe_constantize+ tries to find a declared constant with the name specified
|
70
|
-
# in the string. It returns nil when the name is not in CamelCase
|
71
|
-
# or is not initialized. See ActiveSupport::Inflector.safe_constantize
|
72
|
-
#
|
73
|
-
# 'Module'.safe_constantize # => Module
|
74
|
-
# 'Class'.safe_constantize # => Class
|
75
|
-
# 'blargle'.safe_constantize # => nil
|
76
|
-
def safe_constantize
|
77
|
-
ActiveSupport::Inflector.safe_constantize(self)
|
78
|
-
end
|
79
|
-
|
80
|
-
# By default, +camelize+ converts strings to UpperCamelCase. If the argument to camelize
|
81
|
-
# is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
|
82
|
-
#
|
83
|
-
# +camelize+ will also convert '/' to '::' which is useful for converting paths to namespaces.
|
84
|
-
#
|
85
|
-
# 'active_record'.camelize # => "ActiveRecord"
|
86
|
-
# 'active_record'.camelize(:lower) # => "activeRecord"
|
87
|
-
# 'active_record/errors'.camelize # => "ActiveRecord::Errors"
|
88
|
-
# 'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"
|
89
|
-
def camelize(first_letter = :upper)
|
90
|
-
case first_letter
|
91
|
-
when :upper
|
92
|
-
ActiveSupport::Inflector.camelize(self, true)
|
93
|
-
when :lower
|
94
|
-
ActiveSupport::Inflector.camelize(self, false)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
alias_method :camelcase, :camelize
|
98
|
-
|
99
|
-
# Capitalizes all the words and replaces some characters in the string to create
|
100
|
-
# a nicer looking title. +titleize+ is meant for creating pretty output. It is not
|
101
|
-
# used in the Rails internals.
|
102
|
-
#
|
103
|
-
# +titleize+ is also aliased as +titlecase+.
|
104
|
-
#
|
105
|
-
# 'man from the boondocks'.titleize # => "Man From The Boondocks"
|
106
|
-
# 'x-men: the last stand'.titleize # => "X Men: The Last Stand"
|
107
|
-
def titleize
|
108
|
-
ActiveSupport::Inflector.titleize(self)
|
109
|
-
end
|
110
|
-
alias_method :titlecase, :titleize
|
111
|
-
|
112
|
-
# The reverse of +camelize+. Makes an underscored, lowercase form from the expression in the string.
|
113
|
-
#
|
114
|
-
# +underscore+ will also change '::' to '/' to convert namespaces to paths.
|
115
|
-
#
|
116
|
-
# 'ActiveModel'.underscore # => "active_model"
|
117
|
-
# 'ActiveModel::Errors'.underscore # => "active_model/errors"
|
118
|
-
def underscore
|
119
|
-
ActiveSupport::Inflector.underscore(self)
|
120
|
-
end
|
121
|
-
|
122
|
-
# Replaces underscores with dashes in the string.
|
123
|
-
#
|
124
|
-
# 'puni_puni'.dasherize # => "puni-puni"
|
125
|
-
def dasherize
|
126
|
-
ActiveSupport::Inflector.dasherize(self)
|
127
|
-
end
|
128
|
-
|
129
|
-
# Removes the module part from the constant expression in the string.
|
130
|
-
#
|
131
|
-
# 'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
|
132
|
-
# 'Inflections'.demodulize # => "Inflections"
|
133
|
-
#
|
134
|
-
# See also +deconstantize+.
|
135
|
-
def demodulize
|
136
|
-
ActiveSupport::Inflector.demodulize(self)
|
137
|
-
end
|
138
|
-
|
139
|
-
# Removes the rightmost segment from the constant expression in the string.
|
140
|
-
#
|
141
|
-
# 'Net::HTTP'.deconstantize # => "Net"
|
142
|
-
# '::Net::HTTP'.deconstantize # => "::Net"
|
143
|
-
# 'String'.deconstantize # => ""
|
144
|
-
# '::String'.deconstantize # => ""
|
145
|
-
# ''.deconstantize # => ""
|
146
|
-
#
|
147
|
-
# See also +demodulize+.
|
148
|
-
def deconstantize
|
149
|
-
ActiveSupport::Inflector.deconstantize(self)
|
150
|
-
end
|
151
|
-
|
152
|
-
# Creates the name of a table like Rails does for models to table names. This method
|
153
|
-
# uses the +pluralize+ method on the last word in the string.
|
154
|
-
#
|
155
|
-
# 'RawScaledScorer'.tableize # => "raw_scaled_scorers"
|
156
|
-
# 'egg_and_ham'.tableize # => "egg_and_hams"
|
157
|
-
# 'fancyCategory'.tableize # => "fancy_categories"
|
158
|
-
def tableize
|
159
|
-
ActiveSupport::Inflector.tableize(self)
|
160
|
-
end
|
161
|
-
|
162
|
-
# Create a class name from a plural table name like Rails does for table names to models.
|
163
|
-
# Note that this returns a string and not a class. (To convert to an actual class
|
164
|
-
# follow +classify+ with +constantize+.)
|
165
|
-
#
|
166
|
-
# 'egg_and_hams'.classify # => "EggAndHam"
|
167
|
-
# 'posts'.classify # => "Post"
|
168
|
-
#
|
169
|
-
# Singular names are not handled correctly.
|
170
|
-
#
|
171
|
-
# 'business'.classify # => "Busines"
|
172
|
-
def classify
|
173
|
-
ActiveSupport::Inflector.classify(self)
|
174
|
-
end
|
175
|
-
|
176
|
-
# Capitalizes the first word, turns underscores into spaces, and strips '_id'.
|
177
|
-
# Like +titleize+, this is meant for creating pretty output.
|
178
|
-
#
|
179
|
-
# 'employee_salary'.humanize # => "Employee salary"
|
180
|
-
# 'author_id'.humanize # => "Author"
|
181
|
-
def humanize
|
182
|
-
ActiveSupport::Inflector.humanize(self)
|
183
|
-
end
|
184
|
-
|
185
|
-
# Creates a foreign key name from a class name.
|
186
|
-
# +separate_class_name_and_id_with_underscore+ sets whether
|
187
|
-
# the method should put '_' between the name and 'id'.
|
188
|
-
#
|
189
|
-
# 'Message'.foreign_key # => "message_id"
|
190
|
-
# 'Message'.foreign_key(false) # => "messageid"
|
191
|
-
# 'Admin::Post'.foreign_key # => "post_id"
|
192
|
-
def foreign_key(separate_class_name_and_id_with_underscore = true)
|
193
|
-
ActiveSupport::Inflector.foreign_key(self, separate_class_name_and_id_with_underscore)
|
194
|
-
end
|
195
|
-
end
|