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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c7bf21743d1350607586cba4561d2806c12788e8
4
- data.tar.gz: 176f478bd91f3c2c2ab2952069bc60a81f971aca
3
+ metadata.gz: 5e243865920465668fdca0f5965ee8d6be9b961f
4
+ data.tar.gz: 7dff3316ad7ee02845121fef9d024d3972e82cab
5
5
  SHA512:
6
- metadata.gz: e32d737ec16e2b3b45840b755ef680a8b5fbfe897b5da8db62671c77d9e6474799e1600762505356e81672c31c62d23a4849f2b2e9d6537ed1592fc48378e433
7
- data.tar.gz: 31ea5db5a193191ac9cfdc577b00c90edb34123a2d0ea07fdb7b197bae9b92c759f3533c2f6f4852591dbf4782258e6e610a0d54e4c0ef1946955ae27be3e135
6
+ metadata.gz: 50fd935098b10669bd6fa9982891eb423dff60063b72358887e9253dd063febbebf41471f3a7db37cf56c1611873ed3a9bb65832002b78811679bd0dba232d99
7
+ data.tar.gz: 3981c7b35cfadf325c46ab2370ec0bf84568fd29cdf370d37ae237a3ace4191b35e3505e24032547515b3c83dc1f3c7a37a3ca0d9ecc88dd4b3bfb1ec49a7e2b
data/Rakefile CHANGED
@@ -1,3 +1,4 @@
1
+ $:.unshift File.expand_path("../lib", __FILE__)
1
2
  require 'rake/testtask'
2
3
 
3
4
  require 'fmanager'
data/bin/fm CHANGED
@@ -3,4 +3,28 @@
3
3
  require 'fmanager'
4
4
 
5
5
 
6
- puts "fmanager #{FManager::VERSION}"
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
@@ -1,7 +1,7 @@
1
- ## 0.0.1 (???)
1
+ ## 0.0.1 (18 Aug 2013)
2
2
 
3
3
  Features:
4
4
 
5
- * Indexing command with file metadata in YAML database.
5
+ * Indexing command 'fm index' with file metadata stored in YAML format.
6
6
  * Ruby gem package.
7
7
  * Support Linux platform.
@@ -1 +1,2 @@
1
1
  require 'fmanager/version'
2
+ require 'fmanager/findex'
@@ -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
@@ -0,0 +1,11 @@
1
+ module FManager
2
+
3
+ def has_folder?(folder, path)
4
+ pos = (path =~ /^#{folder}\/|^#{folder}$|\/#{folder}\/|\/#{folder}$/)
5
+ pos != nil
6
+ end
7
+
8
+
9
+ module_function :has_folder?
10
+
11
+ end
@@ -0,0 +1,15 @@
1
+ module FManager
2
+
3
+ class FileMeta
4
+ attr_reader :fsize
5
+ attr_reader :digest
6
+ attr_reader :path
7
+
8
+ def initialize(fsize, path, digest)
9
+ @fsize = fsize
10
+ @digest = digest
11
+ @path = path
12
+ end
13
+ end
14
+
15
+ end
@@ -1,3 +1,3 @@
1
1
  module FManager
2
- VERSION = '0.0.0'
2
+ VERSION = '0.0.1'
3
3
  end
@@ -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.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