tagmv 0.0.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 +9 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/README.md +28 -0
- data/Rakefile +10 -0
- data/bin/tagmv +11 -0
- data/lib/tagmv/command_line.rb +54 -0
- data/lib/tagmv/config.rb +27 -0
- data/lib/tagmv/entry.rb +9 -0
- data/lib/tagmv/filesystem.rb +48 -0
- data/lib/tagmv/prune_path.rb +27 -0
- data/lib/tagmv/runner.rb +28 -0
- data/lib/tagmv/tree.rb +77 -0
- data/lib/tagmv/version.rb +3 -0
- data/lib/tagmv.rb +7 -0
- data/tagmv.gemspec +33 -0
- metadata +146 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 8f1e38a10e9ca3a7369ab50f4509529016fd430a
|
4
|
+
data.tar.gz: 4beb2cf52b95d5f11eda2277a91ad176d759ce23
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: cd49965ab7495f1eec74d4fce995f50306f8a062b861b4e99f2efeddace1efcf71fbc77cfeb8163b9cc485c08b89a465b465426ae02a0764eb5f97fb316fefee
|
7
|
+
data.tar.gz: e29488194f1d0ea19666509ca25e6daa9006068baad3852d546f1a1ec5de187050ce73af94eb05936c8b82b06408b0cb62045d7c3009e90f8d2a87fb77e9a294
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# Tagmv
|
2
|
+
|
3
|
+
File tagging by moving files into tag-based directories.
|
4
|
+
|
5
|
+
The directories that represent tags are organized hierarchically (either by # of items that have the tag or some other method).
|
6
|
+
|
7
|
+
- Organizes using the plain file system
|
8
|
+
- Extremely flexible (no tag limit, no tag naming restrictions, hierarchical tags, no file naming restrictions, can tag all files and directories).
|
9
|
+
- Transparent to other applications, since it works directly with the file system.
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
$ gem install tagmv
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
$ tag <files/directories> -t <tags>
|
18
|
+
|
19
|
+
## Development
|
20
|
+
|
21
|
+
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.
|
22
|
+
|
23
|
+
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).
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/foucist/tagmv.
|
28
|
+
|
data/Rakefile
ADDED
data/bin/tagmv
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'choice'
|
2
|
+
|
3
|
+
module Tagmv
|
4
|
+
module CommandLine
|
5
|
+
extend self
|
6
|
+
|
7
|
+
Choice.options do
|
8
|
+
header "\033[1m\033[32mtagmv\033[0m\033[0m: A Directory-based Tagging Organizer"
|
9
|
+
header ""
|
10
|
+
header "Options:"
|
11
|
+
|
12
|
+
footer ""
|
13
|
+
footer "tagmv by James Robey (http://jamesrobey.com/tagmv/)"
|
14
|
+
|
15
|
+
option :tags, :required => true do
|
16
|
+
short '-t'
|
17
|
+
long '--tags *tags'
|
18
|
+
desc 'The hostname or ip of the host to bind to (default 127.0.0.1)'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def parse
|
23
|
+
return Choice.help if Choice.rest.empty?
|
24
|
+
|
25
|
+
{files: Choice.rest, tags: Choice.choices[:tags]}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
#module Tagmv
|
31
|
+
# class CommandLine
|
32
|
+
# def self.parse
|
33
|
+
# Choice.options do
|
34
|
+
# header "\033[1m\033[32mtagmv\033[0m\033[0m: A Directory-based Tagging Organizer"
|
35
|
+
# header ""
|
36
|
+
# header "Options:"
|
37
|
+
#
|
38
|
+
# footer ""
|
39
|
+
# footer "tagmv by James Robey (http://jamesrobey.com/tagmv/)"
|
40
|
+
#
|
41
|
+
# option :tags, :required => true do
|
42
|
+
# short '-t'
|
43
|
+
# long '--tags *tags'
|
44
|
+
# desc 'The hostname or ip of the host to bind to (default 127.0.0.1)'
|
45
|
+
# end
|
46
|
+
# end
|
47
|
+
#
|
48
|
+
# return Choice.help if Choice.rest.empty?
|
49
|
+
#
|
50
|
+
# {files: Choice.rest, tags: Choice.choices[:tags]}
|
51
|
+
# end
|
52
|
+
# end
|
53
|
+
#end
|
54
|
+
|
data/lib/tagmv/config.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Tagmv
|
4
|
+
class Config
|
5
|
+
def initialize(options = {})
|
6
|
+
@path = options[:path] || File.expand_path("~/.tagmv.yml")
|
7
|
+
end
|
8
|
+
|
9
|
+
def defaults
|
10
|
+
{top_level_tags: ["blog", "media"]}
|
11
|
+
end
|
12
|
+
|
13
|
+
def load
|
14
|
+
if File.file?(@path)
|
15
|
+
YAML.load(File.open(@path).read)
|
16
|
+
else
|
17
|
+
config = defaults
|
18
|
+
save(config)
|
19
|
+
config
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def save(config)
|
24
|
+
File.open(@path, 'w') { |f| f.write(config.to_yaml) }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/tagmv/entry.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Tagmv
|
4
|
+
class Filesystem
|
5
|
+
@root = File.expand_path('~/t')
|
6
|
+
class << self
|
7
|
+
attr_accessor :root
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_reader :tags, :files, :tag_order, :top_level_tags
|
11
|
+
def initialize(opts={})
|
12
|
+
@tags = opts[:tags].map {|t| scrub_tag(t) }
|
13
|
+
@files = opts[:files].map {|f| File.expand_path(f) }.select {|f| File.exist?(f) }
|
14
|
+
@tag_order = opts[:tag_order]
|
15
|
+
@top_level_tags = opts[:top_level_tags]
|
16
|
+
end
|
17
|
+
|
18
|
+
def scrub_tag(tag)
|
19
|
+
# only keep legit file characters & remove trailing periods
|
20
|
+
tag.gsub(/[^0-9A-Za-z\.\-\_]|[\.]+$/, '')
|
21
|
+
end
|
22
|
+
|
23
|
+
def tag_order
|
24
|
+
top_level_tags | @tag_order
|
25
|
+
end
|
26
|
+
|
27
|
+
def tag_dirs
|
28
|
+
(tag_order & tags).map {|x| x.gsub(/$/, '.') }
|
29
|
+
end
|
30
|
+
|
31
|
+
def target_dir
|
32
|
+
File.join(Filesystem.root, *tag_dirs)
|
33
|
+
end
|
34
|
+
|
35
|
+
def prepare_dir
|
36
|
+
FileUtils.mkdir_p(target_dir)
|
37
|
+
end
|
38
|
+
|
39
|
+
def move_files
|
40
|
+
FileUtils.mv(files, target_dir)
|
41
|
+
rescue ArgumentError
|
42
|
+
end
|
43
|
+
|
44
|
+
def transfer
|
45
|
+
prepare_dir && move_files
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Tagmv
|
2
|
+
class PrunePath
|
3
|
+
attr_reader :path
|
4
|
+
def initialize(path)
|
5
|
+
@path = path
|
6
|
+
end
|
7
|
+
|
8
|
+
def tag_dir?
|
9
|
+
path =~ /\.$/ && path !~ Tagmv::Tree.false_tag_regex
|
10
|
+
end
|
11
|
+
|
12
|
+
def empty_dir?
|
13
|
+
FileTest.directory?(path) && Dir.entries(path) == ['.', '..']
|
14
|
+
end
|
15
|
+
|
16
|
+
def rmdir
|
17
|
+
Dir.rmdir(path) if tag_dir? && empty_dir?
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.prune_tag_dirs
|
21
|
+
Find.find(Tagmv::Filesystem.root).reverse_each do |path|
|
22
|
+
Tagmv::PrunePath.new(path).rmdir
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
data/lib/tagmv/runner.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
module Tagmv
|
2
|
+
module Runner
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def tree
|
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
|
15
|
+
tree.entries.each do |entry|
|
16
|
+
tfs = Tagmv::Filesystem.new(tags: entry.tags, files: entry.files, tag_order: tree.tag_order, top_level_tags: @opts[:top_level_tags])
|
17
|
+
tfs.transfer
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def run
|
22
|
+
@opts = Tagmv::Config.new.load
|
23
|
+
update_tree
|
24
|
+
move_files
|
25
|
+
Tagmv::PrunePath.prune_tag_dirs
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/tagmv/tree.rb
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
require 'find'
|
2
|
+
|
3
|
+
module Tagmv
|
4
|
+
class Tree
|
5
|
+
attr_accessor :entries
|
6
|
+
def initialize(entries = [])
|
7
|
+
@entries = entries
|
8
|
+
end
|
9
|
+
|
10
|
+
def with(opts)
|
11
|
+
entries << Entry.new(opts)
|
12
|
+
end
|
13
|
+
|
14
|
+
def tag_counts
|
15
|
+
entries.map {|x| x.tags }.flatten.each_with_object(Hash.new(0)) { |word,counts| counts[word] += 1 }
|
16
|
+
end
|
17
|
+
|
18
|
+
def tag_order
|
19
|
+
tag_counts.to_a.sort_by{|x| [-x.last, x.first]}.map {|x| x.first }
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def self.false_tag_regex
|
24
|
+
# detect when there's a false tag i.e. tag2. in path/to/tag1./not_a_tag/tag2./
|
25
|
+
/\/.+\.\/[^.]+\/.+\./
|
26
|
+
end
|
27
|
+
def self.tags_in_path_regex
|
28
|
+
/\/\K.+?(?=\.\/)/
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.path_has_file_regex
|
32
|
+
/#{tags_in_path_regex}.*[^\.]$/
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.tags(file)
|
36
|
+
raise StandardError.new('Invalid file path given') unless file[/^#{Filesystem.root}/]
|
37
|
+
file[Filesystem.root.length..-1].scan(tags_in_path_regex).reject {|x| x =~ /\//}
|
38
|
+
end
|
39
|
+
|
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
|
+
def self.scan_tree_entries
|
51
|
+
files = Find.find(Filesystem.root).select {|x| x =~ path_has_file_regex }
|
52
|
+
tree = Tree.new
|
53
|
+
files.map do |file|
|
54
|
+
next if file =~ /\/.+\.\/[^.]+\/.+\./ # break when /dev./oh/blah./foo
|
55
|
+
tree.with(files: [file], tags: tags(file))
|
56
|
+
end
|
57
|
+
tree
|
58
|
+
end
|
59
|
+
|
60
|
+
# only gets existing tree, doesn't build an accurate tree based on all the tag counts etc..
|
61
|
+
# Find.find('.').select {|x| x =~ /([^\/]+\/)*([^\/]+\/)*\.\/.*/}
|
62
|
+
# {"dev."=>{"book."=>{"javascript."=>{"Secrets_of_the_Javascript_Ninja.pdf"=>{}}, "ruby."=>{"rails_antipatterns.pdf"=>{}}}, "ruby."=>{"oh"=>{}, "tagmv"=>{}}}}
|
63
|
+
def self.scan_tree_hash
|
64
|
+
Dir.chdir(Filesystem.root)
|
65
|
+
Dir["**/**./*"].inject({}) do |hash,path|
|
66
|
+
tree = hash
|
67
|
+
path.split("/").each do |n|
|
68
|
+
tree[n] ||= {}
|
69
|
+
tree = tree[n]
|
70
|
+
break if n[-1] != "."
|
71
|
+
end
|
72
|
+
hash
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
data/lib/tagmv.rb
ADDED
data/tagmv.gemspec
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'tagmv/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "tagmv"
|
8
|
+
spec.version = Tagmv::VERSION
|
9
|
+
spec.authors = ["James Robey"]
|
10
|
+
spec.email = ["james.robey+tagmv@gmail.com"]
|
11
|
+
spec.summary = %q{Tag your files by moving them into a tree-like tag structure}
|
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 = "http://jamesrobey.com/tagmv/" #"https://github.com/foucist/tagmv/"
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
#spec.bindir = "exe"
|
18
|
+
#spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.bindir = "bin"
|
22
|
+
spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
|
23
|
+
spec.test_files = spec.files.grep(/^(test|spec|features)\//)
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.11"
|
26
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
27
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
28
|
+
spec.add_development_dependency "simplecov", "~> 0"
|
29
|
+
spec.add_development_dependency "pry", "~> 0"
|
30
|
+
|
31
|
+
spec.add_dependency "choice", "0.2.0"
|
32
|
+
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,146 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tagmv
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Robey
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-07-22 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: '1.11'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.11'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: simplecov
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: pry
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: choice
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '='
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.2.0
|
90
|
+
type: :runtime
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 0.2.0
|
97
|
+
description: Moves your files into directories that represent tags, these tags are
|
98
|
+
kept organized as a hierarchy according to tag counts
|
99
|
+
email:
|
100
|
+
- james.robey+tagmv@gmail.com
|
101
|
+
executables:
|
102
|
+
- tagmv
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- ".gitignore"
|
107
|
+
- ".travis.yml"
|
108
|
+
- Gemfile
|
109
|
+
- README.md
|
110
|
+
- Rakefile
|
111
|
+
- bin/tagmv
|
112
|
+
- lib/tagmv.rb
|
113
|
+
- lib/tagmv/command_line.rb
|
114
|
+
- lib/tagmv/config.rb
|
115
|
+
- lib/tagmv/entry.rb
|
116
|
+
- lib/tagmv/filesystem.rb
|
117
|
+
- lib/tagmv/prune_path.rb
|
118
|
+
- lib/tagmv/runner.rb
|
119
|
+
- lib/tagmv/tree.rb
|
120
|
+
- lib/tagmv/version.rb
|
121
|
+
- tagmv.gemspec
|
122
|
+
homepage: http://jamesrobey.com/tagmv/
|
123
|
+
licenses:
|
124
|
+
- MIT
|
125
|
+
metadata: {}
|
126
|
+
post_install_message:
|
127
|
+
rdoc_options: []
|
128
|
+
require_paths:
|
129
|
+
- lib
|
130
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
131
|
+
requirements:
|
132
|
+
- - ">="
|
133
|
+
- !ruby/object:Gem::Version
|
134
|
+
version: '0'
|
135
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
136
|
+
requirements:
|
137
|
+
- - ">="
|
138
|
+
- !ruby/object:Gem::Version
|
139
|
+
version: '0'
|
140
|
+
requirements: []
|
141
|
+
rubyforge_project:
|
142
|
+
rubygems_version: 2.4.8
|
143
|
+
signing_key:
|
144
|
+
specification_version: 4
|
145
|
+
summary: Tag your files by moving them into a tree-like tag structure
|
146
|
+
test_files: []
|