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 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