surf

package module
v2.1.0+incompatible Latest Latest
Warning

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

Go to latest
Published: Jul 25, 2017 License: MIT Imports: 9 Imported by: 0

README

Go Carrot

Surf

Build Status codecov Go Report Card Gitter

Declaratively convert structs into data access objects.

In Use

Before I dive into explaining how to use this library, let me first show an example of how you will interface with your models after everything is set up:

// Inserts
myAnimal := models.NewAnimal()
myAnimal.Name = "Rigby"
myAnimal.Age = 3
myAnimal.Insert()

// Loads
myAnimalCopy := models.NewAnimal()
myAnimalCopy.Id = myAnimal.Id
myAnimalCopy.Load() // After this, myAnimalCopy's Name will be "Rigby" and Age will be 3 (pulled from the database)

// Updates
myAnimalCopy.Age = 4
myAnimalCopy.Update() // Updates Age in the database

// Deletes
myAnimalCopy.Delete() // Deletes from the database

Getting Started

Setting up a basic Model

Before we start anything, we'll need to create a model. For example, we will use an Animal.

type Animal struct {
    Id   int    `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}
Embed a surf.Model

After this is set up, we can now embed a surf.Model into our model.

type Animal struct {
    surf.Model
    Id   int    `json:"id"`
    Name string `json:"name"`
    Age  int    `json:"age"`
}

A surf.Model is actually just an interface, so we'll need to decide what type of model we want to use!

There's more information below on surf.Model.

For this example, we are going to be using a surf.PqModel.

You can make the decision of what surf.Model to pack into your model at run time, but it will probably be easiest to create a constructor type function and create your models through that. Here I provide a constructor type fuction, but also a Prep function which will provide you the flexibility to not use the constructor.

func NewAnimal() *Animal {
    animal := new(Animal)
    return animal.Prep()
}

func (a *Animal) Prep() *Animal {
    a.Model = &surf.PqModel{
		Database: db.Get(), // This is a *sql.DB, with github.com/lib/pq as a driver
		Config: // TODO
	}
    return a
}
Setting up Config

You'll notice in the last code snippet in the previous section, there is a // TODO mark.

In that Prep method, we still need to define the Config.

Before going into detail, here is the Prep method with a fully filled out Config.

func (a *Animal) Prep() *Animal {
	a.Model = &surf.PqModel{
		Database: db.Get(),
		Config: surf.Configuration{
			TableName: "animals",
			Fields: []surf.Field{
				{
					Pointer:          &a.Id,
					Name:             "id",
					UniqueIdentifier: true,
					IsSet: func(pointer interface{}) bool {
						pointerInt := *pointer.(*int)
						return pointerInt != 0
					},
				},
				{
					Pointer:    &a.Name,
					Name:       "name",
					Insertable: true,
					Updatable:  true,
				},
				{
					Pointer:    &a.Age,
					Name:       "age",
					Insertable: true,
					Updatable:  true,
				},
			},
		},
	}
	return a
}

A surf.Configuration has two fields, TableName and Fields.

TableName is simply a string that represents your table/collection name in your datastore.

Fields is an array of surf.Field (which is explained in detail below).

surf.Field

A surf.Field defines how a surf.Model will interact with a field.

a surf.Field contains a few values that determine this interaction:

Pointer

This is a pointer to the surf.BaseModel's field.

Name

This is the name of the field as specified in the datastore.

Insertable

This value specifies if this surf.Field is to be considered by the Insert() method of our model.

Updatable

This value specifies if this surf.Field is to be considered by the Update() method of our model.

UniqueIdentifier

Note: IsSet is also required if UniqueIdentifier is set to true.

This value specifies that this field can unique identify an entry in the datastore.

You do not need to set this to true for all of your UNIQUE fields in your datastore, but you can.

Setting UniqueIdentifier to true gives you the following:

  • The ability to set that fields value in the surf.BaseModel and call Load() against it.
  • Call Update() with this field in the where clause / filter
  • Call Delete() with this field in the where clause / filter.
IsSet

This is a function that determines if the value in the struct is set or not.

This is only required if UniqueIdentifier is set to true.

This function will likely look something like this:

// ...
IsSet: func(pointer interface{}) bool {
    pointerInt := *pointer.(*int)
    return pointerInt != 0
},
// ...
SkipValidation

This field has no meaning to Surf itself, so if you are only using Surf, don't worry about this field.

This field is used in Turf to skip the validation process in auto-generated controllers.

Models

Models are simply implementations that adhere to the following interface:

type Model interface {
    Insert() error
    Load() error
    Update() error
    Delete() error
    BulkFetch(BulkFetchConfig, BuildModel) ([]Model, error)
    GetConfiguration() *Configuration
}

Right now in this library there is only surf.PqModel written, but I plan to at minimum write a MySQL model in the near future.

surf.PqModel

surf.PqModel is written on top of github.com/lib/pq. This converts your struct into a DAO that can speak with PostgreSQL.

Running Tests

Before running tests, you must set up a database with a single table.

CREATE TABLE animals(
    id    serial          PRIMARY KEY,
    slug  TEXT            UNIQUE NOT NULL,
    name  TEXT            NOT NULL,
    age   int             NOT NULL
);

You'll then need to have an environment variable set pointing to the database URL:

SERF_TEST_DATABASE_URL=""

After this is all set up you can run go test the following to run the tests. To check the coverage run go test -cover

Acknowledgements

Thanks to @roideuniverse for some early guidance that ultimately lead into the creation of this library.

License

MIT

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PrintSqlQuery

func PrintSqlQuery(query string, args ...interface{})

printQuery prints a query if the user has enabled logging

func SetLogging

func SetLogging(enabled bool, writer io.Writer)

SetLogging adjusts the configuration for logging. You can enable and disable the logging here. By default, logging is disabled.

Most calls to this function will be called like SetLogging(true, os.Stdout)

Types

type BuildModel

type BuildModel func() Model

BuildModel is a function that is responsible for returning a Model that is ready to have GetConfiguration() called

type BulkFetchConfig

type BulkFetchConfig struct {
	Limit      int
	Offset     int
	OrderBys   []OrderBy
	Predicates []Predicate
}

BulkFetchConfig is the configuration of a Model.BulkFetch()

func (*BulkFetchConfig) ConsumeSortQuery

func (c *BulkFetchConfig) ConsumeSortQuery(sortQuery string)

ConsumeSortQuery consumes a `sort` query parameter and stuffs them into the OrderBys field

type Configuration

type Configuration struct {
	TableName string
	Fields    []Field
}

Configuration is the metadata to be attached to a model

type Field

type Field struct {
	Pointer          interface{}
	Name             string
	Insertable       bool
	Updatable        bool
	UniqueIdentifier bool
	SkipValidation   bool
	GetReference     func() (BuildModel, string)
	SetReference     func(Model) error
	IsSet            func(interface{}) bool
}

Field is the definition of a single value in a model

type Model

type Model interface {
	Insert() error
	Load() error
	Update() error
	Delete() error
	BulkFetch(BulkFetchConfig, BuildModel) ([]Model, error)
	GetConfiguration() *Configuration
}

Model is the interface that defines the type that will be embedded on models

type OrderBy

type OrderBy struct {
	Field string
	Type  OrderByType
}

OrderBy is the definition of a single order by clause

type OrderByType

type OrderByType int

OrderByType is an enumeration of the SQL standard order by

const (
	ORDER_BY_ASC OrderByType = iota
	ORDER_BY_DESC
)

type PqModel

type PqModel struct {
	Database *sql.DB       `json:"-"`
	Config   Configuration `json:"-"`
}

PqModel is a github.com/lib/pq implementation of a Model

func (*PqModel) BulkFetch

func (w *PqModel) BulkFetch(fetchConfig BulkFetchConfig, buildModel BuildModel) ([]Model, error)

BulkFetch gets an array of models

func (*PqModel) Delete

func (w *PqModel) Delete() error

Delete deletes the model

func (*PqModel) GetConfiguration

func (w *PqModel) GetConfiguration() *Configuration

GetConfiguration returns the configuration for the model

func (*PqModel) Insert

func (w *PqModel) Insert() error

Insert inserts the model into the database

func (*PqModel) Load

func (w *PqModel) Load() error

Load loads the model from the database from its unique identifier and then loads those values into the struct

func (*PqModel) Update

func (w *PqModel) Update() error

Update updates the model with the current values in the struct

type Predicate

type Predicate struct {
	Field         string
	PredicateType PredicateType
	Values        []interface{}
}

Predicate is the definition of a single where SQL predicate

type PredicateType

type PredicateType int
const (
	WHERE_IS_NOT_NULL PredicateType = iota // Default
	WHERE_IS_NULL
	WHERE_IN
	WHERE_NOT_IN
	WHERE_LIKE
	WHERE_EQUAL
	WHERE_NOT_EQUAL
	WHERE_GREATER_THAN
	WHERE_GREATER_THAN_OR_EQUAL_TO
	WHERE_LESS_THAN
	WHERE_LESS_THAN_OR_EQUAL_TO
)

Jump to

Keyboard shortcuts

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