fakefs 0.1.0 → 0.2.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.
data/README.markdown CHANGED
@@ -4,7 +4,7 @@ FakeFS
4
4
  Mocha is great. But when your library is all about manipulating the
5
5
  filesystem, you really want to test the behavior and not the implementation.
6
6
 
7
- If you're mocking and stubbing every call to FileUtils or File, you're
7
+ If you're mocking and stubbing every call to FileUtils or File, you're
8
8
  tightly coupling your tests with the implementation.
9
9
 
10
10
  def test_creates_directory
@@ -51,18 +51,51 @@ Don't Fake the FS Immediately
51
51
  How is this different than MockFS?
52
52
  ----------------------------------
53
53
 
54
- FakeFS provides a test suite and works with symlinks. It's also strictly a
54
+ FakeFS provides a test suite and works with symlinks. It's also strictly a
55
55
  test-time dependency: your actual library does not need to use or know about
56
56
  FakeFS.
57
57
 
58
58
 
59
59
  Speed?
60
60
  ------
61
- http://gist.github.com/156091
61
+ <http://gist.github.com/156091>
62
62
 
63
63
 
64
- Authors
65
- -------
64
+ Installation
65
+ ------------
66
66
 
67
- * Chris Wanstrath [chris@ozmm.org]
68
- * Pat Nakajima [http://github.com/nakajima]
67
+ ### [Gemcutter](http://gemcutter.org/)
68
+
69
+ $ gem install fakefs
70
+
71
+ ### [Rip](http://hellorip.com)
72
+
73
+ $ rip install git://github.com/defunkt/fakefs.git
74
+
75
+
76
+ Contributors
77
+ ------------
78
+
79
+ * Chris Wanstrath
80
+ * David Reese
81
+ * Jeff Hodges
82
+ * Jon Yurek
83
+ * Matt Freels
84
+ * Myles Eftos
85
+ * Pat Nakajima
86
+ * Rob Sanheim
87
+ * Scott Taylor
88
+ * Tymon Tobolski
89
+ * msassak
90
+
91
+
92
+ Meta
93
+ ----
94
+
95
+ * Code: `git clone git://github.com/defunkt/fakefs.git`
96
+ * Docs: <http://defunkt.github.com/fakefs>
97
+ * Bugs: <http://github.com/defunkt/fakefs/issues>
98
+ * List: <http://groups.google.com/group/fakefs>
99
+ * Test: <http://runcoderun.com/defunkt/fakefs>
100
+ * Gems: <http://gemcutter.org/gems/fakefs>
101
+ * Boss: Chris Wanstrath :: <http://github.com/defunkt>
data/Rakefile CHANGED
@@ -1,30 +1,16 @@
1
- task :default do
2
- Dir['test/*_test.rb'].each { |file| require file }
1
+ desc "Run tests"
2
+ task :test do
3
+ Dir['test/**/*_test.rb'].each { |file| require file }
3
4
  end
4
5
 
6
+ task :default => :test
7
+
5
8
  begin
6
9
  require 'jeweler'
7
10
 
8
- # We're not putting VERSION or VERSION.yml in the root,
9
- # so we have to help Jeweler find our version.
10
11
  $LOAD_PATH.unshift File.dirname(__FILE__) + '/lib'
11
12
  require 'fakefs/version'
12
13
 
13
- FakeFS::Version.instance_eval do
14
- def refresh
15
- end
16
- end
17
-
18
- class Jeweler
19
- def version_helper
20
- FakeFS::Version
21
- end
22
-
23
- def version_exists?
24
- true
25
- end
26
- end
27
-
28
14
  Jeweler::Tasks.new do |gemspec|
29
15
  gemspec.name = "fakefs"
30
16
  gemspec.summary = "A fake filesystem. Use it in your tests."
@@ -33,8 +19,15 @@ begin
33
19
  gemspec.description = "A fake filesystem. Use it in your tests."
34
20
  gemspec.authors = ["Chris Wanstrath"]
35
21
  gemspec.has_rdoc = false
22
+ gemspec.version = FakeFS::Version.to_s
36
23
  end
37
24
  rescue LoadError
38
25
  puts "Jeweler not available."
39
- puts "Install it with: gem install technicalpickles-jeweler"
26
+ puts "Install it with: gem install jeweler"
27
+ end
28
+
29
+ begin
30
+ require 'sdoc_helpers'
31
+ rescue LoadError
32
+ puts "sdoc support not enabled. Please gem install sdoc-helpers."
40
33
  end
data/lib/fakefs/base.rb CHANGED
@@ -31,7 +31,8 @@ end
31
31
  def FakeFS
32
32
  return ::FakeFS unless block_given?
33
33
  ::FakeFS.activate!
34
- yield
34
+ result = yield
35
35
  ::FakeFS.deactivate!
36
+ result
36
37
  end
37
38
 
data/lib/fakefs/dir.rb CHANGED
@@ -1,7 +1,55 @@
1
1
  module FakeFS
2
2
  class Dir
3
- def self.glob(pattern)
4
- [FileSystem.find(pattern) || []].flatten.map{|e| e.to_s}.sort
3
+ include Enumerable
4
+
5
+ def initialize(string)
6
+ raise Errno::ENOENT, string unless FileSystem.find(string)
7
+ @path = string
8
+ @open = true
9
+ @pointer = 0
10
+ @contents = [ '.', '..', ] + FileSystem.find(@path).values
11
+ end
12
+
13
+ def close
14
+ @open = false
15
+ @pointer = nil
16
+ @contents = nil
17
+ nil
18
+ end
19
+
20
+ def each(&block)
21
+ while f = read
22
+ yield f
23
+ end
24
+ end
25
+
26
+ def path
27
+ @path
28
+ end
29
+
30
+ def pos
31
+ @pointer
32
+ end
33
+
34
+ def pos=(integer)
35
+ @pointer = integer
36
+ end
37
+
38
+ def read
39
+ raise IOError, "closed directory" if @pointer == nil
40
+ n = @contents[@pointer]
41
+ @pointer += 1
42
+ n.to_s.gsub(path + '/', '') if n
43
+ end
44
+
45
+ def rewind
46
+ @pointer = 0
47
+ end
48
+
49
+ def seek(integer)
50
+ raise IOError, "closed directory" if @pointer == nil
51
+ @pointer = integer
52
+ @contents[integer]
5
53
  end
6
54
 
7
55
  def self.[](pattern)
@@ -12,12 +60,55 @@ module FakeFS
12
60
  FileSystem.chdir(dir, &blk)
13
61
  end
14
62
 
63
+ def self.chroot(string)
64
+ # Not implemented yet
65
+ end
66
+
67
+ def self.delete(string)
68
+ raise SystemCallError, "No such file or directory - #{string}" unless FileSystem.find(string).values.empty?
69
+ FileSystem.delete(string)
70
+ end
71
+
72
+ def self.entries(dirname)
73
+ raise SystemCallError, dirname unless FileSystem.find(dirname)
74
+ Dir.new(dirname).map { |file| file }
75
+ end
76
+
77
+ def self.foreach(dirname, &block)
78
+ Dir.open(dirname) { |file| yield file }
79
+ end
80
+
81
+ def self.glob(pattern)
82
+ [FileSystem.find(pattern) || []].flatten.map{|e| e.to_s}.sort
83
+ end
84
+
85
+ def self.mkdir(string, integer = 0)
86
+ parent = string.split('/')
87
+ parent.pop
88
+ raise Errno::ENOENT, "No such file or directory - #{string}" unless parent.join == "" || FileSystem.find(parent.join('/'))
89
+ FileUtils.mkdir_p(string)
90
+ end
91
+
92
+ def self.open(string, &block)
93
+ if block_given?
94
+ Dir.new(string).each { |file| yield(file) }
95
+ else
96
+ Dir.new(string)
97
+ end
98
+ end
99
+
100
+ def self.tmpdir
101
+ '/tmp'
102
+ end
103
+
15
104
  def self.pwd
16
105
  FileSystem.current_dir.to_s
17
106
  end
18
107
 
19
108
  class << self
20
109
  alias_method :getwd, :pwd
110
+ alias_method :rmdir, :delete
111
+ alias_method :unlink, :delete
21
112
  end
22
113
  end
23
114
  end
@@ -33,5 +33,13 @@ module FakeFS
33
33
  name
34
34
  end
35
35
  end
36
+
37
+ def delete(node = self)
38
+ if node == self
39
+ parent.delete(self)
40
+ else
41
+ super(node.name)
42
+ end
43
+ end
36
44
  end
37
45
  end
@@ -1,16 +1,54 @@
1
1
  module FakeFS
2
2
  class FakeFile
3
- attr_accessor :name, :parent, :content
3
+ attr_accessor :name, :parent
4
+
5
+ class Inode
6
+ def initialize(file_owner)
7
+ @content = ""
8
+ @links = [file_owner]
9
+ end
10
+
11
+ attr_accessor :content
12
+ attr_accessor :links
13
+
14
+ def link(file)
15
+ links << file unless links.include?(file)
16
+ file.inode = self
17
+ end
18
+
19
+ def unlink(file)
20
+ links.delete(file)
21
+ end
22
+ end
4
23
 
5
24
  def initialize(name = nil, parent = nil)
6
- @name = name
25
+ @name = name
7
26
  @parent = parent
8
- @content = ''
27
+ @inode = Inode.new(self)
28
+ end
29
+
30
+ attr_accessor :inode
31
+
32
+ def content
33
+ @inode.content
34
+ end
35
+
36
+ def content=(str)
37
+ @inode.content = str
38
+ end
39
+
40
+ def links
41
+ @inode.links
42
+ end
43
+
44
+ def link(other_file)
45
+ @inode.link(other_file)
9
46
  end
10
47
 
11
48
  def clone(parent = nil)
12
49
  clone = super()
13
50
  clone.parent = parent if parent
51
+ clone.inode = inode.clone
14
52
  clone
15
53
  end
16
54
 
@@ -25,5 +63,10 @@ module FakeFS
25
63
  def to_s
26
64
  File.join(parent.to_s, name)
27
65
  end
66
+
67
+ def delete
68
+ inode.unlink(self)
69
+ parent.delete(self)
70
+ end
28
71
  end
29
72
  end
@@ -15,12 +15,18 @@ module FakeFS
15
15
  FileSystem.find(target)
16
16
  end
17
17
 
18
- def method_missing(*args, &block)
19
- entry.send(*args, &block)
18
+ def delete
19
+ parent.delete(self)
20
20
  end
21
21
 
22
22
  def respond_to?(method)
23
23
  entry.respond_to?(method)
24
24
  end
25
+
26
+ private
27
+
28
+ def method_missing(*args, &block)
29
+ entry.send(*args, &block)
30
+ end
25
31
  end
26
32
  end
data/lib/fakefs/file.rb CHANGED
@@ -2,6 +2,35 @@ module FakeFS
2
2
  class File
3
3
  PATH_SEPARATOR = '/'
4
4
 
5
+ MODES = [
6
+ READ_ONLY = "r",
7
+ READ_WRITE = "r+",
8
+ WRITE_ONLY = "w",
9
+ READ_WRITE_TRUNCATE = "w+",
10
+ APPEND_WRITE_ONLY = "a",
11
+ APPEND_READ_WRITE = "a+"
12
+ ]
13
+
14
+ FILE_CREATION_MODES = MODES - [READ_ONLY, READ_WRITE]
15
+
16
+ READ_ONLY_MODES = [
17
+ READ_ONLY
18
+ ]
19
+
20
+ WRITE_ONLY_MODES = [
21
+ WRITE_ONLY,
22
+ APPEND_WRITE_ONLY
23
+ ]
24
+
25
+ TRUNCATION_MODES = [
26
+ WRITE_ONLY,
27
+ READ_WRITE_TRUNCATE
28
+ ]
29
+
30
+ def self.extname(path)
31
+ RealFile.extname(path)
32
+ end
33
+
5
34
  def self.join(*parts)
6
35
  parts * PATH_SEPARATOR
7
36
  end
@@ -14,6 +43,10 @@ module FakeFS
14
43
  alias_method :exists?, :exist?
15
44
  end
16
45
 
46
+ def self.size(path)
47
+ read(path).length
48
+ end
49
+
17
50
  def self.const_missing(name)
18
51
  RealFile.const_get(name)
19
52
  end
@@ -61,11 +94,11 @@ module FakeFS
61
94
  FileSystem.find(symlink.target).to_s
62
95
  end
63
96
 
64
- def self.open(path, mode='r')
97
+ def self.open(path, mode=READ_ONLY, perm = 0644)
65
98
  if block_given?
66
- yield new(path, mode)
99
+ yield new(path, mode, perm)
67
100
  else
68
- new(path, mode)
101
+ new(path, mode, perm)
69
102
  end
70
103
  end
71
104
 
@@ -81,13 +114,86 @@ module FakeFS
81
114
  def self.readlines(path)
82
115
  read(path).split("\n")
83
116
  end
117
+
118
+ def self.link(source, dest)
119
+ if directory?(source)
120
+ raise Errno::EPERM, "Operation not permitted - #{source} or #{dest}"
121
+ end
122
+
123
+ if !exists?(source)
124
+ raise Errno::ENOENT, "No such file or directory - #{source} or #{dest}"
125
+ end
126
+
127
+ if exists?(dest)
128
+ raise Errno::EEXIST, "File exists - #{source} or #{dest}"
129
+ end
130
+
131
+ source = FileSystem.find(source)
132
+ dest = FileSystem.add(dest, source.entry.clone)
133
+ source.link(dest)
134
+
135
+ 0
136
+ end
137
+
138
+ def self.delete(file_name, *additional_file_names)
139
+ if !exists?(file_name)
140
+ raise Errno::ENOENT, "No such file or directory - #{file_name}"
141
+ end
142
+
143
+ FileUtils.rm(file_name)
144
+
145
+ additional_file_names.each do |file_name|
146
+ FileUtils.rm(file_name)
147
+ end
148
+
149
+ additional_file_names.size + 1
150
+ end
151
+
152
+ class << self
153
+ alias_method :unlink, :delete
154
+ end
155
+
156
+ def self.symlink(source, dest)
157
+ FileUtils.ln_s(source, dest)
158
+ end
159
+
160
+ def self.stat(file)
161
+ File::Stat.new(file)
162
+ end
163
+
164
+ class Stat
165
+ def initialize(file)
166
+ if !File.exists?(file)
167
+ raise(Errno::ENOENT, "No such file or directory - #{file}")
168
+ end
169
+
170
+ @file = file
171
+ end
172
+
173
+ def symlink?
174
+ File.symlink?(@file)
175
+ end
176
+
177
+ def directory?
178
+ File.directory?(@file)
179
+ end
180
+
181
+ def nlink
182
+ FileSystem.find(@file).links.size
183
+ end
184
+ end
84
185
 
85
186
  attr_reader :path
86
- def initialize(path, mode = nil)
187
+
188
+ def initialize(path, mode = READ_ONLY, perm = nil)
87
189
  @path = path
88
190
  @mode = mode
89
191
  @file = FileSystem.find(path)
90
192
  @open = true
193
+
194
+ check_valid_mode
195
+ file_creation_mode? ? create_missing_file : check_file_existence!
196
+ truncate_file if truncation_mode?
91
197
  end
92
198
 
93
199
  def close
@@ -95,7 +201,9 @@ module FakeFS
95
201
  end
96
202
 
97
203
  def read
98
- raise IOError.new('closed stream') unless @open
204
+ raise IOError, 'closed stream' unless @open
205
+ raise IOError, 'not opened for reading' if write_only?
206
+
99
207
  @file.content
100
208
  end
101
209
 
@@ -103,16 +211,15 @@ module FakeFS
103
211
  @file
104
212
  end
105
213
 
106
- def puts(content)
107
- write(content + "\n")
214
+ def puts(*content)
215
+ content.flatten.each do |obj|
216
+ write(obj.to_s + "\n")
217
+ end
108
218
  end
109
219
 
110
220
  def write(content)
111
- raise IOError.new('closed stream') unless @open
112
-
113
- if !File.exists?(@path)
114
- @file = FileSystem.add(path, FakeFile.new)
115
- end
221
+ raise IOError, 'closed stream' unless @open
222
+ raise IOError, 'not open for writing' if read_only?
116
223
 
117
224
  @file.content += content
118
225
  end
@@ -120,5 +227,49 @@ module FakeFS
120
227
  alias_method :<<, :write
121
228
 
122
229
  def flush; self; end
230
+
231
+ private
232
+
233
+ def check_file_existence!
234
+ unless @file
235
+ raise Errno::ENOENT, "No such file or directory - #{@file}"
236
+ end
237
+ end
238
+
239
+ def check_valid_mode
240
+ if !mode_in?(MODES)
241
+ raise ArgumentError, "illegal access mode #{@mode}"
242
+ end
243
+ end
244
+
245
+ def read_only?
246
+ mode_in? READ_ONLY_MODES
247
+ end
248
+
249
+ def file_creation_mode?
250
+ mode_in? FILE_CREATION_MODES
251
+ end
252
+
253
+ def write_only?
254
+ mode_in? WRITE_ONLY_MODES
255
+ end
256
+
257
+ def truncation_mode?
258
+ mode_in? TRUNCATION_MODES
259
+ end
260
+
261
+ def mode_in?(list)
262
+ list.include?(@mode)
263
+ end
264
+
265
+ def create_missing_file
266
+ if !File.exists?(@path)
267
+ @file = FileSystem.add(path, FakeFile.new)
268
+ end
269
+ end
270
+
271
+ def truncate_file
272
+ @file.content = ""
273
+ end
123
274
  end
124
275
  end