rbnotes 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/mnbi/rbnotes.svg?branch=main)](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: []
|