memfs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/lib/memfs/dir.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'memfs/filesystem_access'
2
+
3
+ module MemFs
4
+ class Dir
5
+ extend FilesystemAccess
6
+
7
+ def self.chdir(path, &block)
8
+ fs.chdir path, &block
9
+ return 0
10
+ end
11
+
12
+ def self.entries(dirname, opts = {})
13
+ fs.entries(dirname)
14
+ end
15
+
16
+ def self.exists?(path)
17
+ fs.directory?(path)
18
+ end
19
+
20
+ def self.getwd
21
+ fs.getwd
22
+ end
23
+ class << self; alias :pwd :getwd; end
24
+
25
+ def self.mkdir(path)
26
+ fs.mkdir path
27
+ end
28
+
29
+ def self.rmdir(path)
30
+ fs.rmdir path
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,51 @@
1
+ require 'memfs/fake/entry'
2
+
3
+ module MemFs
4
+ module Fake
5
+ class Directory < Entry
6
+ attr_accessor :entries
7
+
8
+ def add_entry(entry)
9
+ entries[entry.name] = entry
10
+ entry.parent = self
11
+ end
12
+
13
+ def empty?
14
+ (entries.keys - %w[. ..]).empty?
15
+ end
16
+
17
+ def entry_names
18
+ entries.keys
19
+ end
20
+
21
+ def find(path)
22
+ path = path.sub(/\A\/+/, '')
23
+ parts = path.split('/', 2)
24
+
25
+ if entry_names.include?(path)
26
+ entries[path]
27
+ elsif entry_names.include?(parts.first)
28
+ entries[parts.first].find(parts.last)
29
+ end
30
+ end
31
+
32
+ def initialize(*args)
33
+ super
34
+ self.entries = { '.' => self, '..' => nil }
35
+ end
36
+
37
+ def parent=(parent)
38
+ super
39
+ entries['..'] = parent
40
+ end
41
+
42
+ def path
43
+ name == '/' ? '/' : super
44
+ end
45
+
46
+ def remove_entry(entry)
47
+ entries.delete(entry.name)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,73 @@
1
+ module MemFs
2
+ module Fake
3
+ class Entry
4
+ UREAD = 00100
5
+ UWRITE = 00200
6
+ UEXEC = 00400
7
+ GREAD = 00010
8
+ GWRITE = 00020
9
+ GEXEC = 00040
10
+ OREAD = 00001
11
+ OWRITE = 00002
12
+ OEXEC = 00004
13
+ RSTICK = 01000
14
+ USTICK = 05000
15
+
16
+ attr_accessor :atime,
17
+ :gid,
18
+ :mtime,
19
+ :name,
20
+ :parent,
21
+ :uid
22
+ attr_reader :mode
23
+
24
+ def blksize
25
+ 4096
26
+ end
27
+
28
+ def delete
29
+ parent.remove_entry self
30
+ end
31
+
32
+ def dereferenced
33
+ self
34
+ end
35
+
36
+ def dev
37
+ @dev ||= rand(1000)
38
+ end
39
+
40
+ def find(path)
41
+ raise Errno::ENOTDIR, self.path
42
+ end
43
+
44
+ def initialize(path = nil)
45
+ current_user = Etc.getpwuid
46
+ time = Time.now
47
+ self.atime = time
48
+ self.gid = current_user.gid
49
+ self.mode = 0666 - MemFs::File.umask
50
+ self.mtime = time
51
+ self.name = MemFs::File.basename(path || '')
52
+ self.uid = current_user.uid
53
+ end
54
+
55
+ def ino
56
+ @ino ||= rand(1000)
57
+ end
58
+
59
+ def mode=(mode_int)
60
+ @mode = 0100000 | mode_int
61
+ end
62
+
63
+ def path
64
+ parts = [parent && parent.path, name].compact
65
+ MemFs::File.join(parts)
66
+ end
67
+
68
+ def touch
69
+ self.atime = self.mtime = Time.now
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,47 @@
1
+ require "delegate"
2
+
3
+ module MemFs
4
+ module Fake
5
+ class File < Entry
6
+
7
+ class Content < SimpleDelegator
8
+ attr_accessor :pos
9
+
10
+ def close
11
+ end
12
+
13
+ def initialize(obj = '')
14
+ @string = obj.to_s.dup
15
+ @pos = 0
16
+
17
+ __setobj__ @string
18
+ end
19
+
20
+ def puts(*strings)
21
+ strings.each do |str|
22
+ @string << str
23
+ @string << $/ unless str.end_with?($/)
24
+ end
25
+ end
26
+
27
+ def read(length = nil, buffer = '')
28
+ length ||= @string.length - @pos
29
+ buffer.replace @string[@pos, length]
30
+ @pos += buffer.bytesize
31
+ buffer.empty? ? nil : buffer
32
+ end
33
+
34
+ def to_s
35
+ @string
36
+ end
37
+
38
+ def write(string)
39
+ text = string.to_s
40
+ @string << text
41
+ text.size
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,36 @@
1
+ require 'memfs/fake/entry'
2
+ require 'memfs/fake/file/content'
3
+
4
+ module MemFs
5
+ module Fake
6
+ class File < Entry
7
+ attr_accessor :content
8
+
9
+ def close
10
+ @closed = true
11
+ end
12
+
13
+ def closed?
14
+ @closed
15
+ end
16
+
17
+ def initialize(*args)
18
+ super
19
+ @content = Content.new
20
+ @closed = false
21
+ end
22
+
23
+ def pos
24
+ content.pos
25
+ end
26
+
27
+ def pos=(value)
28
+ content.pos = value
29
+ end
30
+
31
+ def size
32
+ content.size
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ require 'memfs/filesystem_access'
2
+
3
+ module MemFs
4
+ module Fake
5
+ class Symlink < Entry
6
+ include MemFs::FilesystemAccess
7
+
8
+ attr_reader :target
9
+
10
+ def dereferenced
11
+ @dereferenced ||= fs.find!(target).dereferenced
12
+ end
13
+
14
+ def initialize(path, target)
15
+ super(path)
16
+ @target = target
17
+ end
18
+
19
+ def method_missing(meth, *args, &block)
20
+ if dereferenced.respond_to?(meth)
21
+ dereferenced.public_send(meth, *args, &block)
22
+ else
23
+ super
24
+ end
25
+ end
26
+
27
+ def respond_to_missing?(meth, include_private)
28
+ dereferenced.respond_to?(meth, include_private) || super
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,48 @@
1
+ require 'forwardable'
2
+ require 'memfs/filesystem_access'
3
+
4
+ module MemFs
5
+ class File
6
+ class Stat
7
+ extend Forwardable
8
+ include FilesystemAccess
9
+
10
+ attr_reader :entry
11
+
12
+ def_delegators :entry,
13
+ :atime,
14
+ :blksize,
15
+ :dev,
16
+ :gid,
17
+ :ino,
18
+ :mode,
19
+ :mtime,
20
+ :uid
21
+
22
+ def directory?
23
+ File.directory? entry.path
24
+ end
25
+
26
+ def file?
27
+ File.file? entry.path
28
+ end
29
+
30
+ def initialize(path, dereference = false)
31
+ entry = fs.find!(path)
32
+ @entry = dereference ? entry.dereferenced : entry
33
+ end
34
+
35
+ def sticky?
36
+ !!(entry.mode & Fake::Entry::USTICK).nonzero?
37
+ end
38
+
39
+ def symlink?
40
+ File.symlink? entry.path
41
+ end
42
+
43
+ def world_writable?
44
+ entry.mode if (entry.mode & Fake::Entry::OWRITE).nonzero?
45
+ end
46
+ end
47
+ end
48
+ end
data/lib/memfs/file.rb ADDED
@@ -0,0 +1,284 @@
1
+ require 'forwardable'
2
+ require 'memfs/filesystem_access'
3
+
4
+ module MemFs
5
+ class File
6
+ extend FilesystemAccess
7
+ extend SingleForwardable
8
+ include FilesystemAccess
9
+
10
+ OriginalFile.constants.grep(/^[A-Z_]+$/).each do |const|
11
+ const_set const, OriginalFile.const_get(const)
12
+ end
13
+
14
+ MODE_MAP = {
15
+ 'r' => RDONLY,
16
+ 'r+' => RDWR,
17
+ 'w' => CREAT|TRUNC|WRONLY,
18
+ 'w+' => CREAT|TRUNC|RDWR,
19
+ 'a' => CREAT|APPEND|WRONLY,
20
+ 'a+' => CREAT|APPEND|RDWR
21
+ }
22
+
23
+ SUCCESS = 0
24
+
25
+ def_delegators :original_file_class,
26
+ :basename,
27
+ :dirname,
28
+ :join,
29
+ :path
30
+
31
+ def self.atime(path)
32
+ stat(path).atime
33
+ end
34
+
35
+ def self.chmod(mode_int, *paths)
36
+ paths.each do |path|
37
+ fs.chmod mode_int, path
38
+ end
39
+ end
40
+
41
+ def self.chown(uid, gid, *paths)
42
+ paths.each do |path|
43
+ fs.chown(uid, gid, path)
44
+ end
45
+ paths.size
46
+ end
47
+
48
+ def self.directory?(path)
49
+ fs.directory? path
50
+ end
51
+
52
+ def self.exists?(path)
53
+ not fs.find(path).nil?
54
+ end
55
+ class << self; alias :exist? :exists?; end
56
+
57
+ def self.expand_path(file_name, dir_string = fs.pwd)
58
+ original_file_class.expand_path(file_name, dir_string)
59
+ end
60
+
61
+ def self.file?(path)
62
+ fs.find(path).is_a?(Fake::File)
63
+ end
64
+
65
+ def self.identical?(path1, path2)
66
+ fs.find!(path1).dereferenced === fs.find!(path2).dereferenced
67
+ rescue Errno::ENOENT
68
+ false
69
+ end
70
+
71
+ def self.lchmod(mode_int, *file_names)
72
+ file_names.each do |file_name|
73
+ fs.chmod mode_int, file_name
74
+ end
75
+ end
76
+
77
+ def self.lchown(uid, gid, *paths)
78
+ chown uid, gid, *paths
79
+ end
80
+
81
+ def self.link(old_name, new_name)
82
+ fs.link old_name, new_name
83
+ SUCCESS
84
+ end
85
+
86
+ def self.lstat(path)
87
+ Stat.new(path)
88
+ end
89
+
90
+ def self.mtime(path)
91
+ stat(path).mtime
92
+ end
93
+
94
+ def self.open(filename, mode = RDONLY, *perm_and_opt)
95
+ file = self.new(filename, mode, *perm_and_opt)
96
+
97
+ if block_given?
98
+ yield file
99
+ else
100
+ file
101
+ end
102
+ ensure
103
+ file.close if file && block_given?
104
+ end
105
+
106
+ def self.read(path, length = nil, offset = 0, mode: RDONLY, encoding: nil, open_args: nil)
107
+ open_args ||= [mode, encoding: encoding]
108
+
109
+ file = open(path, *open_args)
110
+ file.seek(offset)
111
+ file.read(length)
112
+ ensure
113
+ file.close if file
114
+ end
115
+
116
+ def self.readlink(path)
117
+ fs.find!(path).target
118
+ end
119
+
120
+ def self.rename(old_name, new_name)
121
+ fs.rename(old_name, new_name)
122
+ SUCCESS
123
+ end
124
+
125
+ def self.reset!
126
+ @umask = original_file_class.umask
127
+ end
128
+
129
+ def self.size(path)
130
+ fs.find!(path).size
131
+ end
132
+
133
+ def self.stat(path)
134
+ Stat.new(path, true)
135
+ end
136
+
137
+ def self.symlink(old_name, new_name)
138
+ fs.symlink old_name, new_name
139
+ SUCCESS
140
+ end
141
+
142
+ def self.symlink?(path)
143
+ fs.symlink? path
144
+ end
145
+
146
+ def self.umask(integer = nil)
147
+ old_value = @umask || original_file_class.umask
148
+
149
+ @umask = integer if integer
150
+
151
+ old_value
152
+ end
153
+
154
+ def self.unlink(*paths)
155
+ paths.each do |path|
156
+ fs.unlink(path)
157
+ end
158
+ paths.size
159
+ end
160
+ class << self; alias :delete :unlink; end
161
+
162
+ def self.utime(atime, mtime, *file_names)
163
+ file_names.each do |file_name|
164
+ fs.find!(file_name).atime = atime
165
+ fs.find!(file_name).mtime = mtime
166
+ end
167
+ file_names.size
168
+ end
169
+
170
+ attr_accessor :closed,
171
+ :entry,
172
+ :opening_mode
173
+ attr_reader :path
174
+
175
+ def initialize(filename, mode = RDONLY, perm = nil, opt = nil)
176
+ unless opt.nil? || opt.is_a?(Hash)
177
+ raise ArgumentError, "wrong number of arguments (4 for 1..3)"
178
+ end
179
+
180
+ @path = filename
181
+
182
+ self.opening_mode = str_to_mode_int(mode)
183
+
184
+ fs.touch(filename) if create_file?
185
+
186
+ self.entry = fs.find(filename)
187
+ end
188
+
189
+ def chmod(mode_int)
190
+ fs.chmod(mode_int, path)
191
+ SUCCESS
192
+ end
193
+
194
+ def chown(uid, gid = nil)
195
+ fs.chown(uid, gid, path)
196
+ SUCCESS
197
+ end
198
+
199
+ def close
200
+ self.closed = true
201
+ end
202
+
203
+ def closed?
204
+ closed
205
+ end
206
+
207
+ def content
208
+ entry.content
209
+ end
210
+
211
+ def lstat
212
+ File.lstat(path)
213
+ end
214
+
215
+ def pos
216
+ entry.pos
217
+ end
218
+
219
+ def puts(text)
220
+ raise IOError, 'not opened for writing' unless writable?
221
+
222
+ content.puts text
223
+ end
224
+
225
+ def read(length = nil, buffer = '')
226
+ default = length ? nil : ''
227
+ content.read(length, buffer) || default
228
+ end
229
+
230
+ def seek(amount, whence = IO::SEEK_SET)
231
+ new_pos = case whence
232
+ when IO::SEEK_CUR then entry.pos + amount
233
+ when IO::SEEK_END then content.to_s.length + amount
234
+ when IO::SEEK_SET then amount
235
+ end
236
+
237
+ if new_pos.nil? || new_pos < 0
238
+ raise Errno::EINVAL, path
239
+ end
240
+
241
+ entry.pos = new_pos and 0
242
+ end
243
+
244
+ def size
245
+ entry.size
246
+ end
247
+
248
+ def stat
249
+ File.stat(path)
250
+ end
251
+
252
+ def write(string)
253
+ raise IOError, 'not opened for writing' unless writable?
254
+
255
+ content.write(string.to_s)
256
+ end
257
+
258
+ private
259
+
260
+ def self.original_file_class
261
+ MemFs::OriginalFile
262
+ end
263
+
264
+ def str_to_mode_int(mode)
265
+ return mode unless mode.is_a?(String)
266
+
267
+ unless mode =~ /\A([rwa]\+?)([bt])?\z/
268
+ raise ArgumentError, "invalid access mode #{mode}"
269
+ end
270
+
271
+ mode_str = $~[1]
272
+ MODE_MAP[mode_str]
273
+ end
274
+
275
+ def create_file?
276
+ (opening_mode & File::CREAT).nonzero?
277
+ end
278
+
279
+ def writable?
280
+ (opening_mode & File::WRONLY).nonzero? ||
281
+ (opening_mode & File::RDWR).nonzero?
282
+ end
283
+ end
284
+ end