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
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
// a new scribble driver, providing the directory where it will be writing to, and a qualified logger to which it can send any output.
database, err := scribble.New(dir, logger)
// a new scribble driver, providing the directory where it will be writing to,
// and a qualified logger to which it can send any output.
db, err := scribble.New(dir, logger)
if err != nil {
fmt.Println("Error", err)
}
// this is what scribble will either marshal from when writing, or unmarshal into when reading
record := Record{}
// Write a fish to the database
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
- Tests!
- Better support for sub collections
- More methods to allow different types of reads/writes
## Contributing

View File

@ -5,15 +5,11 @@
// at http://mozilla.org/MPL/2.0/.
//
// scribble is a tiny JSON flat file store. It uses transactions that tell it what
// 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
// scribble is a tiny JSON flat file store
package scribble
import (
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"os"
@ -23,7 +19,7 @@ import (
"github.com/nanobox-io/golang-hatchet"
)
const Version = "0.1.0"
const Version = "0.5.0"
type (
@ -32,24 +28,8 @@ type (
Driver struct {
mutexes map[string]sync.Mutex
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
@ -78,42 +58,24 @@ func New(dir string, logger hatchet.Logger) (*Driver, error) {
return scribble, nil
}
// Transact determins the type of transactions to be run, and calls the appropriate
// method to complete the operation
func (d *Driver) Transact(trans Transaction) (err error) {
// Write locks the database and attempts to write the record to the database under
// the [collection] specified with the [resource] name given
//
// 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
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 := d.getOrCreateMutex(collection)
mutex.Lock()
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 {
return err
}
@ -123,10 +85,10 @@ func (d *Driver) write(trans Transaction) error {
return err
}
finalPath := dir + "/" + trans.ResourceID + ".json"
finalPath := dir + "/" + resource + ".json"
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 {
return err
}
@ -135,71 +97,102 @@ func (d *Driver) write(trans Transaction) error {
return os.Rename(tmpPath, finalPath)
}
// read does the opposite operation as write. Reading a record from the database
// (named by the transaction.resourceID, found in the transaction.Collection), and
// unmarshaling the data into the transaction.Container
func (d *Driver) read(trans Transaction) error {
// Read a record from the database
//
// Example:
//
// // 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 {
return err
}
// unmarshal data into the transaction.Container
return json.Unmarshal(b, trans.Container)
}
switch {
// readAll does the same operation as read, reading all the records in the specified
// transaction.Collection
func (d *Driver) readAll(trans Transaction) error {
// if the path is a directory, attempt to read all entries into v
case fi.Mode().IsDir():
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
files, err := ioutil.ReadDir(dir)
// the files read from the database
var f []string
if err != nil {
// an error here just means an empty collection so do nothing
}
// 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(dir + "/" + file.Name())
if err != nil {
return err
}
// the files read from the database
var f []string
// append read file
f = append(f, string(b))
}
// 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(dir + "/" + file.Name())
// 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().IsDir():
// read record from database
b, err := ioutil.ReadFile(dir + ".json")
if err != nil {
return err
}
// append read file
f = append(f, string(b))
// unmarshal data into the transaction.Container
return json.Unmarshal(b, &v)
}
// unmarhsal the read files as a comma delimeted byte array
return json.Unmarshal([]byte("["+strings.Join(f, ",")+"]"), trans.Container)
return nil
}
// delete locks that database and then proceeds to remove the record (specified by
// transaction.ResourceID) from the collection
func (d *Driver) delete(trans Transaction) error {
// Delete locks that database and then attempts to remove the collection/resource
// specified by [path]
//
// 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()
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
return os.Remove(dir + "/" + trans.ResourceID + ".json")
switch {
// 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
// is being modfied to avoid unsafe operations
func (d *Driver) getOrCreateMutex(collection string) sync.Mutex {