Module: FileUtils

Defined in:
lib/nub/fileutils.rb

Overview

Monkey patch FileUtils with some useful methods

Class Method Summary collapse

Class Method Details

.digests_changed?(key, digestfile, files) ⇒ Boolean

Check if any digests have changed based on the given files

Parameters:

  • key (yaml)

    yalm section heading to give digests

  • digestfile (string)

    digest file to check against

  • files (array)

    files to get digests for

Returns:

  • (Boolean)


36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# File 'lib/nub/fileutils.rb', line 36

def self.digests_changed?(key, digestfile, files)
  files = [files] if files.is_a?(String)
  newfiles, modifiedfiles, deletedfiles = [], [], []

  # Since layers are stacked and the digest file may be from a lower
  # layer this should be ignored unless the layer matches
  if (digests = File.exist?(digestfile) ? YAML.load_file(digestfile)[key] : nil)

    # New files: in files but not yaml
    newfiles = files.select{|x| not digests[x]}

    # Delete files: in yaml but not files
    deletedfiles = digests.map{|k,v| k}.select{|x| not files.include?(x)}

    # Modified files: in both but digest is different
    modifiedfiles = files.select{|x| digests[x] and
      Digest::MD5.hexdigest(File.binread(x)) != digests[x]}
  else
    newfiles = files
  end

  return newfiles, modifiedfiles, deletedfiles
end

.exec?(name, path: nil) ⇒ Boolean

Check PATH for executable

Parameters:

  • name (String)

    name of executable to find

  • path (Array) (defaults to: nil)

    path to search

Returns:

  • (Boolean)

    path of executable or nil



65
66
67
68
69
70
71
72
73
# File 'lib/nub/fileutils.rb', line 65

def self.exec?(name, path:nil)
  (path || ENV['PATH'].split(File::PATH_SEPARATOR).uniq).each{|dir|
    target = File.join(dir, name)
    if stat = File.stat(target) rescue nil
      return target if stat.file? && stat.executable?
    end
  }
  return nil
end

.inc_version(path, regex, major: false, minor: false, rev: true, override: nil) ⇒ Object

Increment SemVer formatted versions

Parameters:

  • path (String)

    file to increment version for

  • regex (Regex)

    capture pattern for version match

  • major (Bool) (defaults to: false)

    true then increment the major

  • minor (Bool) (defaults to: false)

    true then increment the minor

  • rev (Bool) (defaults to: true)

    true then increment the revision

  • override (String) (defaults to: nil)

    value to use for version if set

Returns:

  • new version



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
# File 'lib/nub/fileutils.rb', line 83

def self.inc_version(path, regex, major:false, minor:false, rev:true, override:nil)
  version = nil
  self.update(path){|data|

    # Increment version
    line = data.split("\n").find{|x| x[regex, 1]}
    if ver = line[regex, 1]
      new_ver = nil
      if not override
        new_ver = ver.strip.split('.')
        new_ver[0] = new_ver[0].to_i + 1 if major
        new_ver[1] = new_ver[1].to_i + 1 if minor
        new_ver[2] = new_ver[2].to_i + 1 if rev
        new_ver = new_ver * '.'
      else
        new_ver = override
      end

      # Modify the original line
      version = new_ver
      newline = line.gsub(ver, new_ver)
      data.gsub!(line, newline)
    end
  }

  return version
end

.insert(file, values, regex: nil, offset: 1) ⇒ Object

Insert into a file Location of insert is determined by the given regex and offset. Append is used if no regex is given.

Parameters:

  • file (string)

    path of file to modify

  • values (array)

    string or list of string values to insert

  • regex (string) (defaults to: nil)

    regular expression for location, not used if offset is nil

  • offset (int) (defaults to: 1)

    offset insert location by this amount for regexs



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
# File 'lib/nub/fileutils.rb', line 119

def self.insert(file, values, regex:nil, offset:1)
  return false if not values or values.empty?

  values = [values] if values.is_a?(String)
  FileUtils.touch(file) if not File.exist?(file)

  changed = self.update(file){|data|
    lines = data.split("\n")
    lastline = lines[-1].dup

    # Match regex for insert location
    regex = Regexp.new(regex) if regex.is_a?(String)
    if i = regex ? lines.index{|x| x =~ regex} : lines.size
      i += offset if regex and offset

      # Insert at offset
      values.each{|x| lines.insert(i, x) and i += 1}

      # Change data inline
      newdata = lines * "\n"
      newdata += "\n" if lines[-1] != lastline
      data.gsub!(data, newdata)
    end
  }

  return changed
end

.replace(file, regex, value) ⇒ Object

Replace in file

Parameters:

  • file (string)

    file to modify

  • regex (string)

    regular expression match on

  • value (string)

    regular expression string replacement



153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/nub/fileutils.rb', line 153

def self.replace(file, regex, value)
  changed = self.update(file){|data|
    lines = data.split("\n")

    # Search replace
    regex = Regexp.new(regex) if regex.is_a?(String)
    lines.each{|x| x.gsub!(regex, value)}

    # Change data inline
    newdata = lines * "\n"
    newdata += "\n" if data[-1] == "\n"
    data.gsub!(data, newdata)
  }

  return changed
end

.resolve(file, vars) ⇒ Object

Resolve template

Parameters:

  • file (string)

    file to resolve templates for

  • vars (hash/ostruct)

    templating variables to use while resolving



174
175
176
# File 'lib/nub/fileutils.rb', line 174

def self.resolve(file, vars)
  return self.update(file){|data| data.erb!(vars)}
end

.update(filename) ⇒ Object

Update the file using a block, revert on failure. Use this for file edits. Block is passed in the file data

Parameters:

  • filename (String)

    name of the file to change

Returns:

  • true on change



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/nub/fileutils.rb', line 182

def self.update(filename)
  changed = false
  block = Proc.new # Most efficient way to get block

  begin
    data = nil
    File.open(filename, 'r+'){|f|
      data = f.read
      data_orig = data.dup

      # Call block
      block.call(data)
      changed |= data_orig != data

      # Save the changes back to the file
      if changed
        f.seek(0)
        f.truncate(0)
        f << data
      end
    }
  rescue Exception => e
    # Revert back to the original incase of failure
    File.open(filename, 'w'){|f| f << data} if data
    raise
  end

  return changed
end

Check copyright and update if required

Parameters:

  • path (String)

    path of the file to update

  • copyright (String)

    copyright match string e.g. Copyright ©

  • year (String) (defaults to: Time.now.year)

    year to add if needed

Returns:

  • true on change



217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
# File 'lib/nub/fileutils.rb', line 217

def self.update_copyright(path, copyright, year:Time.now.year)
  set_copyright = false

  changed = self.update(path){|data|
    line = data.split("\n").find{|x| x[/#{Regexp.quote(copyright)}/]}
    if !set_copyright && line =~ /#{Regexp.quote(copyright)}/
      set_copyright = true
      _year = line[/#{Regexp.quote(copyright)}\s+((\d{4}\s)|(\d{4}-\d{4})).*/, 1].strip
      if _year.include?("-")
        years = _year.split("-")
        if years.last != year
          newline = line.gsub(_year, "#{years.first}-#{year}")
          data.gsub!(line, newline)
        end
      elsif prev_year = _year != year.to_s ? year.to_i - 1 : nil
        newline = line.gsub(_year.to_s, "#{prev_year}-#{year}")
        data.gsub!(line, newline)
      end
    end
  }
  return changed
end

.update_digests(key, digestfile, files) ⇒ Object

Generate digests for array of files given and save them

Parameters:

  • key (yaml)

    yalm section heading to give digests

  • digestfile (string)

    digest file to update

  • files (array)

    files to get digests for



244
245
246
247
248
249
250
251
252
253
# File 'lib/nub/fileutils.rb', line 244

def self.update_digests(key, digestfile, files)
  files = [files] if files.is_a?(String)

  # Build up digests structure
  digests = {}
  files.each{|x| digests[x] = Digest::MD5.hexdigest(File.binread(x)) if File.exist?(x)}

  # Write out digests structure as yaml with named header
  File.open(digestfile, 'w'){|f| f.puts({key => digests}.to_yaml)}
end

.version(path, regex) ⇒ Object

Get SemVer formatted versions

Parameters:

  • path (String)

    file to increment version for

  • regex (Regex)

    capture pattern for version match

Returns:

  • version



259
260
261
262
263
264
265
266
267
268
# File 'lib/nub/fileutils.rb', line 259

def self.version(path, regex)
  if File.file?(path)
    File.readlines(path).each{|line|
      if ver = line[regex, 1]
        return ver
      end
    }
  end
  return nil
end