rbbt-util 1.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.
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