]> git.cworth.org Git - sup/blobdiff - lib/sup/util.rb
add gpg interface to all outgoing messages (but no implementation yet)
[sup] / lib / sup / util.rb
index c3d4ce6a18ba689aff73918c5e88b394abda9bc4..e2b82e1f6cbbeeb321654c456b4a6d339fe2a838 100644 (file)
@@ -136,6 +136,7 @@ class Object
 
   ## clone of java-style whole-method synchronization
   ## assumes a @mutex variable
+  ## TODO: clean up, try harder to avoid namespace collisions
   def synchronized *meth
     meth.each do
       class_eval <<-EOF
@@ -146,6 +147,32 @@ class Object
       EOF
     end
   end
+
+  def ignore_concurrent_calls *meth
+    meth.each do
+      mutex = "@__concurrent_protector_#{meth}"
+      flag = "@__concurrent_flag_#{meth}"
+      oldmeth = "__unprotected_#{meth}"
+      class_eval <<-EOF
+        alias #{oldmeth} #{meth}
+        def #{meth}(*a, &b)
+          #{mutex} = Mutex.new unless defined? #{mutex}
+          #{flag} = true unless defined? #{flag}
+          run = #{mutex}.synchronize do
+            if #{flag}
+              #{flag} = false
+              true
+            end
+          end
+          if run
+            ret = #{oldmeth}(*a, &b)
+            #{mutex}.synchronize { #{flag} = true }
+            ret
+          end
+        end
+      EOF
+    end
+  end
 end
 
 class String
@@ -165,6 +192,7 @@ class String
     ret
   end
 
+  ## one of the few things i miss from perl
   def ucfirst
     self[0 .. 0].upcase + self[1 .. -1]
   end
@@ -175,6 +203,64 @@ class String
     split(/,\s*(?=(?:[^"]*"[^"]*")*(?![^"]*"))/)
   end
 
+  ## ok, here we do it the hard way. got to have a remainder for purposes of
+  ## tab-completing full email addresses
+  def split_on_commas_with_remainder
+    ret = []
+    state = :outstring
+    pos = 0
+    region_start = 0
+    while pos <= length
+      newpos = case state
+        when :escaped_instring, :escaped_outstring: pos
+        else index(/[,"\\]/, pos)
+      end 
+      
+      if newpos
+        char = self[newpos]
+      else
+        char = nil
+        newpos = length
+      end
+
+      case char
+      when ?"
+        state = case state
+          when :outstring: :instring
+          when :instring: :outstring
+          when :escaped_instring: :instring
+          when :escaped_outstring: :outstring
+        end
+      when ?,, nil
+        state = case state
+          when :outstring, :escaped_outstring:
+            ret << self[region_start ... newpos].gsub(/^\s+|\s+$/, "")
+            region_start = newpos + 1
+            :outstring
+          when :instring: :instring
+          when :escaped_instring: :instring
+        end
+      when ?\\
+        state = case state
+          when :instring: :escaped_instring
+          when :outstring: :escaped_outstring
+          when :escaped_instring: :instring
+          when :escaped_outstring: :outstring
+        end
+      end
+      pos = newpos + 1
+    end
+
+    remainder = case state
+      when :instring
+        self[region_start .. -1].gsub(/^\s+/, "")
+      else
+        nil
+      end
+
+    [ret, remainder]
+  end
+
   def wrap len
     ret = []
     s = self
@@ -223,6 +309,20 @@ class Fixnum
       "<#{self}>"
     end
   end
+
+  ## hacking the english language
+  def pluralize s
+    to_s + " " +
+      if self == 1
+        s
+      else
+        if s =~ /(.*)y$/
+          $1 + "ies"
+        else
+          s + "s"
+        end
+      end
+  end
 end
 
 class Hash
@@ -299,6 +399,7 @@ class Array
   def to_boolean_h; Hash[*map { |x| [x, true] }.flatten]; end
 
   def last= e; self[-1] = e end
+  def nonempty?; !empty? end
 end
 
 class Time
@@ -405,19 +506,20 @@ module Singleton
   end
 end
 
-## wraps an object. if it throws an exception, keeps a copy, and
-## rethrows it for any further method calls.
+## wraps an object. if it throws an exception, keeps a copy.
 class Recoverable
   def initialize o
     @o = o
-    @e = nil
+    @error = nil
+    @mutex = Mutex.new
   end
 
-  def clear_error!; @e = nil; end
-  def has_errors?; !@e.nil?; end
-  def error; @e; end
+  attr_accessor :error
 
-  def method_missing m, *a, &b; __pass m, *a, &b; end
+  def clear_error!; @error = nil; end
+  def has_errors?; !@error.nil?; end
+
+  def method_missing m, *a, &b; __pass m, *a, &b end
   
   def id; __pass :id; end
   def to_s; __pass :to_s; end
@@ -430,8 +532,8 @@ class Recoverable
     begin
       @o.send(m, *a, &b)
     rescue Exception => e
-      @e = e
-      raise e
+      @error ||= e
+      raise
     end
   end
 end
@@ -471,3 +573,30 @@ class SavingHash
 
   defer_all_other_method_calls_to :hash
 end
+
+class OrderedHash < Hash
+  alias_method :store, :[]=
+  alias_method :each_pair, :each
+  attr_reader :keys
+
+  def initialize *a
+    @keys = []
+    a.each { |k, v| self[k] = v }
+  end
+
+  def []= key, val
+    @keys << key unless member?(key)
+    super
+  end
+
+  def values; keys.map { |k| self[k] } end
+  def index key; @keys.index key end
+
+  def delete key
+    @keys.delete key
+    super
+  end
+
+  def each; @keys.each { |k| yield k, self[k] } end
+end
+