rubycut-vclog 1.9.4
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/.yardopts +7 -0
- data/History.md +249 -0
- data/License.txt +23 -0
- data/README.md +133 -0
- data/bin/vclog +6 -0
- data/bin/vclog-autotag +6 -0
- data/bin/vclog-bump +6 -0
- data/bin/vclog-formats +6 -0
- data/bin/vclog-news +6 -0
- data/bin/vclog-version +6 -0
- data/lib/vclog.rb +6 -0
- data/lib/vclog.yml +68 -0
- data/lib/vclog/adapters.rb +12 -0
- data/lib/vclog/adapters/abstract.rb +131 -0
- data/lib/vclog/adapters/darcs.rb +93 -0
- data/lib/vclog/adapters/git.rb +190 -0
- data/lib/vclog/adapters/hg.rb +129 -0
- data/lib/vclog/adapters/svn.rb +155 -0
- data/lib/vclog/change.rb +207 -0
- data/lib/vclog/change_point.rb +77 -0
- data/lib/vclog/changelog.rb +233 -0
- data/lib/vclog/cli.rb +8 -0
- data/lib/vclog/cli/abstract.rb +92 -0
- data/lib/vclog/cli/autotag.rb +36 -0
- data/lib/vclog/cli/bump.rb +29 -0
- data/lib/vclog/cli/formats.rb +28 -0
- data/lib/vclog/cli/log.rb +86 -0
- data/lib/vclog/cli/news.rb +29 -0
- data/lib/vclog/cli/version.rb +30 -0
- data/lib/vclog/config.rb +143 -0
- data/lib/vclog/core_ext.rb +11 -0
- data/lib/vclog/heuristics.rb +192 -0
- data/lib/vclog/heuristics/rule.rb +73 -0
- data/lib/vclog/heuristics/type.rb +29 -0
- data/lib/vclog/history_file.rb +69 -0
- data/lib/vclog/metadata.rb +16 -0
- data/lib/vclog/rc.rb +9 -0
- data/lib/vclog/release.rb +67 -0
- data/lib/vclog/repo.rb +298 -0
- data/lib/vclog/report.rb +200 -0
- data/lib/vclog/tag.rb +151 -0
- data/lib/vclog/templates/changelog.ansi.rb +35 -0
- data/lib/vclog/templates/changelog.atom.erb +52 -0
- data/lib/vclog/templates/changelog.gnu.rb +24 -0
- data/lib/vclog/templates/changelog.html.erb +49 -0
- data/lib/vclog/templates/changelog.json.rb +1 -0
- data/lib/vclog/templates/changelog.markdown.rb +30 -0
- data/lib/vclog/templates/changelog.rdoc.rb +30 -0
- data/lib/vclog/templates/changelog.rss.erb +54 -0
- data/lib/vclog/templates/changelog.xml.erb +28 -0
- data/lib/vclog/templates/changelog.xsl +34 -0
- data/lib/vclog/templates/changelog.yaml.rb +1 -0
- data/lib/vclog/templates/history.ansi.rb +57 -0
- data/lib/vclog/templates/history.atom.erb +84 -0
- data/lib/vclog/templates/history.gnu.rb +39 -0
- data/lib/vclog/templates/history.html.erb +60 -0
- data/lib/vclog/templates/history.json.rb +1 -0
- data/lib/vclog/templates/history.markdown.rb +38 -0
- data/lib/vclog/templates/history.rdoc.rb +36 -0
- data/lib/vclog/templates/history.rss.erb +84 -0
- data/lib/vclog/templates/history.xml.erb +43 -0
- data/lib/vclog/templates/history.yaml.rb +1 -0
- data/man/man1/index.txt +9 -0
- data/man/man1/vclog-autotag.1.ronn +29 -0
- data/man/man1/vclog-bump.1.ronn +21 -0
- data/man/man1/vclog-news.1.ronn +25 -0
- data/man/man1/vclog-version.1.ronn +14 -0
- data/man/man1/vclog.1.ronn +49 -0
- data/spec/feature_git_changes.rb +58 -0
- data/spec/feature_git_history.rb +58 -0
- data/spec/feature_hg_changes.rb +58 -0
- data/spec/feature_hg_history.rb +58 -0
- data/spec/featurettes/repo_creation.rb +64 -0
- data/spec/featurettes/shellout.rb +16 -0
- data/test/case_metadata.rb +10 -0
- metadata +265 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
module VCLog
|
2
|
+
|
3
|
+
class Heuristics
|
4
|
+
|
5
|
+
# Defines a categorization rule for commits.
|
6
|
+
#
|
7
|
+
class Rule
|
8
|
+
|
9
|
+
# Initialize a new log rule.
|
10
|
+
#
|
11
|
+
# @param [Regexp] pattern
|
12
|
+
# An optional regular expression to match agains the commit message.
|
13
|
+
# If the pattern does not match the commit message than the rule
|
14
|
+
# does not apply.
|
15
|
+
#
|
16
|
+
# @yieldparam [Change]
|
17
|
+
# An encapsulation of the commit.
|
18
|
+
#
|
19
|
+
# @yieldreturn [Boolean]
|
20
|
+
# If the return value is +nil+ or +false+, the rule does not apply.
|
21
|
+
# If a rule does not apply then be sure not to alter the commit!
|
22
|
+
#
|
23
|
+
def initialize(pattern=nil, &block)
|
24
|
+
@pattern = pattern
|
25
|
+
@block = block
|
26
|
+
end
|
27
|
+
|
28
|
+
# Message pattern to match for the rule to apply.
|
29
|
+
attr :pattern
|
30
|
+
|
31
|
+
# Process the rule.
|
32
|
+
#
|
33
|
+
# @since 1.9.0
|
34
|
+
# If using a message pattern and the block takes two arguments
|
35
|
+
# then the first will be the commit object, not the message as
|
36
|
+
# was the case in older versions.
|
37
|
+
#
|
38
|
+
def call(commit)
|
39
|
+
if pattern
|
40
|
+
call_pattern(commit)
|
41
|
+
else
|
42
|
+
call_commit(commit)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
#
|
49
|
+
def call_pattern(commit)
|
50
|
+
if matchdata = @pattern.match(commit.message)
|
51
|
+
#case @block.arity
|
52
|
+
#when 0
|
53
|
+
# @block.call
|
54
|
+
#when 1
|
55
|
+
# @block.call(matchdata)
|
56
|
+
#else
|
57
|
+
@block.call(commit, matchdata)
|
58
|
+
#end
|
59
|
+
else
|
60
|
+
nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
def call_commit(commit)
|
66
|
+
@block.call(commit)
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module VCLog
|
2
|
+
|
3
|
+
class Heuristics
|
4
|
+
|
5
|
+
#
|
6
|
+
#
|
7
|
+
class Type
|
8
|
+
#
|
9
|
+
def initialize(type, level, label)
|
10
|
+
@type = type.to_sym
|
11
|
+
@level = level.to_i
|
12
|
+
@label = label.to_s
|
13
|
+
end
|
14
|
+
|
15
|
+
attr :type
|
16
|
+
|
17
|
+
attr :level
|
18
|
+
|
19
|
+
attr :label
|
20
|
+
|
21
|
+
#
|
22
|
+
def to_a
|
23
|
+
[type, level, label]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module VCLog
|
2
|
+
|
3
|
+
# The HistoryFile class will parse a history into an array
|
4
|
+
# of release tags. Of course to do this, it assumes a specific
|
5
|
+
# file format.
|
6
|
+
#
|
7
|
+
class HistoryFile
|
8
|
+
|
9
|
+
FILE = '{HISTORY,HISTORY.*}'
|
10
|
+
|
11
|
+
LINE = /^[=#]/
|
12
|
+
VERS = /(\d+[._])+\d+/
|
13
|
+
DATE = /(\d+[-])+\d+/
|
14
|
+
|
15
|
+
# Alias for `File::FNM_CASEFOLD`.
|
16
|
+
CASEFOLD = File::FNM_CASEFOLD
|
17
|
+
|
18
|
+
# Release tags.
|
19
|
+
attr :tags
|
20
|
+
|
21
|
+
# Setup new HistoryFile instance.
|
22
|
+
def initialize(source=nil)
|
23
|
+
if File.file?(source)
|
24
|
+
@file = source
|
25
|
+
@root = File.dirname(source)
|
26
|
+
elsif File.directory?(source)
|
27
|
+
@file = Dir.glob(File.join(source,FILE), CASEFOLD).first
|
28
|
+
@root = source
|
29
|
+
else
|
30
|
+
@file = Dir.glob(FILE).first
|
31
|
+
@root = Dir.pwd
|
32
|
+
end
|
33
|
+
raise "no history file" unless @file
|
34
|
+
|
35
|
+
@tags = extract_tags
|
36
|
+
end
|
37
|
+
|
38
|
+
# Parse history file.
|
39
|
+
def extract_tags
|
40
|
+
tags = []
|
41
|
+
desc = ''
|
42
|
+
text = File.read(@file)
|
43
|
+
text.lines.each do |line|
|
44
|
+
if LINE =~ line
|
45
|
+
vers = (VERS.match(line) || [])[0]
|
46
|
+
date = (DATE.match(line) || [])[0]
|
47
|
+
next unless vers
|
48
|
+
tags << [vers, date, desc = '']
|
49
|
+
else
|
50
|
+
desc << line
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
tags.map do |vers, date, desc|
|
55
|
+
index = desc.index(/^Changes:/) || desc.index(/^\*/) || desc.size
|
56
|
+
desc = desc[0...index].strip.fold
|
57
|
+
#[vers, date, desc]
|
58
|
+
Tag.new(:name=>vers, :date=>date, :msg=>desc)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
def news
|
64
|
+
tags.first.message
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module VCLog
|
2
|
+
|
3
|
+
def self.metadata
|
4
|
+
@metadata ||= (
|
5
|
+
require 'yaml'
|
6
|
+
YAML.load_file(File.dirname(__FILE__) + '/../vclog.yml')
|
7
|
+
)
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.const_missing(name)
|
11
|
+
key = name.to_s.downcase
|
12
|
+
metadata.key?(key) ? metadata[key] : super(name)
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
data/lib/vclog/rc.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
module VCLog
|
2
|
+
|
3
|
+
# A Release encapsulate a collection of {Change} objects
|
4
|
+
# associated with a {Tag}.
|
5
|
+
#
|
6
|
+
class Release
|
7
|
+
|
8
|
+
# Tag object this release represents.
|
9
|
+
attr :tag
|
10
|
+
|
11
|
+
# Array of Change objects.
|
12
|
+
attr :changes
|
13
|
+
|
14
|
+
#
|
15
|
+
# New Release object.
|
16
|
+
#
|
17
|
+
# @param [Tag] tag
|
18
|
+
# A Tag object.
|
19
|
+
#
|
20
|
+
# @param [Array<Change>] changes
|
21
|
+
# An array of Change objects.
|
22
|
+
#
|
23
|
+
def initialize(tag, changes)
|
24
|
+
@tag = tag
|
25
|
+
@changes = changes
|
26
|
+
end
|
27
|
+
|
28
|
+
#
|
29
|
+
# Group +changes+ by type and sort by level.
|
30
|
+
#
|
31
|
+
# @return [Array<Array>]
|
32
|
+
# Returns an associative array of [type, changes].
|
33
|
+
#
|
34
|
+
def groups
|
35
|
+
@groups ||= (
|
36
|
+
changes.group_by{ |e| e.label }.sort{ |a,b| b[1][0].level <=> a[1][0].level }
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
#
|
41
|
+
# Compare release by tag.
|
42
|
+
#
|
43
|
+
# @param [Release] other
|
44
|
+
# Another release instance.
|
45
|
+
#
|
46
|
+
def <=>(other)
|
47
|
+
@tag <=> other.tag
|
48
|
+
end
|
49
|
+
|
50
|
+
#
|
51
|
+
# Convert Release to Hash.
|
52
|
+
#
|
53
|
+
# @todo Should +version+ be +name+?
|
54
|
+
#
|
55
|
+
def to_h
|
56
|
+
{ 'version' => tag.name,
|
57
|
+
'date' => tag.date,
|
58
|
+
'message' => tag.message,
|
59
|
+
'author' => tag.author,
|
60
|
+
'id' => tag.id,
|
61
|
+
'changes' => changes.map{|change| change.to_h}
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/vclog/repo.rb
ADDED
@@ -0,0 +1,298 @@
|
|
1
|
+
require 'vclog/core_ext'
|
2
|
+
require 'vclog/adapters'
|
3
|
+
require 'vclog/config'
|
4
|
+
require 'vclog/heuristics'
|
5
|
+
require 'vclog/history_file'
|
6
|
+
require 'vclog/changelog'
|
7
|
+
require 'vclog/tag'
|
8
|
+
require 'vclog/release'
|
9
|
+
require 'vclog/report'
|
10
|
+
|
11
|
+
module VCLog
|
12
|
+
|
13
|
+
# Encapsulate representaiton of the repository.
|
14
|
+
#
|
15
|
+
class Repo
|
16
|
+
|
17
|
+
#
|
18
|
+
# File glob used to find project root directory.
|
19
|
+
#
|
20
|
+
ROOT_GLOB = '{.ruby,.map/,.git/,.hg/,_darcs/,.svn/}'
|
21
|
+
|
22
|
+
#
|
23
|
+
# Project's root directory.
|
24
|
+
#
|
25
|
+
attr :root
|
26
|
+
|
27
|
+
#
|
28
|
+
# Options hash.
|
29
|
+
#
|
30
|
+
attr :options
|
31
|
+
|
32
|
+
#
|
33
|
+
# Setup new Repo instance.
|
34
|
+
#
|
35
|
+
def initialize(root, options={})
|
36
|
+
options[:root] = root if root
|
37
|
+
|
38
|
+
@config = Config.new(options)
|
39
|
+
@options = options
|
40
|
+
|
41
|
+
vcs_type = @config.vcs_type
|
42
|
+
|
43
|
+
raise ArgumentError, "Not a recognized version control system." unless vcs_type
|
44
|
+
|
45
|
+
@adapter = Adapters.const_get(vcs_type.capitalize).new(self)
|
46
|
+
end
|
47
|
+
|
48
|
+
#
|
49
|
+
# Returns instance of an Adapter subclass.
|
50
|
+
#
|
51
|
+
def adapter
|
52
|
+
@adapter
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# Configuration.
|
57
|
+
#
|
58
|
+
def config
|
59
|
+
@config
|
60
|
+
end
|
61
|
+
|
62
|
+
#
|
63
|
+
# Project root directory.
|
64
|
+
#
|
65
|
+
def root
|
66
|
+
config.root
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Load heuristics script.
|
71
|
+
#
|
72
|
+
def heuristics
|
73
|
+
config.heuristics
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# Check force option.
|
78
|
+
#
|
79
|
+
def force?
|
80
|
+
config.force?
|
81
|
+
end
|
82
|
+
|
83
|
+
#
|
84
|
+
# Access to Repo's HISTORY file.
|
85
|
+
#
|
86
|
+
def history_file
|
87
|
+
@history_file ||= HistoryFile.new(options[:history_file] || root)
|
88
|
+
end
|
89
|
+
|
90
|
+
#
|
91
|
+
# Read history file and make a commit tag for any release not already
|
92
|
+
# tagged. Unless the force option is set the user will be prompted for
|
93
|
+
# each new tag.
|
94
|
+
#
|
95
|
+
def autotag(prefix=nil)
|
96
|
+
history_file.tags.each do |tag|
|
97
|
+
label = "#{prefix}#{tag.name}"
|
98
|
+
if not adapter.tag?(label)
|
99
|
+
chg = adapter.change_by_date(tag.date)
|
100
|
+
if chg
|
101
|
+
if force? or ask_yn(new_tag_message(label, tag) + "\nCreate tag? [yN] ")
|
102
|
+
adapter.tag(chg.rev, label, tag.date, tag.message)
|
103
|
+
end
|
104
|
+
else
|
105
|
+
puts "No commit found for #{label} #{tag.date.strftime('%Y-%m-%d')}."
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# List of all changes.
|
113
|
+
#
|
114
|
+
def changes
|
115
|
+
@changes ||= apply_heuristics(adapter.changes)
|
116
|
+
end
|
117
|
+
|
118
|
+
#
|
119
|
+
# List of all change points.
|
120
|
+
#
|
121
|
+
def change_points
|
122
|
+
@change_points ||= apply_heuristics(adapter.change_points)
|
123
|
+
end
|
124
|
+
|
125
|
+
#
|
126
|
+
# Apply heuristics to changes.
|
127
|
+
#
|
128
|
+
def apply_heuristics(changes)
|
129
|
+
changes.each do |change|
|
130
|
+
change.apply_heuristics(heuristics)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
#
|
135
|
+
# Collect releases for the given set of +changes+.
|
136
|
+
#
|
137
|
+
# Releases are groups of changes segregated by tags. The release version,
|
138
|
+
# release date and release note are defined by hard tag commits.
|
139
|
+
#
|
140
|
+
# @param [Array<Change>] changes
|
141
|
+
# List of Change objects.
|
142
|
+
#
|
143
|
+
# @return [Array<Release>]
|
144
|
+
# List of Release objects.
|
145
|
+
#
|
146
|
+
def releases(changes)
|
147
|
+
rel = []
|
148
|
+
tags = self.tags.dup
|
149
|
+
|
150
|
+
#ver = repo.bump(version)
|
151
|
+
|
152
|
+
name = config.version || 'HEAD'
|
153
|
+
user = adapter.user
|
154
|
+
date = ::Time.now + (3600 * 24) # one day ahead
|
155
|
+
|
156
|
+
change = Change.new(:id=>'HEAD', :date=>date, :who=>user)
|
157
|
+
|
158
|
+
tags << Tag.new(:name=>name, :date=>date, :who=>user, :msg=>"Current Development", :commit=>change)
|
159
|
+
|
160
|
+
# TODO: Do we need to add a Time.now tag?
|
161
|
+
# add current verion to release list (if given)
|
162
|
+
#previous_version = tags[0].name
|
163
|
+
#if current_version < previous_version # TODO: need to use natural comparision
|
164
|
+
# raise ArgumentError, "Release version is less than previous version (#{previous_version})."
|
165
|
+
#end
|
166
|
+
|
167
|
+
# sort by release date
|
168
|
+
tags = tags.sort{ |a,b| a.date <=> b.date }
|
169
|
+
|
170
|
+
# organize into deltas
|
171
|
+
delta = []
|
172
|
+
last = nil
|
173
|
+
tags.each do |tag|
|
174
|
+
delta << [tag, [last, tag.commit.date]]
|
175
|
+
last = tag.commit.date
|
176
|
+
end
|
177
|
+
|
178
|
+
# gather changes for each delta
|
179
|
+
delta.each do |tag, (started, ended)|
|
180
|
+
if started
|
181
|
+
set = changes.select{ |c| c.date > started && c.date <= ended }
|
182
|
+
#gt_vers, gt_date = gt.name, gt.date
|
183
|
+
#lt_vers, lt_date = lt.name, lt.date
|
184
|
+
#gt_date = Time.parse(gt_date) unless Time===gt_date
|
185
|
+
#lt_date = Time.parse(lt_date) unless Time===lt_date
|
186
|
+
#log = changelog.after(gt).before(lt)
|
187
|
+
else
|
188
|
+
#lt_vers, lt_date = lt.name, lt.date
|
189
|
+
#lt_date = Time.parse(lt_date) unless Time===lt_date
|
190
|
+
#log = changelog.before(lt_date)
|
191
|
+
set = changes.select{ |c| c.date <= ended }
|
192
|
+
end
|
193
|
+
rel << Release.new(tag, set)
|
194
|
+
end
|
195
|
+
rel.sort
|
196
|
+
end
|
197
|
+
|
198
|
+
#
|
199
|
+
# Print a report with given options.
|
200
|
+
#
|
201
|
+
def report(options)
|
202
|
+
report = Report.new(self, options)
|
203
|
+
report.print
|
204
|
+
end
|
205
|
+
|
206
|
+
#
|
207
|
+
# Make an educated guess as to the next version number based on
|
208
|
+
# changes made since previous release.
|
209
|
+
#
|
210
|
+
# @return [String] version number
|
211
|
+
#
|
212
|
+
# @todo Allow configuration of version bump thresholds
|
213
|
+
#
|
214
|
+
def bump
|
215
|
+
last_release = releases(changes).first
|
216
|
+
max = last_release.changes.map{ |c| c.level }.max
|
217
|
+
if max > 1
|
218
|
+
bump_part('major')
|
219
|
+
elsif max >= 0
|
220
|
+
bump_part('minor')
|
221
|
+
else
|
222
|
+
bump_part('patch')
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
#
|
227
|
+
# Provides a bumped version number.
|
228
|
+
#
|
229
|
+
def bump_part(part=nil)
|
230
|
+
raise "bad version part - #{part}" unless ['major', 'minor', 'patch', 'build', ''].include?(part.to_s)
|
231
|
+
|
232
|
+
if tags.last
|
233
|
+
v = tags[-1].name # TODO: ensure the latest version
|
234
|
+
v = tags[-2].name if v == 'HEAD'
|
235
|
+
else
|
236
|
+
v = '0.0.0'
|
237
|
+
end
|
238
|
+
|
239
|
+
v = v.split(/\W/) # TODO: preserve split chars
|
240
|
+
|
241
|
+
case part.to_s
|
242
|
+
when 'major'
|
243
|
+
v[0] = v[0].succ
|
244
|
+
(1..(v.size-1)).each{ |i| v[i] = '0' }
|
245
|
+
v.join('.')
|
246
|
+
when 'minor'
|
247
|
+
v[1] = '0' unless v[1]
|
248
|
+
v[1] = v[1].succ
|
249
|
+
(2..(v.size-1)).each{ |i| v[i] = '0' }
|
250
|
+
v.join('.')
|
251
|
+
when 'patch'
|
252
|
+
v[1] = '0' unless v[1]
|
253
|
+
v[2] = '0' unless v[2]
|
254
|
+
v[2] = v[2].succ
|
255
|
+
(3..(v.size-1)).each{ |i| v[i] = '0' }
|
256
|
+
v.join('.')
|
257
|
+
else
|
258
|
+
v[-1] = '0' unless v[-1]
|
259
|
+
v[-1] = v[-1].succ
|
260
|
+
v.join('.')
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
#
|
265
|
+
# Delegate missing methods to SCM adapter.
|
266
|
+
#
|
267
|
+
def method_missing(s, *a, &b)
|
268
|
+
if adapter.respond_to?(s)
|
269
|
+
adapter.send(s, *a, &b)
|
270
|
+
else
|
271
|
+
super(s,*a,&b)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
private
|
276
|
+
|
277
|
+
#
|
278
|
+
# Ask yes/no question.
|
279
|
+
#
|
280
|
+
def ask_yn(message)
|
281
|
+
case ask(message)
|
282
|
+
when 'y', 'Y', 'yes'
|
283
|
+
true
|
284
|
+
else
|
285
|
+
false
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
#
|
290
|
+
# Returns a String.
|
291
|
+
#
|
292
|
+
def new_tag_message(label, tag)
|
293
|
+
"#{label} / #{tag.date.strftime('%Y-%m-%d')}\n#{tag.message}"
|
294
|
+
end
|
295
|
+
|
296
|
+
end
|
297
|
+
|
298
|
+
end
|