module ALib #{{{ VERSION = '0.0.0' # # utility methods # require 'pathname' require 'socket' require 'tmpdir' require 'rbconfig' module Util #{{{ class << self def export sym #{{{ sym = "#{ sym }".intern module_function sym public sym #}}} end def append_features c #{{{ super c.extend Util #}}} end end def require_version version #{{{ major, minor, teeny = "#{ version }".split(%r/\./o).map{|n| Integer(n)} required = "#{ major }.#{ minor }.#{ teeny }" _major, _minor, _teeny = %w( MAJOR MINOR TEENY ).map{|k| Integer(::Config::CONFIG[k])} actual = "#{ _major }.#{ _minor }.#{ _teeny }" unless _major > major or _major == major and _minor >= minor STDERR.puts("=" * 79) STDERR.puts "<#{ Util::prognam }> requires a ruby version >= <#{ required }>" STDERR.puts STDERR.puts "you are currenlty running ruby version <#{ actual }>" STDERR.puts STDERR.puts "possible problems which could cause this are:" STDERR.puts " - improper PATH environment variable setting" STDERR.puts " - ruby > <#{ required }> has not been installed" STDERR.puts("=" * 79) exit 1 end #}}} end export 'require_version' def prognam #{{{ File::basename $0 #}}} end export 'prognam' def mcp obj #{{{ Marshal.load(Marshal.dump(obj)) #}}} end export 'mcp' def klass #{{{ self.class #}}} end export 'klass' def realpath path #{{{ path = File.expand_path "#{ path }" begin Pathname.new(path).realpath.to_s rescue Errno::ENOENT, Errno::ENOTDIR path end #}}} end export 'realpath' def hashify(*hashes) #{{{ hashes.inject(accum={}){|accum,hash| accum.update hash} #}}} end export 'hashify' def getopt opt, hash #{{{ opt_s = "#{ opt }" hash[opt] || hash[opt_s] || hash[opt_s.intern] #}}} end export 'getopt' def alive pid #{{{ pid = Integer("#{ pid }") begin Process.kill 0, pid true rescue Errno::ESRCH false end #}}} end alias alive? alive export 'alive' export 'alive?' def maim(pid, opts = {}) #{{{ sigs = getopt('signals', opts) || %w(SIGTERM SIGQUIT SIGKILL) suspend = getopt('suspend', opts) || 4 pid = Integer("#{ pid }") existed = false sigs.each do |sig| begin Process.kill(sig, pid) existed = true rescue Errno::ESRCH unless existed return nil else return true end end return true unless alive?(pid) sleep suspend return true unless alive?(pid) end return(not alive?(pid)) #}}} end export 'maim' def timestamp time = Time.now #{{{ usec = "#{ time.usec }" usec << ('0' * (6 - usec.size)) if usec.size < 6 time.strftime('%Y-%m-%d %H:%M:%S.') << usec #}}} end export 'timestamp' def stamptime string, local = true #{{{ string = "#{ string }" pat = %r/^\s*(\d\d\d\d)-(\d\d)-(\d\d) (\d\d):(\d\d):(\d\d).(\d\d\d\d\d\d)\s*$/o match = pat.match string raise ArgumentError, "<#{ string.inspect }>" unless match yyyy,mm,dd,h,m,s,u = match.to_a[1..-1].map{|m| m.to_i} if local Time.local yyyy,mm,dd,h,m,s,u else Time.gm yyyy,mm,dd,h,m,s,u end #}}} end export 'stamptime' def escape! s, char, esc #{{{ re = %r/([#{0x5c.chr << esc}]*)#{char}/ s.gsub!(re) do (($1.size % 2 == 0) ? ($1 << esc) : $1) + char end #}}} end export 'escape!' def escape s, char, esc #{{{ ss = "#{ s }" escape! ss, char, esc ss #}}} end export 'escape' def fork(*args, &block) #{{{ begin verbose = $VERBOSE $VERBOSE = nil Process::fork(*args, &block) ensure $VERBOSE = verbose end #}}} end export 'fork' def exec(*args, &block) #{{{ begin verbose = $VERBOSE $VERBOSE = nil Kernel::exec(*args, &block) ensure $VERBOSE = verbose end #}}} end export 'exec' def hostname #{{{ @__hostname__ ||= Socket::gethostname #}}} end export 'hostname' def host #{{{ @__host__ ||= hostname.gsub(%r/\..*$/o,'') #}}} end export 'host' def emsg e #{{{ "#{ e.message } - (#{ e.class })" #}}} end export 'emsg' def btrace e #{{{ (e.backtrace or []).join("\n") #}}} end export 'btrace' def errmsg e #{{{ emsg(e) << "\n" << btrace(e) #}}} end export 'errmsg' def erreq a, b #{{{ a.class == b.class and a.message == b.message and a.backtrace == b.backtrace #}}} end export 'erreq' def tmpnam dir = Dir.tmpdir, seed = File.basename($0) #{{{ pid = Process.pid path = "%s_%s_%s_%s_%d" % [Util::hostname, seed, pid, Util::timestamp.gsub(/\s+/o,'_'), rand(101010)] File.join(dir, path) #}}} end export 'tmpnam' def uncache file #{{{ refresh = nil begin is_a_file = File === file path = (is_a_file ? file.path : file.to_s) stat = (is_a_file ? file.stat : File::stat(file.to_s)) refresh = tmpnam(File::dirname(path)) File::link path, refresh rescue File::symlink path, refresh File::chmod stat.mode, path File::utime stat.atime, stat.mtime, path open(File::dirname(path)){|d| d.fsync rescue nil} ensure begin File::unlink refresh if refresh rescue Errno::ENOENT end end #}}} end export 'uncache' def system(*args, &block) #{{{ begin verbose = $VERBOSE $VERBOSE = nil Kernel::system(*args, &block) ensure $VERBOSE = verbose end #}}} end export 'system' def columnize buf, width = 80, indent = 0 #{{{ column = [] words = buf.split %r/\s+/o row = ' ' * indent while((word = words.shift)) if((row.size + word.size) < (width - 1)) row << word else column << row row = ' ' * indent row << word end row << ' ' unless row.size == (width - 1) end column << row unless row.strip.empty? column.join "\n" #}}} end export 'columnize' #}}} end # class Util # # logging methods # require 'logger' module Logging #{{{ # # a module that adds an accessor to Logging objects in ored to fix a bug where # not all logging devices are put into sync mode, resulting in improper log # rolling. this is a hack. # module LoggerExt #{{{ attr :logdev #}}} end # module LoggerExt # # implementations of the methods shared by both classes and objects of classes # which include Logging # module LogMethods #{{{ def logger #{{{ if defined?(@logger) and @logger @logger else if Class === self @logger = self.default_logger else @logger = self::class::logger end raise "@logger is undefined!" unless defined?(@logger) and @logger @logger end #}}} end def logger= log #{{{ @logger = log @logger.extend LoggerExt @logger.logdev.dev.sync = true @logger #}}} end def debug(*args, &block); logger.debug(*args, &block); end def info(*args, &block); logger.info(*args, &block) ; end def warn(*args, &block); logger.warn(*args, &block) ; end def error(*args, &block); logger.error(*args, &block); end def fatal(*args, &block); logger.fatal(*args, &block); end def log_err e #{{{ if logger.debug? error{ errmsg e } else error{ emsg e } end #}}} end def emsg e #{{{ "#{ e.message } - (#{ e.class })" #}}} end def btrace e #{{{ e.backtrace.join("\n") #}}} end def errmsg e #{{{ emsg(e) << "\n" << btrace(e) #}}} end #}}} end # module LogMethods EOL = "\n" DIV0 = ("." * 79) << EOL DIV1 = ("-" * 79) << EOL DIV2 = ("=" * 79) << EOL DIV3 = ("#" * 79) << EOL SEC0 = ("." * 16) << EOL SEC1 = ("-" * 16) << EOL SEC2 = ("=" * 16) << EOL SEC3 = ("#" * 16) << EOL class << self #{{{ def append_features c #{{{ ret = super c.extend LogMethods class << c def default_logger #{{{ if defined?(@default_logger) and @default_logger @default_logger else self.default_logger = Logger.new STDERR @default_logger.debug{ "<#{ self }> using default logger"} @default_logger end #}}} end def default_logger= log #{{{ @default_logger = log @default_logger.extend LoggerExt @default_logger.logdev.dev.sync = true @default_logger #}}} end end ret #}}} end #}}} end include LogMethods #}}} end # module Logging # # simple config file class # require 'yaml' class ConfigFile < Hash #{{{ class << self def gen_template(arg = nil) #{{{ @data ||= DATA.read case arg when IO arg.write @data when String open(arg, 'w'){|f| f.write @data} else STDOUT.write @data end self #}}} end def load_default #{{{ @data ||= DATA.read @default ||= YAML::load(munge(@data)) || {} #}}} end def any(basename, *dirnames) #{{{ config = nil dirnames.each do |dirname| path = File::join dirname, basename if test ?e, path config = self::new(path) break end end config || self::new('default') #}}} end def munge buf #{{{ buf.gsub(%r/\t/o,' ') #}}} end end attr :path def initialize path #{{{ @path = nil yaml = nil if path.nil? or path and path =~ /^\s*default/io yaml = self.class.load_default @path = 'DEFAULT' else path yaml = YAML::load(self.class.munge(open(path).read)) @path = path end self.update yaml #}}} end def to_hash #{{{ {}.update self #}}} end #}}} end # # simple list file class (list of commands) # class ListFile < Array #{{{ attr :path def initialize path #{{{ @path = path open(@path) do |f| while((line = f.gets)) line.gsub!(%r/#.*/o,'') next if line =~ %r/^[^\S]+$/o line.gsub!(%r/^[^\S]+|[^\S]+$/o,'') self << line end end #}}} end #}}} end #}}} end