textrepo 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +6 -0
- data/CHANGELOG.md +40 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.txt +21 -0
- data/README.md +77 -0
- data/Rakefile +37 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/examples/rbnotes/lib/rbnotes.rb +8 -0
- data/examples/rbnotes/lib/rbnotes/commands.rb +118 -0
- data/examples/rbnotes/lib/rbnotes/commands/import.rb +62 -0
- data/examples/rbnotes/lib/rbnotes/commands/list.rb +60 -0
- data/examples/rbnotes/lib/rbnotes/commands/show.rb +25 -0
- data/examples/rbnotes/lib/rbnotes/version.rb +4 -0
- data/examples/rbnotes/rbnotes +33 -0
- data/lib/textrepo.rb +6 -0
- data/lib/textrepo/error.rb +50 -0
- data/lib/textrepo/file_system_repository.rb +148 -0
- data/lib/textrepo/repository.rb +62 -0
- data/lib/textrepo/timestamp.rb +63 -0
- data/lib/textrepo/version.rb +3 -0
- data/textrepo.gemspec +29 -0
- metadata +85 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 4d26af6dd86e8b79c9e50300f8c31102bf79d5c751e175740a7225c2ca4a9b33
|
|
4
|
+
data.tar.gz: 8d398c0848c0a4b06d2a775f3cb6bf1f49489be7f5ba5d8cf7eab2aad068b5c7
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ef79855cd99ebc3baea4368c00cdc4d439478cfc9733d45eee1e7a08ca005bf0dc15824cb57b46326c982e724acc762b25b722c112f19ae4f67349d575fffdcb
|
|
7
|
+
data.tar.gz: 4af657999971e6d3cef41dc5d3ef7ea1017a536b772372d38e79ffb91196c239585a0c932fe10e42f9d7ccbfee8fa477ec31bd97afa9cd4e1c31f0c894772110
|
data/.gitignore
ADDED
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.4.0] - 2020-10-14
|
|
11
|
+
### Added
|
|
12
|
+
- Released to rubygems.org.
|
|
13
|
+
|
|
14
|
+
### Changed
|
|
15
|
+
- Rename the method, `Repository#notes` to `entries`.
|
|
16
|
+
- Modify the instruction to install
|
|
17
|
+
|
|
18
|
+
## [0.3.0] - 2020-10-11
|
|
19
|
+
### Added
|
|
20
|
+
- Go public onto GitHub.
|
|
21
|
+
- Add an example (`rbnotes`) to demonstrate how to use `textrepo`.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
- Modify not to handle fraction of time in Timestamp.
|
|
25
|
+
- Instead, Timestamp can have a suffix to distinguish 2 stamps those
|
|
26
|
+
represent the same time.
|
|
27
|
+
|
|
28
|
+
## [0.2.0] - 2020-09-28
|
|
29
|
+
### Changed
|
|
30
|
+
- Modify to handle millisecond in Timestamp
|
|
31
|
+
|
|
32
|
+
## [0.1.0] - 2020-09-23
|
|
33
|
+
### Added
|
|
34
|
+
- Add some target in Rakefile to run test easily.
|
|
35
|
+
- Add error classes
|
|
36
|
+
- Add Timestamp class, it will be an identifier of each text in the repo.
|
|
37
|
+
- Add Repository class (an abstract base class for concrete repository
|
|
38
|
+
implementations)
|
|
39
|
+
- Implement API for FileSystemRepository class (create/read/update/delete).
|
|
40
|
+
- Add tests.
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
textrepo (0.4.1)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
minitest (5.14.2)
|
|
10
|
+
rake (13.0.1)
|
|
11
|
+
|
|
12
|
+
PLATFORMS
|
|
13
|
+
ruby
|
|
14
|
+
|
|
15
|
+
DEPENDENCIES
|
|
16
|
+
bundler (~> 2.1)
|
|
17
|
+
minitest (~> 5.0)
|
|
18
|
+
rake (~> 13.0)
|
|
19
|
+
textrepo!
|
|
20
|
+
|
|
21
|
+
BUNDLED WITH
|
|
22
|
+
2.1.4
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Textrepo
|
|
2
|
+
|
|
3
|
+
[](https://travis-ci.org/mnbi/textrepo)
|
|
4
|
+
|
|
5
|
+
Textrepo is a repository to store a note with a timestamp. Each note
|
|
6
|
+
in the repository operates with the associated timestamp.
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
Add this line to your application's Gemfile:
|
|
11
|
+
|
|
12
|
+
```ruby
|
|
13
|
+
gem 'textrepo'
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
And then execute:
|
|
17
|
+
|
|
18
|
+
$ bundle install
|
|
19
|
+
|
|
20
|
+
Or install it yourself as:
|
|
21
|
+
|
|
22
|
+
$ gem install textrepo
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
Here is a very short sample to use `textrepo`. It will make
|
|
27
|
+
`~/textrepo_sample` directory and store some text into it.
|
|
28
|
+
|
|
29
|
+
``` ruby
|
|
30
|
+
#!/usr/bin/env ruby
|
|
31
|
+
|
|
32
|
+
require "textrepo"
|
|
33
|
+
|
|
34
|
+
conf = {
|
|
35
|
+
:repository_type => :file_system,
|
|
36
|
+
:repository_name => "textrepo_sample",
|
|
37
|
+
:repository_base => File.expand_path("~"),
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
repo = Textrepo.init(conf)
|
|
41
|
+
|
|
42
|
+
t0 = Time.now
|
|
43
|
+
|
|
44
|
+
stamps = []
|
|
45
|
+
stamps << repo.create(Textrepo::Timestamp.new(t0), ["jan", "feb", "mar"])
|
|
46
|
+
stamps << repo.create(Textrepo::Timestamp.new(t0, 1), ["apr", "may", "jun"])
|
|
47
|
+
stamps << repo.create(Textrepo::Timestamp.new(t0, 2), ["jul", "aug", "sep"])
|
|
48
|
+
stamps << repo.create(Textrepo::Timestamp.new(t0, 3), ["oct", "nov", "dec"])
|
|
49
|
+
|
|
50
|
+
entries = repo.notes
|
|
51
|
+
puts entries
|
|
52
|
+
|
|
53
|
+
stamps.each { |stamp|
|
|
54
|
+
text = repo.read(stamp)
|
|
55
|
+
puts "----"
|
|
56
|
+
puts stamp
|
|
57
|
+
puts text
|
|
58
|
+
}
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Also see `examples` directory. There is a small tool to demonstrate
|
|
62
|
+
how to use `textrepo`.
|
|
63
|
+
|
|
64
|
+
## Development
|
|
65
|
+
|
|
66
|
+
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.
|
|
67
|
+
|
|
68
|
+
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).
|
|
69
|
+
|
|
70
|
+
## Contributing
|
|
71
|
+
|
|
72
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mnbi/textrepo.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## License
|
|
76
|
+
|
|
77
|
+
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,37 @@
|
|
|
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
|
+
desc 'Setup test data'
|
|
13
|
+
task :setup_test do
|
|
14
|
+
print "Setting up to execute tests..."
|
|
15
|
+
load('test/fixtures/setup_test_repo.rb', true)
|
|
16
|
+
puts "done."
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
task test: [:setup_test, :clean_sandbox]
|
|
20
|
+
|
|
21
|
+
require "fileutils"
|
|
22
|
+
|
|
23
|
+
desc "Clean up test/sandbox"
|
|
24
|
+
task :clean_sandbox do
|
|
25
|
+
sandbox = File.expand_path('test/sandbox', __dir__)
|
|
26
|
+
entries = Dir.entries(sandbox).filter_map { |e|
|
|
27
|
+
File.expand_path(e, sandbox) unless e == '.' or e == '..'
|
|
28
|
+
}
|
|
29
|
+
entries.each { |e|
|
|
30
|
+
next if File.basename(e) == '.gitkeep'
|
|
31
|
+
FileUtils.remove_entry_secure(e) if FileTest.exist?(e)
|
|
32
|
+
}
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
task :clobber => :clean_sandbox
|
|
36
|
+
CLOBBER << 'test/fixtures/notes'
|
|
37
|
+
CLOBBER << 'test/fixtures/test_repo'
|
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "textrepo"
|
|
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
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
module Rbnotes
|
|
2
|
+
module Commands
|
|
3
|
+
class Command
|
|
4
|
+
def execute(args, conf)
|
|
5
|
+
Builtins::DEFAULT_CMD.new.execute(args, conf)
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Built-in commands:
|
|
10
|
+
# - repo: prints the absolute path of the repository.
|
|
11
|
+
# - conf: prints all of the current configuration settings.
|
|
12
|
+
# - stamp: converts given TIME_STR into a timestamp.
|
|
13
|
+
# - time: converts given STAMP into a time string.
|
|
14
|
+
module Builtins
|
|
15
|
+
class Help < Command
|
|
16
|
+
def execute(_, _)
|
|
17
|
+
puts <<USAGE
|
|
18
|
+
usage: rbnotes [command] [args]
|
|
19
|
+
|
|
20
|
+
command:
|
|
21
|
+
import FILE : import a FILE into the repository
|
|
22
|
+
list NUM : list NUM notes
|
|
23
|
+
show STAMP : show the note specified with STAMP
|
|
24
|
+
|
|
25
|
+
repo : print the repository path
|
|
26
|
+
stamp TIME_STR : convert TIME_STR into a timestamp
|
|
27
|
+
time STAMP : convert STAMP into a time string
|
|
28
|
+
version : print version
|
|
29
|
+
help : show help
|
|
30
|
+
USAGE
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
class Version < Command
|
|
35
|
+
def execute(_, _)
|
|
36
|
+
rbnotes_version = "rbnotes #{Rbnotes::VERSION} (#{Rbnotes::RELEASE})"
|
|
37
|
+
textrepo_version = "textrepo #{Textrepo::VERSION}"
|
|
38
|
+
puts "#{rbnotes_version} (#{textrepo_version})"
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
class Repo < Command
|
|
43
|
+
def execute(_, conf)
|
|
44
|
+
name = conf[:repository_name]
|
|
45
|
+
base = conf[:repository_base]
|
|
46
|
+
type = conf[:repository_type]
|
|
47
|
+
|
|
48
|
+
puts case type
|
|
49
|
+
when :file_system
|
|
50
|
+
"#{base}/#{name}"
|
|
51
|
+
else
|
|
52
|
+
"#{base}/#{name}"
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
class Conf < Command
|
|
58
|
+
def execute(_, conf)
|
|
59
|
+
conf.keys.sort.each { |k|
|
|
60
|
+
puts "#{k}=#{conf[k]}"
|
|
61
|
+
}
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
require "time"
|
|
66
|
+
|
|
67
|
+
class Stamp < Command
|
|
68
|
+
def execute(args, _)
|
|
69
|
+
time_str = args.shift
|
|
70
|
+
unless time_str.nil?
|
|
71
|
+
puts Textrepo::Timestamp.new(::Time.parse(time_str)).to_s
|
|
72
|
+
else
|
|
73
|
+
puts "not specified TIME_STR"
|
|
74
|
+
super
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class Time < Command
|
|
80
|
+
def execute(args, _)
|
|
81
|
+
stamp = args.shift
|
|
82
|
+
unless stamp.nil?
|
|
83
|
+
puts ::Time.new(*Textrepo::Timestamp.split_stamp(stamp).map(&:to_i)).to_s
|
|
84
|
+
else
|
|
85
|
+
puts "not specified STAMP"
|
|
86
|
+
super
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
DEFAULT_CMD = Help
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
DEFAULT_CMD_NAME = "help"
|
|
95
|
+
|
|
96
|
+
class << self
|
|
97
|
+
def load(cmd_name)
|
|
98
|
+
cmd_name ||= DEFAULT_CMD_NAME
|
|
99
|
+
klass_name = cmd_name.capitalize
|
|
100
|
+
|
|
101
|
+
klass = nil
|
|
102
|
+
if Builtins.const_defined?(klass_name, false)
|
|
103
|
+
klass = Builtins::const_get(klass_name, false)
|
|
104
|
+
else
|
|
105
|
+
begin
|
|
106
|
+
require_relative "commands/#{cmd_name}"
|
|
107
|
+
klass = const_get(klass_name, false)
|
|
108
|
+
rescue LoadError => _
|
|
109
|
+
STDERR.puts "unknown command: #{cmd_name}"
|
|
110
|
+
klass = Builtins::DEFAULT_CMD
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
klass.new
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
end
|
|
118
|
+
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 => e
|
|
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 => e
|
|
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,60 @@
|
|
|
1
|
+
require "io/console/size"
|
|
2
|
+
|
|
3
|
+
module Rbnotes
|
|
4
|
+
class Commands::List < Commands::Command
|
|
5
|
+
def execute(args, conf)
|
|
6
|
+
@row, @column = IO.console_size
|
|
7
|
+
max = (args.shift || @row - 3).to_i
|
|
8
|
+
|
|
9
|
+
@repo = Textrepo.init(conf)
|
|
10
|
+
notes = @repo.entries.sort{|a, b| b <=> a}
|
|
11
|
+
notes[0, max].each { |timestamp_str|
|
|
12
|
+
puts make_headline(timestamp_str)
|
|
13
|
+
}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
TIMESTAMP_STR_MAX_WIDTH = "yyyymoddhhmiss_sfx".size
|
|
18
|
+
# Makes a headline with the timestamp and subject of the notes, it
|
|
19
|
+
# looks like as follows:
|
|
20
|
+
#
|
|
21
|
+
# |<------------------ console column size --------------------->|
|
|
22
|
+
# +-- timestamp ---+ +--- subject (the 1st line of each note) --+
|
|
23
|
+
# | | | |
|
|
24
|
+
# 20101010001000_123: # I love Macintosh. [EOL]
|
|
25
|
+
# 20100909090909_999: # This is very very long long loooong subje[EOL]
|
|
26
|
+
# ++
|
|
27
|
+
# ^--- delimiter (2 characters)
|
|
28
|
+
#
|
|
29
|
+
# The subject part will truncate when it is long.
|
|
30
|
+
def make_headline(timestamp_str)
|
|
31
|
+
|
|
32
|
+
delimiter = ": "
|
|
33
|
+
subject_width = @column - TIMESTAMP_STR_MAX_WIDTH - delimiter.size - 1
|
|
34
|
+
|
|
35
|
+
subject = @repo.read(Textrepo::Timestamp.parse_s(timestamp_str))[0]
|
|
36
|
+
prefix = '# '
|
|
37
|
+
subject = prefix + subject.lstrip if subject[0, 2] != prefix
|
|
38
|
+
|
|
39
|
+
ts_part = "#{timestamp_str} "[0..(TIMESTAMP_STR_MAX_WIDTH - 1)]
|
|
40
|
+
sj_part = truncate_str(subject, subject_width)
|
|
41
|
+
|
|
42
|
+
ts_part + delimiter + sj_part
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def truncate_str(str, size)
|
|
46
|
+
count = 0
|
|
47
|
+
result = ""
|
|
48
|
+
str.each_char { |c|
|
|
49
|
+
# TODO: fix
|
|
50
|
+
# This code is ugly. It assumes that each non-ascii character
|
|
51
|
+
# always occupy the width of 2 ascii characters in a terminal.
|
|
52
|
+
# I am not sure the assumption is appropriate or not.
|
|
53
|
+
count += c.ascii_only? ? 1 : 2
|
|
54
|
+
break if count > size
|
|
55
|
+
result << c
|
|
56
|
+
}
|
|
57
|
+
result
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
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,33 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift File.expand_path("../../lib", __dir__)
|
|
4
|
+
$LOAD_PATH.unshift File.expand_path("lib", __dir__)
|
|
5
|
+
|
|
6
|
+
require "rbnotes"
|
|
7
|
+
|
|
8
|
+
DEBUG = true
|
|
9
|
+
|
|
10
|
+
module Rbnotes
|
|
11
|
+
class App
|
|
12
|
+
def initialize
|
|
13
|
+
@conf = default_conf
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def default_conf
|
|
17
|
+
{
|
|
18
|
+
:repository_type => :file_system,
|
|
19
|
+
:repository_name => DEBUG ? "sandbox/notes_dev" : "notes",
|
|
20
|
+
:repository_base => File.expand_path("~"),
|
|
21
|
+
:pager => "bat",
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def run(args)
|
|
26
|
+
cmd = args.shift
|
|
27
|
+
Rbnotes::Commands.load(cmd).execute(args, @conf)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
app = Rbnotes::App.new
|
|
33
|
+
app.run(ARGV)
|
data/lib/textrepo.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
module Textrepo
|
|
2
|
+
class Error < StandardError; end
|
|
3
|
+
|
|
4
|
+
module ErrMsg
|
|
5
|
+
UNKNOWN_REPO_TYPE = 'unknown type for repository: %s'
|
|
6
|
+
DUPLICATE_TIMESTAMP = 'duplicate timestamp: %s'
|
|
7
|
+
EMPTY_TEXT = 'empty text'
|
|
8
|
+
MISSING_TIMESTAMP = 'missing timestamp: %s'
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class UnknownRepoTypeError < Error
|
|
12
|
+
def initialize(type)
|
|
13
|
+
super(ErrMsg::UNKNOWN_REPO_TYPE % type)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Following errors might occur in repository operations:
|
|
18
|
+
# +--------------------------+---------------------+
|
|
19
|
+
# | operation (args) | error type |
|
|
20
|
+
# +--------------------------+---------------------+
|
|
21
|
+
# | create (timestamp, text) | Duplicate timestamp |
|
|
22
|
+
# | | Empty text |
|
|
23
|
+
# +--------------------------+---------------------+
|
|
24
|
+
# | read (timestamp) | Missing timestamp |
|
|
25
|
+
# +--------------------------+---------------------+
|
|
26
|
+
# | update (timestamp, text) | Mssing timestamp |
|
|
27
|
+
# | | Empty text |
|
|
28
|
+
# +--------------------------+---------------------+
|
|
29
|
+
# | delete (timestamp) | Missing timestamp |
|
|
30
|
+
# +--------------------------+---------------------+
|
|
31
|
+
|
|
32
|
+
class DuplicateTimestampError < Error
|
|
33
|
+
def initialize(timestamp)
|
|
34
|
+
super(ErrMsg::DUPLICATE_TIMESTAMP % timestamp)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
class EmptyTextError < Error
|
|
39
|
+
def initialize
|
|
40
|
+
super(ErrMsg::EMPTY_TEXT)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class MissingTimestampError < Error
|
|
45
|
+
def initialize(timestamp)
|
|
46
|
+
super(ErrMsg::MISSING_TIMESTAMP % timestamp)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
require 'fileutils'
|
|
2
|
+
|
|
3
|
+
module Textrepo
|
|
4
|
+
# A concrete repository which uses the default file system as a storage.
|
|
5
|
+
class FileSystemRepository < Repository
|
|
6
|
+
attr_reader :path, :extname
|
|
7
|
+
|
|
8
|
+
FAVORITE_REPOSITORY_NAME = 'notes'
|
|
9
|
+
FAVORITE_EXTNAME = 'md'
|
|
10
|
+
|
|
11
|
+
# `conf` must be a Hash object. It must hold the follwoing
|
|
12
|
+
# values:
|
|
13
|
+
#
|
|
14
|
+
# - :repository_type (:file_system)
|
|
15
|
+
# - :repository_name => basename of the root path for the repository
|
|
16
|
+
# - :repository_base => the parent directory path for the repository
|
|
17
|
+
# - :default_extname => extname for a file stored into in the repository
|
|
18
|
+
#
|
|
19
|
+
# The root path of the repository looks like the following:
|
|
20
|
+
# - conf[:repository_base]/conf[:repository_name]
|
|
21
|
+
#
|
|
22
|
+
# Default values are set when `repository_name` and `default_extname`
|
|
23
|
+
# were not defined in `conf`.
|
|
24
|
+
def initialize(conf)
|
|
25
|
+
super
|
|
26
|
+
base = conf[:repository_base]
|
|
27
|
+
@name ||= FAVORITE_REPOSITORY_NAME
|
|
28
|
+
@path = File.expand_path("#{name}", base)
|
|
29
|
+
FileUtils.mkdir_p(@path)
|
|
30
|
+
@extname = conf[:default_extname] || FAVORITE_EXTNAME
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
#
|
|
34
|
+
# repository operations
|
|
35
|
+
#
|
|
36
|
+
|
|
37
|
+
# Creates a file into the repository, which contains the specified
|
|
38
|
+
# text and is associated to the timestamp.
|
|
39
|
+
def create(timestamp, text)
|
|
40
|
+
abs = abspath(timestamp)
|
|
41
|
+
raise DuplicateTimestampError, timestamp if FileTest.exist?(abs)
|
|
42
|
+
raise EmptyTextError if text.nil? || text.empty?
|
|
43
|
+
|
|
44
|
+
write_text(abs, text)
|
|
45
|
+
timestamp
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Reads the file content in the repository. Then, returns its
|
|
49
|
+
# content.
|
|
50
|
+
def read(timestamp)
|
|
51
|
+
abs = abspath(timestamp)
|
|
52
|
+
raise MissingTimestampError, timestamp unless FileTest.exist?(abs)
|
|
53
|
+
content = nil
|
|
54
|
+
File.open(abs, 'r') { |f|
|
|
55
|
+
content = f.readlines(chomp: true)
|
|
56
|
+
}
|
|
57
|
+
content
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Updates the file content in the repository. A new timestamp
|
|
61
|
+
# will be attached to the text.
|
|
62
|
+
def update(timestamp, text)
|
|
63
|
+
raise EmptyTextError if text.empty?
|
|
64
|
+
org_abs = abspath(timestamp)
|
|
65
|
+
raise MissingTimestampError, timestamp unless FileTest.exist?(org_abs)
|
|
66
|
+
|
|
67
|
+
# the text must be stored with the new timestamp
|
|
68
|
+
new_stamp = Timestamp.new(Time.now)
|
|
69
|
+
new_abs = abspath(new_stamp)
|
|
70
|
+
write_text(new_abs, text)
|
|
71
|
+
|
|
72
|
+
# delete the original file in the repository
|
|
73
|
+
FileUtils.remove_file(org_abs)
|
|
74
|
+
|
|
75
|
+
new_stamp
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Deletes the file in the repository.
|
|
79
|
+
def delete(timestamp)
|
|
80
|
+
abs = abspath(timestamp)
|
|
81
|
+
raise MissingTimestampError, timestamp unless FileTest.exist?(abs)
|
|
82
|
+
content = read(timestamp)
|
|
83
|
+
|
|
84
|
+
FileUtils.remove_file(abs)
|
|
85
|
+
|
|
86
|
+
content
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Finds entries of text those timestamp matches the specified pattern.
|
|
90
|
+
def entries(stamp_pattern = nil)
|
|
91
|
+
results = []
|
|
92
|
+
|
|
93
|
+
case stamp_pattern.to_s.size
|
|
94
|
+
when "yyyymoddhhmiss_lll".size
|
|
95
|
+
stamp = Timestamp.parse_s(stamp_pattern)
|
|
96
|
+
if exist?(stamp)
|
|
97
|
+
results << stamp.to_s
|
|
98
|
+
end
|
|
99
|
+
when 0, "yyyymoddhhmiss".size, "yyyymodd".size
|
|
100
|
+
results += find_entries(stamp_pattern)
|
|
101
|
+
when 4 # "yyyy" or "modd"
|
|
102
|
+
pat = nil
|
|
103
|
+
# The following distinction is practically correct, but not
|
|
104
|
+
# perfect. It simply assumes that a year is greater than
|
|
105
|
+
# 1231. For, a year before 1232 is too old for us to create
|
|
106
|
+
# any text (I believe...).
|
|
107
|
+
if stamp_pattern.to_i > 1231
|
|
108
|
+
# yyyy
|
|
109
|
+
pat = stamp_pattern
|
|
110
|
+
else
|
|
111
|
+
# modd
|
|
112
|
+
pat = "*#{stamp_pattern}"
|
|
113
|
+
end
|
|
114
|
+
results += find_entries(pat)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
results
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
private
|
|
121
|
+
def abspath(timestamp)
|
|
122
|
+
filename = timestamp.to_pathname + ".#{@extname}"
|
|
123
|
+
File.expand_path(filename, @path)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def write_text(abs, text)
|
|
127
|
+
FileUtils.mkdir_p(File.dirname(abs))
|
|
128
|
+
File.open(abs, 'w') { |f|
|
|
129
|
+
text.each {|line| f.puts(line) }
|
|
130
|
+
}
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def timestamp_str(text_path)
|
|
134
|
+
File.basename(text_path).delete_suffix(".#{@extname}")
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def exist?(timestamp)
|
|
138
|
+
FileTest.exist?(abspath(timestamp))
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def find_entries(stamp_pattern)
|
|
142
|
+
Dir.glob("#{@path}/**/#{stamp_pattern}*.#{@extname}").map { |e|
|
|
143
|
+
timestamp_str(e)
|
|
144
|
+
}
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
end
|
|
148
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Textrepo
|
|
2
|
+
class Repository
|
|
3
|
+
attr_reader :type, :name
|
|
4
|
+
|
|
5
|
+
def initialize(conf)
|
|
6
|
+
@type = conf[:repository_type]
|
|
7
|
+
@name = conf[:repository_name]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
# Stores text data into the repository with the specified timestamp.
|
|
11
|
+
# Returns the timestamp.
|
|
12
|
+
def create(timestamp, text); timestamp; end
|
|
13
|
+
|
|
14
|
+
# Reads text data from the repository, which is associated to the
|
|
15
|
+
# timestamp. Returns an array which contains the text.
|
|
16
|
+
def read(timestamp); []; end
|
|
17
|
+
|
|
18
|
+
# Updates the content with text in the repository, which is
|
|
19
|
+
# associated to the timestamp. Returns the timestamp.
|
|
20
|
+
def update(timestamp, text); timestamp; end
|
|
21
|
+
|
|
22
|
+
# Deletes the content in the repository, which is associated to
|
|
23
|
+
# the timestamp. Returns an array which contains the deleted text.
|
|
24
|
+
def delete(timestamp); []; end
|
|
25
|
+
|
|
26
|
+
# Finds all entries of text those have timestamps which mathes the
|
|
27
|
+
# specified pattern of timestamp. Returns an array which contains
|
|
28
|
+
# timestamps. A pattern must be one of the following:
|
|
29
|
+
#
|
|
30
|
+
# - yyyymoddhhmiss_lll : whole stamp
|
|
31
|
+
# - yyyymoddhhmiss : omit millisecond part
|
|
32
|
+
# - yyyymodd : date part only
|
|
33
|
+
# - yyyymo : month and year
|
|
34
|
+
# - yyyy : year only
|
|
35
|
+
# - modd : month and day
|
|
36
|
+
#
|
|
37
|
+
# If `stamp_pattern` is omitted, the recent entries will be listed.
|
|
38
|
+
# Then, how many entries are listed depends on the implementaiton
|
|
39
|
+
# of the concrete repository class.
|
|
40
|
+
def entries(stamp_pattern = nil); []; end
|
|
41
|
+
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
require_relative 'file_system_repository'
|
|
45
|
+
|
|
46
|
+
# Returns an instance which derived from Textrepo::Repository class.
|
|
47
|
+
# `conf` must be a Hash object which has a value of
|
|
48
|
+
# `:repository_type` and `:repository_name` at least. Some concrete
|
|
49
|
+
# class derived from Textrepo::Repository may require more key-value
|
|
50
|
+
# pairs in `conf`.
|
|
51
|
+
def init(conf)
|
|
52
|
+
type = conf[:repository_type]
|
|
53
|
+
klass_name = type.to_s.split(/_/).map(&:capitalize).join + "Repository"
|
|
54
|
+
if Textrepo.const_defined?(klass_name)
|
|
55
|
+
klass = Textrepo.const_get(klass_name, false)
|
|
56
|
+
else
|
|
57
|
+
raise UnknownRepoTypeError, type.nil? ? "(nil)" : type
|
|
58
|
+
end
|
|
59
|
+
klass.new(conf)
|
|
60
|
+
end
|
|
61
|
+
module_function :init
|
|
62
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Textrepo
|
|
2
|
+
class Timestamp
|
|
3
|
+
include Comparable
|
|
4
|
+
|
|
5
|
+
attr_reader :time, :suffix
|
|
6
|
+
|
|
7
|
+
# time: a Time instance
|
|
8
|
+
# suffix: an Integer instance
|
|
9
|
+
def initialize(time, suffix = nil)
|
|
10
|
+
@time = time
|
|
11
|
+
@suffix = suffix
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def <=>(other)
|
|
15
|
+
result = (self.time <=> other.time)
|
|
16
|
+
|
|
17
|
+
sfx = self.suffix || 0
|
|
18
|
+
osfx = other.suffix || 0
|
|
19
|
+
|
|
20
|
+
result == 0 ? (sfx <=> osfx) : result
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# %Y %m %d %H %M %S suffix
|
|
24
|
+
# "2020-12-30 12:34:56 (0 | nil)" => "20201230123456"
|
|
25
|
+
# "2020-12-30 12:34:56 (7)" => "20201230123456_007"
|
|
26
|
+
def to_s
|
|
27
|
+
s = @time.strftime("%Y%m%d%H%M%S")
|
|
28
|
+
s += "_#{"%03u" % @suffix}" unless @suffix.nil? || @suffix == 0
|
|
29
|
+
s
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# %Y %m %d %H %M %S suffix %Y/%m/ %Y%m%d%H%M%S %L
|
|
33
|
+
# "2020-12-30 12:34:56 (0 | nil)" => "2020/12/20201230123456"
|
|
34
|
+
# "2020-12-30 12:34:56 (7)" => "2020/12/20201230123456_007"
|
|
35
|
+
def to_pathname
|
|
36
|
+
@time.strftime("%Y/%m/") + self.to_s
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
class << self
|
|
40
|
+
# yyyymoddhhmiss sfx yyyy mo dd hh mi ss sfx
|
|
41
|
+
# "20201230123456" => "2020", "12", "30", "12", "34", "56"
|
|
42
|
+
# "20201230123456_789" => "2020", "12", "30", "12", "34", "56", "789"
|
|
43
|
+
def split_stamp(stamp_str)
|
|
44
|
+
# yyyy mo dd hh mi ss sfx
|
|
45
|
+
a = [0..3, 4..5, 6..7, 8..9, 10..11, 12..13, 15..17].map {|r| stamp_str[r]}
|
|
46
|
+
a[-1].nil? ? a[0..-2] : a
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def parse_s(stamp_str)
|
|
50
|
+
year, mon, day, hour, min, sec , sfx = split_stamp(stamp_str).map(&:to_i)
|
|
51
|
+
Timestamp.new(Time.new(year, mon, day, hour, min, sec), sfx)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# (-2)
|
|
55
|
+
# 0 8 |(-1)
|
|
56
|
+
# V V VV
|
|
57
|
+
# "2020/12/20201230123456" => "2020-12-30 12:34:56"
|
|
58
|
+
def parse_pathname(pathname)
|
|
59
|
+
parse_s(pathname[8..-1])
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
data/textrepo.gemspec
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require_relative 'lib/textrepo/version'
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |spec|
|
|
4
|
+
spec.name = "textrepo"
|
|
5
|
+
spec.version = Textrepo::VERSION
|
|
6
|
+
spec.authors = ["mnbi"]
|
|
7
|
+
spec.email = ["mnbi@users.noreply.github.com"]
|
|
8
|
+
|
|
9
|
+
spec.summary = %q{A repository to store text with timestamp.}
|
|
10
|
+
spec.description = %q{Textrepo is a repository to store text with timestamp. It can manage text with the attached timestamp (read/update/delte).}
|
|
11
|
+
spec.homepage = "https://github.com/mnbi/textrepo"
|
|
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/textrepo"
|
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/mnbi/textrepo/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_development_dependency "bundler", "~> 2.1"
|
|
29
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: textrepo
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.4.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- mnbi
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2020-10-15 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.1'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.1'
|
|
27
|
+
description: Textrepo is a repository to store text with timestamp. It can manage
|
|
28
|
+
text with the attached timestamp (read/update/delte).
|
|
29
|
+
email:
|
|
30
|
+
- mnbi@users.noreply.github.com
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- ".gitignore"
|
|
36
|
+
- ".travis.yml"
|
|
37
|
+
- CHANGELOG.md
|
|
38
|
+
- Gemfile
|
|
39
|
+
- Gemfile.lock
|
|
40
|
+
- LICENSE.txt
|
|
41
|
+
- README.md
|
|
42
|
+
- Rakefile
|
|
43
|
+
- bin/console
|
|
44
|
+
- bin/setup
|
|
45
|
+
- examples/rbnotes/lib/rbnotes.rb
|
|
46
|
+
- examples/rbnotes/lib/rbnotes/commands.rb
|
|
47
|
+
- examples/rbnotes/lib/rbnotes/commands/import.rb
|
|
48
|
+
- examples/rbnotes/lib/rbnotes/commands/list.rb
|
|
49
|
+
- examples/rbnotes/lib/rbnotes/commands/show.rb
|
|
50
|
+
- examples/rbnotes/lib/rbnotes/version.rb
|
|
51
|
+
- examples/rbnotes/rbnotes
|
|
52
|
+
- lib/textrepo.rb
|
|
53
|
+
- lib/textrepo/error.rb
|
|
54
|
+
- lib/textrepo/file_system_repository.rb
|
|
55
|
+
- lib/textrepo/repository.rb
|
|
56
|
+
- lib/textrepo/timestamp.rb
|
|
57
|
+
- lib/textrepo/version.rb
|
|
58
|
+
- textrepo.gemspec
|
|
59
|
+
homepage: https://github.com/mnbi/textrepo
|
|
60
|
+
licenses:
|
|
61
|
+
- MIT
|
|
62
|
+
metadata:
|
|
63
|
+
homepage_uri: https://github.com/mnbi/textrepo
|
|
64
|
+
source_code_uri: https://github.com/mnbi/textrepo
|
|
65
|
+
changelog_uri: https://github.com/mnbi/textrepo/blob/main/CHANGELOG.md
|
|
66
|
+
post_install_message:
|
|
67
|
+
rdoc_options: []
|
|
68
|
+
require_paths:
|
|
69
|
+
- lib
|
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 2.7.0
|
|
75
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - ">="
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: '0'
|
|
80
|
+
requirements: []
|
|
81
|
+
rubygems_version: 3.1.4
|
|
82
|
+
signing_key:
|
|
83
|
+
specification_version: 4
|
|
84
|
+
summary: A repository to store text with timestamp.
|
|
85
|
+
test_files: []
|