vclog 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/COPYING +674 -0
- data/HISTORY +20 -0
- data/MANIFEST +32 -0
- data/README +38 -0
- data/RELEASE +11 -0
- data/bin/vclog +131 -0
- data/lib/vclog/changelog.rb +484 -0
- data/lib/vclog/core_ext.rb +106 -0
- data/lib/vclog/vcs/darcs.rb +83 -0
- data/lib/vclog/vcs/git.rb +54 -0
- data/lib/vclog/vcs/hg.rb +0 -0
- data/lib/vclog/vcs/svn.rb +58 -0
- data/lib/vclog/vcs.rb +82 -0
- data/meta/authors +1 -0
- data/meta/created +1 -0
- data/meta/description +1 -0
- data/meta/homepage +1 -0
- data/meta/license +1 -0
- data/meta/mailinglist +1 -0
- data/meta/package +1 -0
- data/meta/project +1 -0
- data/meta/repository +2 -0
- data/meta/requires +1 -0
- data/meta/sitemap +1 -0
- data/meta/summary +1 -0
- data/meta/title +1 -0
- data/meta/version +1 -0
- metadata +98 -0
data/HISTORY
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
= CHANGE HISTORY
|
2
|
+
|
3
|
+
== 1.0.0 / 2009-08-03
|
4
|
+
|
5
|
+
This is the first "production" release of VCLog.
|
6
|
+
|
7
|
+
* 2 Major Enhancements
|
8
|
+
|
9
|
+
* Improved command line interface.
|
10
|
+
* Added output option to save changelog.
|
11
|
+
|
12
|
+
|
13
|
+
== 0.1.0 / 2006-06-05
|
14
|
+
|
15
|
+
This is the initial version of vclog.
|
16
|
+
|
17
|
+
* 1 Major Enhancement
|
18
|
+
|
19
|
+
* Happy Birthday
|
20
|
+
|
data/MANIFEST
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
test
|
2
|
+
RELEASE
|
3
|
+
README
|
4
|
+
HISTORY
|
5
|
+
meta
|
6
|
+
meta/created
|
7
|
+
meta/repository
|
8
|
+
meta/homepage
|
9
|
+
meta/summary
|
10
|
+
meta/package
|
11
|
+
meta/title
|
12
|
+
meta/version
|
13
|
+
meta/license
|
14
|
+
meta/sitemap
|
15
|
+
meta/mailinglist
|
16
|
+
meta/authors
|
17
|
+
meta/requires
|
18
|
+
meta/project
|
19
|
+
meta/description
|
20
|
+
lib
|
21
|
+
lib/vclog
|
22
|
+
lib/vclog/changelog.rb
|
23
|
+
lib/vclog/core_ext.rb
|
24
|
+
lib/vclog/vcs
|
25
|
+
lib/vclog/vcs/darcs.rb
|
26
|
+
lib/vclog/vcs/git.rb
|
27
|
+
lib/vclog/vcs/svn.rb
|
28
|
+
lib/vclog/vcs/hg.rb
|
29
|
+
lib/vclog/vcs.rb
|
30
|
+
bin
|
31
|
+
bin/vclog
|
32
|
+
COPYING
|
data/README
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
= VCLog
|
2
|
+
|
3
|
+
* http://proutils.rubyforge.org
|
4
|
+
* http://proutils.rubyforge.org/vclog
|
5
|
+
|
6
|
+
|
7
|
+
== DESCRIPTION
|
8
|
+
|
9
|
+
VClog is an cross-VCS/SCM changelog generator.
|
10
|
+
|
11
|
+
|
12
|
+
== SYNOPSIS
|
13
|
+
|
14
|
+
VCLog generates changelogs. It supports a few different formats. The default
|
15
|
+
format is standard GNU text style. From a repository's root directory try:
|
16
|
+
|
17
|
+
$ vclog
|
18
|
+
|
19
|
+
To generate an XML formatted changelog use:
|
20
|
+
|
21
|
+
$ vclog --xml
|
22
|
+
|
23
|
+
See 'vclog --help' for more options.
|
24
|
+
|
25
|
+
|
26
|
+
== RELEASE NOTES
|
27
|
+
|
28
|
+
Please see RELEASE file.
|
29
|
+
|
30
|
+
|
31
|
+
== LICENSE & COPYRIGHT
|
32
|
+
|
33
|
+
VCLog
|
34
|
+
|
35
|
+
Copyright (c) 2008,2009 TigerOps.org
|
36
|
+
|
37
|
+
VCLog is distributed under the terms of the GPLv3 license.
|
38
|
+
|
data/RELEASE
ADDED
data/bin/vclog
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
# vclog
|
4
|
+
#
|
5
|
+
# SYNOPSIS
|
6
|
+
#
|
7
|
+
# VCLog provides cross-vcs ChangeLogs. It works by
|
8
|
+
# parsing the native changelog a VCS system produces
|
9
|
+
# into a common model, which then can be used to
|
10
|
+
# produce Changelogs in a variety of formats.
|
11
|
+
#
|
12
|
+
# VCLog currently support SVN and Git. CVS, Darcs and
|
13
|
+
# Mercurial/Hg are in the works.
|
14
|
+
#
|
15
|
+
# EXAMPLES
|
16
|
+
#
|
17
|
+
# To produce a GNU-like changelog:
|
18
|
+
#
|
19
|
+
# $ vclog
|
20
|
+
#
|
21
|
+
# For XML format:
|
22
|
+
#
|
23
|
+
# $ vclog --xml
|
24
|
+
#
|
25
|
+
# Or for a micorformat-ish HTML:
|
26
|
+
#
|
27
|
+
# $ vclog --html
|
28
|
+
#
|
29
|
+
#To use the library programmatically, please see the API documentation.
|
30
|
+
#
|
31
|
+
# LICENSE
|
32
|
+
#
|
33
|
+
# VCLog Copyright (c) 2008 Tiger Ops
|
34
|
+
# VCLog is distributed under the terms of the GPLv3.
|
35
|
+
|
36
|
+
require 'vclog/vcs'
|
37
|
+
require 'getoptlong'
|
38
|
+
|
39
|
+
# TODO: rev option.
|
40
|
+
#
|
41
|
+
def vclog
|
42
|
+
|
43
|
+
opts = GetoptLong.new(
|
44
|
+
[ '--help' , '-h', GetoptLong::NO_ARGUMENT ],
|
45
|
+
[ '--debug', GetoptLong::NO_ARGUMENT ],
|
46
|
+
[ '--typed', GetoptLong::NO_ARGUMENT ],
|
47
|
+
[ '--rev' , GetoptLong::NO_ARGUMENT ],
|
48
|
+
[ '--gnu' , GetoptLong::NO_ARGUMENT ],
|
49
|
+
[ '--xml' , GetoptLong::NO_ARGUMENT ],
|
50
|
+
[ '--html' , GetoptLong::NO_ARGUMENT ],
|
51
|
+
[ '--rel' , GetoptLong::REQUIRED_ARGUMENT ],
|
52
|
+
[ '--style', GetoptLong::REQUIRED_ARGUMENT ],
|
53
|
+
[ '--output', '-o', GetoptLong::REQUIRED_ARGUMENT ]
|
54
|
+
)
|
55
|
+
|
56
|
+
format = :gnu
|
57
|
+
typed = false
|
58
|
+
rev = false
|
59
|
+
vers = nil
|
60
|
+
style = nil
|
61
|
+
output = nil
|
62
|
+
|
63
|
+
opts.each do |opt, arg|
|
64
|
+
case opt
|
65
|
+
when '--help'
|
66
|
+
puts "vclog [--gnu|--html|--xml|--rel] [--typed]"
|
67
|
+
exit
|
68
|
+
when '--debug'
|
69
|
+
$DEBUG = true
|
70
|
+
when '--typed'
|
71
|
+
typed = true
|
72
|
+
when '--rev'
|
73
|
+
rev = true
|
74
|
+
when '--xml'
|
75
|
+
format = :xml
|
76
|
+
when '--html'
|
77
|
+
format = :html
|
78
|
+
when '--rel'
|
79
|
+
format = :rel
|
80
|
+
vers = arg
|
81
|
+
when '--style'
|
82
|
+
style = arg
|
83
|
+
when '--output'
|
84
|
+
output = arg
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
vcs = VCLog::VCS.new
|
89
|
+
|
90
|
+
changelog = vcs.changelog
|
91
|
+
|
92
|
+
if typed
|
93
|
+
changelog = changelog.typed
|
94
|
+
end
|
95
|
+
|
96
|
+
case format
|
97
|
+
when :xml
|
98
|
+
log = changelog.format_xml(style) # xsl stylesheet url
|
99
|
+
when :html
|
100
|
+
log = changelog.format_html(style) # css stylesheet url
|
101
|
+
when :rel
|
102
|
+
if output && File.file?(output)
|
103
|
+
file = output
|
104
|
+
else
|
105
|
+
file = Dir.glob('change{s,log}{,.txt}', File::FNM_CASEFOLD).first
|
106
|
+
end
|
107
|
+
log = changelog.format_rel(file, vers)
|
108
|
+
else #:gnu
|
109
|
+
log = changelog.to_s
|
110
|
+
end
|
111
|
+
|
112
|
+
if output
|
113
|
+
File.open(output, 'w') do |f|
|
114
|
+
f << log
|
115
|
+
end
|
116
|
+
else
|
117
|
+
puts log
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
begin
|
123
|
+
vclog
|
124
|
+
rescue => err
|
125
|
+
if $DEBUG
|
126
|
+
raise err
|
127
|
+
else
|
128
|
+
puts err.message
|
129
|
+
exit -1
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,484 @@
|
|
1
|
+
module VCLog
|
2
|
+
|
3
|
+
require 'vclog/core_ext'
|
4
|
+
|
5
|
+
# Supports output formats:
|
6
|
+
#
|
7
|
+
# xml
|
8
|
+
# html
|
9
|
+
# yaml
|
10
|
+
# json
|
11
|
+
# text
|
12
|
+
#
|
13
|
+
class Changelog
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
DAY = 24*60*60
|
17
|
+
|
18
|
+
attr :changes
|
19
|
+
|
20
|
+
def initialize(changes=nil)
|
21
|
+
@changes = changes || []
|
22
|
+
end
|
23
|
+
|
24
|
+
def change(date, who, rev, note)
|
25
|
+
note, type = *split_note(note)
|
26
|
+
note = note.gsub(/^\s*?\n/m,'') # remove blank lines
|
27
|
+
@changes << Entry.new(:when=>date, :who=>who, :rev=>rev, :type=>type, :msg=>note)
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(&block) ; @changes.each(&block) ; end
|
31
|
+
def empty? ; @changes.empty? ; end
|
32
|
+
def size ; @changes.size ; end
|
33
|
+
|
34
|
+
#
|
35
|
+
def <<(entry)
|
36
|
+
raise unless Entry===entry
|
37
|
+
@changes << entry
|
38
|
+
end
|
39
|
+
|
40
|
+
# Return a new changelog with entries that have a specified type.
|
41
|
+
# TODO: Be able to specify which types to include or omit.
|
42
|
+
def typed
|
43
|
+
self.class.new(changes.select{ |e| e.type })
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return a new changelog with entries occuring after the
|
47
|
+
# given date limit.
|
48
|
+
def after(date_limit)
|
49
|
+
after = changes.select{ |entry| entry.date > date_limit + DAY }
|
50
|
+
self.class.new(after)
|
51
|
+
end
|
52
|
+
|
53
|
+
# Return a new changelog with entries occuring before the
|
54
|
+
# given date limit.
|
55
|
+
def before(date_limit)
|
56
|
+
before = changes.select{ |entry| entry.date <= date_limit + DAY }
|
57
|
+
self.class.new(before)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
|
62
|
+
#
|
63
|
+
def by_type
|
64
|
+
mapped = {}
|
65
|
+
changes.each do |entry|
|
66
|
+
mapped[entry.type] ||= self.class.new
|
67
|
+
mapped[entry.type] << entry
|
68
|
+
end
|
69
|
+
mapped
|
70
|
+
end
|
71
|
+
|
72
|
+
#
|
73
|
+
def by_author
|
74
|
+
mapped = {}
|
75
|
+
changes.each do |entry|
|
76
|
+
mapped[entry.author] ||= self.class.new
|
77
|
+
mapped[entry.author] << entry
|
78
|
+
end
|
79
|
+
mapped
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
def by_date
|
84
|
+
mapped = {}
|
85
|
+
changes.each do |entry|
|
86
|
+
mapped[entry.date.strftime('%Y-%m-%d')] ||= self.class.new
|
87
|
+
mapped[entry.date.strftime('%Y-%m-%d')] << entry
|
88
|
+
end
|
89
|
+
mapped = mapped.to_a.sort{ |a,b| b[0] <=> a[0] }
|
90
|
+
mapped
|
91
|
+
end
|
92
|
+
|
93
|
+
#
|
94
|
+
#def by_date
|
95
|
+
# mapped = {}
|
96
|
+
# changes.each do |entry|
|
97
|
+
# mapped[entry.date.strftime('%Y-%m-%d')] ||= self.class.new
|
98
|
+
# mapped[entry.date.strftime('%Y-%m-%d')] << entry
|
99
|
+
# end
|
100
|
+
# mapped
|
101
|
+
#end
|
102
|
+
|
103
|
+
##################
|
104
|
+
# Output Formats #
|
105
|
+
##################
|
106
|
+
|
107
|
+
def format_yaml
|
108
|
+
require 'yaml'
|
109
|
+
changes.to_yaml
|
110
|
+
end
|
111
|
+
|
112
|
+
def format_json
|
113
|
+
require 'json'
|
114
|
+
changes.to_json
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
def format_gnu
|
119
|
+
x = []
|
120
|
+
by_date.each do |date, date_changes|
|
121
|
+
date_changes.by_author.each do |author, author_changes|
|
122
|
+
x << %[#{date} #{author}\n]
|
123
|
+
#author_changes = author_changes.sort{|a,b| b.date <=> a.date}
|
124
|
+
author_changes.each do |entry|
|
125
|
+
if entry.type
|
126
|
+
msg = "#{entry.message} [#{entry.type}]".tabto(10)
|
127
|
+
else
|
128
|
+
msg = "#{entry.message}".tabto(10)
|
129
|
+
end
|
130
|
+
msg[8] = '*'
|
131
|
+
x << msg
|
132
|
+
end
|
133
|
+
end
|
134
|
+
x << "\n"
|
135
|
+
end
|
136
|
+
return x.join("\n")
|
137
|
+
end
|
138
|
+
|
139
|
+
#
|
140
|
+
alias_method :to_s, :format_gnu
|
141
|
+
|
142
|
+
# Create an XML formated changelog.
|
143
|
+
# +xsl+ reference defaults to 'log.xsl'
|
144
|
+
def format_xml(xsl=nil)
|
145
|
+
xsl = 'log.xsl' if xsl.nil?
|
146
|
+
x = []
|
147
|
+
x << %[<?xml version="1.0"?>]
|
148
|
+
x << %[<?xml-stylesheet href="#{xsl}" type="text/xsl" ?>] if xsl
|
149
|
+
x << %[<log>]
|
150
|
+
changes.sort{|a,b| b.date <=> a.date}.each do |entry|
|
151
|
+
x << %[<entry>]
|
152
|
+
x << %[ <date>#{entry.date}</date>]
|
153
|
+
x << %[ <author>#{escxml(entry.author)}</author>]
|
154
|
+
x << %[ <revison>#{escxml(entry.revison)}</revison>]
|
155
|
+
x << %[ <type>#{escxml(entry.type)}</type>]
|
156
|
+
x << %[ <message>#{escxml(entry.message)}</message>]
|
157
|
+
x << %[</entry>]
|
158
|
+
end
|
159
|
+
x << %[</log>]
|
160
|
+
return x.join("\n")
|
161
|
+
end
|
162
|
+
|
163
|
+
# Create an HTML formated changelog.
|
164
|
+
# +css+ reference defaults to 'log.css'
|
165
|
+
def format_html(css=nil)
|
166
|
+
css = 'log.css' if css.nil?
|
167
|
+
x = []
|
168
|
+
x << %[<html>]
|
169
|
+
x << %[<head>]
|
170
|
+
x << %[ <title>Changelog</title>]
|
171
|
+
x << %[ <style>]
|
172
|
+
x << %[ body{font-family: sans-serif;}]
|
173
|
+
x << %[ #changelog{width:800px;margin:0 auto;}]
|
174
|
+
x << %[ li{padding: 10px;}]
|
175
|
+
x << %[ .date{font-weight: bold; color: gray; float: left; padding: 0 5px;}]
|
176
|
+
x << %[ .author{color: red;}]
|
177
|
+
x << %[ .message{padding: 5 0; font-weight: bold;}]
|
178
|
+
x << %[ .revison{font-size: 0.8em;}]
|
179
|
+
x << %[ </style>]
|
180
|
+
x << %[ <link rel="stylesheet" href="#{css}" type="text/css">] if css
|
181
|
+
x << %[</head>]
|
182
|
+
x << %[<body>]
|
183
|
+
x << %[ <div id="changelog">]
|
184
|
+
x << %[ <h1>ChangeLog</h1>]
|
185
|
+
x << %[ <ul class="log">]
|
186
|
+
changes.sort{|a,b| b.date <=> a.date}.each do |entry|
|
187
|
+
x << %[ <li class="entry">]
|
188
|
+
x << %[ <div class="date">#{entry.date}</div>]
|
189
|
+
x << %[ <div class="author">#{escxml(entry.author)}</div>]
|
190
|
+
x << %[ <div class="type">#{escxml(entry.type)}</div>]
|
191
|
+
x << %[ <div class="message">#{escxml(entry.message)}</div>]
|
192
|
+
x << %[ <div class="revison">##{escxml(entry.revison)}</div>]
|
193
|
+
x << %[ </li>]
|
194
|
+
end
|
195
|
+
x << %[ </ul>]
|
196
|
+
x << %[ </div>]
|
197
|
+
x << %[</body>]
|
198
|
+
x << %[</html>]
|
199
|
+
return x.join("\n")
|
200
|
+
end
|
201
|
+
|
202
|
+
#
|
203
|
+
def format_rel(file, current_version=nil, current_release=nil)
|
204
|
+
log = []
|
205
|
+
# collect releases already listed in changelog file
|
206
|
+
rels = releases(file)
|
207
|
+
# add current verion to release list (if given)
|
208
|
+
previous_version = rels[0][0]
|
209
|
+
if current_version < previous_version
|
210
|
+
raise ArgumentError, "Release version is less than previous version (#{previous_version})."
|
211
|
+
end
|
212
|
+
#case current_version
|
213
|
+
#when 'major'
|
214
|
+
# v = previous_verison.split(/\W/)
|
215
|
+
# v[0] = v[0].succ
|
216
|
+
# current_version = v.join('.')
|
217
|
+
#when 'minor'
|
218
|
+
# v = previous_verison.split(/\W/)
|
219
|
+
# v[1] = v[1].succ
|
220
|
+
# current_version = v.join('.')
|
221
|
+
# end
|
222
|
+
#end
|
223
|
+
rels << [current_version, current_release || Time.now]
|
224
|
+
# make sure all release date are Time objects
|
225
|
+
rels = rels.collect{ |v,d| [v, Time===d ? d : Time.parse(d)] }
|
226
|
+
# only uniq releases
|
227
|
+
rels = rels.uniq
|
228
|
+
# sort by release date
|
229
|
+
rels = rels.to_a.sort{ |a,b| a[1] <=> b[1] }
|
230
|
+
# organize into deltas
|
231
|
+
deltas, last = [], nil
|
232
|
+
rels.each do |rel|
|
233
|
+
deltas << [last, rel]
|
234
|
+
last = rel
|
235
|
+
end
|
236
|
+
# gather changes for each delta and build log
|
237
|
+
deltas.each do |gt, lt|
|
238
|
+
if gt
|
239
|
+
gt_vers, gt_date = *gt
|
240
|
+
lt_vers, lt_date = *lt
|
241
|
+
#gt_date = Time.parse(gt_date) unless Time===gt_date
|
242
|
+
#lt_date = Time.parse(lt_date) unless Time===lt_date
|
243
|
+
changes = after(gt_date).before(lt_date)
|
244
|
+
else
|
245
|
+
lt_vers, lt_date = *lt
|
246
|
+
#lt_date = Time.parse(lt_date) unless Time===lt_date
|
247
|
+
changes = before(lt_date)
|
248
|
+
end
|
249
|
+
reltext = changes.format_rel_types
|
250
|
+
unless reltext.strip.empty?
|
251
|
+
log << "== #{lt_vers} / #{lt_date.strftime('%Y-%m-%d')}\n\n#{reltext}"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
# reverse log order and make into document
|
255
|
+
log.reverse.join("\n")
|
256
|
+
end
|
257
|
+
|
258
|
+
#
|
259
|
+
def format_rel_types
|
260
|
+
groups = changes.group_by{ |e| e.type_number }
|
261
|
+
string = ""
|
262
|
+
5.times do |n|
|
263
|
+
entries = groups[n]
|
264
|
+
next if !entries
|
265
|
+
next if entries.empty?
|
266
|
+
string << "* #{entries.size} #{entries[0].type_phrase}\n\n"
|
267
|
+
entries.sort!{|a,b| a.date <=> b.date }
|
268
|
+
entries.each do |entry|
|
269
|
+
#string << "== #{date} #{who}\n\n" # no email :(
|
270
|
+
text = "#{entry.message} (##{entry.revison})"
|
271
|
+
text = text.tabto(6)
|
272
|
+
text[4] = '*'
|
273
|
+
#entry = entry.join(' ').tabto(6)
|
274
|
+
#entry[4] = '*'
|
275
|
+
string << text
|
276
|
+
string << "\n"
|
277
|
+
end
|
278
|
+
string << "\n"
|
279
|
+
end
|
280
|
+
string
|
281
|
+
end
|
282
|
+
|
283
|
+
#
|
284
|
+
def releases(file)
|
285
|
+
return [] unless file
|
286
|
+
clog = File.read(file)
|
287
|
+
tags = clog.scan(/^==(.*?)$/)
|
288
|
+
rels = tags.collect do |t|
|
289
|
+
parse_version_tag(t[0])
|
290
|
+
end
|
291
|
+
return rels
|
292
|
+
end
|
293
|
+
|
294
|
+
#
|
295
|
+
def parse_version_tag(tag)
|
296
|
+
version, date = *tag.split('/')
|
297
|
+
version, date = version.sub(/^\=+\s*/,'').strip, date.strip
|
298
|
+
return version, date
|
299
|
+
end
|
300
|
+
|
301
|
+
###################
|
302
|
+
# Save Chaqngelog #
|
303
|
+
###################
|
304
|
+
|
305
|
+
# Save changelog as file in specified format.
|
306
|
+
def save(file, format=:gnu, *args)
|
307
|
+
case format.to_sym
|
308
|
+
when :xml
|
309
|
+
text = format_xml
|
310
|
+
save_xsl(file)
|
311
|
+
when :html
|
312
|
+
text = format_html(*args)
|
313
|
+
when :rel
|
314
|
+
text = format_rel(file, *args)
|
315
|
+
when :yaml
|
316
|
+
text = format_yaml(file)
|
317
|
+
when :json
|
318
|
+
text = format_json(file)
|
319
|
+
else
|
320
|
+
text = format_gnu
|
321
|
+
end
|
322
|
+
|
323
|
+
FileUtils.mkdir_p(File.dirname(file))
|
324
|
+
|
325
|
+
different = true
|
326
|
+
if File.exist?(file)
|
327
|
+
different = (File.read(file) != text)
|
328
|
+
end
|
329
|
+
|
330
|
+
File.open(file, 'w') do |f|
|
331
|
+
f << text
|
332
|
+
end if different
|
333
|
+
end
|
334
|
+
|
335
|
+
private
|
336
|
+
|
337
|
+
#
|
338
|
+
def escxml(input)
|
339
|
+
result = input.to_s.dup
|
340
|
+
result.gsub!("&", "&")
|
341
|
+
result.gsub!("<", "<")
|
342
|
+
result.gsub!(">", ">")
|
343
|
+
result.gsub!("'", "'")
|
344
|
+
#result.gsub!("@", "&at;")
|
345
|
+
result.gsub!("\"", """)
|
346
|
+
return result
|
347
|
+
end
|
348
|
+
|
349
|
+
#
|
350
|
+
def save_xsl(file)
|
351
|
+
#xslfile = file.chomp(File.extname(file)) + '.xsl'
|
352
|
+
xslfile = File.join(File.dirname(file), 'log.xsl')
|
353
|
+
unless File.exist?(xslfile)
|
354
|
+
FileUtils.mkdir_p(File.dirname(xslfile))
|
355
|
+
File.open(xslfile, 'w') do |f|
|
356
|
+
f << DEFAULT_LOG_XSL
|
357
|
+
end
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
#
|
362
|
+
def split_note(note)
|
363
|
+
note = note.strip
|
364
|
+
if md = /\A.*?\[(.*?)\]\s*$/.match(note)
|
365
|
+
t = md[1].strip.downcase
|
366
|
+
n = note.sub(/\[#{md[1]}\]\s*$/, "")
|
367
|
+
else
|
368
|
+
n, t = note, nil
|
369
|
+
end
|
370
|
+
return n, t
|
371
|
+
end
|
372
|
+
|
373
|
+
# = Change Log Entry class
|
374
|
+
class Entry
|
375
|
+
include Comparable
|
376
|
+
|
377
|
+
attr_accessor :author
|
378
|
+
attr_accessor :date
|
379
|
+
attr_accessor :revison
|
380
|
+
attr_accessor :message
|
381
|
+
attr_accessor :type
|
382
|
+
|
383
|
+
def initialize(opts={})
|
384
|
+
@author = opts[:author] || opts[:who]
|
385
|
+
@date = opts[:date] || opts[:when]
|
386
|
+
@revison = opts[:revison] || opts[:rev]
|
387
|
+
@message = opts[:message] || opts[:msg]
|
388
|
+
@type = opts[:type]
|
389
|
+
end
|
390
|
+
|
391
|
+
#def clean_type(type)
|
392
|
+
# case type.to_s
|
393
|
+
# when 'maj', 'major' then :major
|
394
|
+
# when 'min', 'minor' then :minor
|
395
|
+
# when 'bug' then :bug
|
396
|
+
# when '' then :other
|
397
|
+
# else
|
398
|
+
# type.to_sym
|
399
|
+
# end
|
400
|
+
#end
|
401
|
+
|
402
|
+
#
|
403
|
+
def type_phrase
|
404
|
+
case type.to_s
|
405
|
+
when 'maj', 'major'
|
406
|
+
'Major Enhancements'
|
407
|
+
when 'min', 'minor'
|
408
|
+
'Minor Enhancements'
|
409
|
+
when 'bug'
|
410
|
+
'Bug Fixes'
|
411
|
+
when ''
|
412
|
+
'Further Enhancements'
|
413
|
+
else
|
414
|
+
"#{type.capitalize} Enhancements"
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
#
|
419
|
+
def type_number
|
420
|
+
case type.to_s.downcase
|
421
|
+
when 'maj', 'major'
|
422
|
+
0
|
423
|
+
when 'min', 'minor'
|
424
|
+
1
|
425
|
+
when 'bug'
|
426
|
+
2
|
427
|
+
when ''
|
428
|
+
4
|
429
|
+
else # other
|
430
|
+
3
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
#
|
435
|
+
def <=>(other)
|
436
|
+
other.date <=> date
|
437
|
+
end
|
438
|
+
|
439
|
+
def inspect
|
440
|
+
"#<Entry:#{object_id} #{date}>"
|
441
|
+
end
|
442
|
+
|
443
|
+
end #class Entry
|
444
|
+
|
445
|
+
end
|
446
|
+
|
447
|
+
DEFAULT_LOG_XSL = <<-END.tabto(0)
|
448
|
+
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
|
449
|
+
|
450
|
+
<xsl:output cdata-section-elements="script"/>
|
451
|
+
|
452
|
+
<xsl:template match="/">
|
453
|
+
<html>
|
454
|
+
<head>
|
455
|
+
<title>Changelog</title>
|
456
|
+
<link REL='SHORTCUT ICON' HREF="../img/ruby-sm.png" />
|
457
|
+
<style>
|
458
|
+
td { font-family: sans-serif; padding: 0px 10px; }
|
459
|
+
</style>
|
460
|
+
</head>
|
461
|
+
<body>
|
462
|
+
<div class="container">
|
463
|
+
<h1>Changelog</h1>
|
464
|
+
<table style="width: 100%;">
|
465
|
+
<xsl:apply-templates />
|
466
|
+
</table>
|
467
|
+
</div>
|
468
|
+
</body>
|
469
|
+
</html>
|
470
|
+
</xsl:template>
|
471
|
+
|
472
|
+
<xsl:template match="entry">
|
473
|
+
<tr>
|
474
|
+
<td><b><pre><xsl:value-of select="message"/></pre></b></td>
|
475
|
+
<td><xsl:value-of select="author"/></td>
|
476
|
+
<td><xsl:value-of select="date"/></td>
|
477
|
+
</tr>
|
478
|
+
</xsl:template>
|
479
|
+
|
480
|
+
</xsl:stylesheet>
|
481
|
+
END
|
482
|
+
|
483
|
+
end
|
484
|
+
|