memfs 0.0.1

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.
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