metric_fu 1.3.0 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/HISTORY CHANGED
@@ -1,3 +1,15 @@
1
+ === MetricFu 1.4.0 / 2010-06-19
2
+
3
+ * Added support for rails_best_practices gem - Richard Huang
4
+ * Added rails stats graphing -- Josh Cronemeyer
5
+ * Parameterize the filetypes for flay. By default flay supports haml as well as rb and has a plugin ability for other filetypes. - bfabry
6
+ * Support for Flog 2.4.0 line numbers - Dan Mayer
7
+ * Saikuro multi input directory patch - Spencer Dillard and Dan Mayer
8
+ * Can now parse rcov analysis file coming from multiple sources with an rcov :external option in the config. - Tarsoly András
9
+ * Fixed open file handles problem in the Saikuro analyzer - aselder, erebor
10
+ * Fix some problems with the google charts - Chris Griego
11
+ * Stop showing the googlecharts warning if you are not using google charts.
12
+
1
13
  === MetricFu 1.3.0 / 2010-01-26
2
14
 
3
15
  * Flay can be configured to ignore scores below a threshold (by default it ignores scores less than 100)
@@ -171,7 +183,7 @@
171
183
 
172
184
  === MetricFu 0.5.1 / 2008-04-25
173
185
 
174
- * Fixed bug with Saikuro report generation
186
+ * Fixed bug with Saikuro report generation - thanks Toby Tripp
175
187
 
176
188
  === MetricFu 0.5.0 / 2008-04-25
177
189
 
data/README CHANGED
@@ -1 +1,27 @@
1
1
  See http://metric-fu.rubyforge.org/ for documentation, or the HISTORY file for a change log.
2
+
3
+ How to contribute:
4
+
5
+ 1. Fork metric_fu on github.
6
+ 2. 'gem install metric_fu --development' #to get development dependencies
7
+ 3. Run the tests ('rake')
8
+ 4. Run metric_fu on itself ('rake metrics:all')
9
+ 5. Make the changes you want and back them up with tests.
10
+ 6. Make sure two important rake tests still run ('rake' and 'rake metrics:all')
11
+ 7. Commit and send me a pull request with details as to what has been changed.
12
+
13
+ Extra Credit:
14
+ 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)
15
+ 2. Post to the Google group explaining what you did and why you did it (I don't merge things in immediately so others might want to use what you've done).
16
+ 3. Update the documentation (web page inside the 'home_page' folder)
17
+ 4. Update the History and give yourself credit.
18
+
19
+
20
+ The more of the above steps you do the easier it will be for me to merge in which will greatly increase you chances of getting your changes accepted.
21
+ If you want to do something really radical (which will touch over %30 of all the files) you might what to ask the Google group first to see if anyone is interested in your change before you spend a lot of time on it.
22
+
23
+ Resources:
24
+ Homepage: http://metric-fu.rubyforge.org/
25
+ Github: http://github.com/jscruggs/metric_fu
26
+ Google Group: http://groups.google.com/group/metric_fu
27
+ My Blog: http://jakescruggs.blogspot.com/
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ $LOAD_PATH << '.'
1
2
  require 'rake'
2
3
  require 'rake/rdoctask'
3
4
  require 'spec/rake/spectask'
@@ -12,4 +13,4 @@ MetricFu::Configuration.run do |config|
12
13
  config.template_class = AwesomeTemplate
13
14
  end
14
15
 
15
- task :default => [:"metrics:all"]
16
+ task :default => :spec
@@ -8,7 +8,7 @@ module MetricFu
8
8
  AVAILABLE_METRICS = [:churn, :flog, :flay, :reek,
9
9
  :roodi, :saikuro, :rcov]
10
10
 
11
- AVAILABLE_GRAPHS = [:flog, :flay, :reek, :roodi, :rcov]
11
+ AVAILABLE_GRAPHS = [:flog, :flay, :reek, :roodi, :rcov, :rails_best_practices]
12
12
  AVAILABLE_GRAPH_ENGINES = [:gchart, :bluff]
13
13
 
14
14
  # The @@configuration class variable holds a global type configuration
@@ -110,7 +110,8 @@ module MetricFu
110
110
  set_graphs
111
111
  set_code_dirs
112
112
  @flay = { :dirs_to_flay => @code_dirs,
113
- :minimum_score => 100 }
113
+ :minimum_score => 100,
114
+ :filetypes => ['rb'] }
114
115
  @flog = { :dirs_to_flog => @code_dirs }
115
116
  @reek = { :dirs_to_reek => @code_dirs }
116
117
  @roodi = { :dirs_to_roodi => @code_dirs }
@@ -132,8 +133,10 @@ module MetricFu
132
133
  "--no-color",
133
134
  "--profile",
134
135
  "--rails",
135
- "--exclude /gems/,/Library/,/usr/,spec"]}
136
-
136
+ "--exclude /gems/,/Library/,/usr/,spec"],
137
+ :external => nil
138
+ }
139
+ @rails_best_practices = {}
137
140
  @file_globs_to_ignore = []
138
141
 
139
142
  @graph_engine = :bluff # can be :bluff or :gchart
@@ -151,14 +154,18 @@ module MetricFu
151
154
  # running within rails.
152
155
  def set_metrics
153
156
  if rails?
154
- @metrics = MetricFu::AVAILABLE_METRICS + [:stats]
157
+ @metrics = MetricFu::AVAILABLE_METRICS + [:stats, :rails_best_practices]
155
158
  else
156
159
  @metrics = MetricFu::AVAILABLE_METRICS
157
160
  end
158
161
  end
159
162
 
160
163
  def set_graphs
161
- @graphs = MetricFu::AVAILABLE_GRAPHS
164
+ if rails?
165
+ @graphs = MetricFu::AVAILABLE_GRAPHS + [:stats]
166
+ else
167
+ @graphs = MetricFu::AVAILABLE_GRAPHS
168
+ end
162
169
  end
163
170
 
164
171
  # Add the 'app' directory if we're running within rails.
data/lib/base/graph.rb CHANGED
@@ -13,7 +13,7 @@ module MetricFu
13
13
  end
14
14
 
15
15
  def add(graph_type, graph_engine)
16
- grapher_name = graph_type.to_s.capitalize + graph_engine.to_s.capitalize + "Grapher"
16
+ grapher_name = graph_type.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase } + graph_engine.to_s.capitalize + "Grapher"
17
17
  self.clazz.push MetricFu.const_get(grapher_name).new
18
18
  end
19
19
 
data/lib/base/report.rb CHANGED
@@ -50,7 +50,7 @@ module MetricFu
50
50
  # @param report_type Hash
51
51
  # The hash to add to the aggregate report_hash
52
52
  def add(report_type)
53
- clazz = MetricFu.const_get(report_type.to_s.capitalize)
53
+ clazz = MetricFu.const_get(report_type.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase })
54
54
  report_hash.merge!(clazz.generate_report)
55
55
  end
56
56
 
@@ -8,7 +8,11 @@ module MetricFu
8
8
  end
9
9
 
10
10
  def emit
11
- files_to_flay = MetricFu.flay[:dirs_to_flay].map{|dir| Dir[File.join(dir, "**/*.rb")] }
11
+ files_to_flay = MetricFu.flay[:dirs_to_flay].map{|dir|
12
+ MetricFu.flay[:filetypes].map {|type|
13
+ Dir[ File.join(dir, "**/*.#{type}") ]
14
+ }.flatten
15
+ }
12
16
  files = remove_excluded_files(files_to_flay.flatten)
13
17
  mimimum_score_parameter = MetricFu.flay[:minimum_score] ? "--mass #{MetricFu.flay[:minimum_score]} " : ""
14
18
  @output = `flay #{mimimum_score_parameter}#{files.join(" ")}`
@@ -120,9 +120,13 @@ module MetricFu
120
120
  end
121
121
 
122
122
  class ScannedMethod
123
- attr_accessor :name, :score, :operators
123
+ attr_accessor :name, :score, :operators, :line
124
124
 
125
125
  def initialize(name, score, operators = [])
126
+ if name.match(/\.rb:\d*/)
127
+ @line = name.match(/\.rb:\d*/).to_s.sub('.rb:','')
128
+ name = name.match(/\S*/)
129
+ end
126
130
  @name = name
127
131
  @score = score.to_f
128
132
  @operators = operators
@@ -130,8 +134,9 @@ module MetricFu
130
134
 
131
135
  def to_h
132
136
  {:name => @name,
133
- :score => @score,
134
- :operators => @operators.map {|o| o.to_h}}
137
+ :score => @score,
138
+ :operators => @operators.map {|o| o.to_h},
139
+ :line => @line}
135
140
  end
136
141
  end
137
142
 
@@ -0,0 +1,31 @@
1
+ module MetricFu
2
+ class RailsBestPractices < Generator
3
+
4
+ def self.verify_dependencies!
5
+ `rails_best_practices --help`
6
+ raise 'sudo gem install rails_best_practices # if you want the rails_best_practices tasks' unless $?.success?
7
+ end
8
+
9
+
10
+ def emit
11
+ @output = `rails_best_practices .`
12
+ end
13
+
14
+ def analyze
15
+ @matches = @output.chomp.split("\n").map{|m| m.split(" - ") }
16
+ total = @matches.pop
17
+ 2.times { @matches.pop } # ignore wiki link
18
+ @matches.reject! {|array| array.empty? }
19
+ @matches.map! do |match|
20
+ file, line = match[0].split(':')
21
+ problem = match[1]
22
+ {:file => file, :line => line, :problem => problem}
23
+ end
24
+ @rails_best_practices_results = {:total => total, :problems => @matches}
25
+ end
26
+
27
+ def to_h
28
+ {:rails_best_practices => @rails_best_practices_results}
29
+ end
30
+ end
31
+ end
@@ -5,17 +5,6 @@ module MetricFu
5
5
  class Rcov < Generator
6
6
  NEW_FILE_MARKER = ("=" * 80) + "\n"
7
7
 
8
- def self.verify_dependencies!
9
- `rcov --help`
10
- unless $?.success?
11
- if RUBY_PLATFORM =~ /java/
12
- raise 'running in jruby - rcov tasks not available'
13
- else
14
- raise 'sudo gem install rcov # if you want the rcov tasks'
15
- end
16
- end
17
- end
18
-
19
8
  class Line
20
9
  attr_accessor :content, :was_run
21
10
 
@@ -30,7 +19,7 @@ module MetricFu
30
19
  end
31
20
 
32
21
  def emit
33
- begin
22
+ unless MetricFu.rcov[:external]
34
23
  FileUtils.rm_rf(MetricFu::Rcov.metric_directory, :verbose => false)
35
24
  Dir.mkdir(MetricFu::Rcov.metric_directory)
36
25
  test_files = FileList[*MetricFu.rcov[:test_files]].join(' ')
@@ -38,21 +27,33 @@ module MetricFu
38
27
  output = ">> #{MetricFu::Rcov.metric_directory}/rcov.txt"
39
28
  puts "** Running the specs/tests in the [#{MetricFu.rcov[:environment]}] environment"
40
29
  `RAILS_ENV=#{MetricFu.rcov[:environment]} rcov #{test_files} #{rcov_opts} #{output}`
41
- rescue LoadError
42
- if RUBY_PLATFORM =~ /java/
43
- puts 'running in jruby - rcov tasks not available'
44
- else
45
- puts 'sudo gem install rcov # if you want the rcov tasks'
46
- end
47
30
  end
48
31
  end
49
32
 
50
33
 
51
34
  def analyze
52
- output = File.open(MetricFu::Rcov.metric_directory + '/rcov.txt').read
35
+ output_file = MetricFu.rcov[:external] ? MetricFu.rcov[:external] : MetricFu::Rcov.metric_directory + '/rcov.txt'
36
+ output = File.open(output_file).read
53
37
  output = output.split(NEW_FILE_MARKER)
54
- # Throw away the first entry - it's the execution time etc.
55
- output.shift
38
+
39
+ output.shift # Throw away the first entry - it's the execution time etc.
40
+
41
+ files = assemble_files(output)
42
+
43
+ @global_total_lines = 0
44
+ @global_total_lines_run = 0
45
+
46
+ @rcov = add_coverage_percentage(files)
47
+ end
48
+
49
+ def to_h
50
+ global_percent_run = ((@global_total_lines_run.to_f / @global_total_lines.to_f) * 100)
51
+ {:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
52
+ end
53
+
54
+ private
55
+
56
+ def assemble_files(output)
56
57
  files = {}
57
58
  output.each_slice(2) {|out| files[out.first.strip] = out.last}
58
59
  files.each_pair {|fname, content| files[fname] = content.split("\n") }
@@ -64,12 +65,13 @@ module MetricFu
64
65
  line = Line.new(raw_line, true).to_h
65
66
  end
66
67
  end
68
+ content.reject! {|line| line[:content].blank? }
67
69
  files[fname] = {:lines => content}
68
70
  end
69
-
70
- # Calculate the percentage of lines run in each file
71
- @global_total_lines = 0
72
- @global_total_lines_run = 0
71
+ files
72
+ end
73
+
74
+ def add_coverage_percentage(files)
73
75
  files.each_pair do |fname, content|
74
76
  lines = content[:lines]
75
77
  @global_total_lines_run += lines_run = lines.find_all {|line| line[:was_run] == true }.length
@@ -77,12 +79,7 @@ module MetricFu
77
79
  percent_run = ((lines_run.to_f / total_lines.to_f) * 100).round
78
80
  files[fname][:percent_run] = percent_run
79
81
  end
80
- @rcov = files
81
- end
82
-
83
- def to_h
84
- global_percent_run = ((@global_total_lines_run.to_f / @global_total_lines.to_f) * 100)
85
- {:rcov => @rcov.merge({:global_percent_run => round_to_tenths(global_percent_run) })}
86
82
  end
83
+
87
84
  end
88
85
  end
@@ -10,7 +10,8 @@ module MetricFu
10
10
  def emit
11
11
  files_to_analyze = MetricFu.roodi[:dirs_to_roodi].map{|dir| Dir[File.join(dir, "**/*.rb")] }
12
12
  files = remove_excluded_files(files_to_analyze.flatten)
13
- @output = `roodi #{files.join(" ")}`
13
+ config = MetricFu.roodi[:roodi_config] ? "-config=#{MetricFu.roodi[:roodi_config]}" : ""
14
+ @output = `roodi #{config} #{files.join(" ")}`
14
15
  end
15
16
 
16
17
  def analyze
@@ -2,20 +2,21 @@ module MetricFu
2
2
 
3
3
  class Saikuro < Generator
4
4
 
5
- def emit
6
- MetricFu.saikuro[:input_directory] = format_directories
7
-
8
- options_string = MetricFu.saikuro.inject("") do |options, option|
9
- options + "--#{option.join(' ')} "
10
- end
11
-
12
- sh %{saikuro #{options_string}} do |ok, response|
13
- unless ok
14
- puts "Saikuro failed with exit status: #{response.exitstatus}"
15
- exit 1
16
- end
17
- end
18
- end
5
+ def emit
6
+ options_string = MetricFu.saikuro.inject("") do |options, option|
7
+ options + "--#{option.join(' ')} " unless option == :input_directory
8
+ end
9
+
10
+ MetricFu.saikuro[:input_directory].each do |input_dir|
11
+ options_string += "--input_directory #{input_dir} "
12
+ end
13
+ sh %{saikuro #{options_string}} do |ok, response|
14
+ unless ok
15
+ puts "Saikuro failed with exit status: #{response.exitstatus}"
16
+ exit 1
17
+ end
18
+ end
19
+ end
19
20
 
20
21
  def format_directories
21
22
  dirs = MetricFu.saikuro[:input_directory].join(" | ")
@@ -23,36 +24,9 @@ module MetricFu
23
24
  end
24
25
 
25
26
  def analyze
26
- @files = []
27
- saikuro_results.each do |path|
28
- if Saikuro::SFile.is_valid_text_file?(path)
29
- file = Saikuro::SFile.new(path)
30
- if file
31
- @files << file
32
- end
33
- end
34
- end
35
- @files = @files.sort_by do |file|
36
- file.elements.
37
- max {|a,b| a.complexity.to_i <=> b.complexity.to_i}.
38
- complexity.to_i
39
- end
40
- @files.reverse!
41
- klasses = []
42
- @files.each {|f| klasses << f.elements}
43
- klasses.flatten!
44
- @classes = klasses.sort_by {|k| k.complexity.to_i}
45
- @classes.reverse!
46
- meths = []
47
- @files.each {|f|
48
- f.elements.each {|el|
49
- el.defs.each {|defn|
50
- defn.name = "#{el.name}##{defn.name}"
51
- meths << defn}
52
- }
53
- }
54
- meths = meths.sort_by {|meth| meth.complexity.to_i}
55
- @meths = meths.reverse
27
+ @files = sort_files(assemble_files)
28
+ @classes = sort_classes(assemble_classes(@files))
29
+ @meths = sort_methods(assemble_methods(@files))
56
30
  end
57
31
 
58
32
  def to_h
@@ -67,10 +41,54 @@ module MetricFu
67
41
  }
68
42
  }
69
43
  end
70
-
71
- def saikuro_results
72
- Dir.glob("#{metric_directory}/**/*.html")
44
+
45
+ private
46
+ def sort_methods(methods)
47
+ methods.sort_by {|method| method.complexity.to_i}.reverse
48
+ end
49
+
50
+ def assemble_methods(files)
51
+ methods = []
52
+ files.each do |file|
53
+ file.elements.each do |element|
54
+ element.defs.each do |defn|
55
+ defn.name = "#{element.name}##{defn.name}"
56
+ methods << defn
57
+ end
58
+ end
59
+ end
60
+ methods
61
+ end
62
+
63
+ def sort_classes(classes)
64
+ classes.sort_by {|k| k.complexity.to_i}.reverse
65
+ end
66
+
67
+ def assemble_classes(files)
68
+ files.map {|f| f.elements}.flatten
69
+ end
70
+
71
+ def sort_files(files)
72
+ files.sort_by do |file|
73
+ file.elements.
74
+ max {|a,b| a.complexity.to_i <=> b.complexity.to_i}.
75
+ complexity.to_i
76
+ end.reverse
77
+ end
78
+
79
+ def assemble_files
80
+ files = []
81
+ Dir.glob("#{metric_directory}/**/*.html").each do |path|
82
+ if Saikuro::SFile.is_valid_text_file?(path)
83
+ file = Saikuro::SFile.new(path)
84
+ if file
85
+ files << file
86
+ end
87
+ end
88
+ end
89
+ files
73
90
  end
91
+
74
92
  end
75
93
 
76
94
  class Saikuro::SFile
@@ -82,6 +100,8 @@ module MetricFu
82
100
  @file_handle = File.open(@path, "r")
83
101
  @elements = []
84
102
  get_elements
103
+ ensure
104
+ @file_handle.close if @file_handle
85
105
  end
86
106
 
87
107
  def self.is_valid_text_file?(path)