# TITLE: # # Batchable # # COPYING: # # Copyright (c) 2007 Psi T Corp. # # This file is part of the ProUtils' Ratch program. # # Ratch is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # Ratch is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with Ratch. If not, see . module Ratch class BatchManager # Task cache, which prevents batch runs from re-executing. attr :cache # New BatchManager. def initialize(runspace) @runspace = runspace @cache = {} end # def batch(batchfile, arguments=nil) @cache[batchfile] ||= run(batchfile, arguments) end # Run a batch file. # TODO: How to handle arguments? def run(batchfile, arguments=nil) # # TODO probably should raise error instead # abort "missing batch file -- #{batchfile}" unless File.file?(batchfile) #BatchFile.new(batchfile).call # Old way with batch execution context object. script = File.read($0 = batchfile) eval(script, $batch_binding, $0) end # def done?(batchfile) batchfile == $0 or @cache.key?(batchfile) end end # module Batchable # Reference batch manager. def batch_manager @batch_manager ||= BatchManager.new(self) end # Batch run, ie. run and cache. # Usually this can be take care of by method_missing. # But, in some cases, built in method names block task # calls, so you have to use #batch to invoke those. def batch(batchfile, arguments=nil) batch_manager.batch(batchfile, arguments=nil) # why did I have task instead of batchfile before? end # Lauch a batch file (non-cached) def launch(batchfile, arguments=nil) batch_manager.run(batchfile, arguments=nil) end # Is a batch run complete or in the process of being completed? def done?(batchfile) batch_manager.done?(batchfile) end # Shell runner. # TODO Does this belong here? def sh(cmd) if noharm? puts cmd true else puts "--> system call: #{cmd}" if trace? system(cmd) end end # Is a file a local batch file? def batch?(path) b = File.dirname($0) + "/#{path}" b.chomp!('!') b if FileTest.file?(b) && FileTest.executable?(b) end # Abort running. #def abort(msg=nil) # puts msg if msg # exit 0 #end # If method is missing try to run an external task # or binary by that name. If it is a binary, arguments # translate into commandline parameters. For example: # # tar 'foo/', :x=>true, :v=>true, :z=>true, :f=>'foo.tar.gz' # # or # # tar '-xvzf', "foo.tar.gz", "foo/" # # becomes # # tar -x -v -z -f foo.tar.gz foo/ # # If it is a task, it will be cached. Tasks only ever run once. # To run them more than once you can manually execute them with #run. # Likewise you can manually run and cache by calling #batch. # This is good to know, b/c in some cases built in method names # block task calls, so you have to #batch to invoke them. def method_missing(sym,*args) puts "method_missing: #{sym}" if debug? name = sym.to_s bat = batch?(name) done = bat && done?(bat) cache = bat && !done && name[1,-1] != '!' bin = bin?(name) if (!bat || done) none = bat && done && !bin #bat = name if bin return super unless bat || bin return if none # nothing to do params = args.to_params if bin cmd = "#{File.basename(bin)} #{params}" res = sh(cmd) elsif bat cmd = "./#{bat} #{params}" puts "--> #{cache ? '' : 'not-'}cached execution: #{cmd}" if trace? res = batch(bat, args) if cache #@batch_catch[bat] ||= (system(cmd); true) #batch_cache[bat] ||= res batch_manager.cache ||= res end end return res end end end