Class: RedParse::Cache

Inherits:
Object
  • Object
show all
Defined in:
lib/redparse/cache.rb

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(is_file, name, *params) ⇒ Cache

Returns a new instance of Cache.



26
27
28
29
30
31
32
33
# File 'lib/redparse/cache.rb', line 26

def initialize(is_file,name,*params)
  divider=params.index(:/)
  @input_id=params[0...divider]
  @output_id=params[divider+1..-1]
  @rubyversion=@input_id.pop
  @name=name
  @is_file=is_file
end

Class Method Details

.attempt_read(cache_fn, inputdigest, options, output_identity, want_file = false) ⇒ Object



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
# File 'lib/redparse/cache.rb', line 307

def Cache.attempt_read(cache_fn,inputdigest,options,output_identity,want_file=false)
  return if !File.exist? cache_fn
  
  #warn "...reading from cache file #{cache_fn}"
  #warn "...options=#{options.inspect}"

    outid=Digest::SHA2.hexdigest output_identity.join(',')

    cache_f=File.open(cache_fn,"rb")
    pos=cache_f.pos
    options=options.to_s

    encoder,saved_options,saved_inputdigest,saved_outid=read_trailer cache_f
    error=case
    when !encoder; "trailer not found"
    when saved_inputdigest!=inputdigest; "input changed"
    when saved_outid!=outid; "outputter changed"
    when saved_options!=options; "options changed from #{saved_options.inspect} to #{options.inspect}"
    end
    if error
      #warn "...cache read failed because #{error}"
      cache_f.close
      cache_f=nil
      return
    end
   
    case encoder
    when 'ascii'
      return cache_f if want_file
      return cache_f.read
    when 'Ron'
      begin
        require 'ron'
        return Ron.load( cache_f.read )
      rescue Exception=>e
        warn "#{e.class}: #{e}"
        warn "cache ron read failed for:#{cache_fn}"
        return nil
      end
    when 'Marshal'
      cache_f.pos=pos
      begin
        return Marshal.load( cache_f )
      rescue Exception=>e
        warn "#{e.class}: #{e}"
        warn "cache read failed for:#{cache_fn}"
        return nil
      end
    else 
      warn "unrecognized RedParse::Cache encoder type: #{encoder}"
      return nil
    end
end

.attempt_write(cache_fn, inputdigest, options, output_identity, result) ⇒ Object



361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
# File 'lib/redparse/cache.rb', line 361

def Cache.attempt_write(cache_fn,inputdigest,options,output_identity,result)
  #STDERR.write "...writing to cache file #{cache_fn}... "

  if result.respond_to? :sysread
    path=result.path
    begin
      FileUtils.move(path,cache_fn)
    rescue Exception
      FileUtils.copy(path,cache_fn)
    end
    encoder= "ascii"
    #STDERR.puts 'file'
    cache_f=File.open(cache_fn,"a")
  else
    cache_f=File.open(cache_fn,"wb")

    if String===result
      cache_f.write result
      encoder= "ascii"
      #STDERR.puts 'ascii'
    else begin
      Thread.current["Marshal.ignore_sclass"]=true
      Marshal.dump(result,cache_f)
      encoder= "Marshal"
      #STDERR.puts 'Marshal'
    rescue TypeError #dump failed
      #STDERR.write "Marshal failed => "
      cache_f.close
      cache_f=File.open(cache_fn,"wb")
      begin
        require 'ron'
        cache_f.write Ron.dump(result)
        encoder='Ron'
        #STDERR.puts "Ron"
      rescue Exception
        #STDERR.puts "Ron failed"
        File.unlink(cache_fn)
        return
      end
    ensure
      Thread.current["Marshal.ignore_sclass"]=nil
    end end
  end

  outid=Digest::SHA2.hexdigest output_identity.join(',')
  trailer= "\n#encoded with #{encoder}\n##{options}\n##{inputdigest}\n##{outid}\n"
  sz=trailer.size+2
  szsz=sz.to_s.size
  sz+=szsz
  sz+=1 unless szsz==sz.to_s.size
  trailer<< "##{sz}\n"
  fail 'bad trailer size' unless trailer[/^\#([0-9]+)\n\Z/,1].to_i==trailer.size
  cache_f.write trailer

  return result
ensure 
  cache_f.close if cache_f
  #STDERR.puts("...options=#{options.inspect}")
end

.cachename_for_file(name, output_type) ⇒ Object



261
262
263
264
265
266
267
268
269
270
271
272
# File 'lib/redparse/cache.rb', line 261

def Cache.cachename_for_file(name,output_type)
  cache_fn=File.join(File.dirname(name),CACHEDIRNAME,File.basename(name))+"."+output_type
  dir=File.dirname(cache_fn)
  begin
    Dir.mkdir dir unless File.exists?(dir)
    raise unless File.writable?(dir)
  rescue Exception #chosen dir is not writeable
    cache_fn=File.join(find_home,GLOBALCACHEDIRNAME,name)+"."+output_type
    FileUtils.mkdir_p( File.dirname( cache_fn ) )
  end
  return cache_fn
end

.cachename_for_str(str, options, output_type) ⇒ Object



274
275
276
277
278
279
280
281
282
283
284
285
# File 'lib/redparse/cache.rb', line 274

def Cache.cachename_for_str(str,options,output_type)
  options=options.to_a.sort_by{|k,v| k}.map{|k,v| k+'='+v} if Hash===options
  options=options.join(',') unless String===options
  #options.gsub!('%n','%%n'); options.gsub!("\n",'%n')
  #fail if /\n/===options
  #str=options+"\n"+str
  options=Digest::SHA2.hexdigest(options) if options.size>100
  digest=Digest::SHA2.hexdigest(str)
  cache_fn=File.join(find_home, GLOBALCACHEDIRNAME,"-e",options,digest)+"."+output_type
  FileUtils.mkdir_p( File.dirname( cache_fn ) )
  return cache_fn
end

.find_homeObject

Finds the user’s home directory. – Some comments from the ruby-talk list regarding finding the home directory:

I have HOME, USERPROFILE and HOMEDRIVE + HOMEPATH. Ruby seems
to be depending on HOME in those code samples. I propose that
it should fallback to USERPROFILE and HOMEDRIVE + HOMEPATH (at
least on Win32).

(originally stolen from rubygems) before trying env variables, try getpwuid, since the environment might have been cleansed (eg by a cgi server) or altered (eg by rubygems tests).



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
# File 'lib/redparse/cache.rb', line 115

def Cache.find_home
  begin
    return Etc.getpwuid.dir
  rescue Exception
    #do nothing
  end

  ['HOME', 'USERPROFILE'].each do |homekey|
    return ENV[homekey] if ENV[homekey]
  end

  if ENV['HOMEDRIVE'] && ENV['HOMEPATH'] then
    return "#{ENV['HOMEDRIVE']}#{ENV['HOMEPATH']}"
  end

  begin
    File.expand_path("~")
  rescue
    if File::ALT_SEPARATOR then
        "C:/"
    else
        "/"
    end
  end
end

.get(input, name, input_id, output_type, outputter_id) ⇒ Object



237
238
239
240
241
242
243
# File 'lib/redparse/cache.rb', line 237

def Cache.get input,name,input_id,output_type,outputter_id
  if String===input
    read_for_str(input,input_id,output_type,outputter_id)
  else
    read_for_file(input,name,input_id,output_type,outputter_id)
  end
end

.put(input, name, input_id, output_type, outputter_id, result) ⇒ Object



245
246
247
248
249
250
251
# File 'lib/redparse/cache.rb', line 245

def Cache.put input,name,input_id,output_type,outputter_id,result
  if String===input
    write_for_str(input,input_id,output_type,outputter_id,result)
  else
    write_for_file(input,name,input_id,output_type,outputter_id,result)
  end
end

.read(input, name, input_id, output_type, outputter_id) ⇒ Object



253
254
255
256
257
258
259
# File 'lib/redparse/cache.rb', line 253

def Cache.read input,name,input_id,output_type,outputter_id
  result=get input,name,input_id,output_type,outputter_id
  return result if result

  result=yield
  put input,name,input_id,output_type,outputter_id,result
end

.read_for_file(input, name, options, output_type, output_identity, &if_missing) ⇒ Object



421
422
423
424
425
426
427
428
429
430
431
# File 'lib/redparse/cache.rb', line 421

def Cache.read_for_file(input,name,options,output_type,output_identity,&if_missing)
  name=File.expand_path(name)
  inputdigest=Digest::SHA2.file(name).hexdigest
  cache_fn=cachename_for_file name,output_type
  result=attempt_read(cache_fn,inputdigest,options,output_identity,:want_file)
  return result if result

  if if_missing and result=if_missing.call
    write_for_file(input,name,options,output_type,output_identity,result)
  end
end

.read_for_str(input, options, output_type, output_identity, &if_missing) ⇒ Object



440
441
442
443
444
445
446
447
448
449
# File 'lib/redparse/cache.rb', line 440

def Cache.read_for_str(input,options,output_type,output_identity,&if_missing)
  inputdigest=Digest::SHA2.hexdigest(input)
  cache_fn= cachename_for_str input,options,output_type
  result=attempt_read(cache_fn,inputdigest,options,output_identity)
  return result if result

  if if_missing and result=if_missing.call
    write_for_str(input,options,output_type,output_identity,result)
  end
end

.read_trailer(f) ⇒ Object

encoder, options, inputhash, outputterhash, size



295
296
297
298
299
300
301
302
303
304
305
# File 'lib/redparse/cache.rb', line 295

def Cache.read_trailer(f)
  f.seek(-(9*3+2+1),IO::SEEK_END)
  size=f.read[/\n\# *([0-9]+) *\n\z/,1].to_i
  return if size.zero?
  f.seek(-size,IO::SEEK_END)
  buf=f.read
  return unless result=buf.match(HEADER_REX)
  result=result.to_a
  result.shift
  return result
end

.write_for_file(input, name, options, output_type, output_identity, result) ⇒ Object



433
434
435
436
437
438
# File 'lib/redparse/cache.rb', line 433

def Cache.write_for_file(input,name,options,output_type,output_identity,result)
  name=File.expand_path(name)
  inputdigest=Digest::SHA2.file(name).hexdigest
  cache_fn=cachename_for_file name,output_type
  attempt_write(cache_fn,inputdigest,options,output_identity,result)
end

.write_for_str(input, options, output_type, output_identity, result) ⇒ Object



451
452
453
454
455
# File 'lib/redparse/cache.rb', line 451

def Cache.write_for_str(input,options,output_type,output_identity,result)
  inputdigest=Digest::SHA2.hexdigest(input)
  cache_fn= cachename_for_str input,options,output_type
  attempt_write(cache_fn,inputdigest,options,output_identity,result)
end

Instance Method Details

#all_entry_filesObject



59
60
61
62
63
64
65
# File 'lib/redparse/cache.rb', line 59

def all_entry_files
  Dir[@homedir+"*"].select{|fn| 
    File.directory? fn 
  }.map{|dirname|
    Dir[dirname+"/*"]
  }.flatten
end

#cachedirObject



51
52
53
# File 'lib/redparse/cache.rb', line 51

def cachedir
  @homedir+@callersfile+"/"
end

#get(input, output_type = "parsed") ⇒ Object



215
216
217
218
219
220
221
222
# File 'lib/redparse/cache.rb', line 215

def get input,output_type="parsed"
  output_type=@rubyversion.to_s+output_type
  if @is_file
    Cache.read_for_file(input,@name,@input_id,output_type,@output_id)
  else
    Cache.read_for_str(input,@input_id,output_type,@output_id)
  end
end

#hash_of_input(input) ⇒ Object



143
144
145
146
147
148
149
# File 'lib/redparse/cache.rb', line 143

def hash_of_input input
  if IO===input
    hexdigest_of_file input
  else
    Digest::SHA2.hexdigest input
  end
end

#old_get(input) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/redparse/cache.rb', line 151

def old_get input
  hash=hash_of_input input
  cachefile=cachedir+hash
  if File.exist? cachefile
    result=File.open(cachefile,"rb"){|fd| 
      line=fd.readline
      fd.rewind
      if /#encoded with Ron\n/i===line
        begin
          require 'ron'
          Ron.load fd.read
        rescue Exception
          return nil
        end
      else
        begin
          Marshal.load fd
        rescue Exception=>e              
          warn "#{e.class}: #{e}"
          warn "cache read failed for:\n#{input}"
          return nil
        end
      end
    }

    begin
      t=Time.now
      File.utime(t,t,cachefile)
    rescue Exception
      File.open(cachefile,"a"){|fd| } #touch cache date
    end
    return result
  end
rescue EOFError,Errno::EACCES
  return nil
end

#old_initialize(*params) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/redparse/cache.rb', line 35

def old_initialize *params
  @callersfile=Digest::SHA2.hexdigest params.join(',')
  @homedir=find_home+"/.redparse/"
  Dir.mkdir @homedir unless File.exist? @homedir
  Dir.mkdir cachedir unless File.exist? cachedir
  saved_digest= File.open(@homedir+"/parserdigest","rb"){|fd| fd.read.chomp } if File.exist?(@homedir+"/parserdigest")
  actual_digest= @@saved_parser_digest ||= redparse_rb_hexdigest
  if saved_digest!=actual_digest
    File.unlink(*all_entry_files)        #flush cache
    File.open(@homedir+"/parserdigest","wb"){|fd| fd.puts actual_digest } #update saved digest
  end
  retire_old_entries
rescue Errno::EACCES
  #do nothing
end

#old_put(input, result) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
# File 'lib/redparse/cache.rb', line 188

def old_put input,result
  hash=hash_of_input input
  File.open(cachedir+hash, "wb"){|fd|
    begin
      Thread.current["Marshal.ignore_sclass"]=true
      Marshal.dump(result,fd)
    rescue TypeError #dump failed
      File.unlink cachedir+hash
      begin
        require 'ron'
        File.open(cachedir+hash, "wb"){|fd2|
          fd2.write "#encoded with Ron\n"
          fd2.write Ron.dump(result)
        }
      rescue Exception
        return
      end
    ensure
      Thread.current["Marshal.ignore_sclass"]=nil
    end
  }
rescue Exception=>e #dump failed
  warn "#{e.class}: #{e}"
  warn "cache write failed for:\n#{result.inspect}"
  File.unlink cachedir+hash
end

#put(input, result, output_type = "parsed") ⇒ Object



224
225
226
227
228
229
230
231
# File 'lib/redparse/cache.rb', line 224

def put input,result,output_type="parsed"
  output_type=@rubyversion.to_s+output_type
  if @is_file
    Cache.write_for_file(input,@name,@input_id,output_type,@output_id,result)
  else
    Cache.write_for_str(input,@input_id,output_type,@output_id,result)
  end
end