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