Skip to content

Commit

Permalink
Merge pull request #15 from nickhstr/feat/nickhstr/major-updates
Browse files Browse the repository at this point in the history
refactor: major updates
  • Loading branch information
nickhstr authored Jun 27, 2020
2 parents a5ddc0b + 5f58f4d commit 08eeec2
Show file tree
Hide file tree
Showing 49 changed files with 2,153 additions and 1,232 deletions.
9 changes: 9 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ linters:
- dogsled
- dupl
- goconst
- gofmt
- goimports
- golint
- gosimple
- prealloc
- staticcheck
- unconvert
- unused
- wsl

issues:
# Disable max issues
Expand All @@ -18,3 +21,9 @@ issues:
- text: "SA1019:"
linters:
- staticcheck
- text: "Error return value of `w.Write` is not checked"
linters:
- errcheck
run:
skip-dirs:
- node_modules
33 changes: 18 additions & 15 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,8 @@

PROJECTNAME ?= ${shell basename "${PWD}"}

# try to use globally installed linter to take advantage of cache
LINTER = golangci-lint
ifeq (, ${shell which golangci-lint})
LINTER = go run vendor/github.com/golangci/golangci-lint/cmd/golangci-lint/main.go
endif

MODD = modd
ifeq (, ${shell which modd})
MODD = go run vendor/github.com/cortesi/modd/cmd/modd/main.go
endif
export GOBIN = $(CURDIR)/bin
export PATH := $(GOBIN):$(PATH)

# TARGETS

Expand Down Expand Up @@ -43,16 +35,27 @@ create-coverage:
install:
@echo "🚚 Downloading dependencies..."
@go mod download
@go mod vendor

@echo "🛠 Building Go dependencies..."
@go install github.com/cortesi/modd/cmd/modd
@go install github.com/golangci/golangci-lint/cmd/golangci-lint
@go install github.com/psampaz/go-mod-outdated
@echo "✨ Done."

## lint: Runs golangci-lint against entire project
## lint: Runs linter against Go files
.PHONY: lint
lint:
@echo "🔍 Linting files..."
${LINTER} run ${flags}
@echo "🔍 Linting Go files..."
@golangci-lint run $(flags)
@go mod tidy
@git diff --exit-code -- go.mod go.sum
@echo "✨ Done."

## mod-outdated: Checks for updates to direct go.mod dependencies
.PHONY: mod-outdated
mod-outdated:
@go list -u -m -json all | go-mod-outdated -update -direct

## test: Runs all tests
.PHONY: test
test:
Expand All @@ -66,7 +69,7 @@ test:
.PHONY: test-watch
test-watch:
@echo "🏃 Running test watcher..."
${MODD} --file=./internal/tools/modd.test.conf
@modd --file=./internal/tools/modd.test.conf

## help: List available commands
.PHONY: help
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ Note: Go modules are the only supported dependency tool.
* Cache - a key-value cache, using Redis
* Newrelic - handler wrapper and custom logging, using github.com/newrelic/go-agent
* Environment variable helpers
* Mongodb helpers

## Contributors

Expand Down
90 changes: 50 additions & 40 deletions cache/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
package cache

import (
"context"
"errors"
"sync"
"time"
Expand All @@ -14,82 +15,91 @@ import (
"github.com/nickhstr/goweb/logger"
)

var log = logger.New("cache")
var client Cacher
var (
client Cacher
cacherInit sync.Once
log = logger.New("cache")
)

// Cacher defines the methods of any cache client.
type Cacher interface {
Del(...string) error
Get(string) ([]byte, error)
Set(string, interface{}, time.Duration) error
Del(context.Context, ...string) error
Get(context.Context, string) ([]byte, error)
Set(context.Context, string, interface{}, time.Duration) error
}

// Default returns the default Cacher.
func Default() Cacher {
Init(nil)
return client
}

// Init sets the Cacher to be used for all cache operations.
// If an init func is supplied, it will be used for setup;
// otherwise, the default Cacher will be used.
// The supplied init function must return a Cacher,
// so that `client` may be set.
func Init(init func() Cacher) {
if init == nil {
// default to redis.Cacher
cacherInit.Do(func() {
client = redis.New()
})

return
}

cacherInit.Do(func() {
client = init()
})
}

// Del removes data at the given key(s)
func Del(keys ...string) error {
// init default Cacher if not set already
CacherInit(nil)
// Del removes data at the given key(s).
func Del(ctx context.Context, keys ...string) error {
Init(nil)

log := log.With().Str("operation", "DEL").Logger()

if client == nil {
err := noClientLogErr(log)
return err
}

return client.Del(keys...)
return client.Del(ctx, keys...)
}

// Get returns the data stored under the given key.
func Get(key string) ([]byte, error) {
// init default Cacher if not set already
CacherInit(nil)
func Get(ctx context.Context, key string) ([]byte, error) {
Init(nil)

log := log.With().Str("operation", "GET").Logger()

if client == nil {
err := noClientLogErr(log)
return []byte{}, err
}

return client.Get(key)
return client.Get(ctx, key)
}

// Set stores data for a set period of time at the given key.
func Set(key string, data []byte, expiration time.Duration) error {
// init default Cacher if not set already
CacherInit(nil)
func Set(ctx context.Context, key string, data interface{}, expiration time.Duration) error {
Init(nil)

log := log.With().Str("operation", "SET").Logger()

if client == nil {
err := noClientLogErr(log)
return err
}

return client.Set(key, data, expiration)
return client.Set(ctx, key, data, expiration)
}

var cacherInit sync.Once

// CacherInit sets the Cacher to be used for all cache operations.
// If an init func is supplied, it will be used for setup; otherwise,
// the default Cacher will be used.
// The supplied init function must accept a Cacher as its argument, so
// that `client` may be set.
func CacherInit(init func() Cacher) {
if init == nil {
// default to redis.Cacher
cacherInit.Do(func() {
client = redis.New()
})
return
}

cacherInit.Do(func() {
client = init()
})
}

// Creates no-client error, logs it, and returns it
// Creates no-client error, logs it, and returns it.
func noClientLogErr(log logger.Logger) error {
err := errors.New("no cache client available")
log.Error().Msg(err.Error())

return err
}
32 changes: 19 additions & 13 deletions cache/cache_test.go
Original file line number Diff line number Diff line change
@@ -1,51 +1,57 @@
package cache_test

import (
"context"
"testing"
"time"

"github.com/nickhstr/goweb/cache"
"github.com/stretchr/testify/assert"
)

type mockCacher struct{}
type mockCache struct{}

func (mc mockCacher) Del(key ...string) error {
func (mc mockCache) Del(ctx context.Context, key ...string) error {
return nil
}
func (mc mockCacher) Get(key string) ([]byte, error) {
func (mc mockCache) Get(ctx context.Context, key string) ([]byte, error) {
return []byte{}, nil
}
func (mc mockCacher) Set(key string, val interface{}, t time.Duration) error {
func (mc mockCache) Set(ctx context.Context, key string, val interface{}, t time.Duration) error {
return nil
}

func TestDel(t *testing.T) {
assert := assert.New(t)
cache.CacherInit(func() cache.Cacher {
return &mockCacher{}

cache.Init(func() cache.Cacher {
return &mockCache{}
})
err := cache.Del("key")

err := cache.Del(context.Background(), "key")

assert.Nil(err)
}

func TestGet(t *testing.T) {
assert := assert.New(t)
cache.CacherInit(func() cache.Cacher {
return &mockCacher{}

cache.Init(func() cache.Cacher {
return &mockCache{}
})
val, err := cache.Get("key")

val, err := cache.Get(context.Background(), "key")

assert.Nil(err)
assert.Equal([]byte{}, val)
}

func TestSet(t *testing.T) {
assert := assert.New(t)
cache.CacherInit(func() cache.Cacher {
return &mockCacher{}

cache.Init(func() cache.Cacher {
return &mockCache{}
})

assert.NotPanics(func() { _ = cache.Set("key", []byte{}, 60*time.Second) })
assert.NotPanics(func() { _ = cache.Set(context.Background(), "key", []byte{}, 60*time.Second) })
}
Loading

0 comments on commit 08eeec2

Please sign in to comment.