removing transactions as really they are unneccessary and just adding unneeded complexity
This commit is contained in:
parent
bc1d1e667f
commit
08724b339d
53
README.md
53
README.md
@ -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
|
||||
|
155
scribble.go
155
scribble.go
@ -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,34 +97,34 @@ 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 {
|
||||
|
||||
dir := d.dir + "/" + trans.Collection
|
||||
// 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
|
||||
files, err := ioutil.ReadDir(dir)
|
||||
|
||||
if err != nil {
|
||||
// an error here just means an empty collection so do nothing
|
||||
// an error here just means the collection is either empty or doesn't exist
|
||||
}
|
||||
|
||||
// the files read from the database
|
||||
@ -181,24 +143,55 @@ func (d *Driver) readAll(trans Transaction) error {
|
||||
}
|
||||
|
||||
// unmarhsal the read files as a comma delimeted byte array
|
||||
return json.Unmarshal([]byte("["+strings.Join(f, ",")+"]"), trans.Container)
|
||||
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
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// unmarshal data into the transaction.Container
|
||||
return json.Unmarshal(b, &v)
|
||||
}
|
||||
|
||||
mutex := d.getOrCreateMutex(trans.Collection)
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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(path)
|
||||
mutex.Lock()
|
||||
defer mutex.Unlock()
|
||||
|
||||
dir := d.dir + "/" + trans.Collection
|
||||
|
||||
// remove record from database
|
||||
return os.Remove(dir + "/" + trans.ResourceID + ".json")
|
||||
// stat the file to determine if it is a file or dir
|
||||
fi, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// helpers
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
// getOrCreateMutex creates a new collection specific mutex any time a collection
|
||||
// is being modfied to avoid unsafe operations
|
||||
|
Loading…
Reference in New Issue
Block a user