Skip to content

Commit

Permalink
gometalinter: adds to travis and fixed all warnings
Browse files Browse the repository at this point in the history
docs/readmes updates
more tests
refactor templateConcurrent benchmark
  • Loading branch information
bgadrian committed Nov 25, 2018
1 parent e84223d commit 78d7d17
Show file tree
Hide file tree
Showing 36 changed files with 336 additions and 214 deletions.
3 changes: 3 additions & 0 deletions .gometalinter.json
@@ -0,0 +1,3 @@
{
"Cyclo": 18
}
2 changes: 2 additions & 0 deletions .travis.yml
Expand Up @@ -14,6 +14,8 @@ matrix:
env: COVERAGE=true

install:
- go get github.com/alecthomas/gometalinter
- gometalinter --install --vendored-linters
- make

script:
Expand Down
9 changes: 6 additions & 3 deletions CONTRIBUTING.md
Expand Up @@ -12,10 +12,13 @@ Fixes #issueID
## Requirements
* Go (preferable the latest version)
* bash-like environment with `make`
* activate the `go fmt` and `go imports` file watchers
* keep the 100% test coverage

## All PRs must:
* maintain the 100% test coverage
* pass gometalinter without warnings
* code must be proper formatted with `go fmt` and `go imports`
* each Public method must examples or tests, and a benchmark
* keep the Go 1.0 compatibility
* maintain the Go 1.0 compatibility

## Caveats
The templates system is built on top of generated code. Make sure you run
Expand Down
28 changes: 20 additions & 8 deletions README.md
Expand Up @@ -103,16 +103,23 @@ go func(){
For a heavy usage in a 4 CPU env, the performance benefits of the FastFaker can be up to 10x:
```
BenchmarkNewSafeFaker_Parallel-4 50000 30893 ns/op 0 B/op 0 allocs/op
BenchmarkNewFastFaker_Parallel-4 300000 3829 ns/op 0 B/op 0 allocs/op
go test -benchmem -bench=BenchmarkNew* github.com/bgadrian/fastfaker/faker
goos: linux
goarch: amd64
pkg: github.com/bgadrian/fastfaker/faker
BenchmarkNewSafeFaker_Parallel-4 500000 2668 ns/op 0 B/op 0 allocs/op
BenchmarkNewFastFaker_Parallel-4 10000000 225 ns/op 0 B/op 0 allocs/op
```

## Templates
Template is the most powerful FastFaker feature. It allows custom patterns/templates of text to be filled with over 110 random types of [data (variables)](./TEMPLATE_VARIABLES.md).
Template is the most powerful FastFaker feature. It allows custom patterns/templates (JSON, YAML, HTML, ...) of text to be filled with over 110 random types of [data (variables)](./TEMPLATE_VARIABLES.md).

It can be used directly (faker.Template* methods) or via the faker.Struct fill method and `fake:` tags.

```go
//instead of:
fmt.Sprintf("I'm %s, call me at %s!", fastFaker.Name(), fastFaker.Numerify("###-###-####"))
//you can do:
fastFaker.Template("I'm {name}, call me at ###-###-####!") // I'm John, call me at 152-335-8761!
```

Expand All @@ -127,13 +134,18 @@ The pseudo-data generator has its [own package](./randomizer). At its core it is
## gofakeit
This library started as a fork of [gofakeit](https://github.com/brianvoe/gofakeit/), but I had different requirements from such a library, in particular performance and extensibility and could not guarantee [backward compatibility](https://github.com/brianvoe/gofakeit/issues/32). Future sync **will** be performed between the projects.

Differences between `gofakeit` and `fastfaker` (more in the [changelog](./CHANGELOG.md))
* import path, name
* version `gofakeit` 3.x is `fastfaker` 1.x
* different documentation, new examples and tests
* non deterministic (using the same `Seed` on `fastfaker` may lead to different results, eg Name(), than the same seed with `gofakeit`)
How is FastFaker different?
* performance (up to 10x improvement)
* code quality (fixed over 50 gometalinter warnings)
* Templates
* error logger (stdErr) instead of silent fails
* new functions
* more documentation, new examples and tests
* usage, instead of `gofakeit.Name()` the calls are `faker.Global.Name()` or `faker.NewFastFaker().Name()`
* `fastfaker` uses the semantic version, making it compatible with go modules
* split /data and /randomizer into their own packages
* version `gofakeit` 3.x is `fastfaker` 1.x
* more in the [changelog](./CHANGELOG.md)

## Change log
I try to [keep it updated here](./CHANGELOG.md).
Expand Down
16 changes: 9 additions & 7 deletions TEMPLATES.md
@@ -1,14 +1,16 @@
# Fast Faker Templates
Template is the most powerful FastFaker feature. It allows custom patterns/templates of text to be filled with over 110 random types of [data (variables)](./TEMPLATE_VARIABLES.md).
# FastFaker Templates

```go
fmt.Println(fastFaker.Template("Hello {name}!"))

//Hello Jeromy Schmeler!
```
Template is the most powerful FastFaker feature. It allows custom patterns/templates (JSON, YAML, HTML, ...) of text to be filled with over 110 random types of [data (variables)](./TEMPLATE_VARIABLES.md).

It can be used directly (faker.Template* methods) or via the faker.Struct fill method and `fake:` tags.

```go
//instead of:
fmt.Sprintf("I'm %s, call me at %s!", fastFaker.Name(), fastFaker.Numerify("###-###-####"))
//you can do:
fastFaker.Template("I'm {name}, call me at ###-###-####!") // I'm John, call me at 152-335-8761!
```

### Faker.Template()

The `Template()` function replaces:
Expand Down
13 changes: 8 additions & 5 deletions cmd/template-variable-generator/main.go
Expand Up @@ -57,7 +57,10 @@ func main() {
case string:
example = res[0].String()
call = fmt.Sprintf("func(f *Faker) string { return %s() }", call)
case int, int8, int16, int32, int64:
case int:
example = strconv.Itoa(int(res[0].Int()))
call = fmt.Sprintf("func(f *Faker) string { return strconv.Itoa(%s()) }", call)
case int8, int16, int32, int64:
example = strconv.Itoa(int(res[0].Int()))
call = fmt.Sprintf("func(f *Faker) string { return strconv.Itoa(int(%s())) }", call)
case uint8, uint16, uint32, uint64:
Expand All @@ -68,7 +71,7 @@ func main() {
call = fmt.Sprintf("func(f *Faker) string { return strconv.FormatFloat(float64(%s()), %d, -1, 32) }", call, g)
case float64:
example = strconv.FormatFloat(float64(res[0].Float()), 'g', -1, 64)
call = fmt.Sprintf("func(f *Faker) string { return strconv.FormatFloat(float64(%s()), %d, -1, 64) }", call, g)
call = fmt.Sprintf("func(f *Faker) string { return strconv.FormatFloat(%s(), %d, -1, 64) }", call, g)
default:
continue
//example = fmt.Sprintf("%v", res[0])
Expand Down Expand Up @@ -116,11 +119,11 @@ func init() {
}
func getTemplateVars() map[string]fakerer {
templateVariables := make(map[string]fakerer, 100)
vars := make(map[string]fakerer, 100)
{{range .}}templateVariables["{{.Key}}"] = {{.Call}}
{{range .}}vars["{{.Key}}"] = {{.Call}}
{{end}}
return templateVariables
return vars
}// Code generated by make generate DO NOT EDIT.
`))
Expand Down
14 changes: 7 additions & 7 deletions data/data.go
Expand Up @@ -8,8 +8,8 @@ import (
"github.com/pkg/errors"
)

// NotFound is triggered when a category of Data is not found
const NotFound = "not found"
// ErrNotFound is triggered when a category of Data is not found
var ErrNotFound = errors.New("not found")

// Intn is a part of the randomizer package. It is used to select an item from the database.
type Intn interface {
Expand Down Expand Up @@ -75,15 +75,15 @@ func intDataCheck(dataVal []string) bool {
// GetRandValue from a [category, subcategory]. Returns error if not found.
func GetRandValue(f Intn, dataVal []string) (string, error) {
if !Check(dataVal) {
return "", errors.New(NotFound)
return "", ErrNotFound
}
return Data[dataVal[0]][dataVal[1]][f.Intn(len(Data[dataVal[0]][dataVal[1]]))], nil
}

// GetRandIntValue Value from a [category, subcategory]. Returns error if not found.
func GetRandIntValue(f Intn, dataVal []string) (int, error) {
if !intDataCheck(dataVal) {
return 0, errors.New(NotFound)
return 0, ErrNotFound
}
return IntData[dataVal[0]][dataVal[1]][f.Intn(len(IntData[dataVal[0]][dataVal[1]]))], nil
}
Expand All @@ -101,7 +101,7 @@ func Categories() map[string][]string {
return types
}

// DataListCache serve the same function as the global GetRandValue functionality
// SetCache serve the same function as the global GetRandValue functionality
// but internally keeps a reference to a specific Category/Subcategory list
// improving consecutive calls performance (no more runtime.mapaccess1_faststr)
type SetCache interface {
Expand All @@ -111,8 +111,8 @@ type SetCache interface {
// NewDataListCache creates a new reference to a Data category set
// for faster access
func NewDataListCache(f Intn, dataVal []string) (SetCache, error) {
if &f == nil || Check(dataVal) == false {
return nil, errors.New(NotFound)
if &f == nil || !Check(dataVal) {
return nil, ErrNotFound
}
res := &simpleList{}
res.randomizer = f
Expand Down
4 changes: 2 additions & 2 deletions example/analytics/main.go
Expand Up @@ -24,10 +24,10 @@ func main() {

for ev := range collector {
props := ev.Properties()
userId := props[producer.KeyUserID]
userID := props[producer.KeyUserID]
delete(props, producer.KeyUserID)
delete(props, "event")
fmt.Printf("user %s: [%s]='%s'\n", userId, ev.Name(), props)
fmt.Printf("user %s: [%s]='%s'\n", userID, ev.Name(), props)
}
}

Expand Down
10 changes: 5 additions & 5 deletions example/analytics/producer/dispatcher.go
Expand Up @@ -38,7 +38,7 @@ func (d *Dispatcher) Start(intervalSize time.Duration) {
go func() {
exit:
for range ticker.C {
if d.running == false {
if !d.running {
break exit
}
//must block to avoid concurrent d.online updates
Expand Down Expand Up @@ -69,17 +69,17 @@ func (d *Dispatcher) tick() {
for i := 0; i < diff; i++ {
user := d.usersQueen()
d.online = append(d.online, user)
userId, _ := user.Property(KeyUserID)
fmt.Printf("new user: %s\n", userId)
userID, _ := user.Property(KeyUserID)
fmt.Printf("new user: %s\n", userID)
}
return
}

//remove random users
for len(d.online) > usersShould {
killIndex := rand.Intn(len(d.online))
userId, _ := d.online[killIndex].Property(KeyUserID)
userID, _ := d.online[killIndex].Property(KeyUserID)
d.online = append(d.online[:killIndex], d.online[killIndex+1:]...)
fmt.Printf("kill user: %s\n", userId)
fmt.Printf("kill user: %s\n", userID)
}
}
3 changes: 2 additions & 1 deletion example/analytics/producer/events.go
@@ -1,6 +1,7 @@
package producer

type EventGenerator func(count int) []Event
// EventGenerator is a simple functions that generate n events
type EventGenerator func(n int) []Event

// Event identifies an Analytics Event
type Event interface {
Expand Down
2 changes: 2 additions & 0 deletions example/analytics/producer/random.go
Expand Up @@ -42,10 +42,12 @@ func NewRandomEventGenerator(templateEvents []Event) *RandomEventGenerator {
return gen
}

// RandomEventGenerator creates random fake events
type RandomEventGenerator struct {
templateEvents []Event
}

// NewEvents creates new fake events
func (g *RandomEventGenerator) NewEvents(count int) []Event {
//for now we keep our logic simple, with no correlation between the events
//we just select them at random
Expand Down
3 changes: 1 addition & 2 deletions example/analytics/producer/user.go
Expand Up @@ -41,9 +41,8 @@ func NewSimpleUser(eventsPerMin int, evg EventGenerator, out chan<- Event,
ticker := time.NewTicker(time.Millisecond * time.Duration(ms))

for {
//TODO randomize the value of each interval,
//simulating lag/network issues
if u.running == false {
if !u.running {
break //do not make an extra tick if it was stopped
}
//blocking, no 2 ticks overlap
Expand Down
4 changes: 2 additions & 2 deletions example/users/main.go
Expand Up @@ -33,7 +33,7 @@ func main() {
newUser := &User{}
faker.Global.Struct(newUser)

asJson, _ := json.Marshal(newUser)
fmt.Println(string(asJson))
asJSON, _ := json.Marshal(newUser)
fmt.Println(string(asJSON))
}
}
7 changes: 5 additions & 2 deletions faker/address.go
Expand Up @@ -5,6 +5,9 @@ import (
"strings"
)

// ErrRangeInvalid signals a bad input
var ErrRangeInvalid = errors.New("input range is invalid")

// AddressInfo is a struct full of address information
type AddressInfo struct {
Address string
Expand Down Expand Up @@ -115,7 +118,7 @@ func (f *Faker) Latitude() float64 {
// LatitudeInRange will generate a random latitude within the input range
func (f *Faker) LatitudeInRange(min, max float64) (float64, error) {
if min > max || min < -90 || min > 90 || max < -90 || max > 90 {
return 0, errors.New("input range is invalid")
return 0, ErrRangeInvalid
}
return f.Float64Range(min, max), nil
}
Expand All @@ -128,7 +131,7 @@ func (f *Faker) Longitude() float64 {
// LongitudeInRange will generate a random longitude within the input range
func (f *Faker) LongitudeInRange(min, max float64) (float64, error) {
if min > max || min < -180 || min > 180 || max < -180 || max > 180 {
return 0, errors.New("input range is invalid")
return 0, ErrRangeInvalid
}
return f.Float64Range(min, max), nil
}
10 changes: 8 additions & 2 deletions faker/address_test.go
Expand Up @@ -240,7 +240,10 @@ func ExampleFaker_LatitudeInRange() {
func BenchmarkLatitudeInRange(b *testing.B) {
fastFaker := NewFastFaker()
for i := 0; i < b.N; i++ {
fastFaker.LatitudeInRange(-90, 90)
_, err := fastFaker.LatitudeInRange(-90, 90)
if err != nil {
b.Error(err)
}
}
}

Expand Down Expand Up @@ -276,6 +279,9 @@ func ExampleFaker_LongitudeInRange() {
func BenchmarkLongitudeInRange(b *testing.B) {
fastFaker := NewFastFaker()
for i := 0; i < b.N; i++ {
fastFaker.LongitudeInRange(-180, 180)
_, err := fastFaker.LongitudeInRange(-180, 180)
if err != nil {
b.Error(err)
}
}
}
6 changes: 1 addition & 5 deletions faker/bool.go
Expand Up @@ -2,11 +2,7 @@ package faker

// Bool will generate a random boolean value
func (f *Faker) Bool() bool {
if f.Intn(2) == 1 {
return true
}

return false
return f.Intn(2) == 1
}

// BoolText will generate a random boolean value as text, "true" or "false".
Expand Down
6 changes: 5 additions & 1 deletion faker/datetime.go
Expand Up @@ -89,6 +89,10 @@ func (f *Faker) TimeZoneAbv() string {

// TimeZoneOffset will select a random timezone offset
func (f *Faker) TimeZoneOffset() float32 {
value, _ := strconv.ParseFloat(f.getRandValue([]string{"timezone", "offset"}), 32)
value, err := strconv.ParseFloat(f.getRandValue([]string{"timezone", "offset"}), 32)
if err != nil {
errorLogger.Printf("(TimeZoneOffset) %s\n", err)
return 0
}
return float32(value)
}
15 changes: 15 additions & 0 deletions faker/datetime_test.go
Expand Up @@ -4,6 +4,8 @@ import (
"fmt"
"testing"
"time"

"github.com/bgadrian/fastfaker/data"
)

func ExampleFaker_Date() {
Expand Down Expand Up @@ -210,3 +212,16 @@ func BenchmarkTimeZoneOffset(b *testing.B) {
fastFaker.TimeZoneOffset()
}
}

func TestFaker_TimeZoneOffset(t *testing.T) {
tmp := data.TimeZone["offset"]
data.TimeZone["offset"] = []string{"XXX"}

fastFaker := NewFastFaker()
if fastFaker.TimeZoneOffset() != 0 {
t.Error("at error should return 0")
}

data.TimeZone["offset"] = tmp

}
8 changes: 7 additions & 1 deletion faker/faker.go
@@ -1,9 +1,15 @@
package faker

import "github.com/bgadrian/fastfaker/randomizer"
import (
"log"
"os"

"github.com/bgadrian/fastfaker/randomizer"
)

// Global is a singleton, safe for concurrency instance of a Faker.
var Global *Faker
var errorLogger = log.New(os.Stderr, "[faker] ", log.LstdFlags)

// Faker is the main strut that encapsulate all the package functionality.
type Faker struct {
Expand Down

0 comments on commit 78d7d17

Please sign in to comment.