Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I detect whether a file is writeable in Ruby?

The function should return false if any of these are true:

  1. The complete path is an existing directory.
  2. The path is not a legal file name (invalid characters, too long, etc.)
  3. The path refers to an existing file that is not writeable by current user.
  4. The path includes any directory segments that do not already exist.

The function should return true if all of these are true:

  1. All path segments except the file name are already existing directories.
  2. The file either does not already exist or exists and is writable by the current user.
  3. The directory that will hold the file is writable by the current user.
  4. The path segments and filename are not too long and are composed only of valid characters for filenames.
  5. A relative or absolute path is specified.

The function must work on MAC OSX using Ruby 1.9.3.

The following method returns false when the file does not already exist and should be writable, even though I am running it from a subdirectory of my own user directory:

File.writable?('./my-file-name')

I will accept a solution involving calling file open and catching the exception if a write fails, though I prefer to not rely on exceptions.

Here is what I have so far, but it does not handle some edge cases:

  def file_writable?(filename)
    return false if (filename || '').size == 0
    return false if File.directory?(filename)
    return false if File.exists?(filename) && !File.writable(filename)
    return true;
  end

I still do not know how to conveniently test if any directory segments are missing, or if weird characters are in the name, and some other cases.

like image 695
Paul Chernoch Avatar asked May 07 '26 17:05

Paul Chernoch


1 Answers

Ruby includes Pathname in its std-lib, which is a wrapper around several other file-oriented classes:

All functionality from File, FileTest, and some from Dir and FileUtils is included, in an unsurprising way. It is essentially a facade for all of these, and more.

A "pathname" can be a directory or a file.

Pathname doesn't do all the things you require, however it acts as a very convenient starting point. You can easily add the additional functionality you need since it is a wrapper, much easier than if you tried to implement a solution using the individual classes.

It's my go-to class when I have to do a lot of directory/file processing.

require 'pathname'  # => true

foo = Pathname.new('/usr/bin/ruby')  # => #<Pathname:/usr/bin/ruby>
foo.writable?                        # => false
foo.directory?                       # => false
foo.exist?                           # => true

tmp = Pathname.new('/tmp/foo')  # => #<Pathname:/tmp/foo>
tmp.write('bar')                # => 3
tmp.writable?                   # => true
tmp.directory?                  # => false
tmp.exist?                      # => true

bin = Pathname.new('/usr/bin')  # => #<Pathname:/usr/bin>
bin.writable?                   # => false
bin.directory?                  # => true
bin.exist?                      # => true

fake = Pathname.new('/foo/bar')  # => #<Pathname:/foo/bar>
fake.exist?                      # => false

You can't tell what components are missing from a directory, but normally we'd try to create it and rescue the exception if it occurs, dealing with permission errors. It wouldn't be hard to write code to look for a full directory path, then iterate though the chain of directories if the full-path doesn't exist, looking for each as a child of the previous one. Enumerable's find and find_all would be useful.

like image 151
the Tin Man Avatar answered May 09 '26 06:05

the Tin Man



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!