oktags 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/{ruby.yml → ci.yml} +1 -1
- data/README.org +20 -18
- data/lib/ok/tags.rb +86 -42
- data/lib/ok/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e7c1bf8fa36aa3489c778e0b12a19d353ddfd05df9fc21ef1a86e9b207a094ee
|
4
|
+
data.tar.gz: c9c873a9d5db3d4703544d95eb8f1bfa3b86903ba85dcd46ad4030766a1ad5da
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 792329f1c8911835393d37e47b7f6c40f3f8ce8108ac38320178704f830be794a35cf1a6bb639006f6024543e89ed19ab144ad0fb64cbdd469c5986b19b1e9d5
|
7
|
+
data.tar.gz: 49fc86f66ba0634bc4d3f7344f432a14aa67e270ea15158ccbc0cd716d8c452a4dd68125796db6ba8e4ebc4d11065e48cdb1fcb26d03735fdda136610e91ab05
|
data/README.org
CHANGED
@@ -7,6 +7,9 @@
|
|
7
7
|
|___/
|
8
8
|
#+end_example
|
9
9
|
|
10
|
+
#+html: <img src="https://github.com/200ok-ch/oktags/workflows/CI/badge.svg"/>
|
11
|
+
#+html: <a href="https://rubygems.org/gems/oktags"> <img src="https://badge.fury.io/rb/oktags.svg"/></a>
|
12
|
+
|
10
13
|
* NAME
|
11
14
|
|
12
15
|
=oktags= - manage tags on plain old files
|
@@ -26,10 +29,10 @@
|
|
26
29
|
|
27
30
|
* DESCRIPTION
|
28
31
|
|
29
|
-
=oktags= helps you organize your files by managing tags on them.
|
30
|
-
|
31
|
-
|
32
|
-
on Linux, macOS and Windows.
|
32
|
+
=oktags= helps you organize your files by managing tags on them. It
|
33
|
+
works by adding/removing at the end of the filename into a
|
34
|
+
=--[tag1,tag2]= construct. The implementation is OS-agnostic, so it
|
35
|
+
should work on Linux, macOS and Windows.
|
33
36
|
|
34
37
|
* EXAMPLES
|
35
38
|
|
@@ -38,8 +41,8 @@ Listing all tags in the current folder.
|
|
38
41
|
#+begin_example
|
39
42
|
$ touch foo
|
40
43
|
$ touch bar.txt
|
41
|
-
$ touch foobar--tag1,tag2.pdf
|
42
|
-
$ touch baz--tag1.txt
|
44
|
+
$ touch foobar--[tag1,tag2].pdf
|
45
|
+
$ touch baz--[tag1].txt
|
43
46
|
$ oktags -l
|
44
47
|
tag1(2)
|
45
48
|
tag2(1)
|
@@ -55,9 +58,9 @@ Listing all tags for a given path glob (assuming the same data set as above).
|
|
55
58
|
Adding tags to a file. NB: Tags are always unique.
|
56
59
|
|
57
60
|
#+begin_example
|
58
|
-
oktags -a tag3,tag2 foobar--tag1,tag2.pdf
|
61
|
+
oktags -a tag3,tag2 foobar--[tag1,tag2].pdf
|
59
62
|
$ ls foobar*
|
60
|
-
foobar--tag1,tag2,tag3.pdf
|
63
|
+
foobar--[tag1,tag2,tag3].pdf
|
61
64
|
#+end_example
|
62
65
|
|
63
66
|
Interactively adding tags (with auto-completion through readline) to a file.
|
@@ -68,7 +71,7 @@ Interactively adding tags (with auto-completion through readline) to a file.
|
|
68
71
|
tag1 tag2 tag3
|
69
72
|
> tag2, new tag
|
70
73
|
$ ls foo* | grep new
|
71
|
-
foo--new tag,tag2
|
74
|
+
foo--[new tag,tag2]
|
72
75
|
#+end_example
|
73
76
|
|
74
77
|
Renaming tags.
|
@@ -88,10 +91,10 @@ Remove a tag from a file.
|
|
88
91
|
|
89
92
|
#+begin_example
|
90
93
|
$ find . | grep business_card | head -n 1
|
91
|
-
./archiv/Cyrill_Schwitter--business_card,somedia,seo.pdf
|
92
|
-
$ oktags -d seo ./archiv/Cyrill_Schwitter--business_card,somedia,seo.pdf
|
94
|
+
./archiv/Cyrill_Schwitter--[business_card,somedia,seo].pdf
|
95
|
+
$ oktags -d seo ./archiv/Cyrill_Schwitter--[business_card,somedia,seo].pdf
|
93
96
|
$ find . | grep business_card | head -n 1
|
94
|
-
./archiv/Cyrill_Schwitter--business_card,somedia.pdf
|
97
|
+
./archiv/Cyrill_Schwitter--[business_card,somedia].pdf
|
95
98
|
#+end_example
|
96
99
|
|
97
100
|
* INSTALLATION
|
@@ -111,8 +114,8 @@ You can also run =bin/console= for an interactive prompt that will
|
|
111
114
|
allow you to experiment.
|
112
115
|
|
113
116
|
To install this gem onto your local machine, run =bundle exec rake
|
114
|
-
install=. To
|
115
|
-
=version.rb=, and then run =bundle exec rake
|
117
|
+
install=. To publish a new version, update the version number in
|
118
|
+
=version.rb=, and then run =bundle exec rake publish=, which will
|
116
119
|
create a git tag for the version, push git commits and tags, and push
|
117
120
|
the =.gem= file to [[https://rubygems.org][rubygems.org]].
|
118
121
|
|
@@ -121,11 +124,10 @@ the =.gem= file to [[https://rubygems.org][rubygems.org]].
|
|
121
124
|
Tests are implemented with [[https://rspec.info/][RSpec]] and can be run like this:
|
122
125
|
|
123
126
|
#+begin_src shell
|
124
|
-
|
125
|
-
.....
|
127
|
+
......
|
126
128
|
|
127
|
-
Finished in 0.
|
128
|
-
|
129
|
+
Finished in 0.00408 seconds (files took 0.07109 seconds to load)
|
130
|
+
6 examples, 0 failures
|
129
131
|
#+end_src
|
130
132
|
|
131
133
|
* NOTES
|
data/lib/ok/tags.rb
CHANGED
@@ -10,33 +10,41 @@ module OK
|
|
10
10
|
class Error < StandardError; end
|
11
11
|
extend self
|
12
12
|
|
13
|
-
|
13
|
+
# oktags uses --[] as the place to save tags. [] does have special
|
14
|
+
# meaning for Ruby Dir.glob, though. Hence, it's escaped.
|
15
|
+
def escape_glob(s)
|
16
|
+
s&.gsub(/[\[\]]/) { |x| "\\" + x }
|
17
|
+
end
|
18
|
+
|
19
|
+
def find_tags_for(path = '**/*')
|
14
20
|
tags = []
|
15
|
-
Dir.glob(path).each do |file|
|
16
|
-
file = File.basename(file,
|
17
|
-
file_tags =
|
21
|
+
Dir.glob(escape_glob(path)).each do |file|
|
22
|
+
file = File.basename(file, '.*')
|
23
|
+
file_tags =
|
24
|
+
file.match(/--\[(.*)\]/)&.to_a&.at(1)&.split(',')&.map(&:strip)&.map(
|
25
|
+
&:downcase
|
26
|
+
)
|
18
27
|
tags << file_tags if file_tags
|
19
28
|
end
|
20
29
|
|
21
|
-
tags
|
30
|
+
tags.flatten.compact.sort
|
22
31
|
end
|
23
32
|
|
24
33
|
def count_tags(tags)
|
25
|
-
tags.inject({})
|
34
|
+
tags.inject({}) do |acc, tag|
|
26
35
|
acc[tag] = acc[tag].to_i + 1
|
27
36
|
acc
|
28
|
-
|
37
|
+
end
|
29
38
|
end
|
30
39
|
|
31
|
-
def list_pretty_tags(path =
|
32
|
-
tags = find_tags_for(path
|
40
|
+
def list_pretty_tags(path = '**/*')
|
41
|
+
tags = find_tags_for(path)
|
33
42
|
counts = count_tags(tags)
|
34
43
|
|
35
|
-
pretty_counts =
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
44
|
+
pretty_counts =
|
45
|
+
counts.sort_by { |tag, count| count }.reverse.map do |tag, count|
|
46
|
+
"#{tag}(#{count})"
|
47
|
+
end
|
40
48
|
|
41
49
|
puts pretty_counts
|
42
50
|
end
|
@@ -44,58 +52,75 @@ module OK
|
|
44
52
|
def filename_with_tags(file, tags)
|
45
53
|
dirname = File.dirname(file)
|
46
54
|
ext = File.extname(file)
|
47
|
-
|
48
|
-
File.
|
55
|
+
|
56
|
+
basename = File.basename(file, '.*')
|
57
|
+
tag_part = basename.match(/--\[(.*)\]/)&.to_a&.at(0)
|
58
|
+
basename = basename.gsub(tag_part, '') if tag_part
|
59
|
+
File.join(dirname, "#{basename}--[#{tags.join(',')}]#{ext}")
|
49
60
|
end
|
50
61
|
|
51
62
|
def add_tags_to_file(new_tags, file)
|
52
|
-
|
63
|
+
unless file
|
64
|
+
(
|
65
|
+
puts 'Needs a FILE input; i.e. `-a tag filename`'
|
66
|
+
exit
|
67
|
+
)
|
68
|
+
end
|
53
69
|
|
54
70
|
tags = find_tags_for(file)
|
55
71
|
tags << new_tags.split(',')&.map(&:strip)&.map(&:downcase)
|
56
72
|
tags = tags.flatten.uniq.sort
|
57
73
|
|
58
74
|
new_filename = filename_with_tags(file, tags)
|
59
|
-
if file != new_filename
|
60
|
-
FileUtils.mv(file, new_filename)
|
61
|
-
end
|
75
|
+
FileUtils.mv(file, new_filename) if file != new_filename
|
62
76
|
|
63
77
|
new_filename
|
64
78
|
end
|
65
79
|
|
66
|
-
def read_and_add_tags_for(file, tags_path =
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
80
|
+
def read_and_add_tags_for(file, tags_path = '**/*')
|
81
|
+
unless File.exist?(file)
|
82
|
+
(
|
83
|
+
puts "File '#{file}' does not exist."
|
84
|
+
exit 1
|
85
|
+
)
|
72
86
|
end
|
73
87
|
|
74
|
-
|
75
|
-
|
88
|
+
tags = find_tags_for(tags_path)
|
89
|
+
Readline.completion_proc =
|
90
|
+
proc { |input| tags.select { |tag| tag.start_with?(input) } }
|
91
|
+
|
92
|
+
puts 'Add new tags:'
|
93
|
+
new_tags = Readline.readline('> ', false)
|
76
94
|
new_filename = add_tags_to_file(new_tags, file)
|
77
95
|
[new_tags, new_filename]
|
78
96
|
end
|
79
97
|
|
80
98
|
def delete_tag_from_file(tag, file)
|
81
|
-
|
99
|
+
unless file
|
100
|
+
(
|
101
|
+
puts 'Needs a FILE input; i.e. `-d tag1 filename`'
|
102
|
+
exit
|
103
|
+
)
|
104
|
+
end
|
82
105
|
|
83
106
|
tags = find_tags_for(file)
|
84
107
|
tags = tags.reject { |t| t == tag }
|
85
108
|
|
86
109
|
new_filename = filename_with_tags(file, tags)
|
87
|
-
if file != new_filename
|
88
|
-
FileUtils.mv(file, new_filename)
|
89
|
-
end
|
110
|
+
FileUtils.mv(file, new_filename) if file != new_filename
|
90
111
|
|
91
112
|
new_filename
|
92
113
|
end
|
93
114
|
|
94
|
-
|
95
115
|
def rename_tag(path, old_tag, new_tag)
|
96
|
-
|
116
|
+
unless new_tag
|
117
|
+
(
|
118
|
+
puts 'Needs a NEW_TAG input; i.e. `-r old_tag new_tag`'
|
119
|
+
exit
|
120
|
+
)
|
121
|
+
end
|
97
122
|
|
98
|
-
Dir.glob("#{path}
|
123
|
+
Dir.glob("#{path}/**/*--\\[*#{old_tag}*\\]*").each do |file|
|
99
124
|
file = add_tags_to_file(new_tag, file)
|
100
125
|
delete_tag_from_file(old_tag, file)
|
101
126
|
end
|
@@ -104,28 +129,47 @@ module OK
|
|
104
129
|
def main
|
105
130
|
OptionParser.new do |opts|
|
106
131
|
opts.banner = 'Usage: oktags [options]'
|
107
|
-
opts.on(
|
108
|
-
|
132
|
+
opts.on(
|
133
|
+
'-l',
|
134
|
+
'--list [PATH]',
|
135
|
+
'List file tags (optionally for PATH)'
|
136
|
+
) do |path|
|
137
|
+
path ? list_pretty_tags(path) : list_pretty_tags
|
109
138
|
exit
|
110
139
|
end
|
111
|
-
opts.on(
|
140
|
+
opts.on(
|
141
|
+
'-a',
|
142
|
+
'--add-tags TAGS FILE',
|
143
|
+
'Add comma-separated TAGS to FILE'
|
144
|
+
) do |tags|
|
112
145
|
add_tags_to_file(tags, ARGV[0])
|
113
146
|
exit
|
114
147
|
end
|
115
|
-
opts.on(
|
148
|
+
opts.on(
|
149
|
+
'-i',
|
150
|
+
'--add-tags-interactively FILE',
|
151
|
+
'Auto-complete tags and add them to FILE'
|
152
|
+
) do |file|
|
116
153
|
read_and_add_tags_for(file)
|
117
154
|
exit
|
118
155
|
end
|
119
|
-
opts.on(
|
156
|
+
opts.on(
|
157
|
+
'-r',
|
158
|
+
'--rename-tag OLD_TAG NEW_TAG',
|
159
|
+
'Rename OLD_TAG to NEW_TAG(S) recursively for all files'
|
160
|
+
) do |old_tag|
|
120
161
|
rename_tag('.', old_tag, ARGV[0])
|
121
162
|
exit
|
122
163
|
end
|
123
|
-
opts.on(
|
164
|
+
opts.on(
|
165
|
+
'-d',
|
166
|
+
'--delete-tag-from-file TAG FILE',
|
167
|
+
'Delete TAG from FILE'
|
168
|
+
) do |tag|
|
124
169
|
delete_tag_from_file(tag, ARGV[0])
|
125
170
|
exit
|
126
171
|
end
|
127
172
|
end.parse!
|
128
|
-
|
129
173
|
end
|
130
174
|
end
|
131
175
|
end
|
data/lib/ok/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: oktags
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alain M. Lafon
|
@@ -20,7 +20,7 @@ executables:
|
|
20
20
|
extensions: []
|
21
21
|
extra_rdoc_files: []
|
22
22
|
files:
|
23
|
-
- ".github/workflows/
|
23
|
+
- ".github/workflows/ci.yml"
|
24
24
|
- ".gitignore"
|
25
25
|
- ".rspec"
|
26
26
|
- ".ruby-version"
|