Migrate all backends & readme to use constructorish New...
diff --git a/README.md b/README.md
index f63e748..56d5a0b 100644
--- a/README.md
+++ b/README.md
@@ -61,11 +61,11 @@
First define a package variable and set it to a pointer to a filesystem.
```go
-var AppFs afero.Fs = &afero.MemMapFs{}
+var AppFs afero.Fs = afero.NewMemMapFs()
or
-var AppFs afero.Fs = &afero.OsFs{}
+var AppFs afero.Fs = afero.NewOsFs()
```
It is important to note that if you repeat the composite literal you
will be using a completely new and isolated filesystem. In the case of
@@ -166,7 +166,7 @@
### Calling via Afero
```go
-fs := new(afero.MemMapFs)
+fs := afero.NewMemMapFs
afs := &Afero{Fs: fs}
f, err := afs.TempFile("", "ioutil-test")
```
@@ -187,39 +187,28 @@
* No test cleanup needed
One way to accomplish this is to define a variable as mentioned above.
-In your application this will be set to &afero.OsFs{} during testing you
-can set it to &afero.MemMapFs{}.
+In your application this will be set to afero.NewOsFs() during testing you
+can set it to afero.NewMemMapFs().
It wouldn't be uncommon to have each test initialize a blank slate memory
-backend. To do this I would define my `appFS = &afero.OsFs{}` somewhere
+backend. To do this I would define my `appFS = afero.NewOsFs()` somewhere
appropriate in my application code. This approach ensures that Tests are order
independent, with no test relying on the state left by an earlier test.
Then in my tests I would initialize a new MemMapFs for each test:
```go
func TestExist(t *testing.T) {
- appFS = &afero.MemMapFs{}
+ appFS = afero.NewMemMapFs()
// create test files and directories
appFS.MkdirAll("src/a", 0755))
appFS.WriteFile("src/a/b", []byte("file b"), 0644)
appFS.WriteFile("src/c", []byte("file c"), 0644)
- testExistence("src/c", true, t)
-}
-
-func testExistence(name string, e bool, t *testing.T) {
- _, err := appFS.Stat(name)
+ _, err := appFS.Stat("src/c")
if os.IsNotExist(err) {
- if e {
- t.Errorf("file \"%s\" does not exist.\n", name)
- }
- } else if err != nil {
- panic(err)
- } else {
- if !e {
- t.Errorf("file \"%s\" exists.\n", name)
- }
+ t.Errorf("file \"%s\" does not exist.\n", name)
}
}
+
```
# Available Backends
@@ -233,6 +222,11 @@
calls. It also makes it trivial to have your code use the OS during
operation and a mock filesystem during testing or as needed.
+```go
+appfs := NewOsFs()
+appfs.MkdirAll("src/a", 0755))
+```
+
## Memory Backed Storage
### MemMapFs
@@ -242,6 +236,11 @@
necessary. It is fully concurrent and will work within go routines
safely.
+```go
+mm := NewMemMapFs()
+mm.MkdirAll("src/a", 0755))
+```
+
#### InMemoryFile
As part of MemMapFs, Afero also provides an atomic, fully concurrent memory
@@ -265,7 +264,7 @@
the base path before calling the source Fs.
```go
-bp := &BasePathFs{source: &OsFs{}, path: "/base/path"}
+bp := NewBasePathFs(NewOsFs(), "/base/path")
```
### ReadOnlyFs
@@ -273,7 +272,7 @@
A thin wrapper around the source Fs providing a read only view.
```go
-fs := &ReadOnlyFs{source: &OsFs{}}
+fs := NewReadOnlyFs(NewOsFs())
_, err := fs.Create("/file.txt")
// err = syscall.EPERM
```
@@ -286,7 +285,7 @@
Directories are not filtered.
```go
-fs := &RegexpFs{re: regexp.MustCompile(`\.txt$`), source: &MemMapFs{}}
+fs := NewRegexpFs(NewMemMapFs(), regexp.MustCompile(`\.txt$`))
_, err := fs.Create("/file.html")
// err = syscall.ENOENT
```
@@ -303,7 +302,7 @@
Any Afero FileSystem can be used as an httpFs.
```go
-httpFs := &afero.HttpFs{source: <ExistingFS>}
+httpFs := afero.NewHttpFs(<ExistingFS>)
fileserver := http.FileServer(httpFs.Dir(<PATH>)))
http.Handle("/", fileserver)
```
@@ -335,9 +334,9 @@
caching layer.
```go
-base := &OsFs{}
-layer := &MemMapFs{}
-ufs := &CacheOnReadFs{base: base, layer: layer, cacheTime: 100 * time.Second}
+base := NewOsFs()
+layer := NewMemMapFs()
+ufs := NewCacheOnReadFs(base, layer, 100 * time.Second)
```
### CopyOnWriteFs()
@@ -361,17 +360,17 @@
The writable overlay layer is currently limited to MemMapFs.
```go
- base := &OsFs{}
- roBase := &ReadOnlyFs{source: base}
- ufs := &CopyOnWriteFs{base: roBase, layer: &MemMapFs{}}
+ base := NewOsFs()
+ roBase := NewReadOnlyFs(base)
+ ufs := NewCopyOnWriteFs(roBase, NewMemMapFs())
fh, _ = ufs.Create("/home/test/file2.txt")
fh.WriteString("This is a test")
fh.Close()
```
-In this example all write operations will only occur in memory (&MemMapFs{})
-leaving the base filesystem (&OsFs) untouched.
+In this example all write operations will only occur in memory (MemMapFs)
+leaving the base filesystem (OsFs) untouched.
## Desired/possible backends
diff --git a/basepath.go b/basepath.go
index 083199f..b9fa145 100644
--- a/basepath.go
+++ b/basepath.go
@@ -20,6 +20,10 @@
path string
}
+func NewBasePathFs(source Fs, path string) Fs {
+ return &BasePathFs{source: source, path: path}
+}
+
// on a file outside the base path it returns the given file name and an error,
// else the given file with the base path prepended
func (b *BasePathFs) RealPath(name string) (path string, err error) {
diff --git a/basepath_test.go b/basepath_test.go
index 0db6783..ee9f2d3 100644
--- a/basepath_test.go
+++ b/basepath_test.go
@@ -7,7 +7,7 @@
func TestBasePath(t *testing.T) {
baseFs := &MemMapFs{}
baseFs.MkdirAll("/base/path/tmp", 0777)
- bp := &BasePathFs{source: baseFs, path: "/base/path"}
+ bp := NewBasePathFs(baseFs, "/base/path")
if _, err := bp.Create("/tmp/foo"); err != nil {
t.Errorf("Failed to set real path")
diff --git a/httpFs.go b/httpFs.go
index c7f1955..c421936 100644
--- a/httpFs.go
+++ b/httpFs.go
@@ -20,6 +20,7 @@
"path"
"path/filepath"
"strings"
+ "time"
)
type httpDir struct {
@@ -48,6 +49,10 @@
source Fs
}
+func NewHttpFs(source Fs) *HttpFs {
+ return &HttpFs{source: source}
+}
+
func (h HttpFs) Dir(s string) *httpDir {
return &httpDir{basePath: s, fs: h}
}
@@ -58,6 +63,14 @@
return h.source.Create(name)
}
+func (h HttpFs) Chmod(name string, mode os.FileMode) error {
+ return h.source.Chmod(name, mode)
+}
+
+func (h HttpFs) Chtimes(name string, atime time.Time, mtime time.Time) error {
+ return h.source.Chtimes(name, atime, mtime)
+}
+
func (h HttpFs) Mkdir(name string, perm os.FileMode) error {
return h.source.Mkdir(name, perm)
}
diff --git a/memmap.go b/memmap.go
index 21fbe67..f577ac8 100644
--- a/memmap.go
+++ b/memmap.go
@@ -31,6 +31,10 @@
init sync.Once
}
+func NewMemMapFs() Fs {
+ return &MemMapFs{}
+}
+
var memfsInit sync.Once
func (m *MemMapFs) getData() map[string]*mem.FileData {
diff --git a/memmap_test.go b/memmap_test.go
index bb2769b..6e0f826 100644
--- a/memmap_test.go
+++ b/memmap_test.go
@@ -34,7 +34,7 @@
func TestPathErrors(t *testing.T) {
path := filepath.Join(".", "some", "path")
path2 := filepath.Join(".", "different", "path")
- fs := &MemMapFs{}
+ fs := NewMemMapFs()
perm := os.FileMode(0755)
// relevant functions:
diff --git a/os.go b/os.go
index 30f23ca..4c9c0f6 100644
--- a/os.go
+++ b/os.go
@@ -25,6 +25,10 @@
// (http://golang.org/pkg/os/).
type OsFs struct{}
+func NewOsFs() Fs {
+ return &OsFs{}
+}
+
func (OsFs) Name() string { return "OsFs" }
func (OsFs) Create(name string) (File, error) {
diff --git a/readonlyfs.go b/readonlyfs.go
index fa23703..f1fa55b 100644
--- a/readonlyfs.go
+++ b/readonlyfs.go
@@ -10,6 +10,10 @@
source Fs
}
+func NewReadOnlyFs(source Fs) Fs {
+ return &ReadOnlyFs{source: source}
+}
+
func (r *ReadOnlyFs) ReadDir(name string) ([]os.FileInfo, error) {
return ReadDir(r.source, name)
}
diff --git a/regexpfs.go b/regexpfs.go
index 071b1bd..9d92dbc 100644
--- a/regexpfs.go
+++ b/regexpfs.go
@@ -16,6 +16,10 @@
source Fs
}
+func NewRegexpFs(source Fs, re *regexp.Regexp) Fs {
+ return &RegexpFs{source: source, re: re}
+}
+
type RegexpFile struct {
f File
re *regexp.Regexp
diff --git a/ro_regexp_test.go b/ro_regexp_test.go
index d408fab..ef8a35d 100644
--- a/ro_regexp_test.go
+++ b/ro_regexp_test.go
@@ -20,7 +20,7 @@
fh.Write([]byte("content here"))
fh.Close()
- fs := &ReadOnlyFs{source: mfs}
+ fs := NewReadOnlyFs(mfs)
err = fs.Remove("/file.txt")
if err == nil {
t.Errorf("Did not fail to remove file")
@@ -51,7 +51,7 @@
}
func TestFilterRegexp(t *testing.T) {
- fs := &RegexpFs{re: regexp.MustCompile(`\.txt$`), source: &MemMapFs{}}
+ fs := NewRegexpFs(&MemMapFs{}, regexp.MustCompile(`\.txt$`))
_, err := fs.Create("/file.html")
if err == nil {
diff --git a/union.go b/union.go
index 977232b..a854e5b 100644
--- a/union.go
+++ b/union.go
@@ -21,8 +21,8 @@
// successful read in the overlay will move the cursor position in the base layer
// by the number of bytes read.
type UnionFile struct {
- layer File
base File
+ layer File
off int
files []os.FileInfo
}
diff --git a/union_cache.go b/union_cache.go
index aadcca9..d742425 100644
--- a/union_cache.go
+++ b/union_cache.go
@@ -25,6 +25,10 @@
cacheTime time.Duration
}
+func NewCacheOnReadFs(base Fs, layer Fs, cacheTime time.Duration) Fs {
+ return &CacheOnReadFs{base: base, layer: layer, cacheTime: cacheTime}
+}
+
type cacheState int
const (
diff --git a/union_cow.go b/union_cow.go
index d01b7ae..176aeb7 100644
--- a/union_cow.go
+++ b/union_cow.go
@@ -22,6 +22,10 @@
layer Fs
}
+func NewCopyOnWriteFs(base Fs, layer Fs) Fs {
+ return &CopyOnWriteFs{base: base, layer: layer}
+}
+
func (u *CopyOnWriteFs) isBaseFile(name string) (bool, error) {
if _, err := u.layer.Stat(name); err == nil {
return false, nil
diff --git a/union_test.go b/union_test.go
index 221d032..e8d7be6 100644
--- a/union_test.go
+++ b/union_test.go
@@ -11,7 +11,7 @@
base := &MemMapFs{}
roBase := &ReadOnlyFs{source: base}
- ufs := &CopyOnWriteFs{base: roBase, layer: &MemMapFs{}}
+ ufs := NewCopyOnWriteFs(roBase, &MemMapFs{})
base.MkdirAll("/home/test", 0777)
fh, _ := base.Create("/home/test/file.txt")
@@ -86,7 +86,7 @@
base := &MemMapFs{}
layer := &MemMapFs{}
- ufs := &CacheOnReadFs{base: base, layer: layer, cacheTime: 0}
+ ufs := NewCacheOnReadFs(base, layer, 0)
base.Mkdir("/data", 0777)