turbulence 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ ZWM3NDQzMzE3NGIwODMyNGE0YmJmZjg1N2E1OThhNzY0M2M3NzNhNQ==
5
+ data.tar.gz: !binary |-
6
+ MTM3OTcwYmY3MjhmNTljZmI4M2RhMTA0MjVhOTliYjdiNTNmNDM5OA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ MzdhYzRjNGNmMGQwODEwZTk2ZWNiMzRjMmZjMzUyNzgzY2IzNWY5YWZlODFm
10
+ MmJlYjNjODAwMDE4YmYyNzlkZDM2NTZkNzY2OWU5NjJmNDVmYzE3OWJlN2I4
11
+ YmIyOWZhMGMwOTFhOWJjZWIxY2MwYWU4ODlkODMxNjZiM2I4ZDk=
12
+ data.tar.gz: !binary |-
13
+ ODk3MmQ1MmUwZmIyOTRkMTRhMjkzYjNjM2M0ZmY0NmQ5ZTMxMDg0OTBiZTIw
14
+ NjQ1NDVjNjk5OTgxMWQyMTNlMzZhMmU3NzdlMmFjNTJjYTJlOGEyYjhhZjNl
15
+ MTQ0Mzc0ZDk1ZGFiZjBjNzZlNTAxZDk0YWU3MDAwODE1YTliMDA=
@@ -0,0 +1,6 @@
1
+ rvm:
2
+ - "1.9.3"
3
+ - "1.9.2"
4
+ - "1.8.7"
5
+ - jruby-18mode
6
+ - jruby-19mode
@@ -1,22 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- turbulence (1.0.0)
5
- flog (= 3.2.1)
4
+ turbulence (1.2.0)
5
+ flog (~> 4.1)
6
6
  json (>= 1.4.6)
7
- launchy (~> 2.0.0)
7
+ launchy (>= 2.0.0)
8
8
 
9
9
  GEM
10
10
  remote: http://rubygems.org/
11
11
  specs:
12
- addressable (2.2.8)
12
+ addressable (2.3.5)
13
13
  diff-lcs (1.1.3)
14
- flog (3.2.1)
14
+ flog (4.1.2)
15
15
  ruby_parser (~> 3.1, > 3.1.0)
16
16
  sexp_processor (~> 4.0)
17
- json (1.7.6)
18
- launchy (2.0.5)
19
- addressable (~> 2.2.6)
17
+ json (1.8.0)
18
+ launchy (2.3.0)
19
+ addressable (~> 2.3)
20
+ rake (10.1.0)
20
21
  rspec (2.6.0)
21
22
  rspec-core (~> 2.6.0)
22
23
  rspec-expectations (~> 2.6.0)
@@ -25,14 +26,14 @@ GEM
25
26
  rspec-expectations (2.6.0)
26
27
  diff-lcs (~> 1.1.2)
27
28
  rspec-mocks (2.6.0)
28
- ruby_parser (3.1.1)
29
+ ruby_parser (3.2.2)
29
30
  sexp_processor (~> 4.1)
30
- sexp_processor (4.1.3)
31
+ sexp_processor (4.3.0)
31
32
 
32
33
  PLATFORMS
33
34
  ruby
34
- x86-mingw32
35
35
 
36
36
  DEPENDENCIES
37
+ rake
37
38
  rspec (~> 2.6.0)
38
39
  turbulence!
data/README.md CHANGED
@@ -3,6 +3,25 @@ Hopefully-meaningful Metrics
3
3
 
4
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.
5
5
 
6
+ Here is how to read the graph (extracted from the above article):
7
+
8
+ * The upper right quadrant is particularly important.
9
+ These files have a high degree of complexity, and they change quite frequently.
10
+ There are a number of reasons why this can happen.
11
+ The one to look out for, though, is something I call runaway conditionals.
12
+ Sometimes a class becomes so complex that refactoring seems too difficult.
13
+ Developers hack if-then-elses into if-then-elses, and the rat’s nest grows. These classes are particularly ripe for a refactoring investment.
14
+
15
+ * The lower left quadrant. is the healthy closure region.
16
+ Abstractions here have low complexity and don't change much.
17
+
18
+ * The upper left is what I call the cowboy region. This is complex code that sprang from someone's head and didn't seem to grow incrementally.
19
+
20
+ * The bottom right is very interesting. I call it the fertile ground.
21
+ It can consist of files that are somewhat configurational, but often there are also files that act as incubators for new abstractions.
22
+ People add code, it grows, and then they factor outward, extracting new classes. The files churn frequently, but their complexity remains low.
23
+
24
+
6
25
  Installation
7
26
  ------------
8
27
 
data/Rakefile CHANGED
@@ -1,8 +1,11 @@
1
1
  require 'bundler'
2
2
  require File.join(File.dirname(__FILE__), 'win_rakefile_location_fix')
3
+
3
4
  Bundler::GemHelper.install_tasks
5
+
4
6
  require 'rake'
5
7
  require 'rspec/core/rake_task'
8
+
6
9
  RSpec::Core::RakeTask.new(:spec) do |t|
7
10
  t.skip_bundler = true
8
11
  end
@@ -1,19 +1,30 @@
1
- require 'turbulence/scatter_plot_generator'
1
+ require 'turbulence/file_name_mangler'
2
2
  require 'turbulence/command_line_interface'
3
3
  require 'turbulence/checks_environment'
4
4
  require 'turbulence/calculators/churn'
5
5
  require 'turbulence/calculators/complexity'
6
+ require 'turbulence/generators/treemap'
7
+ require 'turbulence/generators/scatterplot'
6
8
 
7
9
  class Turbulence
8
- CODE_DIRECTORIES = ["app/models", "app/controllers", "app/helpers", "app/jobs", "app/mailers", "app/validators", "lib"]
9
- CALCULATORS = [Turbulence::Calculators::Complexity, Turbulence::Calculators::Churn]
10
+ CODE_DIRECTORIES = ["app/models",
11
+ "app/controllers",
12
+ "app/helpers",
13
+ "app/jobs",
14
+ "app/mailers",
15
+ "app/validators",
16
+ "lib"]
17
+ CALCULATORS = [Turbulence::Calculators::Complexity,
18
+ Turbulence::Calculators::Churn]
10
19
 
11
20
  attr_reader :exclusion_pattern
12
21
  attr_reader :metrics
22
+
13
23
  def initialize(directory, output = nil, exclusion_pattern = nil)
14
- @output = output
15
- @metrics = {}
24
+ @output = output
25
+ @metrics = {}
16
26
  @exclusion_pattern = exclusion_pattern
27
+
17
28
  Dir.chdir(directory) do
18
29
  CALCULATORS.each(&method(:calculate_metrics_with))
19
30
  end
@@ -26,10 +37,12 @@ class Turbulence
26
37
 
27
38
  def calculate_metrics_with(calculator)
28
39
  report "calculating metric: #{calculator}\n"
40
+
29
41
  calculator.for_these_files(files_of_interest) do |filename, score|
30
42
  report "."
31
43
  set_file_metric(filename, calculator, score)
32
44
  end
45
+
33
46
  report "\n"
34
47
  end
35
48
 
@@ -29,17 +29,9 @@ class Turbulence
29
29
  end
30
30
 
31
31
  def score_for_file(filename)
32
+ flogger.reset
32
33
  flogger.flog filename
33
- reporter = Reporter.new
34
- flogger.report(reporter)
35
- reporter.score
36
- end
37
- end
38
-
39
- class Reporter < ::StringIO
40
- SCORE_LINE_DETECTOR = /^\s+([^:]+).*flog total$/
41
- def score
42
- Float(string.scan(SCORE_LINE_DETECTOR).flatten.first)
34
+ flogger.total_score
43
35
  end
44
36
  end
45
37
  end
@@ -7,10 +7,13 @@ require 'turbulence/scm/perforce'
7
7
  class Turbulence
8
8
  class CommandLineInterface
9
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|
10
+ TEMPLATE_FILES = ['turbulence.html',
11
+ 'highcharts.js',
12
+ 'jquery.min.js',
13
+ 'treemap.html'].map do |filename|
11
14
  File.join(TURBULENCE_TEMPLATE_PATH, filename)
12
- }
13
-
15
+ end
16
+
14
17
  attr_reader :exclusion_pattern
15
18
  attr_reader :directory
16
19
  def initialize(argv)
@@ -25,16 +28,24 @@ class Turbulence
25
28
  Turbulence::Calculators::Churn.scm = Scm::Perforce
26
29
  end
27
30
  end
31
+
28
32
  opts.on('--churn-range since..until', String, 'commit range to compute file churn') do |s|
29
33
  Turbulence::Calculators::Churn.commit_range = s
30
34
  end
35
+
31
36
  opts.on('--churn-mean', 'calculate mean churn instead of cummulative') do
32
37
  Turbulence::Calculators::Churn.compute_mean = true
33
38
  end
39
+
34
40
  opts.on('--exclude pattern', String, 'exclude files matching pattern') do |pattern|
35
41
  @exclusion_pattern = pattern
36
42
  end
37
43
 
44
+ opts.on('--treemap', String, 'output treemap graph instead of scatterplot') do |s|
45
+ @graph_type = "treemap"
46
+ end
47
+
48
+
38
49
  opts.on_tail("-h", "--help", "Show this message") do
39
50
  puts opts
40
51
  exit
@@ -47,21 +58,31 @@ class Turbulence
47
58
  def copy_templates_into(directory)
48
59
  FileUtils.cp TEMPLATE_FILES, directory
49
60
  end
50
- private :copy_templates_into
51
61
 
52
62
  def generate_bundle
53
63
  FileUtils.mkdir_p("turbulence")
64
+
54
65
  Dir.chdir("turbulence") do
55
- copy_templates_into(Dir.pwd)
56
- File.open("cc.js", "w") do |f|
57
- turb = Turbulence.new(directory,STDOUT, @exclusion_pattern)
58
- f.write Turbulence::ScatterPlotGenerator.from(turb.metrics).to_js
66
+ turb = Turbulence.new(directory,STDOUT, @exclusion_pattern)
67
+
68
+ generator = case @graph_type
69
+ when "treemap"
70
+ Turbulence::Generators::TreeMap.new({})
71
+ else
72
+ Turbulence::Generators::ScatterPlot.new({})
59
73
  end
74
+
75
+ generator.generate_results(turb.metrics, self)
60
76
  end
61
77
  end
62
78
 
63
79
  def open_bundle
64
- Launchy.open("file://#{directory}/turbulence/turbulence.html")
80
+ case @graph_type
81
+ when "treemap"
82
+ Launchy.open("file://#{directory}/turbulence/treemap.html")
83
+ else
84
+ Launchy.open("file://#{directory}/turbulence/turbulence.html")
85
+ end
65
86
  end
66
87
  end
67
88
  end
@@ -0,0 +1,18 @@
1
+ require 'json'
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
+ end
@@ -0,0 +1,65 @@
1
+ class Turbulence
2
+ module Generators
3
+ class ScatterPlot
4
+ attr_reader :metrics_hash, :x_metric, :y_metric
5
+
6
+ def initialize(metrics_hash,
7
+ x_metric = Turbulence::Calculators::Churn,
8
+ y_metric = Turbulence::Calculators::Complexity)
9
+ @x_metric = x_metric
10
+ @y_metric = y_metric
11
+ @metrics_hash = metrics_hash
12
+ end
13
+
14
+ def self.from(metrics_hash)
15
+ new(metrics_hash)
16
+ end
17
+
18
+ def mangle
19
+ mangler = FileNameMangler.new
20
+ mangled = {}
21
+ metrics_hash.each_pair { |filename, metrics| mangled[mangler.mangle_name(filename)] = metrics}
22
+ @metrics_hash = mangled
23
+ end
24
+
25
+ def to_js
26
+ clean_metrics_from_missing_data
27
+ directory_series = {}
28
+
29
+ grouped_by_directory.each_pair do |directory, metrics_hash|
30
+ directory_series[directory] = file_metrics_for_directory(metrics_hash)
31
+ end
32
+
33
+ "var directorySeries = #{directory_series.to_json};"
34
+ end
35
+
36
+ def clean_metrics_from_missing_data
37
+ metrics_hash.reject! do |filename, metrics|
38
+ metrics[x_metric].nil? || metrics[y_metric].nil?
39
+ end
40
+ end
41
+
42
+ def grouped_by_directory
43
+ metrics_hash.group_by do |filename, _|
44
+ directories = File.dirname(filename).split("/")
45
+ directories[0..1].join("/")
46
+ end
47
+ end
48
+
49
+ def file_metrics_for_directory(metrics_hash)
50
+ metrics_hash.map do |filename, metrics|
51
+ { :filename => filename,
52
+ :x => metrics[x_metric],
53
+ :y => metrics[y_metric]}
54
+ end
55
+ end
56
+
57
+ def generate_results(metrics, ci)
58
+ File.open("cc.js", "w") do |f|
59
+ ci.copy_templates_into(Dir.pwd)
60
+ f.write Turbulence::Generators::ScatterPlot.from(metrics).to_js
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,48 @@
1
+ class Turbulence
2
+ module Generators
3
+ class TreeMap
4
+ attr_reader :metrics_hash, :x_metric, :y_metric
5
+
6
+ def initialize(metrics_hash,
7
+ x_metric = Turbulence::Calculators::Churn,
8
+ y_metric = Turbulence::Calculators::Complexity)
9
+ @x_metric = x_metric
10
+ @y_metric = y_metric
11
+ @metrics_hash = metrics_hash
12
+ end
13
+
14
+ def generate_results(metrics, cli)
15
+ File.open("treemap_data.js", "w") do |f|
16
+ cli.copy_templates_into(Dir.pwd)
17
+ f.write Turbulence::Generators::TreeMap.from(metrics).build_js
18
+ end
19
+ end
20
+
21
+ def build_js
22
+ clean_metrics_from_missing_data
23
+
24
+ output = "var treemap_data = [['File', 'Parent', 'Churn (size)', 'Complexity (color)'],\n"
25
+ output << "['Root', null, 0, 0],\n"
26
+
27
+ @metrics_hash.each do |file|
28
+ output << "['#{file[0]}', 'Root', #{file[1][@x_metric]}, #{file[1][@y_metric]}],\n"
29
+ end
30
+
31
+ output << "];"
32
+
33
+ output
34
+ end
35
+
36
+ private
37
+ def clean_metrics_from_missing_data
38
+ @metrics_hash.reject! do |filename, metrics|
39
+ metrics[@x_metric].nil? || metrics[@y_metric].nil?
40
+ end
41
+ end
42
+
43
+ def self.from(metrics_hash)
44
+ new(metrics_hash)
45
+ end
46
+ end
47
+ end
48
+ end
@@ -1,3 +1,3 @@
1
1
  class Turbulence
2
- VERSION = "1.1.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -18,20 +18,3 @@ describe Turbulence::Calculators::Complexity do
18
18
  end
19
19
  end
20
20
 
21
- describe Turbulence::Calculators::Complexity::Reporter do
22
- subject { Turbulence::Calculators::Complexity::Reporter.new }
23
- it "uses the total value from flog" do
24
- flog_output = <<-FLOG_OUTPUT
25
- 38.7: flog total
26
- 5.5: flog/method average
27
-
28
- 9.3: Turbulence#initialize lib/turbulence.rb:9
29
- 8.7: Turbulence#churn lib/turbulence.rb:41
30
- 6.1: Turbulence#complexity lib/turbulence.rb:26
31
- FLOG_OUTPUT
32
-
33
- subject.stub(:string) { flog_output }
34
-
35
- subject.score.should == 38.7
36
- end
37
- end
@@ -14,7 +14,11 @@ describe Turbulence::CommandLineInterface do
14
14
  end
15
15
  it "bundles the files" do
16
16
  cli.generate_bundle
17
- Dir.glob('turbulence/*').sort.should eq(["turbulence/cc.js", "turbulence/highcharts.js", "turbulence/jquery.min.js", "turbulence/turbulence.html"])
17
+ Dir.glob('turbulence/*').sort.should eq(["turbulence/cc.js",
18
+ "turbulence/highcharts.js",
19
+ "turbulence/jquery.min.js",
20
+ "turbulence/treemap.html",
21
+ "turbulence/turbulence.html"])
18
22
  end
19
23
 
20
24
  it "passes along exclusion pattern" do
@@ -33,7 +37,7 @@ describe Turbulence::CommandLineInterface do
33
37
  cli_churn_range.directory.should == 'path/to/compute'
34
38
  Turbulence::Calculators::Churn.commit_range.should == 'f3e1d7a6..830b9d3d9f'
35
39
  end
36
-
40
+
37
41
  it "sets churn mean" do
38
42
  cli_churn_mean.directory.should == '.'
39
43
  Turbulence::Calculators::Churn.compute_mean.should be_true
@@ -1,49 +1,47 @@
1
1
  require 'turbulence'
2
2
 
3
- describe Turbulence::ScatterPlotGenerator do
3
+ describe Turbulence::Generators::ScatterPlot do
4
4
  context "with both Metrics" do
5
5
  it "generates JavaScript" do
6
- generator = Turbulence::ScatterPlotGenerator.new(
7
- "foo.rb" => {
8
- Turbulence::Calculators::Churn => 1,
9
- Turbulence::Calculators::Complexity => 2
10
- }
6
+ generator = Turbulence::Generators::ScatterPlot.new(
7
+ "foo.rb" => { Turbulence::Calculators::Churn => 1,
8
+ Turbulence::Calculators::Complexity => 2 }
11
9
  )
12
- generator.to_js.should =~ /var directorySeries/
13
- generator.to_js.should =~ /\"filename\"\:\"foo.rb\"/
14
- generator.to_js.should =~ /\"x\":1/
15
- generator.to_js.should =~ /\"y\":2/
16
- end
10
+
11
+ generator.to_js.should =~ /var directorySeries/
12
+ generator.to_js.should =~ /\"filename\"\:\"foo.rb\"/
13
+ generator.to_js.should =~ /\"x\":1/
14
+ generator.to_js.should =~ /\"y\":2/
15
+ end
17
16
  end
18
17
 
19
18
  context "with a missing Metric" do
20
19
  it "generates JavaScript" do
21
- generator = Turbulence::ScatterPlotGenerator.new(
22
- "foo.rb" => {
23
- Turbulence::Calculators::Churn => 1
24
- }
20
+ generator = Turbulence::Generators::ScatterPlot.new(
21
+ "foo.rb" => { Turbulence::Calculators::Churn => 1 }
25
22
  )
23
+
26
24
  generator.to_js.should == 'var directorySeries = {};'
27
- end
25
+ end
28
26
  end
29
27
 
30
- describe "#clean_metrics_from_missing_data" do
31
- let(:spg) {Turbulence::ScatterPlotGenerator.new({})}
28
+ describe "#clean_metrics_from_missing_data" do
29
+ let(:spg) {Turbulence::Generators::ScatterPlot.new({})}
32
30
 
33
- it "removes entries with missing churn" do
34
- spg.stub(:metrics_hash).and_return("foo.rb" => {
31
+ it "removes entries with missing churn" do
32
+ spg.stub(:metrics_hash).and_return("foo.rb" => {
35
33
  Turbulence::Calculators::Complexity => 88.3})
36
34
  spg.clean_metrics_from_missing_data.should == {}
37
35
  end
38
36
 
39
37
  it "removes entries with missing complexity" do
40
- spg.stub(:metrics_hash).and_return("foo.rb" => {
38
+ spg.stub(:metrics_hash).and_return("foo.rb" => {
41
39
  Turbulence::Calculators::Churn => 1})
42
40
  spg.clean_metrics_from_missing_data.should == {}
43
41
  end
44
42
 
45
43
  it "keeps entries with churn and complexity present" do
46
- spg.stub(:metrics_hash).and_return("foo.rb" => {
44
+ spg.stub(:metrics_hash).and_return("foo.rb" => {
47
45
  Turbulence::Calculators::Churn => 1,
48
46
  Turbulence::Calculators::Complexity => 88.3})
49
47
  spg.clean_metrics_from_missing_data.should_not == {}
@@ -51,7 +49,7 @@ describe Turbulence::ScatterPlotGenerator do
51
49
  end
52
50
 
53
51
  describe "#grouped_by_directory" do
54
- let(:spg) {Turbulence::ScatterPlotGenerator.new("lib/foo/foo.rb" => {
52
+ let(:spg) {Turbulence::Generators::ScatterPlot.new("lib/foo/foo.rb" => {
55
53
  Turbulence::Calculators::Churn => 1},
56
54
  "lib/bar.rb" => {
57
55
  Turbulence::Calculators::Churn => 2} )}
@@ -60,17 +58,17 @@ describe Turbulence::ScatterPlotGenerator do
60
58
  spg.stub(:metrics_hash).and_return("foo.rb" => {
61
59
  Turbulence::Calculators::Churn => 1
62
60
  })
63
- spg.grouped_by_directory.should == {"." => [["foo.rb", Turbulence::Calculators::Churn => 1]]}
61
+ spg.grouped_by_directory.should == {"." => [["foo.rb", {Turbulence::Calculators::Churn => 1}]]}
64
62
  end
65
63
 
66
64
  it "takes full path into account" do
67
- spg.grouped_by_directory.should == {"lib/foo" => [["lib/foo/foo.rb", Turbulence::Calculators::Churn => 1]],
68
- "lib" => [["lib/bar.rb", Turbulence::Calculators::Churn => 2]]}
65
+ spg.grouped_by_directory.should == {"lib/foo" => [["lib/foo/foo.rb", {Turbulence::Calculators::Churn => 1}]],
66
+ "lib" => [["lib/bar.rb", {Turbulence::Calculators::Churn => 2}]]}
69
67
  end
70
68
  end
71
69
 
72
70
  describe "#file_metrics_for_directory" do
73
- let(:spg) {Turbulence::ScatterPlotGenerator.new({})}
71
+ let(:spg) {Turbulence::Generators::ScatterPlot.new({})}
74
72
  it "assigns :filename, :x, :y" do
75
73
  spg.file_metrics_for_directory("lib/foo/foo.rb" => {
76
74
  Turbulence::Calculators::Churn => 1,
@@ -0,0 +1,26 @@
1
+ require 'turbulence'
2
+
3
+ describe Turbulence::Generators::TreeMap do
4
+ context "with both Metrics" do
5
+ it "generates JavaScript" do
6
+ generator = Turbulence::Generators::TreeMap.new(
7
+ "foo.rb" => { Turbulence::Calculators::Churn => 1,
8
+ Turbulence::Calculators::Complexity => 2 }
9
+ )
10
+
11
+ generator.build_js.should =~ /var treemap_data/
12
+ generator.build_js.should =~ /\'foo.rb\'/
13
+ end
14
+ end
15
+
16
+ context "with a missing Metric" do
17
+ it "generates JavaScript" do
18
+ generator = Turbulence::Generators::TreeMap.new(
19
+ "foo.rb" => { Turbulence::Calculators::Churn => 1 }
20
+ )
21
+
22
+ generator.build_js.should == "var treemap_data = [['File', 'Parent', 'Churn (size)', 'Complexity (color)'],\n['Root', null, 0, 0],\n];"
23
+ end
24
+ end
25
+ end
26
+
@@ -0,0 +1,29 @@
1
+ <html>
2
+ <head>
3
+ <script src="treemap_data.js"></script>
4
+ <script type="text/javascript" src="https://www.google.com/jsapi"></script>
5
+ <script type="text/javascript">
6
+ google.load("visualization", "1", {packages:["treemap"]});
7
+ google.setOnLoadCallback(drawChart);
8
+ function drawChart() {
9
+ // Create and populate the data table.
10
+ var data = google.visualization.arrayToDataTable(treemap_data);
11
+
12
+ // Create and draw the visualization.
13
+ var tree = new google.visualization.TreeMap(document.getElementById('chart_div'));
14
+ tree.draw(data, {
15
+ minColor: '#0d0',
16
+ midColor: '#ddd',
17
+ maxColor: '#f00',
18
+ headerHeight: 15,
19
+ fontColor: 'black',
20
+ showScale: true,
21
+ showTooltips: true});
22
+ }
23
+ </script>
24
+ </head>
25
+
26
+ <body>
27
+ <div id="chart_div" style="width: 900px; height: 500px;"></div>
28
+ </body>
29
+ </html>
@@ -9,10 +9,11 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["Chad Fowler", "Michael Feathers", "Corey Haines"]
10
10
  s.email = ["chad@chadfowler.com", "mfeathers@obtiva.com", "coreyhaines@gmail.com"]
11
11
  s.homepage = "http://chadfowler.com"
12
- s.add_dependency "flog", "= 3.2.1"
12
+ s.add_dependency "flog", "~>4.1"
13
13
  s.add_dependency "json", ">= 1.4.6"
14
- s.add_dependency "launchy", "~> 2.0.0"
14
+ s.add_dependency "launchy", ">= 2.0.0"
15
15
  s.add_development_dependency 'rspec', '~> 2.6.0'
16
+ s.add_development_dependency 'rake'
16
17
 
17
18
  s.summary = %q{Automates churn + flog scoring on a git repo for a Ruby project}
18
19
  s.description = %q{Based on this http://www.stickyminds.com/sitewide.asp?Function=edetail&ObjectType=COL&ObjectId=16679&tth=DYN&tt=siteemail&iDyn=2}
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: turbulence
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
5
- prerelease:
4
+ version: 1.2.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Chad Fowler
@@ -11,28 +10,25 @@ authors:
11
10
  autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2013-01-10 00:00:00.000000000 Z
13
+ date: 2013-09-07 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: flog
18
17
  requirement: !ruby/object:Gem::Requirement
19
- none: false
20
18
  requirements:
21
- - - '='
19
+ - - ~>
22
20
  - !ruby/object:Gem::Version
23
- version: 3.2.1
21
+ version: '4.1'
24
22
  type: :runtime
25
23
  prerelease: false
26
24
  version_requirements: !ruby/object:Gem::Requirement
27
- none: false
28
25
  requirements:
29
- - - '='
26
+ - - ~>
30
27
  - !ruby/object:Gem::Version
31
- version: 3.2.1
28
+ version: '4.1'
32
29
  - !ruby/object:Gem::Dependency
33
30
  name: json
34
31
  requirement: !ruby/object:Gem::Requirement
35
- none: false
36
32
  requirements:
37
33
  - - ! '>='
38
34
  - !ruby/object:Gem::Version
@@ -40,7 +36,6 @@ dependencies:
40
36
  type: :runtime
41
37
  prerelease: false
42
38
  version_requirements: !ruby/object:Gem::Requirement
43
- none: false
44
39
  requirements:
45
40
  - - ! '>='
46
41
  - !ruby/object:Gem::Version
@@ -48,23 +43,20 @@ dependencies:
48
43
  - !ruby/object:Gem::Dependency
49
44
  name: launchy
50
45
  requirement: !ruby/object:Gem::Requirement
51
- none: false
52
46
  requirements:
53
- - - ~>
47
+ - - ! '>='
54
48
  - !ruby/object:Gem::Version
55
49
  version: 2.0.0
56
50
  type: :runtime
57
51
  prerelease: false
58
52
  version_requirements: !ruby/object:Gem::Requirement
59
- none: false
60
53
  requirements:
61
- - - ~>
54
+ - - ! '>='
62
55
  - !ruby/object:Gem::Version
63
56
  version: 2.0.0
64
57
  - !ruby/object:Gem::Dependency
65
58
  name: rspec
66
59
  requirement: !ruby/object:Gem::Requirement
67
- none: false
68
60
  requirements:
69
61
  - - ~>
70
62
  - !ruby/object:Gem::Version
@@ -72,80 +64,148 @@ dependencies:
72
64
  type: :development
73
65
  prerelease: false
74
66
  version_requirements: !ruby/object:Gem::Requirement
75
- none: false
76
67
  requirements:
77
68
  - - ~>
78
69
  - !ruby/object:Gem::Version
79
70
  version: 2.6.0
71
+ - !ruby/object:Gem::Dependency
72
+ name: rake
73
+ requirement: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ! '>='
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
80
85
  description: Based on this http://www.stickyminds.com/sitewide.asp?Function=edetail&ObjectType=COL&ObjectId=16679&tth=DYN&tt=siteemail&iDyn=2
81
86
  email:
82
87
  - chad@chadfowler.com
83
88
  - mfeathers@obtiva.com
84
89
  - coreyhaines@gmail.com
85
90
  executables:
86
- - bule
91
+ - !binary |-
92
+ YnVsZQ==
87
93
  extensions: []
88
94
  extra_rdoc_files: []
89
95
  files:
90
- - .gitignore
91
- - Gemfile
92
- - Gemfile.lock
93
- - README.md
94
- - Rakefile
95
- - bin/bule
96
- - lib/turbulence.rb
97
- - lib/turbulence/calculators/churn.rb
98
- - lib/turbulence/calculators/complexity.rb
99
- - lib/turbulence/checks_environment.rb
100
- - lib/turbulence/command_line_interface.rb
101
- - lib/turbulence/scatter_plot_generator.rb
102
- - lib/turbulence/scm/git.rb
103
- - lib/turbulence/scm/perforce.rb
104
- - lib/turbulence/version.rb
105
- - spec/turbulence/calculators/churn_spec.rb
106
- - spec/turbulence/calculators/complexity_spec.rb
107
- - spec/turbulence/checks_environoment_spec.rb
108
- - spec/turbulence/command_line_interface_spec.rb
109
- - spec/turbulence/scatter_splot_generator_spec.rb
110
- - spec/turbulence/scm/git_spec.rb
111
- - spec/turbulence/scm/perforce_spec.rb
112
- - spec/turbulence/turbulence_spec.rb
113
- - template/highchart_template.js.erb
114
- - template/highcharts.js
115
- - template/jquery.min.js
116
- - template/turbulence.html
117
- - turbulence.gemspec
118
- - win_rakefile_location_fix.rb
96
+ - !binary |-
97
+ LmdpdGlnbm9yZQ==
98
+ - !binary |-
99
+ LnRyYXZpcy55bWw=
100
+ - !binary |-
101
+ R2VtZmlsZQ==
102
+ - !binary |-
103
+ R2VtZmlsZS5sb2Nr
104
+ - !binary |-
105
+ UkVBRE1FLm1k
106
+ - !binary |-
107
+ UmFrZWZpbGU=
108
+ - !binary |-
109
+ YmluL2J1bGU=
110
+ - !binary |-
111
+ bGliL3R1cmJ1bGVuY2UucmI=
112
+ - !binary |-
113
+ bGliL3R1cmJ1bGVuY2UvY2FsY3VsYXRvcnMvY2h1cm4ucmI=
114
+ - !binary |-
115
+ bGliL3R1cmJ1bGVuY2UvY2FsY3VsYXRvcnMvY29tcGxleGl0eS5yYg==
116
+ - !binary |-
117
+ bGliL3R1cmJ1bGVuY2UvY2hlY2tzX2Vudmlyb25tZW50LnJi
118
+ - !binary |-
119
+ bGliL3R1cmJ1bGVuY2UvY29tbWFuZF9saW5lX2ludGVyZmFjZS5yYg==
120
+ - !binary |-
121
+ bGliL3R1cmJ1bGVuY2UvZmlsZV9uYW1lX21hbmdsZXIucmI=
122
+ - !binary |-
123
+ bGliL3R1cmJ1bGVuY2UvZ2VuZXJhdG9ycy9zY2F0dGVycGxvdC5yYg==
124
+ - !binary |-
125
+ bGliL3R1cmJ1bGVuY2UvZ2VuZXJhdG9ycy90cmVlbWFwLnJi
126
+ - !binary |-
127
+ bGliL3R1cmJ1bGVuY2Uvc2NtL2dpdC5yYg==
128
+ - !binary |-
129
+ bGliL3R1cmJ1bGVuY2Uvc2NtL3BlcmZvcmNlLnJi
130
+ - !binary |-
131
+ bGliL3R1cmJ1bGVuY2UvdmVyc2lvbi5yYg==
132
+ - !binary |-
133
+ c3BlYy90dXJidWxlbmNlL2NhbGN1bGF0b3JzL2NodXJuX3NwZWMucmI=
134
+ - !binary |-
135
+ c3BlYy90dXJidWxlbmNlL2NhbGN1bGF0b3JzL2NvbXBsZXhpdHlfc3BlYy5y
136
+ Yg==
137
+ - !binary |-
138
+ c3BlYy90dXJidWxlbmNlL2NoZWNrc19lbnZpcm9ub21lbnRfc3BlYy5yYg==
139
+ - !binary |-
140
+ c3BlYy90dXJidWxlbmNlL2NvbW1hbmRfbGluZV9pbnRlcmZhY2Vfc3BlYy5y
141
+ Yg==
142
+ - !binary |-
143
+ c3BlYy90dXJidWxlbmNlL2dlbmVyYXRvcnMvc2NhdHRlcl9wbG90X3NwZWMu
144
+ cmI=
145
+ - !binary |-
146
+ c3BlYy90dXJidWxlbmNlL2dlbmVyYXRvcnMvdHJlZW1hcF9zcGVjLnJi
147
+ - !binary |-
148
+ c3BlYy90dXJidWxlbmNlL3NjbS9naXRfc3BlYy5yYg==
149
+ - !binary |-
150
+ c3BlYy90dXJidWxlbmNlL3NjbS9wZXJmb3JjZV9zcGVjLnJi
151
+ - !binary |-
152
+ c3BlYy90dXJidWxlbmNlL3R1cmJ1bGVuY2Vfc3BlYy5yYg==
153
+ - !binary |-
154
+ dGVtcGxhdGUvaGlnaGNoYXJ0X3RlbXBsYXRlLmpzLmVyYg==
155
+ - !binary |-
156
+ dGVtcGxhdGUvaGlnaGNoYXJ0cy5qcw==
157
+ - !binary |-
158
+ dGVtcGxhdGUvanF1ZXJ5Lm1pbi5qcw==
159
+ - !binary |-
160
+ dGVtcGxhdGUvdHJlZW1hcC5odG1s
161
+ - !binary |-
162
+ dGVtcGxhdGUvdHVyYnVsZW5jZS5odG1s
163
+ - !binary |-
164
+ dHVyYnVsZW5jZS5nZW1zcGVj
165
+ - !binary |-
166
+ d2luX3Jha2VmaWxlX2xvY2F0aW9uX2ZpeC5yYg==
119
167
  homepage: http://chadfowler.com
120
168
  licenses: []
169
+ metadata: {}
121
170
  post_install_message:
122
171
  rdoc_options: []
123
172
  require_paths:
124
173
  - lib
125
174
  required_ruby_version: !ruby/object:Gem::Requirement
126
- none: false
127
175
  requirements:
128
176
  - - ! '>='
129
177
  - !ruby/object:Gem::Version
130
178
  version: 1.8.7
131
179
  required_rubygems_version: !ruby/object:Gem::Requirement
132
- none: false
133
180
  requirements:
134
181
  - - ! '>='
135
182
  - !ruby/object:Gem::Version
136
183
  version: '0'
137
184
  requirements: []
138
185
  rubyforge_project: turbulence
139
- rubygems_version: 1.8.24
186
+ rubygems_version: 2.0.7
140
187
  signing_key:
141
- specification_version: 3
188
+ specification_version: 4
142
189
  summary: Automates churn + flog scoring on a git repo for a Ruby project
143
190
  test_files:
144
- - spec/turbulence/calculators/churn_spec.rb
145
- - spec/turbulence/calculators/complexity_spec.rb
146
- - spec/turbulence/checks_environoment_spec.rb
147
- - spec/turbulence/command_line_interface_spec.rb
148
- - spec/turbulence/scatter_splot_generator_spec.rb
149
- - spec/turbulence/scm/git_spec.rb
150
- - spec/turbulence/scm/perforce_spec.rb
151
- - spec/turbulence/turbulence_spec.rb
191
+ - !binary |-
192
+ c3BlYy90dXJidWxlbmNlL2NhbGN1bGF0b3JzL2NodXJuX3NwZWMucmI=
193
+ - !binary |-
194
+ c3BlYy90dXJidWxlbmNlL2NhbGN1bGF0b3JzL2NvbXBsZXhpdHlfc3BlYy5y
195
+ Yg==
196
+ - !binary |-
197
+ c3BlYy90dXJidWxlbmNlL2NoZWNrc19lbnZpcm9ub21lbnRfc3BlYy5yYg==
198
+ - !binary |-
199
+ c3BlYy90dXJidWxlbmNlL2NvbW1hbmRfbGluZV9pbnRlcmZhY2Vfc3BlYy5y
200
+ Yg==
201
+ - !binary |-
202
+ c3BlYy90dXJidWxlbmNlL2dlbmVyYXRvcnMvc2NhdHRlcl9wbG90X3NwZWMu
203
+ cmI=
204
+ - !binary |-
205
+ c3BlYy90dXJidWxlbmNlL2dlbmVyYXRvcnMvdHJlZW1hcF9zcGVjLnJi
206
+ - !binary |-
207
+ c3BlYy90dXJidWxlbmNlL3NjbS9naXRfc3BlYy5yYg==
208
+ - !binary |-
209
+ c3BlYy90dXJidWxlbmNlL3NjbS9wZXJmb3JjZV9zcGVjLnJi
210
+ - !binary |-
211
+ c3BlYy90dXJidWxlbmNlL3R1cmJ1bGVuY2Vfc3BlYy5yYg==
@@ -1,65 +0,0 @@
1
- require 'json'
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
-
19
- class ScatterPlotGenerator
20
- def self.from(metrics_hash)
21
- new(metrics_hash)
22
- end
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
27
- @metrics_hash = metrics_hash
28
- end
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
-
37
- def to_js
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, _|
54
- directories = File.dirname(filename).split("/")
55
- directories[0..1].join("/")
56
- end
57
- end
58
-
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]}
62
- end
63
- end
64
- end
65
- end