+require 'lockfile'
+
+## 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 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 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
## allows for constructors that take arguments.
##
## You must have #initialize call "self.class.i_am_the_instance self"
-## at some point or everything will fail horribly
+## at some point or everything will fail horribly.
module Singleton
module ClassMethods
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
@instance.send meth, *a, &b
klass.extend ClassMethods
end
end
+
+## wraps an object. if it throws an exception, keeps a copy, and
+## rethrows it for any further method calls.
+class Recoverable
+ def initialize o
+ @o = o
+ @e = nil
+ end
+
+ def clear_error!; @e = nil; end
+ def has_errors?; !@e.nil?; end
+ def error; @e; end
+
+ 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