rbnotes 0.2.1 → 0.4.1
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 +20 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +4 -4
- data/README.md +60 -5
- data/conf/config.yml +7 -0
- data/conf/config_deve.yml +7 -0
- data/conf/config_test.yml +5 -0
- data/exe/rbnotes +30 -4
- data/lib/rbnotes/commands.rb +60 -20
- data/lib/rbnotes/commands/add.rb +51 -9
- data/lib/rbnotes/commands/delete.rb +10 -14
- data/lib/rbnotes/commands/import.rb +3 -3
- data/lib/rbnotes/commands/list.rb +61 -18
- data/lib/rbnotes/commands/search.rb +44 -0
- data/lib/rbnotes/commands/show.rb +12 -16
- data/lib/rbnotes/commands/update.rb +5 -14
- data/lib/rbnotes/error.rb +6 -4
- data/lib/rbnotes/utils.rb +35 -0
- data/lib/rbnotes/version.rb +2 -2
- data/rbnotes.gemspec +1 -1
- metadata +8 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: '09f402dd55c244533d8f8ba3518df3d13cde1c5952efad94598f210a43a88c11'
|
|
4
|
+
data.tar.gz: a824e8e2e33c1c1e67d2ff27ab22618d83aa7138a889db20d43f40491f6e1c82
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e52be521f1d1502ca2d36fd6853203436e15c3f830e17ff509529b0b7f08a2fad26a264714d81732603e6f6dabe69c2d40bebdb400b306f4fba1218c86c2ac52
|
|
7
|
+
data.tar.gz: c555b3b74717d45d18a6d63959356ff411457b2f3b47819ca53ecca38fc6de03a9e5d5839e622e5b65d3513c40dfe487b2649206697d075400c1121a61ea99d4
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/).
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
Nothing to record here.
|
|
9
9
|
|
|
10
|
+
## [0.4.1] - 2020-11-04
|
|
11
|
+
### Added
|
|
12
|
+
- Add a feature to accept a timestamp in `add` command. (#34)
|
|
13
|
+
|
|
14
|
+
## [0.4.0] - 2020-11-03
|
|
15
|
+
### Added
|
|
16
|
+
- Add a new command `search` to perform full text search. (#33)
|
|
17
|
+
|
|
18
|
+
## [0.3.1] - 2020-10-30
|
|
19
|
+
### Added
|
|
20
|
+
- Add feature to specify configuration file in the command line. (#21)
|
|
21
|
+
|
|
22
|
+
## [0.3.0] - 2020-10-29
|
|
23
|
+
### Added
|
|
24
|
+
- Add feature to read argument from the standard input. (#27)
|
|
25
|
+
|
|
26
|
+
## [0.2.2] - 2020-10-27
|
|
27
|
+
### Added
|
|
28
|
+
- Add feature to accept a timestamp pattern in `list` command. (#22)
|
|
29
|
+
|
|
10
30
|
## [0.2.1] - 2020-10-25
|
|
11
31
|
### Added
|
|
12
32
|
- Add feature to load the configuration from an external file.
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
rbnotes (0.
|
|
5
|
-
textrepo (~> 0.
|
|
4
|
+
rbnotes (0.4.1)
|
|
5
|
+
textrepo (~> 0.5)
|
|
6
6
|
unicode-display_width (~> 1.7)
|
|
7
7
|
|
|
8
8
|
GEM
|
|
@@ -10,7 +10,7 @@ GEM
|
|
|
10
10
|
specs:
|
|
11
11
|
minitest (5.14.2)
|
|
12
12
|
rake (13.0.1)
|
|
13
|
-
textrepo (0.
|
|
13
|
+
textrepo (0.5.3)
|
|
14
14
|
unicode-display_width (1.7.0)
|
|
15
15
|
|
|
16
16
|
PLATFORMS
|
|
@@ -20,7 +20,7 @@ DEPENDENCIES
|
|
|
20
20
|
minitest (~> 5.0)
|
|
21
21
|
rake (~> 13.0)
|
|
22
22
|
rbnotes!
|
|
23
|
-
textrepo (~> 0.
|
|
23
|
+
textrepo (~> 0.5)
|
|
24
24
|
|
|
25
25
|
BUNDLED WITH
|
|
26
26
|
2.1.4
|
data/README.md
CHANGED
|
@@ -4,18 +4,25 @@
|
|
|
4
4
|
|
|
5
5
|
Rbnotes is a simple utility to write a note in the single repository.
|
|
6
6
|
|
|
7
|
+
This document provides the basic information to use rbnotes.
|
|
8
|
+
You may find more useful information in [Wiki pages](https://github.com/mnbi/rbnotes/wiki).
|
|
9
|
+
|
|
7
10
|
## Installation
|
|
8
11
|
|
|
9
12
|
Add this line to your application's Gemfile:
|
|
10
13
|
|
|
11
14
|
```ruby
|
|
12
|
-
gem 'rbnotes
|
|
15
|
+
gem 'rbnotes'
|
|
13
16
|
```
|
|
14
17
|
|
|
15
18
|
And then execute:
|
|
16
19
|
|
|
17
20
|
$ bundle install
|
|
18
21
|
|
|
22
|
+
Or install it yourself as:
|
|
23
|
+
|
|
24
|
+
$ gem install rbnotes
|
|
25
|
+
|
|
19
26
|
## Usage
|
|
20
27
|
|
|
21
28
|
General syntax is:
|
|
@@ -24,12 +31,19 @@ General syntax is:
|
|
|
24
31
|
rbnotes [global_opts] [command] [command_opts] [args]
|
|
25
32
|
```
|
|
26
33
|
|
|
34
|
+
### Global options
|
|
35
|
+
|
|
36
|
+
- "-c CONF_FILE", "--conf"
|
|
37
|
+
- specifies a configuration file
|
|
38
|
+
|
|
27
39
|
### Commands
|
|
28
40
|
|
|
29
41
|
- import
|
|
30
42
|
- imports existing files
|
|
31
43
|
- list
|
|
32
44
|
- lists notes in the repository with their timestamps and subject
|
|
45
|
+
- search
|
|
46
|
+
- search a word (or words) in the repository
|
|
33
47
|
- show
|
|
34
48
|
- shows the content of a note
|
|
35
49
|
- add
|
|
@@ -53,6 +67,8 @@ Searches the configuration file in the following order:
|
|
|
53
67
|
|
|
54
68
|
None of them is found, then the default configuration is used.
|
|
55
69
|
|
|
70
|
+
The global option "-c CONF_FILE" will override the location.
|
|
71
|
+
|
|
56
72
|
#### Content
|
|
57
73
|
|
|
58
74
|
The format of the configuration file must be written in YAML.
|
|
@@ -71,14 +87,16 @@ A real example:
|
|
|
71
87
|
|
|
72
88
|
``` yaml
|
|
73
89
|
---
|
|
74
|
-
:run_mode: :
|
|
90
|
+
:run_mode: :production
|
|
75
91
|
:repository_type: :file_system
|
|
76
92
|
:repository_name: "notes"
|
|
77
93
|
:repository_base: "~"
|
|
78
|
-
:pager: "bat"
|
|
94
|
+
:pager: "bat -l md"
|
|
79
95
|
:editor: "/usr/local/bin/emacsclient"
|
|
80
96
|
```
|
|
81
97
|
|
|
98
|
+
The content is identical to `conf/config.yml`.
|
|
99
|
+
|
|
82
100
|
#### Variables
|
|
83
101
|
|
|
84
102
|
##### :run-mode (mandatory)
|
|
@@ -120,13 +138,50 @@ the `:repository_name` value. It would be:
|
|
|
120
138
|
|
|
121
139
|
> :repository_base/:repository_name
|
|
122
140
|
|
|
123
|
-
The value
|
|
124
|
-
|
|
141
|
+
The value is recommended to be an absolute path in the production
|
|
142
|
+
time, since relative path will be expanded with the current working
|
|
143
|
+
directory. However, in the development and testing time, the relative
|
|
144
|
+
path may be useful. See `conf/config_deve.yml` or
|
|
145
|
+
`conf/config_test.yml`.
|
|
146
|
+
|
|
147
|
+
The short-hand notation of the home directory ("~") is usable.
|
|
125
148
|
|
|
126
149
|
##### Miscellaneous variables (optional)
|
|
127
150
|
|
|
128
151
|
- :pager : specify a pager program
|
|
129
152
|
- :editor : specify a editor program
|
|
153
|
+
- :searcher: specify a program to perform search
|
|
154
|
+
- :searcher_options: specify options to pass to the searcher program
|
|
155
|
+
|
|
156
|
+
Be careful to set `:searcher` and `:searcher_options`. The searcher
|
|
157
|
+
program must be expected to behave equivalent to `grep` with `-inRE`.
|
|
158
|
+
At least, its output must be the same format and it must runs
|
|
159
|
+
recursively to a directory.
|
|
160
|
+
|
|
161
|
+
If your favorite searcher is one of the followings, see the default
|
|
162
|
+
options which `textrepo` sets for those searchers. In most cases, you
|
|
163
|
+
don't have to set `:searcher_options` for them.
|
|
164
|
+
|
|
165
|
+
| searcher | default options in `textrepo` |
|
|
166
|
+
|:---------|:---------------------------------------------------|
|
|
167
|
+
| `grep` | `["-i", "-n", "-R", "-E"]` |
|
|
168
|
+
| `egrep` | `["-i", "-n", "-R"]` |
|
|
169
|
+
| `ggrep` | `["-i", "-n", "-R", "-E"]` |
|
|
170
|
+
| `gegrep` | `["-i", "-n", "-R"]` |
|
|
171
|
+
| `rg` | `["-S", "-n", "--no-heading", "--color", "never"]` |
|
|
172
|
+
|
|
173
|
+
Those searcher names are used in macOS (with Homebrew). Any other OS
|
|
174
|
+
might use different names.
|
|
175
|
+
|
|
176
|
+
- `grep` and `egrep` -> BSD grep (macOS default)
|
|
177
|
+
- `ggrep` and `gegrep` -> GNU grep
|
|
178
|
+
- `rg` -> ripgrep
|
|
179
|
+
|
|
180
|
+
If the name is different, `:searcher_options` should be set with the
|
|
181
|
+
same value. For example, if you system use the name `gnugrep` as GNU
|
|
182
|
+
grep, and you want to use it as the searcher (that is, set `gnugrep`
|
|
183
|
+
to `:searcher`), you should set `:searcher_options` value with `["-i",
|
|
184
|
+
"-n", "-R", "-E"]`.
|
|
130
185
|
|
|
131
186
|
##### Default values for mandatory variables
|
|
132
187
|
|
data/conf/config.yml
ADDED
data/exe/rbnotes
CHANGED
|
@@ -4,20 +4,46 @@ require "rbnotes"
|
|
|
4
4
|
|
|
5
5
|
include Rbnotes
|
|
6
6
|
|
|
7
|
-
DEBUG = true
|
|
8
|
-
|
|
9
7
|
class App
|
|
8
|
+
def initialize
|
|
9
|
+
@gopts = {}
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def options
|
|
13
|
+
@gopts
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def parse_global_options(args)
|
|
17
|
+
while args.size > 0
|
|
18
|
+
arg = args.shift
|
|
19
|
+
case arg
|
|
20
|
+
when "-c", "--conf"
|
|
21
|
+
file = args.shift
|
|
22
|
+
raise ArgumentError, args.unshift(arg) if file.nil?
|
|
23
|
+
file = File.expand_path(file)
|
|
24
|
+
raise ArgumentError, "no such file: %s" % file unless FileTest.exist?(file)
|
|
25
|
+
@gopts[:conf_file] = file
|
|
26
|
+
else
|
|
27
|
+
args.unshift(arg)
|
|
28
|
+
break
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
10
33
|
def run(args)
|
|
11
34
|
cmd = args.shift
|
|
12
|
-
Commands.load(cmd).execute(args, Rbnotes.conf)
|
|
35
|
+
Commands.load(cmd).execute(args, Rbnotes.conf(@gopts[:conf_file]))
|
|
13
36
|
end
|
|
14
37
|
end
|
|
15
38
|
|
|
16
39
|
app = App.new
|
|
17
40
|
begin
|
|
41
|
+
app.parse_global_options(ARGV)
|
|
18
42
|
app.run(ARGV)
|
|
19
43
|
rescue MissingArgumentError, MissingTimestampError,
|
|
20
|
-
NoEditorError, ProgramAbortError
|
|
44
|
+
NoEditorError, ProgramAbortError,
|
|
45
|
+
Textrepo::InvalidTimestampStringError,
|
|
46
|
+
ArgumentError => e
|
|
21
47
|
puts e.message
|
|
22
48
|
exit 1
|
|
23
49
|
end
|
data/lib/rbnotes/commands.rb
CHANGED
|
@@ -2,16 +2,20 @@ module Rbnotes
|
|
|
2
2
|
##
|
|
3
3
|
# This module defines all command classes of rbnotes. Each command
|
|
4
4
|
# class must be derived from Rbnotes::Commands::Command class.
|
|
5
|
+
|
|
5
6
|
module Commands
|
|
6
7
|
##
|
|
7
8
|
# The base class for a command class.
|
|
9
|
+
|
|
8
10
|
class Command
|
|
11
|
+
|
|
9
12
|
##
|
|
10
13
|
# :call-seq:
|
|
11
|
-
# Array, Hash -> nil
|
|
14
|
+
# execute(Array, Hash) -> nil
|
|
15
|
+
#
|
|
12
16
|
# - Array: arguments for each command
|
|
13
17
|
# - Hash : rbnotes configuration
|
|
14
|
-
|
|
18
|
+
|
|
15
19
|
def execute(args, conf)
|
|
16
20
|
Builtins::DEFAULT_CMD.new.execute(args, conf)
|
|
17
21
|
end
|
|
@@ -28,20 +32,55 @@ module Rbnotes
|
|
|
28
32
|
class Help < Command
|
|
29
33
|
def execute(_, _)
|
|
30
34
|
puts <<USAGE
|
|
31
|
-
usage:
|
|
35
|
+
usage:
|
|
36
|
+
rbnotes [-c|--conf CONF_FILE] [command] [args]
|
|
37
|
+
|
|
38
|
+
option:
|
|
39
|
+
-c, --conf [CONF_FILE] : specifiy the configuration file
|
|
40
|
+
|
|
41
|
+
CONF_FILE must be written in YAML. To know about details of the
|
|
42
|
+
configuration file, see README.md or Wiki page.
|
|
32
43
|
|
|
33
44
|
command:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
+
add : create a new note
|
|
46
|
+
import FILE : import a FILE into the repository
|
|
47
|
+
|
|
48
|
+
list [STAMP_PATTERN] : list notes those timestamp matches PATTERN
|
|
49
|
+
search PATTERN [STAMP_PATTERN] : search PATTERN
|
|
50
|
+
|
|
51
|
+
STAMP_PATTERN must be:
|
|
52
|
+
|
|
53
|
+
(a) full qualified timestamp (with suffix): "20201030160200"
|
|
54
|
+
(b) year and date part: "20201030"
|
|
55
|
+
(c) year and month part: "202010"
|
|
56
|
+
(d) year part only: "2020"
|
|
57
|
+
(e) date part only: "1030"
|
|
58
|
+
|
|
59
|
+
PATTERN is a word (or words) to search, it may also be a regular
|
|
60
|
+
expression.
|
|
61
|
+
|
|
62
|
+
show [STAMP] : show the note specified with STAMP
|
|
63
|
+
update [STAMP] : edit the note with external editor
|
|
64
|
+
delete [STAMP] : delete the note specified with STAMP
|
|
65
|
+
|
|
66
|
+
STAMP must be a sequence of digits to represent year, date and
|
|
67
|
+
time (and suffix), such "20201030160200" or "20201030160200_012".
|
|
68
|
+
|
|
69
|
+
show/update/delete reads its argument from the standard input when
|
|
70
|
+
no argument was passed in the command line.
|
|
71
|
+
|
|
72
|
+
version : print version
|
|
73
|
+
help : show help
|
|
74
|
+
|
|
75
|
+
command for development:
|
|
76
|
+
conf : print the current configuraitons
|
|
77
|
+
repo : print the repository path
|
|
78
|
+
stamp TIME_STR : convert TIME_STR into a timestamp
|
|
79
|
+
time STAMP : convert STAMP into a time string
|
|
80
|
+
|
|
81
|
+
For more information, see Wiki page.
|
|
82
|
+
- https://github.com/mnbi/rbnotes/wiki
|
|
83
|
+
|
|
45
84
|
USAGE
|
|
46
85
|
end
|
|
47
86
|
end
|
|
@@ -62,9 +101,9 @@ USAGE
|
|
|
62
101
|
|
|
63
102
|
puts case type
|
|
64
103
|
when :file_system
|
|
65
|
-
|
|
104
|
+
File.expand_path(name, base)
|
|
66
105
|
else
|
|
67
|
-
|
|
106
|
+
File.join(base, name)
|
|
68
107
|
end
|
|
69
108
|
end
|
|
70
109
|
end
|
|
@@ -111,15 +150,16 @@ USAGE
|
|
|
111
150
|
# :startdoc:
|
|
112
151
|
|
|
113
152
|
class << self
|
|
153
|
+
|
|
114
154
|
##
|
|
115
155
|
# Loads a class to perfom the command, then returns an instance
|
|
116
156
|
# of the class.
|
|
117
157
|
#
|
|
118
158
|
# :call-seq:
|
|
119
|
-
# "import" ->
|
|
120
|
-
# "list" ->
|
|
121
|
-
# "show" ->
|
|
122
|
-
|
|
159
|
+
# load("import") -> Rbnotes::Commnads::Import
|
|
160
|
+
# load("list") -> Rbnotes::Commands::List
|
|
161
|
+
# load("show") -> Rbnotes::Commands::Show
|
|
162
|
+
|
|
123
163
|
def load(cmd_name)
|
|
124
164
|
cmd_name ||= DEFAULT_CMD_NAME
|
|
125
165
|
klass_name = cmd_name.capitalize
|
data/lib/rbnotes/commands/add.rb
CHANGED
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
module Rbnotes::Commands
|
|
2
|
+
|
|
2
3
|
##
|
|
3
|
-
# Adds a new note to the repository.
|
|
4
|
-
# at the execution time, then it is attached to the
|
|
5
|
-
# timestamp has already existed in the repository, the
|
|
6
|
-
# fails.
|
|
4
|
+
# Adds a new note to the repository. If no options, a new timestamp
|
|
5
|
+
# is generated at the execution time, then it is attached to the
|
|
6
|
+
# note. If the timestamp has already existed in the repository, the
|
|
7
|
+
# command fails.
|
|
8
|
+
#
|
|
9
|
+
# Accepts an option with `-t STAMP_PATTERN` (or `--timestamp`), a
|
|
10
|
+
# timestamp is generated according to `STAMP_PATTERN`.
|
|
11
|
+
#
|
|
12
|
+
# STAMP_PATTERN could be one of followings:
|
|
13
|
+
#
|
|
14
|
+
# "20201104172230_078" : full qualified timestamp string
|
|
15
|
+
# "20201104172230" : full qualified timestamp string (no suffix)
|
|
16
|
+
# "202011041722" : year, date and time (omit second part)
|
|
17
|
+
# "11041722" : date and time (omit year and second part)
|
|
7
18
|
#
|
|
8
19
|
# This command starts the external editor program to prepare text to
|
|
9
20
|
# store. The editor program will be searched in the following order:
|
|
@@ -14,23 +25,37 @@ module Rbnotes::Commands
|
|
|
14
25
|
# 4. "vi"
|
|
15
26
|
#
|
|
16
27
|
# If none of the above editor is available, the command fails.
|
|
17
|
-
|
|
28
|
+
|
|
18
29
|
class Add < Command
|
|
19
30
|
include ::Rbnotes::Utils
|
|
20
31
|
|
|
21
32
|
def execute(args, conf)
|
|
22
|
-
|
|
33
|
+
@opts = {}
|
|
34
|
+
while args.size > 0
|
|
35
|
+
arg = args.shift
|
|
36
|
+
case arg
|
|
37
|
+
when "-t", "--timestamp"
|
|
38
|
+
stamp_str = args.shift
|
|
39
|
+
raise ArgumentError, "missing timestamp: %s" % args.unshift(arg) if stamp_str.nil?
|
|
40
|
+
stamp_str = complement_timestamp_pattern(stamp_str)
|
|
41
|
+
@opts[:timestamp] = Textrepo::Timestamp.parse_s(stamp_str)
|
|
42
|
+
else
|
|
43
|
+
args.unshift(arg)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
stamp = @opts[:timestamp] || Textrepo::Timestamp.new(Time.now)
|
|
23
48
|
|
|
24
49
|
candidates = [conf[:editor], ENV["EDITOR"], "nano", "vi"].compact
|
|
25
50
|
editor = find_program(candidates)
|
|
26
51
|
raise Rbnotes::NoEditorError, candidates if editor.nil?
|
|
27
52
|
|
|
28
|
-
tmpfile = run_with_tmpfile(editor,
|
|
53
|
+
tmpfile = run_with_tmpfile(editor, stamp.to_s)
|
|
29
54
|
text = File.readlines(tmpfile)
|
|
30
55
|
|
|
31
56
|
repo = Textrepo.init(conf)
|
|
32
57
|
begin
|
|
33
|
-
repo.create(
|
|
58
|
+
repo.create(stamp, text)
|
|
34
59
|
rescue Textrepo::DuplicateTimestampError => e
|
|
35
60
|
puts e.message
|
|
36
61
|
puts "Just wait a second, then retry."
|
|
@@ -39,11 +64,28 @@ module Rbnotes::Commands
|
|
|
39
64
|
rescue StandardError => e
|
|
40
65
|
puts e.message
|
|
41
66
|
else
|
|
42
|
-
puts "Add a note [%s]" %
|
|
67
|
+
puts "Add a note [%s]" % stamp.to_s
|
|
43
68
|
ensure
|
|
44
69
|
# Don't forget to remove the temporary file.
|
|
45
70
|
File.delete(tmpfile)
|
|
46
71
|
end
|
|
47
72
|
end
|
|
73
|
+
|
|
74
|
+
# :stopdoc:
|
|
75
|
+
private
|
|
76
|
+
def complement_timestamp_pattern(pattern)
|
|
77
|
+
stamp_str = nil
|
|
78
|
+
case pattern.to_s.size
|
|
79
|
+
when "yyyymoddhhmiss_lll".size, "yyyymoddhhmiss".size
|
|
80
|
+
stamp_str = pattern.dup
|
|
81
|
+
when "yyyymoddhhmi".size # omit sec part
|
|
82
|
+
stamp_str = "#{pattern}00"
|
|
83
|
+
when "moddhhmi".size # omit year and sec part
|
|
84
|
+
stamp_str = "#{Time.now.year}#{pattern}00"
|
|
85
|
+
else
|
|
86
|
+
raise Textrepo::InvalidTimestampStringError, pattern
|
|
87
|
+
end
|
|
88
|
+
stamp_str
|
|
89
|
+
end
|
|
48
90
|
end
|
|
49
91
|
end
|
|
@@ -14,21 +14,17 @@
|
|
|
14
14
|
module Rbnotes
|
|
15
15
|
class Commands::Delete < Commands::Command
|
|
16
16
|
def execute(args, conf)
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
puts e.message
|
|
27
|
-
else
|
|
28
|
-
puts "Delete [%s]" % stamp.to_s
|
|
29
|
-
end
|
|
17
|
+
stamp = Rbnotes::Utils.read_timestamp(args)
|
|
18
|
+
|
|
19
|
+
repo = Textrepo.init(conf)
|
|
20
|
+
begin
|
|
21
|
+
repo.delete(stamp)
|
|
22
|
+
rescue Textrepo::MissingTimestampError => e
|
|
23
|
+
puts e.message
|
|
24
|
+
rescue StandardError => e
|
|
25
|
+
puts e.message
|
|
30
26
|
else
|
|
31
|
-
|
|
27
|
+
puts "Delete [%s]" % stamp.to_s
|
|
32
28
|
end
|
|
33
29
|
end
|
|
34
30
|
end
|
|
@@ -29,7 +29,7 @@ module Rbnotes
|
|
|
29
29
|
puts "The note [%s] in the repository exactly matches" \
|
|
30
30
|
" the specified file." % stamp
|
|
31
31
|
puts "It seems there is no need to import the file [%s]." % file
|
|
32
|
-
|
|
32
|
+
break
|
|
33
33
|
else
|
|
34
34
|
puts "The text in the repository does not match the" \
|
|
35
35
|
" specified file."
|
|
@@ -40,8 +40,8 @@ module Rbnotes
|
|
|
40
40
|
end
|
|
41
41
|
rescue Textrepo::EmptyTextError => _
|
|
42
42
|
puts "... aborted."
|
|
43
|
-
puts "The specified file is
|
|
44
|
-
|
|
43
|
+
puts "The specified file is empty."
|
|
44
|
+
break
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
if count > 999
|
|
@@ -2,42 +2,79 @@ require "unicode/display_width"
|
|
|
2
2
|
require "io/console/size"
|
|
3
3
|
|
|
4
4
|
module Rbnotes
|
|
5
|
+
##
|
|
6
|
+
# Defines `list` command for `rbnotes`. See the document of execute
|
|
7
|
+
# method to know about the behavior of this command.
|
|
8
|
+
|
|
5
9
|
class Commands::List < Commands::Command
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
# Shows the list of notes in the repository. The only argument is
|
|
13
|
+
# optional. If it passed, it must be an timestamp pattern. A
|
|
14
|
+
# timestamp is an instance of Textrepo::Timestamp class. A
|
|
15
|
+
# timestamp pattern is a string which would match several
|
|
16
|
+
# Timestamp objects.
|
|
17
|
+
#
|
|
18
|
+
# Here is
|
|
19
|
+
# several examples of timestamp patterns.
|
|
20
|
+
#
|
|
21
|
+
# "20201027093600_012": a complete string to represent a timestamp
|
|
22
|
+
# - this pattern would match exactly one Timestamp object
|
|
23
|
+
#
|
|
24
|
+
# "20201027": specifies year and date
|
|
25
|
+
# - all Timestamps those have the same year and date would match
|
|
26
|
+
#
|
|
27
|
+
# "202011": specifies year and month
|
|
28
|
+
# - all Timestamps those have the same year and month would match
|
|
29
|
+
#
|
|
30
|
+
# "2020": specifies year only
|
|
31
|
+
# - all Timestamps those have the same year would match
|
|
32
|
+
#
|
|
33
|
+
# "1027": specifies date only
|
|
34
|
+
# - all Timestamps those have the same date would match, even if
|
|
35
|
+
# they have the different year.
|
|
36
|
+
#
|
|
37
|
+
# :call-seq:
|
|
38
|
+
# execute(Array, Rbnotes::Conf or Hash) -> nil
|
|
39
|
+
|
|
6
40
|
def execute(args, conf)
|
|
7
|
-
|
|
8
|
-
max = (args.shift || @row - 3).to_i
|
|
41
|
+
pattern = args.shift # `nil` is acceptable
|
|
9
42
|
|
|
10
43
|
@repo = Textrepo.init(conf)
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
44
|
+
# newer stamp shoud be above
|
|
45
|
+
stamps = @repo.entries(pattern).sort{|a, b| b <=> a}
|
|
46
|
+
stamps.each { |timestamp|
|
|
47
|
+
puts make_headline(timestamp)
|
|
14
48
|
}
|
|
15
49
|
end
|
|
16
50
|
|
|
51
|
+
# :stopdoc:
|
|
52
|
+
|
|
17
53
|
private
|
|
18
54
|
TIMESTAMP_STR_MAX_WIDTH = "yyyymoddhhmiss_sfx".size
|
|
55
|
+
|
|
56
|
+
##
|
|
19
57
|
# Makes a headline with the timestamp and subject of the notes, it
|
|
20
58
|
# looks like as follows:
|
|
21
59
|
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
25
|
-
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
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)
|
|
29
67
|
#
|
|
30
68
|
# The subject part will truncate when it is long.
|
|
31
|
-
def make_headline(timestamp_str)
|
|
32
69
|
|
|
70
|
+
def make_headline(timestamp)
|
|
71
|
+
_, column = IO.console_size
|
|
33
72
|
delimiter = ": "
|
|
34
|
-
subject_width =
|
|
73
|
+
subject_width = column - TIMESTAMP_STR_MAX_WIDTH - delimiter.size - 1
|
|
35
74
|
|
|
36
|
-
subject = @repo.read(
|
|
37
|
-
prefix = '# '
|
|
38
|
-
subject = prefix + subject.lstrip if subject[0, 2] != prefix
|
|
75
|
+
subject = remove_heading_markup(@repo.read(timestamp)[0])
|
|
39
76
|
|
|
40
|
-
ts_part = "#{
|
|
77
|
+
ts_part = "#{timestamp.to_s} "[0..(TIMESTAMP_STR_MAX_WIDTH - 1)]
|
|
41
78
|
sj_part = truncate_str(subject, subject_width)
|
|
42
79
|
|
|
43
80
|
ts_part + delimiter + sj_part
|
|
@@ -53,5 +90,11 @@ module Rbnotes
|
|
|
53
90
|
}
|
|
54
91
|
result
|
|
55
92
|
end
|
|
93
|
+
|
|
94
|
+
def remove_heading_markup(str)
|
|
95
|
+
str.sub(/^#+ +/, '')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# :startdoc:
|
|
56
99
|
end
|
|
57
100
|
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
module Rbnotes
|
|
2
|
+
|
|
3
|
+
##
|
|
4
|
+
# Searches a given pattern in notes those have timestamps match a
|
|
5
|
+
# given timestamp pattern. The first argument is a pattern to search.
|
|
6
|
+
# It is a String object represents a portion of text or it may a
|
|
7
|
+
# String represents a regular expression. The second argument is
|
|
8
|
+
# optional and it is a timestamp pattern to specify the search target.
|
|
9
|
+
#
|
|
10
|
+
# A pattern for search is mandatory. If no pattern, raises
|
|
11
|
+
# Rbnotes::MissingArgumentError.
|
|
12
|
+
#
|
|
13
|
+
# Example of PATTERN for search:
|
|
14
|
+
#
|
|
15
|
+
# "rbnotes" (a word)
|
|
16
|
+
# "macOS Big Sur" (a few words)
|
|
17
|
+
# "2[0-9]{3}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])" (a regular expression)
|
|
18
|
+
#
|
|
19
|
+
# A timestamp pattern is optional. If no timestamp pattern, all notes
|
|
20
|
+
# in the repository would be target of search.
|
|
21
|
+
#
|
|
22
|
+
# See the document of `Rbnotes::Commands::List#execute` to know about
|
|
23
|
+
# a timestamp pattern.
|
|
24
|
+
|
|
25
|
+
class Commands::Search < Commands::Command
|
|
26
|
+
def execute(args, conf)
|
|
27
|
+
pattern = args.shift
|
|
28
|
+
raise MissingArgumentError, args if pattern.nil?
|
|
29
|
+
|
|
30
|
+
timestamp_pattern = args.shift # `nil` is acceptable
|
|
31
|
+
|
|
32
|
+
repo = Textrepo.init(conf)
|
|
33
|
+
begin
|
|
34
|
+
result = repo.search(pattern, timestamp_pattern)
|
|
35
|
+
rescue Textrepo::InvalidSearchResultError => e
|
|
36
|
+
puts e.message
|
|
37
|
+
else
|
|
38
|
+
result.each { |stamp, num, match|
|
|
39
|
+
puts "#{stamp}:#{num}:#{match}"
|
|
40
|
+
}
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -1,24 +1,20 @@
|
|
|
1
1
|
module Rbnotes
|
|
2
2
|
class Commands::Show < Commands::Command
|
|
3
3
|
def execute(args, conf)
|
|
4
|
-
|
|
5
|
-
unless stamp_str.nil?
|
|
6
|
-
repo = Textrepo.init(conf)
|
|
7
|
-
stamp = Textrepo::Timestamp.parse_s(stamp_str)
|
|
8
|
-
content = repo.read(stamp)
|
|
4
|
+
stamp = Rbnotes::Utils.read_timestamp(args)
|
|
9
5
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
repo = Textrepo.init(conf)
|
|
7
|
+
content = repo.read(stamp)
|
|
8
|
+
|
|
9
|
+
pager = conf[:pager]
|
|
10
|
+
unless pager.nil?
|
|
11
|
+
require 'open3'
|
|
12
|
+
Open3.pipeline_w(pager) { |stdin|
|
|
13
|
+
stdin.puts content
|
|
14
|
+
stdin.close
|
|
15
|
+
}
|
|
20
16
|
else
|
|
21
|
-
|
|
17
|
+
puts content
|
|
22
18
|
end
|
|
23
19
|
end
|
|
24
20
|
end
|
|
@@ -19,7 +19,7 @@ module Rbnotes::Commands
|
|
|
19
19
|
# as add command.
|
|
20
20
|
#
|
|
21
21
|
# If none of editors is available, the command fails.
|
|
22
|
-
|
|
22
|
+
|
|
23
23
|
class Update < Command
|
|
24
24
|
include ::Rbnotes::Utils
|
|
25
25
|
|
|
@@ -30,19 +30,10 @@ module Rbnotes::Commands
|
|
|
30
30
|
#
|
|
31
31
|
# :call-seq:
|
|
32
32
|
# "20201020112233" -> "20201021123400"
|
|
33
|
-
#
|
|
34
|
-
def execute(args, conf)
|
|
35
|
-
raise Rbnotes::MissingArgumentError, args if args.size < 1
|
|
36
|
-
|
|
37
|
-
target_stamp = nil
|
|
38
|
-
begin
|
|
39
|
-
target_stamp = Textrepo::Timestamp.parse_s(args.shift)
|
|
40
|
-
rescue ArgumentError => e
|
|
41
|
-
raise Rbnotes::MissingArgumentError, args
|
|
42
|
-
end
|
|
43
33
|
|
|
34
|
+
def execute(args, conf)
|
|
35
|
+
target_stamp = Rbnotes::Utils.read_timestamp(args)
|
|
44
36
|
editor = find_editor(conf[:editor])
|
|
45
|
-
|
|
46
37
|
repo = Textrepo.init(conf)
|
|
47
38
|
|
|
48
39
|
text = nil
|
|
@@ -53,7 +44,7 @@ module Rbnotes::Commands
|
|
|
53
44
|
end
|
|
54
45
|
|
|
55
46
|
tmpfile = run_with_tmpfile(editor, target_stamp.to_s, text)
|
|
56
|
-
text = File.readlines(tmpfile)
|
|
47
|
+
text = File.readlines(tmpfile, :chomp => true)
|
|
57
48
|
|
|
58
49
|
unless text.empty?
|
|
59
50
|
newstamp = nil
|
|
@@ -62,7 +53,7 @@ module Rbnotes::Commands
|
|
|
62
53
|
rescue StandardError => e
|
|
63
54
|
puts e.message
|
|
64
55
|
else
|
|
65
|
-
puts "Update the note [%s -> %s]" % [target_stamp, newstamp]
|
|
56
|
+
puts "Update the note [%s -> %s]" % [target_stamp, newstamp] unless target_stamp == newstamp
|
|
66
57
|
ensure
|
|
67
58
|
# Don't forget to remove the temporary file.
|
|
68
59
|
File.delete(tmpfile)
|
data/lib/rbnotes/error.rb
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
module Rbnotes
|
|
2
2
|
##
|
|
3
3
|
# A base class for each error class of rbnotes.
|
|
4
|
-
|
|
4
|
+
|
|
5
5
|
class Error < StandardError; end
|
|
6
6
|
|
|
7
7
|
# :stopdoc:
|
|
8
|
+
|
|
8
9
|
module ErrMsg
|
|
9
10
|
MISSING_ARGUMENT = "missing argument: %s"
|
|
10
11
|
MISSING_TIMESTAMP = "missing timestamp: %s"
|
|
@@ -16,7 +17,7 @@ module Rbnotes
|
|
|
16
17
|
|
|
17
18
|
##
|
|
18
19
|
# An error raised if an essential argument was missing.
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
class MissingArgumentError < Error
|
|
21
22
|
def initialize(args)
|
|
22
23
|
super(ErrMsg::MISSING_ARGUMENT % args.to_s)
|
|
@@ -26,7 +27,7 @@ module Rbnotes
|
|
|
26
27
|
##
|
|
27
28
|
# An error raised if a given timestamp was not found in the
|
|
28
29
|
# repository.
|
|
29
|
-
|
|
30
|
+
|
|
30
31
|
class MissingTimestampError < Error
|
|
31
32
|
def initialize(timestamp)
|
|
32
33
|
super(ErrMsg::MISSING_TIMESTAMP % timestamp)
|
|
@@ -36,7 +37,7 @@ module Rbnotes
|
|
|
36
37
|
##
|
|
37
38
|
# An error raised if no external editor is available to edit a note,
|
|
38
39
|
# even "nano" or "vi".
|
|
39
|
-
|
|
40
|
+
|
|
40
41
|
class NoEditorError < Error
|
|
41
42
|
def initialize(names)
|
|
42
43
|
super(ErrMsg::NO_EDITOR % names.to_s)
|
|
@@ -46,6 +47,7 @@ module Rbnotes
|
|
|
46
47
|
##
|
|
47
48
|
# An error raised when a external program such an editor was aborted
|
|
48
49
|
# during its execution.
|
|
50
|
+
|
|
49
51
|
class ProgramAbortError < Error
|
|
50
52
|
def initialize(cmdline)
|
|
51
53
|
super(ErrMsg::PROGRAM_ABORT % cmdline.join(" "))
|
data/lib/rbnotes/utils.rb
CHANGED
|
@@ -86,6 +86,41 @@ module Rbnotes
|
|
|
86
86
|
end
|
|
87
87
|
module_function :run_with_tmpfile
|
|
88
88
|
|
|
89
|
+
##
|
|
90
|
+
# Generates a Textrepo::Timestamp object from a String which comes
|
|
91
|
+
# from the command line arguments. When no argument is given,
|
|
92
|
+
# then reads from STDIN.
|
|
93
|
+
#
|
|
94
|
+
# :call-seq:
|
|
95
|
+
# read_timestamp(args) -> String
|
|
96
|
+
|
|
97
|
+
def read_timestamp(args)
|
|
98
|
+
str = args.shift || read_arg($stdin)
|
|
99
|
+
Textrepo::Timestamp.parse_s(str)
|
|
100
|
+
end
|
|
101
|
+
module_function :read_timestamp
|
|
102
|
+
|
|
103
|
+
##
|
|
104
|
+
# Reads an argument from the IO object. Typically, it is intended
|
|
105
|
+
# to be used with STDIN.
|
|
106
|
+
#
|
|
107
|
+
# :call-seq:
|
|
108
|
+
# read_arg(IO) -> String
|
|
109
|
+
|
|
110
|
+
def read_arg(io)
|
|
111
|
+
# assumes the reading line looks like:
|
|
112
|
+
#
|
|
113
|
+
# foo bar baz ...
|
|
114
|
+
#
|
|
115
|
+
# then, only the first string is interested
|
|
116
|
+
begin
|
|
117
|
+
io.gets.split(":")[0].rstrip
|
|
118
|
+
rescue NoMethodError => _
|
|
119
|
+
nil
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
module_function :read_arg
|
|
123
|
+
|
|
89
124
|
# :stopdoc:
|
|
90
125
|
|
|
91
126
|
private
|
data/lib/rbnotes/version.rb
CHANGED
data/rbnotes.gemspec
CHANGED
|
@@ -25,6 +25,6 @@ Gem::Specification.new do |spec|
|
|
|
25
25
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
26
26
|
spec.require_paths = ["lib"]
|
|
27
27
|
|
|
28
|
-
spec.add_dependency "textrepo", "~> 0.
|
|
28
|
+
spec.add_dependency "textrepo", "~> 0.5"
|
|
29
29
|
spec.add_dependency "unicode-display_width", "~> 1.7"
|
|
30
30
|
end
|
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
|
+
version: 0.4.1
|
|
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-11-04 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: textrepo
|
|
@@ -16,14 +16,14 @@ dependencies:
|
|
|
16
16
|
requirements:
|
|
17
17
|
- - "~>"
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0.
|
|
19
|
+
version: '0.5'
|
|
20
20
|
type: :runtime
|
|
21
21
|
prerelease: false
|
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
23
23
|
requirements:
|
|
24
24
|
- - "~>"
|
|
25
25
|
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0.
|
|
26
|
+
version: '0.5'
|
|
27
27
|
- !ruby/object:Gem::Dependency
|
|
28
28
|
name: unicode-display_width
|
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -56,6 +56,9 @@ files:
|
|
|
56
56
|
- Rakefile
|
|
57
57
|
- bin/console
|
|
58
58
|
- bin/setup
|
|
59
|
+
- conf/config.yml
|
|
60
|
+
- conf/config_deve.yml
|
|
61
|
+
- conf/config_test.yml
|
|
59
62
|
- exe/rbnotes
|
|
60
63
|
- lib/rbnotes.rb
|
|
61
64
|
- lib/rbnotes/commands.rb
|
|
@@ -63,6 +66,7 @@ files:
|
|
|
63
66
|
- lib/rbnotes/commands/delete.rb
|
|
64
67
|
- lib/rbnotes/commands/import.rb
|
|
65
68
|
- lib/rbnotes/commands/list.rb
|
|
69
|
+
- lib/rbnotes/commands/search.rb
|
|
66
70
|
- lib/rbnotes/commands/show.rb
|
|
67
71
|
- lib/rbnotes/commands/update.rb
|
|
68
72
|
- lib/rbnotes/conf.rb
|