metior 0.1.4 → 0.2.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/.travis.yml +2 -0
- data/.yardopts +1 -0
- data/Changelog.md +23 -0
- data/Gemfile +1 -17
- data/Gemfile.lock +28 -21
- data/README.md +66 -20
- data/lib/metior.rb +30 -14
- data/lib/metior/actor.rb +28 -22
- data/lib/metior/auto_include_vcs.rb +43 -0
- data/lib/metior/collections/actor_collection.rb +97 -0
- data/lib/metior/collections/collection.rb +84 -0
- data/lib/metior/collections/commit_collection.rb +309 -0
- data/lib/metior/commit.rb +128 -48
- data/lib/metior/errors.rb +2 -2
- data/lib/metior/git/commit.rb +32 -31
- data/lib/metior/git/repository.rb +71 -6
- data/lib/metior/github/commit.rb +5 -16
- data/lib/metior/github/repository.rb +68 -40
- data/lib/metior/report.rb +139 -0
- data/lib/metior/report/view.rb +120 -0
- data/lib/metior/report/view_helper.rb +47 -0
- data/lib/metior/repository.rb +225 -56
- data/lib/metior/vcs.rb +12 -3
- data/lib/metior/version.rb +1 -1
- data/metior.gemspec +28 -26
- data/reports/default.rb +17 -0
- data/reports/default/images/favicon.png +0 -0
- data/reports/default/stylesheets/default.css +128 -0
- data/reports/default/templates/actor/minimal.mustache +1 -0
- data/reports/default/templates/commit/minimal.mustache +1 -0
- data/reports/default/templates/index.mustache +27 -0
- data/reports/default/templates/most_significant_authors.mustache +11 -0
- data/reports/default/templates/most_significant_commits.mustache +13 -0
- data/reports/default/templates/repository_information.mustache +17 -0
- data/reports/default/templates/top_committers.mustache +11 -0
- data/reports/default/views/index.rb +33 -0
- data/reports/default/views/most_significant_authors.rb +19 -0
- data/reports/default/views/most_significant_commits.rb +19 -0
- data/reports/default/views/repository_information.rb +47 -0
- data/reports/default/views/top_committers.rb +21 -0
- data/test/fixtures.rb +54 -36
- data/test/helper.rb +10 -3
- data/test/{test_class_loading.rb → test_1st_class_loading.rb} +1 -1
- data/test/test_actor_colletion.rb +78 -0
- data/test/test_collection.rb +61 -0
- data/test/test_commit_collection.rb +139 -0
- data/test/test_git.rb +58 -5
- data/test/test_github.rb +52 -9
- data/test/test_metior.rb +22 -1
- data/test/test_report.rb +49 -0
- data/test/test_repository.rb +46 -9
- data/test/test_vcs.rb +36 -13
- metadata +105 -43
data/test/fixtures.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
#
|
4
4
|
# Copyright (c) 2011, Sebastian Staudt
|
5
5
|
|
6
|
-
require '
|
6
|
+
require 'hashie/mash'
|
7
7
|
require 'time'
|
8
8
|
|
9
9
|
module Fixtures
|
@@ -18,14 +18,21 @@ module Fixtures
|
|
18
18
|
commits = []
|
19
19
|
|
20
20
|
commit = {}
|
21
|
+
last_commit = nil
|
21
22
|
file.lines.each do |line|
|
22
23
|
line.strip!
|
23
24
|
|
24
25
|
next if line.empty?
|
25
26
|
|
26
|
-
if line.match /^[
|
27
|
-
commit[:
|
28
|
-
commit[:
|
27
|
+
if line.match /^[A]\0/
|
28
|
+
commit[:added_files] = [] unless commit.key? :added_files
|
29
|
+
commit[:added_files] << line.split("\0").last.strip
|
30
|
+
elsif line.match /^[D]\0/
|
31
|
+
commit[:modified_files] = [] unless commit.key? :modified_files
|
32
|
+
commit[:modified_files] << line.split("\0").last.strip
|
33
|
+
elsif line.match /^[M]\0/
|
34
|
+
commit[:deleted_files] = [] unless commit.key? :deleted_files
|
35
|
+
commit[:deleted_files] << line.split("\0").last.strip
|
29
36
|
elsif line.match /\d+\0\d+$/
|
30
37
|
commit[:impact] = line.split("\0")
|
31
38
|
else
|
@@ -45,7 +52,9 @@ module Fixtures
|
|
45
52
|
end
|
46
53
|
|
47
54
|
if commit != {}
|
55
|
+
last_commit[:parent] = commit unless last_commit.nil?
|
48
56
|
commits << commit
|
57
|
+
last_commit = commit
|
49
58
|
commit = {}
|
50
59
|
end
|
51
60
|
end
|
@@ -58,42 +67,48 @@ module Fixtures
|
|
58
67
|
end
|
59
68
|
|
60
69
|
def self.commits_as_grit_commits(range)
|
61
|
-
commits = commits(range)
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
70
|
+
commits = commits(range).map do |commit|
|
71
|
+
parents = commit[:parent].nil? ? [] : [commit[:parent]]
|
72
|
+
|
73
|
+
diffs = []
|
74
|
+
commit[:added_files].each do |added_file|
|
75
|
+
diffs << { :new_file => true, :b_path => added_file }
|
76
|
+
end if commit.key? :added_files
|
77
|
+
commit[:deleted_files].each do |deleted_file|
|
78
|
+
diffs << { :deleted_file => true, :b_path => deleted_file }
|
79
|
+
end if commit.key? :deleted_files
|
80
|
+
commit[:modified_files].each do |modified_file|
|
81
|
+
diffs << { :b_path => modified_file }
|
82
|
+
end if commit.key? :modified_files
|
83
|
+
|
84
|
+
Hashie::Mash.new({
|
85
|
+
:author => {
|
86
|
+
:email => commit[:info][5],
|
87
|
+
:name => commit[:info][4]
|
88
|
+
},
|
89
|
+
:authored_data => Time.at(commit[:info][6].to_i),
|
90
|
+
:committer => {
|
91
|
+
:email => commit[:info][2],
|
92
|
+
:name => commit[:info][1]
|
93
|
+
},
|
94
|
+
:committed_date => Time.at(commit[:info][3].to_i),
|
95
|
+
:diffs => diffs,
|
96
|
+
:id => commit[:ids].first,
|
97
|
+
:message => commit[:info].first,
|
98
|
+
:parents => parents.map { |p| { :id => p[:ids].first } },
|
99
|
+
:stats => {
|
100
|
+
:additions => commit.key?(:impact) ? commit[:impact].first.to_i : 0,
|
101
|
+
:deletions => commit.key?(:impact) ? commit[:impact].last.to_i : 0
|
102
|
+
}
|
103
|
+
})
|
89
104
|
end
|
105
|
+
|
106
|
+
HASH_CLASS[commits.map { |c| [c.id, c] }]
|
90
107
|
end
|
91
108
|
|
92
109
|
def self.commits_as_rashies(range)
|
93
|
-
commits = commits(range)
|
94
|
-
|
95
|
-
commits.map do |commit|
|
96
|
-
Hashie::Rash.new({
|
110
|
+
commits = commits(range).map do |commit|
|
111
|
+
Hashie::Mash.new({
|
97
112
|
:author => {
|
98
113
|
:email => commit[:info][4],
|
99
114
|
:login => commit[:info][5],
|
@@ -108,8 +123,11 @@ module Fixtures
|
|
108
123
|
},
|
109
124
|
:id => commit[:ids].first,
|
110
125
|
:message => commit[:info].first,
|
126
|
+
:parents => ([{ :id => commit[:parent][:ids].first }] rescue [])
|
111
127
|
})
|
112
128
|
end
|
129
|
+
|
130
|
+
HASH_CLASS[commits.map { |c| [c.id, c] }]
|
113
131
|
end
|
114
132
|
|
115
133
|
end
|
data/test/helper.rb
CHANGED
@@ -3,14 +3,21 @@
|
|
3
3
|
#
|
4
4
|
# Copyright (c) 2011, Sebastian Staudt
|
5
5
|
|
6
|
+
require 'test/unit'
|
7
|
+
|
8
|
+
require 'rubygems'
|
9
|
+
require 'bundler'
|
10
|
+
|
11
|
+
Bundler.setup
|
12
|
+
|
13
|
+
require 'mocha'
|
14
|
+
require 'shoulda'
|
15
|
+
|
6
16
|
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
7
17
|
$LOAD_PATH.unshift File.dirname(__FILE__)
|
8
18
|
require 'metior'
|
9
19
|
include Metior
|
10
20
|
|
11
|
-
Bundler.setup
|
12
|
-
Bundler.require :test
|
13
|
-
|
14
21
|
# Extends TestCase functionality
|
15
22
|
class Test::Unit::TestCase
|
16
23
|
|
@@ -0,0 +1,78 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2011, Sebastian Staudt
|
5
|
+
|
6
|
+
require 'fixtures'
|
7
|
+
require 'helper'
|
8
|
+
require 'metior/collections/actor_collection'
|
9
|
+
|
10
|
+
class TestActorCollection < Test::Unit::TestCase
|
11
|
+
|
12
|
+
context 'A collection of actors' do
|
13
|
+
|
14
|
+
setup do
|
15
|
+
repo = Metior::Git::Repository.new File.dirname(File.dirname(__FILE__))
|
16
|
+
@@grit_commits ||= Fixtures.commits_as_grit_commits(''..'master')
|
17
|
+
Grit::Repo.any_instance.stubs(:commits).returns @@grit_commits.values
|
18
|
+
@@grit_commits.each do |id, commit|
|
19
|
+
Grit::Repo.any_instance.stubs(:commit).with(id).returns commit
|
20
|
+
end
|
21
|
+
@authors = repo.authors
|
22
|
+
end
|
23
|
+
|
24
|
+
should 'be an instance of Collection' do
|
25
|
+
assert_kind_of Collection, @authors
|
26
|
+
end
|
27
|
+
|
28
|
+
should 'allow to get all the commits authored by those actors' do
|
29
|
+
commits = @authors.authored_commits
|
30
|
+
assert_instance_of CommitCollection, commits
|
31
|
+
assert_equal 460, commits.size
|
32
|
+
assert_equal '1b2fe77', commits.first.id
|
33
|
+
assert_equal '80f136f', commits.last.id
|
34
|
+
end
|
35
|
+
|
36
|
+
should 'allow to get the commits authored by a single of those actors' do
|
37
|
+
commits = @authors.authored_commits 'tom@mojombo.com'
|
38
|
+
assert_instance_of CommitCollection, commits
|
39
|
+
assert_equal 173, commits.size
|
40
|
+
assert_equal 'a3c5139', commits.first.id
|
41
|
+
assert_equal '634396b', commits.last.id
|
42
|
+
end
|
43
|
+
|
44
|
+
should 'allow to get all the commits committed by those actors' do
|
45
|
+
commits = @authors.committed_commits
|
46
|
+
assert_instance_of CommitCollection, commits
|
47
|
+
assert_equal 460, commits.size
|
48
|
+
assert_equal '1b2fe77', commits.first.id
|
49
|
+
assert_equal '80f136f', commits.last.id
|
50
|
+
end
|
51
|
+
|
52
|
+
should 'allow to get the commits committed by a single of those actors' do
|
53
|
+
commits = @authors.committed_commits 'technoweenie@gmail.com'
|
54
|
+
assert_instance_of CommitCollection, commits
|
55
|
+
assert_equal 55, commits.size
|
56
|
+
assert_equal 'ed1b3ae', commits.first.id
|
57
|
+
assert_equal 'c9cf68f', commits.last.id
|
58
|
+
end
|
59
|
+
|
60
|
+
should 'allow to get the most significant actors' do
|
61
|
+
authors = @authors.most_significant
|
62
|
+
assert_instance_of ActorCollection, authors
|
63
|
+
assert_equal 3, authors.size
|
64
|
+
assert_equal 'tom@mojombo.com', authors.first.id
|
65
|
+
assert_equal 'rsanheim@gmail.com', authors.last.id
|
66
|
+
end
|
67
|
+
|
68
|
+
should 'allow to get the top contributing actors' do
|
69
|
+
authors = @authors.top
|
70
|
+
assert_instance_of ActorCollection, authors
|
71
|
+
assert_equal 3, authors.size
|
72
|
+
assert_equal 'tom@mojombo.com', authors.first.id
|
73
|
+
assert_equal 'technoweenie@gmail.com', authors.last.id
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2011, Sebastian Staudt
|
5
|
+
|
6
|
+
require 'helper'
|
7
|
+
require 'metior/collections/collection'
|
8
|
+
|
9
|
+
class Dummy
|
10
|
+
def id
|
11
|
+
__id__
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class TestCollections < Test::Unit::TestCase
|
16
|
+
|
17
|
+
context 'A collection of objects' do
|
18
|
+
|
19
|
+
setup do
|
20
|
+
@collection = Collection.new
|
21
|
+
@object1 = Dummy.new
|
22
|
+
@object2 = Dummy.new
|
23
|
+
@object3 = Dummy.new
|
24
|
+
@collection << @object1
|
25
|
+
@collection << @object2
|
26
|
+
@collection << @object3
|
27
|
+
end
|
28
|
+
|
29
|
+
should 'have a simple constructor' do
|
30
|
+
collection = Collection.new [@object1, @object2, @object3]
|
31
|
+
assert_equal [@object1.id, @object2.id, @object3.id], collection.keys
|
32
|
+
assert_equal @object1, collection[@object1.id]
|
33
|
+
assert_equal @object2, collection[@object2.id]
|
34
|
+
assert_equal @object3, collection[@object3.id]
|
35
|
+
end
|
36
|
+
|
37
|
+
should 'be a subclass of Hash' do
|
38
|
+
assert_kind_of Hash, @collection
|
39
|
+
assert_kind_of OrderedHash, @collection if RUBY_VERSION.match(/^1\.8/)
|
40
|
+
end
|
41
|
+
|
42
|
+
should 'have a working << operator' do
|
43
|
+
object = Dummy.new
|
44
|
+
@collection << object
|
45
|
+
assert_equal object, @collection[object.id]
|
46
|
+
end
|
47
|
+
|
48
|
+
should 'have a working #each method' do
|
49
|
+
objects = []
|
50
|
+
result = @collection.each { |obj| objects << obj }
|
51
|
+
assert_equal @collection, result
|
52
|
+
assert_equal [@object1, @object2, @object3], objects
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'have a working #last method' do
|
56
|
+
assert_equal @object3, @collection.last
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
@@ -0,0 +1,139 @@
|
|
1
|
+
# This code is free software; you can redistribute it and/or modify it under
|
2
|
+
# the terms of the new BSD License.
|
3
|
+
#
|
4
|
+
# Copyright (c) 2011, Sebastian Staudt
|
5
|
+
|
6
|
+
require 'fixtures'
|
7
|
+
require 'helper'
|
8
|
+
require 'metior/collections/commit_collection'
|
9
|
+
|
10
|
+
class TestCommitCollection < Test::Unit::TestCase
|
11
|
+
|
12
|
+
context 'A collection of commits' do
|
13
|
+
|
14
|
+
setup do
|
15
|
+
repo = Metior::Git::Repository.new File.dirname(File.dirname(__FILE__))
|
16
|
+
@@grit_commits ||= Fixtures.commits_as_grit_commits(''..'master')
|
17
|
+
Grit::Repo.any_instance.stubs(:commits).returns @@grit_commits.values
|
18
|
+
@@grit_commits.each do |id, commit|
|
19
|
+
Grit::Repo.any_instance.stubs(:commit).with(id).returns commit
|
20
|
+
end
|
21
|
+
@commits = repo.commits
|
22
|
+
end
|
23
|
+
|
24
|
+
should 'be an instance of Collection' do
|
25
|
+
assert_kind_of Collection, @commits
|
26
|
+
end
|
27
|
+
|
28
|
+
should 'allow to get all the authors of those commits' do
|
29
|
+
authors = @commits.authors
|
30
|
+
assert_instance_of ActorCollection, authors
|
31
|
+
assert_equal 37, authors.size
|
32
|
+
assert_equal 'rtomayko@gmail.com', authors.first.id
|
33
|
+
assert_equal 'tom@taco.(none)', authors.last.id
|
34
|
+
end
|
35
|
+
|
36
|
+
should 'allow to get the author of a single of those commits' do
|
37
|
+
authors = @commits.authors '4c592b4'
|
38
|
+
assert_instance_of ActorCollection, authors
|
39
|
+
assert_equal 1, authors.size
|
40
|
+
assert_equal 'bobbywilson0@gmail.com', authors.first.id
|
41
|
+
end
|
42
|
+
|
43
|
+
should 'allow to get all commits of a specific author' do
|
44
|
+
commits = @commits.by 'rtomayko@gmail.com'
|
45
|
+
assert_instance_of CommitCollection, commits
|
46
|
+
assert_equal 47, commits.size
|
47
|
+
assert_equal '1b2fe77', commits.first.id
|
48
|
+
assert_equal 'd731f85', commits.last.id
|
49
|
+
end
|
50
|
+
|
51
|
+
should 'allow to get all commits of a some authors' do
|
52
|
+
commits = @commits.by 'tom@mojombo.com', 'rtomayko@gmail.com'
|
53
|
+
assert_instance_of CommitCollection, commits
|
54
|
+
assert_equal 220, commits.size
|
55
|
+
assert_equal '1b2fe77', commits.first.id
|
56
|
+
assert_equal '634396b', commits.last.id
|
57
|
+
end
|
58
|
+
|
59
|
+
should 'allow to get all commits after a given date' do
|
60
|
+
commits = @commits.after '31-12-2010'
|
61
|
+
assert_instance_of CommitCollection, commits
|
62
|
+
assert_equal 29, commits.size
|
63
|
+
assert_equal '1b2fe77', commits.first.id
|
64
|
+
assert_equal '696761d', commits.last.id
|
65
|
+
end
|
66
|
+
|
67
|
+
should 'allow to get all commits before a given date' do
|
68
|
+
commits = @commits.before '31-12-2009'
|
69
|
+
assert_instance_of CommitCollection, commits
|
70
|
+
assert_equal 325, commits.size
|
71
|
+
assert_equal '2f1f63e', commits.first.id
|
72
|
+
assert_equal '634396b', commits.last.id
|
73
|
+
end
|
74
|
+
|
75
|
+
should 'allow to get all the committers of those commits' do
|
76
|
+
committers = @commits.committers
|
77
|
+
assert_instance_of ActorCollection, committers
|
78
|
+
assert_equal 29, committers.size
|
79
|
+
assert_equal 'rtomayko@gmail.com', committers.first.id
|
80
|
+
assert_equal 'tom@taco.(none)', committers.last.id
|
81
|
+
end
|
82
|
+
|
83
|
+
should 'allow to get the committers of a single of those commits' do
|
84
|
+
committers = @commits.committers '4c592b4'
|
85
|
+
assert_instance_of ActorCollection, committers
|
86
|
+
assert_equal 1, committers.size
|
87
|
+
assert_equal 'bobbywilson0@gmail.com', committers.first.id
|
88
|
+
end
|
89
|
+
|
90
|
+
should 'allow to get the activity statistics of those commits' do
|
91
|
+
activity = @commits.activity
|
92
|
+
assert_instance_of Hash, activity
|
93
|
+
assert_instance_of Hash, activity[:active_days]
|
94
|
+
assert_equal 157, activity[:active_days].size
|
95
|
+
assert activity[:active_days].keys.all? { |k| k.is_a? Time }
|
96
|
+
assert activity[:active_days].values.all? { |v| v.is_a? Fixnum }
|
97
|
+
assert_in_delta 2.9299, activity[:commits_per_active_day], 0.0001
|
98
|
+
assert_equal Time.at(1191997100), activity[:first_commit_date]
|
99
|
+
assert_equal Time.at(1306794294), activity[:last_commit_date]
|
100
|
+
assert_equal Time.parse('14-02-2009 00:00 +0000'), activity[:most_active_day]
|
101
|
+
end
|
102
|
+
|
103
|
+
should 'allow to get all commits changing specified files' do
|
104
|
+
commits = @commits.changing('lib/grit.rb')
|
105
|
+
assert_instance_of CommitCollection, commits
|
106
|
+
assert_equal 57, commits.size
|
107
|
+
assert_equal '18bfda9', commits.first.id
|
108
|
+
assert_equal '634396b', commits.last.id
|
109
|
+
end
|
110
|
+
|
111
|
+
should 'allow to get all commits with a minimum impact' do
|
112
|
+
commits = @commits.with_impact(100)
|
113
|
+
assert_instance_of CommitCollection, commits
|
114
|
+
assert_equal 55, commits.size
|
115
|
+
assert_equal 'ef2870b', commits.first.id
|
116
|
+
assert_equal 'a47fd41', commits.last.id
|
117
|
+
end
|
118
|
+
|
119
|
+
should 'allow to get the most significant commits' do
|
120
|
+
commits = @commits.most_significant(5)
|
121
|
+
assert_instance_of CommitCollection, commits
|
122
|
+
assert_equal 5, commits.size
|
123
|
+
assert_equal 'f91f3c8', commits.first.id
|
124
|
+
assert_equal '3c230a3', commits.last.id
|
125
|
+
end
|
126
|
+
|
127
|
+
should 'allow to get the line stats of the commits' do
|
128
|
+
assert_equal 25546, @commits.additions
|
129
|
+
assert_equal 6359, @commits.deletions
|
130
|
+
end
|
131
|
+
|
132
|
+
should 'lazy load the line stats of the commits' do
|
133
|
+
@commits.expects(:load_line_stats).once
|
134
|
+
@commits.additions
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|