rbnotes 0.4.7 → 0.4.12
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +41 -1
- data/Gemfile.lock +2 -2
- data/conf/config.yml +1 -0
- data/conf/config_deve.yml +1 -0
- data/conf/config_deve_fzf_no_opts.yml +8 -0
- data/conf/config_deve_no_picker.yml +7 -0
- data/conf/config_deve_peco.yml +8 -0
- data/etc/zsh/_rbnotes +93 -0
- data/exe/rbnotes +11 -1
- data/lib/rbnotes.rb +1 -0
- data/lib/rbnotes/commands.rb +5 -3
- data/lib/rbnotes/commands/add.rb +26 -1
- data/lib/rbnotes/commands/commands.rb +121 -0
- data/lib/rbnotes/commands/help.rb +1 -38
- data/lib/rbnotes/commands/import.rb +26 -5
- data/lib/rbnotes/commands/list.rb +93 -10
- data/lib/rbnotes/commands/pick.rb +5 -1
- data/lib/rbnotes/commands/search.rb +5 -1
- data/lib/rbnotes/commands/show.rb +60 -15
- data/lib/rbnotes/commands/statistics.rb +55 -0
- data/lib/rbnotes/commands/update.rb +1 -1
- data/lib/rbnotes/conf.rb +25 -11
- data/lib/rbnotes/error.rb +55 -3
- data/lib/rbnotes/statistics.rb +101 -0
- data/lib/rbnotes/utils.rb +111 -25
- data/lib/rbnotes/version.rb +2 -2
- metadata +9 -2
data/lib/rbnotes/error.rb
CHANGED
@@ -9,9 +9,13 @@ module Rbnotes
|
|
9
9
|
module ErrMsg
|
10
10
|
MISSING_ARGUMENT = "missing argument: %s"
|
11
11
|
MISSING_TIMESTAMP = "missing timestamp: %s"
|
12
|
-
NO_EDITOR = "
|
13
|
-
PROGRAM_ABORT = "
|
14
|
-
UNKNOWN_KEYWORD = "
|
12
|
+
NO_EDITOR = "no editor is available: %s"
|
13
|
+
PROGRAM_ABORT = "external program was aborted: %s"
|
14
|
+
UNKNOWN_KEYWORD = "unknown keyword: %s"
|
15
|
+
INVALID_TIMESTAMP_PATTERN = "invalid timestamp pattern: %s"
|
16
|
+
NO_CONF_FILE = "no configuration file: %s"
|
17
|
+
NO_TEMPLATE_FILE = "no template file: %s"
|
18
|
+
INVALID_TIMESTAMP_PATTERN_AS_DATE = "invalid timestamp pattern as date: %s"
|
15
19
|
end
|
16
20
|
|
17
21
|
# :startdoc:
|
@@ -64,4 +68,52 @@ module Rbnotes
|
|
64
68
|
super(ErrMsg::UNKNOWN_KEYWORD % keyword)
|
65
69
|
end
|
66
70
|
end
|
71
|
+
|
72
|
+
##
|
73
|
+
# An error raised when an invalid timestamp pattern was specified.
|
74
|
+
|
75
|
+
class InvalidTimestampPatternError < Error
|
76
|
+
def initialize(pattern)
|
77
|
+
super(ErrMsg::INVALID_TIMESTAMP_PATTERN % pattern)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
##
|
82
|
+
# An error raised when the specified configuration file does not
|
83
|
+
# exist.
|
84
|
+
|
85
|
+
class NoConfFileError < Error
|
86
|
+
def initialize(filename)
|
87
|
+
super(ErrMsg::NO_CONF_FILE % filename)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
##
|
92
|
+
# An error raised when no arguments is spcified.
|
93
|
+
|
94
|
+
class NoArgumentError < Error
|
95
|
+
def initialize
|
96
|
+
super
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
##
|
101
|
+
# An error raised when the specified template files does not exist.
|
102
|
+
#
|
103
|
+
class NoTemplateFileError < Error
|
104
|
+
def initialize(filepath)
|
105
|
+
super(ErrMsg::NO_TEMPLATE_FILE % filepath)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
##
|
110
|
+
# An error raised when the specified pattern cannot be converted
|
111
|
+
# into a date.
|
112
|
+
#
|
113
|
+
class InvalidTimestampPatternAsDateError < Error
|
114
|
+
def initialize(pattern)
|
115
|
+
super(ErrMsg::INVALID_TIMESTAMP_PATTERN_AS_DATE % pattern)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
67
119
|
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
module Rbnotes
|
2
|
+
##
|
3
|
+
# Calculates statistics of the repository.
|
4
|
+
class Statistics
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(conf)
|
8
|
+
@repo = Textrepo.init(conf)
|
9
|
+
@values = construct_values(@repo)
|
10
|
+
end
|
11
|
+
|
12
|
+
def total_report
|
13
|
+
puts @repo.entries.size
|
14
|
+
end
|
15
|
+
|
16
|
+
def yearly_report
|
17
|
+
self.each_year { |year, monthly_values|
|
18
|
+
num_of_notes = monthly_values.map { |_mon, values| values.size }.sum
|
19
|
+
puts "#{year}: #{num_of_notes}"
|
20
|
+
}
|
21
|
+
end
|
22
|
+
|
23
|
+
def monthly_report
|
24
|
+
self.each { |year, mon, values|
|
25
|
+
num_of_notes = values.size
|
26
|
+
puts "#{year}/#{mon}: #{num_of_notes}"
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
def each(&block)
|
31
|
+
if block.nil?
|
32
|
+
@values.map { |year, monthly_values|
|
33
|
+
monthly_values.each { |mon, values|
|
34
|
+
[year, mon, values]
|
35
|
+
}
|
36
|
+
}.to_enum(:each)
|
37
|
+
else
|
38
|
+
@values.each { |year, monthly_values|
|
39
|
+
monthly_values.each { |mon, values|
|
40
|
+
yield [year, mon, values]
|
41
|
+
}
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def years
|
47
|
+
@values.keys
|
48
|
+
end
|
49
|
+
|
50
|
+
def months(year)
|
51
|
+
@values[year] || []
|
52
|
+
end
|
53
|
+
|
54
|
+
def each_year(&block)
|
55
|
+
if block.nil?
|
56
|
+
@values.map { |year, monthly_values|
|
57
|
+
[year, monthly_values]
|
58
|
+
}.to_enum(:each)
|
59
|
+
else
|
60
|
+
@values.each { |year, monthly_values|
|
61
|
+
yield [year, monthly_values]
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
private
|
67
|
+
|
68
|
+
def construct_values(repo)
|
69
|
+
values = {}
|
70
|
+
repo.each { |timestamp, text|
|
71
|
+
value = StatisticValue.new(timestamp, text)
|
72
|
+
y = value.year
|
73
|
+
m = value.mon
|
74
|
+
values[y] ||= {}
|
75
|
+
values[y][m] ||= []
|
76
|
+
|
77
|
+
values[y][m] << value
|
78
|
+
}
|
79
|
+
values
|
80
|
+
end
|
81
|
+
|
82
|
+
class StatisticValue
|
83
|
+
|
84
|
+
attr_reader :lines
|
85
|
+
|
86
|
+
def initialize(timestamp, text)
|
87
|
+
@timestamp = timestamp
|
88
|
+
@lines = text.size
|
89
|
+
end
|
90
|
+
|
91
|
+
def year
|
92
|
+
@timestamp[:year]
|
93
|
+
end
|
94
|
+
|
95
|
+
def mon
|
96
|
+
@timestamp[:mon]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
101
|
+
end
|
data/lib/rbnotes/utils.rb
CHANGED
@@ -99,27 +99,22 @@ module Rbnotes
|
|
99
99
|
|
100
100
|
def read_timestamp(args)
|
101
101
|
str = args.shift || read_arg($stdin)
|
102
|
+
raise NoArgumentError if str.nil?
|
102
103
|
Textrepo::Timestamp.parse_s(str)
|
103
104
|
end
|
104
105
|
|
105
106
|
##
|
106
|
-
#
|
107
|
-
#
|
107
|
+
# Generates multiple Textrepo::Timestamp objects from the command
|
108
|
+
# line arguments. When no argument is given, try to read from
|
109
|
+
# STDIN.
|
108
110
|
#
|
109
111
|
# :call-seq:
|
110
|
-
#
|
112
|
+
# read_multiple_timestamps(args) -> [String]
|
111
113
|
|
112
|
-
def
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
#
|
117
|
-
# then, only the first string is interested
|
118
|
-
begin
|
119
|
-
io.gets.split(":")[0].rstrip
|
120
|
-
rescue NoMethodError => _
|
121
|
-
nil
|
122
|
-
end
|
114
|
+
def read_multiple_timestamps(args)
|
115
|
+
strings = args.size < 1 ? read_multiple_args($stdin) : args
|
116
|
+
raise NoArgumentError if (strings.nil? || strings.empty?)
|
117
|
+
strings.map { |str| Textrepo::Timestamp.parse_s(str) }
|
123
118
|
end
|
124
119
|
|
125
120
|
##
|
@@ -143,6 +138,8 @@ module Rbnotes
|
|
143
138
|
# - "yeasterday" (or "ye")
|
144
139
|
# - "this_week" (or "tw")
|
145
140
|
# - "last_week" (or "lw")
|
141
|
+
# - "this_month" (or "tm")
|
142
|
+
# - "last_month" (or "lm")
|
146
143
|
#
|
147
144
|
# :call-seq:
|
148
145
|
# expand_keyword_in_args(Array of Strings) -> Array of Strings
|
@@ -154,7 +151,8 @@ module Rbnotes
|
|
154
151
|
while args.size > 0
|
155
152
|
arg = args.shift
|
156
153
|
if ["today", "to", "yesterday", "ye",
|
157
|
-
"this_week", "tw", "last_week", "lw"
|
154
|
+
"this_week", "tw", "last_week", "lw",
|
155
|
+
"this_month", "tm", "last_month", "lm"].include?(arg)
|
158
156
|
patterns.concat(Rbnotes.utils.expand_keyword(arg))
|
159
157
|
else
|
160
158
|
patterns << arg
|
@@ -180,6 +178,10 @@ module Rbnotes
|
|
180
178
|
patterns.concat(dates_in_this_week.map { |d| timestamp_pattern(d) })
|
181
179
|
when "last_week", "lw"
|
182
180
|
patterns.concat(dates_in_last_week.map { |d| timestamp_pattern(d) })
|
181
|
+
when "this_month", "tm"
|
182
|
+
patterns.concat(dates_in_this_month.map { |d| timestamp_pattern(d) })
|
183
|
+
when "last_month", "lm"
|
184
|
+
patterns.concat(dates_in_last_month.map { |d| timestamp_pattern(d) })
|
183
185
|
else
|
184
186
|
raise UnknownKeywordError, keyword
|
185
187
|
end
|
@@ -190,25 +192,28 @@ module Rbnotes
|
|
190
192
|
# Makes a headline with the timestamp and subject of the notes, it
|
191
193
|
# looks like as follows:
|
192
194
|
#
|
193
|
-
#
|
194
|
-
#
|
195
|
-
# |
|
196
|
-
# 20101010001000_123: I love Macintosh.
|
197
|
-
# 20100909090909_999: This is very very long
|
198
|
-
#
|
199
|
-
#
|
195
|
+
# |<--------------- console column size -------------------->|
|
196
|
+
# | |+-- timestamp ---+ +-subject (the 1st line of note) -+
|
197
|
+
# | | | |
|
198
|
+
# | |20101010001000_123: I love Macintosh. [EOL]
|
199
|
+
# | |20100909090909_999: This is very very long looong subj[EOL]
|
200
|
+
# |<->| | |
|
201
|
+
# ^--- pad ++
|
202
|
+
# ^--- delimiter (2 characters)
|
200
203
|
#
|
201
204
|
# The subject part will truncate when it is long.
|
202
205
|
|
203
|
-
def make_headline(timestamp, text)
|
206
|
+
def make_headline(timestamp, text, pad = nil)
|
204
207
|
_, column = IO.console_size
|
205
208
|
delimiter = ": "
|
206
209
|
timestamp_width = timestamp.to_s.size
|
207
210
|
subject_width = column - timestamp_width - delimiter.size - 1
|
211
|
+
subject_width -= pad.size unless pad.nil?
|
208
212
|
|
209
213
|
subject = remove_heading_markup(text[0])
|
210
214
|
|
211
215
|
ts_part = "#{timestamp.to_s} "[0..(timestamp_width - 1)]
|
216
|
+
ts_part.prepend(pad) unless pad.nil?
|
212
217
|
sj_part = truncate_str(subject, subject_width)
|
213
218
|
|
214
219
|
ts_part + delimiter + sj_part
|
@@ -217,6 +222,7 @@ module Rbnotes
|
|
217
222
|
##
|
218
223
|
# Finds all notes those timestamps match to given patterns in the
|
219
224
|
# given repository. Returns an Array contains Timestamp objects.
|
225
|
+
# The returned Array is sorted by Timestamp.
|
220
226
|
#
|
221
227
|
# :call-seq:
|
222
228
|
# find_notes(Array of timestamp patterns, Textrepo::Repository)
|
@@ -227,9 +233,55 @@ module Rbnotes
|
|
227
233
|
}.flatten.sort{ |a, b| b <=> a }.uniq
|
228
234
|
end
|
229
235
|
|
236
|
+
##
|
237
|
+
# Enumerates all timestamp patterns in a week which contains a
|
238
|
+
# given timestamp as a day of the week.
|
239
|
+
#
|
240
|
+
# :call-seq:
|
241
|
+
# timestamp_patterns_in_week(timestamp) -> [Array of Strings]
|
242
|
+
|
243
|
+
def timestamp_patterns_in_week(timestamp)
|
244
|
+
dates_in_week(start_date_in_the_week(timestamp.time)).map { |date| timestamp_pattern(date) }
|
245
|
+
end
|
246
|
+
|
230
247
|
# :stopdoc:
|
231
248
|
|
232
249
|
private
|
250
|
+
|
251
|
+
##
|
252
|
+
# Reads an argument from the IO object. Typically, it is intended
|
253
|
+
# to be used with STDIN.
|
254
|
+
#
|
255
|
+
# :call-seq:
|
256
|
+
# read_arg(IO) -> String
|
257
|
+
|
258
|
+
def read_arg(io)
|
259
|
+
read_multiple_args(io)[0]
|
260
|
+
end
|
261
|
+
|
262
|
+
##
|
263
|
+
# Reads arguments from the IO object. Typically, it is intended
|
264
|
+
# to be used with STDIN.
|
265
|
+
#
|
266
|
+
# :call-seq:
|
267
|
+
# read_multiple_arg(IO) -> [String]
|
268
|
+
|
269
|
+
def read_multiple_args(io)
|
270
|
+
strings = io.readlines
|
271
|
+
strings.map { |str|
|
272
|
+
# assumes the reading line looks like:
|
273
|
+
#
|
274
|
+
# foo bar baz ...
|
275
|
+
#
|
276
|
+
# then, only the first string is interested
|
277
|
+
begin
|
278
|
+
str.split(":")[0].rstrip
|
279
|
+
rescue NoMethodError => _
|
280
|
+
nil
|
281
|
+
end
|
282
|
+
}.compact
|
283
|
+
end
|
284
|
+
|
233
285
|
def search_in_path(name)
|
234
286
|
search_paths = ENV["PATH"].split(":")
|
235
287
|
found = search_paths.map { |path|
|
@@ -268,14 +320,18 @@ module Rbnotes
|
|
268
320
|
end
|
269
321
|
|
270
322
|
def start_date_in_this_week
|
271
|
-
|
272
|
-
Date.new(today.year, today.mon, today.day).prev_day(wday(today))
|
323
|
+
start_date_in_the_week(Time.now)
|
273
324
|
end
|
274
325
|
|
275
326
|
def start_date_in_last_week
|
276
327
|
start_date_in_this_week.prev_day(7)
|
277
328
|
end
|
278
329
|
|
330
|
+
def start_date_in_the_week(time)
|
331
|
+
parts = [:year, :mon, :day].map { |sym| time.send(sym) }
|
332
|
+
Date.new(*parts).prev_day(wday(time))
|
333
|
+
end
|
334
|
+
|
279
335
|
def wday(time)
|
280
336
|
(time.wday - 1) % 7
|
281
337
|
end
|
@@ -286,6 +342,36 @@ module Rbnotes
|
|
286
342
|
dates
|
287
343
|
end
|
288
344
|
|
345
|
+
def dates_in_this_month
|
346
|
+
today = Time.now
|
347
|
+
first_date = date(Time.new(today.year, today.mon, 1))
|
348
|
+
dates_in_month(first_date)
|
349
|
+
end
|
350
|
+
|
351
|
+
def dates_in_last_month
|
352
|
+
today = Time.now
|
353
|
+
first_date_of_this_month = date(Time.new(today.year, today.mon, 1))
|
354
|
+
dates_in_month(first_date_of_this_month.prev_month)
|
355
|
+
end
|
356
|
+
|
357
|
+
def dates_in_month(first_date)
|
358
|
+
days = days_in_month(first_date.mon, leap: first_date.leap?)
|
359
|
+
dates = [first_date]
|
360
|
+
1.upto(days - 1) { |i| dates << first_date.next_day(i) }
|
361
|
+
dates
|
362
|
+
end
|
363
|
+
|
364
|
+
DAYS = {
|
365
|
+
# 1 2 3 4 5 6 7 8 9 10 11 12
|
366
|
+
# Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
|
367
|
+
false => [0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
|
368
|
+
true => [0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
|
369
|
+
}
|
370
|
+
|
371
|
+
def days_in_month(mon, leap: false)
|
372
|
+
DAYS[leap][mon]
|
373
|
+
end
|
374
|
+
|
289
375
|
def truncate_str(str, size)
|
290
376
|
count = 0
|
291
377
|
result = ""
|
data/lib/rbnotes/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rbnotes
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- mnbi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-12-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: textrepo
|
@@ -58,11 +58,16 @@ files:
|
|
58
58
|
- bin/setup
|
59
59
|
- conf/config.yml
|
60
60
|
- conf/config_deve.yml
|
61
|
+
- conf/config_deve_fzf_no_opts.yml
|
62
|
+
- conf/config_deve_no_picker.yml
|
63
|
+
- conf/config_deve_peco.yml
|
61
64
|
- conf/config_test.yml
|
65
|
+
- etc/zsh/_rbnotes
|
62
66
|
- exe/rbnotes
|
63
67
|
- lib/rbnotes.rb
|
64
68
|
- lib/rbnotes/commands.rb
|
65
69
|
- lib/rbnotes/commands/add.rb
|
70
|
+
- lib/rbnotes/commands/commands.rb
|
66
71
|
- lib/rbnotes/commands/delete.rb
|
67
72
|
- lib/rbnotes/commands/export.rb
|
68
73
|
- lib/rbnotes/commands/help.rb
|
@@ -71,9 +76,11 @@ files:
|
|
71
76
|
- lib/rbnotes/commands/pick.rb
|
72
77
|
- lib/rbnotes/commands/search.rb
|
73
78
|
- lib/rbnotes/commands/show.rb
|
79
|
+
- lib/rbnotes/commands/statistics.rb
|
74
80
|
- lib/rbnotes/commands/update.rb
|
75
81
|
- lib/rbnotes/conf.rb
|
76
82
|
- lib/rbnotes/error.rb
|
83
|
+
- lib/rbnotes/statistics.rb
|
77
84
|
- lib/rbnotes/utils.rb
|
78
85
|
- lib/rbnotes/version.rb
|
79
86
|
- rbnotes.gemspec
|