class Exedb
Database-like interface for long-running tasks.
Each instance can run single command (get). If two or more instances with the same command do 'get', then only first will really execute command, all others will wait for result (flock is used).
Results of command execution (stdout) is cached. Next 'get' will return cached results (unless cache timeout happened).
You can force execution, calling 'update' method.
Exit code is available via 'code' method
Example:
d=Exedb.new("ls -la .") d.cache_timeout=5 # 5 seconds for cache expire list=d.get # execute 'ls' and get list of files list=d.get # just get list again (cached) sleep 5 list=d.get # execute 'ls' again and get result list=d.update # force 'ls' execution! d.line_transform {|l| if l =~ /^d.*(\S+)$/ return "DIR: $1" # not correct, do not use in production :) elsif l =~ /^-.*(\S+)$/ return "FILE: $1" # just transform example else return nil # skip this line end } d.update # get list of directories and files in special format
Constants
- DEF_CACHE_TIMEOUT
- DEF_DIR
- SLEEP_TIME
- VERSION
Attributes
directory for cache files
default cache timeout
last cache update time
Public Class Methods
Constructor @str - command to be executed
# File lib/exedb.rb, line 54 def initialize(str='') @update_time=Time.parse("1970-01-01") @cache_timeout=DEF_CACHE_TIMEOUT @cache_dir=DEF_DIR @code=-1 Dir.mkdir DEF_DIR unless File.directory? DEF_DIR self.update_method=(str) end
Public Instance Methods
transform all command output at end of execution block is called with parameters: content, return code returned content replaces original output
Example:
@d.all_transform {|str,code| "Total: #{str.lines.count} lines\nExit code: #{code}" }
# File lib/exedb.rb, line 148 def all_transform(&block) if block obj = Object.new obj.define_singleton_method(:_, &block) @alltransform=obj.method(:_).to_proc else @alltransform=nil end end
Returns symbol of cache state:
-
:updated = actual
-
:need_update = new command execution needed
-
:need_reread = just cache file reread is neede
# File lib/exedb.rb, line 216 def cache_state if File.exists? @path mtime=File.mtime(@path) return :need_update if mtime+@cache_timeout<Time.now return :need_reread if @update_time<mtime return :updated end :need_update end
Get last execution return code. NOTE!!! It is also cached, even on error.
# File lib/exedb.rb, line 193 def code actualize @code end
Get last execution result (stdout), or start new command execution and return result if cache is invalid.
# File lib/exedb.rb, line 184 def get actualize @content end
transform each line in command output if nil is returned, line is skipped
Example:
@d.line_transform {|str| str.downcase }
# File lib/exedb.rb, line 122 def line_transform(&block) if block obj = Object.new obj.define_singleton_method(:_, &block) @transform=obj.method(:_).to_proc else @transform=nil end end
cancel transformation of full output
# File lib/exedb.rb, line 161 def no_all_transform @alltransform=nil end
cancel transformation each line
# File lib/exedb.rb, line 135 def no_line_transform @transform=nil end
Do not execute command - just peek in cache file⦠Usefull for intermediate command output peeking
# File lib/exedb.rb, line 202 def peek begin File.read(@path) rescue '' end end
Just alias for update_method=
# File lib/exedb.rb, line 175 def put(str) self.update_method=(str) end
Force command execution. If another instance with the same command is in progress, no new execution will be started
# File lib/exedb.rb, line 66 def update @content='' # create file if needed unless File.file?(@path) File.open(@path,File::RDWR|File::CREAT,0644){|f|} end File.open(@path, "r+:UTF-8") { |file| if file.flock(File::LOCK_EX|File::LOCK_NB) begin IO.popen(@update_method){|pipe| line=pipe.gets while line line=@transform.call(line) if @transform if line file.puts line file.flush @content = @content+line end line=pipe.gets end } @code=$?.exitstatus rescue @content='' @code=-1 end if @alltransform @content=@alltransform.call(@content,@code) file.seek(0,IO::SEEK_SET) file.write @content file.truncate(@content.size) file.flush end File.open("#{@path}.code",File::RDWR|File::CREAT, 0644){|code_file| code_file.puts @code } file.flock(File::LOCK_UN) else read_cache end @update_time=Time.now } #!!!warn "UPDATED!'" @content end
# File lib/exedb.rb, line 226 def update_in_progress? if File.exists? @path File.open(@path, File::RDONLY) { |file| if file.flock(File::LOCK_EX|File::LOCK_NB) file.flock(File::LOCK_UN) return false end } return true end return false end
Replace executing command
# File lib/exedb.rb, line 167 def update_method=(str) @update_method=str @key=generate_key str @path=File.join(DEF_DIR, @key) # warn "key=#{@key}; path=#{@path}; u=#{str}" end
Protected Instance Methods
# File lib/exedb.rb, line 241 def actualize case cache_state when :need_update update when :need_reread read_cache end end
# File lib/exedb.rb, line 250 def generate_key u f=u.tr('^qwertyuiopasdfghjklzxcvbnm_-','') d=Digest::SHA256.hexdigest(u) return f[0,60]+'..'+f[-60,60]+d if f.size>128 return f+d end
# File lib/exedb.rb, line 257 def read_cache File.open(@path, File::RDONLY) { |file| file.flock(File::LOCK_EX) @content=file.read # warn "CACHE READ: #{@content}" begin File.open("#{@path}.code", File::RDONLY) { |code_file| c=code_file.gets c =~ /([0-9-]+)/ @code=$1.to_i } rescue @code=-1 end file.flock(File::LOCK_UN) } end