seedr

package module
v0.1.0-beta.1 Latest Latest
Warning

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

Go to latest
Published: Aug 5, 2017 License: MIT Imports: 13 Imported by: 0

README

Seedr

GoDoc

WORK IN PROGRESS

Seedr, being heavily inspired by Factory Girl, allows to easily declare factories, insert records into database (optional) and initialize objects (structs) from those newly created records.

Available DB drivers

  • MySQL

Basic Usage

Let's assume we have MySQL database testdb with users table:

create table users (
    id         int(10) unsigned not null auto_increment,
    name       varchar(250) not null,
    sex        varchar(10),
    age        int(5) unsigned,
    created_at datetime not null default NOW(),
    primary key (id)
) engine=InnoDB default charset=utf8;

Basic seedr usage:

package whatever

import (
  "database/sql"
  "time"

  _ "github.com/go-sql-driver/mysql"

  . "github.com/josephbuchma/seedr" // dot import for convenience.
  "github.com/josephbuchma/seedr/driver/sql/mysql"
)

// Define 'users' factory

var users = Factory{
  FactoryConfig: {
    // Entity is a MySQL table name in this case
    Entity:     "users",
    PrimaryKey: "id",
  },
  // to see relations in action check out tests and docs (links below)
  Relations: {},
  // Traits are different variations of user.
  // Each field of trait must be same as name of field in `users` table.
  Traits: {
    "base": {
      // Auto means that this field will be initialized by driver (DB)
      "id":         Auto(),
      "name":       SequenceString("John-%d"), // produces John-1, John-2, John-3...
      "age":        nil,                       // nil -> NULL
      "sex":        nil,
      "created_at": Func(func()interface{}{ return time.Now() },
    },
    "old": {
      "age": 80,
    },
    "young": {
      "age": 15,
    },
    "male": {
      "sex": "male",
    },
    "female": {
      "sex": "female",
    },

    "User": {
      Include: "base",
    },
    "OldWoman": {
      Include: "base old female", // traits can be combined using include
      "name":  "Ann",             // you can override any field
    },
    "YoungMan": {
      Include: "base young male",
    },
    "User9999": {
      Include: "base",
      "id":    9999,
    },
  },
}

// Create new Seedr (usually you'll do it in separate file,
// so New() will be seedr.New()

var testdb = New("test_seedr",
  SetCreateDriver(mysql.New(openTestDB())), // configure MySQL driver (openTestDB is at the end)
  // field mapper is used for mapping trait fields to struct fields.
  // In this case seedr will look for `sql:"column_name"` tag, and if it's not
  // provided it'll fall back to SnakeFieldMapper, which converts struct field
  // name to snake case.
  SetFieldMapper(
    TagFieldMapper("sql", seedr.SnakeFieldMapper()),
  ),
).Add("users", users) // add users factory

// Create some records
func test() {
  var u User
  var users []User

  // Lines below are doing exactly what you're thinking (creating records in DB and initializing objects)
  // And yes, you can only create traits that starts with
  // capital letter (other traits are "private" to the factory,
  // and can only be included by other traits in this factory)
  testdb.Create("User").Scan(&u)
  testdb.Create("User9999").Scan(&u)
  testdb.CreateBatch("OldWoman", 10).Scan(&users)
  testdb.CreateCustom("User", Trait{
    "name": "Mike",
    "age":  22,
  }).Scan(&u)
  testdb.CreateCustomBatch("User", 4, Trait{
    "name": "Mike",
    "age":  22,
  }).Scan(&users)

  // You can also only "build" the object, without inserting to DB
  testdb.Build("User").Scan(&u) // u.ID == 0
}

// User model
type User struct {
  ID        int       `sql:"id"`
  Name      string    `sql:"name"`
  Age       *int      `sql:"age"`
  Sex       *string   `sql:"sex"`
  CreatedAt time.Time `sql:"created_at"`
}

func openTestDB() *sql.DB {
  db, err := sql.Open("mysql", "root:@/testdb?parseTime=true")
  if err != nil {
    panic(err)
  }
  return db
}

To see how to work with relations and other features, check out tests and docs

Documentation

Overview

Package seedr is inspired by Ruby's factory_girl. It's a fixtures replacement that allows to define factories and build/store objects in expressive and flexible way.

Index

Constants

View Source
const (
	// Include is a special key for Trait definition
	// that defines on what traits current trait is based on.
	// Value must be a string with names of traits (of current factory only)
	// separated by whitespace.
	Include = "SEEDR_INCLUDE_TRAITS"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type ConfigFunc

type ConfigFunc func(*Seedr)

ConfigFunc is used to configure Seedr

func SetBuildDriver

func SetBuildDriver(b driver.Driver) ConfigFunc

SetBuildDriver sets driver for `Build*` methods

func SetCreateDriver

func SetCreateDriver(b driver.Driver) ConfigFunc

SetCreateDriver sets driver for `Create*` methods

func SetFieldMapper

func SetFieldMapper(f MapFieldFunc) ConfigFunc

SetFieldMapper sets MapFieldFunc func for Seedr

type Factory

type Factory struct {
	FactoryConfig
	Relations
	Traits
}

Factory is a container for factory definition

type FactoryConfig

type FactoryConfig struct {

	// Entity is a name of database schema/table, index, etc.
	// Entity may not be required, depends on driver.
	// If not given, factory name is used.
	Entity string
	// PrimaryKey is a name of primary key field.
	// It is required if you define any relations.
	// It also may be required by your Driver.
	PrimaryKey string
	// contains filtered or unexported fields
}

FactoryConfig ...

type Func

type Func func() interface{}

Func allows to convert func()interface{} to Generator

func (Func) Next

func (g Func) Next() interface{}

Next implements Generator.Next

type Generator

type Generator interface {
	// Next must return any value of supported type:
	//    - any numeric type
	//    - time.Time
	//    - sql.Scanner
	//    - nil value (interface{}(nil))
	Next() interface{}
}

Generator provides a way to generate dynamic field values for every instance created by seedr.

func Auto

func Auto() Generator

Auto is a special kind of Generator. "Auto" field must be initialized by Driver on Create. May be useful for fields like "id", "created_at", etc.

func ChainInt

func ChainInt(init int, f func(prev int) int) Generator

ChainInt creates Generator that yields result of `f` with result of previous call to `f` as parameter (prev). For initial call `init` value is used.

func CreateRelated

func CreateRelated(traitName string) Generator

CreateRelated is a special Generator that will create related trait

func CreateRelatedBatch

func CreateRelatedBatch(traitName string, n int) Generator

CreateRelatedBatch is a special Generator that will create a batch of related traits

func CreateRelatedCustom

func CreateRelatedCustom(traitName string, override Trait) Generator

CreateRelatedCustom is a special Generator that will create related trait with additional changes.

func CreateRelatedCustomBatch

func CreateRelatedCustomBatch(traitName string, n int, override Trait) Generator

CreateRelatedCustomBatch is a special Generator that will create a batch of related traits with additional changes.

func DummyText

func DummyText(limit int) Generator

DummyText returns meaningless text up to `limit` bytes Limit 0 means 'no limit' (returns full length of hardcoded "Lorem Ipsum ....")

func Loop

func Loop(slice interface{}) Generator

Loop returns values from given slice sequentially. When last element is reached, it starts from beginning.

func PickRandom

func PickRandom(slice interface{}) Generator

PickRandom picks value by random index from given slice/array/string.

func SequenceFunc

func SequenceFunc(f func(int) interface{}, startFrom ...int) Generator

SequenceFunc creates generator from given func. On n'th .Next call it calls given func with startFrom + n argument. By default startFrom == 1

func SequenceInt

func SequenceInt(startFrom ...int) Generator

SequenceInt generates sequence of ints starting from `startFrom` (1 by default)

func SequenceString

func SequenceString(fmtStr string, startFrom ...int) Generator

SequenceString generates strings by given template `fmtStr`. Example: `SequenceString("MyString-%d")` => "MyString-1", "MyString-2",...

type MapFieldFunc

type MapFieldFunc func(reflect.StructField) (traitFieldName string, err error)

MapFieldFunc returns name of Trait's field based on StructField

func NoopFieldMapper

func NoopFieldMapper() MapFieldFunc

NoopFieldMapper returns field's `Name` without changes

func RegexpTagFieldMapper

func RegexpTagFieldMapper(regex string, fallback MapFieldFunc) MapFieldFunc

RegexpTagFieldMapper applies regex to whole struct tag string and uses first match as result. If it fails fallback mapper is used.

func SnakeFieldMapper

func SnakeFieldMapper() MapFieldFunc

SnakeFieldMapper converts field name to snake case; acronyms are converted to lower-case and preceded by an underscore. Note that multiple consequent acronyms will be considered a single acronym (e.g. ACDCTime will become acdc_time, not ac_dc_time)

func TagFieldMapper

func TagFieldMapper(tag string, fallback MapFieldFunc) MapFieldFunc

TagFieldMapper looks for specific struct field tag or uses fallback mapper if tag is not found.

type Relation

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

Relation defines Factory relation. Can be defined using one of:

  • BelongsTo
  • HasMany
  • HasManyThrough

func BelongsTo

func BelongsTo(factory string, joinField ...string) *Relation

BelongsTo defines "belogs to" relation. By default joinField is eql to relation name (key of this relation in Relations map of Factory)

func HasMany

func HasMany(factory, foreignKey string) *Relation

HasMany defines "has many" relation.

func HasManyThrough

func HasManyThrough(joinTrait, lfield, rfield string, relatedFactory ...string) *Relation

HasManyThrough defines "M2M" relation through joinFactory, where lfield is a foreign key on this factory, and rfield is a foreignKey on related factory. By default relatedFactory is eql to relation name (key of this relation in Relations map of Factory)

type Relations

type Relations map[string]*Relation

Relations defines relations of the Factory

type Seedr

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

Seedr is a collection of factories.

func New

func New(name string, config ...ConfigFunc) *Seedr

New creates Seedr instance with NoopFieldMapper and NoopDriver for create and build methods by default.

func (*Seedr) Add

func (sdr *Seedr) Add(factoryName string, f Factory) *Seedr

Add adds new factory to this seedr

func (*Seedr) Build

func (sdr *Seedr) Build(traitName string) TraitInstance

Build builds trait. It uses "build" driver (see SetBuildDriver)

func (*Seedr) BuildBatch

func (sdr *Seedr) BuildBatch(traitName string, n int) *TraitInstances

BuildBatch builds n trait instances. It uses "build" driver (see SetBuildDriver)

func (*Seedr) BuildCustom

func (sdr *Seedr) BuildCustom(traitName string, override Trait) TraitInstance

BuildCustom builds trait with additional changes. It uses "build" driver (see SetBuildDriver)

func (*Seedr) BuildCustomBatch

func (sdr *Seedr) BuildCustomBatch(traitName string, n int, override Trait) *TraitInstances

BuildCustomBatch builds n trait instances with additional changes. It uses "build" driver (see SetBuildDriver)

func (*Seedr) Create

func (sdr *Seedr) Create(traitName string) TraitInstance

Create creates an instance of trait It uses "create" driver (see SetCreateDriver)

func (*Seedr) CreateBatch

func (sdr *Seedr) CreateBatch(traitName string, n int) *TraitInstances

CreateBatch creates n instances of trait It uses "create" driver (see SetCreateDriver)

func (*Seedr) CreateCustom

func (sdr *Seedr) CreateCustom(traitName string, override Trait) TraitInstance

CreateCustom overrides values of trait definition and creates resulting trait. It uses "create" driver (see SetCreateDriver)

func (*Seedr) CreateCustomBatch

func (sdr *Seedr) CreateCustomBatch(traitName string, n int, override Trait) *TraitInstances

CreateCustomBatch overrides values of trait definition and creates n instances of resulting trait. It uses "create" driver (see SetCreateDriver)

type Trait

type Trait map[string]interface{}

Trait contains factory fields definitions. Key is a field name and value is any of supported:

  • any numeric type
  • time.Time
  • sql.Scanner
  • Generator
  • nil (interface{}(nil))

Special key is `Include` constant that defines on what traits this Trait is based on. Every field of trait is a name of respective field in database.

type TraitInstance

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

TraitInstance is a created trait instance

func (TraitInstance) CreateRelated

func (ti TraitInstance) CreateRelated(relation, traitName string) TraitInstance

CreateRelated creates single related instance. Works for FK (child only) and M2M. It returns original TraitInstance, not one that was created. Fetch created one using Related method.

func (TraitInstance) CreateRelatedBatch

func (ti TraitInstance) CreateRelatedBatch(relation, traitName string, n int) TraitInstance

CreateRelatedBatch creates n related instances. Works for FK (child only) and M2M It returns original TraitInstance, not one that was created. Fetch created one using Related method.

func (TraitInstance) CreateRelatedCustom

func (ti TraitInstance) CreateRelatedCustom(relation, traitName string, overrides Trait) TraitInstance

CreateRelatedCustom creates single related instance with additional changes. Works for FK (child only) and M2M It returns original TraitInstance, not one that was created. Fetch created one using Related method.

func (TraitInstance) CreateRelatedCustomBatch

func (ti TraitInstance) CreateRelatedCustomBatch(relation, traitName string, n int, overrides Trait) TraitInstance

CreateRelatedCustomBatch creates n related instances with additional changes. Works for FK (child only) and M2M It returns original TraitInstance, not one that was created. Fetch created one using Related method.

func (TraitInstance) Related

func (ti TraitInstance) Related(relationName string) *TraitInstances

Related returns related TraitInstances (that was created by CreateRelated*)

func (TraitInstance) Scan

func (ti TraitInstance) Scan(v interface{}) TraitInstance

Scan initializes given struct instance `v` by TraitInstance's values. `v` must be a pointer to struct.

func (TraitInstance) ScanRelated

func (ti TraitInstance) ScanRelated(relationName string, v interface{}) TraitInstance

ScanRelated scans related TraitInstance(s) into v and returns this TraitInstance.

type TraitInstances

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

TraitInstances is a collection of created trait instances

func (*TraitInstances) Index

func (ti *TraitInstances) Index(index int) TraitInstance

Index returns TraitInstance by index

func (*TraitInstances) Len

func (ti *TraitInstances) Len() int

Len returns total count of trait instances in this collection

func (*TraitInstances) Scan

func (ti *TraitInstances) Scan(dest interface{}) (ret *TraitInstances)

Scan initializes given list of struct instances `dest` by TraitInstance's values. `v` must be a pointer to slice of structs. But, if there is only one instance, dest can be pointer to struct.

func (*TraitInstances) ScanRelated

func (ti *TraitInstances) ScanRelated(relationName string, dest interface{})

ScanRelated is a shortcut for #Index(0).ScanRelated() It panics if there is not exactly one instance in this TraitInstances.

type Traits

type Traits map[string]Trait

Traits is a map of Trait declarations (name -> Trait). Capitalized names are 'exported', others are private (e.g. only can be included by other Trait of this Traits).

Directories

Path Synopsis
sql
sql/mysql
Package mysql is a MySQL driver for Seedr.
Package mysql is a MySQL driver for Seedr.
sql/mysql/internal/tests/models
Package test_models is an example of basic models setup.
Package test_models is an example of basic models setup.

Jump to

Keyboard shortcuts

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