turbulence 0.0.9 → 1.0.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/.gitignore +4 -0
- data/Gemfile +0 -8
- data/Gemfile.lock +26 -23
- data/README.md +15 -4
- data/Rakefile +1 -0
- data/bin/bule +5 -4
- data/lib/turbulence/calculators/churn.rb +29 -14
- data/lib/turbulence/calculators/complexity.rb +3 -4
- data/lib/turbulence/checks_environment.rb +2 -5
- data/lib/turbulence/command_line_interface.rb +40 -11
- data/lib/turbulence/scatter_plot_generator.rb +47 -13
- data/lib/turbulence/scm/git.rb +17 -0
- data/lib/turbulence/scm/perforce.rb +90 -0
- data/lib/turbulence/version.rb +1 -1
- data/lib/turbulence.rb +27 -20
- data/spec/turbulence/calculators/churn_spec.rb +63 -11
- data/spec/turbulence/checks_environoment_spec.rb +11 -0
- data/spec/turbulence/command_line_interface_spec.rb +36 -6
- data/spec/turbulence/scatter_splot_generator_spec.rb +97 -0
- data/spec/turbulence/scm/git_spec.rb +29 -0
- data/spec/turbulence/scm/perforce_spec.rb +148 -0
- data/spec/turbulence/turbulence_spec.rb +15 -0
- data/turbulence.gemspec +4 -2
- data/win_rakefile_location_fix.rb +8 -0
- metadata +41 -35
- data/.DS_Store +0 -0
- data/cc.js +0 -1
- data/highcharts.js +0 -162
- data/jquery.min.js +0 -16
- data/turbulence.html +0 -81
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,42 +1,45 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
turbulence (0.0.
|
4
|
+
turbulence (0.0.9)
|
5
|
+
ParseTree (~> 3.0.7)
|
5
6
|
flog (= 2.5.0)
|
6
|
-
json (
|
7
|
-
launchy (~> 0.
|
7
|
+
json (>= 1.4.6)
|
8
|
+
launchy (~> 2.0.0)
|
8
9
|
|
9
10
|
GEM
|
10
11
|
remote: http://rubygems.org/
|
11
12
|
specs:
|
12
|
-
|
13
|
-
|
13
|
+
ParseTree (3.0.8)
|
14
|
+
RubyInline (>= 3.7.0)
|
15
|
+
sexp_processor (>= 3.0.0)
|
16
|
+
RubyInline (3.11.0)
|
17
|
+
ZenTest (~> 4.3)
|
18
|
+
ZenTest (4.6.2)
|
19
|
+
addressable (2.2.6)
|
20
|
+
diff-lcs (1.1.3)
|
14
21
|
flog (2.5.0)
|
15
22
|
ruby_parser (~> 2.0)
|
16
23
|
sexp_processor (~> 3.0)
|
17
|
-
json (1.
|
18
|
-
launchy (0.
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
rspec-
|
24
|
-
|
25
|
-
|
26
|
-
rspec-core (2.5.1)
|
27
|
-
rspec-expectations (2.5.0)
|
24
|
+
json (1.6.1)
|
25
|
+
launchy (2.0.5)
|
26
|
+
addressable (~> 2.2.6)
|
27
|
+
rspec (2.6.0)
|
28
|
+
rspec-core (~> 2.6.0)
|
29
|
+
rspec-expectations (~> 2.6.0)
|
30
|
+
rspec-mocks (~> 2.6.0)
|
31
|
+
rspec-core (2.6.4)
|
32
|
+
rspec-expectations (2.6.0)
|
28
33
|
diff-lcs (~> 1.1.2)
|
29
|
-
rspec-mocks (2.
|
30
|
-
ruby_parser (2.
|
34
|
+
rspec-mocks (2.6.0)
|
35
|
+
ruby_parser (2.3.1)
|
31
36
|
sexp_processor (~> 3.0)
|
32
|
-
sexp_processor (3.0.
|
37
|
+
sexp_processor (3.0.7)
|
33
38
|
|
34
39
|
PLATFORMS
|
35
40
|
ruby
|
41
|
+
x86-mingw32
|
36
42
|
|
37
43
|
DEPENDENCIES
|
38
|
-
|
39
|
-
json (= 1.4.6)
|
40
|
-
launchy (~> 0.4.0)
|
41
|
-
rspec
|
44
|
+
rspec (~> 2.6.0)
|
42
45
|
turbulence!
|
data/README.md
CHANGED
@@ -1,17 +1,28 @@
|
|
1
1
|
Hopefully-meaningful Metrics
|
2
2
|
============================
|
3
3
|
|
4
|
-
|
5
|
-
http://www.stickyminds.com/sitewide.asp?Function=edetail&ObjectType=COL&ObjectId=16679&tth=DYN&tt=siteemail&iDyn=2
|
4
|
+
Based on Michael Feathers' [recent work](http://www.stickyminds.com/sitewide.asp?Function=edetail&ObjectType=COL&ObjectId=16679&tth=DYN&tt=siteemail&iDyn=2) in project churn and complexity.
|
6
5
|
|
6
|
+
Installation
|
7
|
+
------------
|
8
|
+
|
9
|
+
$ gem install turbulence
|
7
10
|
|
8
11
|
Usage
|
9
12
|
-----
|
13
|
+
In your project directory, run:
|
14
|
+
|
15
|
+
$ bule
|
16
|
+
|
17
|
+
and it will generate (and open) turbulence/turbulence.html
|
10
18
|
|
11
|
-
|
19
|
+
Supported SCM systems
|
20
|
+
---------------------
|
21
|
+
Currently, bule defaults to using git. If you are using Perforce, call it like so:
|
12
22
|
|
13
|
-
|
23
|
+
$ bule --scm p4
|
14
24
|
|
25
|
+
You need to have an environment variable P4CLIENT set to the name of your client workspace.
|
15
26
|
|
16
27
|
WARNING
|
17
28
|
-------
|
data/Rakefile
CHANGED
data/bin/bule
CHANGED
@@ -2,12 +2,13 @@
|
|
2
2
|
require 'turbulence'
|
3
3
|
require 'turbulence/checks_environment'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
cli = Turbulence::CommandLineInterface.new(ARGV)
|
6
|
+
|
7
|
+
unless Turbulence::ChecksEnvironment.scm_repo?(Dir.pwd)
|
8
|
+
STDERR.puts "Turbulence could not calculate metrics, as we could not find a repository in the current directory."
|
9
|
+
STDERR.puts "Please run bule from inside a repository."
|
8
10
|
exit
|
9
11
|
end
|
10
12
|
|
11
|
-
cli = Turbulence::CommandLineInterface.new(Dir.pwd)
|
12
13
|
cli.generate_bundle
|
13
14
|
cli.open_bundle
|
@@ -1,38 +1,53 @@
|
|
1
1
|
class Turbulence
|
2
2
|
module Calculators
|
3
3
|
class Churn
|
4
|
+
RUBY_FILE_EXTENSION = ".rb"
|
5
|
+
|
4
6
|
class << self
|
7
|
+
attr_accessor :scm, :compute_mean, :commit_range
|
8
|
+
|
5
9
|
def for_these_files(files)
|
6
|
-
changes_by_ruby_file.
|
10
|
+
changes_by_ruby_file.each do |filename, count|
|
7
11
|
yield filename, count if files.include?(filename)
|
8
12
|
end
|
9
13
|
end
|
10
14
|
|
11
15
|
def changes_by_ruby_file
|
12
|
-
|
13
|
-
|
16
|
+
ruby_files_changed_in_scm.group_by(&:first).map do |filename, stats|
|
17
|
+
churn_for_file(filename,stats)
|
14
18
|
end
|
15
19
|
end
|
16
20
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
+
def churn_for_file(filename,stats)
|
22
|
+
churn = stats[0..-2].map(&:last).inject(0){|running_total, changes| running_total + changes}
|
23
|
+
churn = calculate_mean_of_churn(churn, stats.size - 1) if compute_mean
|
24
|
+
[filename, churn]
|
25
|
+
end
|
26
|
+
|
27
|
+
def calculate_mean_of_churn(churn, sample_size)
|
28
|
+
return churn if sample_size < 1
|
29
|
+
churn /= sample_size
|
30
|
+
end
|
31
|
+
|
32
|
+
def ruby_files_changed_in_scm
|
33
|
+
counted_line_changes_by_file_by_commit.select do |filename, _|
|
34
|
+
filename.end_with?(RUBY_FILE_EXTENSION) && File.exist?(filename)
|
21
35
|
end
|
22
36
|
end
|
23
37
|
|
24
|
-
def
|
25
|
-
|
26
|
-
|
38
|
+
def counted_line_changes_by_file_by_commit
|
39
|
+
scm_log_file_lines.map do |line|
|
40
|
+
adds, deletes, filename = line.split(/\t/)
|
41
|
+
[filename, adds.to_i + deletes.to_i]
|
27
42
|
end
|
28
43
|
end
|
29
44
|
|
30
|
-
def
|
31
|
-
|
45
|
+
def scm_log_file_lines
|
46
|
+
scm_log_command.each_line.reject{|line| line == "\n"}.map(&:chomp)
|
32
47
|
end
|
33
48
|
|
34
|
-
def
|
35
|
-
|
49
|
+
def scm_log_command
|
50
|
+
scm.log_command(commit_range)
|
36
51
|
end
|
37
52
|
end
|
38
53
|
end
|
@@ -5,7 +5,7 @@ class Turbulence
|
|
5
5
|
class Complexity
|
6
6
|
class << self
|
7
7
|
def flogger
|
8
|
-
@flogger ||= Flog.new
|
8
|
+
@flogger ||= Flog.new(:continue => true)
|
9
9
|
end
|
10
10
|
def for_these_files(files)
|
11
11
|
files.each do |filename|
|
@@ -18,14 +18,13 @@ class Turbulence
|
|
18
18
|
reporter = Reporter.new
|
19
19
|
flogger.report(reporter)
|
20
20
|
reporter.score
|
21
|
-
rescue SyntaxError, Racc::ParseError
|
22
|
-
STDERR.puts "\nError flogging: #{filename}\n"
|
23
21
|
end
|
24
22
|
end
|
25
23
|
|
26
24
|
class Reporter < ::StringIO
|
25
|
+
SCORE_LINE_DETECTOR = /^\s+([^:]+).*flog total$/
|
27
26
|
def score
|
28
|
-
Float(string.scan(
|
27
|
+
Float(string.scan(SCORE_LINE_DETECTOR).flatten.first)
|
29
28
|
end
|
30
29
|
end
|
31
30
|
end
|
@@ -1,11 +1,8 @@
|
|
1
|
-
require 'open3'
|
2
1
|
class Turbulence
|
3
2
|
class ChecksEnvironment
|
4
3
|
class << self
|
5
|
-
def
|
6
|
-
|
7
|
-
return !(err.read =~ /Not a git repository/)
|
8
|
-
end
|
4
|
+
def scm_repo?(directory)
|
5
|
+
Turbulence::Calculators::Churn.scm.is_repo?(directory)
|
9
6
|
end
|
10
7
|
end
|
11
8
|
end
|
@@ -1,23 +1,51 @@
|
|
1
1
|
require 'fileutils'
|
2
2
|
require 'launchy'
|
3
|
+
require 'optparse'
|
4
|
+
require 'turbulence/scm/git'
|
5
|
+
require 'turbulence/scm/perforce'
|
3
6
|
|
4
7
|
class Turbulence
|
5
8
|
class CommandLineInterface
|
6
|
-
|
7
|
-
|
9
|
+
TURBULENCE_TEMPLATE_PATH = File.join(File.expand_path(File.dirname(__FILE__)), "..", "..", "template")
|
10
|
+
TEMPLATE_FILES = ['turbulence.html', 'highcharts.js', 'jquery.min.js'].map { |filename|
|
11
|
+
File.join(TURBULENCE_TEMPLATE_PATH, filename)
|
12
|
+
}
|
13
|
+
|
14
|
+
attr_reader :exclusion_pattern
|
8
15
|
attr_reader :directory
|
9
|
-
def initialize(
|
10
|
-
|
11
|
-
|
16
|
+
def initialize(argv)
|
17
|
+
Turbulence::Calculators::Churn.scm = Scm::Git
|
18
|
+
OptionParser.new do |opts|
|
19
|
+
opts.banner = "Usage: bule [options] [dir]"
|
20
|
+
|
21
|
+
opts.on('--scm p4|git', String, 'scm to use (default: git)') do |s|
|
22
|
+
case s
|
23
|
+
when "git", "", nil
|
24
|
+
when "p4"
|
25
|
+
Turbulence::Calculators::Churn.scm = Scm::Perforce
|
26
|
+
end
|
27
|
+
end
|
28
|
+
opts.on('--churn-range since..until', String, 'commit range to compute file churn') do |s|
|
29
|
+
Turbulence::Calculators::Churn.commit_range = s
|
30
|
+
end
|
31
|
+
opts.on('--churn-mean', 'calculate mean churn instead of cummulative') do
|
32
|
+
Turbulence::Calculators::Churn.compute_mean = true
|
33
|
+
end
|
34
|
+
opts.on('--exclude pattern', String, 'exclude files matching pattern') do |pattern|
|
35
|
+
@exclusion_pattern = pattern
|
36
|
+
end
|
12
37
|
|
13
|
-
|
14
|
-
|
38
|
+
opts.on_tail("-h", "--help", "Show this message") do
|
39
|
+
puts opts
|
40
|
+
exit
|
41
|
+
end
|
42
|
+
end.parse!(argv)
|
43
|
+
|
44
|
+
@directory = argv.first || Dir.pwd
|
15
45
|
end
|
16
46
|
|
17
47
|
def copy_templates_into(directory)
|
18
|
-
|
19
|
-
FileUtils.cp path_to_template(filename), directory
|
20
|
-
end
|
48
|
+
FileUtils.cp TEMPLATE_FILES, directory
|
21
49
|
end
|
22
50
|
private :copy_templates_into
|
23
51
|
|
@@ -26,7 +54,8 @@ class Turbulence
|
|
26
54
|
Dir.chdir("turbulence") do
|
27
55
|
copy_templates_into(Dir.pwd)
|
28
56
|
File.open("cc.js", "w") do |f|
|
29
|
-
|
57
|
+
turb = Turbulence.new(directory,STDOUT, @exclusion_pattern)
|
58
|
+
f.write Turbulence::ScatterPlotGenerator.from(turb.metrics).to_js
|
30
59
|
end
|
31
60
|
end
|
32
61
|
end
|
@@ -1,31 +1,65 @@
|
|
1
1
|
require 'json'
|
2
|
+
|
2
3
|
class Turbulence
|
4
|
+
class FileNameMangler
|
5
|
+
def initialize
|
6
|
+
@current_id = 0
|
7
|
+
@segment_map = { "" => "", "app" => "app", "controllers" => "controllers", "helpers" => "helpers", "lib" => "lib" }
|
8
|
+
end
|
9
|
+
|
10
|
+
def transform(segment)
|
11
|
+
@segment_map[segment] ||= (@current_id += 1)
|
12
|
+
end
|
13
|
+
|
14
|
+
def mangle_name(filename)
|
15
|
+
filename.split('/').map {|seg|transform(seg)}.join('/') + ".rb"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
3
19
|
class ScatterPlotGenerator
|
4
20
|
def self.from(metrics_hash)
|
5
21
|
new(metrics_hash)
|
6
22
|
end
|
7
|
-
attr_reader :metrics_hash
|
8
|
-
def initialize(metrics_hash)
|
23
|
+
attr_reader :metrics_hash, :x_metric, :y_metric
|
24
|
+
def initialize(metrics_hash, x_metric = Turbulence::Calculators::Churn, y_metric = Turbulence::Calculators::Complexity)
|
25
|
+
@x_metric = x_metric
|
26
|
+
@y_metric = y_metric
|
9
27
|
@metrics_hash = metrics_hash
|
10
28
|
end
|
11
29
|
|
30
|
+
def mangle
|
31
|
+
mangler = FileNameMangler.new
|
32
|
+
mangled = {}
|
33
|
+
metrics_hash.each_pair { |filename, metrics| mangled[mangler.mangle_name(filename)] = metrics}
|
34
|
+
@metrics_hash = mangled
|
35
|
+
end
|
36
|
+
|
12
37
|
def to_js
|
13
|
-
|
38
|
+
clean_metrics_from_missing_data
|
39
|
+
directory_series = {}
|
40
|
+
grouped_by_directory.each_pair do |directory, metrics_hash|
|
41
|
+
directory_series[directory] = file_metrics_for_directory(metrics_hash) end
|
42
|
+
|
43
|
+
"var directorySeries = #{directory_series.to_json};"
|
44
|
+
end
|
45
|
+
|
46
|
+
def clean_metrics_from_missing_data
|
47
|
+
metrics_hash.reject! do |filename, metrics|
|
48
|
+
metrics[x_metric].nil? || metrics[y_metric].nil?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def grouped_by_directory
|
53
|
+
metrics_hash.group_by do |filename, _|
|
14
54
|
directories = File.dirname(filename).split("/")
|
15
55
|
directories[0..1].join("/")
|
16
56
|
end
|
57
|
+
end
|
17
58
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
{:filename => filename, :x => metrics[Turbulence::Calculators::Churn], :y => metrics[Turbulence::Calculators::Complexity]}
|
22
|
-
end.reject do |metrics|
|
23
|
-
metrics[:x].nil? || metrics[:y].nil?
|
24
|
-
end
|
25
|
-
directory_series[directory] = data_in_json_format
|
59
|
+
def file_metrics_for_directory(metrics_hash)
|
60
|
+
metrics_hash.map do |filename, metrics|
|
61
|
+
{:filename => filename, :x => metrics[x_metric], :y => metrics[y_metric]}
|
26
62
|
end
|
27
|
-
|
28
|
-
"var directorySeries = #{directory_series.to_json};"
|
29
63
|
end
|
30
64
|
end
|
31
65
|
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Turbulence
|
2
|
+
module Scm
|
3
|
+
class Git
|
4
|
+
class << self
|
5
|
+
def log_command(commit_range = "")
|
6
|
+
`git log --all -M -C --numstat --format="%n" #{commit_range}`
|
7
|
+
end
|
8
|
+
|
9
|
+
def is_repo?(directory)
|
10
|
+
FileUtils.cd(directory) {
|
11
|
+
return !(`git status 2>&1` =~ /Not a git repository/)
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
class Turbulence
|
5
|
+
module Scm
|
6
|
+
class Perforce
|
7
|
+
class << self
|
8
|
+
def log_command(commit_range = "")
|
9
|
+
full_log = ""
|
10
|
+
changes.each do |cn|
|
11
|
+
files_per_change(cn).each do |file|
|
12
|
+
full_log << transform_for_output(file)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
return full_log
|
16
|
+
end
|
17
|
+
|
18
|
+
def is_repo?(directory)
|
19
|
+
p4client = ENV['P4CLIENT']
|
20
|
+
return !((p4client.nil? or p4client.empty?) and not self.has_p4?)
|
21
|
+
end
|
22
|
+
|
23
|
+
def has_p4?
|
24
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).any? do |directory|
|
25
|
+
File.executable?(File.join(directory, 'p4'))
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def changes(commit_range = "")
|
30
|
+
p4_list_changes.each_line.map do |change|
|
31
|
+
change.match(/Change (\d+)/)[1]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def depot_to_local(depot_file)
|
36
|
+
abs_path = extract_clientfile_from_fstat_of(depot_file)
|
37
|
+
Pathname.new(abs_path).relative_path_from(Pathname.new(FileUtils.pwd)).to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
def extract_clientfile_from_fstat_of(depot_file)
|
41
|
+
p4_fstat(depot_file).each_line.select {
|
42
|
+
|line| line =~ /clientFile/
|
43
|
+
}[0].split(" ")[2].tr("\\","/")
|
44
|
+
end
|
45
|
+
|
46
|
+
def files_per_change(change)
|
47
|
+
describe_output = p4_describe_change(change).split("\n")
|
48
|
+
map = []
|
49
|
+
describe_output.each_index do |index|
|
50
|
+
if describe_output[index].start_with?("====")
|
51
|
+
fn = filename_from_describe(describe_output, index)
|
52
|
+
churn = sum_of_changes(describe_output[index .. index + 4].join("\n"))
|
53
|
+
map << [churn,fn]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
return map
|
57
|
+
end
|
58
|
+
|
59
|
+
def filename_from_describe(output,index)
|
60
|
+
depot_to_local(output[index].match(/==== (\/\/.*)#\d+/)[1])
|
61
|
+
end
|
62
|
+
|
63
|
+
def transform_for_output(arr)
|
64
|
+
"#{arr[0]}\t0\t#{arr[1]}\n"
|
65
|
+
end
|
66
|
+
|
67
|
+
def sum_of_changes(p4_describe_output)
|
68
|
+
churn = 0
|
69
|
+
p4_describe_output.each_line do |line|
|
70
|
+
next unless line =~ /(add|deleted|changed) .* (\d+) lines/
|
71
|
+
churn += line.match(/(\d+) lines/)[1].to_i
|
72
|
+
end
|
73
|
+
return churn
|
74
|
+
end
|
75
|
+
|
76
|
+
def p4_list_changes(commit_range = "")
|
77
|
+
`p4 changes -s submitted ...#{commit_range}`
|
78
|
+
end
|
79
|
+
|
80
|
+
def p4_fstat(depot_file)
|
81
|
+
`p4 fstat #{depot_file}`
|
82
|
+
end
|
83
|
+
|
84
|
+
def p4_describe_change(change)
|
85
|
+
`p4 describe -ds #{change}`
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
data/lib/turbulence/version.rb
CHANGED
data/lib/turbulence.rb
CHANGED
@@ -1,43 +1,43 @@
|
|
1
1
|
require 'turbulence/scatter_plot_generator'
|
2
2
|
require 'turbulence/command_line_interface'
|
3
|
+
require 'turbulence/checks_environment'
|
3
4
|
require 'turbulence/calculators/churn'
|
4
5
|
require 'turbulence/calculators/complexity'
|
5
6
|
|
6
7
|
class Turbulence
|
7
|
-
|
8
|
+
CODE_DIRECTORIES = ["app/models", "app/controllers", "app/helpers", "lib"]
|
9
|
+
CALCULATORS = [Turbulence::Calculators::Complexity, Turbulence::Calculators::Churn]
|
10
|
+
|
11
|
+
attr_reader :exclusion_pattern
|
8
12
|
attr_reader :metrics
|
9
|
-
def initialize(
|
10
|
-
@
|
13
|
+
def initialize(directory, output = nil, exclusion_pattern = nil)
|
14
|
+
@output = output
|
11
15
|
@metrics = {}
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
@exclusion_pattern = exclusion_pattern
|
17
|
+
Dir.chdir(directory) do
|
18
|
+
CALCULATORS.each(&method(:calculate_metrics_with))
|
15
19
|
end
|
16
20
|
end
|
17
21
|
|
18
22
|
def files_of_interest
|
19
|
-
|
20
|
-
@ruby_files ||= Dir[*
|
21
|
-
end
|
22
|
-
|
23
|
-
def complexity
|
24
|
-
calculate_metrics Turbulence::Calculators::Complexity
|
25
|
-
end
|
26
|
-
|
27
|
-
def churn
|
28
|
-
calculate_metrics Turbulence::Calculators::Churn
|
23
|
+
file_list = CODE_DIRECTORIES.map{|base_dir| "#{base_dir}/**/*\.rb"}
|
24
|
+
@ruby_files ||= exclude_files(Dir[*file_list])
|
29
25
|
end
|
30
26
|
|
31
|
-
def
|
32
|
-
|
27
|
+
def calculate_metrics_with(calculator)
|
28
|
+
report "calculating metric: #{calculator}\n"
|
33
29
|
calculator.for_these_files(files_of_interest) do |filename, score|
|
30
|
+
report "."
|
34
31
|
set_file_metric(filename, calculator, score)
|
35
32
|
end
|
36
|
-
|
33
|
+
report "\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
def report(this)
|
37
|
+
@output.print this unless @output.nil?
|
37
38
|
end
|
38
39
|
|
39
40
|
def set_file_metric(filename, metric, value)
|
40
|
-
print "."
|
41
41
|
metrics_for(filename)[metric] = value
|
42
42
|
end
|
43
43
|
|
@@ -45,4 +45,11 @@ class Turbulence
|
|
45
45
|
@metrics[filename] ||= {}
|
46
46
|
end
|
47
47
|
|
48
|
+
private
|
49
|
+
def exclude_files(files)
|
50
|
+
if not @exclusion_pattern.nil?
|
51
|
+
files = files.reject { |f| f =~ Regexp.new(@exclusion_pattern) }
|
52
|
+
end
|
53
|
+
files
|
54
|
+
end
|
48
55
|
end
|