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.
Files changed (53) hide show
  1. data/.travis.yml +2 -0
  2. data/.yardopts +1 -0
  3. data/Changelog.md +23 -0
  4. data/Gemfile +1 -17
  5. data/Gemfile.lock +28 -21
  6. data/README.md +66 -20
  7. data/lib/metior.rb +30 -14
  8. data/lib/metior/actor.rb +28 -22
  9. data/lib/metior/auto_include_vcs.rb +43 -0
  10. data/lib/metior/collections/actor_collection.rb +97 -0
  11. data/lib/metior/collections/collection.rb +84 -0
  12. data/lib/metior/collections/commit_collection.rb +309 -0
  13. data/lib/metior/commit.rb +128 -48
  14. data/lib/metior/errors.rb +2 -2
  15. data/lib/metior/git/commit.rb +32 -31
  16. data/lib/metior/git/repository.rb +71 -6
  17. data/lib/metior/github/commit.rb +5 -16
  18. data/lib/metior/github/repository.rb +68 -40
  19. data/lib/metior/report.rb +139 -0
  20. data/lib/metior/report/view.rb +120 -0
  21. data/lib/metior/report/view_helper.rb +47 -0
  22. data/lib/metior/repository.rb +225 -56
  23. data/lib/metior/vcs.rb +12 -3
  24. data/lib/metior/version.rb +1 -1
  25. data/metior.gemspec +28 -26
  26. data/reports/default.rb +17 -0
  27. data/reports/default/images/favicon.png +0 -0
  28. data/reports/default/stylesheets/default.css +128 -0
  29. data/reports/default/templates/actor/minimal.mustache +1 -0
  30. data/reports/default/templates/commit/minimal.mustache +1 -0
  31. data/reports/default/templates/index.mustache +27 -0
  32. data/reports/default/templates/most_significant_authors.mustache +11 -0
  33. data/reports/default/templates/most_significant_commits.mustache +13 -0
  34. data/reports/default/templates/repository_information.mustache +17 -0
  35. data/reports/default/templates/top_committers.mustache +11 -0
  36. data/reports/default/views/index.rb +33 -0
  37. data/reports/default/views/most_significant_authors.rb +19 -0
  38. data/reports/default/views/most_significant_commits.rb +19 -0
  39. data/reports/default/views/repository_information.rb +47 -0
  40. data/reports/default/views/top_committers.rb +21 -0
  41. data/test/fixtures.rb +54 -36
  42. data/test/helper.rb +10 -3
  43. data/test/{test_class_loading.rb → test_1st_class_loading.rb} +1 -1
  44. data/test/test_actor_colletion.rb +78 -0
  45. data/test/test_collection.rb +61 -0
  46. data/test/test_commit_collection.rb +139 -0
  47. data/test/test_git.rb +58 -5
  48. data/test/test_github.rb +52 -9
  49. data/test/test_metior.rb +22 -1
  50. data/test/test_report.rb +49 -0
  51. data/test/test_repository.rb +46 -9
  52. data/test/test_vcs.rb +36 -13
  53. metadata +105 -43
@@ -3,7 +3,7 @@
3
3
  #
4
4
  # Copyright (c) 2011, Sebastian Staudt
5
5
 
6
- require 'rash'
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 /^[AMD]\0/
27
- commit[:files] = [] unless commit.key? :files
28
- commit[:files] << line.split("\0")
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
- commits.map do |commit|
64
- grit_commit = Grit::Commit.new(
65
- nil,
66
- commit[:ids].first,
67
- [],
68
- nil,
69
- Grit::Actor.new(commit[:info][4], commit[:info][5]),
70
- Time.at(commit[:info][6].to_i),
71
- Grit::Actor.new(commit[:info][1], commit[:info][2]),
72
- Time.at(commit[:info][3].to_i),
73
- commit[:info].first.lines.to_a
74
- )
75
-
76
- grit_commit.stubs(:diffs).returns []
77
-
78
- stats = Hashie::Rash.new
79
- if commit.key? :impact
80
- stats.additions = commit[:impact].first.to_i
81
- stats.deletions = commit[:impact].last.to_i
82
- else
83
- stats.additions = 0
84
- stats.deletions = 0
85
- end
86
- grit_commit.stubs(:stats).returns stats
87
-
88
- grit_commit
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
@@ -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
 
@@ -5,7 +5,7 @@
5
5
 
6
6
  require 'helper'
7
7
 
8
- class TestClassLoading < Test::Unit::TestCase
8
+ class Test1stClassLoading < Test::Unit::TestCase
9
9
 
10
10
  context 'Metior' do
11
11
 
@@ -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