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.
@@ -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
- include ::Rbnotes::Utils
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
- # :markup: markdown
1
+ module Rbnotes::Commands
2
2
 
3
- # Delete command deletes one note in the repository, which specified
4
- # with a given timestamp string. The timestamp string must be a fully
5
- # qualified one, like "20201016165130". The argument to specify a
6
- # note is mandatory. If no argument was passed, it would print help
7
- # message and exit.
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
- # :stopdoc:
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::Utils.read_timestamp(args)
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
- class Commands::Import < Commands::Command
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
- require "unicode/display_width"
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 Commands::List < Commands::Command
7
+ class List < Command
8
+
9
+ def description # :nodoc:
10
+ "List notes"
11
+ end
10
12
 
11
13
  ##
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.
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
- # Here is
19
- # several examples of timestamp patterns.
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
- pattern = args.shift # `nil` is acceptable
42
-
54
+ patterns = Rbnotes.utils.expand_keyword_in_args(args)
43
55
  @repo = Textrepo.init(conf)
44
56
  # newer stamp shoud be above
45
- stamps = @repo.entries(pattern).sort{|a, b| b <=> a}
46
- stamps.each { |timestamp|
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
- # :stopdoc:
62
+ def help # :nodoc:
63
+ puts <<HELP
64
+ usage:
65
+ #{Rbnotes::NAME} list [STAMP_PATTERN|KEYWORD]
52
66
 
53
- private
54
- TIMESTAMP_STR_MAX_WIDTH = "yyyymoddhhmiss_sfx".size
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
- def make_headline(timestamp)
71
- _, column = IO.console_size
72
- delimiter = ": "
73
- subject_width = column - TIMESTAMP_STR_MAX_WIDTH - delimiter.size - 1
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
- subject = remove_heading_markup(@repo.read(timestamp)[0])
80
+ KEYWORD:
76
81
 
77
- ts_part = "#{timestamp.to_s} "[0..(TIMESTAMP_STR_MAX_WIDTH - 1)]
78
- sj_part = truncate_str(subject, subject_width)
79
-
80
- ts_part + delimiter + sj_part
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
- def remove_heading_markup(str)
95
- str.sub(/^#+ +/, '')
87
+ HELP
96
88
  end
97
89
 
98
- # :startdoc:
99
90
  end
100
91
  end