Class: RunLoop::Directory

Inherits:
Object
  • Object
show all
Defined in:
lib/run_loop/directory.rb

Overview

Class for performing operations on directories.

Class Method Summary collapse

Class Method Details

.directory_digest(path, options = {}) ⇒ Object

Computes the digest of directory.

Parameters:

  • path

    A path to a directory.

  • options (defaults to: {})

    Control the behavior of the method.

Options Hash (options):

  • :handle_errors_by (Object) — default: :raising

    Controls what to do when File.read causes an error. The default behavior is to raise. Other options are: :logging and :ignoring. Logging will only happen if running in debug mode.

Raises:

  • ArgumentError When ‘path` is not a directory or path does not exist.

  • ArgumentError When options has n unsupported value.



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
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
# File 'lib/run_loop/directory.rb', line 33

def self.directory_digest(path, options={})
  default_options = {
    :handle_errors_by => :raising
  }

  merged_options = default_options.merge(options)
  handle_errors_by = merged_options[:handle_errors_by]
  unless [:raising, :logging, :ignoring].include?(handle_errors_by)
    raise ArgumentError,
%Q{Expected :handle_errors_by to be :raising, :logging, or :ignoring;
found '#{handle_errors_by}'
}
  end

  unless File.exist?(path)
    raise ArgumentError, "Expected '#{path}' to exist"
  end

  unless File.directory?(path)
    raise ArgumentError, "Expected '#{path}' to be a directory"
  end

  entries = self.recursive_glob_for_entries(path)

  if entries.empty?
    raise ArgumentError, "Expected a non-empty dir at '#{path}' found '#{entries}'"
  end

  debug = RunLoop::Environment.debug?

  sha = OpenSSL::Digest::SHA256.new
  entries.each do |file|
    unless self.skip_file?(file, 'SHA1', debug)
      begin
        sha << File.read(file)
      rescue => e
        case handle_errors_by
        when :logging
          message =
%Q{RunLoop::Directory.directory_digest raised an error:

     #{e}

   while trying to find the SHA of this file:

     #{file}

   This is not a fatal error; it can be ignored.
}
          RunLoop.log_debug(message)
        when :raising
          raise e.class, e.message
        when :ignoring
           # nop
        else
           # nop
        end
      end
    end
  end
  sha.hexdigest
end

.iterate_for_size(entries) ⇒ Object



168
169
170
171
172
173
174
175
176
177
# File 'lib/run_loop/directory.rb', line 168

def self.iterate_for_size(entries)
  debug = RunLoop::Environment.debug?
  size = 0
  entries.each do |file|
    unless self.skip_file?(file, "SIZE", debug)
      size = size + File.size(file)
    end
  end
  size
end

.recursive_glob_for_entries(base_dir) ⇒ Object

Dir.glob ignores files that start with ‘.’, but we often need to find dotted files and directories.

Ruby 2.* does the right thing by ignoring ‘..’ and ‘.’.

Ruby < 2.0 includes ‘..’ and ‘.’ in results which causes problems for some of run-loop’s internal methods. In particular ‘reset_app_sandbox`.



16
17
18
19
20
# File 'lib/run_loop/directory.rb', line 16

def self.recursive_glob_for_entries(base_dir)
  Dir.glob("#{base_dir}/{**/.*,**/*}").select do |entry|
    !(entry.end_with?('..') || entry.end_with?('.'))
  end
end

.size(path, format) ⇒ Object



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/run_loop/directory.rb', line 96

def self.size(path, format)

  allowed_formats = [:bytes, :kb, :mb, :gb]
  unless allowed_formats.include?(format)
    raise ArgumentError, "Expected '#{format}' to be one of #{allowed_formats.join(', ')}"
  end

  unless File.exist?(path)
    raise ArgumentError, "Expected '#{path}' to exist"
  end

  unless File.directory?(path)
    raise ArgumentError, "Expected '#{path}' to be a directory"
  end

  entries = self.recursive_glob_for_entries(path)

  if entries.empty?
    raise ArgumentError, "Expected a non-empty dir at '#{path}' found '#{entries}'"
  end

  size = self.iterate_for_size(entries)

  case format
    when :bytes
      size
    when :kb
      size/1000.0
    when :mb
      size/1000.0/1000.0
    when :gb
      size/1000.0/1000.0/1000.0
    else
      # Not expected to reach this.
      size
  end
end

.skip_file?(file, task, debug) ⇒ Boolean

Returns:

  • (Boolean)


136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/run_loop/directory.rb', line 136

def self.skip_file?(file, task, debug)
  skip = false
  if File.directory?(file)
    # Skip directories
    skip = true
  elsif !Pathname.new(file).exist?
    # Skip broken symlinks
    skip = true
  elsif !File.exist?(file)
    # Skip files that don't exist
    skip = true
  else
    case File.ftype(file)
      when 'fifo'
        RunLoop.log_warn("#{task} IS SKIPPING FIFO #{file}") if debug
        skip = true
      when 'socket'
        RunLoop.log_warn("#{task} IS SKIPPING SOCKET #{file}") if debug
        skip = true
      when 'characterSpecial'
        RunLoop.log_warn("#{task} IS SKIPPING CHAR SPECIAL #{file}") if debug
        skip = true
      when 'blockSpecial'
        skip = true
        RunLoop.log_warn("#{task} SKIPPING BLOCK SPECIAL #{file}") if debug
      else

    end
  end
  skip
end