File: cached_glob.rb

package info (click to toggle)
ruby-rgen 0.10.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,428 kB
  • sloc: ruby: 11,344; xml: 1,368; yacc: 72; makefile: 10
file content (67 lines) | stat: -rw-r--r-- 1,827 bytes parent folder | download | duplicates (11)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
module RGen

module Util

# WARNING: the mechanism of taking timestamps of directories in order to find out if the
# content has changed doesn't work reliably across all kinds of filesystems
#
class CachedGlob

  def initialize(dir_glob, file_glob)
    @dir_glob = dir_glob
    @file_glob = file_glob
    @root_dirs = []
    @dirs = {}
    @files = {}
    @timestamps = {}
  end

  # returns all files contained in directories matched by +dir_glob+ which match +file_glob+.
  # +file_glob+ must be relative to +dir_glob+.
  # dir_glob "*/a" with file_glob "**/*.txt" is basically equivalent with Dir.glob("*/a/**/*.txt")
  # the idea is that the file glob will only be re-eavluated when the content of one of the 
  # directories matched by dir_glob has changed.
  # this will only be faster than a normal Dir.glob if the number of dirs matched by dir_glob is
  # relatively large and changes in files affect only a few of them at a time.
  def glob
    root_dirs = Dir.glob(@dir_glob)
    (@root_dirs - root_dirs).each do |d|
      remove_root_dir(d)
    end
    (@root_dirs & root_dirs).each do |d|
      update_root_dir(d) if dir_changed?(d)
    end
    (root_dirs - @root_dirs).each do |d|
      update_root_dir(d)
    end
    @root_dirs = root_dirs
    @root_dirs.sort.collect{|d| @files[d]}.flatten
  end

  private

  def dir_changed?(dir)
    @dirs[dir].any?{|d| File.mtime(d) != @timestamps[dir][d]}
  end

  def update_root_dir(dir)
    @dirs[dir] = Dir.glob(dir+"/**/")
    @files[dir] = Dir.glob(dir+"/"+@file_glob)
    @timestamps[dir] = {}
    @dirs[dir].each do |d|
      @timestamps[dir][d] = File.mtime(d)
    end
  end

  def remove_root_dir(dir)
    @dirs.delete(dir)
    @files.delete(dir)
    @timestamps.delete(dir)
  end

end

end

end