lib/sup/util.rb in sup-0.0.8 vs lib/sup/util.rb in sup-0.1

- old
+ new

@@ -1,5 +1,100 @@ +require 'lockfile' +require 'mime/types' +require 'pathname' + +## time for some monkeypatching! +class Lockfile + def gen_lock_id + Hash[ + 'host' => "#{ Socket.gethostname }", + 'pid' => "#{ Process.pid }", + 'ppid' => "#{ Process.ppid }", + 'time' => timestamp, + 'pname' => $0, + 'user' => ENV["USER"] + ] + end + + def dump_lock_id lock_id = @lock_id + "host: %s\npid: %s\nppid: %s\ntime: %s\nuser: %s\npname: %s\n" % + lock_id.values_at('host','pid','ppid','time','user', 'pname') + end + + def lockinfo_on_disk + h = load_lock_id IO.read(path) + h['mtime'] = File.mtime path + h + end + + def touch_yourself; touch path end +end + +class Pathname + def human_size + s = + begin + size + rescue SystemCallError + return "?" + end + + if s < 1024 + s.to_s + "b" + elsif s < (1024 * 1024) + (s / 1024).to_s + "k" + elsif s < (1024 * 1024 * 1024) + (s / 1024 / 1024).to_s + "m" + else + (s / 1024 / 1024 / 1024).to_s + "g" + end + end + + def human_time + begin + ctime.strftime("%Y-%m-%d %H:%M") + rescue SystemCallError + "?" + end + end +end + +## more monkeypatching! +module RMail + class EncodingUnsupportedError < StandardError; end + + class Message + def add_attachment fn + bfn = File.basename fn + a = Message.new + t = MIME::Types.type_for(bfn).first || MIME::Types.type_for("exe").first + + a.header.add "Content-Disposition", "attachment; filename=#{bfn}" + a.header.add "Content-Type", "#{t.content_type}; name=#{bfn}" + a.header.add "Content-Transfer-Encoding", t.encoding + a.body = + case t.encoding + when "base64" + [IO.read(fn)].pack "m" + when "quoted-printable" + [IO.read(fn)].pack "M" + else + raise EncodingUnsupportedError, t.encoding + end + + add_part a + end + end +end + +class Range + ## only valid for integer ranges (unless I guess it's exclusive) + def size + last - first + (exclude_end? ? 0 : 1) + end +end + class Module def bool_reader *args args.each { |sym| class_eval %{ def #{sym}?; @#{sym}; end } } end def bool_writer *args; attr_writer(*args); end @@ -7,11 +102,14 @@ bool_reader(*args) bool_writer(*args) end def defer_all_other_method_calls_to obj - class_eval %{ def method_missing meth, *a, &b; @#{obj}.send meth, *a, &b; end } + class_eval %{ + def method_missing meth, *a, &b; @#{obj}.send meth, *a, &b; end + def respond_to? meth; @#{obj}.respond_to?(meth); end + } end end class Object def ancestors @@ -36,10 +134,12 @@ ## ## with(expensive_operation) { |x| log "got #{x}" } ## ## i'm sure there's pithy comment i could make here about the ## superiority of lisp, but fuck lisp. + ## + ## addendum: apparently this is a "k combinator". whoda thunk it? def returning x; yield x; x; end ## clone of java-style whole-method synchronization ## assumes a @mutex variable def synchronized *meth @@ -172,10 +272,28 @@ best, bestval = e, val end end best end + + ## returns the maximum shared prefix of an array of strings + ## optinally excluding a prefix + def shared_prefix exclude="" + return "" if empty? + prefix = "" + (0 ... first.length).each do |i| + c = first[i] + break unless all? { |s| s[i] == c } + next if exclude[i] == c + prefix += c.chr + end + prefix + end + + def max_of + map { |e| yield e }.max + end end class Array def flatten_one_level inject([]) { |a, e| a + e } @@ -183,10 +301,12 @@ def to_h; Hash[*flatten]; end def rest; self[1..-1]; end def to_boolean_h; Hash[*map { |x| [x, true] }.flatten]; end + + def last= e; self[-1] = e end end class Time def to_indexable_s sprintf "%012d", self @@ -269,10 +389,17 @@ def instance; @instance; end def instantiated?; defined?(@instance) && !@instance.nil?; end def deinstantiate!; @instance = nil; end def method_missing meth, *a, &b raise "no instance defined!" unless defined? @instance + + ## if we've been deinstantiated, just drop all calls. this is + ## useful because threads that might be active during the + ## cleanup process (e.g. polling) would otherwise have to + ## special-case every call to a Singleton object + return nil if @instance.nil? + @instance.send meth, *a, &b end def i_am_the_instance o raise "there can be only one! (instance)" if defined? @instance @instance = o @@ -299,15 +426,54 @@ def method_missing m, *a, &b; __pass m, *a, &b; end def id; __pass :id; end def to_s; __pass :to_s; end def to_yaml x; __pass :to_yaml, x; end + def is_a? c; @o.is_a? c; end + def respond_to? m; @o.respond_to? m end + def __pass m, *a, &b begin @o.send(m, *a, &b) rescue Exception => e @e = e raise e end end +end + +## acts like a hash with an initialization block, but saves any +## newly-created value even upon lookup. +## +## for example: +## +## class C +## attr_accessor :val +## def initialize; @val = 0 end +## end +## +## h = Hash.new { C.new } +## h[:a].val # => 0 +## h[:a].val = 1 +## h[:a].val # => 0 +## +## h2 = SavingHash.new { C.new } +## h2[:a].val # => 0 +## h2[:a].val = 1 +## h2[:a].val # => 1 +## +## important note: you REALLY want to use #member? to test existence, +## because just checking h[anything] will always evaluate to true +## (except for degenerate constructor blocks that return nil or false) +class SavingHash + def initialize &b + @constructor = b + @hash = Hash.new + end + + def [] k + @hash[k] ||= @constructor.call(k) + end + + defer_all_other_method_calls_to :hash end