Module: MemFs

Defined in:
lib/memfs.rb,
lib/memfs/io.rb,
lib/memfs/dir.rb,
lib/memfs/file.rb,
lib/memfs/version.rb,
lib/memfs/fake/file.rb,
lib/memfs/file/stat.rb,
lib/memfs/fake/entry.rb,
lib/memfs/file_system.rb,
lib/memfs/fake/symlink.rb,
lib/memfs/fake/directory.rb,
lib/memfs/fake/file/content.rb,
lib/memfs/filesystem_access.rb

Overview

Provides a clean way to interact with a fake file system.

Examples:

Calling activate with a block.

MemFs.activate do
  Dir.mkdir '/hello_world'
  # /hello_world exists here, in memory
end
# /hello_world doesn't exist and has never been on the real FS

Calling activate! and deactivate!.

MemFs.activate!
  # The fake file system is running here
MemFs.deactivate!
# Everything back to normal

Defined Under Namespace

Modules: Fake, FilesystemAccess Classes: Dir, File, FileSystem, IO

Constant Summary collapse

OriginalDir =

Keeps track of the original Ruby Dir class.

::Dir
OriginalFile =

Keeps track of the original Ruby File class.

::File
OriginalIO =

Keeps track of the original Ruby IO class.

::IO
VERSION =
'2.0.0'

Class Method Summary collapse

Class Method Details

.activate { ... } ⇒ Object

Calls the given block with MemFs activated.

The advantage of using #activate against #activate! is that, in case an exception occurs, MemFs is deactivated.

Examples:

MemFs.activate do
  Dir.mkdir '/hello_world'
  # /hello_world exists here, in memory
end
# /hello_world doesn't exist and has never been on the real FS

Exception in activate block.

MemFs.activate do
  raise "Some Error"
end
# Still back to the original Ruby classes

Yields:

  • with no argument.

Returns:

  • nothing.



136
137
138
139
140
141
# File 'lib/memfs.rb', line 136

def activate
  activate!
  yield
ensure
  deactivate!
end

.activate!(clear: true) ⇒ Object

Note:

Don't forget to call #deactivate! to disable the fake file system, you may have some issues in your scripts or tests otherwise.

Activates the fake file system.

Examples:

MemFs.activate!
Dir.mkdir '/hello_world'
# /hello_world exists here, in memory
MemFs.deactivate!
# /hello_world doesn't exist and has never been on the real FS

Returns:

  • nothing.

See Also:

  • #deactivate!


158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/memfs.rb', line 158

def activate!(clear: true)
  Object.class_eval do
    remove_const :Dir
    remove_const :File
    remove_const :IO

    const_set :Dir, MemFs::Dir
    const_set :IO, MemFs::IO
    const_set :File, MemFs::File
  end

  MemFs::FileSystem.instance.clear! if clear
end

.deactivate!Object

Note:

This method should always be called when using activate!

Deactivates the fake file system.

Returns:

  • nothing.

See Also:

  • #activate!


179
180
181
182
183
184
185
186
187
188
189
# File 'lib/memfs.rb', line 179

def deactivate!
  Object.class_eval do
    remove_const :Dir
    remove_const :File
    remove_const :IO

    const_set :Dir, MemFs::OriginalDir
    const_set :IO, MemFs::OriginalIO
    const_set :File, MemFs::OriginalFile
  end
end

.default_platform_rootObject

Returns the default platform root based on the current OS



54
55
56
57
58
59
60
61
# File 'lib/memfs.rb', line 54

def self.default_platform_root
  if windows?
    # Normalize drive letter to uppercase
    OriginalFile.expand_path('/').sub(/\A([a-z]):/) { "#{::Regexp.last_match(1).upcase}:" }
  else
    '/'
  end
end

.haltObject

Switches back to the original file system, calls the given block (if any), and switches back afterwards.

If a block is given, all file & dir operations (like reading dir contents or requiring files) will operate on the original fs.

Examples:

MemFs.halt do
  puts Dir.getwd
end

Returns:

  • nothing



203
204
205
206
207
208
209
# File 'lib/memfs.rb', line 203

def halt
  deactivate!

  yield if block_given?
ensure
  activate!(clear: false)
end

.normalize_path(path) ⇒ Object

Normalize path for consistent handling rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength rubocop:disable Metrics/PerceivedComplexity



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/memfs.rb', line 74

def self.normalize_path(path)
  return path unless path.is_a?(String)

  # Reject UNC paths
  fail ArgumentError, "UNC paths are not supported: #{path}" if path.start_with?('\\\\', '//')

  # Convert backslashes to forward slashes
  path = path.tr('\\', '/')

  return path unless windows?

  # Normalize drive letter to uppercase
  path = path.sub(/\A([a-z]):/) { "#{::Regexp.last_match(1).upcase}:" }

  # Handle drive-relative paths like 'D:foo' or 'D:.' (no slash after colon)
  # and bare drive letters like 'D:' (current directory on drive D)
  # Convert to absolute paths since our fake fs doesn't support per-drive working directories
  if path.match?(/\A[A-Z]:\z/) # Bare drive like 'D:'
    path = "#{path}/"
  elsif path.match?(%r{\A[A-Z]:[^/]}) # Drive-relative like 'D:foo' or 'D:.'
    path = path.sub(/\A([A-Z]):/, '\1:/')
  end

  # Convert bare '/' to platform root on Windows
  if path == '/'
    platform_root
  elsif path.start_with?('/') && !path.match?(%r{\A[A-Z]:/})
    # Convert '/foo' to 'D:/foo' on Windows
    "#{platform_root}#{path[1..]}"
  else
    path
  end
end

.platform_rootObject

Returns the platform-specific root path (e.g., '/' on Unix, 'D:/' on Windows)



39
40
41
# File 'lib/memfs.rb', line 39

def self.platform_root
  @platform_root || default_platform_root
end

.platform_root=(value) ⇒ Object

Allows setting a custom platform root (mainly for testing)



44
45
46
# File 'lib/memfs.rb', line 44

def self.platform_root=(value)
  @platform_root = value
end

.reset_platform_root!Object

Resets platform_root to the default value



49
50
51
# File 'lib/memfs.rb', line 49

def self.reset_platform_root!
  @platform_root = nil
end

.root_path?(path) ⇒ Boolean

Check if a path is the root path (handles both '/' and 'D:/')

Returns:

  • (Boolean)


64
65
66
67
68
69
# File 'lib/memfs.rb', line 64

def self.root_path?(path)
  return false if path.nil?

  normalized = normalize_path(path)
  normalized == platform_root || normalized == '/'
end

.ruby_version_gte?(version) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


30
31
32
# File 'lib/memfs.rb', line 30

def self.ruby_version_gte?(version) # :nodoc:
  Gem::Version.new(RUBY_VERSION) >= Gem::Version.new(version)
end

.touch(*paths) ⇒ Object

Creates a file and all its parent directories.

Parameters:

  • path:

    The path of the file to create.

Returns:

  • nothing.



217
218
219
220
221
222
223
224
# File 'lib/memfs.rb', line 217

def touch(*paths)
  fail 'Always call MemFs.touch inside a MemFs active context.' if ::File != MemFs::File

  paths.each do |path|
    FileUtils.mkdir_p File.dirname(path)
    FileUtils.touch path
  end
end

.windows?Boolean

Returns:

  • (Boolean)


34
35
36
# File 'lib/memfs.rb', line 34

def self.windows?
  /mswin|bccwin|mingw/ =~ RUBY_PLATFORM
end