metric_fu 1.5.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +5 -0
- data/MIT-LICENSE +1 -1
- data/README +8 -6
- data/Rakefile +5 -3
- data/TODO +0 -5
- data/lib/base/base_template.rb +16 -0
- data/lib/base/churn_analyzer.rb +52 -0
- data/lib/base/code_issue.rb +97 -0
- data/lib/base/configuration.rb +4 -2
- data/lib/base/flay_analyzer.rb +50 -0
- data/lib/base/flog_analyzer.rb +43 -0
- data/lib/base/line_numbers.rb +65 -0
- data/lib/base/location.rb +83 -0
- data/lib/base/metric_analyzer.rb +404 -0
- data/lib/base/ranking.rb +33 -0
- data/lib/base/rcov_analyzer.rb +43 -0
- data/lib/base/reek_analyzer.rb +114 -0
- data/lib/base/roodi_analyzer.rb +37 -0
- data/lib/base/saikuro_analyzer.rb +48 -0
- data/lib/base/scoring_strategies.rb +29 -0
- data/lib/base/stats_analyzer.rb +37 -0
- data/lib/base/table.rb +102 -0
- data/lib/generators/hotspots.rb +52 -0
- data/lib/generators/rcov.rb +41 -0
- data/lib/metric_fu.rb +5 -3
- data/lib/templates/awesome/hotspots.html.erb +54 -0
- data/lib/templates/awesome/index.html.erb +3 -0
- data/lib/templates/standard/hotspots.html.erb +54 -0
- data/spec/base/line_numbers_spec.rb +62 -0
- data/spec/generators/rails_best_practices_spec.rb +52 -0
- data/spec/generators/rcov_spec.rb +180 -0
- data/spec/generators/roodi_spec.rb +24 -0
- data/spec/graphs/rails_best_practices_grapher_spec.rb +61 -0
- data/spec/graphs/stats_grapher_spec.rb +68 -0
- data/spec/resources/line_numbers/foo.rb +33 -0
- data/spec/resources/line_numbers/module.rb +11 -0
- data/spec/resources/line_numbers/module_surrounds_class.rb +15 -0
- data/spec/resources/line_numbers/two_classes.rb +11 -0
- data/spec/resources/yml/metric_missing.yml +1 -0
- metadata +51 -11
data/HISTORY
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
=== MetricFu 2.0.0 / 2010-11-10
|
2
|
+
|
3
|
+
* Hotspots - Dan Mayer, Ben Brinckerhoff, Jake Scruggs
|
4
|
+
* Rcov integration with Hotspots - Jake Scruggs, Tony Castiglione, Rob Meyer
|
5
|
+
|
1
6
|
=== MetricFu 1.5.1 / 2010-7-28
|
2
7
|
|
3
8
|
* Patch that allows graphers to skip dates that didn't generate metrics for that graph (GitHub Issue #20). - Chris Griego
|
data/MIT-LICENSE
CHANGED
data/README
CHANGED
@@ -3,12 +3,13 @@ See http://metric-fu.rubyforge.org/ for documentation, or the HISTORY file for a
|
|
3
3
|
How to contribute:
|
4
4
|
|
5
5
|
1. Fork metric_fu on github.
|
6
|
-
2. 'gem install metric_fu
|
7
|
-
3.
|
8
|
-
4. Run
|
9
|
-
5.
|
10
|
-
6. Make
|
11
|
-
7.
|
6
|
+
2. 'gem install metric_fu' #to get gem dependencies
|
7
|
+
3. Install the gems RSpec 1.3.0, test-construct, and googlecharts
|
8
|
+
4. Run the tests ('rake')
|
9
|
+
5. Run metric_fu on itself ('rake metrics:all')
|
10
|
+
6. Make the changes you want and back them up with tests.
|
11
|
+
7. Make sure two important rake tests still run ('rake' and 'rake metrics:all')
|
12
|
+
8. Commit and send me a pull request with details as to what has been changed.
|
12
13
|
|
13
14
|
Extra Credit:
|
14
15
|
1. Make sure your changes work in 1.8.7, Ruby Enterprise Edition, and 1.9.1 (Hint use 'rvm' to help install multiple rubies)
|
@@ -24,4 +25,5 @@ Resources:
|
|
24
25
|
Homepage: http://metric-fu.rubyforge.org/
|
25
26
|
Github: http://github.com/jscruggs/metric_fu
|
26
27
|
Google Group: http://groups.google.com/group/metric_fu
|
28
|
+
Issue Tracker: http://github.com/jscruggs/metric_fu/issues
|
27
29
|
My Blog: http://jakescruggs.blogspot.com/
|
data/Rakefile
CHANGED
@@ -8,9 +8,11 @@ desc "Run all specs in spec directory"
|
|
8
8
|
Spec::Rake::SpecTask.new(:spec) do |t|
|
9
9
|
t.spec_files = FileList['spec/**/*_spec.rb']
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
MetricFu::Configuration.run do |config|
|
13
|
-
config.roodi
|
13
|
+
config.roodi = config.roodi.merge(:roodi_config => 'config/roodi_config.yml')
|
14
|
+
config.churn = { :start_date => "1 year ago", :minimum_churn_count => 10}
|
15
|
+
config.hotspots = { :start_date => "1 year ago", :minimum_churn_count => 10}
|
14
16
|
end
|
15
|
-
|
17
|
+
|
16
18
|
task :default => :spec
|
data/TODO
CHANGED
@@ -1,9 +1,4 @@
|
|
1
1
|
== TODO list
|
2
2
|
|
3
3
|
* Color code flog results with scale from: http://jakescruggs.blogspot.com/2008/08/whats-good-flog-score.html
|
4
|
-
* Integrate Flog, Saikuro, and Coverage into one report so you can see methods that have high complexity and low coverage (this is a big one)
|
5
|
-
* Update flog specs so that they actually run flog
|
6
|
-
* Add flay specs that run flay
|
7
|
-
* Convert readme to markdown and rename to README.mkdn so github will render it
|
8
|
-
* Saikuro.rb falls over if tries to parse an empty file. Fair enough. We shouldn't feed it empty files
|
9
4
|
* Make running metric_fu on metric_fu less embarrassing
|
data/lib/base/base_template.rb
CHANGED
@@ -117,6 +117,22 @@ module MetricFu
|
|
117
117
|
name
|
118
118
|
end
|
119
119
|
end
|
120
|
+
|
121
|
+
def display_location(location, stat)
|
122
|
+
file_path, class_name, method_name = location.file_path, location.class_name, location.method_name
|
123
|
+
str = ""
|
124
|
+
str += link_to_filename(file_path)
|
125
|
+
str += " : " if method_name || class_name
|
126
|
+
if(method_name)
|
127
|
+
str += "#{method_name}"
|
128
|
+
else
|
129
|
+
#TODO HOTSPOTS BUG ONLY exists on move over to metric_fu
|
130
|
+
if class_name.is_a?(String)
|
131
|
+
str+= "#{class_name}"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
str
|
135
|
+
end
|
120
136
|
|
121
137
|
def file_url(name, line) # :nodoc:
|
122
138
|
return '' unless name
|
@@ -0,0 +1,52 @@
|
|
1
|
+
class ChurnAnalyzer
|
2
|
+
include ScoringStrategies
|
3
|
+
|
4
|
+
COLUMNS = %w{times_changed}
|
5
|
+
|
6
|
+
def columns
|
7
|
+
COLUMNS
|
8
|
+
end
|
9
|
+
|
10
|
+
def name
|
11
|
+
:churn
|
12
|
+
end
|
13
|
+
|
14
|
+
def map(row)
|
15
|
+
ScoringStrategies.present(row)
|
16
|
+
end
|
17
|
+
|
18
|
+
def reduce(scores)
|
19
|
+
ScoringStrategies.sum(scores)
|
20
|
+
end
|
21
|
+
|
22
|
+
def score(metric_ranking, item)
|
23
|
+
flat_churn_score = 0.50
|
24
|
+
metric_ranking.scored?(item) ? flat_churn_score : 0
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_records(data, table)
|
28
|
+
return if data==nil
|
29
|
+
Array(data[:changes]).each do |change|
|
30
|
+
table << {
|
31
|
+
"metric" => :churn,
|
32
|
+
"times_changed" => change[:times_changed],
|
33
|
+
"file_path" => change[:file_path]
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def self.update_changes(total, changed)
|
41
|
+
changed.each do |change|
|
42
|
+
#should work as has_key(change), but hash == doesn't work on 1.8.6 here for some reason it never matches
|
43
|
+
if total.has_key?(change.to_a.sort)
|
44
|
+
total[change.to_a.sort] += 1
|
45
|
+
else
|
46
|
+
total[change.to_a.sort] = 1
|
47
|
+
end
|
48
|
+
end
|
49
|
+
total
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require MetricFu::LIB_ROOT + '/base/metric_analyzer'
|
3
|
+
require MetricFu::LIB_ROOT + '/base/flog_analyzer'
|
4
|
+
require MetricFu::LIB_ROOT + '/base/saikuro_analyzer'
|
5
|
+
require MetricFu::LIB_ROOT + '/base/churn_analyzer'
|
6
|
+
require MetricFu::LIB_ROOT + '/base/reek_analyzer'
|
7
|
+
require MetricFu::LIB_ROOT + '/base/flay_analyzer'
|
8
|
+
|
9
|
+
module CarefulArray
|
10
|
+
|
11
|
+
def carefully_remove(elements)
|
12
|
+
missing_elements = elements - self
|
13
|
+
raise "Cannot delete missing elements: #{missing_elements.inspect}" unless missing_elements.empty?
|
14
|
+
(self - elements).extend(CarefulArray)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
class CodeIssue < DelegateClass(Record) #DelegateClass(Ruport::Data::Record)
|
20
|
+
include Comparable
|
21
|
+
|
22
|
+
# TODO: Yuck! 'stat_value' is a column for StatAnalyzer
|
23
|
+
EXCLUDED_COLUMNS = FlogAnalyzer::COLUMNS + SaikuroAnalyzer::COLUMNS + ['stat_value'] + ChurnAnalyzer::COLUMNS + ReekAnalyzer.new.columns.extend(CarefulArray).carefully_remove(['reek__type_name', 'reek__comparable_message']) + FlayAnalyzer.new.columns.extend(CarefulArray).carefully_remove(['flay_matching_reason'])
|
24
|
+
|
25
|
+
def <=>(other)
|
26
|
+
spaceship_for_columns(self.attributes, other)
|
27
|
+
end
|
28
|
+
|
29
|
+
def ===(other)
|
30
|
+
self.hash_for(included_columns_hash, included_columns) == other.hash_for(included_columns_hash, included_columns)
|
31
|
+
end
|
32
|
+
|
33
|
+
def spaceship_for_columns(columns, other)
|
34
|
+
columns.each do |column|
|
35
|
+
equality = self[column].to_s <=> other[column].to_s
|
36
|
+
return equality if equality!=0
|
37
|
+
end
|
38
|
+
return 0
|
39
|
+
end
|
40
|
+
|
41
|
+
def hash_for(column_hash, columns)
|
42
|
+
@hashes ||= {}
|
43
|
+
# fetch would be cleaner, but slower
|
44
|
+
if @hashes.has_key?(column_hash)
|
45
|
+
@hashes[column_hash]
|
46
|
+
else
|
47
|
+
values = columns.map {|column| self[column]}
|
48
|
+
hash_for_columns = values.join('').hash
|
49
|
+
@hashes[column_hash]=hash_for_columns
|
50
|
+
hash_for_columns
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def included_columns_hash
|
55
|
+
@included_columns_hash ||= included_columns.hash
|
56
|
+
end
|
57
|
+
|
58
|
+
def included_columns
|
59
|
+
@included_columns ||= self.attributes.extend(CarefulArray).carefully_remove(EXCLUDED_COLUMNS)
|
60
|
+
end
|
61
|
+
|
62
|
+
def find_counterpart_index_in(collection)
|
63
|
+
# each_with_index is cleaner, but it is slower and we
|
64
|
+
# spend a lot of time in this loop
|
65
|
+
index = 0
|
66
|
+
collection.each do |issue|
|
67
|
+
return index if self === issue
|
68
|
+
index += 1
|
69
|
+
end
|
70
|
+
return nil
|
71
|
+
end
|
72
|
+
|
73
|
+
def modifies?(other)
|
74
|
+
case self.metric
|
75
|
+
when :reek
|
76
|
+
#return false unless ["Large Class", "Long Method", "Long Parameter List"].include?(self.reek__type_name)
|
77
|
+
return false if self.reek__type_name != other.reek__type_name
|
78
|
+
self.reek__value != other.reek__value
|
79
|
+
when :flog
|
80
|
+
self.score != other.score
|
81
|
+
when :saikuro
|
82
|
+
self.complexity != other.complexity
|
83
|
+
when :stats
|
84
|
+
self.stat_value != other.stat_value
|
85
|
+
when :churn
|
86
|
+
self.times_changed != other.times_changed
|
87
|
+
when :flay
|
88
|
+
#self.flay_reason != other.flay_reason
|
89
|
+
# do nothing for now
|
90
|
+
when :roodi, :stats
|
91
|
+
# do nothing
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Invalid metric type #{self.metric}"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
data/lib/base/configuration.rb
CHANGED
@@ -6,7 +6,8 @@ module MetricFu
|
|
6
6
|
# course, in order to use these metrics, their respective gems must
|
7
7
|
# be installed on the system.
|
8
8
|
AVAILABLE_METRICS = [:churn, :flog, :flay, :reek,
|
9
|
-
:roodi, :saikuro, :rcov
|
9
|
+
:roodi, :saikuro, :rcov,
|
10
|
+
:hotspots]
|
10
11
|
|
11
12
|
AVAILABLE_GRAPHS = [:flog, :flay, :reek, :roodi, :rcov, :rails_best_practices]
|
12
13
|
AVAILABLE_GRAPH_ENGINES = [:gchart, :bluff]
|
@@ -139,6 +140,7 @@ module MetricFu
|
|
139
140
|
:external => nil
|
140
141
|
}
|
141
142
|
@rails_best_practices = {}
|
143
|
+
@hotspots = {}
|
142
144
|
@file_globs_to_ignore = []
|
143
145
|
|
144
146
|
@graph_engine = :bluff # can be :bluff or :gchart
|
@@ -159,7 +161,7 @@ module MetricFu
|
|
159
161
|
@metrics = MetricFu::AVAILABLE_METRICS + [:stats, :rails_best_practices]
|
160
162
|
else
|
161
163
|
@metrics = MetricFu::AVAILABLE_METRICS
|
162
|
-
end
|
164
|
+
end
|
163
165
|
end
|
164
166
|
|
165
167
|
def set_graphs
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class FlayAnalyzer
|
2
|
+
include ScoringStrategies
|
3
|
+
|
4
|
+
COLUMNS = %w{flay_reason flay_matching_reason}
|
5
|
+
|
6
|
+
def columns
|
7
|
+
COLUMNS
|
8
|
+
end
|
9
|
+
|
10
|
+
def name
|
11
|
+
:flay
|
12
|
+
end
|
13
|
+
|
14
|
+
def map(row)
|
15
|
+
ScoringStrategies.present(row)
|
16
|
+
end
|
17
|
+
|
18
|
+
def reduce(scores)
|
19
|
+
ScoringStrategies.sum(scores)
|
20
|
+
end
|
21
|
+
|
22
|
+
def score(metric_ranking, item)
|
23
|
+
ScoringStrategies.percentile(metric_ranking, item)
|
24
|
+
end
|
25
|
+
|
26
|
+
def generate_records(data, table)
|
27
|
+
return if data==nil
|
28
|
+
Array(data[:matches]).each do |match|
|
29
|
+
problems = match[:reason]
|
30
|
+
matching_reason = problems.gsub(/^[0-9]+\) /,'').gsub(/\:[0-9]+/,'')
|
31
|
+
files = []
|
32
|
+
locations = []
|
33
|
+
match[:matches].each do |file_match|
|
34
|
+
file_path = file_match[:name].sub(%r{^/},'')
|
35
|
+
locations << "#{file_path}:#{file_match[:line]}"
|
36
|
+
files << file_path
|
37
|
+
end
|
38
|
+
files = files.uniq
|
39
|
+
files.each do |file|
|
40
|
+
table << {
|
41
|
+
"metric" => self.name,
|
42
|
+
"file_path" => file,
|
43
|
+
"flay_reason" => problems+" files: #{locations.join(', ')}",
|
44
|
+
"flay_matching_reason" => matching_reason
|
45
|
+
}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class FlogAnalyzer
|
2
|
+
include ScoringStrategies
|
3
|
+
|
4
|
+
COLUMNS = %w{score}
|
5
|
+
|
6
|
+
def columns
|
7
|
+
COLUMNS
|
8
|
+
end
|
9
|
+
|
10
|
+
def name
|
11
|
+
:flog
|
12
|
+
end
|
13
|
+
|
14
|
+
def map(row)
|
15
|
+
row.score
|
16
|
+
end
|
17
|
+
|
18
|
+
def reduce(scores)
|
19
|
+
ScoringStrategies.average(scores)
|
20
|
+
end
|
21
|
+
|
22
|
+
def score(metric_ranking, item)
|
23
|
+
ScoringStrategies.identity(metric_ranking, item)
|
24
|
+
end
|
25
|
+
|
26
|
+
def generate_records(data, table)
|
27
|
+
return if data==nil
|
28
|
+
Array(data[:method_containers]).each do |method_container|
|
29
|
+
Array(method_container[:methods]).each do |entry|
|
30
|
+
file_path = entry[1][:path].sub(%r{^/},'') if entry[1][:path]
|
31
|
+
location = Location.for(entry.first)
|
32
|
+
table << {
|
33
|
+
"metric" => name,
|
34
|
+
"score" => entry[1][:score],
|
35
|
+
"file_path" => file_path,
|
36
|
+
"class_name" => location.class_name,
|
37
|
+
"method_name" => location.method_name
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'ruby_parser'
|
2
|
+
|
3
|
+
class LineNumbers
|
4
|
+
|
5
|
+
def initialize(contents)
|
6
|
+
rp = RubyParser.new
|
7
|
+
file_sexp = rp.parse(contents)
|
8
|
+
@locations = {}
|
9
|
+
case file_sexp[0]
|
10
|
+
when :class
|
11
|
+
process_class(file_sexp)
|
12
|
+
when :module
|
13
|
+
process_module(file_sexp)
|
14
|
+
when :block
|
15
|
+
file_sexp.each_of_type(:class) { |sexp| process_class(sexp) }
|
16
|
+
else
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def in_method? line_number
|
21
|
+
!!@locations.detect do |method_name, line_number_range|
|
22
|
+
line_number_range.include?(line_number)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def method_at_line line_number
|
27
|
+
found_method_and_range = @locations.detect do |method_name, line_number_range|
|
28
|
+
line_number_range.include?(line_number)
|
29
|
+
end
|
30
|
+
return nil unless found_method_and_range
|
31
|
+
found_method_and_range.first
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def process_module(sexp)
|
37
|
+
module_name = sexp[1]
|
38
|
+
sexp.each_of_type(:class) do |sexp|
|
39
|
+
process_class(sexp, module_name)
|
40
|
+
hide_methods_from_next_round(sexp)
|
41
|
+
end
|
42
|
+
process_class(sexp)
|
43
|
+
end
|
44
|
+
|
45
|
+
def process_class(sexp, module_name=nil)
|
46
|
+
class_name = sexp[1]
|
47
|
+
process_class_self_blocks(sexp, class_name)
|
48
|
+
module_name_string = module_name ? "#{module_name}::" : nil
|
49
|
+
sexp.each_of_type(:defn) { |s| @locations["#{module_name_string}#{class_name}##{s[1]}"] = (s.line)..(s.last.line) }
|
50
|
+
sexp.each_of_type(:defs) { |s| @locations["#{module_name_string}#{class_name}::#{s[2]}"] = (s.line)..(s.last.line) }
|
51
|
+
end
|
52
|
+
|
53
|
+
def process_class_self_blocks(sexp, class_name)
|
54
|
+
sexp.each_of_type(:sclass) do |sexp_in_class_self_block|
|
55
|
+
sexp_in_class_self_block.each_of_type(:defn) { |s| @locations["#{class_name}::#{s[1]}"] = (s.line)..(s.last.line) }
|
56
|
+
hide_methods_from_next_round(sexp_in_class_self_block)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def hide_methods_from_next_round(sexp)
|
61
|
+
sexp.find_and_replace_all(:defn, :ignore_me)
|
62
|
+
sexp.find_and_replace_all(:defs, :ignore_me)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class Location
|
2
|
+
include Comparable
|
3
|
+
|
4
|
+
attr_accessor :class_name, :method_name, :file_path, :simple_method_name, :hash
|
5
|
+
|
6
|
+
def self.get(file_path, class_name, method_name)
|
7
|
+
# This could be more 'confident' using Maybe, but we want it to be as fast as possible
|
8
|
+
file_path_copy = file_path == nil ? nil : file_path.clone
|
9
|
+
class_name_copy = class_name == nil ? nil : class_name.clone
|
10
|
+
method_name_copy = method_name == nil ? nil : method_name.clone
|
11
|
+
key = [file_path_copy, class_name_copy, method_name_copy]
|
12
|
+
@@locations ||= {}
|
13
|
+
if @@locations.has_key?(key)
|
14
|
+
@@locations[key]
|
15
|
+
else
|
16
|
+
location = self.new(file_path_copy, class_name_copy, method_name_copy)
|
17
|
+
@@locations[key] = location
|
18
|
+
location.freeze # we cache a lot of method call results, so we want location to be immutable
|
19
|
+
location
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(file_path, class_name, method_name)
|
24
|
+
@file_path = file_path
|
25
|
+
@class_name = class_name
|
26
|
+
@method_name = method_name
|
27
|
+
@simple_method_name = @method_name.sub(@class_name,'') unless @method_name == nil
|
28
|
+
@hash = [@file_path, @class_name, @method_name].hash
|
29
|
+
end
|
30
|
+
|
31
|
+
# TODO - we need this method (and hash accessor above) as a temporary hack where we're using Location as a hash key
|
32
|
+
def eql?(other)
|
33
|
+
[self.file_path.to_s, self.class_name.to_s, self.method_name.to_s] == [other.file_path.to_s, other.class_name.to_s, other.method_name.to_s]
|
34
|
+
end
|
35
|
+
|
36
|
+
# END we need these methods as a temporary hack where we're using Location as a hash key
|
37
|
+
|
38
|
+
def self.for(class_or_method_name)
|
39
|
+
class_or_method_name = strip_modules(class_or_method_name)
|
40
|
+
if(class_or_method_name)
|
41
|
+
begin
|
42
|
+
match = class_or_method_name.match(/(.*)((\.|\#|\:\:[a-z])(.+))/)
|
43
|
+
rescue => error
|
44
|
+
#new error during port to metric_fu occasionally a unintialized
|
45
|
+
#MatchData object shows up here. Not expected.
|
46
|
+
match = nil
|
47
|
+
end
|
48
|
+
|
49
|
+
# reek reports the method with :: not # on modules like
|
50
|
+
# module ApplicationHelper \n def signed_in?, convert it so it records correctly
|
51
|
+
# but classes have to start with a capital letter... HACK for REEK bug, reported underlying issue to REEK
|
52
|
+
if(match)
|
53
|
+
class_name = strip_modules(match[1])
|
54
|
+
method_name = class_or_method_name.gsub(/\:\:/,"#")
|
55
|
+
else
|
56
|
+
class_name = strip_modules(class_or_method_name)
|
57
|
+
method_name = nil
|
58
|
+
end
|
59
|
+
else
|
60
|
+
class_name = nil
|
61
|
+
method_name = nil
|
62
|
+
end
|
63
|
+
self.get(nil, class_name, method_name)
|
64
|
+
end
|
65
|
+
|
66
|
+
def <=>(other)
|
67
|
+
[self.file_path.to_s, self.class_name.to_s, self.method_name.to_s] <=> [other.file_path.to_s, other.class_name.to_s, other.method_name.to_s]
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def self.strip_modules(class_or_method_name)
|
73
|
+
# reek reports the method with :: not # on modules like
|
74
|
+
# module ApplicationHelper \n def signed_in?, convert it so it records correctly
|
75
|
+
# but classes have to start with a capital letter... HACK for REEK bug, reported underlying issue to REEK
|
76
|
+
if(class_or_method_name=~/\:\:[A-Z]/)
|
77
|
+
class_or_method_name.split("::").last
|
78
|
+
else
|
79
|
+
class_or_method_name
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|