Class: Exedb

Inherits:
Object
  • Object
show all
Defined in:
lib/exedb.rb,
lib/exedb/version.rb

Overview

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

Constant Summary collapse

SLEEP_TIME =
1
DEF_DIR =
'/tmp/Exedb'
DEF_CACHE_TIMEOUT =
60
VERSION =
"0.2.4"

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(str = '') ⇒ Exedb

Constructor



54
55
56
57
58
59
60
61
# 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

Instance Attribute Details

#cache_dirObject

directory for cache files



45
46
47
# File 'lib/exedb.rb', line 45

def cache_dir
  @cache_dir
end

#cache_timeoutObject

default cache timeout



47
48
49
# File 'lib/exedb.rb', line 47

def cache_timeout
  @cache_timeout
end

#update_timeObject (readonly)

last cache update time



49
50
51
# File 'lib/exedb.rb', line 49

def update_time
  @update_time
end

Instance Method Details

#all_transform(&block) ⇒ Object

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}"
}


148
149
150
151
152
153
154
155
156
# 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

#cache_stateObject

Returns symbol of cache state:

  • :updated = actual

  • :need_update = new command execution needed

  • :need_reread = just cache file reread is neede



216
217
218
219
220
221
222
223
224
# 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

#codeObject

Get last execution return code. NOTE!!! It is also cached, even on error.



193
194
195
196
# File 'lib/exedb.rb', line 193

def code
  actualize
  @code
end

#getObject

Get last execution result (stdout), or start new command execution and return result if cache is invalid.



184
185
186
187
# File 'lib/exedb.rb', line 184

def get
  actualize
  @content
end

#line_transform(&block) ⇒ Object

transform each line in command output if nil is returned, line is skipped

Example:

@d.line_transform {|str|
   str.downcase
}


122
123
124
125
126
127
128
129
130
# 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

#no_all_transformObject

cancel transformation of full output



161
162
163
# File 'lib/exedb.rb', line 161

def no_all_transform
  @alltransform=nil
end

#no_line_transformObject

cancel transformation each line



135
136
137
# File 'lib/exedb.rb', line 135

def no_line_transform
  @transform=nil    
end

#peekObject

Do not execute command - just peek in cache file… Usefull for intermediate command output peeking



202
203
204
205
206
207
208
# File 'lib/exedb.rb', line 202

def peek
  begin
    File.read(@path)      
  rescue
    ''
  end
end

#put(str) ⇒ Object

Just alias for update_method=



175
176
177
# File 'lib/exedb.rb', line 175

def put(str)
  self.update_method=(str)
end

#updateObject

Force command execution. If another instance with the same command is in progress, no new execution will be started



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# 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

#update_in_progress?Boolean

Returns:

  • (Boolean)


226
227
228
229
230
231
232
233
234
235
236
237
# 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

#update_method=(str) ⇒ Object

Replace executing command



167
168
169
170
171
172
# 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