grimoire

package module
v1.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 11, 2020 License: MIT Imports: 9 Imported by: 0

README

grimoire

GoDoc Build Status Go Report Card Maintainability Test Coverage FOSSA Status

Grimoire is a database access layer inspired by Ecto. It features a flexible query API and built-in validation. It currently supports MySQL, PostgreSQL, and SQLite3 but a custom adapter can be implemented easily using the Adapter interface.

Features:

  • Query Builder
  • Association Preloading
  • Struct style create and update
  • Changeset Style create and update
  • Builtin validation using changeset
  • Multi adapter support
  • Logger

Motivation

Common go ORM accepts struct as a value for modifying records which has a problem of unable to differentiate between an empty, nil, or undefined value. It's a tricky problem especially when you want to have an endpoint that supports partial updates. Grimoire attempts to solve that problem by integrating Changeset system inspired from Elixir's Ecto. Changeset is a form like entity which allows us to not only solve that problem but also help us with casting, validations, and constraints check.

Install

go get github.com/Fs02/grimoire

Quick Start

package main

import (
	"time"

	"github.com/Fs02/grimoire"
	"github.com/Fs02/grimoire/adapter/mysql"
	"github.com/Fs02/grimoire/changeset"
	"github.com/Fs02/grimoire/params"
)

type Product struct {
	ID        int
	Name      string
	Price     int
	CreatedAt time.Time
	UpdatedAt time.Time
}

// ChangeProduct prepares data before database operation.
// Such as casting value to appropriate types and perform validations.
func ChangeProduct(product interface{}, params params.Params) *changeset.Changeset {
	ch := changeset.Cast(product, params, []string{"name", "price"})
	changeset.ValidateRequired(ch, []string{"name", "price"})
	changeset.ValidateMin(ch, "price", 100)
	return ch
}

func main() {
	// initialize mysql adapter.
	adapter, err := mysql.Open("root@(127.0.0.1:3306)/db?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err)
	}
	defer adapter.Close()

	// initialize grimoire's repo.
	repo := grimoire.New(adapter)

	var product Product

	// Inserting Products.
	// Changeset is used when creating or updating your data.
	ch := ChangeProduct(product, params.Map{
		"name":  "shampoo",
		"price": 1000,
	})

	if ch.Error() != nil {
		// handle error
	}

	// Changeset can also be created directly from json string.
	jsonch := ChangeProduct(product, params.ParseJSON(`{
		"name":  "soap",
		"price": 2000,
	}`))

	// Create products with changeset and return the result to &product,
	if err = repo.From("products").Insert(&product, ch); err != nil {
		// handle error
	}

	// or panic when insertion pailed
	repo.From("products").MustInsert(&product, jsonch)

	// Querying Products.
	// Find a product with id 1.
	repo.From("products").Find(1).MustOne(&product)

	// Updating Products.
	// Update products with id=1.
	repo.From("products").Find(1).MustUpdate(&product, ch)

	// Deleting Products.
	// Delete Product with id=1.
	repo.From("products").Find(1).MustDelete()
}

Examples

Documentation

Guides: https://fs02.github.io/grimoire

API Documentation: https://godoc.org/github.com/Fs02/grimoire

License

Released under the MIT License

FOSSA Status

Documentation

Overview

Package grimoire is a data access layer and validation for go.

Grimoire is a database access layer inspired by Ecto's changeset system. It features flexible query API and builtin validation. It currently supports MySQL, PostgreSQL and SQLite3 but a custom adapter can be implemented easily using the Adapter interface.

Example
package main

import (
	"time"

	"github.com/Fs02/grimoire"
	"github.com/Fs02/grimoire/adapter/mysql"
	"github.com/Fs02/grimoire/changeset"
	"github.com/Fs02/grimoire/params"
)

type Product struct {
	ID        int
	Name      string
	Price     int
	CreatedAt time.Time
	UpdatedAt time.Time
}

// ChangeProduct prepares data before database operation.
// Such as casting value to appropriate types and perform validations.
func ChangeProduct(product interface{}, params params.Params) *changeset.Changeset {
	ch := changeset.Cast(product, params, []string{"name", "price"})
	changeset.ValidateRequired(ch, []string{"name", "price"})
	changeset.ValidateMin(ch, "price", 100)
	return ch
}

func main() {
	// initialize mysql adapter.
	adapter, err := mysql.Open("root@(127.0.0.1:3306)/db?charset=utf8&parseTime=True&loc=Local")
	if err != nil {
		panic(err)
	}
	defer adapter.Close()

	// initialize grimoire's repo.
	repo := grimoire.New(adapter)

	var product Product

	// Inserting Products.
	// Changeset is used when creating or updating your data.
	ch := ChangeProduct(product, params.Map{
		"name":  "shampoo",
		"price": 1000,
	})

	if ch.Error() != nil {
		// handle error
	}

	// Changeset can also be created directly from json string.
	jsonch := ChangeProduct(product, params.ParseJSON(`{
		"name":  "soap",
		"price": 2000,
	}`))

	// Create products with changeset and return the result to &product,
	if err = repo.From("products").Insert(&product, ch); err != nil {
		// handle error
	}

	// or panic when insertion pailed
	repo.From("products").MustInsert(&product, jsonch)

	// Querying Products.
	// Find a product with id 1.
	repo.From("products").Find(1).MustOne(&product)

	// Updating Products.
	// Update products with id=1.
	repo.From("products").Find(1).MustUpdate(&product, ch)

	// Deleting Products.
	// Delete Product with id=1.
	repo.From("products").Find(1).MustDelete()
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func DefaultLogger

func DefaultLogger(query string, duration time.Duration, err error)

DefaultLogger log query suing standard log library.

func Log

func Log(logger []Logger, statement string, duration time.Duration, err error)

Log using multiple logger. This function intended to be used within adapter.

Types

type Adapter

type Adapter interface {
	All(Query, interface{}, ...Logger) (int, error)
	Aggregate(Query, interface{}, ...Logger) error
	Insert(Query, map[string]interface{}, ...Logger) (interface{}, error)
	InsertAll(Query, []string, []map[string]interface{}, ...Logger) ([]interface{}, error)
	Update(Query, map[string]interface{}, ...Logger) error
	Delete(Query, ...Logger) error

	Begin() (Adapter, error)
	Commit() error
	Rollback() error
}

Adapter interface

type Logger

type Logger func(string, time.Duration, error)

Logger defines function signature for custom logger.

type Query

type Query struct {
	Collection      string
	Fields          []string
	AggregateField  string
	AggregateMode   string
	AsDistinct      bool
	JoinClause      []c.Join
	Condition       c.Condition
	GroupFields     []string
	HavingCondition c.Condition
	OrderClause     []c.Order
	OffsetResult    int
	LimitResult     int
	LockClause      string
	Changes         map[string]interface{}
	// contains filtered or unexported fields
}

Query defines information about query generated by query builder.

func (Query) Aggregate

func (query Query) Aggregate(mode string, field string, out interface{}) error

Aggregate calculate aggregate over the given field.

func (Query) All

func (query Query) All(record interface{}) error

All retrieves all results that match the query.

func (Query) Count

func (query Query) Count() (int, error)

Count retrieves count of results that match the query.

func (Query) Delete

func (query Query) Delete() error

Delete deletes all results that match the query.

func (Query) Distinct

func (query Query) Distinct() Query

Distinct add distinct option to select query.

func (Query) Find

func (query Query) Find(id interface{}) Query

Find adds where id=? into query. This is short cut for Where(Eq(I("id"), 1))

func (Query) FindBy

func (query Query) FindBy(col string, val interface{}) Query

FindBy adds where col=? into query.

func (Query) Group

func (query Query) Group(fields ...string) Query

Group query using fields.

func (Query) Having

func (query Query) Having(condition ...c.Condition) Query

Having adds condition for group query.

func (Query) Insert

func (query Query) Insert(record interface{}, chs ...*changeset.Changeset) error

Insert records to database.

func (Query) Join

func (query Query) Join(collection string, condition ...c.Condition) Query

Join current collection with other collection.

func (Query) JoinWith

func (query Query) JoinWith(mode string, collection string, condition ...c.Condition) Query

JoinWith current collection with other collection with custom join mode.

func (Query) Limit

func (query Query) Limit(limit int) Query

Limit result returned by database.

func (Query) Lock added in v1.3.0

func (query Query) Lock(lock ...string) Query

Lock query using pessimistic locking. Lock expression can be specified as first parameter, default to FOR UPDATE.

func (Query) MustAggregate

func (query Query) MustAggregate(mode string, field string, out interface{})

MustAggregate calculate aggregate over the given field. It'll panic if any error eccured.

func (Query) MustAll

func (query Query) MustAll(record interface{})

MustAll retrieves all results that match the query. It'll panic if any error eccured.

func (Query) MustCount

func (query Query) MustCount() int

MustCount retrieves count of results that match the query. It'll panic if any error eccured.

func (Query) MustDelete

func (query Query) MustDelete()

MustDelete deletes all results that match the query. It'll panic if any error eccured.

func (Query) MustInsert

func (query Query) MustInsert(record interface{}, chs ...*changeset.Changeset)

MustInsert records to database. It'll panic if any error occurred.

func (Query) MustOne

func (query Query) MustOne(record interface{})

MustOne retrieves one result that match the query. If no result found, it'll panic.

func (Query) MustPreload

func (query Query) MustPreload(record interface{}, field string)

MustPreload loads association with given query. It'll panic if any error occurred.

func (Query) MustSave

func (query Query) MustSave(record interface{})

MustSave puts a record to database. It'll panic if any error eccured.

func (Query) MustUpdate

func (query Query) MustUpdate(record interface{}, chs ...*changeset.Changeset)

MustUpdate records in database. It'll panic if any error occurred.

func (Query) Offset

func (query Query) Offset(offset int) Query

Offset the result returned by database.

func (Query) One

func (query Query) One(record interface{}) error

One retrieves one result that match the query. If no result found, it'll return not found error.

func (Query) OrHaving

func (query Query) OrHaving(condition ...c.Condition) Query

OrHaving behaves exactly the same as having except it combines with any previous expression by using an OR.

func (Query) OrWhere

func (query Query) OrWhere(condition ...c.Condition) Query

OrWhere behaves exactly the same as where except it combines with any previous expression by using an OR.

func (Query) Order

func (query Query) Order(order ...c.Order) Query

Order the result returned by database.

func (Query) Preload

func (query Query) Preload(record interface{}, field string) error

Preload loads association with given query.

func (Query) Save

func (query Query) Save(record interface{}) error

Save a record to database. If condition exist, it will try to update the record, otherwise it'll insert it. Save ignores id from record.

func (Query) Select

func (query Query) Select(fields ...string) Query

Select filter fields to be selected from database.

func (Query) Set

func (query Query) Set(field string, value interface{}) Query

Set value for insert or update operation that will replace changeset value.

func (Query) Update

func (query Query) Update(record interface{}, chs ...*changeset.Changeset) error

Update records in database. It'll panic if any error occurred.

func (Query) Where

func (query Query) Where(condition ...c.Condition) Query

Where expressions are used to filter the result set. If there is more than one where expression, they are combined with an and operator.

type Repo

type Repo struct {
	// contains filtered or unexported fields
}

Repo defines grimoire repository.

func New

func New(adapter Adapter) Repo

New create new repo using adapter.

func (*Repo) Adapter

func (repo *Repo) Adapter() Adapter

Adapter returns adapter of repo.

func (Repo) From

func (repo Repo) From(collection string) Query

From initiates a query for a collection.

func (*Repo) SetLogger

func (repo *Repo) SetLogger(logger ...Logger)

SetLogger replace default logger with custom logger.

func (Repo) Transaction

func (repo Repo) Transaction(fn func(Repo) error) error

Transaction performs transaction with given function argument.

Directories

Path Synopsis
adapter
mysql
Package mysql wraps mysql driver as an adapter for grimoire.
Package mysql wraps mysql driver as an adapter for grimoire.
postgres
Package postgres wraps postgres (pq) driver as an adapter for grimoire.
Package postgres wraps postgres (pq) driver as an adapter for grimoire.
specs
Package specs defines test specifications for grimoire's adapter.
Package specs defines test specifications for grimoire's adapter.
sql
Package sql is general sql adapter that wraps database/sql.
Package sql is general sql adapter that wraps database/sql.
sqlite3
Package sqlite3 wraps go-sqlite3 driver as an adapter for grimoire.
Package sqlite3 wraps go-sqlite3 driver as an adapter for grimoire.
c
Package c defines function for building condition in query.
Package c defines function for building condition in query.
Package changeset used to cast and validate data before saving it to the database.
Package changeset used to cast and validate data before saving it to the database.
Package errors wraps driver and changeset error as a grimoire's error.
Package errors wraps driver and changeset error as a grimoire's error.
Package params defines different types of params used for changeset input.
Package params defines different types of params used for changeset input.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL