churn 0.0.34 → 0.0.35
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +3 -4
- data/Gemfile.lock +15 -17
- data/README.md +24 -4
- data/bin/churn +25 -2
- data/churn.gemspec +2 -2
- data/lib/churn/{churn_calculator.rb → calculator.rb} +41 -51
- data/lib/churn/{churn_history.rb → history.rb} +0 -0
- data/lib/{churn_options.rb → churn/options.rb} +15 -1
- data/lib/churn/{bzr_analyzer.rb → scm/bzr_analyzer.rb} +5 -0
- data/lib/churn/{git_analyzer.rb → scm/git_analyzer.rb} +5 -0
- data/lib/churn/{hg_analyzer.rb → scm/hg_analyzer.rb} +5 -0
- data/lib/churn/{source_control.rb → scm/source_control.rb} +27 -0
- data/lib/churn/{svn_analyzer.rb → scm/svn_analyzer.rb} +11 -2
- data/lib/churn/version.rb +1 -1
- data/lib/tasks/churn_tasks.rb +4 -4
- data/test/test_helper.rb +1 -1
- data/test/unit/churn_calculator_test.rb +18 -21
- data/test/unit/svn_analyzer_test.rb +26 -1
- metadata +59 -86
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: b1ef198f417e616080e9ada053a19596286c0ceb
|
4
|
+
data.tar.gz: d42960ccea815f89f846123808fd346fb5f5f65b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e92d32eade6ace59e1ed9b663f12d90ba1d1fe91de7d6675fa4910c4e8bb2e95df14d8cf20f53a1dd574c3ed5ea737c965f72c955188399c86ca41d2442bd376
|
7
|
+
data.tar.gz: 4a69afaa2311f8847089b8e1b28ee820679e790b9795bbfc843554db832b6d5c48c540930529b4c29ee0ba7a30b0d5559f36fc32ddd6cf5b919c59159a04cc2a
|
data/.travis.yml
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
churn (0.0.
|
4
|
+
churn (0.0.35)
|
5
5
|
chronic (>= 0.2.3)
|
6
6
|
hirb
|
7
7
|
json_pure
|
8
8
|
main
|
9
|
+
rest-client (>= 1.6.0)
|
9
10
|
ruby_parser (~> 3.0)
|
10
11
|
sexp_processor (~> 4.1)
|
11
12
|
|
@@ -15,33 +16,30 @@ GEM
|
|
15
16
|
activesupport (3.2.10)
|
16
17
|
i18n (~> 0.6)
|
17
18
|
multi_json (~> 1.0)
|
18
|
-
arrayfields (4.
|
19
|
-
chronic (0.
|
19
|
+
arrayfields (4.9.0)
|
20
|
+
chronic (0.10.2)
|
20
21
|
fattr (2.2.1)
|
21
|
-
git (1.2.5)
|
22
22
|
hirb (0.7.1)
|
23
23
|
i18n (0.6.1)
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
rake
|
28
|
-
rdoc
|
29
|
-
json (1.7.6)
|
30
|
-
json_pure (1.7.6)
|
31
|
-
main (5.1.1)
|
24
|
+
json (1.8.1)
|
25
|
+
json_pure (1.8.0)
|
26
|
+
main (5.2.0)
|
32
27
|
arrayfields (>= 4.7.4)
|
33
28
|
chronic (>= 0.6.2)
|
34
29
|
fattr (>= 2.2.0)
|
35
30
|
map (>= 5.1.0)
|
36
|
-
map (6.
|
31
|
+
map (6.5.1)
|
32
|
+
mime-types (1.25)
|
37
33
|
mocha (0.9.12)
|
38
34
|
multi_json (1.5.0)
|
39
35
|
rake (10.0.3)
|
40
|
-
rdoc (
|
36
|
+
rdoc (4.0.1)
|
41
37
|
json (~> 1.4)
|
42
|
-
|
38
|
+
rest-client (1.6.7)
|
39
|
+
mime-types (>= 1.16)
|
40
|
+
ruby_parser (3.2.2)
|
43
41
|
sexp_processor (~> 4.1)
|
44
|
-
sexp_processor (4.
|
42
|
+
sexp_processor (4.3.0)
|
45
43
|
shoulda (3.3.2)
|
46
44
|
shoulda-context (~> 1.0.1)
|
47
45
|
shoulda-matchers (~> 1.4.1)
|
@@ -59,9 +57,9 @@ PLATFORMS
|
|
59
57
|
|
60
58
|
DEPENDENCIES
|
61
59
|
churn!
|
62
|
-
jeweler
|
63
60
|
mocha (~> 0.9.5)
|
64
61
|
rake
|
62
|
+
rdoc
|
65
63
|
shoulda
|
66
64
|
simplecov
|
67
65
|
test-construct
|
data/README.md
CHANGED
@@ -4,7 +4,7 @@ Churn
|
|
4
4
|
A Project to give the churn file, class, and method for a project for a given checkin. Over time the tool adds up the history of churns to give the number of times a file, class, or method is changing during the life of a project.
|
5
5
|
Churn for files is immediate, but classes and methods requires buildings up a history using churn between revisions. The history is stored in ./tmp
|
6
6
|
|
7
|
-
Currently has full Git, Mercurial (hg),
|
7
|
+
Currently has full Git, Mercurial (hg), Bazaar (bzr) support, and partial SVN support (supports only file level churn currently)
|
8
8
|
|
9
9
|
File changes can be calculated on any single commit to look at method changes you need to be running churn over time. Using a git post-commit hook, configuring your CI to run churn, or using [churn-site](http://churn.picoappz.com) is the best way to build up your churn history. See the --past_history (-p) option to do a one time run building up past class and method level churn.
|
10
10
|
|
@@ -38,15 +38,18 @@ The reason you want require false is that when required by default churn is expe
|
|
38
38
|
* then run`rake churn` or `bundle exec rake churn`
|
39
39
|
* use environment variables to control churn defaults
|
40
40
|
|
41
|
+
``` ruby
|
41
42
|
ENV['CHURN_MINIMUM_CHURN_COUNT']
|
42
43
|
ENV['CHURN_START_DATE']
|
43
44
|
ENV['CHURN_IGNORE_FILES']
|
45
|
+
```
|
44
46
|
|
45
47
|
* CLI:
|
46
48
|
* on command line run `churn` or `bundle exec churn`
|
47
49
|
* need help run `churn -h` to get additional information
|
48
50
|
* run the executable passing in options to override defaults
|
49
|
-
|
51
|
+
|
52
|
+
``` bash
|
50
53
|
churn -i "churn.gemspec, Gemfile" #ignore files
|
51
54
|
churn -y #output yaml format opposed to text
|
52
55
|
churn -c 10 #set minimum churn count on a file to 10
|
@@ -54,7 +57,7 @@ The reason you want require false is that when required by default churn is expe
|
|
54
57
|
churn --start_date "6 months ago" #Start looking at file changes from 6 months ago
|
55
58
|
churn -p "4 months ago" #churn the past history to build up data for the last 4 months
|
56
59
|
churn --past_history #churn the past history for default 3 months to build up data
|
57
|
-
|
60
|
+
```
|
58
61
|
|
59
62
|
## Example Output
|
60
63
|
|
@@ -124,7 +127,7 @@ The reason you want require false is that when required by default churn is expe
|
|
124
127
|
| lib/churn/churn_calculator.rb | ChurnCalculator | ChurnCalculator#to_s | 1 |
|
125
128
|
+-------------------------------+-----------------+-----------------------------------------+---------------+
|
126
129
|
|
127
|
-
## Options
|
130
|
+
## CLI Options
|
128
131
|
|
129
132
|
[~/projects/churn] churn -h
|
130
133
|
NAME
|
@@ -142,6 +145,23 @@ The reason you want require false is that when required by default churn is expe
|
|
142
145
|
--data_directory=[data_directory], -d (0 ~> string(data_directory=))
|
143
146
|
--past_history=[past_history], -p (0 ~> string(past_history=))
|
144
147
|
--help, -h
|
148
|
+
|
149
|
+
## Library Options
|
150
|
+
|
151
|
+
All the CLI options are parsed and just passed into the library. If you want to run the library directly from other code. The best way to see current options is where the [churn executable](https://github.com/danmayer/churn/blob/master/bin/churn) passes the parsed options into the `ChurnCalculator` class
|
152
|
+
|
153
|
+
###
|
154
|
+
# Available options
|
155
|
+
###
|
156
|
+
options = {:minimum_churn_count => params['minimum_churn_count'].value,
|
157
|
+
:ignore_files => params['ignore_files'].value,
|
158
|
+
:start_date => params['start_date'].value,
|
159
|
+
:data_directory => params['data_directory'].value,
|
160
|
+
:history => params['past_history'].value,
|
161
|
+
:report => params['report'].value,
|
162
|
+
:name => params['name'].value
|
163
|
+
}
|
164
|
+
result = Churn::ChurnCalculator.new(options).report(false)
|
145
165
|
|
146
166
|
## Notes on Patches/Pull Requests
|
147
167
|
|
data/bin/churn
CHANGED
@@ -1,4 +1,13 @@
|
|
1
1
|
#! /usr/bin/env ruby
|
2
|
+
# resolve bin path, ignoring symlinks
|
3
|
+
require "pathname"
|
4
|
+
bin_file = Pathname.new(__FILE__).realpath
|
5
|
+
|
6
|
+
# add self to libpath
|
7
|
+
puts File.expand_path("../../lib", bin_file)
|
8
|
+
$:.unshift File.expand_path("../../lib", bin_file)
|
9
|
+
|
10
|
+
require 'churn/calculator'
|
2
11
|
require 'main'
|
3
12
|
require 'yaml'
|
4
13
|
|
@@ -21,6 +30,19 @@ Main do
|
|
21
30
|
default ''
|
22
31
|
end
|
23
32
|
|
33
|
+
option('report', 'r') do
|
34
|
+
cast :string
|
35
|
+
argument :optional
|
36
|
+
default ''
|
37
|
+
end
|
38
|
+
|
39
|
+
option('name', 'n') do
|
40
|
+
cast :string
|
41
|
+
argument :optional
|
42
|
+
default ''
|
43
|
+
description 'name is require if remotely reporting churn results. If available as a github project, pass name in the form of username/project_name'
|
44
|
+
end
|
45
|
+
|
24
46
|
option('start_date', 's') do
|
25
47
|
cast :string
|
26
48
|
argument :optional
|
@@ -41,12 +63,13 @@ Main do
|
|
41
63
|
end
|
42
64
|
|
43
65
|
def report_churn(output_string)
|
44
|
-
require File.join(File.dirname(__FILE__), '..', 'lib', 'churn', 'churn_calculator')
|
45
66
|
options = {:minimum_churn_count => params['minimum_churn_count'].value,
|
46
67
|
:ignore_files => params['ignore_files'].value,
|
47
68
|
:start_date => params['start_date'].value,
|
48
69
|
:data_directory => params['data_directory'].value,
|
49
|
-
:history => params['past_history'].value
|
70
|
+
:history => params['past_history'].value,
|
71
|
+
:report => params['report'].value,
|
72
|
+
:name => params['name'].value
|
50
73
|
}
|
51
74
|
result = Churn::ChurnCalculator.new(options).report(output_string)
|
52
75
|
unless output_string
|
data/churn.gemspec
CHANGED
@@ -27,11 +27,11 @@ Gem::Specification.new do |s|
|
|
27
27
|
]
|
28
28
|
|
29
29
|
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
30
|
-
s.add_development_dependency(%q<jeweler>, [">= 0"])
|
31
30
|
s.add_development_dependency(%q<test-construct>, [">= 0"])
|
32
31
|
s.add_development_dependency(%q<rake>, [">= 0"])
|
33
32
|
s.add_development_dependency(%q<mocha>, ["~> 0.9.5"])
|
34
33
|
s.add_development_dependency(%q<simplecov>,[">= 0"])
|
34
|
+
s.add_development_dependency(%q<rdoc>,[">= 0"])
|
35
35
|
#s.add_development_dependency(%q<ruby-debug>, ["~> 0.10.4"])
|
36
36
|
s.add_runtime_dependency(%q<main>, [">= 0"])
|
37
37
|
s.add_runtime_dependency(%q<json_pure>, [">= 0"])
|
@@ -39,5 +39,5 @@ Gem::Specification.new do |s|
|
|
39
39
|
s.add_runtime_dependency(%q<sexp_processor>, ["~> 4.1"])
|
40
40
|
s.add_runtime_dependency(%q<ruby_parser>, ["~> 3.0"])
|
41
41
|
s.add_runtime_dependency(%q<hirb>, [">= 0"])
|
42
|
-
|
42
|
+
s.add_runtime_dependency(%q<rest-client>, [">= 1.6.0"])
|
43
43
|
end
|
@@ -4,16 +4,18 @@ require 'ruby_parser'
|
|
4
4
|
require 'json'
|
5
5
|
require 'hirb'
|
6
6
|
require 'fileutils'
|
7
|
+
require 'rest_client'
|
7
8
|
|
8
9
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
9
|
-
require 'source_control'
|
10
|
-
require 'git_analyzer'
|
11
|
-
require 'svn_analyzer'
|
12
|
-
require 'hg_analyzer'
|
13
|
-
require 'bzr_analyzer'
|
10
|
+
require 'scm/source_control'
|
11
|
+
require 'scm/git_analyzer'
|
12
|
+
require 'scm/svn_analyzer'
|
13
|
+
require 'scm/hg_analyzer'
|
14
|
+
require 'scm/bzr_analyzer'
|
15
|
+
|
14
16
|
require 'location_mapping'
|
15
|
-
require '
|
16
|
-
require '
|
17
|
+
require 'history'
|
18
|
+
require 'options'
|
17
19
|
|
18
20
|
module Churn
|
19
21
|
|
@@ -30,8 +32,7 @@ module Churn
|
|
30
32
|
|
31
33
|
@minimum_churn_count = @churn_options.minimum_churn_count
|
32
34
|
@ignore_files = @churn_options.ignore_files
|
33
|
-
|
34
|
-
@source_control = set_source_control(start_date)
|
35
|
+
@source_control = SourceControl.set_source_control(@churn_options.start_date)
|
35
36
|
|
36
37
|
@changes = {}
|
37
38
|
@revision_changes = {}
|
@@ -47,12 +48,23 @@ module Churn
|
|
47
48
|
if @churn_options.history
|
48
49
|
generate_history
|
49
50
|
else
|
50
|
-
|
51
|
-
|
51
|
+
emit
|
52
|
+
analyze
|
53
|
+
remote_report
|
52
54
|
print ? self.to_s : self.to_h
|
53
55
|
end
|
54
56
|
end
|
55
57
|
|
58
|
+
def remote_report
|
59
|
+
if @churn_options.report_host
|
60
|
+
puts "posting churn results to #{@churn_options.report_host}"
|
61
|
+
data = {:name => @churn_options.name, :revision => @revisions.first, :data => self.to_h}.to_json
|
62
|
+
RestClient.post @churn_options.report_host, {"results" => data}, :content_type => :json, :accept => :json
|
63
|
+
end
|
64
|
+
rescue Errno::ECONNREFUSED
|
65
|
+
puts "error posting churn results connection refused to host: #{@churn_options.report_host}"
|
66
|
+
end
|
67
|
+
|
56
68
|
# this method generates the past history of a churn project from first commit to current
|
57
69
|
# running the report for oldest commits first so they are built up correctly
|
58
70
|
def generate_history
|
@@ -124,23 +136,22 @@ module Churn
|
|
124
136
|
result = seperator
|
125
137
|
result +="* Revision Changes \n"
|
126
138
|
result += seperator
|
127
|
-
result += "Files:
|
128
|
-
result +=
|
129
|
-
result += "
|
130
|
-
result +=
|
131
|
-
result += "
|
132
|
-
result += display_array(hash[:changed_methods]) + "\n"
|
139
|
+
result += display_array("Files", hash[:changed_files], :fields=>[:to_str], :headers=>{:to_str=>'file'})
|
140
|
+
result += "\n"
|
141
|
+
result += display_array("Classes", hash[:changed_classes])
|
142
|
+
result += "\n"
|
143
|
+
result += display_array("Methods", hash[:changed_methods]) + "\n"
|
133
144
|
result += seperator
|
134
145
|
result +="* Project Churn \n"
|
135
146
|
result += seperator
|
136
|
-
result += "
|
137
|
-
result += display_array(hash[:changes])
|
138
|
-
result += "\
|
147
|
+
result += "\n"
|
148
|
+
result += display_array("Files", hash[:changes])
|
149
|
+
result += "\n"
|
139
150
|
class_churn = collect_items(hash[:class_churn], 'klass')
|
140
|
-
result += display_array(class_churn)
|
141
|
-
result += "\
|
151
|
+
result += display_array("Classes", class_churn)
|
152
|
+
result += "\n"
|
142
153
|
method_churn = collect_items(hash[:method_churn], 'method')
|
143
|
-
result += display_array(method_churn)
|
154
|
+
result += display_array("Methods", method_churn)
|
144
155
|
end
|
145
156
|
|
146
157
|
private
|
@@ -158,40 +169,19 @@ module Churn
|
|
158
169
|
/.*\.rb/
|
159
170
|
end
|
160
171
|
|
161
|
-
def self.display_array(array, options={})
|
162
|
-
|
172
|
+
def self.display_array(title, array, options={})
|
173
|
+
response = ''
|
174
|
+
if array && array.length > 0
|
175
|
+
response = "#{title}\n"
|
176
|
+
response << Hirb::Helpers::AutoTable.render(array, options.merge(:description=>false)) + "\n"
|
177
|
+
end
|
178
|
+
response
|
163
179
|
end
|
164
180
|
|
165
181
|
def self.seperator
|
166
182
|
"*"*70+"\n"
|
167
183
|
end
|
168
184
|
|
169
|
-
def self.git?
|
170
|
-
!!(`git branch 2>&1` && $?.success?)
|
171
|
-
end
|
172
|
-
|
173
|
-
def self.hg?
|
174
|
-
!!(`hg branch 2>&1` && $?.success?)
|
175
|
-
end
|
176
|
-
|
177
|
-
def self.bzr?
|
178
|
-
!!(`bzr nick 2>&1` && $?.success?)
|
179
|
-
end
|
180
|
-
|
181
|
-
def set_source_control(start_date)
|
182
|
-
if self.class.git?
|
183
|
-
GitAnalyzer.new(start_date)
|
184
|
-
elsif self.class.hg?
|
185
|
-
HgAnalyzer.new(start_date)
|
186
|
-
elsif self.class.bzr?
|
187
|
-
BzrAnalyzer.new(start_date)
|
188
|
-
elsif File.exist?(".svn")
|
189
|
-
SvnAnalyzer.new(start_date)
|
190
|
-
else
|
191
|
-
raise "Churning requires a bazaar, git, mercurial, or subversion repo"
|
192
|
-
end
|
193
|
-
end
|
194
|
-
|
195
185
|
def calculate_revision_changes
|
196
186
|
@revisions.each do |revision|
|
197
187
|
if revision == @revisions.first
|
File without changes
|
@@ -8,8 +8,9 @@ module Churn
|
|
8
8
|
DEFAULT_CHURN_DIRECTORY = "tmp/churn"
|
9
9
|
DEFAULT_MINIMUM_CHURN_COUNT = 5
|
10
10
|
DEFAULT_START_TIME = '3 months ago'
|
11
|
+
DEFAULT_REPORT_HOST = 'http://churn.picoappz.com'
|
11
12
|
|
12
|
-
attr_accessor :data_directory, :minimum_churn_count, :ignore_files, :start_date, :history
|
13
|
+
attr_accessor :data_directory, :minimum_churn_count, :ignore_files, :start_date, :history, :report_host, :name
|
13
14
|
|
14
15
|
def initialize()
|
15
16
|
@data_directory = DEFAULT_CHURN_DIRECTORY
|
@@ -17,6 +18,8 @@ module Churn
|
|
17
18
|
@ignore_files = ['/dev/null']
|
18
19
|
@start_date = DEFAULT_START_TIME
|
19
20
|
@history = nil
|
21
|
+
@report_host = nil
|
22
|
+
@name = nil
|
20
23
|
end
|
21
24
|
|
22
25
|
def set_options(options = {})
|
@@ -29,6 +32,17 @@ module Churn
|
|
29
32
|
if @history=='true'
|
30
33
|
@history = DEFAULT_START_TIME
|
31
34
|
end
|
35
|
+
if !options[:report].nil? && options[:report]!=''
|
36
|
+
@report_host = options[:report]
|
37
|
+
if @report_host=='true'
|
38
|
+
@report_host = DEFAULT_REPORT_HOST
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
@name = options[:name] if !options[:name].nil? && options[:name]!=''
|
43
|
+
if !@report_host.nil? && @name.nil?
|
44
|
+
raise ArgumentError.new "If you are reporting churn results you must pass a valid github project name in the form of username/project_name"
|
45
|
+
end
|
32
46
|
|
33
47
|
self
|
34
48
|
end
|
@@ -2,6 +2,11 @@ module Churn
|
|
2
2
|
|
3
3
|
#analizes Bzr / Bazaar SCM to find recently changed files, and what lines have been altered
|
4
4
|
class BzrAnalyzer < SourceControl
|
5
|
+
|
6
|
+
def self.supported?
|
7
|
+
!!(`bzr nick 2>&1` && $?.success?)
|
8
|
+
end
|
9
|
+
|
5
10
|
def get_logs
|
6
11
|
`bzr log -v --short #{date_range}`.split("\n").reject{|line| line !~ /^[ ]*(M|A) /}.map{|line| line.strip.split(" ")[1..-1]}.flatten
|
7
12
|
end
|
@@ -2,6 +2,11 @@ module Churn
|
|
2
2
|
|
3
3
|
#analizes git SCM to find recently changed files, and what lines have been altered
|
4
4
|
class GitAnalyzer < SourceControl
|
5
|
+
|
6
|
+
def self.supported?
|
7
|
+
!!(`git branch 2>&1` && $?.success?)
|
8
|
+
end
|
9
|
+
|
5
10
|
def get_logs
|
6
11
|
`git log #{date_range} --name-only --pretty=format:`.split(/\n/).reject{|line| line == ""}
|
7
12
|
end
|
@@ -2,6 +2,11 @@ module Churn
|
|
2
2
|
|
3
3
|
#analizes Hg / Mercurial SCM to find recently changed files, and what lines have been altered
|
4
4
|
class HgAnalyzer < SourceControl
|
5
|
+
|
6
|
+
def self.supported?
|
7
|
+
!!(`hg branch 2>&1` && $?.success?)
|
8
|
+
end
|
9
|
+
|
5
10
|
def get_logs
|
6
11
|
`hg log -v#{date_range}`.split("\n").reject{|line| line !~ /^files:/}.map{|line| line.split(" ")[1..-1]}.flatten
|
7
12
|
end
|
@@ -2,10 +2,37 @@ module Churn
|
|
2
2
|
|
3
3
|
# Base clase for analyzing various SCM systems like git, HG, and SVN
|
4
4
|
class SourceControl
|
5
|
+
|
6
|
+
def self.set_source_control(start_date)
|
7
|
+
if GitAnalyzer.supported?
|
8
|
+
GitAnalyzer.new(start_date)
|
9
|
+
elsif HgAnalyzer.supported?
|
10
|
+
HgAnalyzer.new(start_date)
|
11
|
+
elsif BzrAnalyzer.supported?
|
12
|
+
BzrAnalyzer.new(start_date)
|
13
|
+
elsif SvnAnalyzer.supported?
|
14
|
+
SvnAnalyzer.new(start_date)
|
15
|
+
else
|
16
|
+
raise "Churn requires a bazaar, git, mercurial, or subversion source control"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.supported?
|
21
|
+
raise "child class must implement"
|
22
|
+
end
|
23
|
+
|
5
24
|
def initialize(start_date=nil)
|
6
25
|
@start_date = start_date
|
7
26
|
end
|
8
27
|
|
28
|
+
def get_logs
|
29
|
+
raise "child class must implement"
|
30
|
+
end
|
31
|
+
|
32
|
+
def get_revisions
|
33
|
+
raise "child class must implement"
|
34
|
+
end
|
35
|
+
|
9
36
|
def get_updated_files_change_info(revision, revisions)
|
10
37
|
updated = {}
|
11
38
|
logs = get_updated_files_from_log(revision, revisions)
|
@@ -2,15 +2,24 @@ module Churn
|
|
2
2
|
|
3
3
|
#analizes SVN SCM to find recently changed files, and what lines have been altered
|
4
4
|
class SvnAnalyzer < SourceControl
|
5
|
+
|
6
|
+
def self.supported?
|
7
|
+
File.exist?(".svn")
|
8
|
+
end
|
9
|
+
|
5
10
|
def get_logs
|
6
|
-
`svn log #{date_range}
|
11
|
+
`svn log --verbose#{date_range}#{svn_credentials}`.split(/\n/).map { |line| clean_up_svn_line(line) }.compact
|
7
12
|
end
|
8
13
|
|
9
14
|
private
|
15
|
+
def svn_credentials
|
16
|
+
" --username #{ENV['SVN_USR']} --password #{ENV['SVN_PWD']}" if ENV['SVN_PWD'] && ENV['SVN_USR']
|
17
|
+
end
|
18
|
+
|
10
19
|
def date_range
|
11
20
|
if @start_date
|
12
21
|
date = Chronic.parse(@start_date)
|
13
|
-
"--revision {#{date.strftime('%Y-%m-%d')}}:{#{Time.now.strftime('%Y-%m-%d')}}"
|
22
|
+
" --revision {#{date.strftime('%Y-%m-%d')}}:{#{Time.now.strftime('%Y-%m-%d')}}"
|
14
23
|
end
|
15
24
|
end
|
16
25
|
|
data/lib/churn/version.rb
CHANGED
data/lib/tasks/churn_tasks.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
if defined?(RakeFileUtils) # self.respond_to?(:desc)
|
2
2
|
def report_churn()
|
3
|
-
require File.join(File.dirname(__FILE__), '..', 'churn', '
|
4
|
-
|
3
|
+
require File.join(File.dirname(__FILE__), '..', 'churn', 'calculator')
|
4
|
+
options = {}
|
5
5
|
{ :minimum_churn_count => ENV['CHURN_MINIMUM_CHURN_COUNT'],
|
6
6
|
:start_date => ENV['CHURN_START_DATE'],
|
7
7
|
:ignore_files => ENV['CHURN_IGNORE_FILES'],
|
8
8
|
:data_directory => ENV['CHURN_DATA_DIRECTORY'],
|
9
|
-
}.each {|k,v|
|
10
|
-
Churn::ChurnCalculator.new(
|
9
|
+
}.each {|k,v| options[k] = v unless v.nil? }
|
10
|
+
Churn::ChurnCalculator.new(options).report
|
11
11
|
end
|
12
12
|
|
13
13
|
desc "Report the current churn for the project"
|
data/test/test_helper.rb
CHANGED
@@ -13,7 +13,7 @@ end
|
|
13
13
|
|
14
14
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
15
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
|
-
require 'churn/
|
16
|
+
require 'churn/calculator'
|
17
17
|
Mocha::Configuration.prevent(:stubbing_non_existent_method)
|
18
18
|
|
19
19
|
class Test::Unit::TestCase
|
@@ -4,7 +4,7 @@ class ChurnCalculatorTest < Test::Unit::TestCase
|
|
4
4
|
|
5
5
|
should "use minimum churn count" do
|
6
6
|
within_construct do |container|
|
7
|
-
Churn::
|
7
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(true)
|
8
8
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
9
9
|
|
10
10
|
churn.stubs(:parse_log_for_changes).returns([['file.rb', 4],['less.rb',1]])
|
@@ -18,7 +18,7 @@ class ChurnCalculatorTest < Test::Unit::TestCase
|
|
18
18
|
|
19
19
|
should "ensure that minimum churn count is initialized as a Fixnum" do
|
20
20
|
within_construct do |container|
|
21
|
-
Churn::
|
21
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(true)
|
22
22
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => "3"})
|
23
23
|
|
24
24
|
assert_equal 3, churn.instance_variable_get(:@minimum_churn_count)
|
@@ -27,7 +27,7 @@ class ChurnCalculatorTest < Test::Unit::TestCase
|
|
27
27
|
|
28
28
|
should "use ignore_files filter" do
|
29
29
|
within_construct do |container|
|
30
|
-
Churn::
|
30
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(true)
|
31
31
|
churn = Churn::ChurnCalculator.new({:ignore_files => "file.rb"})
|
32
32
|
|
33
33
|
churn.stubs(:parse_log_for_changes).returns([['file.rb', 10],['new.rb',11]])
|
@@ -41,7 +41,7 @@ class ChurnCalculatorTest < Test::Unit::TestCase
|
|
41
41
|
|
42
42
|
should "analize sorts changes" do
|
43
43
|
within_construct do |container|
|
44
|
-
Churn::
|
44
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(true)
|
45
45
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
46
46
|
|
47
47
|
churn.stubs(:parse_log_for_changes).returns([['file.rb', 4],['most.rb', 9],['less.rb',1]])
|
@@ -57,7 +57,7 @@ class ChurnCalculatorTest < Test::Unit::TestCase
|
|
57
57
|
|
58
58
|
should "have correct changed_files data" do
|
59
59
|
within_construct do |container|
|
60
|
-
Churn::
|
60
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(true)
|
61
61
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
62
62
|
|
63
63
|
churn.stubs(:parse_log_for_changes).returns([['less.rb',1]])
|
@@ -70,7 +70,7 @@ class ChurnCalculatorTest < Test::Unit::TestCase
|
|
70
70
|
|
71
71
|
should "have correct changed classes and methods data" do
|
72
72
|
within_construct do |container|
|
73
|
-
Churn::
|
73
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(true)
|
74
74
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
75
75
|
|
76
76
|
churn.stubs(:parse_log_for_changes).returns([['less.rb',1]])
|
@@ -87,7 +87,7 @@ class ChurnCalculatorTest < Test::Unit::TestCase
|
|
87
87
|
|
88
88
|
should "have correct churn method and classes at 1 change" do
|
89
89
|
within_construct do |container|
|
90
|
-
Churn::
|
90
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(true)
|
91
91
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
92
92
|
|
93
93
|
churn.stubs(:parse_log_for_changes).returns([['less.rb',1]])
|
@@ -117,40 +117,37 @@ class ChurnCalculatorTest < Test::Unit::TestCase
|
|
117
117
|
end
|
118
118
|
|
119
119
|
should "initialize a churn calculator for hg repositories" do
|
120
|
-
Churn::
|
121
|
-
Churn::
|
120
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(false)
|
121
|
+
Churn::HgAnalyzer.expects(:`).with("hg branch 2>&1").returns(true) #` fix syntax hilighting
|
122
122
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
123
123
|
assert churn.instance_variable_get(:@source_control).is_a?(Churn::HgAnalyzer)
|
124
124
|
end
|
125
125
|
|
126
126
|
should "initialize a churn calculator for bzr repositories" do
|
127
|
-
Churn::
|
128
|
-
Churn::
|
129
|
-
Churn::
|
127
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(false)
|
128
|
+
Churn::HgAnalyzer.stubs(:supported?).returns(false)
|
129
|
+
Churn::BzrAnalyzer.expects(:`).with("bzr nick 2>&1").returns(true) #` fix syntax hilighting
|
130
130
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
131
131
|
assert churn.instance_variable_get(:@source_control).is_a?(Churn::BzrAnalyzer)
|
132
132
|
end
|
133
133
|
|
134
134
|
should "initialize a churn calculator for svn repositories" do
|
135
|
-
Churn::
|
136
|
-
Churn::
|
137
|
-
Churn::
|
135
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(false)
|
136
|
+
Churn::HgAnalyzer.stubs(:supported?).returns(false)
|
137
|
+
Churn::BzrAnalyzer.stubs(:supported?).returns(false)
|
138
138
|
File.stubs(:exist?).returns(true)
|
139
139
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
140
140
|
assert churn.instance_variable_get(:@source_control).is_a?(Churn::SvnAnalyzer)
|
141
141
|
end
|
142
142
|
|
143
143
|
should "raise exception on a churn calculator for unknown repositories" do
|
144
|
-
Churn::
|
145
|
-
Churn::
|
146
|
-
Churn::
|
144
|
+
Churn::GitAnalyzer.stubs(:supported?).returns(false)
|
145
|
+
Churn::HgAnalyzer.stubs(:supported?).returns(false)
|
146
|
+
Churn::BzrAnalyzer.stubs(:supported?).returns(false)
|
147
147
|
File.stubs(:exist?).returns(false)
|
148
148
|
assert_raises RuntimeError do
|
149
149
|
churn = Churn::ChurnCalculator.new({:minimum_churn_count => 3})
|
150
150
|
end
|
151
151
|
end
|
152
152
|
|
153
|
-
|
154
|
-
|
155
|
-
|
156
153
|
end
|
@@ -21,7 +21,32 @@ class SvnAnalyzerTest < Test::Unit::TestCase
|
|
21
21
|
|
22
22
|
should "run date range correctly" do
|
23
23
|
svn_analyzer = Churn::SvnAnalyzer.new(Date.parse('3/3/2010'))
|
24
|
-
assert_equal "--revision {2010-03-03}:{#{Date.today.to_s}}", svn_analyzer.send(:date_range)
|
24
|
+
assert_equal " --revision {2010-03-03}:{#{Date.today.to_s}}", svn_analyzer.send(:date_range)
|
25
|
+
end
|
26
|
+
|
27
|
+
context "SvnAnalyzer#get_logs" do
|
28
|
+
should "return a list of changed files" do
|
29
|
+
ENV['SVN_PWD']= nil
|
30
|
+
ENV['SVN_USR']= nil
|
31
|
+
svn_analyzer = Churn::SvnAnalyzer.new
|
32
|
+
svn_analyzer.expects(:`).with('svn log --verbose').returns('
|
33
|
+
A /file1.rb
|
34
|
+
A /dir/file2.rb
|
35
|
+
A /file3.rb
|
36
|
+
M /file1.rb')
|
37
|
+
assert_equal ['/file1.rb', '/dir/file2.rb', '/file3.rb', '/file1.rb'], svn_analyzer.get_logs
|
38
|
+
end
|
39
|
+
should "invoke with svn credentials " do
|
40
|
+
ENV['SVN_PWD']= '123qwe'
|
41
|
+
ENV['SVN_USR']= 'user123'
|
42
|
+
svn_analyzer = Churn::SvnAnalyzer.new
|
43
|
+
svn_analyzer.expects(:`).with('svn log --verbose --username user123 --password 123qwe').returns('
|
44
|
+
A /file1.rb
|
45
|
+
A /dir/file2.rb
|
46
|
+
A /file3.rb
|
47
|
+
M /file1.rb')
|
48
|
+
assert_equal ['/file1.rb', '/dir/file2.rb', '/file3.rb', '/file1.rb'], svn_analyzer.get_logs
|
49
|
+
end
|
25
50
|
end
|
26
51
|
|
27
52
|
protected
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: churn
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.35
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Dan Mayer
|
@@ -14,151 +13,132 @@ dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: shoulda
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
28
|
+
name: test-construct
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
42
|
+
name: rake
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - '>='
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '0'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - '>='
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '0'
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
|
-
name:
|
56
|
+
name: mocha
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- -
|
59
|
+
- - ~>
|
68
60
|
- !ruby/object:Gem::Version
|
69
|
-
version:
|
61
|
+
version: 0.9.5
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- -
|
66
|
+
- - ~>
|
76
67
|
- !ruby/object:Gem::Version
|
77
|
-
version:
|
68
|
+
version: 0.9.5
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
|
-
name:
|
70
|
+
name: simplecov
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
|
-
- -
|
73
|
+
- - '>='
|
84
74
|
- !ruby/object:Gem::Version
|
85
|
-
version: 0
|
75
|
+
version: '0'
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
|
-
- -
|
80
|
+
- - '>='
|
92
81
|
- !ruby/object:Gem::Version
|
93
|
-
version: 0
|
82
|
+
version: '0'
|
94
83
|
- !ruby/object:Gem::Dependency
|
95
|
-
name:
|
84
|
+
name: rdoc
|
96
85
|
requirement: !ruby/object:Gem::Requirement
|
97
|
-
none: false
|
98
86
|
requirements:
|
99
|
-
- -
|
87
|
+
- - '>='
|
100
88
|
- !ruby/object:Gem::Version
|
101
89
|
version: '0'
|
102
90
|
type: :development
|
103
91
|
prerelease: false
|
104
92
|
version_requirements: !ruby/object:Gem::Requirement
|
105
|
-
none: false
|
106
93
|
requirements:
|
107
|
-
- -
|
94
|
+
- - '>='
|
108
95
|
- !ruby/object:Gem::Version
|
109
96
|
version: '0'
|
110
97
|
- !ruby/object:Gem::Dependency
|
111
98
|
name: main
|
112
99
|
requirement: !ruby/object:Gem::Requirement
|
113
|
-
none: false
|
114
100
|
requirements:
|
115
|
-
- -
|
101
|
+
- - '>='
|
116
102
|
- !ruby/object:Gem::Version
|
117
103
|
version: '0'
|
118
104
|
type: :runtime
|
119
105
|
prerelease: false
|
120
106
|
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
107
|
requirements:
|
123
|
-
- -
|
108
|
+
- - '>='
|
124
109
|
- !ruby/object:Gem::Version
|
125
110
|
version: '0'
|
126
111
|
- !ruby/object:Gem::Dependency
|
127
112
|
name: json_pure
|
128
113
|
requirement: !ruby/object:Gem::Requirement
|
129
|
-
none: false
|
130
114
|
requirements:
|
131
|
-
- -
|
115
|
+
- - '>='
|
132
116
|
- !ruby/object:Gem::Version
|
133
117
|
version: '0'
|
134
118
|
type: :runtime
|
135
119
|
prerelease: false
|
136
120
|
version_requirements: !ruby/object:Gem::Requirement
|
137
|
-
none: false
|
138
121
|
requirements:
|
139
|
-
- -
|
122
|
+
- - '>='
|
140
123
|
- !ruby/object:Gem::Version
|
141
124
|
version: '0'
|
142
125
|
- !ruby/object:Gem::Dependency
|
143
126
|
name: chronic
|
144
127
|
requirement: !ruby/object:Gem::Requirement
|
145
|
-
none: false
|
146
128
|
requirements:
|
147
|
-
- -
|
129
|
+
- - '>='
|
148
130
|
- !ruby/object:Gem::Version
|
149
131
|
version: 0.2.3
|
150
132
|
type: :runtime
|
151
133
|
prerelease: false
|
152
134
|
version_requirements: !ruby/object:Gem::Requirement
|
153
|
-
none: false
|
154
135
|
requirements:
|
155
|
-
- -
|
136
|
+
- - '>='
|
156
137
|
- !ruby/object:Gem::Version
|
157
138
|
version: 0.2.3
|
158
139
|
- !ruby/object:Gem::Dependency
|
159
140
|
name: sexp_processor
|
160
141
|
requirement: !ruby/object:Gem::Requirement
|
161
|
-
none: false
|
162
142
|
requirements:
|
163
143
|
- - ~>
|
164
144
|
- !ruby/object:Gem::Version
|
@@ -166,7 +146,6 @@ dependencies:
|
|
166
146
|
type: :runtime
|
167
147
|
prerelease: false
|
168
148
|
version_requirements: !ruby/object:Gem::Requirement
|
169
|
-
none: false
|
170
149
|
requirements:
|
171
150
|
- - ~>
|
172
151
|
- !ruby/object:Gem::Version
|
@@ -174,7 +153,6 @@ dependencies:
|
|
174
153
|
- !ruby/object:Gem::Dependency
|
175
154
|
name: ruby_parser
|
176
155
|
requirement: !ruby/object:Gem::Requirement
|
177
|
-
none: false
|
178
156
|
requirements:
|
179
157
|
- - ~>
|
180
158
|
- !ruby/object:Gem::Version
|
@@ -182,7 +160,6 @@ dependencies:
|
|
182
160
|
type: :runtime
|
183
161
|
prerelease: false
|
184
162
|
version_requirements: !ruby/object:Gem::Requirement
|
185
|
-
none: false
|
186
163
|
requirements:
|
187
164
|
- - ~>
|
188
165
|
- !ruby/object:Gem::Version
|
@@ -190,20 +167,32 @@ dependencies:
|
|
190
167
|
- !ruby/object:Gem::Dependency
|
191
168
|
name: hirb
|
192
169
|
requirement: !ruby/object:Gem::Requirement
|
193
|
-
none: false
|
194
170
|
requirements:
|
195
|
-
- -
|
171
|
+
- - '>='
|
196
172
|
- !ruby/object:Gem::Version
|
197
173
|
version: '0'
|
198
174
|
type: :runtime
|
199
175
|
prerelease: false
|
200
176
|
version_requirements: !ruby/object:Gem::Requirement
|
201
|
-
none: false
|
202
177
|
requirements:
|
203
|
-
- -
|
178
|
+
- - '>='
|
204
179
|
- !ruby/object:Gem::Version
|
205
180
|
version: '0'
|
206
|
-
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: rest-client
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - '>='
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: 1.6.0
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - '>='
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: 1.6.0
|
195
|
+
description: 'High method and class churn has been shown to have increased bug and
|
207
196
|
error rates. This gem helps you know what is changing a lot so you can do additional
|
208
197
|
testing, code review, or refactoring to try to tame the volatile code. '
|
209
198
|
email: dan@mayerdan.com
|
@@ -226,16 +215,16 @@ files:
|
|
226
215
|
- bin/churn
|
227
216
|
- churn.gemspec
|
228
217
|
- lib/churn.rb
|
229
|
-
- lib/churn/
|
230
|
-
- lib/churn/
|
231
|
-
- lib/churn/churn_history.rb
|
232
|
-
- lib/churn/git_analyzer.rb
|
233
|
-
- lib/churn/hg_analyzer.rb
|
218
|
+
- lib/churn/calculator.rb
|
219
|
+
- lib/churn/history.rb
|
234
220
|
- lib/churn/location_mapping.rb
|
235
|
-
- lib/churn/
|
236
|
-
- lib/churn/
|
221
|
+
- lib/churn/options.rb
|
222
|
+
- lib/churn/scm/bzr_analyzer.rb
|
223
|
+
- lib/churn/scm/git_analyzer.rb
|
224
|
+
- lib/churn/scm/hg_analyzer.rb
|
225
|
+
- lib/churn/scm/source_control.rb
|
226
|
+
- lib/churn/scm/svn_analyzer.rb
|
237
227
|
- lib/churn/version.rb
|
238
|
-
- lib/churn_options.rb
|
239
228
|
- lib/tasks/churn_tasks.rb
|
240
229
|
- man/churn.1
|
241
230
|
- man/churn.html
|
@@ -254,41 +243,25 @@ files:
|
|
254
243
|
homepage: http://github.com/danmayer/churn
|
255
244
|
licenses:
|
256
245
|
- MIT
|
246
|
+
metadata: {}
|
257
247
|
post_install_message:
|
258
248
|
rdoc_options: []
|
259
249
|
require_paths:
|
260
250
|
- lib
|
261
251
|
required_ruby_version: !ruby/object:Gem::Requirement
|
262
|
-
none: false
|
263
252
|
requirements:
|
264
|
-
- -
|
253
|
+
- - '>='
|
265
254
|
- !ruby/object:Gem::Version
|
266
255
|
version: '0'
|
267
|
-
segments:
|
268
|
-
- 0
|
269
|
-
hash: 837759494224756390
|
270
256
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
271
|
-
none: false
|
272
257
|
requirements:
|
273
|
-
- -
|
258
|
+
- - '>='
|
274
259
|
- !ruby/object:Gem::Version
|
275
260
|
version: '0'
|
276
261
|
requirements: []
|
277
262
|
rubyforge_project: churn
|
278
|
-
rubygems_version:
|
263
|
+
rubygems_version: 2.0.3
|
279
264
|
signing_key:
|
280
|
-
specification_version:
|
265
|
+
specification_version: 4
|
281
266
|
summary: Providing additional churn metrics over the original metric_fu churn
|
282
|
-
test_files:
|
283
|
-
- test/data/churn_calculator.rb
|
284
|
-
- test/data/test_helper.rb
|
285
|
-
- test/test_helper.rb
|
286
|
-
- test/unit/bzr_analyzer_test.rb
|
287
|
-
- test/unit/churn_calculator_test.rb
|
288
|
-
- test/unit/churn_history_test.rb
|
289
|
-
- test/unit/churn_options_test.rb
|
290
|
-
- test/unit/git_analyzer_test.rb
|
291
|
-
- test/unit/hg_analyzer_test.rb
|
292
|
-
- test/unit/location_mapping_test.rb
|
293
|
-
- test/unit/source_control_test.rb
|
294
|
-
- test/unit/svn_analyzer_test.rb
|
267
|
+
test_files: []
|