memfs 0.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/.github/dependabot.yml +19 -0
- data/.github/workflows/ci.yml +64 -0
- data/.gitignore +1 -0
- data/.rspec +2 -2
- data/.rubocop.yml +81 -12
- data/CHANGELOG.md +41 -10
- data/Gemfile +19 -0
- data/Guardfile +5 -3
- data/README.md +7 -6
- data/Rakefile +4 -2
- data/bin/_guard-core +16 -0
- data/bin/coverage_summary +51 -0
- data/bin/guard +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/bin/rubocop +16 -0
- data/lib/memfs/dir.rb +86 -21
- data/lib/memfs/fake/directory.rb +31 -3
- data/lib/memfs/fake/entry.rb +41 -25
- data/lib/memfs/fake/file/content.rb +6 -5
- data/lib/memfs/fake/file.rb +6 -5
- data/lib/memfs/fake/symlink.rb +2 -0
- data/lib/memfs/file/stat.rb +21 -14
- data/lib/memfs/file.rb +92 -77
- data/lib/memfs/file_system.rb +17 -12
- data/lib/memfs/filesystem_access.rb +2 -0
- data/lib/memfs/io.rb +183 -153
- data/lib/memfs/version.rb +3 -1
- data/lib/memfs.rb +112 -5
- data/memfs.gemspec +0 -14
- data/spec/fileutils_spec.rb +86 -58
- data/spec/memfs/dir_spec.rb +175 -27
- data/spec/memfs/fake/directory_spec.rb +8 -4
- data/spec/memfs/fake/entry_spec.rb +16 -10
- data/spec/memfs/fake/file/content_spec.rb +1 -1
- data/spec/memfs/fake/file_spec.rb +1 -1
- data/spec/memfs/fake/symlink_spec.rb +3 -3
- data/spec/memfs/file/stat_spec.rb +30 -15
- data/spec/memfs/file_spec.rb +271 -104
- data/spec/memfs/file_system_spec.rb +20 -19
- data/spec/memfs_spec.rb +67 -2
- data/spec/spec_helper.rb +40 -23
- metadata +17 -135
- data/.travis.yml +0 -6
data/lib/memfs/io.rb
CHANGED
|
@@ -1,204 +1,234 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'forwardable'
|
|
2
4
|
require 'memfs/filesystem_access'
|
|
3
5
|
|
|
4
6
|
module MemFs
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
9
|
-
options = { mode: File::RDONLY, encoding: nil, open_args: nil }.merge(options)
|
|
10
|
-
open_args = options[:open_args] ||
|
|
11
|
-
[options[:mode], encoding: options[:encoding]]
|
|
12
|
-
|
|
13
|
-
length, offset = args
|
|
14
|
-
|
|
15
|
-
file = open(path, *open_args)
|
|
16
|
-
file.seek(offset || 0)
|
|
17
|
-
file.read(length)
|
|
18
|
-
ensure
|
|
19
|
-
file.close if file
|
|
20
|
-
end
|
|
21
|
-
end
|
|
7
|
+
class IO
|
|
8
|
+
extend SingleForwardable
|
|
9
|
+
include OriginalFile::Constants
|
|
22
10
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
(OriginalIO.constants - OriginalFile::Constants.constants).each do |const_name|
|
|
12
|
+
const_set(const_name, OriginalIO.const_get(const_name))
|
|
13
|
+
end
|
|
26
14
|
|
|
27
|
-
|
|
28
|
-
|
|
15
|
+
def_delegators :original_io_class,
|
|
16
|
+
:copy_stream
|
|
29
17
|
|
|
30
|
-
|
|
31
|
-
|
|
18
|
+
def self.read(path, *args)
|
|
19
|
+
options = args.last.is_a?(Hash) ? args.pop : {}
|
|
20
|
+
options = { encoding: nil, mode: File::RDONLY, open_args: nil }.merge(options)
|
|
21
|
+
open_args = options[:open_args] || [options[:mode], { encoding: options[:encoding] }]
|
|
32
22
|
|
|
33
|
-
|
|
34
|
-
advice_types = [
|
|
35
|
-
:dontneed,
|
|
36
|
-
:noreuse,
|
|
37
|
-
:normal,
|
|
38
|
-
:random,
|
|
39
|
-
:sequential,
|
|
40
|
-
:willneed
|
|
41
|
-
]
|
|
42
|
-
unless advice_types.include?(advice_type)
|
|
43
|
-
fail NotImplementedError, "Unsupported advice: #{advice_type.inspect}"
|
|
44
|
-
end
|
|
45
|
-
nil
|
|
46
|
-
end
|
|
23
|
+
length, offset = args
|
|
47
24
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
25
|
+
file = open(path, *open_args)
|
|
26
|
+
file.seek(offset || 0)
|
|
27
|
+
file.read(length)
|
|
28
|
+
ensure
|
|
29
|
+
file&.close
|
|
30
|
+
end
|
|
51
31
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
self
|
|
56
|
-
end
|
|
32
|
+
# rubocop:disable Metrics/MethodLength
|
|
33
|
+
def self.write(path, string, offset = 0, open_args = nil)
|
|
34
|
+
open_args ||= [File::WRONLY, { encoding: nil }]
|
|
57
35
|
|
|
58
|
-
|
|
59
|
-
|
|
36
|
+
offset = 0 if offset.nil?
|
|
37
|
+
unless offset.respond_to?(:to_int)
|
|
38
|
+
fail TypeError, "no implicit conversion from #{offset.class}"
|
|
60
39
|
end
|
|
40
|
+
offset = offset.to_int
|
|
61
41
|
|
|
62
|
-
|
|
63
|
-
|
|
42
|
+
if offset.positive?
|
|
43
|
+
fail(NotImplementedError, 'MemFs::IO.write with offset not yet supported.')
|
|
64
44
|
end
|
|
65
45
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
46
|
+
file = open(path, *open_args)
|
|
47
|
+
file.seek(offset)
|
|
48
|
+
file.write(string)
|
|
49
|
+
ensure
|
|
50
|
+
file&.close
|
|
51
|
+
end
|
|
52
|
+
# rubocop:enable Metrics/MethodLength
|
|
69
53
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
54
|
+
def self.original_io_class
|
|
55
|
+
MemFs::OriginalIO
|
|
56
|
+
end
|
|
57
|
+
private_class_method :original_io_class
|
|
73
58
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
end
|
|
77
|
-
alias_method :eof, :eof?
|
|
59
|
+
attr_writer :autoclose,
|
|
60
|
+
:close_on_exec
|
|
78
61
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
@external_encoding
|
|
82
|
-
else
|
|
83
|
-
@external_encoding ||= Encoding.default_external
|
|
84
|
-
end
|
|
85
|
-
end
|
|
62
|
+
def <<(object)
|
|
63
|
+
fail IOError, 'not opened for writing' unless writable?
|
|
86
64
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
fail IOError, 'not opened for reading' unless readable?
|
|
90
|
-
content.each_line(sep) { |line| block.call(line) }
|
|
91
|
-
self
|
|
92
|
-
end
|
|
65
|
+
content << object.to_s
|
|
66
|
+
end
|
|
93
67
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
fail IOError, 'not opened for reading' unless readable?
|
|
97
|
-
content.each_byte { |byte| block.call(byte) }
|
|
98
|
-
self
|
|
99
|
-
end
|
|
100
|
-
alias_method :bytes, :each_byte
|
|
68
|
+
def advise(advice_type, _offset = 0, _len = 0)
|
|
69
|
+
advice_types = %i[dontneed noreuse normal random sequential willneed]
|
|
101
70
|
|
|
102
|
-
|
|
103
|
-
return to_enum(__callee__) unless block_given?
|
|
104
|
-
fail IOError, 'not opened for reading' unless readable?
|
|
105
|
-
content.each_char { |char| block.call(char) }
|
|
106
|
-
self
|
|
107
|
-
end
|
|
108
|
-
alias_method :chars, :each_char
|
|
71
|
+
return if advice_types.include?(advice_type)
|
|
109
72
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
end
|
|
73
|
+
fail NotImplementedError, "Unsupported advice: #{advice_type.inspect}"
|
|
74
|
+
end
|
|
113
75
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
self << objs.join($,) << $\.to_s
|
|
118
|
-
nil
|
|
119
|
-
end
|
|
76
|
+
def autoclose?
|
|
77
|
+
defined?(@autoclose) ? !!@autoclose : true
|
|
78
|
+
end
|
|
120
79
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
80
|
+
def binmode
|
|
81
|
+
@binmode = true
|
|
82
|
+
@external_encoding = Encoding::ASCII_8BIT
|
|
83
|
+
self
|
|
84
|
+
end
|
|
124
85
|
|
|
125
|
-
|
|
126
|
-
|
|
86
|
+
def binmode?
|
|
87
|
+
defined?(@binmode) ? @binmode : false
|
|
88
|
+
end
|
|
127
89
|
|
|
128
|
-
|
|
129
|
-
|
|
90
|
+
def close
|
|
91
|
+
self.closed = true
|
|
92
|
+
end
|
|
130
93
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
end
|
|
135
|
-
default = length ? nil : ''
|
|
136
|
-
content.read(length, buffer) || default
|
|
137
|
-
end
|
|
94
|
+
def closed?
|
|
95
|
+
closed
|
|
96
|
+
end
|
|
138
97
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
when ::IO::SEEK_END then content.to_s.length + amount
|
|
143
|
-
when ::IO::SEEK_SET then amount
|
|
144
|
-
end
|
|
98
|
+
def close_on_exec?
|
|
99
|
+
defined?(@close_on_exec) ? !!@close_on_exec : true
|
|
100
|
+
end
|
|
145
101
|
|
|
146
|
-
|
|
102
|
+
def eof?
|
|
103
|
+
pos >= content.size
|
|
104
|
+
end
|
|
105
|
+
alias eof eof?
|
|
147
106
|
|
|
148
|
-
|
|
149
|
-
|
|
107
|
+
def external_encoding
|
|
108
|
+
if writable?
|
|
109
|
+
@external_encoding
|
|
110
|
+
else
|
|
111
|
+
@external_encoding ||= Encoding.default_external
|
|
150
112
|
end
|
|
113
|
+
end
|
|
151
114
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
115
|
+
def each(sep = $/, &block)
|
|
116
|
+
return to_enum(__callee__, sep) unless block_given?
|
|
117
|
+
fail IOError, 'not opened for reading' unless readable?
|
|
118
|
+
content.each_line(sep, &block)
|
|
119
|
+
self
|
|
120
|
+
end
|
|
155
121
|
|
|
156
|
-
|
|
157
|
-
|
|
122
|
+
def each_byte(&block)
|
|
123
|
+
return to_enum(__callee__) unless block_given?
|
|
124
|
+
fail IOError, 'not opened for reading' unless readable?
|
|
125
|
+
content.each_byte(&block)
|
|
126
|
+
self
|
|
127
|
+
end
|
|
128
|
+
alias bytes each_byte
|
|
158
129
|
|
|
159
|
-
|
|
160
|
-
|
|
130
|
+
def each_char(&block)
|
|
131
|
+
return to_enum(__callee__) unless block_given?
|
|
132
|
+
fail IOError, 'not opened for reading' unless readable?
|
|
133
|
+
content.each_char(&block)
|
|
134
|
+
self
|
|
135
|
+
end
|
|
136
|
+
alias chars each_char
|
|
161
137
|
|
|
162
|
-
|
|
138
|
+
def fileno
|
|
139
|
+
entry.fileno
|
|
140
|
+
end
|
|
163
141
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
142
|
+
def pos
|
|
143
|
+
entry.pos
|
|
144
|
+
end
|
|
167
145
|
|
|
168
|
-
|
|
146
|
+
def print(*objs)
|
|
147
|
+
objs << $_ if objs.empty?
|
|
148
|
+
self << objs.join($,) << $\.to_s
|
|
149
|
+
nil
|
|
150
|
+
end
|
|
169
151
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
152
|
+
def printf(format_string, *objs)
|
|
153
|
+
print format_string % objs
|
|
154
|
+
end
|
|
173
155
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
end
|
|
156
|
+
def puts(text)
|
|
157
|
+
fail IOError, 'not opened for writing' unless writable?
|
|
177
158
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
(opening_mode | File::RDONLY).zero?
|
|
181
|
-
end
|
|
159
|
+
content.puts text
|
|
160
|
+
end
|
|
182
161
|
|
|
183
|
-
|
|
184
|
-
|
|
162
|
+
def read(length = nil, buffer = +'')
|
|
163
|
+
fail(Errno::ENOENT, path) unless entry
|
|
185
164
|
|
|
186
|
-
|
|
187
|
-
|
|
165
|
+
default = length ? nil : ''
|
|
166
|
+
content.read(length, buffer) || default
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def seek(amount, whence = ::IO::SEEK_SET)
|
|
170
|
+
new_pos =
|
|
171
|
+
case whence
|
|
172
|
+
when ::IO::SEEK_CUR then entry.pos + amount
|
|
173
|
+
when ::IO::SEEK_END then content.to_s.length + amount
|
|
174
|
+
when ::IO::SEEK_SET then amount
|
|
188
175
|
end
|
|
189
176
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
177
|
+
fail Errno::EINVAL, path if new_pos.nil? || new_pos.negative?
|
|
178
|
+
|
|
179
|
+
entry.pos = new_pos
|
|
180
|
+
0
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def stat
|
|
184
|
+
File.stat(path)
|
|
185
|
+
end
|
|
193
186
|
|
|
194
|
-
|
|
195
|
-
|
|
187
|
+
def write(string)
|
|
188
|
+
fail(IOError, 'not opened for writing') unless writable?
|
|
189
|
+
|
|
190
|
+
content.write(string.to_s)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
private
|
|
194
|
+
|
|
195
|
+
attr_accessor :closed,
|
|
196
|
+
:entry,
|
|
197
|
+
:opening_mode
|
|
198
|
+
|
|
199
|
+
attr_reader :path
|
|
200
|
+
|
|
201
|
+
def content
|
|
202
|
+
entry.content
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
def create_file?
|
|
206
|
+
(opening_mode & File::CREAT).nonzero?
|
|
207
|
+
end
|
|
208
|
+
|
|
209
|
+
def readable?
|
|
210
|
+
(opening_mode & File::RDWR).nonzero? ||
|
|
211
|
+
(opening_mode | File::RDONLY).zero?
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def str_to_mode_int(mode)
|
|
215
|
+
return mode unless mode.is_a?(String)
|
|
216
|
+
|
|
217
|
+
unless mode =~ /\A([rwa]\+?)([bt])?(:(bom|UTF-8|utf-8))?(\|.+)?\z/
|
|
218
|
+
fail ArgumentError, "invalid access mode #{mode}"
|
|
196
219
|
end
|
|
197
220
|
|
|
198
|
-
|
|
199
|
-
|
|
221
|
+
mode_str = $~[1]
|
|
222
|
+
File::MODE_MAP[mode_str]
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
def truncate_file?
|
|
226
|
+
(opening_mode & File::TRUNC).nonzero?
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def writable?
|
|
230
|
+
(opening_mode & File::WRONLY).nonzero? ||
|
|
200
231
|
(opening_mode & File::RDWR).nonzero?
|
|
201
|
-
end
|
|
202
232
|
end
|
|
203
233
|
end
|
|
204
234
|
end
|
data/lib/memfs/version.rb
CHANGED
data/lib/memfs.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require 'memfs/version'
|
|
2
4
|
require 'fileutils'
|
|
3
5
|
|
|
@@ -22,6 +24,89 @@ module MemFs
|
|
|
22
24
|
# Keeps track of the original Ruby File class.
|
|
23
25
|
OriginalFile = ::File
|
|
24
26
|
|
|
27
|
+
# Keeps track of the original Ruby IO class.
|
|
28
|
+
OriginalIO = ::IO
|
|
29
|
+
|
|
30
|
+
def self.ruby_version_gte?(version) # :nodoc:
|
|
31
|
+
Gem::Version.new(RUBY_VERSION) >= Gem::Version.new(version)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.windows?
|
|
35
|
+
/mswin|bccwin|mingw/ =~ RUBY_PLATFORM
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Returns the platform-specific root path (e.g., '/' on Unix, 'D:/' on Windows)
|
|
39
|
+
def self.platform_root
|
|
40
|
+
@platform_root || default_platform_root
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Allows setting a custom platform root (mainly for testing)
|
|
44
|
+
def self.platform_root=(value)
|
|
45
|
+
@platform_root = value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Resets platform_root to the default value
|
|
49
|
+
def self.reset_platform_root!
|
|
50
|
+
@platform_root = nil
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# Returns the default platform root based on the current OS
|
|
54
|
+
def self.default_platform_root
|
|
55
|
+
if windows?
|
|
56
|
+
# Normalize drive letter to uppercase
|
|
57
|
+
OriginalFile.expand_path('/').sub(/\A([a-z]):/) { "#{::Regexp.last_match(1).upcase}:" }
|
|
58
|
+
else
|
|
59
|
+
'/'
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Check if a path is the root path (handles both '/' and 'D:/')
|
|
64
|
+
def self.root_path?(path)
|
|
65
|
+
return false if path.nil?
|
|
66
|
+
|
|
67
|
+
normalized = normalize_path(path)
|
|
68
|
+
normalized == platform_root || normalized == '/'
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Normalize path for consistent handling
|
|
72
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
73
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
|
74
|
+
def self.normalize_path(path)
|
|
75
|
+
return path unless path.is_a?(String)
|
|
76
|
+
|
|
77
|
+
# Reject UNC paths
|
|
78
|
+
fail ArgumentError, "UNC paths are not supported: #{path}" if path.start_with?('\\\\', '//')
|
|
79
|
+
|
|
80
|
+
# Convert backslashes to forward slashes
|
|
81
|
+
path = path.tr('\\', '/')
|
|
82
|
+
|
|
83
|
+
return path unless windows?
|
|
84
|
+
|
|
85
|
+
# Normalize drive letter to uppercase
|
|
86
|
+
path = path.sub(/\A([a-z]):/) { "#{::Regexp.last_match(1).upcase}:" }
|
|
87
|
+
|
|
88
|
+
# Handle drive-relative paths like 'D:foo' or 'D:.' (no slash after colon)
|
|
89
|
+
# and bare drive letters like 'D:' (current directory on drive D)
|
|
90
|
+
# Convert to absolute paths since our fake fs doesn't support per-drive working directories
|
|
91
|
+
if path.match?(/\A[A-Z]:\z/) # Bare drive like 'D:'
|
|
92
|
+
path = "#{path}/"
|
|
93
|
+
elsif path.match?(%r{\A[A-Z]:[^/]}) # Drive-relative like 'D:foo' or 'D:.'
|
|
94
|
+
path = path.sub(/\A([A-Z]):/, '\1:/')
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# Convert bare '/' to platform root on Windows
|
|
98
|
+
if path == '/'
|
|
99
|
+
platform_root
|
|
100
|
+
elsif path.start_with?('/') && !path.match?(%r{\A[A-Z]:/})
|
|
101
|
+
# Convert '/foo' to 'D:/foo' on Windows
|
|
102
|
+
"#{platform_root}#{path[1..]}"
|
|
103
|
+
else
|
|
104
|
+
path
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength
|
|
108
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
|
109
|
+
|
|
25
110
|
require 'memfs/file_system'
|
|
26
111
|
require 'memfs/dir'
|
|
27
112
|
require 'memfs/file'
|
|
@@ -70,16 +155,18 @@ module MemFs
|
|
|
70
155
|
#
|
|
71
156
|
# @see #deactivate!
|
|
72
157
|
# @return nothing.
|
|
73
|
-
def activate!
|
|
158
|
+
def activate!(clear: true)
|
|
74
159
|
Object.class_eval do
|
|
75
160
|
remove_const :Dir
|
|
76
161
|
remove_const :File
|
|
162
|
+
remove_const :IO
|
|
77
163
|
|
|
78
164
|
const_set :Dir, MemFs::Dir
|
|
165
|
+
const_set :IO, MemFs::IO
|
|
79
166
|
const_set :File, MemFs::File
|
|
80
167
|
end
|
|
81
168
|
|
|
82
|
-
MemFs::FileSystem.instance.clear!
|
|
169
|
+
MemFs::FileSystem.instance.clear! if clear
|
|
83
170
|
end
|
|
84
171
|
module_function :activate!
|
|
85
172
|
|
|
@@ -93,22 +180,42 @@ module MemFs
|
|
|
93
180
|
Object.class_eval do
|
|
94
181
|
remove_const :Dir
|
|
95
182
|
remove_const :File
|
|
183
|
+
remove_const :IO
|
|
96
184
|
|
|
97
185
|
const_set :Dir, MemFs::OriginalDir
|
|
186
|
+
const_set :IO, MemFs::OriginalIO
|
|
98
187
|
const_set :File, MemFs::OriginalFile
|
|
99
188
|
end
|
|
100
189
|
end
|
|
101
190
|
module_function :deactivate!
|
|
102
191
|
|
|
192
|
+
# Switches back to the original file system, calls the given block (if any),
|
|
193
|
+
# and switches back afterwards.
|
|
194
|
+
#
|
|
195
|
+
# If a block is given, all file & dir operations (like reading dir contents or
|
|
196
|
+
# requiring files) will operate on the original fs.
|
|
197
|
+
#
|
|
198
|
+
# @example
|
|
199
|
+
# MemFs.halt do
|
|
200
|
+
# puts Dir.getwd
|
|
201
|
+
# end
|
|
202
|
+
# @return nothing
|
|
203
|
+
def halt
|
|
204
|
+
deactivate!
|
|
205
|
+
|
|
206
|
+
yield if block_given?
|
|
207
|
+
ensure
|
|
208
|
+
activate!(clear: false)
|
|
209
|
+
end
|
|
210
|
+
module_function :halt
|
|
211
|
+
|
|
103
212
|
# Creates a file and all its parent directories.
|
|
104
213
|
#
|
|
105
214
|
# @param path: The path of the file to create.
|
|
106
215
|
#
|
|
107
216
|
# @return nothing.
|
|
108
217
|
def touch(*paths)
|
|
109
|
-
if ::File != MemFs::File
|
|
110
|
-
fail 'Always call MemFs.touch inside a MemFs active context.'
|
|
111
|
-
end
|
|
218
|
+
fail 'Always call MemFs.touch inside a MemFs active context.' if ::File != MemFs::File
|
|
112
219
|
|
|
113
220
|
paths.each do |path|
|
|
114
221
|
FileUtils.mkdir_p File.dirname(path)
|
data/memfs.gemspec
CHANGED
|
@@ -12,20 +12,6 @@ Gem::Specification.new do |gem|
|
|
|
12
12
|
'for tests. Strongly inspired by FakeFS.'
|
|
13
13
|
gem.summary = "memfs-#{MemFs::VERSION}"
|
|
14
14
|
gem.homepage = 'http://github.com/simonc/memfs'
|
|
15
|
-
|
|
16
15
|
gem.license = 'MIT'
|
|
17
|
-
|
|
18
16
|
gem.files = `git ls-files`.split($/)
|
|
19
|
-
gem.executables = gem.files.grep(/^bin\//).map { |f| File.basename(f) }
|
|
20
|
-
gem.test_files = gem.files.grep(/^(test|spec|features)\//)
|
|
21
|
-
gem.require_paths = ['lib']
|
|
22
|
-
|
|
23
|
-
gem.add_development_dependency 'coveralls', '~> 0.6'
|
|
24
|
-
gem.add_development_dependency 'rake', '~> 10.0'
|
|
25
|
-
gem.add_development_dependency 'rspec', '~> 3.0'
|
|
26
|
-
gem.add_development_dependency 'guard', '~> 2.6'
|
|
27
|
-
gem.add_development_dependency 'guard-rspec', '~> 4.3'
|
|
28
|
-
gem.add_development_dependency 'rb-inotify', '~> 0.8'
|
|
29
|
-
gem.add_development_dependency 'rb-fsevent', '~> 0.9'
|
|
30
|
-
gem.add_development_dependency 'rb-fchange', '~> 0.0'
|
|
31
17
|
end
|