From 04b81d64d4bd8546512e96c49934ad14ad0473ae Mon Sep 17 00:00:00 2001 From: Steve Domino Date: Fri, 13 Nov 2015 15:19:09 -0700 Subject: [PATCH 1/3] updating the mkDir wrapper to be less weird (you'll see what i mean) --- scribble.go | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/scribble.go b/scribble.go index 0a2be6e..9682c06 100644 --- a/scribble.go +++ b/scribble.go @@ -12,7 +12,7 @@ import ( "sync" ) -const Version = "1.0.2" +const Version = "1.0.3" type ( @@ -214,14 +214,9 @@ func (d *Driver) getOrCreateMutex(collection string) sync.Mutex { func mkDir(d string) (err error) { // - dir, _ := os.Stat(d) - - switch { - case dir == nil: - err = os.MkdirAll(d, 0755) - case !dir.IsDir(): - err = os.ErrInvalid + if _, err = os.Stat(d); err != nil { + return } - return + return os.MkdirAll(d, 0755) } From f6d7310e91748ff6b9c5b4f006405872435e115b Mon Sep 17 00:00:00 2001 From: Steve Domino Date: Sun, 15 Nov 2015 23:44:00 -0700 Subject: [PATCH 2/3] updated some tests which uncovered some issues --- scribble.go | 172 +++++++++++++++++++++++--------------------- scribble_test.go | 184 ++++++++++++++++++++++++++--------------------- 2 files changed, 193 insertions(+), 163 deletions(-) diff --git a/scribble.go b/scribble.go index 9682c06..af0b7ac 100644 --- a/scribble.go +++ b/scribble.go @@ -8,7 +8,6 @@ import ( "io/ioutil" "os" "path/filepath" - "strings" "sync" ) @@ -48,8 +47,6 @@ func New(dir string, logger Logger) (driver *Driver, err error) { logger = lumber.NewConsoleLogger(lumber.INFO) } - logger.Info("Creating scribble database at '%v'...\n", dir) - // driver = &Driver{ dir: dir, @@ -57,75 +54,39 @@ func New(dir string, logger Logger) (driver *Driver, err error) { log: logger, } + logger.Info("Creating scribble database at '%v'...\n", dir) + // create database - return driver, mkDir(dir) -} - -// Read a record from the database -func (d *Driver) Read(collection, resource string, v interface{}) error { - - // - path := filepath.Join(collection, resource) - dir := filepath.Join(d.dir, path) - - // - switch fi, err := stat(dir); { - - // if fi is nil or error is not nil return - case fi == nil, err != nil: - return fmt.Errorf("Unable to find file or directory named %v\n", path) - - // if the path is a directory, attempt to read all entries into v - case fi.Mode().IsDir(): - - // read all the files in the transaction.Collection; an error here just means - // the collection is either empty or doesn't exist - files, _ := ioutil.ReadDir(dir) - - // the files read from the database - var f []string - - // iterate over each of the files, attempting to read the file. If successful - // append the files to the collection of read files - for _, file := range files { - b, err := ioutil.ReadFile(filepath.Join(dir, file.Name())) - if err != nil { - return err - } - - // append read file - f = append(f, string(b)) - } - - // unmarhsal the read files as a comma delimeted byte array - return json.Unmarshal([]byte("["+strings.Join(f, ",")+"]"), v) - - // if the path is a file, attempt to read the single file - case fi.Mode().IsRegular(): - - // read record from database - b, err := ioutil.ReadFile(dir + ".json") - if err != nil { - return err - } - - // unmarshal data - return json.Unmarshal(b, &v) - } - - return nil + return driver, os.MkdirAll(dir, 0755) } // Write locks the database and attempts to write the record to the database under // the [collection] specified with the [resource] name given func (d *Driver) Write(collection, resource string, v interface{}) error { + // ensure there is a place to save record + if collection == "" { + return fmt.Errorf("Missing collection - no place to save record!") + } + + // ensure there is a resource (name) to save record as + if resource == "" { + return fmt.Errorf("Missing resource - unable to save record (no name)!") + } + mutex := d.getOrCreateMutex(collection) mutex.Lock() defer mutex.Unlock() // dir := filepath.Join(d.dir, collection) + fnlPath := filepath.Join(dir, resource+".json") + tmpPath := fnlPath + ".tmp" + + // create collection directory + if err := os.MkdirAll(dir, 0755); err != nil { + return err + } // b, err := json.MarshalIndent(v, "", "\t") @@ -133,21 +94,84 @@ func (d *Driver) Write(collection, resource string, v interface{}) error { return err } - // create collection directory - if err := mkDir(dir); err != nil { - return err - } - - finalPath := filepath.Join(dir, resource+".json") - tmpPath := finalPath + "~" - // write marshaled data to the temp file if err := ioutil.WriteFile(tmpPath, b, 0644); err != nil { return err } // move final file into place - return os.Rename(tmpPath, finalPath) + return os.Rename(tmpPath, fnlPath) +} + +// Read a record from the database +func (d *Driver) Read(collection, resource string, v interface{}) error { + + // ensure there is a place to save record + if collection == "" { + return fmt.Errorf("Missing collection - no place to save record!") + } + + // ensure there is a resource (name) to save record as + if resource == "" { + return fmt.Errorf("Missing resource - unable to save record (no name)!") + } + + // + record := filepath.Join(d.dir, collection, resource) + + // check to see if file exists + if _, err := stat(record); err != nil { + return err + } + + // read record from database + b, err := ioutil.ReadFile(record + ".json") + if err != nil { + return err + } + + // unmarshal data + return json.Unmarshal(b, &v) +} + +// ReadAll records from a collection; this is returned as a slice of strings because +// there is no way of knowing what type they record is +func (d *Driver) ReadAll(collection string) ([]string, error) { + + // ensure there is a collection to read + if collection == "" { + return nil, fmt.Errorf("Missing collection - unable to record location!") + } + + // + dir := filepath.Join(d.dir, collection) + + // check to see if collection (directory) exists + if _, err := stat(dir); err != nil { + return nil, err + } + + // read all the files in the transaction.Collection; an error here just means + // the collection is either empty or doesn't exist + files, _ := ioutil.ReadDir(dir) + + // the files read from the database + var records []string + + // iterate over each of the files, attempting to read the file. If successful + // append the files to the collection of read files + for _, file := range files { + b, err := ioutil.ReadFile(filepath.Join(dir, file.Name())) + if err != nil { + return nil, err + } + + // append read file + records = append(records, string(b)) + } + + // unmarhsal the read files as a comma delimeted byte array + return records, nil } // Delete locks that database and then attempts to remove the collection/resource @@ -208,15 +232,3 @@ func (d *Driver) getOrCreateMutex(collection string) sync.Mutex { return m } - -// mkDir is a simple wrapper that attempts to make a directory at a specified -// location -func mkDir(d string) (err error) { - - // - if _, err = os.Stat(d); err != nil { - return - } - - return os.MkdirAll(d, 0755) -} diff --git a/scribble_test.go b/scribble_test.go index 44ab94c..116c7b4 100644 --- a/scribble_test.go +++ b/scribble_test.go @@ -6,146 +6,164 @@ import ( ) // -type Friend struct { - Name string `json:"name"` +type Fish struct { + Type string `json:"type"` } // var ( - db *Driver - testRoot = "./test_db" - friendsPath = "/friends" - friend0 = Friend{} - friend1 = Friend{Name: "wocket"} - friend2 = Friend{Name: "wasket"} + db *Driver + database = "./school" + collection = "fish" + onefish = Fish{} + twofish = Fish{} + redfish = Fish{Type: "red"} + bluefish = Fish{Type: "blue"} ) // -func init() { - startup() -} +func TestMain(m *testing.M) { -// -func startup() { - db, _ = New(testRoot, nil) -} + var err error -// -func teardown() { - os.RemoveAll(testRoot) -} - -// -func createFriend(t *testing.T) { - if err := db.Write(friendsPath, "friend1", friend1); err != nil { - t.Error("Failed to write", err) - } -} - -// -func createFriends(t *testing.T) { - if err := db.Write(friendsPath, "friend1", friend1); err != nil { - t.Error("Failed to write", err) + // create a new scribble + if db, err = New(database, nil); err != nil { + panic(err) } - if err := db.Write(friendsPath, "friend2", friend2); err != nil { - t.Error("Failed to write", err) - } + // run + code := m.Run() + + // cleanup + os.RemoveAll(database) + + // exit + os.Exit(code) } // func TestNew(t *testing.T) { - if _, err := os.Stat(testRoot); os.IsNotExist(err) { - t.Error("Expected file, got none", err) + if _, err := os.Stat(database); err != nil { + t.Error("Expected file, got none") } - - teardown() } // -func TestWrite(t *testing.T) { - createFriend(t) - teardown() -} +func TestWriteAndRead(t *testing.T) { -// -func TestRead(t *testing.T) { - createFriend(t) - - if err := db.Read(friendsPath, "friend1", &friend0); err != nil { - t.Error("Failed to read", err) + // add fish to database + if err := db.Write(collection, "redfish", redfish); err != nil { + t.Error("Create fish failed: ", err.Error()) } - if friend0.Name == "" { - t.Error("Expected friend, have none") + // read fish from database + if err := db.Read(collection, "redfish", &onefish); err != nil { + t.Error("Failed to read: ", err.Error()) } - teardown() -} - -// -func TestReadEmpty(t *testing.T) { - - if err := db.Read(friendsPath, "friend1", &friend0); err == nil { - t.Error("Expected nothing, found friend") + // + if onefish.Type != "red" { + t.Error("Expected red fish, got: ", onefish.Type) } - teardown() + destroySchool() } // func TestReadall(t *testing.T) { - createFriends(t) + createSchool() - friends := []Friend{} - if err := db.Read(friendsPath, "", &friends); err != nil { - t.Error("Failed to read", err) + fish, err := db.ReadAll(collection) + if err != nil { + t.Error("Failed to read: ", err.Error()) } - if len(friends) <= 0 { - t.Error("Expected friends, have none") + if len(fish) <= 0 { + t.Error("Expected some fish, have none") } - teardown() + destroySchool() } // -func TestReadallEmpty(t *testing.T) { +func TestWriteAndReadEmpty(t *testing.T) { - friends := []Friend{} - if err := db.Read(friendsPath, "", &friends); err == nil { - t.Error("Expected nothing, found friends") + // create a fish with no home + if err := db.Write("", "redfish", redfish); err == nil { + t.Error("Allowed write of empty resource", err.Error()) } - teardown() + // create a home with no fish + if err := db.Write(collection, "", redfish); err == nil { + t.Error("Allowed write of empty resource", err.Error()) + } + + // no place to read + if err := db.Read("", "redfish", onefish); err == nil { + t.Error("Allowed read of empty resource", err.Error()) + } + + destroySchool() } // func TestDelete(t *testing.T) { - createFriend(t) - if err := db.Delete(friendsPath, "friend1"); err != nil { - t.Error("Failed to delete", err) + // add fish to database + if err := db.Write(collection, "redfish", redfish); err != nil { + t.Error("Create fish failed: ", err.Error()) } - if fi, err := os.Stat(friendsPath + "/friend1"); fi != nil { - t.Error("Expected nothing, have friends", err) + // delete the fish + if err := db.Delete(collection, "redfish"); err != nil { + t.Error("Failed to delete: ", err.Error()) } - teardown() + // read fish from database + if err := db.Read(collection, "redfish", &onefish); err == nil { + t.Error("Expected nothing, got fish") + } + + destroySchool() } // func TestDeleteall(t *testing.T) { - createFriends(t) + createSchool() - if err := db.Delete(friendsPath, ""); err != nil { - t.Error("Failed to delete ", err) + if err := db.Delete(collection, ""); err != nil { + t.Error("Failed to delete: ", err.Error()) } - if fi, err := os.Stat(friendsPath); fi != nil { - t.Error("Expected nothing, have friends", err) + if _, err := os.Stat(collection); err == nil { + t.Error("Expected nothing, have fish") } - teardown() + destroySchool() +} + +// +func createFish(fish Fish) error { + return db.Write(collection, fish.Type, fish) +} + +// +func createSchool() error { + for _, f := range []Fish{Fish{Type: "red"}, Fish{Type: "blue"}} { + if err := db.Write(collection, f.Type, f); err != nil { + return err + } + } + + return nil +} + +// +func destroyFish(name string) error { + return db.Delete(collection, name) +} + +// +func destroySchool() error { + return db.Delete(collection, "") } From b43a2d16d25c79ab082e863b72d2a8ad176e340e Mon Sep 17 00:00:00 2001 From: Steve Domino Date: Mon, 16 Nov 2015 10:46:22 -0700 Subject: [PATCH 3/3] updating some comments --- scribble.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/scribble.go b/scribble.go index af0b7ac..6e84d8e 100644 --- a/scribble.go +++ b/scribble.go @@ -42,6 +42,13 @@ func New(dir string, logger Logger) (driver *Driver, err error) { // dir = filepath.Clean(dir) + // ensure the database location doesn't already exist (we don't want to overwrite + // any existing files/database) + if _, err := os.Stat(dir); err == nil { + fmt.Printf("Unable to create database, '%s' already exists. Please specify a different location.\n", dir) + os.Exit(1) + } + // if logger == nil { logger = lumber.NewConsoleLogger(lumber.INFO) @@ -135,7 +142,7 @@ func (d *Driver) Read(collection, resource string, v interface{}) error { } // ReadAll records from a collection; this is returned as a slice of strings because -// there is no way of knowing what type they record is +// there is no way of knowing what type the record is. func (d *Driver) ReadAll(collection string) ([]string, error) { // ensure there is a collection to read