metric_fu 1.3.0 → 1.4.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.
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)