From 8c73487c081d474e014c6faf5d1c83898699b73c Mon Sep 17 00:00:00 2001 From: Gregory Eremin Date: Tue, 26 Jul 2016 21:59:27 +0200 Subject: [PATCH] Caller is moved into its own repo --- caller/README.md | 27 ------ caller/caller.go | 84 ------------------ caller/caller_test.go | 202 ------------------------------------------ 3 files changed, 313 deletions(-) delete mode 100644 caller/README.md delete mode 100644 caller/caller.go delete mode 100644 caller/caller_test.go diff --git a/caller/README.md b/caller/README.md deleted file mode 100644 index 1e628f8..0000000 --- a/caller/README.md +++ /dev/null @@ -1,27 +0,0 @@ -# Caller - -Package caller is used to dynamically call functions with data unmarshalled -into the functions' first argument. Its main purpose is to hide common -unmarshalling code from each function implementation thus reducing -boilerplate and making package interaction code sexier. - -[Documentation](https://godoc.org/github.com/localhots/shezmu/caller) - -Caller abstracts away the process of unmarshaling data before processing. - -```go -type PriceUpdate struct { - Product string `json:"product"` - Amount float32 `json:"amount"` -} - -func PriceUpdatePrinter(p PriceUpdate) { - log.Printf("Price for %q is now $%.2f", p.Product, p.Amount) -} - -// Error handling is skipped for clarity -func main() { - printer, _ := caller.New(PriceUpdatePrinter) - _ = printer.Call([]byte(`{"product": "Paperclip", "amount": 0.01}`)) -} -``` diff --git a/caller/caller.go b/caller/caller.go deleted file mode 100644 index 76584bd..0000000 --- a/caller/caller.go +++ /dev/null @@ -1,84 +0,0 @@ -// Package caller is used to dynamically call functions with data unmarshalled -// into the functions' first argument. Its main purpose is to hide common -// unmarshalling code from each function implementation thus reducing -// boilerplate and making package interaction code sexier. -package caller - -import ( - "encoding/json" - "errors" - "reflect" -) - -// Caller wraps a function and makes it ready to be dynamically called. -type Caller struct { - // Unmarshaller is a BYOB unmarshaller function. By default it uses JSON. - Unmarshaller func(data []byte, v interface{}) error - fun reflect.Value - argtyp reflect.Type -} - -var ( - // ErrInvalidFunctionType is an error that is returned by the New function - // when its argument is not a function. - ErrInvalidFunctionType = errors.New("argument must be function") - // ErrInvalidFunctionInArguments is an error that is returned by the New - // function when its argument-function has a number of input arguments other - // than 1. - ErrInvalidFunctionInArguments = errors.New("function must have only one input argument") - // ErrInvalidFunctionOutArguments is an error that is returned by the New - // function when its argument-function returs any values. - ErrInvalidFunctionOutArguments = errors.New("function must not have output arguments") -) - -// New creates a new Caller instance using the function given as an argument. -// It returns the Caller instance and an error if something is wrong with the -// argument-function. -func New(fun interface{}) (c *Caller, err error) { - fval := reflect.ValueOf(fun) - ftyp := reflect.TypeOf(fun) - if ftyp.Kind() != reflect.Func { - return nil, ErrInvalidFunctionType - } - if ftyp.NumIn() != 1 { - return nil, ErrInvalidFunctionInArguments - } - if ftyp.NumOut() != 0 { - return nil, ErrInvalidFunctionOutArguments - } - - c = &Caller{ - Unmarshaller: json.Unmarshal, - fun: fval, - argtyp: ftyp.In(0), - } - - return c, nil -} - -// Call creates an instance of the Caller function's argument type, unmarshalls -// the payload into it and dynamically calls the Caller function with this -// instance. -func (c *Caller) Call(data []byte) error { - val, err := c.unmarshal(data) - if err != nil { - return err - } - - c.makeDynamicCall(val) - return nil -} - -func (c *Caller) unmarshal(data []byte) (val reflect.Value, err error) { - val = c.newValue() - err = c.Unmarshaller(data, val.Interface()) - return -} - -func (c *Caller) makeDynamicCall(val reflect.Value) { - c.fun.Call([]reflect.Value{val.Elem()}) -} - -func (c *Caller) newValue() reflect.Value { - return reflect.New(c.argtyp) -} diff --git a/caller/caller_test.go b/caller/caller_test.go deleted file mode 100644 index bf74670..0000000 --- a/caller/caller_test.go +++ /dev/null @@ -1,202 +0,0 @@ -package caller - -import ( - "encoding/json" - "fmt" - "io/ioutil" - "os" - "testing" -) - -// -// Testing targets -// - -type testMessage struct { - Body string `json:"body"` -} - -const testPayload = `{"body":"Success!"}` - -func testFun(m testMessage) { - fmt.Print(m.Body) -} - -func testFunSilent(_ testMessage) {} - -// -// Tests -// - -func TestNewCallerSuccess(t *testing.T) { - c, err := New(testFun) - if err != nil { - t.Errorf("Expected no error, got: %v", err) - } - if c == nil { - t.Error("Expected an instance of Caller, got nil") - } -} - -func TestNewCallerWithNonFunc(t *testing.T) { - c, err := New(1) - if err != ErrInvalidFunctionType { - t.Errorf("Expected ErrInvalidFunctionType, got: %v", err) - } - if c != nil { - t.Error("Expected nil, got an instance of Caller") - } -} - -func TestNewCallerWithFuncMultipleArgs(t *testing.T) { - fun := func(a, b int) {} - c, err := New(fun) - if err != ErrInvalidFunctionInArguments { - t.Errorf("Expected ErrInvalidFunctionInArguments, got: %v", err) - } - if c != nil { - t.Error("Expected nil, got an instance of Caller") - } -} - -func TestNewCallerWithFuncReturnValue(t *testing.T) { - fun := func(a int) int { return 0 } - c, err := New(fun) - if err != ErrInvalidFunctionOutArguments { - t.Errorf("Expected ErrInvalidFunctionOutArguments, got: %v", err) - } - if c != nil { - t.Error("Expected nil, got an instance of Caller") - } -} - -func TestCallSuccess(t *testing.T) { - c, err := New(testFun) - if err != nil { - t.Fatal(err.Error()) - } - - out := captureStdoutAround(func() { - if err := c.Call([]byte(testPayload)); err != nil { - t.Fatal(err.Error()) - } - }) - - if string(out) != "Success!" { - t.Errorf("Expected output to be %q, got %q", "Success!", out) - } -} - -func TestCallFalure(t *testing.T) { - c, _ := New(testFunSilent) - - err := c.Call([]byte("{")) - if err == nil { - t.Error("Expected unmarshalling error, got nil") - } -} - -func TestUnmarshalSuccess(t *testing.T) { - c, _ := New(testFunSilent) - - _, err := c.unmarshal([]byte(testPayload)) - if err != nil { - t.Errorf("Expected no error, got: %v", err) - } -} - -func TestUnmarshalFailure(t *testing.T) { - c, _ := New(testFunSilent) - - _, err := c.unmarshal([]byte("{")) - if err == nil { - t.Error("Expected unmarshalling error, got nil") - } -} - -func captureStdoutAround(f func()) []byte { - origStdout := os.Stdout - r, w, _ := os.Pipe() - os.Stdout = w - - f() - - w.Close() - out, err := ioutil.ReadAll(r) - if err != nil { - os.Stdout = origStdout - panic(err) - } - r.Close() - os.Stdout = origStdout - - return out -} - -// -// Benchmarks -// - -func BenchmarkCaller(b *testing.B) { - c, _ := New(testFunSilent) - - for i := 0; i < b.N; i++ { - c.Call([]byte(testPayload)) - } -} - -func BenchmarkNoCaller(b *testing.B) { - for i := 0; i < b.N; i++ { - var msg testMessage - json.Unmarshal([]byte(testPayload), &msg) - testFunSilent(msg) - } -} - -func BenchmarkDynamicNew(b *testing.B) { - c, _ := New(testFunSilent) - - for i := 0; i < b.N; i++ { - _ = c.newValue() - } -} - -func BenchmarkStaticNew(b *testing.B) { - for i := 0; i < b.N; i++ { - _ = testMessage{} - } -} - -func BenchmarkDynamicCall(b *testing.B) { - c, _ := New(testFunSilent) - val, _ := c.unmarshal([]byte(testPayload)) - - for i := 0; i < b.N; i++ { - c.makeDynamicCall(val) - } -} - -func BenchmarkStaticCall(b *testing.B) { - var msg testMessage - - for i := 0; i < b.N; i++ { - testFunSilent(msg) - } -} - -func BenchmarkUnmarshalIntoInterface(b *testing.B) { - c, _ := New(testFunSilent) - val := c.newValue() - - for i := 0; i < b.N; i++ { - json.Unmarshal([]byte(testPayload), val.Interface()) - } -} - -func BenchmarkUnmarshalIntoTypedValue(b *testing.B) { - var msg testMessage - - for i := 0; i < b.N; i++ { - json.Unmarshal([]byte(testPayload), &msg) - } -}