rbbt-util 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010-2011 Miguel Vázquez García
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/bin/tchash.rb ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbbt-util'
4
+ require 'rbbt/util/simpleopt'
5
+
6
+ options = SOPT.parse "-h--help"
7
+
8
+ command = ARGV.shift
9
+ file = ARGV.shift
10
+
11
+ case command
12
+ when 'cat'
13
+ puts TSV.new(TCHash.get(file))
14
+ end
15
+
data/bin/tsv.rb ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rbbt/util/simpleopt'
4
+
5
+ options = SOPT.parse "-h--help:-to--tsv-options*:-p--persistence"
6
+
7
+ command = ARGV.shift
8
+ file = ARGV.shift
9
+
10
+ case command
11
+ when 'cat'
12
+ puts TSV.new(file, options["tsv-options"].merge(options["persistence"]))
13
+ when '
14
+
@@ -0,0 +1,100 @@
1
+ require 'digest'
2
+
3
+ module CacheHelper
4
+ CACHE_DIR = '/tmp/cachehelper'
5
+ FileUtils.mkdir_p(CACHE_DIR) unless File.exist?(CACHE_DIR)
6
+
7
+ LOG_TIME = false
8
+ class CacheLocked < Exception; end
9
+
10
+ def self.time(id)
11
+ t = Time.now
12
+ data = block.call
13
+ STDERR.puts "#{ id } time: #{Time.now - t}"
14
+ data
15
+ end
16
+
17
+ def self.cachedir=(dir)
18
+ @@cachedir=dir
19
+ FileUtils.mkdir_p(dir) unless File.exist?(dir)
20
+ end
21
+
22
+ def self.cachedir
23
+ @@cachedir ||= CACHE_DIR
24
+ end
25
+
26
+
27
+ def self.reset
28
+ FileUtils.rm Dir.glob(cachedir + '*')
29
+ end
30
+
31
+ def self.reset_locks
32
+ FileUtils.rm Dir.glob(cachedir + '*.lock')
33
+ end
34
+
35
+
36
+ def self.build_filename(name, key)
37
+ File.join(cachedir, name + ": " + Digest::MD5.hexdigest(key.to_s))
38
+ end
39
+
40
+ def self.do(filename, block)
41
+ FileUtils.touch(filename + '.lock')
42
+
43
+ if LOG_TIME
44
+ data = time do
45
+ block.call
46
+ end
47
+ else
48
+ data = block.call
49
+ end
50
+
51
+ File.open(filename, 'w'){|f| f.write data}
52
+ FileUtils.rm(filename + '.lock')
53
+ return data
54
+ end
55
+
56
+ def self.clean(name)
57
+ FileUtils.rm Dir.glob(File.join(cachedir, "#{ name }*"))
58
+ end
59
+
60
+ def self.cache_ready?(name, key)
61
+ filename = CacheHelper.build_filename(name, key)
62
+ File.exist?(filename)
63
+ end
64
+
65
+ def self.cache(name, key = [], wait = nil, &block)
66
+ filename = CacheHelper.build_filename(name, key)
67
+ begin
68
+ case
69
+ when File.exist?(filename)
70
+ return File.open(filename){|f| f.read}
71
+ when File.exist?(filename + '.lock')
72
+ raise CacheLocked
73
+ else
74
+ if wait.nil?
75
+ CacheHelper.do(filename, block)
76
+ else
77
+ Thread.new{CacheHelper.do(filename, block)}
78
+ return wait
79
+ end
80
+
81
+ end
82
+ rescue CacheLocked
83
+ if wait.nil?
84
+ sleep 30
85
+ retry
86
+ else
87
+ return wait
88
+ end
89
+ rescue Exception
90
+ FileUtils.rm(filename + '.lock') if File.exist?(filename + '.lock')
91
+ raise $!
92
+ end
93
+ end
94
+
95
+ def self.marshal_cache(name, key = [])
96
+ Marshal::load( cache(name, key) do
97
+ Marshal::dump(yield)
98
+ end)
99
+ end
100
+ end
@@ -0,0 +1,140 @@
1
+ require 'rbbt/util/misc'
2
+ require 'rbbt/util/log'
3
+ require 'stringio'
4
+
5
+ module CMD
6
+ class CMDError < StandardError;end
7
+
8
+ module SmartIO
9
+ def self.tie(io, pid = nil, post = nil)
10
+ io.instance_eval{
11
+ @pid = pid
12
+ @post = post
13
+ alias original_close close
14
+ def close
15
+ begin
16
+ Process.waitpid(@pid, Process::WNOHANG) if @pid
17
+ rescue
18
+ end
19
+
20
+ @post.call if @post
21
+ original_close
22
+ end
23
+
24
+ alias original_read read
25
+ def read
26
+ data = Misc.fixutf8(original_read)
27
+ self.close unless self.closed?
28
+ data
29
+ end
30
+ }
31
+ io
32
+ end
33
+ end
34
+
35
+ def self.process_cmd_options(options = {})
36
+ string = ""
37
+ options.each do |option, value|
38
+ case
39
+ when value.nil? || FalseClass === value
40
+ next
41
+ when TrueClass === value
42
+ string << "#{option} "
43
+ else
44
+ if option.chars.to_a.last == "="
45
+ string << "#{option}#{value} "
46
+ else
47
+ string << "#{option} #{value} "
48
+ end
49
+ end
50
+ end
51
+
52
+ string.strip
53
+ end
54
+
55
+ def self.cmd(cmd, options = {}, &block)
56
+ options = Misc.add_defaults options, :stderr => Log::DEBUG
57
+ in_content = options.delete(:in)
58
+ stderr = options.delete(:stderr)
59
+ pipe = options.delete(:pipe)
60
+ post = options.delete(:post)
61
+
62
+ if stderr == true
63
+ stderr = Log::HIGH
64
+ end
65
+
66
+ # Process cmd_options
67
+ cmd_options = process_cmd_options options
68
+ if cmd =~ /'\{opt\}'/
69
+ cmd.sub!('\'{opt}\'', cmd_options)
70
+ else
71
+ cmd << " " << cmd_options
72
+ end
73
+
74
+ sout, serr = IO.pipe, IO.pipe
75
+
76
+ case
77
+ when (IO === in_content and not StringIO === in_content)
78
+ sin = [in_content, nil]
79
+ else StringIO === in_content
80
+ sin = IO.pipe
81
+ end
82
+
83
+ pid = fork {
84
+ begin
85
+
86
+ sin.last.close if sin.last
87
+ STDIN.reopen sin.first
88
+ sin.first.close
89
+
90
+
91
+ serr.first.close
92
+ STDERR.reopen serr.last
93
+ serr.last.close
94
+
95
+ sout.first.close
96
+ STDOUT.reopen sout.last
97
+ sout.last.close
98
+
99
+ STDOUT.sync = STDERR.sync = true
100
+ exec(cmd)
101
+ rescue Exception
102
+ raise CMDError, $!.message
103
+ end
104
+
105
+ }
106
+ sin.first.close
107
+ sout.last.close
108
+ serr.last.close
109
+
110
+ case
111
+ when String === in_content
112
+ sin.last.write in_content
113
+ sin.last.close
114
+ when StringIO === in_content
115
+ Thread.new do
116
+ while l = in_content.gets
117
+ sin.last.write l
118
+ end
119
+ sin.last.close
120
+ end
121
+ end
122
+
123
+ Thread.new do
124
+ while l = serr.first.gets
125
+ Log.log l, stderr if Integer === stderr
126
+ end
127
+ serr.first.close
128
+ end
129
+
130
+ if pipe
131
+ SmartIO.tie sout.first, pid, post
132
+ sout.first
133
+ else
134
+ out = StringIO.new sout.first.read
135
+ SmartIO.tie out
136
+ Process.waitpid pid
137
+ out
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,81 @@
1
+ module DataModule
2
+
3
+ def self.extended(base)
4
+ if defined? base::PKG and base::PKG
5
+ base.pkg_module = base::PKG
6
+ else
7
+ base.pkg_module = Rbbt
8
+ end
9
+
10
+ base.sharedir = PKGData.get_caller_sharedir
11
+ end
12
+
13
+ def pkg_module
14
+ @pkg_module
15
+ end
16
+
17
+ def pkg_module=(pkg_module)
18
+ @pkg_module = pkg_module
19
+ end
20
+
21
+ def sharedir
22
+ @sharedir
23
+ end
24
+
25
+ def sharedir=(sharedir)
26
+ @sharedir = sharedir
27
+ end
28
+
29
+ alias old_method_missing method_missing
30
+ def method_missing(name, *args, &block)
31
+ if args.any?
32
+ filename = File.join(self.to_s, args.first, name.to_s)
33
+ else
34
+ filename = File.join(self.to_s, name.to_s)
35
+ end
36
+
37
+ begin
38
+ pkg_module.add_datafiles filename => ['', self.to_s, sharedir]
39
+ rescue
40
+ Log.debug $!.message
41
+ old_method_missing name, *args, &block
42
+ end
43
+
44
+ pkg_module.find_datafile filename
45
+ end
46
+
47
+ module WithKey
48
+ def klass=(klass)
49
+ @klass = klass
50
+ end
51
+
52
+ def klass
53
+ @klass
54
+ end
55
+
56
+ def key=(key)
57
+ @key = key
58
+ end
59
+
60
+ def key
61
+ @key
62
+ end
63
+
64
+ def method_missing(name, *args)
65
+ if key
66
+ klass.send(name, key, *args)
67
+ else
68
+ klass.send(name, *args)
69
+ end
70
+ end
71
+ end
72
+
73
+ def with_key(key)
74
+ klass = self
75
+ o = Object.new
76
+ o.extend WithKey
77
+ o.klass = self
78
+ o.key = key
79
+ o
80
+ end
81
+ end
@@ -0,0 +1,32 @@
1
+ require 'rbbt/util/tsv'
2
+ require 'spreadsheet'
3
+
4
+ class TSV
5
+ def self.excel2tsv(file, options = {})
6
+ sheet = options.delete :sheet
7
+ header = options.delete :header
8
+ header = true unless header == false
9
+ sheet ||= 0
10
+ TmpFile.with_file do |filename|
11
+ workbook = Spreadsheet.open File.open(file)
12
+ sheet = workbook.worksheet sheet
13
+
14
+ rows = []
15
+
16
+ sheet.each do |row|
17
+ rows << row.values_at(0..(row.size - 1))
18
+ end
19
+
20
+ File.open(filename, 'w') do |f|
21
+ if header
22
+ header = rows.shift
23
+ f.puts "#" + header * "\t"
24
+ end
25
+
26
+ rows.each do |row| f.puts row * "\t" end
27
+ end
28
+
29
+ TSV.new(filename, options)
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,58 @@
1
+ require 'fileutils'
2
+ require 'rbbt/util/misc'
3
+
4
+ # Provides caching functionality for files downloaded from the internet
5
+ module FileCache
6
+ CACHEDIR = "/tmp/rbbt_cache"
7
+ FileUtils.mkdir CACHEDIR unless File.exist? CACHEDIR
8
+
9
+ def self.cachedir=(cachedir)
10
+ CACHEDIR.replace cachedir
11
+ FileUtils.mkdir_p CACHEDIR unless File.exist? CACHEDIR
12
+ end
13
+
14
+ def self.cachedir
15
+ CACHEDIR
16
+ end
17
+
18
+ def self.path(filename)
19
+ filename = File.basename filename
20
+
21
+ filename.match(/(.+)\.(.+)/)
22
+
23
+ base = filename.sub(/\..+/,'')
24
+ dirs = base.scan(/./).values_at(0,1,2,3,4).compact.reverse
25
+
26
+ File.join(File.join(CACHEDIR, *dirs), filename)
27
+ end
28
+
29
+ def self.add(filename, content)
30
+ path = path(filename)
31
+
32
+ FileUtils.makedirs(File.dirname(path), :mode => 0777)
33
+
34
+ Misc.sensiblewrite(path, content)
35
+
36
+ FileUtils.chmod 0666, path
37
+
38
+ path
39
+ end
40
+
41
+ def self.found(filename)
42
+ File.exists? FileCache.path(filename)
43
+ end
44
+
45
+ def self.get(filename)
46
+ path = path(filename)
47
+
48
+ return nil if ! File.exists? path
49
+
50
+ File.open(path)
51
+ end
52
+
53
+ def self.del(filename)
54
+ path = path(filename)
55
+
56
+ FileUtils.rm path if File.exist? path
57
+ end
58
+ end
@@ -0,0 +1,50 @@
1
+ module Log
2
+
3
+ DEBUG = 0
4
+ LOW = 1
5
+ MEDIUM = 2
6
+ HIGH = 3
7
+
8
+ def severity=(severity)
9
+ @@severity = severity
10
+ end
11
+
12
+ def severity
13
+ @@severity
14
+ end
15
+
16
+ def self.log(message, severity = MEDIUM)
17
+ STDERR.puts "#{Time.now}[#{severity.to_s}]: " + message if severity >= @@severity
18
+ end
19
+
20
+ def self.debug(message)
21
+ log(message, DEBUG)
22
+ end
23
+
24
+ def self.low(message)
25
+ log(message, LOW)
26
+ end
27
+
28
+ def self.medium(message)
29
+ log(message, MEDIUM)
30
+ end
31
+
32
+ def self.high(message)
33
+ log(message, HIGH)
34
+ end
35
+
36
+ case ENV['RBBT_LOG']
37
+ when 'DEBUG'
38
+ @@severity = DEBUG
39
+ when 'LOW'
40
+ @@severity = LOW
41
+ when 'MEDIUM'
42
+ @@severity = MEDIUM
43
+ when 'HIGH'
44
+ @@severity = HIGH
45
+ when nil
46
+ @@severity = HIGH
47
+ else
48
+ @@severity = ENV['RBBT_LOG'].to_i
49
+ end
50
+ end
@@ -0,0 +1,158 @@
1
+ require 'iconv'
2
+ module Misc
3
+ class FieldNotFoundError < StandardError;end
4
+
5
+ def self.env_add(var, value, sep = ":", prepend = true)
6
+ ENV[var] ||= ""
7
+ return if ENV[var] =~ /(#{sep}|^)#{Regexp.quote value}(#{sep}|$)/
8
+ if prepend
9
+ ENV[var] = value + sep + ENV[var]
10
+ else
11
+ ENV[var] += sep + ENV[var]
12
+ end
13
+ end
14
+
15
+ def self.count(list)
16
+ counts = Hash.new 0
17
+ list.each do |item|
18
+ counts[item] += 1
19
+ end
20
+
21
+ counts
22
+ end
23
+
24
+ def self.profile
25
+ require 'ruby-prof'
26
+ RubyProf.start
27
+ begin
28
+ res = yield
29
+ rescue Exception
30
+ puts "Profiling aborted"
31
+ raise $!
32
+ ensure
33
+ result = RubyProf.stop
34
+ printer = RubyProf::FlatPrinter.new(result)
35
+ printer.print(STDOUT, 0)
36
+ end
37
+
38
+ res
39
+ end
40
+
41
+ def self.fixutf8(string)
42
+ if string.respond_to?(:valid_encoding?) and ! string.valid_encoding?
43
+ @@ic ||= Iconv.new('UTF-8//IGNORE', 'UTF-8')
44
+ @@ic.iconv(string)
45
+ else
46
+ string
47
+ end
48
+ end
49
+
50
+ def self.add_defaults(options, defaults = {})
51
+ case
52
+ when Hash === options
53
+ new_options = options.dup
54
+ when String === options
55
+ new_options = string2hash options
56
+ else
57
+ raise "Format of '#{options.inspect}' not understood"
58
+ end
59
+ defaults.each do |key, value|
60
+ new_options[key] = value if new_options[key].nil?
61
+ end
62
+ new_options
63
+ end
64
+
65
+ def self.string2hash(string)
66
+
67
+ options = {}
68
+ string.split(/#/).each do |str|
69
+ if str.match(/(.*)=(.*)/)
70
+ option, value = $1, $2
71
+ else
72
+ option, value = str, true
73
+ end
74
+
75
+ option = option.sub(":",'').to_sym if option.chars.first == ':'
76
+
77
+
78
+ if value == true
79
+ options[option] = option.to_s.chars.first != '!'
80
+ else
81
+ options[option] = begin eval(value) rescue value end
82
+ end
83
+ end
84
+
85
+ options
86
+ end
87
+
88
+ def self.sensiblewrite(path, content)
89
+ begin
90
+ case
91
+ when String === content
92
+ File.open(path, 'w') do |f| f.write content end
93
+ when (IO === content or StringIO === content)
94
+ File.open(path, 'w') do |f| while l = content.gets; f.write l; end end
95
+ else
96
+ File.open(path, 'w') do |f| end
97
+ end
98
+ rescue Interrupt
99
+ raise "Interrupted (Ctrl-c)"
100
+ rescue Exception
101
+ FileUtils.rm_f path
102
+ raise $!
103
+ end
104
+ end
105
+
106
+ def self.field_position(fields, field, quiet = false)
107
+ return field if Integer === field or Range === field
108
+ raise FieldNotFoundError, "Field information missing" if fields.nil? && ! quiet
109
+ fields.each_with_index{|f,i| return i if f == field}
110
+ field_re = Regexp.new /#{field}/i
111
+ fields.each_with_index{|f,i| return i if f =~ field_re}
112
+ raise FieldNotFoundError, "Field '#{ field }' was not found" unless quiet
113
+ end
114
+ end
115
+
116
+ class NamedArray < Array
117
+ attr_accessor :fields
118
+
119
+ def self.name(array, fields)
120
+ a = self.new(array)
121
+ a.fields = fields
122
+ a
123
+ end
124
+
125
+ def positions(fields)
126
+ fields.collect{|field|
127
+ Misc.field_position(@fields, field)
128
+ }
129
+ end
130
+
131
+ alias original_get_brackets []
132
+ def [](key)
133
+ original_get_brackets(Misc.field_position(fields, key))
134
+ end
135
+
136
+ alias original_set_brackets []=
137
+ def []=(key,value)
138
+ original_set_brackets(Misc.field_position(fields, key), value)
139
+ end
140
+
141
+ alias original_values_at values_at
142
+ def values_at(*keys)
143
+ keys = keys.collect{|k| Misc.field_position(fields, k) }
144
+ original_values_at(*keys)
145
+ end
146
+ end
147
+
148
+
149
+ def profile
150
+ require 'ruby-prof'
151
+ RubyProf.start
152
+ yield
153
+ result = RubyProf.stop
154
+
155
+ # Print a flat profile to text
156
+ printer = RubyProf::FlatPrinter.new(result)
157
+ printer.print(STDOUT, 0)
158
+ end