Skip to content
This repository was archived by the owner on Sep 21, 2022. It is now read-only.

adding a caching abstraction layer to the Utron framework. #93

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 10 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,20 @@ go:
- 1.9.x
- tip
services:
-postgresql
- postgresql
- redis-server
- memcached
before_script:
- psql -c 'create database utron;' -U postgres
before_install:
- go get -t -v
- go get github.com/mattn/goveralls

matrix:
allow_failures:
- go: 1.5.x
- go: 1.6.x
- go: tip

script:
- $HOME/gopath/bin/goveralls -v -service=travis-ci -repotoken=$COVERALLS
32 changes: 32 additions & 0 deletions cache/cache.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package cache

import (
"strings"
)

// REDIS_DRIVER specifies the redis driver name
const REDIS_DRIVER = "redis"

// MEMCACHE_DRIVER specifies the memcache driver name
const MEMCACHE_DRIVER = "memcache"

// MAP_DRIVER specifies the map driver name
const MAP_DRIVER = "map"

// New new-ups an instance of StoreInterface
func New(driver string, params map[string]interface{}) (StoreInterface, error) {
switch strings.ToLower(driver) {
case REDIS_DRIVER:
return connect(new(RedisConnector), params)
case MEMCACHE_DRIVER:
return connect(new(MemcacheConnector), params)
case MAP_DRIVER:
return connect(new(MapConnector), params)
}

return connect(new(MapConnector), params)
}

func connect(connector CacheConnectorInterface, params map[string]interface{}) (StoreInterface, error) {
return connector.Connect(params)
}
81 changes: 81 additions & 0 deletions cache/connector_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package cache

import (
"testing"
)

func TestMemcacheConnector(t *testing.T) {
memcacheConnector := new(MemcacheConnector)

memcacheStore, err := memcacheConnector.Connect(memcacheStore())

if err != nil {
panic(err)
}

_, ok := memcacheStore.(StoreInterface)

if !ok {
t.Error("Expected StoreInterface got", memcacheStore)
}
}

func TestRedisConnector(t *testing.T) {
redisConnector := new(RedisConnector)

redisStore, err := redisConnector.Connect(redisStore())

if err != nil {
panic(err)
}

_, ok := redisStore.(StoreInterface)

if !ok {
t.Error("Expected StoreInterface got", redisStore)
}
}

func TestArrayConnector(t *testing.T) {
mapConnector := new(MapConnector)

mapStore, err := mapConnector.Connect(mapStore())

if err != nil {
panic(err)
}

_, ok := mapStore.(StoreInterface)

if !ok {
t.Error("Expected StoreInterface got", mapStore)
}
}

func redisStore() map[string]interface{} {
params := make(map[string]interface{})

params["address"] = "localhost:6379"
params["password"] = ""
params["database"] = 0
params["prefix"] = "golavel:"

return params
}

func memcacheStore() map[string]interface{} {
params := make(map[string]interface{})

params["server 1"] = "127.0.0.1:11211"
params["prefix"] = "golavel:"

return params
}

func mapStore() map[string]interface{} {
params := make(map[string]interface{})

params["prefix"] = "golavel:"

return params
}
58 changes: 58 additions & 0 deletions cache/contracts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package cache

// CacheConnectorInterface represents the connector methods to be implemented
type CacheConnectorInterface interface {
Connect(params map[string]interface{}) (StoreInterface, error)

validate(params map[string]interface{}) (map[string]interface{}, error)
}

// CacheInterface represents the caching methods to be implemented
type CacheInterface interface {
Get(key string) (interface{}, error)

Put(key string, value interface{}, minutes int) error

Increment(key string, value int64) (int64, error)

Decrement(key string, value int64) (int64, error)

Forget(key string) (bool, error)

Forever(key string, value interface{}) error

Flush() (bool, error)

GetInt(key string) (int64, error)

GetFloat(key string) (float64, error)

GetPrefix() string

Many(keys []string) (map[string]interface{}, error)

PutMany(values map[string]interface{}, minutes int) error

GetStruct(key string, entity interface{}) (interface{}, error)
}

// TagsInterface represents the tagging methods to be implemented
type TagsInterface interface {
Tags(names ...string) TaggedStoreInterface
}

// StoreInterface represents the methods a caching store needs to implement
type StoreInterface interface {
CacheInterface

TagsInterface
}

// TaggedStoreInterface represents the methods a tagged-caching store needs to implement
type TaggedStoreInterface interface {
CacheInterface

TagFlush() error

GetTags() TagSet
}
84 changes: 84 additions & 0 deletions cache/helpers.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package cache

import (
"bytes"
"encoding/json"
"math"
"strconv"
)

// Encode encodes json.Marshal to a string
func Encode(item interface{}) (string, error) {
value, err := json.Marshal(item)

return string(value), err
}

// SimpleDecode decodes json.Unmarshal to a string
func SimpleDecode(value string) (string, error) {
err := json.Unmarshal([]byte(value), &value)

return string(value), err
}

// Decode performs a json.Unmarshal
func Decode(value string, entity interface{}) (interface{}, error) {
err := json.Unmarshal([]byte(value), &entity)

return entity, err
}

// IsNumeric check if the provided value is numeric
func IsNumeric(s interface{}) bool {
switch s.(type) {
case int:
return true
case int32:
return true
case float32:
return true
case float64:
return true
default:
return false
}
}

// GetTaggedManyKey returns a tagged many key
func GetTaggedManyKey(prefix string, key string) string {
count := len(prefix) + 41

sub := ""
subs := []string{}

runs := bytes.Runes([]byte(key))

for i, run := range runs {
sub = sub + string(run)
if (i+1)%count == 0 {
subs = append(subs, sub)
sub = ""
} else if (i + 1) == len(runs) {
subs = append(subs, sub)
}
}

return subs[1]
}

// IsStringNumeric checks if a string is numeric
func IsStringNumeric(value string) bool {
_, err := strconv.ParseFloat(value, 64)

return err == nil
}

// StringToFloat64 converts a string to float64
func StringToFloat64(value string) (float64, error) {
return strconv.ParseFloat(value, 64)
}

// IsFloat checks if the provided value can be truncated to an int
func IsFloat(value float64) bool {
return value != math.Trunc(value)
}
34 changes: 34 additions & 0 deletions cache/map_connector.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package cache

import (
"errors"
)

// MapConnector is a representation of the array store connector
type MapConnector struct{}

// Connect is responsible for connecting with the caching store
func (ac *MapConnector) Connect(params map[string]interface{}) (StoreInterface, error) {
params, err := ac.validate(params)

if err != nil {
return &MapStore{}, err
}

prefix := params["prefix"].(string)

delete(params, "prefix")

return &MapStore{
Client: make(map[string]interface{}),
Prefix: prefix,
}, nil
}

func (ac *MapConnector) validate(params map[string]interface{}) (map[string]interface{}, error) {
if _, ok := params["prefix"]; !ok {
return params, errors.New("You need to specify a caching prefix.")
}

return params, nil
}
Loading