fmanager 0.0.0 → 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 +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
|