gitstats-ruby 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. data/.gitignore +3 -0
  2. data/LICENSE +674 -0
  3. data/README.markdown +53 -0
  4. data/bin/gitstats +176 -0
  5. data/gitstats-ruby.gemspec +22 -0
  6. data/lib/gitstats.rb +18 -0
  7. data/lib/gitstats/author.rb +41 -0
  8. data/lib/gitstats/git.rb +158 -0
  9. data/lib/gitstats/renderer.rb +30 -0
  10. data/lib/gitstats/renderer/gnuplot.rb +150 -0
  11. data/lib/gitstats/renderer/haml.rb +70 -0
  12. data/lib/gitstats/renderer/sass.rb +33 -0
  13. data/lib/gitstats/statgen.rb +116 -0
  14. data/lib/gitstats/stats.rb +12 -0
  15. data/lib/gitstats/stats/commit.rb +48 -0
  16. data/lib/gitstats/stats/commit/author.rb +17 -0
  17. data/lib/gitstats/stats/commit/time.rb +92 -0
  18. data/lib/gitstats/stats/file.rb +15 -0
  19. data/lib/gitstats/stats/file/filetype.rb +14 -0
  20. data/lib/gitstats/yearmonth.rb +39 -0
  21. data/template/activity.haml +3 -0
  22. data/template/asc.gif +0 -0
  23. data/template/authors.haml +2 -0
  24. data/template/bg.gif +0 -0
  25. data/template/commits_per_hour.plot +14 -0
  26. data/template/commits_per_month.plot +15 -0
  27. data/template/commits_per_wday.plot +15 -0
  28. data/template/commits_per_year.plot +11 -0
  29. data/template/commits_per_yearmonth.plot +11 -0
  30. data/template/desc.gif +0 -0
  31. data/template/filechanges_by_yearmonth.plot +17 -0
  32. data/template/files.haml +2 -0
  33. data/template/files_by_yearmonth.plot +11 -0
  34. data/template/helpers/block.rb +26 -0
  35. data/template/helpers/config.rb +12 -0
  36. data/template/helpers/names.rb +76 -0
  37. data/template/helpers/utils.rb +16 -0
  38. data/template/index.haml +2 -0
  39. data/template/jquery.js +4 -0
  40. data/template/jquery.tablesorter.js +4 -0
  41. data/template/lastweeks.plot +14 -0
  42. data/template/layouts/default.haml +43 -0
  43. data/template/linechanges_by_yearmonth.plot +17 -0
  44. data/template/lines.haml +2 -0
  45. data/template/lines_by_yearmonth.plot +11 -0
  46. data/template/partials/authors.haml +30 -0
  47. data/template/partials/blockheader.haml +2 -0
  48. data/template/partials/blocktoc.haml +7 -0
  49. data/template/partials/commits_per_month.haml +27 -0
  50. data/template/partials/commits_per_year.haml +27 -0
  51. data/template/partials/commits_per_yearmonth.haml +27 -0
  52. data/template/partials/day_of_week.haml +28 -0
  53. data/template/partials/filechanges_by_yearmonth.haml +3 -0
  54. data/template/partials/files_by_yearmonth.haml +3 -0
  55. data/template/partials/filetypes.haml +23 -0
  56. data/template/partials/general.haml +34 -0
  57. data/template/partials/hour_of_day.haml +28 -0
  58. data/template/partials/hour_of_week.haml +24 -0
  59. data/template/partials/lastweeks.haml +24 -0
  60. data/template/partials/linechanges_by_yearmonth.haml +3 -0
  61. data/template/partials/lines_by_yearmonth.haml +3 -0
  62. data/template/partials/repos.haml +13 -0
  63. data/template/partials/top_authors_of_year.haml +30 -0
  64. data/template/partials/top_authors_of_yearmonth.haml +30 -0
  65. data/template/style.scss +132 -0
  66. metadata +187 -0
@@ -0,0 +1,30 @@
1
+ class Renderer
2
+ def self.register(cls)
3
+ @@renderers ||= Array.new
4
+ @@renderers << cls
5
+ end
6
+
7
+ def initialize(templatedir, outdir, verbose)
8
+ @templatedir = templatedir
9
+ @outdir = outdir
10
+ @verbose = verbose
11
+ @renderers = @@renderers.map { |x| x.new(templatedir, outdir, verbose) }
12
+ end
13
+
14
+ def render(stats)
15
+ Dir.chdir(@templatedir) { Dir.glob('*').sort }.each do |file|
16
+ next unless File.file?(File.join(@templatedir, file))
17
+
18
+ r = @renderers.find { |r| r.handle?(file) }
19
+
20
+ if r.nil?
21
+ puts "copying '#{file}' ..." if @verbose
22
+ File.copy(File.join(@templatedir, file), @outdir)
23
+ else
24
+ puts "rendering '#{file}' using #{r.name} ..." if @verbose
25
+ r.render(file, stats)
26
+ end
27
+ end
28
+ end
29
+ end
30
+
@@ -0,0 +1,150 @@
1
+ class GnuplotRenderer
2
+ class PlotHelper
3
+ class Plotter
4
+ attr_reader :plot
5
+
6
+ def initialize(helper)
7
+ @helper = helper
8
+ @plot = nil
9
+ end
10
+
11
+ def run
12
+ Gnuplot.open do |gp|
13
+ Gnuplot::Plot.new(gp) do |plot|
14
+ @plot = plot
15
+
16
+ plot.terminal 'png transparent size 640,240'
17
+ plot.size '1.0,1.0'
18
+ plot.output File.join(@helper.outdir, File.basename(@helper.filename, '.plot') + '.png')
19
+ plot.nokey
20
+ plot.xtics 'rotate'
21
+ plot.ytics 'autofreq'
22
+ plot.grid 'y'
23
+
24
+ yield self
25
+ end
26
+ end
27
+ end
28
+
29
+ def add_boxes(args = {})
30
+ args = {
31
+ :setrange => true,
32
+ :limitlabels => true,
33
+ :labelcount => 15
34
+ }.merge(args)
35
+
36
+ x = Array.new
37
+ l = Array.new
38
+ y = Array.new
39
+
40
+ yield x, l, y
41
+
42
+ limitlabels(l, args[:labelcount]) if args[:limitlabels]
43
+
44
+ @plot.xrange "[\"#{x.first - 1}\":\"#{x.last + 1}\"]" if args[:setrange]
45
+
46
+ @plot.data << Gnuplot::DataSet.new([x, l, y]) do |ds|
47
+ ds.using = '1:3:(0.5):xtic(2)'
48
+ ds.with = 'boxes fs solid'
49
+ ds.notitle
50
+ end
51
+ end
52
+
53
+ def add_steps(args = {})
54
+ args = {
55
+ :setrange => true,
56
+ :limitlabels => true,
57
+ :labelcount => 15,
58
+ :firstlabel => '""'
59
+ }.merge(args)
60
+
61
+ x = Array.new
62
+ l = Array.new
63
+ y = Array.new
64
+
65
+ yield x, l, y
66
+
67
+ limitlabels(l, args[:labelcount]) if args[:limitlabels]
68
+
69
+ @plot.xrange "[\"#{x.first - 1}\":\"#{x.last + 1}\"]" if args[:setrange]
70
+
71
+ unless args[:firstlabel].nil?
72
+ x.insert(0, x.first - 1)
73
+ l.insert(0, args[:firstlabel])
74
+ y.insert(0, 0)
75
+ end
76
+
77
+ @plot.data << Gnuplot::DataSet.new([x, l, y]) do |ds|
78
+ ds.using = '1:3:xtic(2)'
79
+ ds.with = 'steps'
80
+ ds.notitle
81
+ end
82
+ end
83
+
84
+ private
85
+ def limitlabels(l, maxcount)
86
+ cnt = l.size
87
+ step = (cnt > maxcount) ? (cnt / maxcount + 0.5).round.to_i : 1
88
+
89
+ i = 0
90
+ l.map! do |e|
91
+ unless ((i % step) == 0) || (i == (cnt - 1))
92
+ e = '""'
93
+ end
94
+ i += 1
95
+ e
96
+ end
97
+ l
98
+ end
99
+ end
100
+
101
+ attr_reader :filename
102
+ attr_reader :outdir
103
+ attr_reader :stats
104
+ attr_reader :verbose
105
+
106
+ def initialize(templatedir, filename, outdir, stats, verbose)
107
+ @filename = filename
108
+ @outdir = outdir
109
+ @stats = stats
110
+ @verbose = verbose
111
+
112
+ Dir.glob(File.join(templatedir, 'helpers', '*.rb')).sort.each do |file|
113
+ eval(IO::readlines(file).join(''))
114
+ end
115
+ end
116
+
117
+ def run(lines)
118
+ eval(lines)
119
+ end
120
+
121
+ def defplot(&block)
122
+ plotter = Plotter.new(self)
123
+ plotter.run(&block)
124
+ end
125
+ end
126
+
127
+ def initialize(templatedir, outdir, verbose)
128
+ @templatedir = templatedir
129
+ @outdir = outdir
130
+ @verbose = verbose
131
+ end
132
+
133
+ def name
134
+ 'gnuplot'
135
+ end
136
+
137
+ def handle?(file)
138
+ file =~ /\.plot$/
139
+ end
140
+
141
+ def render(file, stats)
142
+ ifile = File.join(@templatedir, file)
143
+
144
+ lines = IO::readlines(ifile).join('')
145
+
146
+ PlotHelper.new(@templatedir, file, @outdir, stats, @verbose).run(lines)
147
+ end
148
+ end
149
+
150
+ Renderer.register GnuplotRenderer
@@ -0,0 +1,70 @@
1
+ class HamlRenderer
2
+ class HamlHelper
3
+ attr_reader :stats
4
+
5
+ def initialize(templatedir, stats, verbose)
6
+ @templatedir = templatedir
7
+ @stats = stats
8
+ @verbose = verbose
9
+ @layout = nil
10
+
11
+ Dir.glob(File.join(templatedir, 'helpers', '*.rb')).sort.each do |file|
12
+ eval(IO::readlines(file).join(''))
13
+ end
14
+ end
15
+
16
+ def partial(name, hash = {})
17
+ name = name.to_s
18
+ puts "rendering partial '#{name}' ..." if @verbose
19
+ hash = hash.to_h unless hash.is_a? Hash
20
+ lines = IO::readlines(File.join(@templatedir, 'partials', "#{name}.haml")).join('')
21
+ engine = Haml::Engine.new(lines)
22
+ engine.render(self, hash)
23
+ end
24
+
25
+ def layout(name)
26
+ @layout = name.to_s
27
+ end
28
+
29
+ def get_layout
30
+ @layout
31
+ end
32
+ end
33
+
34
+ def initialize(templatedir, outdir, verbose)
35
+ @templatedir = templatedir
36
+ @outdir = outdir
37
+ @verbose = verbose
38
+ end
39
+
40
+ def name
41
+ 'haml'
42
+ end
43
+
44
+ def handle?(file)
45
+ file =~ /\.haml$/
46
+ end
47
+
48
+ def render(file, stats)
49
+ ifile = File.join(@templatedir, file)
50
+ ofile = File.join(@outdir, File.basename(file, '.haml') + '.html')
51
+
52
+ lines = IO::readlines(ifile).join('')
53
+
54
+ helper = HamlHelper.new(@templatedir, stats, @verbose)
55
+
56
+ engine = Haml::Engine.new(lines)
57
+ lines = engine.render(helper)
58
+
59
+ if !helper.get_layout.nil?
60
+ puts "rendering layout '#{helper.get_layout}' ..." if @verbose
61
+ layout = IO::readlines(File.join(@templatedir, 'layouts', helper.get_layout + '.haml')).join('')
62
+ engine = Haml::Engine.new(layout)
63
+ lines = engine.render(Object.new, :content => lines)
64
+ end
65
+
66
+ File.new(ofile, 'w').write(lines)
67
+ end
68
+ end
69
+
70
+ Renderer.register HamlRenderer
@@ -0,0 +1,33 @@
1
+ class SassRenderer
2
+ def initialize(templatedir, outdir, verbose)
3
+ @templatedir = templatedir
4
+ @outdir = outdir
5
+ @verbose = verbose
6
+ end
7
+
8
+ def name
9
+ 'sass/compass'
10
+ end
11
+
12
+ def handle?(file)
13
+ (file =~ /\.sass$/) || (file =~ /\.scss$/)
14
+ end
15
+
16
+ def render(file, stats)
17
+ scss = file =~ /\.scss$/
18
+
19
+ ifile = File.join(@templatedir, file)
20
+ ofile = File.join(@outdir, File.basename(file, scss ? '.scss' : '.sass') + '.css')
21
+
22
+ lines = IO::readlines(ifile).join('')
23
+
24
+ options = Compass.sass_engine_options
25
+ options[:syntax] = scss ? :scss : :sass
26
+ engine = Sass::Engine.new(lines, options)
27
+ lines = engine.render
28
+
29
+ File.new(ofile, 'w').write(lines)
30
+ end
31
+ end
32
+
33
+ Renderer.register SassRenderer
@@ -0,0 +1,116 @@
1
+ class StatGen
2
+ attr_accessor :verbose
3
+ attr_accessor :debug
4
+ attr_accessor :quiet
5
+ attr_accessor :future
6
+ attr_accessor :maxage
7
+ attr_accessor :commitcache
8
+ attr_accessor :include_mail
9
+ attr_reader :repos
10
+
11
+ attr_reader :num_commits
12
+
13
+ attr_reader :general_stats
14
+ attr_reader :author_stats
15
+ attr_reader :year_stats
16
+ attr_reader :month_stats
17
+ attr_reader :yearmonth_stats
18
+ attr_reader :hour_stats
19
+ attr_reader :wday_stats
20
+ attr_reader :lastweeks_stats
21
+
22
+ attr_reader :file_stats
23
+ attr_reader :filetype_stats
24
+
25
+ def initialize
26
+ @repos = Array.new
27
+ @repostate = Hash.new
28
+
29
+ @verbose = false
30
+ @debug = false
31
+ @quiet = false
32
+ @future = true
33
+ @maxage = 0
34
+ @commitcache = nil
35
+ @include_mail = false
36
+
37
+ @num_commits = 0
38
+ @general_stats = CommitStats.new
39
+ @author_stats = AuthorsCommitStats.new
40
+ @year_stats = YearCommitStats.new
41
+ @month_stats = MonthCommitStats.new
42
+ @yearmonth_stats = YearMonthCommitStats.new
43
+ @hour_stats = HourCommitStats.new
44
+ @wday_stats = DayOfWeekCommitStats.new
45
+ @lastweeks_stats = LastWeeksCommitStats.new
46
+
47
+ @file_stats = FileStats.new
48
+ @filetype_stats = FileTypeFileStats.new
49
+ end
50
+
51
+ def num_authors
52
+ @author_stats.size
53
+ end
54
+
55
+ def clear_repos
56
+ @repos = Array.new
57
+ end
58
+
59
+ def check_repostate
60
+ @repostate.keys.each do |name|
61
+ return false if @repos.find { |x| x.name == name }.nil?
62
+ end
63
+ true
64
+ end
65
+
66
+ def add(name, directory, ref = 'HEAD')
67
+ cache = nil
68
+ unless @commitcache.nil?
69
+ cache = File.join(@commitcache, ".commitcache.#{name.tr('^a-zA-Z0-9_', '_')}")
70
+ end
71
+ @repos << Git.new(name, directory, ref, @debug, cache)
72
+ end
73
+
74
+ def <<(value)
75
+ add(value[0], value[1], value[2])
76
+ end
77
+
78
+ def calc
79
+ # reset because of caching for now
80
+ @file_stats = FileStats.new
81
+ @filetype_stats = FileTypeFileStats.new
82
+
83
+ @repos.each do |repo|
84
+ @repostate[repo.name] ||= {
85
+ :last => nil
86
+ }
87
+
88
+ puts " repository '#{repo.name}' ..." unless @quiet
89
+
90
+ repo.get_commits(@repostate[repo.name][:last]) do |commit|
91
+ next if !@future && (commit[:time] > Time.now)
92
+ next if (@maxage > 0) && ((Time.now - commit[:time]) > @maxage)
93
+
94
+ puts " commit #{@num_commits} ..." if @verbose && ((@num_commits % 100) == 0)
95
+
96
+ @num_commits += 1
97
+ @general_stats.update(commit)
98
+ @author_stats.update(commit)
99
+ @year_stats.update(commit)
100
+ @month_stats.update(commit)
101
+ @yearmonth_stats.update(commit)
102
+ @hour_stats.update(commit)
103
+ @wday_stats.update(commit)
104
+ @lastweeks_stats.update(commit)
105
+
106
+ @repostate[repo.name][:last] = commit[:hash]
107
+ end
108
+
109
+ repo.get_files do |file|
110
+ @file_stats.update(file)
111
+ @filetype_stats.update(file)
112
+ end
113
+ end
114
+ end
115
+ end
116
+
@@ -0,0 +1,12 @@
1
+ module StatsHash
2
+ def method_missing(method, *args, &block)
3
+ @hash.send(method, *args, &block)
4
+ end
5
+
6
+ def each_sorted
7
+ @hash.keys.sort.each do |key|
8
+ yield key, @hash[key]
9
+ end
10
+ end
11
+ end
12
+
@@ -0,0 +1,48 @@
1
+ class CommitStats
2
+ attr_reader :commits
3
+ attr_reader :files_added
4
+ attr_reader :files_deleted
5
+ attr_reader :lines_added
6
+ attr_reader :lines_deleted
7
+ attr_reader :files
8
+ attr_reader :lines
9
+ attr_reader :first_commit
10
+ attr_reader :last_commit
11
+
12
+ def days
13
+ @days.size
14
+ end
15
+
16
+ def initialize
17
+ @commits = 0
18
+ @files_added = 0
19
+ @files_deleted = 0
20
+ @lines_added = 0
21
+ @lines_deleted = 0
22
+ @files = 0
23
+ @lines = 0
24
+ @first_commit = nil
25
+ @last_commit = nil
26
+ @days = Array.new
27
+ end
28
+
29
+ def update(commit)
30
+ @commits += 1
31
+ @files_added += commit[:files_added]
32
+ @files_deleted += commit[:files_deleted]
33
+ @lines_added += commit[:lines_added]
34
+ @lines_deleted += commit[:lines_deleted]
35
+ @files = @files_added - @files_deleted
36
+ @lines = @lines_added - @lines_deleted
37
+
38
+ @first_commit ||= commit[:time]
39
+ @last_commit ||= commit[:time]
40
+
41
+ @first_commit = commit[:time] if commit[:time] < @first_commit
42
+ @last_commit = commit[:time] if commit[:time] > @last_commit
43
+
44
+ day = commit[:time].year * 10000 + commit[:time].month * 100 + commit[:time].day
45
+ @days << day unless @days.include? day
46
+ end
47
+ end
48
+