tagmv 0.0.2 → 0.0.3
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 +5 -5
- data/README.md +19 -7
- data/lib/tagmv.rb +2 -0
- data/lib/tagmv/command_line.rb +31 -7
- data/lib/tagmv/filesystem.rb +43 -11
- data/lib/tagmv/options.rb +24 -0
- data/lib/tagmv/prune_path.rb +1 -1
- data/lib/tagmv/runner.rb +20 -15
- data/lib/tagmv/tree.rb +18 -19
- data/lib/tagmv/version.rb +3 -1
- data/tagmv.gemspec +2 -2
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 7bc6f70a29aae56b307b09be9a3108070ed3330934020412932d62bac97a1202
|
4
|
+
data.tar.gz: b082ddb01729079be19620e3be09e80a3f8f1b51fc311c9f7b7f7fd072232666
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 19567f27d8539032061a6bcd615f4289466c19f991f1c417a56916c4ef27dd1a87ced04c28a575c85ee920c6f57dbc57db56ad51cea15d4566ba827eb8235e5a
|
7
|
+
data.tar.gz: fd6e0bc16ac871938088ceba64b251b8a088ec3781fb74c4a4b73d33844c3b52c49ae3c02321e918c664b497b238c4f30cb4bfeda7b24b0160891c230907a6fb
|
data/README.md
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
|
3
3
|
The ultimate keep-your-files-organized solution!
|
4
4
|
|
5
|
-
Moves your files into directories that represent tags, these tags are kept organized as a hierarchy according to
|
5
|
+
Moves your files into directories that represent tags, these tags are kept organized as a hierarchy according to usage.
|
6
6
|
|
7
7
|
Tag all your files with multiple tags, and watch them end up organized!
|
8
8
|
|
9
|
-
|
9
|
+
Relies on convention-only, no FUSE, no symbolic links, no hard links. No special tools required.
|
10
10
|
|
11
11
|
## Installation
|
12
12
|
|
@@ -18,17 +18,29 @@ Works using the most basic parts of the filesystem - no FUSE, no symbolic or har
|
|
18
18
|
|
19
19
|
This will move your files and directories into:
|
20
20
|
|
21
|
-
~/t/tag1
|
21
|
+
~/t/tag1-/tag2-/tag3-/
|
22
22
|
|
23
|
-
As you tag more files and folders, it will order the tag directories
|
23
|
+
As you tag more files and folders, it will re-order the tag directories by the most commonly used tags, keeping your files automatically organized for you.
|
24
24
|
|
25
25
|
## Current features
|
26
26
|
|
27
|
-
Default directory is
|
27
|
+
Default root directory for all your tagged files is `~/t/` (not configurable yet)
|
28
28
|
|
29
|
-
|
29
|
+
## Configuration
|
30
30
|
|
31
|
-
|
31
|
+
Configuration file: `~/.tagmv.yml`
|
32
|
+
|
33
|
+
Supports top level tags - so that you can "pin" certain tags to always show as the top level directories within `~/t/`
|
34
|
+
|
35
|
+
## Commands
|
36
|
+
|
37
|
+
`-d, --dry-run` Check to see what gets moved where
|
38
|
+
|
39
|
+
`-r, --reorder` **[default]** Move everything in `~/t/` into order of tag usage (example: `tagmv -r`)
|
40
|
+
|
41
|
+
`-s, --skip-reorder` Skip reorder (e.g. you are editing tagged files and don't want them moved around yet)
|
42
|
+
|
43
|
+
`-t, --tags *tags` Tags for your files or directories, as many as you want
|
32
44
|
|
33
45
|
|
34
46
|
## Upcoming features
|
data/lib/tagmv.rb
CHANGED
data/lib/tagmv/command_line.rb
CHANGED
@@ -5,26 +5,50 @@ module Tagmv
|
|
5
5
|
extend self
|
6
6
|
|
7
7
|
Choice.options do
|
8
|
-
header "
|
8
|
+
header "A Directory-based Tagging Organizer"
|
9
|
+
header ""
|
10
|
+
header "Usage:"
|
11
|
+
header " $ tagmv file1 file2 directory1 directory2 -t tag1 tag2 tag3 -d"
|
9
12
|
header ""
|
10
13
|
header "Options:"
|
11
14
|
|
12
15
|
footer ""
|
13
|
-
footer "tagmv by
|
16
|
+
footer "tagmv by #{Tagmv::AUTHORS.join(', ')} (#{Tagmv::HOMEPAGE})"
|
17
|
+
|
18
|
+
option :dry_run do
|
19
|
+
short '-d'
|
20
|
+
long '--dry-run'
|
21
|
+
desc 'Check to see what gets moved where'
|
22
|
+
end
|
23
|
+
|
24
|
+
option :reorder do
|
25
|
+
short '-r'
|
26
|
+
long '--reorder'
|
27
|
+
desc 'Move all tagged files & directories into order of tag usage [default]'
|
28
|
+
end
|
29
|
+
|
30
|
+
option :skip_reorder do
|
31
|
+
short '-s'
|
32
|
+
long '--skip-reorder'
|
33
|
+
desc "Skip reorder (for example: you are editing tagged files and don't want them moved around yet)"
|
34
|
+
end
|
14
35
|
|
15
|
-
option :tags
|
36
|
+
option :tags do
|
16
37
|
short '-t'
|
17
38
|
long '--tags *tags'
|
18
|
-
desc '
|
39
|
+
desc 'Tags for your files or directories, as many as you want'
|
19
40
|
end
|
20
41
|
end
|
21
42
|
|
22
43
|
def parse
|
23
|
-
return Choice.help if Choice.rest.empty?
|
44
|
+
return Choice.help if !Choice.choices[:reorder] && (Choice.choices.empty? || Choice.rest.empty?)
|
24
45
|
|
25
46
|
opts = Hash.new
|
26
|
-
opts[:files]
|
27
|
-
opts[:
|
47
|
+
opts[:files] = Choice.rest
|
48
|
+
opts[:dry_run] = Choice.choices[:dry_run]
|
49
|
+
opts[:tags] = Choice.choices[:tags]
|
50
|
+
opts[:reorder] = Choice.choices[:reorder]
|
51
|
+
opts[:skip_reorder]= Choice.choices[:skip_reorder]
|
28
52
|
opts
|
29
53
|
end
|
30
54
|
end
|
data/lib/tagmv/filesystem.rb
CHANGED
@@ -7,25 +7,42 @@ module Tagmv
|
|
7
7
|
attr_accessor :root
|
8
8
|
end
|
9
9
|
|
10
|
-
attr_reader :tags, :files, :tag_order, :top_level_tags
|
10
|
+
attr_reader :tags, :files, :reorder, :tag_order, :top_level_tags
|
11
11
|
def initialize(opts={})
|
12
|
-
@tags = opts[:tags]
|
13
|
-
@files = opts[:files]
|
12
|
+
@tags = scrub_tags(opts[:tags])
|
13
|
+
@files = opts[:files]
|
14
|
+
@dry_run = opts[:dry_run]
|
15
|
+
@reorder = opts[:reorder]
|
14
16
|
@tag_order = opts[:tag_order]
|
15
17
|
@top_level_tags = opts[:top_level_tags]
|
16
18
|
end
|
17
19
|
|
18
|
-
def
|
19
|
-
# only keep legit file characters & remove trailing periods
|
20
|
-
|
20
|
+
def scrub_tags(tags)
|
21
|
+
# only keep legit file characters & remove trailing periods, remove duplicates after
|
22
|
+
bad_chars = /^[\-]|[^0-9A-Za-z\.\-\_]|[\.]+$/
|
23
|
+
tags.map {|t| t.gsub(bad_chars, '') }.uniq
|
21
24
|
end
|
22
25
|
|
23
|
-
def
|
24
|
-
|
26
|
+
def scrub_files
|
27
|
+
files.select do |file|
|
28
|
+
path = File.expand_path(file)
|
29
|
+
if File.exist?(path)
|
30
|
+
path
|
31
|
+
else
|
32
|
+
puts "tmv: rename #{file} to #{target_dir}/#{File.basename(file)}: #{Errno::ENOENT.exception}"
|
33
|
+
false
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def tags_in_order
|
39
|
+
return tags unless reorder
|
40
|
+
|
41
|
+
(top_level_tags | tag_order) & tags
|
25
42
|
end
|
26
43
|
|
27
44
|
def tag_dirs
|
28
|
-
|
45
|
+
tags_in_order.map {|x| x.gsub(/$/, '-') }
|
29
46
|
end
|
30
47
|
|
31
48
|
def target_dir
|
@@ -33,16 +50,31 @@ module Tagmv
|
|
33
50
|
end
|
34
51
|
|
35
52
|
def prepare_dir
|
36
|
-
|
53
|
+
@@prepare_dir ||= Hash.new do |h, key|
|
54
|
+
h[key] = FileUtils.mkdir_p(key, options)
|
55
|
+
end
|
56
|
+
@@prepare_dir[target_dir]
|
37
57
|
end
|
38
58
|
|
39
59
|
def move_files
|
40
|
-
|
60
|
+
# skip duplicate moves
|
61
|
+
return if reorder && scrub_files.size == 1 && (scrub_files.first.sub(target_dir + '/','') !=~ /\//)
|
62
|
+
|
63
|
+
FileUtils.mv(scrub_files, target_dir, options)
|
41
64
|
rescue ArgumentError
|
42
65
|
end
|
43
66
|
|
44
67
|
def transfer
|
45
68
|
prepare_dir && move_files
|
46
69
|
end
|
70
|
+
|
71
|
+
private
|
72
|
+
def options
|
73
|
+
if @dry_run
|
74
|
+
{noop: true, verbose: true}
|
75
|
+
else
|
76
|
+
{}
|
77
|
+
end
|
78
|
+
end
|
47
79
|
end
|
48
80
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Tagmv
|
2
|
+
class Options
|
3
|
+
attr_accessor :additions
|
4
|
+
def initialize(opts = {})
|
5
|
+
@additions = opts
|
6
|
+
end
|
7
|
+
|
8
|
+
def input
|
9
|
+
Tagmv::CommandLine.parse || {}
|
10
|
+
end
|
11
|
+
|
12
|
+
def load_config
|
13
|
+
Tagmv::Config.new.load
|
14
|
+
end
|
15
|
+
|
16
|
+
def options
|
17
|
+
return if @options
|
18
|
+
|
19
|
+
@options = load_config
|
20
|
+
@options.merge(additions)
|
21
|
+
@options.merge(input)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/tagmv/prune_path.rb
CHANGED
data/lib/tagmv/runner.rb
CHANGED
@@ -2,27 +2,32 @@ module Tagmv
|
|
2
2
|
module Runner
|
3
3
|
extend self
|
4
4
|
|
5
|
-
def
|
6
|
-
@tree ||= Tagmv::Tree.scan_tree_entries
|
7
|
-
end
|
8
|
-
|
9
|
-
def update_tree
|
10
|
-
opts = Tagmv::CommandLine.parse
|
11
|
-
tree.with(opts)
|
12
|
-
end
|
13
|
-
|
14
|
-
def move_files
|
5
|
+
def reorder_files
|
15
6
|
tree.entries.each do |entry|
|
16
|
-
|
17
|
-
|
7
|
+
fs_options = options.merge(tags: entry.tags, files: entry.files, tag_order: tree.tag_order, reorder: true)
|
8
|
+
Tagmv::Filesystem.new(fs_options).transfer
|
18
9
|
end
|
19
10
|
end
|
20
11
|
|
12
|
+
def move_new_files
|
13
|
+
tree.entries << Entry.new(options)
|
14
|
+
fs_options = options.merge(tag_order: tree.tag_order, reorder: false)
|
15
|
+
Tagmv::Filesystem.new(fs_options).transfer
|
16
|
+
end
|
17
|
+
|
21
18
|
def run
|
22
|
-
|
23
|
-
|
24
|
-
move_files
|
19
|
+
reorder_files unless options[:skip_reorder]
|
20
|
+
move_new_files unless options[:files].empty?
|
25
21
|
Tagmv::PrunePath.prune_tag_dirs
|
26
22
|
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def tree
|
26
|
+
@tree ||= Tagmv::Tree.scan_tree_entries
|
27
|
+
end
|
28
|
+
|
29
|
+
def options
|
30
|
+
@options ||= Tagmv::Options.new.options
|
31
|
+
end
|
27
32
|
end
|
28
33
|
end
|
data/lib/tagmv/tree.rb
CHANGED
@@ -22,14 +22,20 @@ module Tagmv
|
|
22
22
|
|
23
23
|
def self.false_tag_regex
|
24
24
|
# detect when there's a false tag i.e. tag2. in path/to/tag1./not_a_tag/tag2./
|
25
|
-
|
25
|
+
/\/.+-\/[^-]+\/.+-/
|
26
|
+
#/\/(.*[^-]\/|[^\/]{0,1}?-)/
|
26
27
|
end
|
28
|
+
|
27
29
|
def self.tags_in_path_regex
|
28
|
-
|
30
|
+
/[^(\.|\-)\/]\K.+?(?=\-\/)/
|
29
31
|
end
|
30
32
|
|
31
33
|
def self.path_has_file_regex
|
32
|
-
/#{tags_in_path_regex}.*[
|
34
|
+
/#{tags_in_path_regex}.*[^\-]$/
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.file_in_tag_dir
|
38
|
+
/.+(\-\/[^\/]+)$/
|
33
39
|
end
|
34
40
|
|
35
41
|
def self.tags(file)
|
@@ -37,22 +43,12 @@ module Tagmv
|
|
37
43
|
file[Filesystem.root.length..-1].scan(tags_in_path_regex).reject {|x| x =~ /\//}
|
38
44
|
end
|
39
45
|
|
40
|
-
def self.empty_dirs
|
41
|
-
files = Find.find(Filesystem.root).select {|x| x =~ path_has_file_regex }
|
42
|
-
tree = Tree.new
|
43
|
-
files.map do |file|
|
44
|
-
next if file =~ false_tag_regex
|
45
|
-
tree.with(files: [file], tags: tags(file))
|
46
|
-
end
|
47
|
-
tree
|
48
|
-
end
|
49
|
-
|
50
46
|
def self.scan_tree_entries
|
51
|
-
files = Find.find(Filesystem.root).select {|x| x =~ path_has_file_regex }
|
47
|
+
files = Find.find(Filesystem.root).select {|x| x =~ path_has_file_regex }.select {|x| x =~ file_in_tag_dir }
|
52
48
|
tree = Tree.new
|
53
|
-
files.
|
54
|
-
next if file =~
|
55
|
-
tree.
|
49
|
+
files.each do |file|
|
50
|
+
next if file =~ false_tag_regex # break when /dev./oh/blah./foo
|
51
|
+
tree.entries << Entry.new(files: [file], tags: tags(file))
|
56
52
|
end
|
57
53
|
tree
|
58
54
|
end
|
@@ -62,12 +58,15 @@ module Tagmv
|
|
62
58
|
# {"dev."=>{"book."=>{"javascript."=>{"Secrets_of_the_Javascript_Ninja.pdf"=>{}}, "ruby."=>{"rails_antipatterns.pdf"=>{}}}, "ruby."=>{"oh"=>{}, "tagmv"=>{}}}}
|
63
59
|
def self.scan_tree_hash
|
64
60
|
Dir.chdir(Filesystem.root)
|
65
|
-
|
61
|
+
|
62
|
+
# reject /dev-/-, /dev-/j-/ and /dev-/notag/tag-
|
63
|
+
paths = Dir["**/*"].delete_if {|x| /\/(.*[^-]\/|[^\/]{0,1}?-)/ =~ x}
|
64
|
+
paths.inject({}) do |hash,path|
|
66
65
|
tree = hash
|
67
66
|
path.split("/").each do |n|
|
68
67
|
tree[n] ||= {}
|
69
68
|
tree = tree[n]
|
70
|
-
break if n[-1] != "
|
69
|
+
break if n[-1] != "-"
|
71
70
|
end
|
72
71
|
hash
|
73
72
|
end
|
data/lib/tagmv/version.rb
CHANGED
data/tagmv.gemspec
CHANGED
@@ -6,11 +6,11 @@ require 'tagmv/version'
|
|
6
6
|
Gem::Specification.new do |spec|
|
7
7
|
spec.name = "tagmv"
|
8
8
|
spec.version = Tagmv::VERSION
|
9
|
-
spec.authors =
|
9
|
+
spec.authors = Tagmv::AUTHORS
|
10
10
|
spec.email = ["james.robey+tagmv@gmail.com"]
|
11
11
|
spec.summary = %q{Tag your files by moving them into a tree-like tag structure}
|
12
12
|
spec.description = %q{Moves your files into directories that represent tags, these tags are kept organized as a hierarchy according to tag counts}
|
13
|
-
spec.homepage =
|
13
|
+
spec.homepage = Tagmv::HOMEPAGE
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tagmv
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Robey
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2019-06-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -114,12 +114,13 @@ files:
|
|
114
114
|
- lib/tagmv/config.rb
|
115
115
|
- lib/tagmv/entry.rb
|
116
116
|
- lib/tagmv/filesystem.rb
|
117
|
+
- lib/tagmv/options.rb
|
117
118
|
- lib/tagmv/prune_path.rb
|
118
119
|
- lib/tagmv/runner.rb
|
119
120
|
- lib/tagmv/tree.rb
|
120
121
|
- lib/tagmv/version.rb
|
121
122
|
- tagmv.gemspec
|
122
|
-
homepage: https://github.com/foucist/tagmv
|
123
|
+
homepage: https://github.com/foucist/tagmv
|
123
124
|
licenses:
|
124
125
|
- MIT
|
125
126
|
metadata: {}
|
@@ -139,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
139
140
|
version: '0'
|
140
141
|
requirements: []
|
141
142
|
rubyforge_project:
|
142
|
-
rubygems_version: 2.
|
143
|
+
rubygems_version: 2.7.7
|
143
144
|
signing_key:
|
144
145
|
specification_version: 4
|
145
146
|
summary: Tag your files by moving them into a tree-like tag structure
|