Class: FakeFS::File

Inherits:
StringIO
  • Object
show all
Defined in:
lib/fakefs/file.rb

Overview

FakeFS File class inherit StringIO

Defined Under Namespace

Classes: Stat

Constant Summary collapse

MODES =
[
  READ_ONLY           = 'r'.freeze,
  READ_WRITE          = 'r+'.freeze,
  WRITE_ONLY          = 'w'.freeze,
  READ_WRITE_TRUNCATE = 'w+'.freeze,
  APPEND_WRITE_ONLY   = 'a'.freeze,
  APPEND_READ_WRITE   = 'a+'.freeze
].freeze
FILE_CREATION_MODES =
(MODES - [READ_ONLY, READ_WRITE]).freeze
MODE_BITMASK =
(
  RealFile::RDONLY |
  RealFile::WRONLY |
  RealFile::RDWR |
  RealFile::APPEND |
  RealFile::CREAT |
  RealFile::EXCL |
  RealFile::NONBLOCK |
  RealFile::TRUNC |
  (RealFile.const_defined?(:NOCTTY) ? RealFile::NOCTTY : 0) |
  (RealFile.const_defined?(:SYNC) ? RealFile::SYNC : 0)
)
FILE_CREATION_BITMASK =
RealFile::CREAT

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(path, mode = READ_ONLY, _perm = nil) ⇒ File

Returns a new instance of File.



493
494
495
496
497
498
499
500
501
502
503
504
# File 'lib/fakefs/file.rb', line 493

def initialize(path, mode = READ_ONLY, _perm = nil)
  @path = path
  @mode = mode.is_a?(Hash) ? (mode[:mode] || READ_ONLY) : mode
  @file = FileSystem.find(path)
  @autoclose = true

  check_modes!

  file_creation_mode? ? create_missing_file : check_file_existence!

  super(@file.content, @mode)
end

Instance Attribute Details

#autocloseObject

Returns the value of attribute autoclose.



647
648
649
# File 'lib/fakefs/file.rb', line 647

def autoclose
  @autoclose
end

#pathObject (readonly)

Returns the value of attribute path.



491
492
493
# File 'lib/fakefs/file.rb', line 491

def path
  @path
end

Class Method Details

.absolute_path(file_name, dir_name = Dir.getwd) ⇒ Object



643
644
645
# File 'lib/fakefs/file.rb', line 643

def self.absolute_path(file_name, dir_name = Dir.getwd)
  RealFile.absolute_path(file_name, dir_name)
end

.atime(path) ⇒ Object



90
91
92
93
94
95
96
# File 'lib/fakefs/file.rb', line 90

def self.atime(path)
  if exist?(path)
    FileSystem.find(path).atime
  else
    raise Errno::ENOENT
  end
end

.basename(*args) ⇒ Object



171
172
173
# File 'lib/fakefs/file.rb', line 171

def self.basename(*args)
  RealFile.basename(*args)
end

.binread(file, length = nil, offset = 0) ⇒ Object



328
329
330
# File 'lib/fakefs/file.rb', line 328

def self.binread(file, length = nil, offset = 0)
  File.read(file, length, offset, mode: 'rb:ASCII-8BIT')
end

.binwrite(file, content, offset = nil) ⇒ Object



332
333
334
335
# File 'lib/fakefs/file.rb', line 332

def self.binwrite(file, content, offset = nil)
  mode = offset ? 'r+b:ASCII-8BIT' : 'wb:ASCII-8BIT'
  File.write(file, content, offset, mode: mode)
end

.birthtime(path) ⇒ Object



692
693
694
695
696
697
698
# File 'lib/fakefs/file.rb', line 692

def self.birthtime(path)
  if exist?(path)
    FileSystem.find(path).birthtime
  else
    raise Errno::ENOENT
  end
end

.chmod(new_mode, filename) ⇒ Object



291
292
293
294
295
296
297
298
299
300
301
# File 'lib/fakefs/file.rb', line 291

def self.chmod(new_mode, filename)
  # chmod's mode can either be passed in in absolute mode, or symbolic mode
  # for reference: https://ruby-doc.org/stdlib-2.2.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-chmod
  # if the mode is passed in symbolic mode we must convert it to absolute mode
  is_absolute_mode = new_mode.is_a? Numeric
  unless is_absolute_mode
    current_mode = FileSystem.find(filename).mode
    new_mode = convert_symbolic_chmod_to_absolute(new_mode, current_mode)
  end
  FileSystem.find(filename).mode = 0o100000 + new_mode
end

.chown(owner_int, group_int, filename) ⇒ Object



311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/fakefs/file.rb', line 311

def self.chown(owner_int, group_int, filename)
  file = FileSystem.find(filename)

  if owner_int && owner_int != -1
    owner_int.is_a?(Integer) || raise(TypeError, "can't convert String into Integer")
    file.uid = owner_int
  end
  if group_int && group_int != -1
    group_int.is_a?(Integer) || raise(TypeError, "can't convert String into Integer")
    file.gid = group_int
  end
end

.const_missing(name) ⇒ Object



133
134
135
# File 'lib/fakefs/file.rb', line 133

def self.const_missing(name)
  RealFile.const_get(name)
end

.ctime(path) ⇒ Object



82
83
84
85
86
87
88
# File 'lib/fakefs/file.rb', line 82

def self.ctime(path)
  if exist?(path)
    FileSystem.find(path).ctime
  else
    raise Errno::ENOENT
  end
end

.delete(*files) ⇒ Object Also known as: unlink



260
261
262
263
264
265
266
267
268
269
# File 'lib/fakefs/file.rb', line 260

def self.delete(*files)
  files.each do |file|
    file_name = (file.class == FakeFS::File ? file.path : file.to_s)
    raise Errno::ENOENT, file_name unless exist?(file_name)

    FileUtils.rm(file_name)
  end

  files.size
end

.directory?(path) ⇒ Boolean

Returns:

  • (Boolean)


137
138
139
140
141
142
143
144
# File 'lib/fakefs/file.rb', line 137

def self.directory?(path)
  if path.respond_to? :entry
    path.entry.is_a? FakeDir
  else
    result = FileSystem.find(path)
    result ? result.entry.is_a?(FakeDir) : false
  end
end

.dirname(*args) ⇒ Object



175
176
177
# File 'lib/fakefs/file.rb', line 175

def self.dirname(*args)
  RealFile.dirname(*args)
end

.executable?(filename) ⇒ Boolean

Not exactly right, returns true if the file is chmod +x for owner. In the context of when you would use fakefs, this is usually what you want.

Returns:

  • (Boolean)


305
306
307
308
309
# File 'lib/fakefs/file.rb', line 305

def self.executable?(filename)
  file = FileSystem.find(filename)
  return false unless file
  (file.mode - 0o100000) & 0o100 != 0
end

.exist?(path) ⇒ Boolean

Returns:

  • (Boolean)


44
45
46
47
48
49
50
51
# File 'lib/fakefs/file.rb', line 44

def self.exist?(path)
  if File.symlink?(path)
    referent = File.expand_path(File.readlink(path), File.dirname(path))
    exist?(referent)
  else
    !FileSystem.find(path).nil?
  end
end

.expand_path(file_name, dir_string = FileSystem.current_dir.to_s) ⇒ Object



167
168
169
# File 'lib/fakefs/file.rb', line 167

def self.expand_path(file_name, dir_string = FileSystem.current_dir.to_s)
  RealFile.expand_path(file_name, RealFile.expand_path(dir_string, Dir.pwd))
end

.extname(path) ⇒ Object



32
33
34
# File 'lib/fakefs/file.rb', line 32

def self.extname(path)
  RealFile.extname(path)
end

.file?(path) ⇒ Boolean

Returns:

  • (Boolean)


154
155
156
157
158
159
160
161
# File 'lib/fakefs/file.rb', line 154

def self.file?(path)
  if path.respond_to? :entry
    path.entry.is_a? FakeFile
  else
    result = FileSystem.find(path)
    result ? result.entry.is_a?(FakeFile) : false
  end
end

.fnmatch?(pattern, path, flags = 0) ⇒ Boolean Also known as: fnmatch

Returns:

  • (Boolean)


337
338
339
# File 'lib/fakefs/file.rb', line 337

def self.fnmatch?(pattern, path, flags = 0)
  RealFile.fnmatch?(pattern, path, flags)
end

.foreach(path, *args, &block) ⇒ Object



208
209
210
211
212
213
214
215
216
217
218
219
220
# File 'lib/fakefs/file.rb', line 208

def self.foreach(path, *args, &block)
  file = new(path)
  if file.exists?
    FileSystem.find(path).atime = Time.now
    if block_given?
      file.each_line(*args, &block)
    else
      file.each_line(*args)
    end
  else
    raise Errno::ENOENT
  end
end

.ftype(filename) ⇒ Object



163
164
165
# File 'lib/fakefs/file.rb', line 163

def self.ftype(filename)
  File.lstat(filename).ftype
end

.identical?(one_path, another_path) ⇒ Boolean

Returns:

  • (Boolean)


54
55
56
# File 'lib/fakefs/file.rb', line 54

def identical?(one_path, another_path)
  FileSystem.find(one_path) == FileSystem.find(another_path)
end

.join(*parts) ⇒ Object



36
37
38
# File 'lib/fakefs/file.rb', line 36

def self.join(*parts)
  RealFile.join(parts)
end

Raises:

  • (Errno::EPERM)


248
249
250
251
252
253
254
255
256
257
258
# File 'lib/fakefs/file.rb', line 248

def self.link(source, dest)
  raise Errno::EPERM, "#{source} or #{dest}" if directory?(source)
  raise Errno::ENOENT, "#{source} or #{dest}" unless exist?(source)
  raise Errno::EEXIST, "#{source} or #{dest}" if exist?(dest)

  source = FileSystem.find(source)
  dest = FileSystem.add(dest, source.entry.clone)
  source.link(dest)

  0
end

.lstat(file) ⇒ Object



283
284
285
# File 'lib/fakefs/file.rb', line 283

def self.lstat(file)
  File::Stat.new(file, true)
end

.mtime(path) ⇒ Object



74
75
76
77
78
79
80
# File 'lib/fakefs/file.rb', line 74

def self.mtime(path)
  if exist?(path)
    FileSystem.find(path).mtime
  else
    raise Errno::ENOENT
  end
end

.path(file) ⇒ Object



40
41
42
# File 'lib/fakefs/file.rb', line 40

def self.path(file)
  RealFile.path(file)
end

.read(path, *args) ⇒ Object

Raises:

  • (Errno::ENOENT)


184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/fakefs/file.rb', line 184

def self.read(path, *args)
  options = args[-1].is_a?(Hash) ? args.pop : {}
  length = args.empty? ? nil : args.shift
  offset = args.empty? ? 0 : args.shift
  file = new(path, **options)

  raise Errno::ENOENT unless file.exists?
  raise Errno::EISDIR, path.to_s if directory?(path)

  FileSystem.find(path).atime = Time.now
  file.seek(offset)
  file.read(length)
end

.readable?(path) ⇒ Boolean

Returns:

  • (Boolean)


64
65
66
67
# File 'lib/fakefs/file.rb', line 64

def self.readable?(path)
  return false unless exist? path
  File.lstat(path).readable?
end

.readlines(path, chomp: false) ⇒ Object



198
199
200
201
202
203
204
205
206
# File 'lib/fakefs/file.rb', line 198

def self.readlines(path, chomp: false)
  file = new(path)
  if file.exists?
    FileSystem.find(path).atime = Time.now
    chomp ? file.readlines.map(&:chomp) : file.readlines
  else
    raise Errno::ENOENT
  end
end


179
180
181
182
# File 'lib/fakefs/file.rb', line 179

def self.readlink(path)
  symlink = FileSystem.find(path)
  symlink.target
end

.realdirpath(*args) ⇒ Object



659
660
661
# File 'lib/fakefs/file.rb', line 659

def self.realdirpath(*args)
  RealFile.realdirpath(*args)
end

.realpath(*args) ⇒ Object



620
621
622
# File 'lib/fakefs/file.rb', line 620

def self.realpath(*args)
  RealFile.realpath(*args)
end

.rename(source, dest) ⇒ Object



222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/fakefs/file.rb', line 222

def self.rename(source, dest)
  if directory?(source) && file?(dest)
    raise Errno::ENOTDIR, "#{source} or #{dest}"
  elsif file?(source) && directory?(dest)
    raise Errno::EISDIR, "#{source} or #{dest}"
  elsif !exist?(dirname(dest))
    raise Errno::ENOENT, "#{source} or #{dest}"
  end

  if (target = FileSystem.find(source))
    return 0 if source == dest

    if target.is_a?(FakeFS::FakeSymlink)
      File.symlink(target.target, dest)
    else
      FileSystem.add(dest, target.entry.clone)
    end

    FileSystem.delete(source)
  else
    raise Errno::ENOENT, "#{source} or #{dest}"
  end

  0
end

.size(path) ⇒ Object



111
112
113
114
115
116
117
# File 'lib/fakefs/file.rb', line 111

def self.size(path)
  if directory?(path)
    64 + (32 * FileSystem.find(path).entries.size)
  else
    read(path).bytesize
  end
end

.size?(path) ⇒ Boolean

Returns:

  • (Boolean)


119
120
121
# File 'lib/fakefs/file.rb', line 119

def self.size?(path)
  size(path) if exist?(path) && !size(path).zero?
end

.split(path) ⇒ Object



287
288
289
# File 'lib/fakefs/file.rb', line 287

def self.split(path)
  RealFile.split(path)
end

.stat(file) ⇒ Object



279
280
281
# File 'lib/fakefs/file.rb', line 279

def self.stat(file)
  File::Stat.new(file)
end

.sticky?(_path) ⇒ Boolean

Assume nothing is sticky.

Returns:

  • (Boolean)


59
60
61
# File 'lib/fakefs/file.rb', line 59

def sticky?(_path)
  false
end


275
276
277
# File 'lib/fakefs/file.rb', line 275

def self.symlink(source, dest)
  FileUtils.ln_s(source, dest)
end

.symlink?(path) ⇒ Boolean

Returns:

  • (Boolean)


146
147
148
149
150
151
152
# File 'lib/fakefs/file.rb', line 146

def self.symlink?(path)
  if path.respond_to? :entry
    path.is_a? FakeSymlink
  else
    FileSystem.find(path).is_a? FakeSymlink
  end
end

.umask(*args) ⇒ Object



324
325
326
# File 'lib/fakefs/file.rb', line 324

def self.umask(*args)
  RealFile.umask(*args)
end

.utime(atime, mtime, *paths) ⇒ Object



98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/fakefs/file.rb', line 98

def self.utime(atime, mtime, *paths)
  paths.each do |path|
    if exist?(path)
      FileSystem.find(path).atime = atime
      FileSystem.find(path).mtime = mtime
    else
      raise Errno::ENOENT
    end
  end

  paths.size
end

.writable?(path) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
72
# File 'lib/fakefs/file.rb', line 69

def self.writable?(path)
  return false unless exist? path
  File.lstat(path).writable?
end

.write(filename, contents, offset = nil, open_args = {}) ⇒ Object



665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
# File 'lib/fakefs/file.rb', line 665

def self.write(filename, contents, offset = nil, open_args = {})
  offset, open_args = nil, offset if offset.is_a?(Hash)
  mode = offset ? 'r+' : 'w'
  if open_args.any?
    if open_args[:open_args]
      args = [filename, *open_args[:open_args]]
    else
      mode = open_args[:mode] || mode
      args = [filename, mode, open_args]
    end
  else
    args = [filename, mode]
  end
  if offset
    open(*args) do |f| # rubocop:disable Security/Open
      f.seek(offset)
      f.write(contents)
    end
  else
    open(*args) do |f| # rubocop:disable Security/Open
      f << contents
    end
  end

  contents.length
end

.zero?(path) ⇒ Boolean Also known as: empty?

Returns:

  • (Boolean)


123
124
125
# File 'lib/fakefs/file.rb', line 123

def self.zero?(path)
  exist?(path) && size(path) == 0
end

Instance Method Details

#advise(_advice, _offset = 0, _len = 0) ⇒ Object



663
# File 'lib/fakefs/file.rb', line 663

def advise(_advice, _offset = 0, _len = 0); end

#atimeObject



578
579
580
# File 'lib/fakefs/file.rb', line 578

def atime
  self.class.atime(@path)
end

#autoclose?Boolean

Returns:

  • (Boolean)


649
650
651
# File 'lib/fakefs/file.rb', line 649

def autoclose?
  @autoclose ? true : false
end

#binmode?Boolean

Returns:

  • (Boolean)


624
625
626
627
628
629
# File 'lib/fakefs/file.rb', line 624

def binmode?
  @mode.is_a?(String) && (
    @mode.include?('b') ||
    @mode.include?('binary')
  ) && !@mode.include?('bom')
end

#birthtimeObject



700
701
702
# File 'lib/fakefs/file.rb', line 700

def birthtime
  self.class.birthtime(@path)
end

#chmod(new_mode) ⇒ Object



594
595
596
597
598
599
600
601
602
603
604
# File 'lib/fakefs/file.rb', line 594

def chmod(new_mode)
  # chmod's mode can either be passed in in absolute mode, or symbolic mode
  # for reference: https://ruby-doc.org/stdlib-2.2.2/libdoc/fileutils/rdoc/FileUtils.html#method-c-chmod
  # if the mode is passed in symbolic mode we must convert it to absolute mode
  is_absolute_mode = new_mode.is_a? Numeric
  unless is_absolute_mode
    current_mode = @file.mode
    new_mode = convert_symbolic_chmod_to_absolute(new_mode, current_mode)
  end
  @file.mode = 0o100000 + new_mode
end

#chown(owner_int, group_int) ⇒ Object



606
607
608
609
610
611
612
613
614
615
616
617
618
# File 'lib/fakefs/file.rb', line 606

def chown(owner_int, group_int)
  return unless group_int && group_int != -1

  owner_int.is_a?(Integer) || raise(
    TypeError, "can't convert String into Integer"
  )
  @file.uid = owner_int

  group_int.is_a?(Integer) || raise(
    TypeError, "can't convert String into Integer"
  )
  @file.gid = group_int
end

#close_on_exec=(_bool) ⇒ Object

Raises:

  • (NotImplementedError)


631
632
633
# File 'lib/fakefs/file.rb', line 631

def close_on_exec=(_bool)
  raise NotImplementedError
end

#close_on_exec?Boolean

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)


635
636
637
# File 'lib/fakefs/file.rb', line 635

def close_on_exec?
  raise NotImplementedError
end

#ctimeObject



582
583
584
# File 'lib/fakefs/file.rb', line 582

def ctime
  self.class.ctime(@path)
end

#exists?Boolean

Returns:

  • (Boolean)


506
507
508
# File 'lib/fakefs/file.rb', line 506

def exists?
  true
end

#flockObject

Raises:

  • (NotImplementedError)


586
587
588
# File 'lib/fakefs/file.rb', line 586

def flock(*)
  raise NotImplementedError
end

#ioctlObject

Raises:

  • (NotImplementedError)


543
544
545
# File 'lib/fakefs/file.rb', line 543

def ioctl(*)
  raise NotImplementedError
end

#is_a?(klass) ⇒ Boolean

Returns:

  • (Boolean)


535
536
537
# File 'lib/fakefs/file.rb', line 535

def is_a?(klass)
  RealFile.allocate.is_a?(klass)
end

#lstatObject



555
556
557
# File 'lib/fakefs/file.rb', line 555

def lstat
  self.class.lstat(@path)
end

#mtimeObject



590
591
592
# File 'lib/fakefs/file.rb', line 590

def mtime
  self.class.mtime(@path)
end

#read(length = nil, buf = '') ⇒ Object



704
705
706
707
708
709
710
711
712
# File 'lib/fakefs/file.rb', line 704

def read(length = nil, buf = '')
  read_buf = super(length, buf)
  if binmode?
    read_buf&.force_encoding('ASCII-8BIT')
  else
    read_buf&.force_encoding(Encoding.default_external)
  end
  read_buf
end

#read_nonblockObject

Raises:

  • (NotImplementedError)


547
548
549
# File 'lib/fakefs/file.rb', line 547

def read_nonblock
  raise NotImplementedError
end

#readpartialObject



574
575
576
# File 'lib/fakefs/file.rb', line 574

def readpartial(*)
  super
end

#sizeObject



655
656
657
# File 'lib/fakefs/file.rb', line 655

def size
  File.size(@path)
end

#statObject



551
552
553
# File 'lib/fakefs/file.rb', line 551

def stat
  self.class.stat(@path)
end

#stringObject



539
540
541
# File 'lib/fakefs/file.rb', line 539

def string
  gets(nil)
end

#sysreadObject



517
# File 'lib/fakefs/file.rb', line 517

alias sysread read

#sysseek(position, whence = SEEK_SET) ⇒ Object



559
560
561
562
# File 'lib/fakefs/file.rb', line 559

def sysseek(position, whence = SEEK_SET)
  seek(position, whence)
  pos
end

#syswriteObject



518
519
520
521
522
# File 'lib/fakefs/file.rb', line 518

def write(*args)
  val = super(*args)
  @file.mtime = Time.now
  val
end

#to_ioObject



566
567
568
# File 'lib/fakefs/file.rb', line 566

def to_io
  self
end

#to_pathObject



639
640
641
# File 'lib/fakefs/file.rb', line 639

def to_path
  @path
end

#write(*args) ⇒ Object



510
511
512
513
514
# File 'lib/fakefs/file.rb', line 510

def write(*args)
  val = super(*args)
  @file.mtime = Time.now
  val
end

#write_nonblockObject

Raises:

  • (NotImplementedError)


570
571
572
# File 'lib/fakefs/file.rb', line 570

def write_nonblock(*)
  raise NotImplementedError
end