]> git.donarmstrong.com Git - biopieces.git/blobdiff - code_ruby/lib/maasha/biopieces.rb
refactor foo to record
[biopieces.git] / code_ruby / lib / maasha / biopieces.rb
index 7a0c50e52fc1dadd0100008313fe822bf30f05fc..f18bd90aa2e9da46363571c388b4f52f9807a096 100644 (file)
@@ -31,6 +31,8 @@ require 'pp'
 require 'stringio'
 require 'zlib'
 
+BEGIN { Dir.mkdir ENV["BP_TMP"] unless File.directory? ENV["BP_TMP"] }
+
 TEST = false
 
 # Monkey patch (evil) changing the to_s method of the Hash class to
@@ -79,18 +81,18 @@ class Biopieces
   # records. Records are read from STDIN (default) or file (possibly gzipped)
   # and written to STDOUT (default) or file.
   def self.open(input = STDIN, output = STDOUT)
-    input  = self.open_input(input)
-    output = self.open_output(output)
+    io_in  = self.open_input(input)
+    io_out = self.open_output(output)
 
-    if block_given?
+    if block_given?   # FIXME begin block outmost?
       begin
-        yield input, output
+        yield io_in, io_out
       ensure
-        input.close
-        output.close
+        io_in.close
+        io_out.close
       end
     else
-      return input, output
+      return io_in, io_out
     end
   end
 
@@ -112,8 +114,8 @@ class Biopieces
   end
 
   # Method to write a Biopiece record to _ios_.
-  def puts(foo)
-    @ios << foo.to_s
+  def puts(record)
+    @ios << record.to_s
   end
 
   # Method to close _ios_.
@@ -123,6 +125,16 @@ class Biopieces
 
   # Method to parse and yield a Biopiece record from _ios_.
   def each_record
+    while record = get_entry
+      yield record
+    end
+
+    self # conventionally
+  end
+
+  alias :each :each_record
+
+  def get_entry
     record = {}
 
     @ios.each_line do |line|
@@ -130,19 +142,16 @@ class Biopieces
       when /^([^:]+): (.*)$/
         record[$1.to_sym] = $2
       when /^---$/
-        yield record unless record.empty?
-        record = {}
+        break
       else
         raise BiopiecesError, "Bad record format: #{line}"
       end
     end
 
-    yield record unless record.empty?
-
-    self # conventionally
+    return record unless record.empty?
   end
 
-  alias :each :each_record
+  alias :get_record :each_entry
 
   private
 
@@ -153,10 +162,10 @@ class Biopieces
       if STDIN.tty?
         input = self.new(StringIO.new)
       else
-        input = self.new(STDIN, stdio = true)
+        input = self.new(STDIN, true)
       end
     elsif File.exists? input
-      ios = File.open(input, mode='r')
+      ios = File.open(input, 'r')
 
       begin
         ios = Zlib::GzipReader.new(ios)
@@ -174,9 +183,9 @@ class Biopieces
   # Records are written to STDOUT (default) or file.
   def self.open_output(output)
     if output.nil?
-      output = self.new(STDOUT, stdio = true)
+      output = self.new(STDOUT, true)
     elsif not output.is_a? IO
-      output = self.new(File.open(output, mode='w'))
+      output = self.new(File.open(output, 'w'))
     end
 
     output
@@ -226,7 +235,7 @@ class Casts < Array
   def check_keys
     @cast_list.each do |cast|
       MANDATORY.each do |mandatory|
-        raise CastError, "Missing symbol in cast: '#{mandatory.to_sym}'" unless cast.has_key? mandatory.to_sym
+        raise CastError, "Missing symbol in cast: '#{mandatory}'" unless cast.has_key? mandatory.to_sym
       end
     end
   end
@@ -265,7 +274,7 @@ class Casts < Array
       type_hash[type] = true
     end
 
-    unless type_hash.has_key? cast[:type]
+    unless type_hash[cast[:type]]
       raise CastError, "Illegal cast of type: '#{cast[:type]}'"
     end
   end
@@ -305,8 +314,8 @@ class Casts < Array
   def check_duplicates
     check_hash = {}
     @cast_list.each do |cast|
-      raise CastError, "Duplicate argument: '--#{cast[:long]}'" if check_hash.has_key? cast[:long]
-      raise CastError, "Duplicate argument: '-#{cast[:short]}'" if check_hash.has_key? cast[:short]
+      raise CastError, "Duplicate argument: '--#{cast[:long]}'" if check_hash[cast[:long]]
+      raise CastError, "Duplicate argument: '-#{cast[:short]}'" if check_hash[cast[:short]]
       check_hash[cast[:long]]  = true
       check_hash[cast[:short]] = true
     end
@@ -377,7 +386,7 @@ class OptionHandler
     option_parser.parse!(@argv)
 
     if print_usage_full?
-      print_usage_and_exit(full=true)
+      print_usage_and_exit(true)
     elsif print_usage_short?
       print_usage_and_exit
     end
@@ -439,7 +448,7 @@ class OptionHandler
   def options_default
     @casts.each do |cast|
       if cast[:default]
-        unless @options.has_key? cast[:long]
+        unless @options[cast[:long]]
           if cast[:type] == 'list'
             @options[cast[:long]] = cast[:default].split ','
           else
@@ -455,7 +464,7 @@ class OptionHandler
   def options_glob
     @casts.each do |cast|
       if cast[:type] == 'files' or cast[:type] == 'files!'
-        if @options.has_key? cast[:long]
+        if @options[cast[:long]]
           files = []
         
           @options[cast[:long]].each do |path|
@@ -491,13 +500,13 @@ class OptionHandler
   # Check if a mandatory option is set and raise if it isn't.
   def options_check_mandatory(cast)
     if cast[:mandatory]
-      raise ArgumentError, "Mandatory argument: --#{cast[:long]}" unless @options.has_key? cast[:long]
+      raise ArgumentError, "Mandatory argument: --#{cast[:long]}" unless @options[cast[:long]]
     end
   end
 
   # Check int type option and raise if not an integer.
   def options_check_int(cast)
-    if cast[:type] == 'int' and @options.has_key? cast[:long]
+    if cast[:type] == 'int' and @options[cast[:long]]
       unless @options[cast[:long]].is_a? Integer
         raise ArgumentError, "Argument to --#{cast[:long]} must be an integer, not '#{@options[cast[:long]]}'"
       end
@@ -506,7 +515,7 @@ class OptionHandler
   
   # Check uint type option and raise if not an unsinged integer.
   def options_check_uint(cast)
-    if cast[:type] == 'uint' and @options.has_key? cast[:long]
+    if cast[:type] == 'uint' and @options[cast[:long]]
       unless @options[cast[:long]].is_a? Integer and @options[cast[:long]] >= 0
         raise ArgumentError, "Argument to --#{cast[:long]} must be an unsigned integer, not '#{@options[cast[:long]]}'"
       end
@@ -515,14 +524,14 @@ class OptionHandler
 
   # Check file! type argument and raise if file don't exists.
   def options_check_file(cast)
-    if cast[:type] == 'file!' and @options.has_key? cast[:long]
+    if cast[:type] == 'file!' and @options[cast[:long]]
       raise ArgumentError, "No such file: '#{@options[cast[:long]]}'" unless File.file? @options[cast[:long]]
     end
   end
 
   # Check files! type argument and raise if files don't exists.
   def options_check_files(cast)
-    if cast[:type] == 'files!' and @options.has_key? cast[:long]
+    if cast[:type] == 'files!' and @options[cast[:long]]
       @options[cast[:long]].each do |path|
         next if path == "-"
         raise ArgumentError, "File not readable: '#{path}'" unless File.readable? path
@@ -532,24 +541,24 @@ class OptionHandler
   
   # Check dir! type argument and raise if directory don't exist.
   def options_check_dir(cast)
-    if cast[:type] == 'dir!' and @options.has_key? cast[:long]
+    if cast[:type] == 'dir!' and @options[cast[:long]]
       raise ArgumentError, "No such directory: '#{@options[cast[:long]]}'" unless File.directory? @options[cast[:long]]
     end
   end
   
   # Check options and raise unless allowed.
   def options_check_allowed(cast)
-    if cast[:allowed] and @options.has_key? cast[:long]
+    if cast[:allowed] and @options[cast[:long]]
       allowed_hash = {}
       cast[:allowed].split(',').each { |a| allowed_hash[a.to_s] = 1 }
   
-      raise ArgumentError, "Argument '#{@options[cast[:long]]}' to --#{cast[:long]} not allowed" unless allowed_hash.has_key? @options[cast[:long]].to_s
+      raise ArgumentError, "Argument '#{@options[cast[:long]]}' to --#{cast[:long]} not allowed" unless allowed_hash[@options[cast[:long]].to_s]
     end
   end
   
   # Check disallowed argument values and raise if disallowed.
   def options_check_disallowed(cast)
-    if cast[:disallowed] and @options.has_key? cast[:long]
+    if cast[:disallowed] and @options[cast[:long]]
       cast[:disallowed].split(',').each do |val|
         raise ArgumentError, "Argument '#{@options[cast[:long]]}' to --#{cast[:long]} is disallowed" if val.to_s == @options[cast[:long]].to_s
       end
@@ -566,7 +575,8 @@ class Status
   def set
     time0  = Time.new.strftime("%Y-%m-%d %X")
 
-    File.open(path, mode="w") do |fh|
+    File.open(path, "w") do |fh|
+      fh.flock(File::LOCK_EX)
       fh.puts [time0, ARGV.join(" ")].join(";")
     end
   end
@@ -575,13 +585,15 @@ class Status
   def set_tmpdir(tmpdir_path)
     status = ""
 
-    File.open(path, mode="r") do |fh|
+    File.open(path, "r") do |fh|
+      fh.flock(File::LOCK_SH)
       status = fh.read.chomp
     end
 
     status = "#{status};#{tmpdir_path}\n"
 
-    File.open(path, mode="w") do |fh|
+    File.open(path, "w") do |fh|
+      fh.flock(File::LOCK_EX)
       fh << status
     end
   end
@@ -589,7 +601,8 @@ class Status
   # Extract the temporary directory path from the status file,
   # and return this or nil if not found.
   def get_tmpdir
-    File.open(path, mode="r") do |fh|
+    File.open(path, "r") do |fh|
+      fh.flock(File::LOCK_SH)
       tmpdir_path = fh.read.chomp.split(";").last
       return tmpdir_path if File.directory?(tmpdir_path)
     end
@@ -603,15 +616,22 @@ class Status
     user    = ENV["USER"]
     script  = File.basename($0)
 
-    stream = File.open(path)
-    time0, args, tmp_dir = stream.first.split(";")
-    stream.close
+    time0 = nil
+    args  = nil
+
+    File.open(path, "r") do |fh|
+      fh.flock(File::LOCK_SH)
+      time0, args = fh.first.split(";")
+    end
 
     elap     = time_diff(time0, time1)
     command  = [script, args].join(" ") 
     log_file = File.join(ENV["BP_LOG"], "biopieces.log")
 
-    File.open(log_file, mode = "a") { |file| file.puts [time0, time1, elap, user, exit_status, command].join("\t") }
+    File.open(log_file, "a") do |fh|
+      fh.flock(File::LOCK_EX)
+      fh.puts [time0, time1, elap, user, exit_status, command].join("\t")
+    end
   end
 
   # Delete status file.
@@ -627,6 +647,8 @@ class Status
     script = File.basename($0)
     pid    = $$
     path   = File.join(ENV["BP_TMP"], [user, script, pid, "status"].join("."))
+
+    path
   end
 
   # Get the elapsed time from the difference between two time stamps.