fmanager 0.0.0 → 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -0
- data/bin/fm +25 -1
- data/history.txt +2 -2
- data/lib/fmanager.rb +1 -0
- data/lib/fmanager/findex.rb +142 -0
- data/lib/fmanager/fmatch.rb +11 -0
- data/lib/fmanager/fmeta.rb +15 -0
- data/lib/fmanager/version.rb +1 -1
- data/test/test_cmd_index.rb +147 -0
- data/test/test_fmatch_hasfolder.rb +46 -0
- metadata +8 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5e243865920465668fdca0f5965ee8d6be9b961f
|
4
|
+
data.tar.gz: 7dff3316ad7ee02845121fef9d024d3972e82cab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 50fd935098b10669bd6fa9982891eb423dff60063b72358887e9253dd063febbebf41471f3a7db37cf56c1611873ed3a9bb65832002b78811679bd0dba232d99
|
7
|
+
data.tar.gz: 3981c7b35cfadf325c46ab2370ec0bf84568fd29cdf370d37ae237a3ace4191b35e3505e24032547515b3c83dc1f3c7a37a3ca0d9ecc88dd4b3bfb1ec49a7e2b
|
data/Rakefile
CHANGED
data/bin/fm
CHANGED
@@ -3,4 +3,28 @@
|
|
3
3
|
require 'fmanager'
|
4
4
|
|
5
5
|
|
6
|
-
|
6
|
+
def usage
|
7
|
+
<<EOS
|
8
|
+
SGC File Manager #{FManager::VERSION}
|
9
|
+
|
10
|
+
usage: fm <command> [args]
|
11
|
+
|
12
|
+
Available fm commands:
|
13
|
+
index Index files in a folder recursively.
|
14
|
+
EOS
|
15
|
+
end
|
16
|
+
|
17
|
+
|
18
|
+
if ARGV.size == 0
|
19
|
+
$stderr << usage
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
|
23
|
+
cmd = ARGV.shift
|
24
|
+
case cmd
|
25
|
+
when "index"
|
26
|
+
FManager.cmd_index(ARGV.clone)
|
27
|
+
else
|
28
|
+
$stderr << "fm: '#{cmd}' is not a fm command.\n"
|
29
|
+
exit
|
30
|
+
end
|
data/history.txt
CHANGED
data/lib/fmanager.rb
CHANGED
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'digest/md5'
|
2
|
+
require 'optparse'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require 'fmanager/fmatch'
|
6
|
+
require 'fmanager/fmeta'
|
7
|
+
|
8
|
+
|
9
|
+
DBFILE = "fmetadata.db"
|
10
|
+
|
11
|
+
|
12
|
+
module FManager
|
13
|
+
|
14
|
+
def cmd_index(argv)
|
15
|
+
|
16
|
+
banner = "usage: fm index [options] <PATH>"
|
17
|
+
options = {
|
18
|
+
dbfile: DBFILE,
|
19
|
+
updatedb: true,
|
20
|
+
}
|
21
|
+
OptionParser.new do |opts|
|
22
|
+
opts.banner = banner
|
23
|
+
|
24
|
+
opts.on "-d", "--db-file PATH", "Specify the DB PATH to use." do |path|
|
25
|
+
options[:dbfile] = path
|
26
|
+
end
|
27
|
+
|
28
|
+
opts.on "-n", "--no-update-db", "Do not update the DB." do
|
29
|
+
options[:updatedb] = false
|
30
|
+
end
|
31
|
+
|
32
|
+
opts.on "-u", "--update-db", "Update the DB when applicable." do
|
33
|
+
options[:updatedb] = true
|
34
|
+
end
|
35
|
+
|
36
|
+
opts.on "-h", "--help", "Show this message." do
|
37
|
+
puts opts
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
end.parse!(argv)
|
41
|
+
|
42
|
+
if argv.size != 1
|
43
|
+
$stderr << "#{banner}\n"
|
44
|
+
exit
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
puts "Loading DB #{options[:dbfile]} ..."
|
49
|
+
t0 = Time.now
|
50
|
+
h = {}
|
51
|
+
needupdate = true
|
52
|
+
if File.exists?(options[:dbfile])
|
53
|
+
obj = YAML::load(File.read(options[:dbfile]))
|
54
|
+
if obj.is_a?(Hash)
|
55
|
+
h = obj
|
56
|
+
needupdate = false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
t1 = Time.now
|
60
|
+
puts "Loaded DB in #{(t1-t0).round(2)}s."
|
61
|
+
|
62
|
+
indexpath = argv[0]
|
63
|
+
nnewfiles = 0
|
64
|
+
nskipfiles = 0
|
65
|
+
ndupfiles = 0
|
66
|
+
nfailfiles = 0
|
67
|
+
puts "Indexing #{indexpath} ..."
|
68
|
+
t0 = Time.now
|
69
|
+
|
70
|
+
for fpath in Dir.glob("#{indexpath}/**/*", File::FNM_DOTMATCH).select { |e| e.force_encoding("binary"); File.ftype(e) == "file" && has_folder?(".git", e) == false && has_folder?(".hg", e) == false }
|
71
|
+
begin
|
72
|
+
fpath = File.realpath(fpath).force_encoding("binary")
|
73
|
+
fsize = File.size(fpath)
|
74
|
+
|
75
|
+
if fsize == 0
|
76
|
+
nskipfiles += 1
|
77
|
+
puts "[SKIP]: #{fpath}"
|
78
|
+
next
|
79
|
+
end
|
80
|
+
|
81
|
+
# Lazy index the file.
|
82
|
+
if h[fsize].nil?
|
83
|
+
h[fsize] = fpath
|
84
|
+
needupdate = true
|
85
|
+
nnewfiles += 1
|
86
|
+
puts "[NEW]: #{fpath} nil"
|
87
|
+
else
|
88
|
+
if h[fsize].is_a?(String)
|
89
|
+
p = h[fsize]
|
90
|
+
if p == fpath
|
91
|
+
ndupfiles += 1
|
92
|
+
puts "[DUP]: #{fpath}"
|
93
|
+
puts " #{p}"
|
94
|
+
next
|
95
|
+
end
|
96
|
+
|
97
|
+
d = Digest::MD5.hexdigest(File.read(p))
|
98
|
+
h[fsize] = { d => FileMeta.new(fsize, p, d) }
|
99
|
+
needupdate = true
|
100
|
+
end
|
101
|
+
digest = Digest::MD5.hexdigest(File.read(fpath))
|
102
|
+
if h[fsize][digest].nil?
|
103
|
+
h[fsize][digest] = FileMeta.new(fsize, fpath, digest)
|
104
|
+
needupdate = true
|
105
|
+
nnewfiles += 1
|
106
|
+
puts "[NEW]: #{fpath} #{digest}"
|
107
|
+
else
|
108
|
+
ndupfiles += 1
|
109
|
+
puts "[DUP]: #{fpath}"
|
110
|
+
puts " #{h[fsize][digest].path}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
rescue StandardError => e
|
114
|
+
nfailfiles += 1
|
115
|
+
puts "[FAIL]: file=\"#{fpath}\" #{e.message}"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
t1 = Time.now
|
119
|
+
puts "Indexing completed in #{(t1-t0).round(2)}s."
|
120
|
+
|
121
|
+
puts "Indexed:"
|
122
|
+
puts " NEW: #{nnewfiles}"
|
123
|
+
puts " DUP: #{ndupfiles}"
|
124
|
+
puts " SKIP: #{nskipfiles}"
|
125
|
+
puts " FAIL: #{nfailfiles}"
|
126
|
+
|
127
|
+
if needupdate && options[:updatedb]
|
128
|
+
puts "Updating DB #{options[:dbfile]} ..."
|
129
|
+
t0 = Time.now
|
130
|
+
File.write(options[:dbfile], YAML::dump(h))
|
131
|
+
t1 = Time.now
|
132
|
+
puts "Updated DB in #{(t1-t0).round(2)}s."
|
133
|
+
else
|
134
|
+
puts "Skip updating DB."
|
135
|
+
end
|
136
|
+
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
module_function :cmd_index
|
141
|
+
|
142
|
+
end
|
data/lib/fmanager/version.rb
CHANGED
@@ -0,0 +1,147 @@
|
|
1
|
+
require 'tempfile'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
require 'fmanager'
|
6
|
+
|
7
|
+
|
8
|
+
RB = "ruby -Ilib"
|
9
|
+
FM = "bin/fm"
|
10
|
+
DATA = "test/data"
|
11
|
+
NEWDATA = "test/newdata"
|
12
|
+
|
13
|
+
|
14
|
+
class TestCmdIndex < Test::Unit::TestCase
|
15
|
+
|
16
|
+
include FManager
|
17
|
+
|
18
|
+
|
19
|
+
def setup
|
20
|
+
@stdout = Tempfile.new("test.out")
|
21
|
+
@stderr = Tempfile.new("test.err")
|
22
|
+
@stdout.close
|
23
|
+
@stderr.close
|
24
|
+
end
|
25
|
+
|
26
|
+
def teardown
|
27
|
+
@stdout.unlink
|
28
|
+
@stderr.unlink
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def test_no_arguments
|
33
|
+
assert( execute_fm_index() )
|
34
|
+
assert_match(/usage: fm index/, File.read(@stderr.path))
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def test_help_option
|
39
|
+
assert( execute_fm_index("-h") )
|
40
|
+
assert_match(/Show this message./, File.read(@stdout.path))
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def test_default_db
|
45
|
+
fname = "fmetadata.db"
|
46
|
+
assert( File.exists?(fname) == false, "File #{fname} already exists. Test is unable to continue." )
|
47
|
+
assert( execute_fm_index("#{DATA}") )
|
48
|
+
assert_test_db_content(fname)
|
49
|
+
File.unlink(fname)
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
def test_db_file_option
|
54
|
+
dbfile = Tempfile.new("test.db")
|
55
|
+
dbfile.close
|
56
|
+
fname = dbfile.path
|
57
|
+
assert( execute_fm_index("-d #{fname} #{DATA}") )
|
58
|
+
assert_test_db_content(fname)
|
59
|
+
dbfile.unlink
|
60
|
+
end
|
61
|
+
|
62
|
+
|
63
|
+
def test_no_update_db_option
|
64
|
+
data = "hello world"
|
65
|
+
dbfile = Tempfile.new("test.db")
|
66
|
+
dbfile.write(data)
|
67
|
+
dbfile.close
|
68
|
+
fname = dbfile.path
|
69
|
+
assert( execute_fm_index("-n -d #{fname} #{DATA}") )
|
70
|
+
assert_match( /Indexing completed/, File.read(@stdout.path) )
|
71
|
+
fdata = File.read(fname)
|
72
|
+
assert_equal(data, fdata)
|
73
|
+
dbfile.unlink
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def test_update_db_option
|
78
|
+
dbfile = Tempfile.new("test.db")
|
79
|
+
dbfile.close
|
80
|
+
fname = dbfile.path
|
81
|
+
assert( execute_fm_index("-u -d #{fname} #{DATA}") )
|
82
|
+
assert_test_db_content(fname)
|
83
|
+
dbfile.unlink
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
def test_existing_db
|
88
|
+
dbfile = Tempfile.new("test.db")
|
89
|
+
dbfile.close
|
90
|
+
fname = dbfile.path
|
91
|
+
assert( execute_fm_index("-d #{fname} #{DATA}") )
|
92
|
+
assert( execute_fm_index("-d #{fname} #{NEWDATA}") )
|
93
|
+
h = YAML::load( File.read(fname) )
|
94
|
+
|
95
|
+
# file0.txt
|
96
|
+
fsize = 13
|
97
|
+
digest = "8b362a13f2467448f69c826a7450cf3b"
|
98
|
+
assert(f0 = h[fsize][digest])
|
99
|
+
assert_equal(fsize, f0.fsize)
|
100
|
+
assert_equal(digest, f0.digest)
|
101
|
+
assert_match(/newdata\/file0.txt/, f0.path)
|
102
|
+
dbfile.unlink
|
103
|
+
|
104
|
+
assert_equal(4, h.size)
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
def execute_fm_index(argv = "")
|
109
|
+
system("#{RB} #{FM} index #{argv} > #{@stdout.path} 2> #{@stderr.path}")
|
110
|
+
end
|
111
|
+
|
112
|
+
def assert_test_db_content(path)
|
113
|
+
assert( File.exists?(path) )
|
114
|
+
h = YAML::load( File.read(path) )
|
115
|
+
|
116
|
+
# data0.txt and data2.txt
|
117
|
+
fsize = 53
|
118
|
+
digest = "be4af635154944fdf305dcb91a9613ba"
|
119
|
+
assert(f0 = h[fsize][digest])
|
120
|
+
assert_equal(fsize, f0.fsize)
|
121
|
+
assert_equal(digest, f0.digest)
|
122
|
+
assert_match(/data\/data0.txt|data\/data2.txt/, f0.path)
|
123
|
+
|
124
|
+
# data1.txt
|
125
|
+
fsize = 13
|
126
|
+
digest = "2632561a9eb94e0d05e3e032cc5adeed"
|
127
|
+
assert(f1 = h[fsize][digest])
|
128
|
+
assert_equal(fsize, f1.fsize)
|
129
|
+
assert_equal(digest, f1.digest)
|
130
|
+
assert_match(/data\/data1.txt/, f1.path)
|
131
|
+
|
132
|
+
#data3.txt
|
133
|
+
fsize = 13
|
134
|
+
digest = "412cb2c7b6a2cd60c55e3548066cbdeb"
|
135
|
+
assert(f3 = h[fsize][digest])
|
136
|
+
assert_equal(fsize, f3.fsize)
|
137
|
+
assert_equal(digest, f3.digest)
|
138
|
+
assert_match(/data\/data3.txt/, f3.path)
|
139
|
+
|
140
|
+
# data4.txt
|
141
|
+
fsize = 77
|
142
|
+
assert_match(/data\/data4.txt/, h[fsize])
|
143
|
+
|
144
|
+
assert_equal(3, h.size)
|
145
|
+
end
|
146
|
+
|
147
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
require 'fmanager'
|
4
|
+
|
5
|
+
|
6
|
+
class TestFMatchHasFolder < Test::Unit::TestCase
|
7
|
+
|
8
|
+
include FManager
|
9
|
+
|
10
|
+
|
11
|
+
def test_absolute_path
|
12
|
+
path = "/abc/defg/hij"
|
13
|
+
|
14
|
+
# Full folder name.
|
15
|
+
assert( has_folder?("abc", path) )
|
16
|
+
assert( has_folder?("defg", path) )
|
17
|
+
assert( has_folder?("hij", path) )
|
18
|
+
|
19
|
+
# Partial folder name.
|
20
|
+
assert( has_folder?("efg", path) == false )
|
21
|
+
assert( has_folder?("ac", path) == false )
|
22
|
+
|
23
|
+
# Non-existing name.
|
24
|
+
assert( has_folder?("jih", path) == false )
|
25
|
+
assert( has_folder?("xyz", path) == false )
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_relative_path_trailing_slash
|
29
|
+
path = "abc0123/defg/hij456/"
|
30
|
+
|
31
|
+
# Full folder name.
|
32
|
+
assert( has_folder?("abc0123", path) )
|
33
|
+
assert( has_folder?("defg", path) )
|
34
|
+
assert( has_folder?("hij456", path) )
|
35
|
+
|
36
|
+
# Partial folder name.
|
37
|
+
assert( has_folder?("abc012", path) == false )
|
38
|
+
assert( has_folder?("efg", path) == false )
|
39
|
+
assert( has_folder?("456", path) == false )
|
40
|
+
|
41
|
+
# Non-existing name.
|
42
|
+
assert( has_folder?("xyz", path) == false )
|
43
|
+
assert( has_folder?("aabbc", path) == false )
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fmanager
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ShinYee
|
@@ -21,11 +21,16 @@ extra_rdoc_files: []
|
|
21
21
|
files:
|
22
22
|
- lib/fmanager.rb
|
23
23
|
- lib/fmanager/version.rb
|
24
|
+
- lib/fmanager/fmatch.rb
|
25
|
+
- lib/fmanager/fmeta.rb
|
26
|
+
- lib/fmanager/findex.rb
|
24
27
|
- COPYING
|
25
28
|
- fmanager.gemspec
|
26
29
|
- Rakefile
|
27
30
|
- history.txt
|
28
31
|
- readme.md
|
32
|
+
- test/test_fmatch_hasfolder.rb
|
33
|
+
- test/test_cmd_index.rb
|
29
34
|
- test/test_version.rb
|
30
35
|
- bin/fm
|
31
36
|
homepage: http://github.com/xman/ruby-fmanager
|
@@ -53,4 +58,6 @@ signing_key:
|
|
53
58
|
specification_version: 4
|
54
59
|
summary: Managing files over multiple storage resources.
|
55
60
|
test_files:
|
61
|
+
- test/test_fmatch_hasfolder.rb
|
62
|
+
- test/test_cmd_index.rb
|
56
63
|
- test/test_version.rb
|