oktags 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|