removing transactions as really they are unneccessary and just adding unneeded complexity

This commit is contained in:
Steve Domino 2015-10-12 13:30:50 -06:00
parent bc1d1e667f
commit 08724b339d
2 changed files with 114 additions and 116 deletions

View File

@ -11,37 +11,40 @@ Install using `go get github.com/nanobox-io/golang-scribble`.
### Usage ### Usage
Create a 'transaction' for scribble to transact.
`t := scribble.Transaction{Action: "read", Collection: "records", ResourceID: "<UniqueID>", Container: &v}`
+ Action - the action for scribble to perform
+ write - write to the scribble db
+ read - read from the scribble db
+ readall - read from the scribble db (all files in a collection)
+ delete - remove a record from the scribble db
+ Collection - the folder scribble will create to store grouped records
+ ResourceID - the unique ID of the resource being stored (bson, uuid, etc.)
+ Container - the Struct that contains the data scribble will marshal into the store, or what it will unmarshal into from the store
#### Full Example
```go ```go
// a new scribble driver, providing the directory where it will be writing to, and a qualified logger to which it can send any output. // a new scribble driver, providing the directory where it will be writing to,
database, err := scribble.New(dir, logger) // and a qualified logger to which it can send any output.
db, err := scribble.New(dir, logger)
if err != nil { if err != nil {
fmt.Println("Error", err) fmt.Println("Error", err)
} }
// this is what scribble will either marshal from when writing, or unmarshal into when reading // Write a fish to the database
record := Record{} fish := Fish{}
if err := db.Write("fish", "onefish", fish); err != nil {
// create a new transaction for scribble to run }
t := scribble.Transaction{Action: scribble.READ, Collection: "records", ResourceID: "<UniqueID>", Container: &record}
// Read all fish from the database
fish := []Fish{}
if err := db.Read("fish", fish); err != nil {
}
// Read a fish from the database
fish := Fish{}
if err := db.Read("/fish/onefish", fish); err != nil {
}
// Delete all fish from the database
if err := db.Delete("/fish"); err != nil {
}
// Delete a fish from the database
if err := db.Delete("/fish/onefish"); err != nil {
// have scribble attempt to run the transaction
if err := database.Transact(t); err != nil {
fmt.Println("Error", err)
} }
``` ```
@ -54,6 +57,8 @@ Complete documentation is available on [godoc](http://godoc.org/github.com/nanob
## Todo/Doing ## Todo/Doing
- Tests! - Tests!
- Better support for sub collections
- More methods to allow different types of reads/writes
## Contributing ## Contributing

View File

@ -5,15 +5,11 @@
// at http://mozilla.org/MPL/2.0/. // at http://mozilla.org/MPL/2.0/.
// //
// scribble is a tiny JSON flat file store. It uses transactions that tell it what // scribble is a tiny JSON flat file store
// actions to perform, where it is to store data, and what it is going to write
// that data from, or read the data into. It creates a very simple database
// structure under a specified directory
package scribble package scribble
import ( import (
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"os" "os"
@ -23,7 +19,7 @@ import (
"github.com/nanobox-io/golang-hatchet" "github.com/nanobox-io/golang-hatchet"
) )
const Version = "0.1.0" const Version = "0.5.0"
type ( type (
@ -32,24 +28,8 @@ type (
Driver struct { Driver struct {
mutexes map[string]sync.Mutex mutexes map[string]sync.Mutex
dir string // the directory where scribble will create the database dir string // the directory where scribble will create the database
log hatchet.Logger // the logger scirbble will log to log hatchet.Logger // the logger scribble will log to
} }
// a Transactions is what is used by a Driver to complete database operations
Transaction struct {
Action int // the action for scribble to preform
Collection string // the forlder for scribble to read/write to/from
ResourceID string // the unique ID of the record
Container interface{} // what scribble will marshal from or unmarshal into
}
)
//
const (
WRITE = iota
READ
READALL
DELETE
) )
// New creates a new scribble database at the desired directory location, and // New creates a new scribble database at the desired directory location, and
@ -78,42 +58,24 @@ func New(dir string, logger hatchet.Logger) (*Driver, error) {
return scribble, nil return scribble, nil
} }
// Transact determins the type of transactions to be run, and calls the appropriate // Write locks the database and attempts to write the record to the database under
// method to complete the operation // the [collection] specified with the [resource] name given
func (d *Driver) Transact(trans Transaction) (err error) { //
// Example:
//
// // write [fish] to database
// Write("/fish/onefish", fish{name:"onefish"})
func (d *Driver) Write(collection, resource string, v interface{}) error {
// determin transaction to be run mutex := d.getOrCreateMutex(collection)
switch trans.Action {
case WRITE:
return d.write(trans)
case READ:
return d.read(trans)
case READALL:
return d.readAll(trans)
case DELETE:
return d.delete(trans)
default:
return errors.New(fmt.Sprintf("Unsupported action %+v", trans.Action))
}
return
}
// write locks the database and then proceeds to write the data represented by a
// transaction.Container. It will create a direcotry that represents the collection
// to wich the record belongs (if it doesn't already exist), and write a file
// (named by he transaction.ResourceID) to that directory
func (d *Driver) write(trans Transaction) error {
mutex := d.getOrCreateMutex(trans.Collection)
mutex.Lock() mutex.Lock()
defer mutex.Unlock() defer mutex.Unlock()
// //
dir := d.dir + "/" + trans.Collection dir := d.dir + "/" + collection
// //
b, err := json.MarshalIndent(trans.Container, "", "\t") b, err := json.MarshalIndent(v, "", "\t")
if err != nil { if err != nil {
return err return err
} }
@ -123,10 +85,10 @@ func (d *Driver) write(trans Transaction) error {
return err return err
} }
finalPath := dir + "/" + trans.ResourceID + ".json" finalPath := dir + "/" + resource + ".json"
tmpPath := finalPath + "~" tmpPath := finalPath + "~"
// write marshaled data to a file, named by the resourceID // write marshaled data to the temp file
if err := ioutil.WriteFile(tmpPath, b, 0644); err != nil { if err := ioutil.WriteFile(tmpPath, b, 0644); err != nil {
return err return err
} }
@ -135,71 +97,102 @@ func (d *Driver) write(trans Transaction) error {
return os.Rename(tmpPath, finalPath) return os.Rename(tmpPath, finalPath)
} }
// read does the opposite operation as write. Reading a record from the database // Read a record from the database
// (named by the transaction.resourceID, found in the transaction.Collection), and //
// unmarshaling the data into the transaction.Container // Example:
func (d *Driver) read(trans Transaction) error { //
// // read a single fish
// Read("/fish/twofish", &fish)
//
// // read all fish
// Read("/fish", &fish)
func (d *Driver) Read(path string, v interface{}) error {
dir := d.dir + "/" + trans.Collection dir := d.dir + "/" + path
// read record from database //
b, err := ioutil.ReadFile(dir + "/" + trans.ResourceID + ".json") fi, err := os.Stat(path)
if err != nil { if err != nil {
return err return err
} }
// unmarshal data into the transaction.Container switch {
return json.Unmarshal(b, trans.Container)
}
// readAll does the same operation as read, reading all the records in the specified // if the path is a directory, attempt to read all entries into v
// transaction.Collection case fi.Mode().IsDir():
func (d *Driver) readAll(trans Transaction) error {
dir := d.dir + "/" + trans.Collection // read all the files in the transaction.Collection
files, err := ioutil.ReadDir(dir)
if err != nil {
// an error here just means the collection is either empty or doesn't exist
}
// read all the files in the transaction.Collection // the files read from the database
files, err := ioutil.ReadDir(dir) var f []string
if err != nil { // iterate over each of the files, attempting to read the file. If successful
// an error here just means an empty collection so do nothing // append the files to the collection of read files
} for _, file := range files {
b, err := ioutil.ReadFile(dir + "/" + file.Name())
if err != nil {
return err
}
// the files read from the database // append read file
var f []string f = append(f, string(b))
}
// iterate over each of the files, attempting to read the file. If successful // unmarhsal the read files as a comma delimeted byte array
// append the files to the collection of read files return json.Unmarshal([]byte("["+strings.Join(f, ",")+"]"), v)
for _, file := range files {
b, err := ioutil.ReadFile(dir + "/" + file.Name()) // if the path is a file, attempt to read the single file
case !fi.Mode().IsDir():
// read record from database
b, err := ioutil.ReadFile(dir + ".json")
if err != nil { if err != nil {
return err return err
} }
// append read file // unmarshal data into the transaction.Container
f = append(f, string(b)) return json.Unmarshal(b, &v)
} }
// unmarhsal the read files as a comma delimeted byte array return nil
return json.Unmarshal([]byte("["+strings.Join(f, ",")+"]"), trans.Container)
} }
// delete locks that database and then proceeds to remove the record (specified by // Delete locks that database and then attempts to remove the collection/resource
// transaction.ResourceID) from the collection // specified by [path]
func (d *Driver) delete(trans Transaction) error { //
// Example:
//
// // delete the fish 'redfish.json'
// Delete("/fish/redfish")
//
// // delete the fish collection
// Delete("/fish")
func (d *Driver) Delete(path string) error {
mutex := d.getOrCreateMutex(trans.Collection) mutex := d.getOrCreateMutex(path)
mutex.Lock() mutex.Lock()
defer mutex.Unlock() defer mutex.Unlock()
dir := d.dir + "/" + trans.Collection // stat the file to determine if it is a file or dir
fi, err := os.Stat(path)
if err != nil {
return err
}
// remove record from database switch {
return os.Remove(dir + "/" + trans.ResourceID + ".json") // remove the collection from database
case fi.Mode().IsDir():
return os.Remove(d.dir + "/" + path)
// remove the record from database
default:
return os.Remove(d.dir + "/" + path + ".json")
}
} }
// helpers
// getOrCreateMutex creates a new collection specific mutex any time a collection // getOrCreateMutex creates a new collection specific mutex any time a collection
// is being modfied to avoid unsafe operations // is being modfied to avoid unsafe operations
func (d *Driver) getOrCreateMutex(collection string) sync.Mutex { func (d *Driver) getOrCreateMutex(collection string) sync.Mutex {