X-Git-Url: https://git.donarmstrong.com/?a=blobdiff_plain;f=code_ruby%2Flib%2Fmaasha%2Fbiopieces.rb;h=1797d9211ed4e16ddcc1101b86d1170437ae314f;hb=c4b49c5ce1ed3b46ae37e6ebd73c21d67d6d4810;hp=5fd6edf4a505154bb929976a86c58246ac5d3f85;hpb=4b299e55753c044bb6f80094a1748bf94416d749;p=biopieces.git diff --git a/code_ruby/lib/maasha/biopieces.rb b/code_ruby/lib/maasha/biopieces.rb index 5fd6edf..1797d92 100644 --- a/code_ruby/lib/maasha/biopieces.rb +++ b/code_ruby/lib/maasha/biopieces.rb @@ -79,18 +79,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? 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 @@ -123,6 +123,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 +140,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,15 +160,15 @@ 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) + ios = Zlib::GzipReader.new(ios) rescue - ios.rewind + ios.rewind end input = self.new(ios) @@ -171,12 +178,12 @@ class Biopieces end # Class method for opening data stream for writing Biopiece records. - # Records are written to STDOU (default) or file. + # 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 @@ -209,10 +216,10 @@ class Casts < Array # Add ubiquitous options casts. def ubiquitous - @cast_list << {:long=>'help', :short=>'?', :type=>'flag', :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} - @cast_list << {:long=>'stream_in', :short=>'I', :type=>'file!', :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} - @cast_list << {:long=>'stream_out', :short=>'O', :type=>'file', :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} - @cast_list << {:long=>'verbose', :short=>'v', :type=>'flag', :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} + @cast_list << {:long=>'help', :short=>'?', :type=>'flag', :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} + @cast_list << {:long=>'stream_in', :short=>'I', :type=>'file!', :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} + @cast_list << {:long=>'stream_out', :short=>'O', :type=>'file', :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} + @cast_list << {:long=>'verbose', :short=>'v', :type=>'flag', :mandatory=>false, :default=>nil, :allowed=>nil, :disallowed=>nil} end # Check integrity of the casts. @@ -226,7 +233,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 +272,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 +312,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 @@ -334,14 +341,13 @@ class OptionHandler @argv = argv @casts = casts @script_path = script_path + @options = {} end # Parse options from argv using OptionParser and casts denoting long and # short option names. Usage information is printed and exit called. # A hash with options is returned. def options_parse - @options = {} - option_parser = OptionParser.new do |option| @casts.each do |cast| case cast[:type] @@ -355,7 +361,7 @@ class OptionHandler end when 'string' option.on("-#{cast[:short]}", "--#{cast[:long]} S", String) do |s| - @options[cast[:long]] = s.to_sym # TODO: this to_sym - is that needed? + @options[cast[:long]] = s end when REGEX_LIST option.on( "-#{cast[:short]}", "--#{cast[:long]} A", Array) do |a| @@ -378,7 +384,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 @@ -440,7 +446,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 @@ -456,7 +462,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| @@ -492,13 +498,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 @@ -507,7 +513,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 @@ -516,14 +522,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 @@ -533,24 +539,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 @@ -567,7 +573,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 @@ -576,13 +583,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 @@ -590,7 +599,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 @@ -604,15 +614,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. @@ -628,6 +645,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. @@ -657,7 +676,7 @@ at_exit do status = Status.new tmpdir = status.get_tmpdir - FileUtils.remove_entry_secure(tmpdir) unless tmpdir.nil? + FileUtils.remove_entry_secure(tmpdir, true) unless tmpdir.nil? status.log(exit_status) status.delete end