rbnotes 0.2.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 +7 -0
- data/.gitignore +56 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +40 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +26 -0
- data/LICENSE +21 -0
- data/README.md +156 -0
- data/Rakefile +30 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/exe/rbnotes +23 -0
- data/lib/rbnotes.rb +9 -0
- data/lib/rbnotes/commands.rb +144 -0
- data/lib/rbnotes/commands/add.rb +49 -0
- data/lib/rbnotes/commands/delete.rb +35 -0
- data/lib/rbnotes/commands/import.rb +62 -0
- data/lib/rbnotes/commands/list.rb +57 -0
- data/lib/rbnotes/commands/show.rb +25 -0
- data/lib/rbnotes/commands/update.rb +75 -0
- data/lib/rbnotes/conf.rb +118 -0
- data/lib/rbnotes/error.rb +54 -0
- data/lib/rbnotes/utils.rb +107 -0
- data/lib/rbnotes/version.rb +4 -0
- data/rbnotes.gemspec +30 -0
- metadata +99 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e3b44e20631a0216391b389aee7faca4ecebe31502bbff92606c938023a98240
|
4
|
+
data.tar.gz: 4a39041ad77631da13be01ebde0745edd38f22f52b5c9ad2ae1a002053739c1b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e98fabf28f56131caf2ec40c1111aa74a0819d923937cd033eeed6921c5ce43102657e8c3d787a8b9718b3057e7505921d2f7d316a327a041e80dcc2bce4b2ae
|
7
|
+
data.tar.gz: 672cd941dd68c545056c752a714bc29df9a9de5b14ce00b8c6568562df619e796c7cea77b376380bb3c32c06d680f834552c3c6367033bf2ab02b7460c4e1b0f
|
data/.gitignore
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
/.config
|
4
|
+
/coverage/
|
5
|
+
/InstalledFiles
|
6
|
+
/pkg/
|
7
|
+
/spec/reports/
|
8
|
+
/spec/examples.txt
|
9
|
+
/test/tmp/
|
10
|
+
/test/version_tmp/
|
11
|
+
/tmp/
|
12
|
+
|
13
|
+
# Used by dotenv library to load environment variables.
|
14
|
+
# .env
|
15
|
+
|
16
|
+
# Ignore Byebug command history file.
|
17
|
+
.byebug_history
|
18
|
+
|
19
|
+
## Specific to RubyMotion:
|
20
|
+
.dat*
|
21
|
+
.repl_history
|
22
|
+
build/
|
23
|
+
*.bridgesupport
|
24
|
+
build-iPhoneOS/
|
25
|
+
build-iPhoneSimulator/
|
26
|
+
|
27
|
+
## Specific to RubyMotion (use of CocoaPods):
|
28
|
+
#
|
29
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
30
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
31
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
32
|
+
#
|
33
|
+
# vendor/Pods/
|
34
|
+
|
35
|
+
## Documentation cache and generated files:
|
36
|
+
/.yardoc/
|
37
|
+
/_yardoc/
|
38
|
+
/doc/
|
39
|
+
/rdoc/
|
40
|
+
|
41
|
+
## Environment normalization:
|
42
|
+
/.bundle/
|
43
|
+
/vendor/bundle
|
44
|
+
/lib/bundler/man/
|
45
|
+
|
46
|
+
# for a library or gem, you might want to ignore these files since the code is
|
47
|
+
# intended to run in multiple environments; otherwise, check them in:
|
48
|
+
# Gemfile.lock
|
49
|
+
# .ruby-version
|
50
|
+
# .ruby-gemset
|
51
|
+
|
52
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
53
|
+
.rvmrc
|
54
|
+
|
55
|
+
# Used by RuboCop. Remote config files pulled in from inherit_from directive.
|
56
|
+
# .rubocop-https?--*
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
# Change Log
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/)
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
Nothing to record here.
|
9
|
+
|
10
|
+
## [0.2.1] - 2020-10-25
|
11
|
+
### Added
|
12
|
+
- Add feature to load the configuration from an external file.
|
13
|
+
- Add description about the configuration file in README.md
|
14
|
+
|
15
|
+
## [0.2.0] - 2020-10-23
|
16
|
+
### Added
|
17
|
+
- Add more commands (add/update/delete).
|
18
|
+
- Add and update commands use a external editor to edit a note.
|
19
|
+
- Delete command remove the specified note from the repository.
|
20
|
+
- Add a new task into `Rakefile` to generate RI docs.
|
21
|
+
- The intention of the task is to verify RI docs.
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
- Refactor some tests.
|
25
|
+
|
26
|
+
## [0.1.3] - 2020-10-15
|
27
|
+
### Fixed
|
28
|
+
- Add help text for the `conf` command.
|
29
|
+
|
30
|
+
## [0.1.2] - 2020-10-15
|
31
|
+
### Fixed
|
32
|
+
- Adapt the API change in `textrepo` (0.4.0).
|
33
|
+
|
34
|
+
## [0.1.0] - 2020-10-12
|
35
|
+
### Added
|
36
|
+
- Import files those are part of `examples` in the `textrepo` gem.
|
37
|
+
- All commands in the example version works fine.
|
38
|
+
- Wrote tests for all commands.
|
39
|
+
- All files those are generated by `bundler`.
|
40
|
+
- Add CHANGELOG.md to start recording changes.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
rbnotes (0.2.1)
|
5
|
+
textrepo (~> 0.4)
|
6
|
+
unicode-display_width (~> 1.7)
|
7
|
+
|
8
|
+
GEM
|
9
|
+
remote: https://rubygems.org/
|
10
|
+
specs:
|
11
|
+
minitest (5.14.2)
|
12
|
+
rake (13.0.1)
|
13
|
+
textrepo (0.4.2)
|
14
|
+
unicode-display_width (1.7.0)
|
15
|
+
|
16
|
+
PLATFORMS
|
17
|
+
ruby
|
18
|
+
|
19
|
+
DEPENDENCIES
|
20
|
+
minitest (~> 5.0)
|
21
|
+
rake (~> 13.0)
|
22
|
+
rbnotes!
|
23
|
+
textrepo (~> 0.4)
|
24
|
+
|
25
|
+
BUNDLED WITH
|
26
|
+
2.1.4
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2020 mnbi
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
# Rbnotes
|
2
|
+
|
3
|
+
[](https://travis-ci.org/mnbi/rbnotes)
|
4
|
+
|
5
|
+
Rbnotes is a simple utility to write a note in the single repository.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
```ruby
|
12
|
+
gem 'rbnotes, github: 'mnbi/rbnotes, branch: 'main'
|
13
|
+
```
|
14
|
+
|
15
|
+
And then execute:
|
16
|
+
|
17
|
+
$ bundle install
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
General syntax is:
|
22
|
+
|
23
|
+
``` shell
|
24
|
+
rbnotes [global_opts] [command] [command_opts] [args]
|
25
|
+
```
|
26
|
+
|
27
|
+
### Commands
|
28
|
+
|
29
|
+
- import
|
30
|
+
- imports existing files
|
31
|
+
- list
|
32
|
+
- lists notes in the repository with their timestamps and subject
|
33
|
+
- show
|
34
|
+
- shows the content of a note
|
35
|
+
- add
|
36
|
+
- adds a new note to the repository using an external editor
|
37
|
+
- update
|
38
|
+
- update the content of the specified note using an external editor
|
39
|
+
- delete
|
40
|
+
- deletes the specified note form the repository
|
41
|
+
|
42
|
+
### Configuration file
|
43
|
+
|
44
|
+
The `rbnotes` command reads the configuration file during its startup.
|
45
|
+
This section describes the specification of the configuration file.
|
46
|
+
|
47
|
+
#### Location
|
48
|
+
|
49
|
+
Searches the configuration file in the following order:
|
50
|
+
|
51
|
+
1. `$XDG_CONFIG_HOME/rbnotes/config.yml`
|
52
|
+
2. `$HOME/.config/rbnotes/config.yml`
|
53
|
+
|
54
|
+
None of them is found, then the default configuration is used.
|
55
|
+
|
56
|
+
#### Content
|
57
|
+
|
58
|
+
The format of the configuration file must be written in YAML.
|
59
|
+
|
60
|
+
The configuration of `rbnotes` is represented as a Hash object in the
|
61
|
+
program code. So, each line of the configuration YAML looks like:
|
62
|
+
|
63
|
+
> name: value
|
64
|
+
|
65
|
+
Name must be written as Ruby's symbol, such `:repository_type`. On
|
66
|
+
the other hand, value will be written in 2 types. Some are Ruby's
|
67
|
+
symbols, the others are strings surrounded with double/single
|
68
|
+
quotations.
|
69
|
+
|
70
|
+
A real example:
|
71
|
+
|
72
|
+
``` yaml
|
73
|
+
---
|
74
|
+
:run_mode: :development
|
75
|
+
:repository_type: :file_system
|
76
|
+
:repository_name: "notes"
|
77
|
+
:repository_base: "~"
|
78
|
+
:pager: "bat"
|
79
|
+
:editor: "/usr/local/bin/emacsclient"
|
80
|
+
```
|
81
|
+
|
82
|
+
#### Variables
|
83
|
+
|
84
|
+
##### :run-mode (mandatory)
|
85
|
+
|
86
|
+
- :production (default)
|
87
|
+
- :development
|
88
|
+
- :test
|
89
|
+
|
90
|
+
The run-mode affects to behavior of `rbnotes`, such logging
|
91
|
+
information, the location of the repository, ..., etc.
|
92
|
+
|
93
|
+
##### :repository_type (mandatory)
|
94
|
+
|
95
|
+
- :file_system (default)
|
96
|
+
|
97
|
+
This value depends on classes those derived from
|
98
|
+
`Textrepo::Repository` of `textrepo`. Currently (`textrepo` 0.4.x),
|
99
|
+
Textrepo::FileSystemRepository is the only one class usable for
|
100
|
+
`rbnotes`.
|
101
|
+
|
102
|
+
##### :repository_name (mandatory)
|
103
|
+
|
104
|
+
User can set an arbitrary string as this value. However, the value
|
105
|
+
will be used as a part of the repository location in the file system.
|
106
|
+
Some characters in the string may cause a problem.
|
107
|
+
|
108
|
+
In addition to this, the run-mode affects the actual value when it is
|
109
|
+
used in the program.
|
110
|
+
|
111
|
+
| original | :production | :development | :test |
|
112
|
+
|:------ |:------------|:-------------|:-----------|
|
113
|
+
| notes | notes | notes_dev | notes_test |
|
114
|
+
|
115
|
+
##### :repository_base (mandatory)
|
116
|
+
|
117
|
+
This value is used as a base directory of the repository. That is,
|
118
|
+
the values constructs the root path of the repository with
|
119
|
+
the `:repository_name` value. It would be:
|
120
|
+
|
121
|
+
> :repository_base/:repository_name
|
122
|
+
|
123
|
+
The value must be an absolute path. The short-hand notation of the
|
124
|
+
home directory ("~") is usable.
|
125
|
+
|
126
|
+
##### Miscellaneous variables (optional)
|
127
|
+
|
128
|
+
- :pager : specify a pager program
|
129
|
+
- :editor : specify a editor program
|
130
|
+
|
131
|
+
##### Default values for mandatory variables
|
132
|
+
|
133
|
+
All mandatory variables have their default values. Here is the list
|
134
|
+
of them:
|
135
|
+
|
136
|
+
| variable | default value |
|
137
|
+
|:-----------------|:--------------|
|
138
|
+
| :run_mode | :production |
|
139
|
+
| :repository_type | :file_system |
|
140
|
+
| :repository_name | "notes" |
|
141
|
+
| :repository_base | "~" |
|
142
|
+
|
143
|
+
## Development
|
144
|
+
|
145
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
146
|
+
|
147
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
148
|
+
|
149
|
+
## Contributing
|
150
|
+
|
151
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mnbi/rbnotes.
|
152
|
+
|
153
|
+
|
154
|
+
## License
|
155
|
+
|
156
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
|
4
|
+
Rake::TestTask.new(:test) do |t|
|
5
|
+
t.libs << "test"
|
6
|
+
t.libs << "lib"
|
7
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
8
|
+
end
|
9
|
+
|
10
|
+
task :default => :test
|
11
|
+
|
12
|
+
task test: [:clean, :setup_test]
|
13
|
+
|
14
|
+
desc "Setup test data"
|
15
|
+
task :setup_test do
|
16
|
+
print "Set up to execute tests..."
|
17
|
+
load("test/fixtures/setup_test_repo.rb", true)
|
18
|
+
puts "done."
|
19
|
+
end
|
20
|
+
|
21
|
+
CLEAN << "test/sandbox"
|
22
|
+
CLOBBER << "test/fixtures/test_repo"
|
23
|
+
|
24
|
+
require "rdoc/task"
|
25
|
+
|
26
|
+
RDoc::Task.new do |rdoc|
|
27
|
+
rdoc.generator = "ri"
|
28
|
+
rdoc.rdoc_dir = "doc"
|
29
|
+
rdoc.rdoc_files.include("lib/**/*.rb")
|
30
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "rbnotes"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/exe/rbnotes
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "rbnotes"
|
4
|
+
|
5
|
+
include Rbnotes
|
6
|
+
|
7
|
+
DEBUG = true
|
8
|
+
|
9
|
+
class App
|
10
|
+
def run(args)
|
11
|
+
cmd = args.shift
|
12
|
+
Commands.load(cmd).execute(args, Rbnotes.conf)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
app = App.new
|
17
|
+
begin
|
18
|
+
app.run(ARGV)
|
19
|
+
rescue MissingArgumentError, MissingTimestampError,
|
20
|
+
NoEditorError, ProgramAbortError => e
|
21
|
+
puts e.message
|
22
|
+
exit 1
|
23
|
+
end
|
data/lib/rbnotes.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
module Rbnotes
|
2
|
+
##
|
3
|
+
# This module defines all command classes of rbnotes. Each command
|
4
|
+
# class must be derived from Rbnotes::Commands::Command class.
|
5
|
+
module Commands
|
6
|
+
##
|
7
|
+
# The base class for a command class.
|
8
|
+
class Command
|
9
|
+
##
|
10
|
+
# :call-seq:
|
11
|
+
# Array, Hash -> nil
|
12
|
+
# - Array: arguments for each command
|
13
|
+
# - Hash : rbnotes configuration
|
14
|
+
#
|
15
|
+
def execute(args, conf)
|
16
|
+
Builtins::DEFAULT_CMD.new.execute(args, conf)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# :stopdoc:
|
21
|
+
|
22
|
+
# Built-in commands:
|
23
|
+
# - repo: prints the absolute path of the repository.
|
24
|
+
# - conf: prints all of the current configuration settings.
|
25
|
+
# - stamp: converts given TIME_STR into a timestamp.
|
26
|
+
# - time: converts given STAMP into a time string.
|
27
|
+
module Builtins
|
28
|
+
class Help < Command
|
29
|
+
def execute(_, _)
|
30
|
+
puts <<USAGE
|
31
|
+
usage: rbnotes [command] [args]
|
32
|
+
|
33
|
+
command:
|
34
|
+
import FILE : import a FILE into the repository
|
35
|
+
list NUM : list NUM notes
|
36
|
+
show STAMP : show the note specified with STAMP
|
37
|
+
delete STAMP : delete the note specified with STAMP
|
38
|
+
|
39
|
+
conf : print the current configuraitons
|
40
|
+
repo : print the repository path
|
41
|
+
stamp TIME_STR : convert TIME_STR into a timestamp
|
42
|
+
time STAMP : convert STAMP into a time string
|
43
|
+
version : print version
|
44
|
+
help : show help
|
45
|
+
USAGE
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Version < Command
|
50
|
+
def execute(_, _)
|
51
|
+
rbnotes_version = "rbnotes #{Rbnotes::VERSION} (#{Rbnotes::RELEASE})"
|
52
|
+
textrepo_version = "textrepo #{Textrepo::VERSION}"
|
53
|
+
puts "#{rbnotes_version} [#{textrepo_version}]"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class Repo < Command
|
58
|
+
def execute(_, conf)
|
59
|
+
name = conf[:repository_name]
|
60
|
+
base = conf[:repository_base]
|
61
|
+
type = conf[:repository_type]
|
62
|
+
|
63
|
+
puts case type
|
64
|
+
when :file_system
|
65
|
+
"#{base}/#{name}"
|
66
|
+
else
|
67
|
+
"#{base}/#{name}"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class Conf < Command
|
73
|
+
def execute(_, conf)
|
74
|
+
conf.keys.sort.each { |k|
|
75
|
+
puts "#{k}=#{conf[k]}"
|
76
|
+
}
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
require "time"
|
81
|
+
|
82
|
+
class Stamp < Command
|
83
|
+
def execute(args, _)
|
84
|
+
time_str = args.shift
|
85
|
+
unless time_str.nil?
|
86
|
+
puts Textrepo::Timestamp.new(::Time.parse(time_str)).to_s
|
87
|
+
else
|
88
|
+
puts "not specified TIME_STR"
|
89
|
+
super
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class Time < Command
|
95
|
+
def execute(args, _)
|
96
|
+
stamp = args.shift
|
97
|
+
unless stamp.nil?
|
98
|
+
puts ::Time.new(*Textrepo::Timestamp.split_stamp(stamp).map(&:to_i)).to_s
|
99
|
+
else
|
100
|
+
puts "not specified STAMP"
|
101
|
+
super
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
DEFAULT_CMD = Help
|
107
|
+
end
|
108
|
+
|
109
|
+
DEFAULT_CMD_NAME = "help"
|
110
|
+
|
111
|
+
# :startdoc:
|
112
|
+
|
113
|
+
class << self
|
114
|
+
##
|
115
|
+
# Loads a class to perfom the command, then returns an instance
|
116
|
+
# of the class.
|
117
|
+
#
|
118
|
+
# :call-seq:
|
119
|
+
# "import" -> an object of Rbnotes::Commnads::Import
|
120
|
+
# "list" -> an object of Rbnotes::Commands::List
|
121
|
+
# "show" -> an object of Rbnotes::Commands::Show
|
122
|
+
#
|
123
|
+
def load(cmd_name)
|
124
|
+
cmd_name ||= DEFAULT_CMD_NAME
|
125
|
+
klass_name = cmd_name.capitalize
|
126
|
+
|
127
|
+
klass = nil
|
128
|
+
if Builtins.const_defined?(klass_name, false)
|
129
|
+
klass = Builtins::const_get(klass_name, false)
|
130
|
+
else
|
131
|
+
begin
|
132
|
+
require_relative "commands/#{cmd_name}"
|
133
|
+
klass = const_get(klass_name, false)
|
134
|
+
rescue LoadError => _
|
135
|
+
STDERR.puts "unknown command: #{cmd_name}"
|
136
|
+
klass = Builtins::DEFAULT_CMD
|
137
|
+
end
|
138
|
+
end
|
139
|
+
klass.new
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Rbnotes::Commands
|
2
|
+
##
|
3
|
+
# Adds a new note to the repository. A new timestamp is generated
|
4
|
+
# at the execution time, then it is attached to the note. If the
|
5
|
+
# timestamp has already existed in the repository, the command
|
6
|
+
# fails.
|
7
|
+
#
|
8
|
+
# This command starts the external editor program to prepare text to
|
9
|
+
# store. The editor program will be searched in the following order:
|
10
|
+
#
|
11
|
+
# 1. conf[:editor] (conf is the 1st arg of execute method)
|
12
|
+
# 2. ENV["EDITOR"]
|
13
|
+
# 3. "nano"
|
14
|
+
# 4. "vi"
|
15
|
+
#
|
16
|
+
# If none of the above editor is available, the command fails.
|
17
|
+
#
|
18
|
+
class Add < Command
|
19
|
+
include ::Rbnotes::Utils
|
20
|
+
|
21
|
+
def execute(args, conf)
|
22
|
+
newstamp = Textrepo::Timestamp.new(Time.now)
|
23
|
+
|
24
|
+
candidates = [conf[:editor], ENV["EDITOR"], "nano", "vi"].compact
|
25
|
+
editor = find_program(candidates)
|
26
|
+
raise Rbnotes::NoEditorError, candidates if editor.nil?
|
27
|
+
|
28
|
+
tmpfile = run_with_tmpfile(editor, newstamp.to_s)
|
29
|
+
text = File.readlines(tmpfile)
|
30
|
+
|
31
|
+
repo = Textrepo.init(conf)
|
32
|
+
begin
|
33
|
+
repo.create(newstamp, text)
|
34
|
+
rescue Textrepo::DuplicateTimestampError => e
|
35
|
+
puts e.message
|
36
|
+
puts "Just wait a second, then retry."
|
37
|
+
rescue Textrepo::EmptyTextError => e
|
38
|
+
puts e.message
|
39
|
+
rescue StandardError => e
|
40
|
+
puts e.message
|
41
|
+
else
|
42
|
+
puts "Add a note [%s]" % newstamp.to_s
|
43
|
+
ensure
|
44
|
+
# Don't forget to remove the temporary file.
|
45
|
+
File.delete(tmpfile)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# :markup: markdown
|
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.
|
11
|
+
|
12
|
+
# :stopdoc:
|
13
|
+
|
14
|
+
module Rbnotes
|
15
|
+
class Commands::Delete < Commands::Command
|
16
|
+
def execute(args, conf)
|
17
|
+
stamp_str = args.shift
|
18
|
+
unless stamp_str.nil?
|
19
|
+
repo = Textrepo.init(conf)
|
20
|
+
begin
|
21
|
+
stamp = Textrepo::Timestamp.parse_s(stamp_str)
|
22
|
+
repo.delete(stamp)
|
23
|
+
rescue Textrepo::MissingTimestampError => e
|
24
|
+
puts e.message
|
25
|
+
rescue StandardError => e
|
26
|
+
puts e.message
|
27
|
+
else
|
28
|
+
puts "Delete [%s]" % stamp.to_s
|
29
|
+
end
|
30
|
+
else
|
31
|
+
super
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Rbnotes
|
2
|
+
class Commands::Import < Commands::Command
|
3
|
+
def execute(args, conf)
|
4
|
+
file = args.shift
|
5
|
+
unless file.nil?
|
6
|
+
st = File::Stat.new(file)
|
7
|
+
btime = st.respond_to?(:birthtime) ? st.birthtime : st.mtime
|
8
|
+
stamp = Textrepo::Timestamp.new(btime)
|
9
|
+
puts "Import [%s] (timestamp [%s]) ..." % [file, stamp]
|
10
|
+
|
11
|
+
repo = Textrepo.init(conf)
|
12
|
+
content = nil
|
13
|
+
File.open(file, "r") {|f| content = f.readlines(chomp: true)}
|
14
|
+
|
15
|
+
count = 0
|
16
|
+
while count <= 999
|
17
|
+
begin
|
18
|
+
repo.create(stamp, content)
|
19
|
+
break # success to create a note
|
20
|
+
rescue Textrepo::DuplicateTimestampError => _
|
21
|
+
puts "A text with the timestamp [%s] has been already exists" \
|
22
|
+
" in the repository." % stamp
|
23
|
+
|
24
|
+
repo_text = repo.read(stamp)
|
25
|
+
if content == repo_text
|
26
|
+
# if the content is the same to the target file,
|
27
|
+
# the specified file has been already imported.
|
28
|
+
# Then, there is nothing to do. Just exit.
|
29
|
+
puts "The note [%s] in the repository exactly matches" \
|
30
|
+
" the specified file." % stamp
|
31
|
+
puts "It seems there is no need to import the file [%s]." % file
|
32
|
+
exit # normal end
|
33
|
+
else
|
34
|
+
puts "The text in the repository does not match the" \
|
35
|
+
" specified file."
|
36
|
+
count += 1
|
37
|
+
stamp = Textrepo::Timestamp.new(stamp.time, count)
|
38
|
+
puts "Try to create a note again with a new " \
|
39
|
+
"timestamp [%s]." % stamp
|
40
|
+
end
|
41
|
+
rescue Textrepo::EmptyTextError => _
|
42
|
+
puts "... aborted."
|
43
|
+
puts "The specified file is empyt."
|
44
|
+
exit 1 # error
|
45
|
+
end
|
46
|
+
end
|
47
|
+
if count > 999
|
48
|
+
puts "Cannot create a text into the repository with the" \
|
49
|
+
" specified file [%s]." % file
|
50
|
+
puts "For, the birthtime [%s] is identical to some notes" \
|
51
|
+
" already exists in the reopsitory." % btime
|
52
|
+
puts "Change the birthtime of the target file, then retry."
|
53
|
+
else
|
54
|
+
puts "... Done."
|
55
|
+
end
|
56
|
+
else
|
57
|
+
puts "not supecified FILE"
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "unicode/display_width"
|
2
|
+
require "io/console/size"
|
3
|
+
|
4
|
+
module Rbnotes
|
5
|
+
class Commands::List < Commands::Command
|
6
|
+
def execute(args, conf)
|
7
|
+
@row, @column = IO.console_size
|
8
|
+
max = (args.shift || @row - 3).to_i
|
9
|
+
|
10
|
+
@repo = Textrepo.init(conf)
|
11
|
+
notes = @repo.entries.sort{|a, b| b <=> a}
|
12
|
+
notes[0, max].each { |timestamp_str|
|
13
|
+
puts make_headline(timestamp_str)
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
TIMESTAMP_STR_MAX_WIDTH = "yyyymoddhhmiss_sfx".size
|
19
|
+
# Makes a headline with the timestamp and subject of the notes, it
|
20
|
+
# looks like as follows:
|
21
|
+
#
|
22
|
+
# |<------------------ console column size --------------------->|
|
23
|
+
# +-- timestamp ---+ +--- subject (the 1st line of each note) --+
|
24
|
+
# | | | |
|
25
|
+
# 20101010001000_123: # I love Macintosh. [EOL]
|
26
|
+
# 20100909090909_999: # This is very very long long loooong subje[EOL]
|
27
|
+
# ++
|
28
|
+
# ^--- delimiter (2 characters)
|
29
|
+
#
|
30
|
+
# The subject part will truncate when it is long.
|
31
|
+
def make_headline(timestamp_str)
|
32
|
+
|
33
|
+
delimiter = ": "
|
34
|
+
subject_width = @column - TIMESTAMP_STR_MAX_WIDTH - delimiter.size - 1
|
35
|
+
|
36
|
+
subject = @repo.read(Textrepo::Timestamp.parse_s(timestamp_str))[0]
|
37
|
+
prefix = '# '
|
38
|
+
subject = prefix + subject.lstrip if subject[0, 2] != prefix
|
39
|
+
|
40
|
+
ts_part = "#{timestamp_str} "[0..(TIMESTAMP_STR_MAX_WIDTH - 1)]
|
41
|
+
sj_part = truncate_str(subject, subject_width)
|
42
|
+
|
43
|
+
ts_part + delimiter + sj_part
|
44
|
+
end
|
45
|
+
|
46
|
+
def truncate_str(str, size)
|
47
|
+
count = 0
|
48
|
+
result = ""
|
49
|
+
str.each_char { |c|
|
50
|
+
count += Unicode::DisplayWidth.of(c)
|
51
|
+
break if count > size
|
52
|
+
result << c
|
53
|
+
}
|
54
|
+
result
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Rbnotes
|
2
|
+
class Commands::Show < Commands::Command
|
3
|
+
def execute(args, conf)
|
4
|
+
stamp_str = args.shift
|
5
|
+
unless stamp_str.nil?
|
6
|
+
repo = Textrepo.init(conf)
|
7
|
+
stamp = Textrepo::Timestamp.parse_s(stamp_str)
|
8
|
+
content = repo.read(stamp)
|
9
|
+
|
10
|
+
pager = conf[:pager]
|
11
|
+
unless pager.nil?
|
12
|
+
require 'open3'
|
13
|
+
Open3.pipeline_w(pager) { |stdin|
|
14
|
+
stdin.puts content
|
15
|
+
stdin.close
|
16
|
+
}
|
17
|
+
else
|
18
|
+
puts content
|
19
|
+
end
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
module Rbnotes::Commands
|
2
|
+
##
|
3
|
+
# Updates the content of the note associated with given timestamp.
|
4
|
+
# Actual modification is done interactively by the external editor.
|
5
|
+
# The timestamp associated with the note will be updated to new one,
|
6
|
+
# which is generated while the command exection.
|
7
|
+
#
|
8
|
+
# A timestamp string must be specified as the only argument. It
|
9
|
+
# must exactly match to the one of the target note in the
|
10
|
+
# repository. When the given timestamp was not found, the command
|
11
|
+
# fails.
|
12
|
+
#
|
13
|
+
# Timestamp which is associated to the target note will be newly
|
14
|
+
# generated with the command execution time. That is, the timestamp
|
15
|
+
# before the command exection will be obsolete.
|
16
|
+
#
|
17
|
+
# This command starts the external editor program to edit the
|
18
|
+
# content of the note. The editor program will be searched as same
|
19
|
+
# as add command.
|
20
|
+
#
|
21
|
+
# If none of editors is available, the command fails.
|
22
|
+
#
|
23
|
+
class Update < Command
|
24
|
+
include ::Rbnotes::Utils
|
25
|
+
|
26
|
+
##
|
27
|
+
# The 1st and only one argument is the timestamp to speficy the
|
28
|
+
# note to update. Returns the new timestamp which is associated
|
29
|
+
# to the note updated.
|
30
|
+
#
|
31
|
+
# :call-seq:
|
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
|
+
|
44
|
+
editor = find_editor(conf[:editor])
|
45
|
+
|
46
|
+
repo = Textrepo.init(conf)
|
47
|
+
|
48
|
+
text = nil
|
49
|
+
begin
|
50
|
+
text = repo.read(target_stamp)
|
51
|
+
rescue Textrepo::MissingTimestampError => _
|
52
|
+
raise Rbnotes::MissingTimestampError, target_stamp
|
53
|
+
end
|
54
|
+
|
55
|
+
tmpfile = run_with_tmpfile(editor, target_stamp.to_s, text)
|
56
|
+
text = File.readlines(tmpfile)
|
57
|
+
|
58
|
+
unless text.empty?
|
59
|
+
newstamp = nil
|
60
|
+
begin
|
61
|
+
newstamp = repo.update(target_stamp, text)
|
62
|
+
rescue StandardError => e
|
63
|
+
puts e.message
|
64
|
+
else
|
65
|
+
puts "Update the note [%s -> %s]" % [target_stamp, newstamp]
|
66
|
+
ensure
|
67
|
+
# Don't forget to remove the temporary file.
|
68
|
+
File.delete(tmpfile)
|
69
|
+
end
|
70
|
+
else
|
71
|
+
puts "Nothing is updated, since the specified content is empty."
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
data/lib/rbnotes/conf.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
require "yaml"
|
3
|
+
|
4
|
+
module Rbnotes
|
5
|
+
##
|
6
|
+
# Holds the configuration settings. Each value of setting can be
|
7
|
+
# retrieved like a Hash object. Here is some examples.
|
8
|
+
#
|
9
|
+
# conf = Rbnotes.conf
|
10
|
+
# type = conf[:repository_type]
|
11
|
+
# name = conf[:repository_name]
|
12
|
+
# base = conf[:repository_base]
|
13
|
+
|
14
|
+
class Conf
|
15
|
+
extend Forwardable
|
16
|
+
include Enumerable
|
17
|
+
|
18
|
+
##
|
19
|
+
# Name of the file to store configuration settings.
|
20
|
+
|
21
|
+
FILENAME_CONF = "config.yml"
|
22
|
+
|
23
|
+
##
|
24
|
+
# Name of the directory indicates which belongs to "rbnotes".
|
25
|
+
|
26
|
+
DIRNAME_RBNOTES = "rbnotes"
|
27
|
+
|
28
|
+
##
|
29
|
+
# Name of the directory which is used to indicate to put
|
30
|
+
# configuration files. Many tools use this name as the role.
|
31
|
+
|
32
|
+
DIRNAME_COMMON_CONF = ".config"
|
33
|
+
|
34
|
+
def initialize(conf_path = nil) # :nodoc:
|
35
|
+
@conf_path = conf_path || File.join(base_path, FILENAME_CONF)
|
36
|
+
|
37
|
+
@conf = {}
|
38
|
+
if FileTest.exist?(@conf_path)
|
39
|
+
yaml_str = File.open(@conf_path, "r") { |f| f.read }
|
40
|
+
@conf = YAML.load(yaml_str)
|
41
|
+
else
|
42
|
+
@conf.merge(DEFAULT_VALUES)
|
43
|
+
end
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def_delegators(:@conf,
|
48
|
+
:empyt?, :keys, :values,
|
49
|
+
:has_key?, :include?, :key?, :member?,
|
50
|
+
:length, :size,
|
51
|
+
:to_s,
|
52
|
+
:each, :each_pair)
|
53
|
+
|
54
|
+
##
|
55
|
+
# Retrieves the value for the given key.
|
56
|
+
#
|
57
|
+
# :call-seq:
|
58
|
+
# self[sym] -> value
|
59
|
+
|
60
|
+
def [](sym)
|
61
|
+
mode = @conf[:run_mode] || :production
|
62
|
+
value = @conf[sym] || DEFAULT_VALUES[sym]
|
63
|
+
if [:repository_name].include?(sym)
|
64
|
+
value += MODE_POSTFIX[mode]
|
65
|
+
end
|
66
|
+
value
|
67
|
+
end
|
68
|
+
|
69
|
+
# :stopdoc:
|
70
|
+
|
71
|
+
def initialize_copy(_)
|
72
|
+
@conf = @conf.dup
|
73
|
+
end
|
74
|
+
|
75
|
+
def freeze; @conf.freeze; super; end
|
76
|
+
def taint; @conf.taint; super; end
|
77
|
+
def untaint; @conf.untaint; super; end
|
78
|
+
|
79
|
+
private
|
80
|
+
DEFAULT_VALUES = {
|
81
|
+
:repository_type => :file_system,
|
82
|
+
:repository_name => "notes",
|
83
|
+
:repository_base => "~",
|
84
|
+
}
|
85
|
+
|
86
|
+
MODE_POSTFIX = {
|
87
|
+
:production => "",
|
88
|
+
:development => "_deve",
|
89
|
+
:test => "_test",
|
90
|
+
}
|
91
|
+
|
92
|
+
def base_path
|
93
|
+
path = nil
|
94
|
+
xdg, user = ["XDG_CONFIG_HOME", "HOME"].map{|n| ENV[n]}
|
95
|
+
if xdg
|
96
|
+
path = File.join(File.expand_path(xdg), DIRNAME_RBNOTES)
|
97
|
+
else
|
98
|
+
path = File.join(user, DIRNAME_COMMON_CONF, DIRNAME_RBNOTES)
|
99
|
+
end
|
100
|
+
return path
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# :startdoc:
|
105
|
+
|
106
|
+
class << self
|
107
|
+
##
|
108
|
+
# Gets the instance of Rbnotes::Conf. An optional argument is to
|
109
|
+
# specify the absolute path for the configuration file.
|
110
|
+
#
|
111
|
+
# :call-seq:
|
112
|
+
# conf(String) => Rbnotes::Conf
|
113
|
+
|
114
|
+
def conf(conf_path = nil)
|
115
|
+
Conf.new(conf_path)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Rbnotes
|
2
|
+
##
|
3
|
+
# A base class for each error class of rbnotes.
|
4
|
+
#
|
5
|
+
class Error < StandardError; end
|
6
|
+
|
7
|
+
# :stopdoc:
|
8
|
+
module ErrMsg
|
9
|
+
MISSING_ARGUMENT = "missing argument: %s"
|
10
|
+
MISSING_TIMESTAMP = "missing timestamp: %s"
|
11
|
+
NO_EDITOR = "No editor is available: %s"
|
12
|
+
PROGRAM_ABORT = "External program was aborted: %s"
|
13
|
+
end
|
14
|
+
|
15
|
+
# :startdoc:
|
16
|
+
|
17
|
+
##
|
18
|
+
# An error raised if an essential argument was missing.
|
19
|
+
#
|
20
|
+
class MissingArgumentError < Error
|
21
|
+
def initialize(args)
|
22
|
+
super(ErrMsg::MISSING_ARGUMENT % args.to_s)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
##
|
27
|
+
# An error raised if a given timestamp was not found in the
|
28
|
+
# repository.
|
29
|
+
#
|
30
|
+
class MissingTimestampError < Error
|
31
|
+
def initialize(timestamp)
|
32
|
+
super(ErrMsg::MISSING_TIMESTAMP % timestamp)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
##
|
37
|
+
# An error raised if no external editor is available to edit a note,
|
38
|
+
# even "nano" or "vi".
|
39
|
+
#
|
40
|
+
class NoEditorError < Error
|
41
|
+
def initialize(names)
|
42
|
+
super(ErrMsg::NO_EDITOR % names.to_s)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# An error raised when a external program such an editor was aborted
|
48
|
+
# during its execution.
|
49
|
+
class ProgramAbortError < Error
|
50
|
+
def initialize(cmdline)
|
51
|
+
super(ErrMsg::PROGRAM_ABORT % cmdline.join(" "))
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require "pathname"
|
2
|
+
require "tmpdir"
|
3
|
+
|
4
|
+
module Rbnotes
|
5
|
+
##
|
6
|
+
# Defines several utility methods those are intended to be used in
|
7
|
+
# Rbnotes classes.
|
8
|
+
#
|
9
|
+
module Utils
|
10
|
+
|
11
|
+
##
|
12
|
+
# Finds a external editor program which is specified with the
|
13
|
+
# argument, then returns the absolute path of the editor. If the
|
14
|
+
# specified editor was not found, then search default editors in
|
15
|
+
# the command search paths (i.e. `ENV["PATH"]). See also the
|
16
|
+
# document for `find_program`.
|
17
|
+
#
|
18
|
+
# The default editors to search in the search paths are:
|
19
|
+
#
|
20
|
+
# 1. ENV["EDITOR"]
|
21
|
+
# 2. "nano"
|
22
|
+
# 3. "vi"
|
23
|
+
#
|
24
|
+
# When all the default editors were not found, returns `nil`.
|
25
|
+
#
|
26
|
+
def find_editor(preferred_editor)
|
27
|
+
find_program([preferred_editor, ENV["EDITOR"], "nano", "vi"].compact)
|
28
|
+
end
|
29
|
+
module_function :find_editor
|
30
|
+
|
31
|
+
##
|
32
|
+
# Finds a executable program in given names. When the executable
|
33
|
+
# was found, it stops searching then returns an absolute path of
|
34
|
+
# the executable.
|
35
|
+
#
|
36
|
+
# The actual searching is done in 2 cases. That is, a given name is:
|
37
|
+
#
|
38
|
+
# 1. an absolute path:
|
39
|
+
# returns the path itself if it exists and is executable.
|
40
|
+
# 2. just a program name:
|
41
|
+
# searchs the name in the search paths (ENV["PATH"]);
|
42
|
+
# if it is found in a path, construct an absolute path from
|
43
|
+
# the name and the path, then returns the path.
|
44
|
+
#
|
45
|
+
# :call-seq:
|
46
|
+
# ["nano", "vi"] -> "/usr/bin/nano"
|
47
|
+
# ["vi", "/usr/local/bin/emacs"] -> "/usr/bin/vi"
|
48
|
+
# ["/usr/local/bin/emacs", "vi"] -> "/usr/bin/vi" (if emacs doesn't exist)
|
49
|
+
# ["/usr/local/bin/emacs", "vi"] -> "/usr/local/bin/emacs" (if exists)
|
50
|
+
#
|
51
|
+
def find_program(names)
|
52
|
+
names.each { |name|
|
53
|
+
pathname = Pathname.new(name)
|
54
|
+
if pathname.absolute?
|
55
|
+
return pathname.to_path if pathname.exist? && pathname.executable?
|
56
|
+
else
|
57
|
+
abs = search_in_path(name)
|
58
|
+
return abs unless abs.nil?
|
59
|
+
end
|
60
|
+
}
|
61
|
+
nil
|
62
|
+
end
|
63
|
+
module_function :find_program
|
64
|
+
|
65
|
+
##
|
66
|
+
# Executes the program with passing the given filename as argument.
|
67
|
+
# The file will be created into `Dir.tmpdir`.
|
68
|
+
#
|
69
|
+
# If initial_content is not nil, it must be an array of strings
|
70
|
+
# then it provides the initial content of a temporary file.
|
71
|
+
#
|
72
|
+
# :call-seq:
|
73
|
+
# "/usr/bin/nano", "20201021131300.md", nil -> "/somewhere/tmpdir/20201021131300.md"
|
74
|
+
# "/usr/bin/vi", "20201021131301.md", ["apple\n", "orange\n"] -> "/somewhere/tmpdir/20201021131301.md"
|
75
|
+
#
|
76
|
+
def run_with_tmpfile(prog, filename, initial_content = nil)
|
77
|
+
tmpfile = File.expand_path(add_extension(filename), Dir.tmpdir)
|
78
|
+
|
79
|
+
unless initial_content.nil?
|
80
|
+
File.open(tmpfile, "w") {|f| f.print(initial_content.join("\n"))}
|
81
|
+
end
|
82
|
+
|
83
|
+
rc = system(prog, tmpfile)
|
84
|
+
raise ProgramAbortError, [prog, tmpfile] unless rc
|
85
|
+
tmpfile
|
86
|
+
end
|
87
|
+
module_function :run_with_tmpfile
|
88
|
+
|
89
|
+
# :stopdoc:
|
90
|
+
|
91
|
+
private
|
92
|
+
def search_in_path(name)
|
93
|
+
search_paths = ENV["PATH"].split(":")
|
94
|
+
found = search_paths.map { |path|
|
95
|
+
abs = File.expand_path(name, path)
|
96
|
+
FileTest.exist?(abs) ? abs : nil
|
97
|
+
}
|
98
|
+
found.compact[0]
|
99
|
+
end
|
100
|
+
module_function :search_in_path
|
101
|
+
|
102
|
+
def add_extension(basename)
|
103
|
+
"#{basename}.md"
|
104
|
+
end
|
105
|
+
module_function :add_extension
|
106
|
+
end
|
107
|
+
end
|
data/rbnotes.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require_relative 'lib/rbnotes/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "rbnotes"
|
5
|
+
spec.version = Rbnotes::VERSION
|
6
|
+
spec.authors = ["mnbi"]
|
7
|
+
spec.email = ["mnbi@users.noreply.github.com"]
|
8
|
+
|
9
|
+
spec.summary = %q{A simple utility to write a note.}
|
10
|
+
spec.description = %q{Rbnotes allows you to write a note into a single repository.}
|
11
|
+
spec.homepage = "https://github.com/mnbi/rbnotes"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/mnbi/rbnotes"
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/mnbi/rbnotes/blob/main/CHANGELOG.md"
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
end
|
24
|
+
spec.bindir = "exe"
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
|
28
|
+
spec.add_dependency "textrepo", "~> 0.4"
|
29
|
+
spec.add_dependency "unicode-display_width", "~> 1.7"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,99 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: rbnotes
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- mnbi
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-10-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: textrepo
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0.4'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0.4'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: unicode-display_width
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '1.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '1.7'
|
41
|
+
description: Rbnotes allows you to write a note into a single repository.
|
42
|
+
email:
|
43
|
+
- mnbi@users.noreply.github.com
|
44
|
+
executables:
|
45
|
+
- rbnotes
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- ".travis.yml"
|
51
|
+
- CHANGELOG.md
|
52
|
+
- Gemfile
|
53
|
+
- Gemfile.lock
|
54
|
+
- LICENSE
|
55
|
+
- README.md
|
56
|
+
- Rakefile
|
57
|
+
- bin/console
|
58
|
+
- bin/setup
|
59
|
+
- exe/rbnotes
|
60
|
+
- lib/rbnotes.rb
|
61
|
+
- lib/rbnotes/commands.rb
|
62
|
+
- lib/rbnotes/commands/add.rb
|
63
|
+
- lib/rbnotes/commands/delete.rb
|
64
|
+
- lib/rbnotes/commands/import.rb
|
65
|
+
- lib/rbnotes/commands/list.rb
|
66
|
+
- lib/rbnotes/commands/show.rb
|
67
|
+
- lib/rbnotes/commands/update.rb
|
68
|
+
- lib/rbnotes/conf.rb
|
69
|
+
- lib/rbnotes/error.rb
|
70
|
+
- lib/rbnotes/utils.rb
|
71
|
+
- lib/rbnotes/version.rb
|
72
|
+
- rbnotes.gemspec
|
73
|
+
homepage: https://github.com/mnbi/rbnotes
|
74
|
+
licenses:
|
75
|
+
- MIT
|
76
|
+
metadata:
|
77
|
+
homepage_uri: https://github.com/mnbi/rbnotes
|
78
|
+
source_code_uri: https://github.com/mnbi/rbnotes
|
79
|
+
changelog_uri: https://github.com/mnbi/rbnotes/blob/main/CHANGELOG.md
|
80
|
+
post_install_message:
|
81
|
+
rdoc_options: []
|
82
|
+
require_paths:
|
83
|
+
- lib
|
84
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
requirements:
|
86
|
+
- - ">="
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: 2.7.0
|
89
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
requirements: []
|
95
|
+
rubygems_version: 3.1.4
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: A simple utility to write a note.
|
99
|
+
test_files: []
|