# class File::Stat

Objects of class `File::Stat` encapsulate common status information for `File` objects. The information is recorded at the moment the `File::Stat` object is created; changes made to the file after that point will not be reflected. `File::Stat` objects are returned by `IO#stat`, `File::stat`, `File#lstat`, and `File::lstat`. Many of these methods return platform-specific values, and not all values are meaningful on all systems. See also `Kernel#test`.

### Public Class Methods

new (p1)
Source

    static VALUE
    rb_stat_init(VALUE obj, VALUE fname)
    {
        struct stat st;

        FilePathValue(fname);
        fname = rb_str_encode_ospath(fname);
        if (STAT(StringValueCStr(fname), &st) == -1) {
            rb_sys_fail_path(fname);
        }

        struct rb_stat *rb_st;
        TypedData_Get_Struct(obj, struct rb_stat, &stat_data_type, rb_st);

        rb_st->stat = st;
        rb_st->initialized = true;

        return Qnil;
    }

    File::Stat.new(file_name)  -> stat

Create a `File::Stat` object for the given file name (raising an exception if the file doesn’t exist).

### Public Instance Methods

stat <=> other_stat → -1, 0, 1, nil
Source

    static VALUE
    rb_stat_cmp(VALUE self, VALUE other)
    {
        if (rb_obj_is_kind_of(other, rb_obj_class(self))) {
            struct timespec ts1 = stat_mtimespec(get_stat(self));
            struct timespec ts2 = stat_mtimespec(get_stat(other));
            if (ts1.tv_sec == ts2.tv_sec) {
                if (ts1.tv_nsec == ts2.tv_nsec) return INT2FIX(0);
                if (ts1.tv_nsec < ts2.tv_nsec) return INT2FIX(-1);
                return INT2FIX(1);
            }
            if (ts1.tv_sec < ts2.tv_sec) return INT2FIX(-1);
            return INT2FIX(1);
        }
        return Qnil;
    }

Compares `File::Stat` objects by comparing their respective modification times.

`nil` is returned if `other_stat` is not a `File::Stat` object

    f1 = File.new("f1", "w")
    sleep 1
    f2 = File.new("f2", "w")
    f1.stat <=> f2.stat   #=> -1

atime → time
Source

    static VALUE
    rb_stat_atime(VALUE self)
    {
        return stat_atime(get_stat(self));
    }

Returns the last access time for this file as an object of class `Time`.

    File.stat("testfile").atime   #=> Wed Dec 31 18:00:00 CST 1969

birthtime → time
Source

    static VALUE
    rb_stat_birthtime(VALUE self)
    {
        return stat_birthtime(get_stat(self));
    }

Returns the birth time for stat.

If the platform doesn’t have birthtime, raises `NotImplementedError`.

    File.write("testfile", "foo")
    sleep 10
    File.write("testfile", "bar")
    sleep 10
    File.chmod(0644, "testfile")
    sleep 10
    File.read("testfile")
    File.stat("testfile").birthtime   #=> 2014-02-24 11:19:17 +0900
    File.stat("testfile").mtime       #=> 2014-02-24 11:19:27 +0900
    File.stat("testfile").ctime       #=> 2014-02-24 11:19:37 +0900
    File.stat("testfile").atime       #=> 2014-02-24 11:19:47 +0900

blksize → integer or nil
Source

    static VALUE
    rb_stat_blksize(VALUE self)
    {
    #ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
        return ULONG2NUM(get_stat(self)->st_blksize);
    #else
        return Qnil;
    #endif
    }

Returns the native file system’s block size. Will return `nil` on platforms that don’t support this information.

    File.stat("testfile").blksize   #=> 4096

blockdev? → true or false
Source

    static VALUE
    rb_stat_b(VALUE obj)
    {
    #ifdef S_ISBLK
        if (S_ISBLK(get_stat(obj)->st_mode)) return Qtrue;

    #endif
        return Qfalse;
    }

Returns `true` if the file is a block device, `false` if it isn’t or if the operating system doesn’t support this feature.

    File.stat("testfile").blockdev?    #=> false
    File.stat("/dev/hda1").blockdev?   #=> true

blocks → integer or nil
Source

    static VALUE
    rb_stat_blocks(VALUE self)
    {
    #ifdef HAVE_STRUCT_STAT_ST_BLOCKS
    # if SIZEOF_STRUCT_STAT_ST_BLOCKS > SIZEOF_LONG
        return ULL2NUM(get_stat(self)->st_blocks);
    # else
        return ULONG2NUM(get_stat(self)->st_blocks);
    # endif
    #else
        return Qnil;
    #endif
    }

Returns the number of native file system blocks allocated for this file, or `nil` if the operating system doesn’t support this feature.

    File.stat("testfile").blocks   #=> 2

chardev? → true or false
Source

    static VALUE
    rb_stat_c(VALUE obj)
    {
        if (S_ISCHR(get_stat(obj)->st_mode)) return Qtrue;

        return Qfalse;
    }

Returns `true` if the file is a character device, `false` if it isn’t or if the operating system doesn’t support this feature.

    File.stat("/dev/tty").chardev?   #=> true

ctime → time
Source

    static VALUE
    rb_stat_ctime(VALUE self)
    {
        return stat_ctime(get_stat(self));
    }

Returns the change time for stat (that is, the time directory information about the file was changed, not the file itself).

Note that on Windows (NTFS), returns creation time (birth time).

    File.stat("testfile").ctime   #=> Wed Apr 09 08:53:14 CDT 2003

dev → integer
Source

    static VALUE
    rb_stat_dev(VALUE self)
    {
    #if SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_DEV_T
        return DEVT2NUM(get_stat(self)->st_dev);
    #elif SIZEOF_STRUCT_STAT_ST_DEV <= SIZEOF_LONG
        return ULONG2NUM(get_stat(self)->st_dev);
    #else
        return ULL2NUM(get_stat(self)->st_dev);
    #endif
    }

Returns an integer representing the device on which stat resides.

    File.stat("testfile").dev   #=> 774

dev_major → integer
Source

    static VALUE
    rb_stat_dev_major(VALUE self)
    {
    #if defined(major)
        return UINT2NUM(major(get_stat(self)->st_dev));
    #else
        return Qnil;
    #endif
    }

Returns the major part of `File_Stat#dev` or `nil`.

    File.stat("/dev/fd1").dev_major   #=> 2
    File.stat("/dev/tty").dev_major   #=> 5

dev_minor → integer
Source

    static VALUE
    rb_stat_dev_minor(VALUE self)
    {
    #if defined(minor)
        return UINT2NUM(minor(get_stat(self)->st_dev));
    #else
        return Qnil;
    #endif
    }

Returns the minor part of `File_Stat#dev` or `nil`.

    File.stat("/dev/fd1").dev_minor   #=> 1
    File.stat("/dev/tty").dev_minor   #=> 0

directory? → true or false
Source

    static VALUE
    rb_stat_d(VALUE obj)
    {
        if (S_ISDIR(get_stat(obj)->st_mode)) return Qtrue;
        return Qfalse;
    }

Returns `true` if stat is a directory, `false` otherwise.

    File.stat("testfile").directory?   #=> false
    File.stat(".").directory?          #=> true

executable? → true or false
Source

    static VALUE
    rb_stat_x(VALUE obj)
    {
        struct stat *st = get_stat(obj);

    #ifdef USE_GETEUID
        if (geteuid() == 0) {
            return RBOOL(st->st_mode & S_IXUGO);
        }
    #endif
    #ifdef S_IXUSR
        if (rb_stat_owned(obj))
            return RBOOL(st->st_mode & S_IXUSR);
    #endif
    #ifdef S_IXGRP
        if (rb_stat_grpowned(obj))
            return RBOOL(st->st_mode & S_IXGRP);
    #endif
    #ifdef S_IXOTH
        if (!(st->st_mode & S_IXOTH)) return Qfalse;
    #endif
        return Qtrue;
    }

Returns `true` if stat is executable or if the operating system doesn’t distinguish executable files from nonexecutable files. The tests are made using the effective owner of the process.

    File.stat("testfile").executable?   #=> false

executable_real? → true or false
Source

    static VALUE
    rb_stat_X(VALUE obj)
    {
        struct stat *st = get_stat(obj);

    #ifdef USE_GETEUID
        if (getuid() == 0) {
            return RBOOL(st->st_mode & S_IXUGO);
        }
    #endif
    #ifdef S_IXUSR
        if (rb_stat_rowned(obj))
            return RBOOL(st->st_mode & S_IXUSR);
    #endif
    #ifdef S_IXGRP
        if (rb_group_member(get_stat(obj)->st_gid))
            return RBOOL(st->st_mode & S_IXGRP);
    #endif
    #ifdef S_IXOTH
        if (!(st->st_mode & S_IXOTH)) return Qfalse;
    #endif
        return Qtrue;
    }

Same as `executable?`, but tests using the real owner of the process.

file? → true or false
Source

    static VALUE
    rb_stat_f(VALUE obj)
    {
        if (S_ISREG(get_stat(obj)->st_mode)) return Qtrue;
        return Qfalse;
    }

Returns `true` if stat is a regular file (not a device file, pipe, socket, etc.).

    File.stat("testfile").file?   #=> true

ftype → string
Source

    static VALUE
    rb_stat_ftype(VALUE obj)
    {
        return rb_file_ftype(get_stat(obj));
    }

Identifies the type of stat. The return string is one of: “`file`”, “`directory`”, “`characterSpecial`”, “`blockSpecial`”, “`fifo`”, “`link`”, “`socket`”, or “`unknown`”.

    File.stat("/dev/tty").ftype   #=> "characterSpecial"

gid → integer
Source

    static VALUE
    rb_stat_gid(VALUE self)
    {
        return GIDT2NUM(get_stat(self)->st_gid);
    }

Returns the numeric group id of the owner of stat.

    File.stat("testfile").gid   #=> 500

grpowned? → true or false
Source

    static VALUE
    rb_stat_grpowned(VALUE obj)
    {
    #ifndef _WIN32
        if (rb_group_member(get_stat(obj)->st_gid)) return Qtrue;
    #endif
        return Qfalse;
    }

Returns true if the effective group id of the process is the same as the group id of stat. On Windows, returns `false`.

    File.stat("testfile").grpowned?      #=> true
    File.stat("/etc/passwd").grpowned?   #=> false

ino → integer
Source

    static VALUE
    rb_stat_ino(VALUE self)
    {
    #ifdef HAVE_STRUCT_STAT_ST_INOHIGH
        /* assume INTEGER_PACK_LSWORD_FIRST and st_inohigh is just next of st_ino */
        return rb_integer_unpack(&get_stat(self)->st_ino, 2,
                SIZEOF_STRUCT_STAT_ST_INO, 0,
                INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER|
                INTEGER_PACK_2COMP);
    #elif SIZEOF_STRUCT_STAT_ST_INO > SIZEOF_LONG
        return ULL2NUM(get_stat(self)->st_ino);
    #else
        return ULONG2NUM(get_stat(self)->st_ino);
    #endif
    }

Returns the inode number for stat.

    File.stat("testfile").ino   #=> 1083669

inspect → string
Source

    static VALUE
    rb_stat_inspect(VALUE self)
    {
        VALUE str;
        size_t i;
        static const struct {
            const char *name;
            VALUE (*func)(VALUE);
        } member[] = {
            {"dev",     rb_stat_dev},
            {"ino",     rb_stat_ino},
            {"mode",    rb_stat_mode},
            {"nlink",   rb_stat_nlink},
            {"uid",     rb_stat_uid},
            {"gid",     rb_stat_gid},
            {"rdev",    rb_stat_rdev},
            {"size",    rb_stat_size},
            {"blksize", rb_stat_blksize},
            {"blocks",  rb_stat_blocks},
            {"atime",   rb_stat_atime},
            {"mtime",   rb_stat_mtime},
            {"ctime",   rb_stat_ctime},
    #if defined(HAVE_STRUCT_STAT_ST_BIRTHTIMESPEC)
            {"birthtime",   rb_stat_birthtime},
    #endif
        };

        struct rb_stat* rb_st;
        TypedData_Get_Struct(self, struct rb_stat, &stat_data_type, rb_st);
        if (!rb_st->initialized) {
            return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
        }

        str = rb_str_buf_new2("#<");
        rb_str_buf_cat2(str, rb_obj_classname(self));
        rb_str_buf_cat2(str, " ");

        for (i = 0; i < sizeof(member)/sizeof(member[0]); i++) {
            VALUE v;

            if (i > 0) {
                rb_str_buf_cat2(str, ", ");
            }
            rb_str_buf_cat2(str, member[i].name);
            rb_str_buf_cat2(str, "=");
            v = (*member[i].func)(self);
            if (i == 2) {           /* mode */
                rb_str_catf(str, "0%lo", (unsigned long)NUM2ULONG(v));
            }
            else if (i == 0 || i == 6) { /* dev/rdev */
                rb_str_catf(str, "0x%"PRI_DEVT_PREFIX"x", NUM2DEVT(v));
            }
            else {
                rb_str_append(str, rb_inspect(v));
            }
        }
        rb_str_buf_cat2(str, ">");

        return str;
    }

Produce a nicely formatted description of stat.

    File.stat("/etc/passwd").inspect
       #=> "#<File::Stat dev=0xe000005, ino=1078078, mode=0100644,
       #    nlink=1, uid=0, gid=0, rdev=0x0, size=1374, blksize=4096,
       #    blocks=8, atime=Wed Dec 10 10:16:12 CST 2003,
       #    mtime=Fri Sep 12 15:41:41 CDT 2003,
       #    ctime=Mon Oct 27 11:20:27 CST 2003,
       #    birthtime=Mon Aug 04 08:13:49 CDT 2003>"

mode → integer
Source

    static VALUE
    rb_stat_mode(VALUE self)
    {
        return UINT2NUM(ST2UINT(get_stat(self)->st_mode));
    }

Returns an integer representing the permission bits of stat. The meaning of the bits is platform dependent; on Unix systems, see `stat(2)`.

    File.chmod(0644, "testfile")   #=> 1
    s = File.stat("testfile")
    sprintf("%o", s.mode)          #=> "100644"

mtime → time
Source

    static VALUE
    rb_stat_mtime(VALUE self)
    {
        return stat_mtime(get_stat(self));
    }

Returns the modification time of stat.

    File.stat("testfile").mtime   #=> Wed Apr 09 08:53:14 CDT 2003

nlink → integer
Source

    static VALUE
    rb_stat_nlink(VALUE self)
    {
        /* struct stat::st_nlink is nlink_t in POSIX.  Not the case for Windows. */
        const struct stat *ptr = get_stat(self);

        if (sizeof(ptr->st_nlink) <= sizeof(int)) {
            return UINT2NUM((unsigned)ptr->st_nlink);
        }
        else if (sizeof(ptr->st_nlink) == sizeof(long)) {
            return ULONG2NUM((unsigned long)ptr->st_nlink);
        }
        else if (sizeof(ptr->st_nlink) == sizeof(LONG_LONG)) {
            return ULL2NUM((unsigned LONG_LONG)ptr->st_nlink);
        }
        else {
            rb_bug(":FIXME: don't know what to do");
        }
    }

Returns the number of hard links to stat.

    File.stat("testfile").nlink             #=> 1
    File.link("testfile", "testfile.bak")   #=> 0
    File.stat("testfile").nlink             #=> 2

owned? → true or false
Source

    static VALUE
    rb_stat_owned(VALUE obj)
    {
        if (get_stat(obj)->st_uid == geteuid()) return Qtrue;
        return Qfalse;
    }

Returns `true` if the effective user id of the process is the same as the owner of stat.

    File.stat("testfile").owned?      #=> true
    File.stat("/etc/passwd").owned?   #=> false

pipe? → true or false
Source

    static VALUE
    rb_stat_p(VALUE obj)
    {
    #ifdef S_IFIFO
        if (S_ISFIFO(get_stat(obj)->st_mode)) return Qtrue;

    #endif
        return Qfalse;
    }

Returns `true` if the operating system supports pipes and stat is a pipe; `false` otherwise.

rdev → integer or nil
Source

    static VALUE
    rb_stat_rdev(VALUE self)
    {
    #ifdef HAVE_STRUCT_STAT_ST_RDEV
    # if SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_DEV_T
        return DEVT2NUM(get_stat(self)->st_rdev);
    # elif SIZEOF_STRUCT_STAT_ST_RDEV <= SIZEOF_LONG
        return ULONG2NUM(get_stat(self)->st_rdev);
    # else
        return ULL2NUM(get_stat(self)->st_rdev);
    # endif
    #else
        return Qnil;
    #endif
    }

Returns an integer representing the device type on which stat resides. Returns `nil` if the operating system doesn’t support this feature.

    File.stat("/dev/fd1").rdev   #=> 513
    File.stat("/dev/tty").rdev   #=> 1280

rdev_major → integer
Source

    static VALUE
    rb_stat_rdev_major(VALUE self)
    {
    #if defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(major)
        return UINT2NUM(major(get_stat(self)->st_rdev));
    #else
        return Qnil;
    #endif
    }

Returns the major part of `File_Stat#rdev` or `nil`.

    File.stat("/dev/fd1").rdev_major   #=> 2
    File.stat("/dev/tty").rdev_major   #=> 5

rdev_minor → integer
Source

    static VALUE
    rb_stat_rdev_minor(VALUE self)
    {
    #if defined(HAVE_STRUCT_STAT_ST_RDEV) && defined(minor)
        return UINT2NUM(minor(get_stat(self)->st_rdev));
    #else
        return Qnil;
    #endif
    }

Returns the minor part of `File_Stat#rdev` or `nil`.

    File.stat("/dev/fd1").rdev_minor   #=> 1
    File.stat("/dev/tty").rdev_minor   #=> 0

readable? → true or false
Source

    static VALUE
    rb_stat_r(VALUE obj)
    {
        struct stat *st = get_stat(obj);

    #ifdef USE_GETEUID
        if (geteuid() == 0) return Qtrue;
    #endif
    #ifdef S_IRUSR
        if (rb_stat_owned(obj))
            return RBOOL(st->st_mode & S_IRUSR);
    #endif
    #ifdef S_IRGRP
        if (rb_stat_grpowned(obj))
            return RBOOL(st->st_mode & S_IRGRP);
    #endif
    #ifdef S_IROTH
        if (!(st->st_mode & S_IROTH)) return Qfalse;
    #endif
        return Qtrue;
    }

Returns `true` if stat is readable by the effective user id of this process.

    File.stat("testfile").readable?   #=> true

readable_real? → true or false
Source

    static VALUE
    rb_stat_R(VALUE obj)
    {
        struct stat *st = get_stat(obj);

    #ifdef USE_GETEUID
        if (getuid() == 0) return Qtrue;
    #endif
    #ifdef S_IRUSR
        if (rb_stat_rowned(obj))
            return RBOOL(st->st_mode & S_IRUSR);
    #endif
    #ifdef S_IRGRP
        if (rb_group_member(get_stat(obj)->st_gid))
            return RBOOL(st->st_mode & S_IRGRP);
    #endif
    #ifdef S_IROTH
        if (!(st->st_mode & S_IROTH)) return Qfalse;
    #endif
        return Qtrue;
    }

Returns `true` if stat is readable by the real user id of this process.

    File.stat("testfile").readable_real?   #=> true

setgid? → true or false
Source

    static VALUE
    rb_stat_sgid(VALUE obj)
    {
    #ifdef S_ISGID
        if (get_stat(obj)->st_mode & S_ISGID) return Qtrue;
    #endif
        return Qfalse;
    }

Returns `true` if stat has the set-group-id permission bit set, `false` if it doesn’t or if the operating system doesn’t support this feature.

    File.stat("/usr/sbin/lpc").setgid?   #=> true

setuid? → true or false
Source

    static VALUE
    rb_stat_suid(VALUE obj)
    {
    #ifdef S_ISUID
        if (get_stat(obj)->st_mode & S_ISUID) return Qtrue;
    #endif
        return Qfalse;
    }

Returns `true` if stat has the set-user-id permission bit set, `false` if it doesn’t or if the operating system doesn’t support this feature.

    File.stat("/bin/su").setuid?   #=> true

size → integer
Source

    static VALUE
    rb_stat_size(VALUE self)
    {
        return OFFT2NUM(get_stat(self)->st_size);
    }

Returns the size of stat in bytes.

    File.stat("testfile").size   #=> 66

size? → Integer or nil
Source

    static VALUE
    rb_stat_s(VALUE obj)
    {
        rb_off_t size = get_stat(obj)->st_size;

        if (size == 0) return Qnil;
        return OFFT2NUM(size);
    }

Returns `nil` if stat is a zero-length file, the size of the file otherwise.

    File.stat("testfile").size?   #=> 66
    File.stat(File::NULL).size?   #=> nil

socket? → true or false
Source

    static VALUE
    rb_stat_S(VALUE obj)
    {
    #ifdef S_ISSOCK
        if (S_ISSOCK(get_stat(obj)->st_mode)) return Qtrue;

    #endif
        return Qfalse;
    }

Returns `true` if stat is a socket, `false` if it isn’t or if the operating system doesn’t support this feature.

    File.stat("testfile").socket?   #=> false

sticky? → true or false
Source

    static VALUE
    rb_stat_sticky(VALUE obj)
    {
    #ifdef S_ISVTX
        if (get_stat(obj)->st_mode & S_ISVTX) return Qtrue;
    #endif
        return Qfalse;
    }

Returns `true` if stat has its sticky bit set, `false` if it doesn’t or if the operating system doesn’t support this feature.

    File.stat("testfile").sticky?   #=> false

symlink? → true or false
Source

    static VALUE
    rb_stat_l(VALUE obj)
    {
    #ifdef S_ISLNK
        if (S_ISLNK(get_stat(obj)->st_mode)) return Qtrue;
    #endif
        return Qfalse;
    }

Returns `true` if stat is a symbolic link, `false` if it isn’t or if the operating system doesn’t support this feature. As `File::stat` automatically follows symbolic links, `symlink?` will always be `false` for an object returned by `File::stat`.

    File.symlink("testfile", "alink")   #=> 0
    File.stat("alink").symlink?         #=> false
    File.lstat("alink").symlink?        #=> true

uid → integer
Source

    static VALUE
    rb_stat_uid(VALUE self)
    {
        return UIDT2NUM(get_stat(self)->st_uid);
    }

Returns the numeric user id of the owner of stat.

    File.stat("testfile").uid   #=> 501

world_readable? → integer or nil
Source

    static VALUE
    rb_stat_wr(VALUE obj)
    {
    #ifdef S_IROTH
        struct stat *st = get_stat(obj);
        if ((st->st_mode & (S_IROTH)) == S_IROTH) {
            return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
        }
    #endif
        return Qnil;
    }

If stat is readable by others, returns an integer representing the file permission bits of stat. Returns `nil` otherwise. The meaning of the bits is platform dependent; on Unix systems, see `stat(2)`.

    m = File.stat("/etc/passwd").world_readable?  #=> 420
    sprintf("%o", m)                              #=> "644"

world_writable? → integer or nil
Source

    static VALUE
    rb_stat_ww(VALUE obj)
    {
    #ifdef S_IWOTH
        struct stat *st = get_stat(obj);
        if ((st->st_mode & (S_IWOTH)) == S_IWOTH) {
            return UINT2NUM(st->st_mode & (S_IRUGO|S_IWUGO|S_IXUGO));
        }
    #endif
        return Qnil;
    }

If stat is writable by others, returns an integer representing the file permission bits of stat. Returns `nil` otherwise. The meaning of the bits is platform dependent; on Unix systems, see `stat(2)`.

    m = File.stat("/tmp").world_writable?         #=> 511
    sprintf("%o", m)                              #=> "777"

writable? → true or false
Source

    static VALUE
    rb_stat_w(VALUE obj)
    {
        struct stat *st = get_stat(obj);

    #ifdef USE_GETEUID
        if (geteuid() == 0) return Qtrue;
    #endif
    #ifdef S_IWUSR
        if (rb_stat_owned(obj))
            return RBOOL(st->st_mode & S_IWUSR);
    #endif
    #ifdef S_IWGRP
        if (rb_stat_grpowned(obj))
            return RBOOL(st->st_mode & S_IWGRP);
    #endif
    #ifdef S_IWOTH
        if (!(st->st_mode & S_IWOTH)) return Qfalse;
    #endif
        return Qtrue;
    }

Returns `true` if stat is writable by the effective user id of this process.

    File.stat("testfile").writable?   #=> true

writable_real? → true or false
Source

    static VALUE
    rb_stat_W(VALUE obj)
    {
        struct stat *st = get_stat(obj);

    #ifdef USE_GETEUID
        if (getuid() == 0) return Qtrue;
    #endif
    #ifdef S_IWUSR
        if (rb_stat_rowned(obj))
            return RBOOL(st->st_mode & S_IWUSR);
    #endif
    #ifdef S_IWGRP
        if (rb_group_member(get_stat(obj)->st_gid))
            return RBOOL(st->st_mode & S_IWGRP);
    #endif
    #ifdef S_IWOTH
        if (!(st->st_mode & S_IWOTH)) return Qfalse;
    #endif
        return Qtrue;
    }

Returns `true` if stat is writable by the real user id of this process.

    File.stat("testfile").writable_real?   #=> true

zero? → true or false
Source

    static VALUE
    rb_stat_z(VALUE obj)
    {
        if (get_stat(obj)->st_size == 0) return Qtrue;
        return Qfalse;
    }

Returns `true` if stat is a zero-length file; `false` otherwise.

    File.stat("testfile").zero?   #=> false