rbnotes 0.4.1 → 0.4.6
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 -10
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -4
- data/README.md +4 -4
- data/conf/config.yml +1 -0
- data/conf/config_deve.yml +1 -0
- data/exe/rbnotes +10 -1
- data/lib/rbnotes.rb +8 -0
- data/lib/rbnotes/commands.rb +161 -59
- data/lib/rbnotes/commands/add.rb +43 -3
- data/lib/rbnotes/commands/delete.rb +25 -13
- data/lib/rbnotes/commands/export.rb +58 -0
- data/lib/rbnotes/commands/help.rb +98 -0
- data/lib/rbnotes/commands/import.rb +39 -2
- data/lib/rbnotes/commands/list.rb +47 -56
- data/lib/rbnotes/commands/pick.rb +47 -0
- data/lib/rbnotes/commands/search.rb +25 -2
- data/lib/rbnotes/commands/show.rb +34 -3
- data/lib/rbnotes/commands/update.rb +59 -21
- data/lib/rbnotes/error.rb +11 -0
- data/lib/rbnotes/utils.rb +174 -8
- data/lib/rbnotes/version.rb +2 -2
- data/rbnotes.gemspec +1 -1
- metadata +7 -4
data/lib/rbnotes/commands/add.rb
CHANGED
@@ -27,7 +27,10 @@ module Rbnotes::Commands
|
|
27
27
|
# If none of the above editor is available, the command fails.
|
28
28
|
|
29
29
|
class Add < Command
|
30
|
-
|
30
|
+
|
31
|
+
def description # :nodoc:
|
32
|
+
"Add a new note"
|
33
|
+
end
|
31
34
|
|
32
35
|
def execute(args, conf)
|
33
36
|
@opts = {}
|
@@ -41,16 +44,23 @@ module Rbnotes::Commands
|
|
41
44
|
@opts[:timestamp] = Textrepo::Timestamp.parse_s(stamp_str)
|
42
45
|
else
|
43
46
|
args.unshift(arg)
|
47
|
+
break
|
44
48
|
end
|
45
49
|
end
|
46
50
|
|
47
51
|
stamp = @opts[:timestamp] || Textrepo::Timestamp.new(Time.now)
|
48
52
|
|
49
53
|
candidates = [conf[:editor], ENV["EDITOR"], "nano", "vi"].compact
|
50
|
-
editor = find_program(candidates)
|
54
|
+
editor = Rbnotes.utils.find_program(candidates)
|
51
55
|
raise Rbnotes::NoEditorError, candidates if editor.nil?
|
52
56
|
|
53
|
-
tmpfile = run_with_tmpfile(editor, stamp.to_s)
|
57
|
+
tmpfile = Rbnotes.utils.run_with_tmpfile(editor, stamp.to_s)
|
58
|
+
|
59
|
+
unless FileTest.exist?(tmpfile)
|
60
|
+
puts "Cancel adding, since nothing to store"
|
61
|
+
return
|
62
|
+
end
|
63
|
+
|
54
64
|
text = File.readlines(tmpfile)
|
55
65
|
|
56
66
|
repo = Textrepo.init(conf)
|
@@ -71,6 +81,36 @@ module Rbnotes::Commands
|
|
71
81
|
end
|
72
82
|
end
|
73
83
|
|
84
|
+
def help # :nodoc:
|
85
|
+
puts <<HELP
|
86
|
+
usage:
|
87
|
+
#{Rbnotes::NAME} add [(-t|--timestamp) STAMP_PATTERN]
|
88
|
+
|
89
|
+
Add a new note to the repository. If no options, a new timestamp is
|
90
|
+
generated at the execution time, then it is attached to the note.
|
91
|
+
|
92
|
+
Accept an option with `-t STAMP_PATTERN` (or `--timestamp`), a
|
93
|
+
timestamp is generated according to `STAMP_PATTERN`.
|
94
|
+
|
95
|
+
STAMP_PATTERN could be one of followings:
|
96
|
+
|
97
|
+
"20201104172230_078" : full qualified timestamp string
|
98
|
+
"20201104172230" : full qualified timestamp string (no suffix)
|
99
|
+
"202011041722" : year, date and time (omit second part)
|
100
|
+
"11041722" : date and time (omit year and second part)
|
101
|
+
|
102
|
+
This command starts the external editor program to prepare text to
|
103
|
+
store. The editor program will be searched in the following order:
|
104
|
+
|
105
|
+
1. configuration setting of ":editor"
|
106
|
+
2. ENV["EDITOR"]
|
107
|
+
3. "nano"
|
108
|
+
4. "vi"
|
109
|
+
|
110
|
+
If none of the above editor is available, the execution fails.
|
111
|
+
HELP
|
112
|
+
end
|
113
|
+
|
74
114
|
# :stopdoc:
|
75
115
|
private
|
76
116
|
def complement_timestamp_pattern(pattern)
|
@@ -1,20 +1,19 @@
|
|
1
|
-
|
1
|
+
module Rbnotes::Commands
|
2
2
|
|
3
|
-
#
|
4
|
-
#
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
# It does nothing when the specified note does not exist except to
|
10
|
-
# print error message.
|
3
|
+
# Deletes a given note in the repository. The timestamp string must
|
4
|
+
# be a fully qualified one, like "20201016165130". If no argument
|
5
|
+
# was passed, it would try to read from the standard input.
|
6
|
+
#
|
7
|
+
# It does nothing to change the repository when the specified note
|
8
|
+
# does not exist.
|
11
9
|
|
12
|
-
|
10
|
+
class Delete < Command
|
11
|
+
def description # :nodoc:
|
12
|
+
"Delete a note"
|
13
|
+
end
|
13
14
|
|
14
|
-
module Rbnotes
|
15
|
-
class Commands::Delete < Commands::Command
|
16
15
|
def execute(args, conf)
|
17
|
-
stamp = Rbnotes
|
16
|
+
stamp = Rbnotes.utils.read_timestamp(args)
|
18
17
|
|
19
18
|
repo = Textrepo.init(conf)
|
20
19
|
begin
|
@@ -27,5 +26,18 @@ module Rbnotes
|
|
27
26
|
puts "Delete [%s]" % stamp.to_s
|
28
27
|
end
|
29
28
|
end
|
29
|
+
|
30
|
+
def help # :nodoc:
|
31
|
+
puts <<HELP
|
32
|
+
usage:
|
33
|
+
#{Rbnotes::NAME} delete [TIMESTAMP]
|
34
|
+
|
35
|
+
Delete a given note. TIMESTAMP must be a fully qualified one, such
|
36
|
+
"20201016165130" or "20201016165130_012" if it has a suffix.
|
37
|
+
|
38
|
+
Delete reads its argument from the standard input when no argument was
|
39
|
+
passed in the command line.
|
40
|
+
HELP
|
41
|
+
end
|
30
42
|
end
|
31
43
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "pathname"
|
2
|
+
|
3
|
+
module Rbnotes::Commands
|
4
|
+
|
5
|
+
##
|
6
|
+
# Writes out a given note into a specified file. The file will be
|
7
|
+
# created in the current working directory unless an absolute path
|
8
|
+
# is specified as a filename.
|
9
|
+
#
|
10
|
+
# When no argument was passed, would try to read a timestamp string
|
11
|
+
# from the standard input.
|
12
|
+
|
13
|
+
class Export < Command
|
14
|
+
|
15
|
+
def description # :nodoc:
|
16
|
+
"Write out a note into a file"
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# :call-seq:
|
21
|
+
# execute([a String as timestring], Rbnotes::Conf or Hash) -> nil
|
22
|
+
|
23
|
+
def execute(args, conf)
|
24
|
+
stamp = Rbnotes.utils.read_timestamp(args)
|
25
|
+
|
26
|
+
repo = Textrepo.init(conf)
|
27
|
+
begin
|
28
|
+
content = repo.read(stamp)
|
29
|
+
rescue Textrepo::MissingTimestampError => _
|
30
|
+
raise MissingTimestampError, stamp
|
31
|
+
end
|
32
|
+
|
33
|
+
pathname = Pathname.new(args.shift || "#{stamp}.md")
|
34
|
+
pathname.parent.mkpath
|
35
|
+
pathname.open("w"){ |f| f.puts content }
|
36
|
+
puts "Export a note [%s] into a file [%s]" % [stamp, pathname]
|
37
|
+
end
|
38
|
+
|
39
|
+
def help # :nodoc:
|
40
|
+
puts <<HELP
|
41
|
+
usage:
|
42
|
+
#{Rbnotes::NAME} export [TIMESTAMP [FILENAME]]
|
43
|
+
|
44
|
+
Write out a given note into a specified file. TIMESTAMP must be a
|
45
|
+
fully qualified one, such "20201108141600", or "20201108141600_012" if
|
46
|
+
it has a suffix. FILENAME is optional. When it omitted, the filename
|
47
|
+
would be a timestamp string with ".md" as its extension, such
|
48
|
+
"20201108141600.md"
|
49
|
+
|
50
|
+
The file will be created into the current working directory unless an
|
51
|
+
absolute path is specified as FILENAME.
|
52
|
+
|
53
|
+
When no argument was passed, it would try to read a timestamp string
|
54
|
+
from the standard input. Then, FILENAME would be regarded as omitted.
|
55
|
+
HELP
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module Rbnotes::Commands
|
2
|
+
|
3
|
+
##
|
4
|
+
# Shows help message for the command which specifies with the
|
5
|
+
# argument.
|
6
|
+
|
7
|
+
class Help < Command
|
8
|
+
|
9
|
+
def description # :nodoc:
|
10
|
+
"Provide help on each command"
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# :call-seq:
|
15
|
+
# execute(["add"], Rbnotes::Conf or Hash) -> nil
|
16
|
+
# execute(["delete"], Rbnotes::Conf or Hash) -> nil
|
17
|
+
|
18
|
+
def execute(args, conf)
|
19
|
+
cmd_name = args.shift
|
20
|
+
case cmd_name
|
21
|
+
when nil
|
22
|
+
self.help
|
23
|
+
when "commands"
|
24
|
+
print_commands
|
25
|
+
else
|
26
|
+
Commands.load(cmd_name).help
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def help # :nodoc:
|
31
|
+
puts <<HELP
|
32
|
+
#{Rbnotes::NAME.capitalize} is a simple tool to write a note into a single repository.
|
33
|
+
|
34
|
+
When creates a new note, a timestamp is attached to the note. #{Rbnotes::NAME.capitalize}
|
35
|
+
manages notes with those timestamps, such update, delete, ...etc.
|
36
|
+
|
37
|
+
Timestamp is a series of digits which represents year, date, and time.
|
38
|
+
It looks like "20201106121100", means "2020-11-06 12:11:00". It is
|
39
|
+
generated in the local time.
|
40
|
+
|
41
|
+
usage:
|
42
|
+
#{Rbnotes::NAME} [option] [command] [args]
|
43
|
+
|
44
|
+
option:
|
45
|
+
-c, --conf [CONF_FILE] : specifiy the configuration file
|
46
|
+
-v, --version : print version
|
47
|
+
-h, --help : show this message
|
48
|
+
|
49
|
+
CONF_FILE must be written in YAML. To know about details of the
|
50
|
+
configuration file, see README.md or Wiki page.
|
51
|
+
|
52
|
+
Further help:
|
53
|
+
#{Rbnotes::NAME} help commands
|
54
|
+
#{Rbnotes::NAME} help COMMAND
|
55
|
+
#{Rbnotes::NAME} usage
|
56
|
+
|
57
|
+
Further information:
|
58
|
+
https://github.com/mnbi/rbnotes/wiki
|
59
|
+
HELP
|
60
|
+
end
|
61
|
+
|
62
|
+
# :stopdoc:
|
63
|
+
private
|
64
|
+
|
65
|
+
def print_commands
|
66
|
+
Dir.glob("*.rb", :base => __dir__) { |rb|
|
67
|
+
next if rb == "help.rb"
|
68
|
+
require_relative rb
|
69
|
+
}
|
70
|
+
commands = Commands.constants.difference([:Builtins, :Command])
|
71
|
+
builtins = Commands::Builtins.constants
|
72
|
+
|
73
|
+
puts "#{Rbnotes::NAME.capitalize} Commands:"
|
74
|
+
print_commands_desc(commands.sort)
|
75
|
+
puts
|
76
|
+
puts "for development purpose"
|
77
|
+
print_builtins_desc(builtins.sort)
|
78
|
+
end
|
79
|
+
|
80
|
+
def print_commands_desc(commands)
|
81
|
+
print_desc(Commands, commands)
|
82
|
+
end
|
83
|
+
|
84
|
+
def print_builtins_desc(builtins)
|
85
|
+
print_desc(Commands::Builtins, builtins)
|
86
|
+
end
|
87
|
+
|
88
|
+
def print_desc(mod, commands)
|
89
|
+
commands.map { |cmd|
|
90
|
+
name = "#{cmd.to_s.downcase} "[0, 8]
|
91
|
+
desc = mod.const_get(cmd, false).new.description
|
92
|
+
puts " #{name} #{desc}"
|
93
|
+
}
|
94
|
+
end
|
95
|
+
|
96
|
+
# :startdoc:
|
97
|
+
end
|
98
|
+
end
|
@@ -1,5 +1,29 @@
|
|
1
|
-
module Rbnotes
|
2
|
-
|
1
|
+
module Rbnotes::Commands
|
2
|
+
|
3
|
+
##
|
4
|
+
# Imports a existing file which specified by the argument as a note.
|
5
|
+
#
|
6
|
+
# A timestamp is generated referring to the birthtime of the given
|
7
|
+
# file. If birthtime is not available on the system, use mtime
|
8
|
+
# (modification time).
|
9
|
+
#
|
10
|
+
# Occasionally, there is another note which has the same timestmap
|
11
|
+
# in the repository. Then, tries to create a new timestamp with a
|
12
|
+
# suffix. Unluckily, when such timestamp with a suffix already
|
13
|
+
# exists, tries to create a new one with increasing suffix. Suffix
|
14
|
+
# will be "001", "002", ..., or "999". In worst case, all suffix
|
15
|
+
# might have been already used. Then, abandons to import.
|
16
|
+
|
17
|
+
class Import < Command
|
18
|
+
|
19
|
+
def description # :nodoc:
|
20
|
+
"Import a file as a note"
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# :call-seq:
|
25
|
+
# execute([PATHNAME], Rbnotes::Conf or Hash) -> nil
|
26
|
+
|
3
27
|
def execute(args, conf)
|
4
28
|
file = args.shift
|
5
29
|
unless file.nil?
|
@@ -58,5 +82,18 @@ module Rbnotes
|
|
58
82
|
super
|
59
83
|
end
|
60
84
|
end
|
85
|
+
|
86
|
+
def help # :nodoc:
|
87
|
+
puts <<HELP
|
88
|
+
usage:
|
89
|
+
#{Rbnotes::NAME} import FILE
|
90
|
+
|
91
|
+
Imports a existing file which specified by the argument as a note.
|
92
|
+
|
93
|
+
A timestamp is generated referring to the birthtime of the given file.
|
94
|
+
If birthtime is not available on the system, use mtime (modification
|
95
|
+
time).
|
96
|
+
HELP
|
97
|
+
end
|
61
98
|
end
|
62
99
|
end
|
@@ -1,22 +1,35 @@
|
|
1
|
-
|
2
|
-
require "io/console/size"
|
1
|
+
module Rbnotes::Commands
|
3
2
|
|
4
|
-
module Rbnotes
|
5
3
|
##
|
6
4
|
# Defines `list` command for `rbnotes`. See the document of execute
|
7
5
|
# method to know about the behavior of this command.
|
8
6
|
|
9
|
-
class
|
7
|
+
class List < Command
|
8
|
+
|
9
|
+
def description # :nodoc:
|
10
|
+
"List notes"
|
11
|
+
end
|
10
12
|
|
11
13
|
##
|
12
|
-
# Shows
|
13
|
-
# optional. If
|
14
|
-
# timestamp
|
15
|
-
#
|
16
|
-
#
|
14
|
+
# Shows a list of notes in the repository. Arguments are
|
15
|
+
# optional. If several args are passed, each of them must be a
|
16
|
+
# timestamp pattern or a keyword.
|
17
|
+
#
|
18
|
+
# Any order of timestamp patterns and keywords mixture is
|
19
|
+
# acceptable. The redundant patterns are just ignored.
|
20
|
+
#
|
21
|
+
# A timestamp pattern is a string which would match several
|
22
|
+
# Timestamp objects. A timestamp is an instance of
|
23
|
+
# Textrepo::Timestamp class.
|
17
24
|
#
|
18
|
-
#
|
19
|
-
#
|
25
|
+
# A keyword must be one of them:
|
26
|
+
#
|
27
|
+
# - "today" (or "to")
|
28
|
+
# - "yeasterday" (or "ye")
|
29
|
+
# - "this_week" (or "tw")
|
30
|
+
# - "last_week" (or "lw")
|
31
|
+
#
|
32
|
+
# Here is several examples of timestamp patterns.
|
20
33
|
#
|
21
34
|
# "20201027093600_012": a complete string to represent a timestamp
|
22
35
|
# - this pattern would match exactly one Timestamp object
|
@@ -38,63 +51,41 @@ module Rbnotes
|
|
38
51
|
# execute(Array, Rbnotes::Conf or Hash) -> nil
|
39
52
|
|
40
53
|
def execute(args, conf)
|
41
|
-
|
42
|
-
|
54
|
+
patterns = Rbnotes.utils.expand_keyword_in_args(args)
|
43
55
|
@repo = Textrepo.init(conf)
|
44
56
|
# newer stamp shoud be above
|
45
|
-
|
46
|
-
|
47
|
-
puts make_headline(timestamp)
|
57
|
+
Rbnotes.utils.find_notes(patterns, @repo).each { |timestamp|
|
58
|
+
puts Rbnotes.utils.make_headline(timestamp, @repo.read(timestamp))
|
48
59
|
}
|
49
60
|
end
|
50
61
|
|
51
|
-
# :
|
62
|
+
def help # :nodoc:
|
63
|
+
puts <<HELP
|
64
|
+
usage:
|
65
|
+
#{Rbnotes::NAME} list [STAMP_PATTERN|KEYWORD]
|
52
66
|
|
53
|
-
|
54
|
-
|
67
|
+
Show a list of notes. When no arguments, make a list with all notes
|
68
|
+
in the repository. When specified STAMP_PATTERN, only those match the
|
69
|
+
pattern are listed. Instead of STAMP_PATTERN, some KEYWORDs could be
|
70
|
+
used.
|
55
71
|
|
56
|
-
|
57
|
-
# Makes a headline with the timestamp and subject of the notes, it
|
58
|
-
# looks like as follows:
|
59
|
-
#
|
60
|
-
# |<------------------ console column size ------------------->|
|
61
|
-
# +-- timestamp ---+ +- subject (the 1st line of each note) -+
|
62
|
-
# | | | |
|
63
|
-
# 20101010001000_123: I love Macintosh. [EOL]
|
64
|
-
# 20100909090909_999: This is very very long long loooong subje[EOL]
|
65
|
-
# ++
|
66
|
-
# ^--- delimiter (2 characters)
|
67
|
-
#
|
68
|
-
# The subject part will truncate when it is long.
|
72
|
+
STAMP_PATTERN must be:
|
69
73
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
+
(a) full qualified timestamp (with suffix): "20201030160200"
|
75
|
+
(b) year and date part: "20201030"
|
76
|
+
(c) year and month part: "202010"
|
77
|
+
(d) year part only: "2020"
|
78
|
+
(e) date part only: "1030"
|
74
79
|
|
75
|
-
|
80
|
+
KEYWORD:
|
76
81
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
end
|
82
|
-
|
83
|
-
def truncate_str(str, size)
|
84
|
-
count = 0
|
85
|
-
result = ""
|
86
|
-
str.each_char { |c|
|
87
|
-
count += Unicode::DisplayWidth.of(c)
|
88
|
-
break if count > size
|
89
|
-
result << c
|
90
|
-
}
|
91
|
-
result
|
92
|
-
end
|
82
|
+
- "today" (or "to")
|
83
|
+
- "yeasterday" (or "ye")
|
84
|
+
- "this_week" (or "tw")
|
85
|
+
- "last_week" (or "lw")
|
93
86
|
|
94
|
-
|
95
|
-
str.sub(/^#+ +/, '')
|
87
|
+
HELP
|
96
88
|
end
|
97
89
|
|
98
|
-
# :startdoc:
|
99
90
|
end
|
100
91
|
end
|