Class: File

Inherits:
Object show all
Defined in:
lib/core/facets/file/ext.rb,
lib/core/facets/file/null.rb,
lib/core/facets/file/write.rb,
lib/core/facets/file/append.rb,
lib/core/facets/file/create.rb,
lib/core/facets/file/rewrite.rb,
lib/core/facets/file/sanitize.rb,
lib/core/facets/file/rootname.rb,
lib/core/facets/file/read_list.rb,
lib/core/facets/file/atomic_id.rb,
lib/core/facets/file/split_all.rb,
lib/core/facets/file/split_root.rb,
lib/core/facets/file/writelines.rb,
lib/core/facets/file/read_binary.rb,
lib/core/facets/file/common_path.rb,
lib/core/facets/file/atomic_open.rb,
lib/core/facets/file/atomic_write.rb,
lib/standard/facets/yaml/file.rb

Class Method Summary collapse

Class Method Details

.append(file, str) ⇒ Object

Append to a file.

CREDIT: George Moschovitis



7
8
9
10
11
# File 'lib/core/facets/file/append.rb', line 7

def self.append(file, str)
  File.open(file, 'ab') { |f|
    f << str
  }
end

.atomic_idObject



4
5
6
7
# File 'lib/core/facets/file/atomic_id.rb', line 4

def self.atomic_id
  @atomic_id ||= 0
  @atomic_id += 1
end

.atomic_open(file_name, mode = "r", temp_dir = nil, &block) ⇒ Object

Same as `File.open`, but acts on a temporary copy of named file, copying the file back to the original on completion.

Uncommon:

  • require 'facets/fileutils/atomic_open'



13
14
15
16
17
18
19
20
21
22
23
24
# File 'lib/core/facets/file/atomic_open.rb', line 13

def self.atomic_open(file_name, mode="r", temp_dir=nil, &block)
  temp_dir  = temp_dir || Dir.tmpdir
  temp_file = Tempfile.new("#{aomtic_id}-" + basename(file_name), temp_dir)

  if File.exist?(file_name)
    FileUtils.cp(file_name, temp_file)
  end

  open(temp_file, mode, &block)

  FileUtils.cp(temp_file, file_name)
end

.atomic_write(file_name, temp_dir = nil) {|temp_file| ... } ⇒ Object

Write to a file atomically. Useful for situations where you don't want other processes or threads to see half-written files.

File.atomic_write("important.txt") do |file|
  file.write("hello")
end

If your temporary directory is not on the same filesystem as the file you're trying to write, you can provide a different temporary directory.

File.atomic_write("important.txt", "tmp") do |file|
  file.write("hello")
end

NOTE: This method is not a common core extension and is not loaded automatically when using require 'facets'.

CREDIT: David Heinemeier Hansson

Yields:

  • (temp_file)

Uncommon:

  • require 'facets/fileutils/atomic_write'



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
# File 'lib/core/facets/file/atomic_write.rb', line 32

def self.atomic_write(file_name, temp_dir=nil)
  temp_dir  = temp_dir || Dir.tmpdir
  temp_file = Tempfile.new(basename(file_name), temp_dir)

  yield temp_file
  temp_file.close

  begin
    ## Get original file permissions
    old_stat = stat(file_name)
  rescue Errno::ENOENT
    ## No old permissions, write a temp file to determine the defaults
    check_name = join(dirname(file_name), ".permissions_check.#{Thread.current.object_id}.#{Process.pid}.#{rand(1000000)}")
    open(check_name, "w") { }
    old_stat = stat(check_name)
    unlink(check_name)
  end

  ## Overwrite original file with temp file
  FileUtils.mv(temp_file.path, file_name)

  ## Set correct permissions on new file
  chown(old_stat.uid, old_stat.gid, file_name)
  chmod(old_stat.mode, file_name)
end

.common_path(*paths) ⇒ Object

Given an array of path strings, find the longest common prefix path.

Author:

  • Aaron Gibralter



6
7
8
9
10
11
12
13
14
15
# File 'lib/core/facets/file/common_path.rb', line 6

def self.common_path(*paths)
  return paths.first if paths.length <= 1

  arr = paths.sort
  f = arr.first.split('/')
  l = arr.last.split('/')
  i = 0
  i += 1 while f[i] == l[i] && i <= f.length
  f.slice(0, i).join('/')
end

.create(path, str = '', &blk) ⇒ Object

Creates a new file, or overwrites an existing file, and writes a string into it. Can also take a block just like File#open, which is yielded after the string is writ.

str = 'The content for the file'
File.create('myfile.txt', str)

CREDIT: George Moschovitis



13
14
15
16
17
18
# File 'lib/core/facets/file/create.rb', line 13

def self.create(path, str='', &blk)
  open(path, 'wb') do |f|
    f << str
    blk.call(f) if blk
  end
end

.ext(filename, new_ext = nil) ⇒ Object

Takes a file name string and returns or changes its extension.

Without a new extension argument, returns the extension of the file name. In this respect #ext is like #extname, but unlike #extname it does not include the dot prefix.

With a new extension argument, changes the exension of the file name to the new extension and returns it.

Examples

File.ext('file.rb')          # => 'rb'
File.ext('file.rb', 'txt')   # => 'file.txt'
File.ext('file.rb', '.txt')  # => 'file.txt'
File.ext('file.rb', '')      # => 'file'

This method can be used with String#file for more object-oriented notation:

'file.rb'.file.ext('txt')    # => 'file.txt'

CREDIT: Lavir the Whiolet



25
26
27
28
29
30
31
32
33
# File 'lib/core/facets/file/ext.rb', line 25

def self.ext(filename, new_ext=nil)
  old_ext = extname(filename)
  if new_ext == nil
    old_ext.sub(/^\./, '')
  else
    new_ext = '.' + new_ext unless (new_ext.empty? || new_ext[0,1] == '.')
    filename.chomp(old_ext) + new_ext
  end
end

.nullObject

Platform dependent null device.

CREDIT: Daniel Burger



7
8
9
10
11
12
13
14
15
16
17
18
# File 'lib/core/facets/file/null.rb', line 7

def self.null
  case RUBY_PLATFORM
  when /mswin/i
    'NUL'
  when /amiga/i
    'NIL:'
  when /openvms/i
    'NL:'
  else
    '/dev/null'
  end
end

.read_binary(fname) ⇒ Object

Read in a file as binary data.

Assuming we had a binary file 'binary.dat'.

File.read_binary('binary.dat')

CREDIT: George Moschovitis



11
12
13
14
15
# File 'lib/core/facets/file/read_binary.rb', line 11

def self.read_binary(fname)
  open(fname, 'rb') do |f|
    return f.read
  end
end

.read_list(filepath, options = {}) ⇒ Object

Reads in a file, removes blank lines and removes lines starting with '#' and then returns an array of all the remaining lines.

Thr remark indicator can be overridden via the :omit: option, which can be a regualar expression or a string that is match against the start of a line.

CREDIT: Trans



12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/core/facets/file/read_list.rb', line 12

def self.read_list(filepath, options={})
  chomp = options[:chomp]
  omit  = case options[:omit]
          when Regexp
            omit
          when nil
            /^\s*\#/
          else
            /^\s*#{Regexp.escape(omit)}/
          end

  list = []
  readlines(filepath).each do |line|
    line = line.strip.chomp(chomp)
    next if line.empty?
    next if omit === line
    list << line
  end
  list
end

.rewrite(name, mode = "") ⇒ Object

Opens a file as a string and writes back the string to the file at the end of the block.

Returns the number of written bytes or nil if the file wasn't modified.

Note that the file will even be written back in case the block raises an exception.

Mode can either be “b” or “+” and specifies to open the file in binary mode (no mapping of the plattform's newlines to “n” is done) or to append to it.

Assuming we had a file 'message.txt' and had a binary file 'binary.dat'.

# Reverse contents of "message.txt"
File.rewrite("message.txt") { |str| str.reverse }

# Replace "foo" by "bar" in "binary.dat".
File.rewrite("binary.dat", "b") { |str| str.gsub("foo", "bar") }

IMPORTANT: The old version of this method required in place modification of the file string. The new version will write whatever the block returns instead!!!

CREDIT: George Moschovitis



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/core/facets/file/rewrite.rb', line 30

def self.rewrite(name, mode = "") #:yield:
  unless block_given?
    raise(ArgumentError, "Need to supply block to File.rewrite")
  end

  if mode.is_a?(Numeric) then
    flag, mode = mode, ""
    mode += "b" if flag & File::Constants::BINARY != 0
    mode += "+" if flag & File::Constants::APPEND != 0
  else
    mode.delete!("^b+")
  end

  old_str = open(name, "r#{mode}") { |file| file.read } #rescue ""
  old_str = old_str.clone

  begin
    new_str = yield(old_str)
  ensure
    if old_str != new_str
      open(name, "w#{mode}") { |file| file.write(new_str) }
    end
  end
end

.rewrite!(name, mode = "") ⇒ Object

In place version of #rewrite. This version of method requires that the string be modified in place within the block.

# Reverse contents of "message"
File.rewrite("message.txt") { |str| str.reverse! }

# Replace "foo" by "bar" in "binary"
File.rewrite("binary.dat", "b") { |str| str.gsub!("foo", "bar") }


64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
# File 'lib/core/facets/file/rewrite.rb', line 64

def self.rewrite!(name, mode = "") #:yield:
  unless block_given?
    raise(ArgumentError, "Need to supply block to File.rewrite")
  end

  if mode.is_a?(Numeric) then
    flag, mode = mode, ""
    mode += "b" if flag & File::Constants::BINARY != 0
    mode += "+" if flag & File::Constants::APPEND != 0
  else
    mode.delete!("^b+")
  end

  old_str = open(name, "r#{mode}") { |file| file.read } #rescue ""
  new_str = old_str.clone

  begin
    yield(new_str)
  ensure
    if old_str != new_str
      open(name, "w#{mode}") { |file| file.write(new_str) }
    end
  end
end

.rootname(path) ⇒ Object

Returns only the first portion of the directory of a file path name.

File.rootname('lib/jump.rb')  #=> 'lib'
File.rootname('/jump.rb')     #=> '/'
File.rootname('jump.rb')      #=> '.'

CREDIT: Trans



12
13
14
15
16
17
18
19
20
# File 'lib/core/facets/file/rootname.rb', line 12

def self.rootname(path)
  # -- this should be fairly robust
  path_re = Regexp.new('[' + Regexp.escape(File::Separator + %q{\/}) + ']')

  head, _ = path.split(path_re, 2)
  return '.' if path == head
  return '/' if head.empty?
  return head
end

.sanitize(filename) ⇒ Object

Cleans up a filename to ensure it will work on a filesystem.

File.sanitize("yo+baby!")   #=> 'yo+baby_'
File.sanitize(".what&up")  #=> '.what_up'

CREDIT: George Moschovitis



10
11
12
13
14
15
# File 'lib/core/facets/file/sanitize.rb', line 10

def self.sanitize(filename)
  filename = File.basename(filename.gsub("\\", "/")) # work-around for IE
  filename.gsub!(/[^a-zA-Z0-9\.\-\+_]/,"_")
  filename = "_#{filename}" if filename =~ /^\.+$/
  filename
end

.split_all(path) ⇒ Object

Splits a file path into an array of individual path components. This differs from File.split, which divides the path into only two parts, directory path and basename.

File.split_all("a/b/c")  #=> ['a', 'b', 'c']

CREDIT: Trans



11
12
13
14
15
16
# File 'lib/core/facets/file/split_all.rb', line 11

def self.split_all(path)
  head, tail = File.split(path)
  return [tail] if head == '.' || tail == '/'
  return [head, tail] if head == '/'
  return split_all(head) + [tail]
end

.split_root(path) ⇒ Object

Return the head of path from the rest of the path.

File.split_root('etc/xdg/gtk')  #=> ['etc', 'xdg/gtk']


7
8
9
10
# File 'lib/core/facets/file/split_root.rb', line 7

def self.split_root(path)
  path_re = Regexp.new('[' + Regexp.escape(File::Separator + %q{\/}) + ']')
  path.split(path_re, 2)
end

.write(path, data) ⇒ Object

Writes the given data to the given path and closes the file. This is done in binary mode, complementing IO.read in standard Ruby.

str = 'The content for the file'
File.write('write.txt', str)

Returns the number of bytes written.

CREDIT: Gavin Sinclair



15
16
17
18
19
# File 'lib/core/facets/file/write.rb', line 15

def self.write(path, data)
  File.open(path, "wb") do |file|
    return file.write(data)
  end
end

.writelines(path, data) ⇒ Object

Writes the given array of data to the given path and closes the file. This is done in binary mode, complementing IO.readlines in standard Ruby.

Note that readlines (the standard Ruby method) returns an array of lines with newlines intact, whereas writelines uses puts, and so appends newlines if necessary. In this small way, readlines and writelines are not exact opposites.

data = ['The content', ['for the file']]
File.writelines('writelines.txt', data)

Returns number of lines written.

CREDIT: Noah Gibbs, Gavin Sinclair



19
20
21
22
23
24
# File 'lib/core/facets/file/writelines.rb', line 19

def self.writelines(path, data)
  File.open(path, "wb") do |file|
    file.puts(data)
  end
  data.size
end

.yaml?(file) ⇒ Boolean

Same as `YAML.file?(file)`.

File.yaml?('project.yaml')  #=> true
File.yaml?('project.xml')   #=> false


36
37
38
# File 'lib/standard/facets/yaml/file.rb', line 36

def self.yaml?(file)
  YAML.file?(file)
end