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)