Created
October 25, 2017 21:41
-
-
Save nathanborror/7d0ccc7b22370761ad26cba05c09abdc to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package ledger | |
import ( | |
"encoding/json" | |
"time" | |
"github.com/jmoiron/sqlx" | |
"github.com/jmoiron/sqlx/types" | |
uuid "github.com/satori/go.uuid" | |
) | |
// Identifider is the interface that wraps methods used to identify items. | |
type Identifider interface { | |
IdentifyID() string | |
IdentifyType() string | |
} | |
// Applyer is the interface that wraps methods for applying ledger values to | |
// the underlying types they wrap. | |
type Applyer interface { | |
ApplyID(id string) | |
ApplyTime(created, modified time.Time) | |
} | |
// Record is a storable record that typically wraps a given blob of data. | |
// It maintains an unique identifier and a creation time. | |
type Record struct { | |
db *sqlx.DB | |
err error | |
DataType string `db:"datatype"` | |
Data types.JSONText `db:"data"` | |
Time time.Time `db:"time"` | |
ID string `db:"id"` | |
} | |
// Options describes options for Record. | |
type Options struct { | |
DB *sqlx.DB | |
} | |
// Schema describes the SQL table used to store records. | |
const Schema = ` | |
CREATE TABLE IF NOT EXISTS record ( | |
added_id serial PRIMARY KEY, | |
id uuid NOT NULL, | |
datatype varchar(32) NOT NULL, | |
data jsonb, | |
time timestamp NOT NULL DEFAULT current_timestamp | |
)` | |
// NewRecord returns a new Record that wraps data. | |
func NewRecord(data Identifider, options Options) *Record { | |
r := Record{ | |
db: options.DB, | |
ID: data.IdentifyID(), | |
DataType: data.IdentifyType(), | |
} | |
if r.ID == "" { | |
r.ID = uuid.NewV4().String() | |
} | |
r.Data, r.err = json.Marshal(data) | |
return &r | |
} | |
// Read returns an existing Record that matches id. | |
func Read(id string, options Options) *Record { | |
r := Record{ID: id, db: options.DB} | |
row := r.db.QueryRow(`SELECT id,datatype,data,time FROM record WHERE id = $1 ORDER BY time DESC LIMIT 1`, r.ID) | |
r.err = row.Scan(&r.ID, &r.DataType, &r.Data, &r.Time) | |
return &r | |
} | |
// Write stores a copy of the current Record. | |
func (r *Record) Write() { | |
if r.err != nil { | |
return | |
} | |
row := r.db.QueryRow(`INSERT INTO record (id, datatype, data) VALUES ($1, $2, $3) RETURNING time`, r.ID, r.DataType, r.Data) | |
r.err = row.Scan(&r.Time) | |
} | |
// Delete clears the record data. | |
func (r *Record) Delete() { | |
if r.err != nil { | |
return | |
} | |
r.Data = []byte("{}") | |
} | |
// Restore restores the record to a given time. | |
func (r *Record) Restore(t time.Time) { | |
if r.err != nil { | |
return | |
} | |
row := r.db.QueryRow(`SELECT id,datatype,data FROM record WHERE id = $1 AND time = $2 LIMIT 1`, r.ID, t) | |
r.err = row.Scan(&r.ID, &r.DataType, &r.Data) | |
} | |
// Unmarshal parses the JSON-encoded data and stores the result in the | |
// value pointed to by v. The Record ID is applied to the data as well | |
// as the Created and Modified times. | |
func (r *Record) Unmarshal(v Applyer) { | |
if r.err != nil { | |
return | |
} | |
err := r.Data.Unmarshal(v) | |
if err != nil { | |
r.err = err | |
return | |
} | |
var created time.Time | |
row := r.db.QueryRow(`SELECT time FROM record WHERE id = $1 ORDER BY time ASC LIMIT 1`, r.ID) | |
err = row.Scan(&created) | |
if err != nil { | |
r.err = err | |
return | |
} | |
v.ApplyID(r.ID) | |
v.ApplyTime(created, r.Time) | |
} | |
// Err returns the first error that was encountered by the Record. | |
func (r *Record) Err() error { | |
return r.err | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment