<commit_before>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/lexer"
	"github.com/goby-lang/goby/compiler/parser"
	"os"
	"runtime"
	"testing"
)

func TestVM_REPLExec(t *testing.T) {
	tests := []struct {
		inputs   []string
		expected interface{}
	}{
		{
			[]string{
				`
				a, b = [3, 6]
				a + b
				`,
			},
			9,
		}, {
			[]string{
				`
				a, _, c = [1, 2, 3]
				c
				`,
			},
			3,
		}, {
			[]string{`
def foo(x)
  yield(x + 10)
end
def bar(y)
  foo(y) do |f|
	yield(f)
  end
end
def baz(z)
  bar(z + 100) do |b|
	yield(b)
  end
end
a = 0
baz(100) do |b|
  a = b
end
a
			`,
				`
class Foo
  def bar
	100
  end
end
module Baz
  class Bar
	def bar
	  Foo.new.bar
	end
  end
end
				`,
				`
Baz::Bar.new.bar + a
`,
			},
			310,
		},
		{
			[]string{`

def foo
  123
end
`, `
foo
`},
			123},
		{
			[]string{
				`
class Foo
  def bar(x)
    x + 10
  end
end
`,
				`
Foo.new.bar(90)
`},
			100},
		{
			[]string{
				`
def foo
  123
end
`,
				`
foo
`,
				`
def foo
  345
end
`,

				`
foo
`,
			}, 345},
	}

	for i, test := range tests {
		v := initTestVM()
		v.InitForREPL()

		// Initialize parser, lexer is not important here
		p := parser.New(lexer.New(""))
		p.Mode = parser.REPLMode

		program, _ := p.ParseProgram()

		// Initialize code generator, and it will behavior a little different in REPL mode.
		g := bytecode.NewGenerator()
		g.REPL = true
		g.InitTopLevelScope(program)

		for _, input := range test.inputs {
			p := parser.New(lexer.New(input))
			p.Mode = parser.REPLMode

			program, _ := p.ParseProgram()
			sets := g.GenerateInstructions(program.Statements)

			v.REPLExec(sets)
		}

		evaluated := v.GetExecResult()
		verifyExpected(t, i, evaluated, test.expected)
		// Because REPL should maintain a base call frame so that the whole program won't exit
		v.checkCFP(t, i, 1)
	}
}

func initTestVM() *VM {
	fn, err := os.Getwd()

	if err != nil {
		panic(err)
	}

	v, err := New(fn, []string{})

	if err != nil {
		panic(err)
	}

	v.mode = TestMode
	return v
}

func (v *VM) testEval(t *testing.T, input, filepath string) Object {
	iss, err := compiler.CompileToInstructions(input, parser.TestMode)

	if err != nil {
		t.Helper()
		t.Errorf("Error when compiling input: %s", input)
		t.Fatal(err.Error())
	}

	v.ExecInstructions(iss, filepath)

	return v.mainThread.stack.top().Target
}

func (v *VM) checkCFP(t *testing.T, index, expectedCFP int) {
	t.Helper()
	if v.mainThread.cfp != expectedCFP {
		t.Errorf("At case %d expect main thread's cfp to be %d. got: %d", index, expectedCFP, v.mainThread.cfp)
	}
}

func (v *VM) checkSP(t *testing.T, index, expectedSp int) {
	t.Helper()
	if v.mainThread.sp != expectedSp {
		fmt.Println(v.mainThread.stack.inspect())
		t.Errorf("At case %d expect main thread's sp to be %d. got: %d", index, expectedSp, v.mainThread.sp)
	}

}

// Verification helpers

func verifyExpected(t *testing.T, i int, evaluated Object, expected interface{}) {
	t.Helper()
	if isError(evaluated) {
		t.Errorf("At test case %d: %s", i, evaluated.toString())
		return
	}

	switch expected := expected.(type) {
	case int:
		verifyIntegerObject(t, i, evaluated, expected)
	case float64:
		verifyFloatObject(t, i, evaluated, expected)
	case string:
		verifyStringObject(t, i, evaluated, expected)
	case bool:
		verifyBooleanObject(t, i, evaluated, expected)
	case []interface{}:
		verifyArrayObject(t, i, evaluated, expected)
	case nil:
		verifyNullObject(t, i, evaluated)
	default:
		t.Errorf("Unknown type %T at case %d", expected, i)
	}
}

func verifyIntegerObject(t *testing.T, i int, obj Object, expected int) bool {
	t.Helper()
	switch result := obj.(type) {
	case *IntegerObject:
		if result.value != expected {
			t.Errorf("At test case %d: object has wrong value. expect=%d, got=%d", i, expected, result.value)
			return false
		}

		return true
	case *Error:
		t.Errorf("At test case %d: %s", i, result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not Integer. got=%s (%+v).", i, obj.Class().Name, obj)
		return false
	}
}

func verifyFloatObject(t *testing.T, i int, obj Object, expected float64) bool {
	t.Helper()
	switch result := obj.(type) {
	case *FloatObject:
		if result.value != expected {
			t.Errorf("At test case %d: object has wrong value. expect=%f, got=%f", i, expected, result.value)
			return false
		}

		return true
	case *Error:
		t.Errorf("At test case %d: %s", i, result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not Float. got=%s (%+v).", i, obj.Class().Name, obj)
		return false
	}
}

func verifyNullObject(t *testing.T, i int, obj Object) bool {
	t.Helper()
	switch result := obj.(type) {
	case *NullObject:
		return true
	case *Error:
		t.Errorf("At test case %d: %s", i, result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not NULL. got=%s (%+v)", i, obj.Class().Name, obj)
		return false
	}
}

func verifyStringObject(t *testing.T, i int, obj Object, expected string) bool {
	t.Helper()
	switch result := obj.(type) {
	case *StringObject:
		if result.value != expected {
			t.Errorf("At test case %d: object has wrong value. expect=%q, got=%q", i, expected, result.value)
			return false
		}

		return true
	case *Error:
		t.Errorf(result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not String. got=%s (%+v).", i, obj.Class().Name, obj)
		return false
	}
}

func verifyBooleanObject(t *testing.T, i int, obj Object, expected bool) bool {
	t.Helper()
	switch result := obj.(type) {
	case *BooleanObject:
		if result.value != expected {
			t.Errorf("At test case %d: object has wrong value. expect=%t, got=%t", i, expected, result.value)
			return false
		}

		return true
	case *Error:
		t.Errorf(result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not Boolean. got=%s (%+v).", i, obj.Class().Name, obj)
		return false
	}
}

func verifyArrayObject(t *testing.T, index int, obj Object, expected []interface{}) bool {
	t.Helper()
	result, ok := obj.(*ArrayObject)
	if !ok {
		t.Errorf("At test case %d: object is not Array. got=%s (%+v)", index, obj.Class().Name, obj)
		return false
	}

	if len(result.Elements) != len(expected) {
		t.Errorf("Don't equals length of array. Expect %d, got=%d", len(expected), len(result.Elements))
	}

	for i := 0; i < len(result.Elements); i++ {
		verifyExpected(t, index, result.Elements[i], expected[i])
	}

	return true
}

// Same as testHashObject(), but expects a ConcurrentArray.
func verifyConcurrentArrayObject(t *testing.T, index int, obj Object, expected []interface{}) bool {
	t.Helper()
	result, ok := obj.(*ConcurrentArrayObject)
	if !ok {
		t.Errorf("At test case %d: object is not ConcurrentArray. got=%s (%+v)", index, obj.Class().Name, obj)
		return false
	}

	if len(result.InternalArray.Elements) != len(expected) {
		t.Errorf("Don't equals length of array. Expect %d, got=%d", len(expected), len(result.InternalArray.Elements))
	}

	for i := 0; i < len(result.InternalArray.Elements); i++ {
		verifyExpected(t, i, result.InternalArray.Elements[i], expected[i])
	}

	return true
}

// Same as testHashObject(), but expects a ConcurrentHash.
//
func verifyConcurrentHashObject(t *testing.T, index int, objectResult Object, expected map[string]interface{}) bool {
	t.Helper()
	result, ok := objectResult.(*ConcurrentHashObject)

	if !ok {
		t.Errorf("At test case %d: result is not ConcurrentHash. got=%s", index, objectResult.Class().Name)
		return false
	}

	pairs := make(map[string]Object)

	iterator := func(key, value interface{}) bool {
		pairs[key.(string)] = value.(Object)
		return true
	}

	result.internalMap.Range(iterator)

	return _checkHashPairs(t, pairs, expected)
}

// Tests a Hash Object, with a few limitations:
//
// - the tested hash must be shallow (no nested objects as values);
// - the test hash must have strings as keys;
// - the error message won't mention the key - only the value.
//
// The second limitation is currently the only Hash format in Goby, anyway.
//
func verifyHashObject(t *testing.T, index int, objectResult Object, expected map[string]interface{}) bool {
	t.Helper()
	result, ok := objectResult.(*HashObject)

	if !ok {
		t.Errorf("At test case %d: result is not Hash. got=%s", index, objectResult.Class().Name)
		return false
	}

	return _checkHashPairs(t, result.Pairs, expected)
}

// Testing API like testArrayObject(), but performed on bidimensional arrays.
//
// Input example:
//
//		evaluated = '[["a", 1], ["b", "2"]]'
//		expected = [][]interface{}{{"a", 1}, {"b", "2"}}
//		testBidimensionalArrayObject(t, i, evaluated, expected)
//
func verifyBidimensionalArrayObject(t *testing.T, index int, obj Object, expected [][]interface{}) bool {
	t.Helper()
	result, ok := obj.(*ArrayObject)
	if !ok {
		t.Errorf("At test case %d: object is not Array. got=%T (%+v)", index, obj, obj)
		return false
	}

	if len(result.Elements) != len(expected) {
		t.Errorf("Unexpected result size. Expect %d, got=%d", len(expected), len(result.Elements))
	}

	for i := 0; i < len(result.Elements); i++ {
		resultRow := result.Elements[i]
		expectedRow := expected[i]

		verifyArrayObject(t, index, resultRow, expectedRow)
	}

	return true
}

func isError(obj Object) bool {
	if obj != nil {
		_, ok := obj.(*Error)
		return ok
	}
	return false
}

// Internal helpers -----------------------------------------------------

func _checkHashPairs(t *testing.T, actual map[string]Object, expected map[string]interface{}) bool {
	if len(actual) != len(expected) {
		t.Errorf("Unexpected result size. Expected %d, got=%d", len(expected), len(actual))
	}

	for expectedKey, expectedValue := range expected {
		resultValue := actual[expectedKey]

		verifyExpected(t, i, resultValue, expectedValue)
	}

	return true
}

func getFilename() string {
	_, filename, _, _ := runtime.Caller(1)
	return filename
}
<commit_msg>added fail tests for vm's REPL mode<commit_after>package vm

import (
	"fmt"
	"github.com/goby-lang/goby/compiler"
	"github.com/goby-lang/goby/compiler/bytecode"
	"github.com/goby-lang/goby/compiler/lexer"
	"github.com/goby-lang/goby/compiler/parser"
	"os"
	"runtime"
	"testing"
	"github.com/goby-lang/goby/vm/errors"
)

func TestVM_REPLExec(t *testing.T) {
	tests := []struct {
		inputs   []string
		expected interface{}
	}{
		{
			[]string{
				`
				a, b = [3, 6]
				a + b
				`,
			},
			9,
		}, {
			[]string{
				`
				a, _, c = [1, 2, 3]
				c
				`,
			},
			3,
		}, {
			[]string{`
def foo(x)
  yield(x + 10)
end
def bar(y)
  foo(y) do |f|
	yield(f)
  end
end
def baz(z)
  bar(z + 100) do |b|
	yield(b)
  end
end
a = 0
baz(100) do |b|
  a = b
end
a
			`,
				`
class Foo
  def bar
	100
  end
end
module Baz
  class Bar
	def bar
	  Foo.new.bar
	end
  end
end
				`,
				`
Baz::Bar.new.bar + a
`,
			},
			310,
		},
		{
			[]string{`

def foo
  123
end
`, `
foo
`},
			123},
		{
			[]string{
				`
class Foo
  def bar(x)
    x + 10
  end
end
`,
				`
Foo.new.bar(90)
`},
			100},
		{
			[]string{
				`
def foo
  123
end
`,
				`
foo
`,
				`
def foo
  345
end
`,

				`
foo
`,
			}, 345},
		{
			[]string{
				`raise ArgumentError`,
			}, "error",
		},
	}

	for i, test := range tests {
		v := initTestVM()
		v.InitForREPL()

		// Initialize parser, lexer is not important here
		p := parser.New(lexer.New(""))
		p.Mode = parser.REPLMode

		program, _ := p.ParseProgram()

		// Initialize code generator, and it will behavior a little different in REPL mode.
		g := bytecode.NewGenerator()
		g.REPL = true
		g.InitTopLevelScope(program)

		for _, input := range test.inputs {
			p := parser.New(lexer.New(input))
			p.Mode = parser.REPLMode

			program, _ := p.ParseProgram()
			sets := g.GenerateInstructions(program.Statements)

			v.REPLExec(sets)
		}

		evaluated := v.GetExecResult()
		verifyExpected(t, i, evaluated, test.expected)
		// Because REPL should maintain a base call frame so that the whole program won't exit
		v.checkCFP(t, i, 1)
	}
}

func TestVM_REPLExecFail(t *testing.T) {

	tests := []struct {
		inputs  []string
		expected string
	}{
		{
			[]string{
				`raise ArgumentError`,
			},
			fmt.Sprintf("InternalError: '%s'",errors.ArgumentError),
		},
		{
			[]string{
				"NonExistentBuiltinMethod",
			},
			"NameError: uninitialized constant NonExistentBuiltinMethod",
		},
		{
			[]string{
				"Hash.notExist",
			},
			"UndefinedMethodError: Undefined Method 'notExist' for Hash",
		},
	}

	for i, test := range tests {
		v := initTestVM()
		v.InitForREPL()

		// Initialize parser, lexer is not important here
		p := parser.New(lexer.New(""))
		p.Mode = parser.REPLMode

		program, _ := p.ParseProgram()

		// Initialize code generator, and it will behavior a little different in REPL mode.
		g := bytecode.NewGenerator()
		g.REPL = true
		g.InitTopLevelScope(program)

		for _, input := range test.inputs {
			p := parser.New(lexer.New(input))
			p.Mode = parser.REPLMode

			// prevent parse errors from panicking tests
			program, Err := p.ParseProgram()
			if Err != nil {
				t.Fatalf("At case %d unexpected parse error %q", i, Err.Message)
			}
			sets := g.GenerateInstructions(program.Statements)

			v.REPLExec(sets)
		}

		evaluated := v.GetExecResult()

		if evaluated.toString() != test.expected {
			t.Fatalf("At case %d expected %s got %s", i,
				test.expected, evaluated.toString())
		}

		// Because REPL should maintain a base call frame so that the whole program won't exit
		v.checkCFP(t, i, 1)
	}
}

func initTestVM() *VM {
	fn, err := os.Getwd()

	if err != nil {
		panic(err)
	}

	v, err := New(fn, []string{})

	if err != nil {
		panic(err)
	}

	v.mode = TestMode
	return v
}

func (v *VM) testEval(t *testing.T, input, filepath string) Object {
	iss, err := compiler.CompileToInstructions(input, parser.TestMode)

	if err != nil {
		t.Helper()
		t.Errorf("Error when compiling input: %s", input)
		t.Fatal(err.Error())
	}

	v.ExecInstructions(iss, filepath)

	return v.mainThread.stack.top().Target
}

func (v *VM) checkCFP(t *testing.T, index, expectedCFP int) {
	t.Helper()
	if v.mainThread.cfp != expectedCFP {
		t.Errorf("At case %d expect main thread's cfp to be %d. got: %d", index, expectedCFP, v.mainThread.cfp)
	}
}

func (v *VM) checkSP(t *testing.T, index, expectedSp int) {
	t.Helper()
	if v.mainThread.sp != expectedSp {
		fmt.Println(v.mainThread.stack.inspect())
		t.Errorf("At case %d expect main thread's sp to be %d. got: %d", index, expectedSp, v.mainThread.sp)
	}

}

// Verification helpers

func verifyExpected(t *testing.T, i int, evaluated Object, expected interface{}) {
	t.Helper()
	if isError(evaluated) {
		t.Errorf("At test case %d: %s", i, evaluated.toString())
		return
	}

	switch expected := expected.(type) {
	case int:
		verifyIntegerObject(t, i, evaluated, expected)
	case float64:
		verifyFloatObject(t, i, evaluated, expected)
	case string:
		verifyStringObject(t, i, evaluated, expected)
	case bool:
		verifyBooleanObject(t, i, evaluated, expected)
	case []interface{}:
		verifyArrayObject(t, i, evaluated, expected)
	case nil:
		verifyNullObject(t, i, evaluated)
	default:
		t.Errorf("Unknown type %T at case %d", expected, i)
	}
}

func verifyIntegerObject(t *testing.T, i int, obj Object, expected int) bool {
	t.Helper()
	switch result := obj.(type) {
	case *IntegerObject:
		if result.value != expected {
			t.Errorf("At test case %d: object has wrong value. expect=%d, got=%d", i, expected, result.value)
			return false
		}

		return true
	case *Error:
		t.Errorf("At test case %d: %s", i, result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not Integer. got=%s (%+v).", i, obj.Class().Name, obj)
		return false
	}
}

func verifyFloatObject(t *testing.T, i int, obj Object, expected float64) bool {
	t.Helper()
	switch result := obj.(type) {
	case *FloatObject:
		if result.value != expected {
			t.Errorf("At test case %d: object has wrong value. expect=%f, got=%f", i, expected, result.value)
			return false
		}

		return true
	case *Error:
		t.Errorf("At test case %d: %s", i, result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not Float. got=%s (%+v).", i, obj.Class().Name, obj)
		return false
	}
}

func verifyNullObject(t *testing.T, i int, obj Object) bool {
	t.Helper()
	switch result := obj.(type) {
	case *NullObject:
		return true
	case *Error:
		t.Errorf("At test case %d: %s", i, result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not NULL. got=%s (%+v)", i, obj.Class().Name, obj)
		return false
	}
}

func verifyStringObject(t *testing.T, i int, obj Object, expected string) bool {
	t.Helper()
	switch result := obj.(type) {
	case *StringObject:
		if result.value != expected {
			t.Errorf("At test case %d: object has wrong value. expect=%q, got=%q", i, expected, result.value)
			return false
		}

		return true
	case *Error:
		t.Errorf(result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not String. got=%s (%+v).", i, obj.Class().Name, obj)
		return false
	}
}

func verifyBooleanObject(t *testing.T, i int, obj Object, expected bool) bool {
	t.Helper()
	switch result := obj.(type) {
	case *BooleanObject:
		if result.value != expected {
			t.Errorf("At test case %d: object has wrong value. expect=%t, got=%t", i, expected, result.value)
			return false
		}

		return true
	case *Error:
		t.Errorf(result.Message())
		return false
	default:
		t.Errorf("At test case %d: object is not Boolean. got=%s (%+v).", i, obj.Class().Name, obj)
		return false
	}
}

func verifyArrayObject(t *testing.T, index int, obj Object, expected []interface{}) bool {
	t.Helper()
	result, ok := obj.(*ArrayObject)
	if !ok {
		t.Errorf("At test case %d: object is not Array. got=%s (%+v)", index, obj.Class().Name, obj)
		return false
	}

	if len(result.Elements) != len(expected) {
		t.Errorf("Don't equals length of array. Expect %d, got=%d", len(expected), len(result.Elements))
	}

	for i := 0; i < len(result.Elements); i++ {
		verifyExpected(t, index, result.Elements[i], expected[i])
	}

	return true
}

// Same as testHashObject(), but expects a ConcurrentArray.
func verifyConcurrentArrayObject(t *testing.T, index int, obj Object, expected []interface{}) bool {
	t.Helper()
	result, ok := obj.(*ConcurrentArrayObject)
	if !ok {
		t.Errorf("At test case %d: object is not ConcurrentArray. got=%s (%+v)", index, obj.Class().Name, obj)
		return false
	}

	if len(result.InternalArray.Elements) != len(expected) {
		t.Errorf("Don't equals length of array. Expect %d, got=%d", len(expected), len(result.InternalArray.Elements))
	}

	for i := 0; i < len(result.InternalArray.Elements); i++ {
		verifyExpected(t, i, result.InternalArray.Elements[i], expected[i])
	}

	return true
}

// Same as testHashObject(), but expects a ConcurrentHash.
//
func verifyConcurrentHashObject(t *testing.T, index int, objectResult Object, expected map[string]interface{}) bool {
	t.Helper()
	result, ok := objectResult.(*ConcurrentHashObject)

	if !ok {
		t.Errorf("At test case %d: result is not ConcurrentHash. got=%s", index, objectResult.Class().Name)
		return false
	}

	pairs := make(map[string]Object)

	iterator := func(key, value interface{}) bool {
		pairs[key.(string)] = value.(Object)
		return true
	}

	result.internalMap.Range(iterator)

	return _checkHashPairs(t, pairs, expected)
}

// Tests a Hash Object, with a few limitations:
//
// - the tested hash must be shallow (no nested objects as values);
// - the test hash must have strings as keys;
// - the error message won't mention the key - only the value.
//
// The second limitation is currently the only Hash format in Goby, anyway.
//
func verifyHashObject(t *testing.T, index int, objectResult Object, expected map[string]interface{}) bool {
	t.Helper()
	result, ok := objectResult.(*HashObject)

	if !ok {
		t.Errorf("At test case %d: result is not Hash. got=%s", index, objectResult.Class().Name)
		return false
	}

	return _checkHashPairs(t, result.Pairs, expected)
}

// Testing API like testArrayObject(), but performed on bidimensional arrays.
//
// Input example:
//
//		evaluated = '[["a", 1], ["b", "2"]]'
//		expected = [][]interface{}{{"a", 1}, {"b", "2"}}
//		testBidimensionalArrayObject(t, i, evaluated, expected)
//
func verifyBidimensionalArrayObject(t *testing.T, index int, obj Object, expected [][]interface{}) bool {
	t.Helper()
	result, ok := obj.(*ArrayObject)
	if !ok {
		t.Errorf("At test case %d: object is not Array. got=%T (%+v)", index, obj, obj)
		return false
	}

	if len(result.Elements) != len(expected) {
		t.Errorf("Unexpected result size. Expect %d, got=%d", len(expected), len(result.Elements))
	}

	for i := 0; i < len(result.Elements); i++ {
		resultRow := result.Elements[i]
		expectedRow := expected[i]

		verifyArrayObject(t, index, resultRow, expectedRow)
	}

	return true
}

func isError(obj Object) bool {
	if obj != nil {
		_, ok := obj.(*Error)
		return ok
	}
	return false
}

// Internal helpers -----------------------------------------------------

func _checkHashPairs(t *testing.T, actual map[string]Object, expected map[string]interface{}) bool {
	if len(actual) != len(expected) {
		t.Errorf("Unexpected result size. Expected %d, got=%d", len(expected), len(actual))
	}

	for expectedKey, expectedValue := range expected {
		resultValue := actual[expectedKey]

		verifyExpected(t, i, resultValue, expectedValue)
	}

	return true
}

func getFilename() string {
	_, filename, _, _ := runtime.Caller(1)
	return filename
}
<|endoftext|>


<commit_before>package mysql

import (
	"database/sql"
	"fmt"
	"github.com/elwinar/rambler/configuration"
	"github.com/elwinar/rambler/migration/driver"
	_ "github.com/go-sql-driver/mysql"
)

func init() {
	driver.Register("mysql", Driver{})
}

type Driver struct {
}

func (d Driver) New(env configuration.Environment) (driver.Conn, error) {
	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@%s(%s:%d)/%s", env.User, env.Password, env.Protocol, env.Host, env.Port, env.Database))
	if err != nil {
		return nil, err
	}

	c := &Conn{
		db: db,
		schema: env.Database,
	}

	return c, nil
}

type Conn struct {
	db *sql.DB
	schema string
}

func (c *Conn) MigrationTableExists() (bool, error) {
	var name string
	err := c.db.QueryRow(fmt.Sprintf(`
		SELECT table_name as name
		FROM information_schema.tables
		WHERE table_schema = '%s'
		AND table_name = 'migrations'
	`, c.schema)).Scan(&name)
	if err != nil && err != sql.ErrNoRows {
		return false, err
	}
	if err != nil {
		return false, nil
	}
	return true, nil
}

func (c *Conn) CreateMigrationTable() error {
	_, err := c.db.Exec(`
		CREATE TABLE migrations (
			version BIGINT UNSIGNED NOT NULL PRIMARY KEY,
			description CHAR(255) NOT NULL,
			applied_at DATETIME NOT NULL
		) DEFAULT CHARSET=utf8
	`)
	return err
}

func (c *Conn) ListAppliedMigrations() ([]uint64, error) {
	rows, err := c.db.Query(`
		SELECT version
		FROM migrations
		ORDER BY version ASC
	`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var versions []uint64
	for rows.Next() {
		var version uint64
		err := rows.Scan(&version)
		if err != nil {
			return nil, err
		}

		versions = append(versions, version)
	}

	return versions, nil
}

func (c *Conn) SetMigrationApplied(version uint64, description string) error {
	_, err := c.db.Exec(`
		INSERT INTO migrations (version, description, applied_at)
		VALUES (?, ?, NOW())
	`, version, description)
	return err
}

func (c *Conn) UnsetMigrationApplied(version uint64) error {
	_, err := c.db.Exec(`
		DELETE FROM migrations
		WHERE version = ?
	`, version)
	return err
}

func (c *Conn) StartTransaction() (driver.Tx, error) {
	tx, err := c.db.Begin()
	return tx, err
}
<commit_msg>Fix the migration's table field description for mysql driver<commit_after>package mysql

import (
	"database/sql"
	"fmt"
	"github.com/elwinar/rambler/configuration"
	"github.com/elwinar/rambler/migration/driver"
	_ "github.com/go-sql-driver/mysql"
)

func init() {
	driver.Register("mysql", Driver{})
}

type Driver struct {
}

func (d Driver) New(env configuration.Environment) (driver.Conn, error) {
	db, err := sql.Open("mysql", fmt.Sprintf("%s:%s@%s(%s:%d)/%s", env.User, env.Password, env.Protocol, env.Host, env.Port, env.Database))
	if err != nil {
		return nil, err
	}

	c := &Conn{
		db: db,
		schema: env.Database,
	}

	return c, nil
}

type Conn struct {
	db *sql.DB
	schema string
}

func (c *Conn) MigrationTableExists() (bool, error) {
	var name string
	err := c.db.QueryRow(fmt.Sprintf(`
		SELECT table_name as name
		FROM information_schema.tables
		WHERE table_schema = '%s'
		AND table_name = 'migrations'
	`, c.schema)).Scan(&name)
	if err != nil && err != sql.ErrNoRows {
		return false, err
	}
	if err != nil {
		return false, nil
	}
	return true, nil
}

func (c *Conn) CreateMigrationTable() error {
	_, err := c.db.Exec(`
		CREATE TABLE migrations (
			version BIGINT UNSIGNED NOT NULL PRIMARY KEY,
			description VARCHAR(255) NOT NULL,
			applied_at DATETIME NOT NULL
		) DEFAULT CHARSET=utf8
	`)
	return err
}

func (c *Conn) ListAppliedMigrations() ([]uint64, error) {
	rows, err := c.db.Query(`
		SELECT version
		FROM migrations
		ORDER BY version ASC
	`)
	if err != nil {
		return nil, err
	}
	defer rows.Close()

	var versions []uint64
	for rows.Next() {
		var version uint64
		err := rows.Scan(&version)
		if err != nil {
			return nil, err
		}

		versions = append(versions, version)
	}

	return versions, nil
}

func (c *Conn) SetMigrationApplied(version uint64, description string) error {
	_, err := c.db.Exec(`
		INSERT INTO migrations (version, description, applied_at)
		VALUES (?, ?, NOW())
	`, version, description)
	return err
}

func (c *Conn) UnsetMigrationApplied(version uint64) error {
	_, err := c.db.Exec(`
		DELETE FROM migrations
		WHERE version = ?
	`, version)
	return err
}

func (c *Conn) StartTransaction() (driver.Tx, error) {
	tx, err := c.db.Begin()
	return tx, err
}
<|endoftext|>


<commit_before>package wikihelper

import (
	"github.com/shurcooL/go/github_flavored_markdown"
	"net/url"
	"regexp"
	"strings"
)

func UrlEncode(str string) (encoded string) {
	encoded = strings.Replace(str, "/", "-", -1)
	encoded = url.QueryEscape(encoded)
	encoded = strings.Replace(encoded, "+", "%20", -1)
	return encoded
}

func Render(markdown string) string {
	// ブラケットリンクを置換する
	body := markdown
	re := regexp.MustCompile("\\[\\[([^\\]\\[\\|]+)(\\|([^\\]\\[]+))?\\]\\]")
	for _, match := range re.FindAllStringSubmatch(body, -1) {
		bracketLink := match[0]
		title := match[1]
		alias := match[3]
		if alias == "" {
			alias = title
		}
		body = strings.Replace(body, bracketLink, ""+title+"", -1)
	}

	// Markdownへ変換
	html := string(github_flavored_markdown.Markdown([]byte(body)))
	html = strings.Replace(html, "
", "
", -1)
	return html
}
<commit_msg>Changed Github Flavored Markdown url.<commit_after>package wikihelper

import (
	"github.com/shurcooL/github_flavored_markdown"
	"net/url"
	"regexp"
	"strings"
)

func UrlEncode(str string) (encoded string) {
	encoded = strings.Replace(str, "/", "-", -1)
	encoded = url.QueryEscape(encoded)
	encoded = strings.Replace(encoded, "+", "%20", -1)
	return encoded
}

func Render(markdown string) string {
	// ブラケットリンクを置換する
	body := markdown
	re := regexp.MustCompile("\\[\\[([^\\]\\[\\|]+)(\\|([^\\]\\[]+))?\\]\\]")
	for _, match := range re.FindAllStringSubmatch(body, -1) {
		bracketLink := match[0]
		title := match[1]
		alias := match[3]
		if alias == "" {
			alias = title
		}
		body = strings.Replace(body, bracketLink, ""+title+"", -1)
	}

	// Markdownへ変換
	html := string(github_flavored_markdown.Markdown([]byte(body)))
	html = strings.Replace(html, "
", "
", -1)
	return html
}
<|endoftext|>


<commit_before>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nvimutil

import (
	"testing"

	"github.com/neovim/go-client/nvim"
)

func TestTerminal_getWindowSize(t *testing.T) {
	n := TestNvim(t)
	type fields struct {
		Nvim *nvim.Nvim
	}
	type args struct {
		cfg int64
		f   func(nvim.Window) (int, error)
	}
	tests := []struct {
		name   string
		fields fields
		args   args
		want   int
	}{
		{
			name: "Use config",
			fields: fields{
				Nvim: n,
			},
			args: args{
				cfg: 120,
				f:   n.WindowWidth,
			},
			want: 120,
		},
		{
			name: "Use (WindowWidth / 3 )",
			fields: fields{
				Nvim: n,
			},
			args: args{
				cfg: 0,
				f:   n.WindowWidth,
			},
			// default embedded Nvim Window width is 80.
			want: 26,
		},
		{
			name: "Use (WindowHeight / 3 )",
			fields: fields{
				Nvim: n,
			},
			args: args{
				cfg: 0,
				f:   n.WindowHeight,
			},
			// default embedded Nvim Window height is 22.
			want: 7,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			term := &Terminal{
				Nvim: tt.fields.Nvim,
			}
			if got := term.getSplitWindowSize(tt.args.cfg, tt.args.f); got != tt.want {
				t.Errorf("Terminal.getWindowSize(%v, %v) = %v, want %v", tt.args.cfg, tt.args.f, got, tt.want)
			}
		})
	}
}
<commit_msg>test/nvimutil: fix getSplitWindowSize test funcname<commit_after>// Copyright 2016 The nvim-go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package nvimutil

import (
	"testing"

	"github.com/neovim/go-client/nvim"
)

func TestTerminal_getSplitWindowSize(t *testing.T) {
	n := TestNvim(t)
	type fields struct {
		Nvim *nvim.Nvim
	}
	type args struct {
		cfg int64
		f   func(nvim.Window) (int, error)
	}
	tests := []struct {
		name   string
		fields fields
		args   args
		want   int
	}{
		{
			name: "Use config",
			fields: fields{
				Nvim: n,
			},
			args: args{
				cfg: 120,
				f:   n.WindowWidth,
			},
			want: 120,
		},
		{
			name: "Use (WindowWidth / 3 )",
			fields: fields{
				Nvim: n,
			},
			args: args{
				cfg: 0,
				f:   n.WindowWidth,
			},
			// default embedded Nvim Window width is 80.
			want: 26,
		},
		{
			name: "Use (WindowHeight / 3 )",
			fields: fields{
				Nvim: n,
			},
			args: args{
				cfg: 0,
				f:   n.WindowHeight,
			},
			// default embedded Nvim Window height is 22.
			want: 7,
		},
	}
	for _, tt := range tests {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()
			term := &Terminal{
				Nvim: tt.fields.Nvim,
			}
			if got := term.getSplitWindowSize(tt.args.cfg, tt.args.f); got != tt.want {
				t.Errorf("getSplitWindowSize(%v) = %v, want %v", tt.args.cfg, got, tt.want)
			}
		})
	}
}
<|endoftext|>


<commit_before>package ui

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
)

// Window is a window (UI component)
type Window struct {
	component
	titlebarHeight  int
	childLeftPad    int
	titleColor      color.Color
	backgroundColor color.Color
	borderColor     color.Color
	title           *Text
	close           *Button
}

var (
	windowBgColor     = color.RGBA{0x50, 0x50, 0x50, 192} // gray, 25% transparent
	windowBorderColor = color.RGBA{0x40, 0x40, 0x40, 192} // white, 25% transparent
	windowTitleColor  = color.RGBA{0x50, 0x50, 0x50, 192} //gray
)

// NewWindow creates a new window with height of `height` + height of title bar
func NewWindow(width, height int, titleText string) *Window {
	wnd := Window{}
	wnd.Dimension.Width = width
	wnd.Dimension.Height = height

	wnd.backgroundColor = windowBgColor
	wnd.borderColor = windowBorderColor
	wnd.titleColor = windowTitleColor

	if titleText != "" {
		fnt, err := NewFont(defaultFontName, 10, 72, White)
		if err != nil {
			fmt.Println("error:", err)
			return nil
		}
		title := NewText(fnt)
		title.Position = Point{X: 1, Y: 0}
		wnd.title = title
		wnd.title.SetText(titleText)
		wnd.addChild(title)

		wnd.titlebarHeight = wnd.title.GetHeight() + 1
		wnd.childLeftPad = 1
	}

	close := NewButton(wnd.titlebarHeight, wnd.titlebarHeight)
	close.OnClick = func() {
		wnd.Hide()
	}
	wnd.close = close
	wnd.addChild(close)

	return &wnd
}

// AddChild adds a child component to the window, adjusting position
func (wnd *Window) AddChild(c Component) {
	if val, ok := c.(Positioner); ok {
		pos := val.GetPosition()
		pos.X += wnd.childLeftPad
		pos.Y += wnd.titlebarHeight
		val.SetPosition(pos)
	}
	wnd.addChild(c)
}

func (wnd *Window) addChild(c Component) {
	wnd.isClean = false
	wnd.children = append(wnd.children, c)
}

// HideCloseButton ...
func (wnd *Window) HideCloseButton() {
	wnd.close.Hide()
}

// HideTitle ...
func (wnd *Window) HideTitle() {
	wnd.title.Hide()
}

// SetTitle ...
func (wnd *Window) SetTitle(s string) *Window {
	wnd.title.SetText(s)
	return wnd
}

// SetTitleColor ...
func (wnd *Window) SetTitleColor(col color.Color) {
	wnd.titleColor = col
}

// SetBackgroundColor ...
func (wnd *Window) SetBackgroundColor(col color.Color) {
	wnd.backgroundColor = col
}

// SetBorderColor ...
func (wnd *Window) SetBorderColor(col color.Color) {
	wnd.borderColor = col
}

// Draw redraws internal buffer
func (wnd *Window) Draw(mx, my int) *image.RGBA {
	if wnd.isHidden {
		return nil
	}
	if wnd.isClean {
		if !wnd.isChildrenClean() {
			wnd.isClean = false
		} else {
			return wnd.Image
		}
	}
	wnd.initImage()

	wnd.close.Position = Point{X: wnd.Dimension.Width - wnd.close.Dimension.Width, Y: 0}

	// draw background color
	rect := image.Rect(0, 0, wnd.Dimension.Width, wnd.Dimension.Height)
	draw.Draw(wnd.Image, rect, &image.Uniform{wnd.backgroundColor}, image.ZP, draw.Over)

	// draw titlebar rect
	if wnd.title != nil && !wnd.title.isHidden {
		titleRect := image.Rect(0, 0, wnd.Dimension.Width, wnd.titlebarHeight)
		draw.Draw(wnd.Image, titleRect, &image.Uniform{wnd.titleColor}, image.ZP, draw.Over)
	}

	// draw outline
	outlineRect := image.Rect(0, 0, wnd.Dimension.Width-1, wnd.Dimension.Height-1)
	DrawRect(wnd.Image, outlineRect, wnd.borderColor)

	wnd.drawChildren(mx, my)

	wnd.isClean = true
	return wnd.Image
}

// Click pass click to window child components
func (wnd *Window) Click(mouse Point) bool {
	childPoint := Point{X: mouse.X - wnd.Position.X, Y: mouse.Y - wnd.Position.Y}
	for _, c := range wnd.children {
		if childPoint.In(c.GetBounds()) {
			c.Click(childPoint)
			return true
		}
	}
	return false
}

// TitlebarHeight ...
func (wnd *Window) TitlebarHeight() int {
	return wnd.titlebarHeight
}
<commit_msg>window: HideTitle() check for nil<commit_after>package ui

import (
	"fmt"
	"image"
	"image/color"
	"image/draw"
)

// Window is a window (UI component)
type Window struct {
	component
	titlebarHeight  int
	childLeftPad    int
	titleColor      color.Color
	backgroundColor color.Color
	borderColor     color.Color
	title           *Text
	close           *Button
}

var (
	windowBgColor     = color.RGBA{0x50, 0x50, 0x50, 192} // gray, 25% transparent
	windowBorderColor = color.RGBA{0x40, 0x40, 0x40, 192} // white, 25% transparent
	windowTitleColor  = color.RGBA{0x50, 0x50, 0x50, 192} //gray
)

// NewWindow creates a new window with height of `height` + height of title bar
func NewWindow(width, height int, titleText string) *Window {
	wnd := Window{}
	wnd.Dimension.Width = width
	wnd.Dimension.Height = height

	wnd.backgroundColor = windowBgColor
	wnd.borderColor = windowBorderColor
	wnd.titleColor = windowTitleColor

	if titleText != "" {
		fnt, err := NewFont(defaultFontName, 10, 72, White)
		if err != nil {
			fmt.Println("error:", err)
			return nil
		}
		title := NewText(fnt)
		title.Position = Point{X: 1, Y: 0}
		wnd.title = title
		wnd.title.SetText(titleText)
		wnd.addChild(title)

		wnd.titlebarHeight = wnd.title.GetHeight() + 1
		wnd.childLeftPad = 1
	}

	close := NewButton(wnd.titlebarHeight, wnd.titlebarHeight)
	close.OnClick = func() {
		wnd.Hide()
	}
	wnd.close = close
	wnd.addChild(close)

	return &wnd
}

// AddChild adds a child component to the window, adjusting position
func (wnd *Window) AddChild(c Component) {
	if val, ok := c.(Positioner); ok {
		pos := val.GetPosition()
		pos.X += wnd.childLeftPad
		pos.Y += wnd.titlebarHeight
		val.SetPosition(pos)
	}
	wnd.addChild(c)
}

func (wnd *Window) addChild(c Component) {
	wnd.isClean = false
	wnd.children = append(wnd.children, c)
}

// HideCloseButton ...
func (wnd *Window) HideCloseButton() {
	wnd.close.Hide()
}

// HideTitle ...
func (wnd *Window) HideTitle() {
	if wnd.title != nil {
		wnd.title.Hide()
	}
}

// SetTitle ...
func (wnd *Window) SetTitle(s string) *Window {
	wnd.title.SetText(s)
	return wnd
}

// SetTitleColor ...
func (wnd *Window) SetTitleColor(col color.Color) {
	wnd.titleColor = col
}

// SetBackgroundColor ...
func (wnd *Window) SetBackgroundColor(col color.Color) {
	wnd.backgroundColor = col
}

// SetBorderColor ...
func (wnd *Window) SetBorderColor(col color.Color) {
	wnd.borderColor = col
}

// Draw redraws internal buffer
func (wnd *Window) Draw(mx, my int) *image.RGBA {
	if wnd.isHidden {
		return nil
	}
	if wnd.isClean {
		if !wnd.isChildrenClean() {
			wnd.isClean = false
		} else {
			return wnd.Image
		}
	}
	wnd.initImage()

	wnd.close.Position = Point{X: wnd.Dimension.Width - wnd.close.Dimension.Width, Y: 0}

	// draw background color
	rect := image.Rect(0, 0, wnd.Dimension.Width, wnd.Dimension.Height)
	draw.Draw(wnd.Image, rect, &image.Uniform{wnd.backgroundColor}, image.ZP, draw.Over)

	// draw titlebar rect
	if wnd.title != nil && !wnd.title.isHidden {
		titleRect := image.Rect(0, 0, wnd.Dimension.Width, wnd.titlebarHeight)
		draw.Draw(wnd.Image, titleRect, &image.Uniform{wnd.titleColor}, image.ZP, draw.Over)
	}

	// draw outline
	outlineRect := image.Rect(0, 0, wnd.Dimension.Width-1, wnd.Dimension.Height-1)
	DrawRect(wnd.Image, outlineRect, wnd.borderColor)

	wnd.drawChildren(mx, my)

	wnd.isClean = true
	return wnd.Image
}

// Click pass click to window child components
func (wnd *Window) Click(mouse Point) bool {
	childPoint := Point{X: mouse.X - wnd.Position.X, Y: mouse.Y - wnd.Position.Y}
	for _, c := range wnd.children {
		if childPoint.In(c.GetBounds()) {
			c.Click(childPoint)
			return true
		}
	}
	return false
}

// TitlebarHeight ...
func (wnd *Window) TitlebarHeight() int {
	return wnd.titlebarHeight
}
<|endoftext|>


<commit_before>package urlutil

import (
	"errors"
	"io"
	"net/http"
	"net/url"
	"os"
)

func open(rawurl string) (io.ReadCloser, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}
	if u.Scheme == "file" {
		return os.Open(u.Path)
	}
	if u.Scheme == "http" || u.Scheme == "https" {
		resp, err := http.Get(rawurl)
		if err != nil {
			return nil, err
		}
		if resp.StatusCode != http.StatusOK {
			return nil, errors.New(resp.Status)
		}
		return resp.Body, nil
	}
	return nil, errors.New("unknown scheme: " + u.Scheme)
}
<commit_msg>Improve error messages in lib/url/urlutil.Open().<commit_after>package urlutil

import (
	"errors"
	"io"
	"net/http"
	"net/url"
	"os"
)

func open(rawurl string) (io.ReadCloser, error) {
	u, err := url.Parse(rawurl)
	if err != nil {
		return nil, err
	}
	if u.Scheme == "file" {
		return os.Open(u.Path)
	}
	if u.Scheme == "http" || u.Scheme == "https" {
		resp, err := http.Get(rawurl)
		if err != nil {
			return nil,
				errors.New("error getting: " + rawurl + ": " + err.Error())
		}
		if resp.StatusCode != http.StatusOK {
			return nil,
				errors.New("error getting: " + rawurl + ": " + resp.Status)
		}
		return resp.Body, nil
	}
	return nil, errors.New("unknown scheme: " + u.Scheme)
}
<|endoftext|>


<commit_before>package clc

import (
	"errors"
	"fmt"
	"time"
)

type Server struct {
	Name           string `json:"name"`
	Description    string `json:"description"`
	GroupID        string `json:"groupId"`
	SourceServerID string `json:"sourceServerId"`
	IsManagedOS    bool   `json:"isManagedOS,omitempty"`
	PrimaryDNS     string `json:"primaryDns,omitempty"`
	SecondaryDNS   string `json:"secondaryDns,omitempty"`
	NetworkID      string `json:"networkId,omitempty"`
	IPaddress      string `json:"ipAddress,omitempty"`
	Password       string `json:"password,omitempty"`
	CPU            int    `json:"cpu"`
	MemoryGB       int    `json:"memoryGB"`
	Type           string `json:"type"`
	Storagetype    string `json:"storageType,omitempty"`
	Customfields   []struct {
		ID    string `json:"id"`
		Value string `json:"value"`
	} `json:"customFields,omitempty"`
	Additionaldisks []struct {
		Path   string `json:"path"`
		SizeGB int    `json:"sizeGB"`
		Type   string `json:"type"`
	} `json:"additionalDisks,omitempty"`
	TTL time.Time `json:"ttl,omitempty"`
}

func (s *Server) Valid() bool {
	return s.Name != "" && s.CPU != 0 && s.MemoryGB != 0 && s.GroupID != "" && s.SourceServerID != ""
}

type ServerResponse struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	GroupID     string `json:"groupId"`
	IsTemplate  bool   `json:"isTemplate"`
	LocationID  string `json:"locationId"`
	OStype      string `json:"osType"`
	Status      string `json:"status"`
	Details     struct {
		IPaddresses []struct {
			Internal string `json:"internal"`
		} `json:"ipAddresses"`
		AlertPolicies []struct {
			ID    string `json:"id"`
			Name  string `json:"name"`
			Links []Link `json:"links"`
		} `json:"alertPolicies"`
		CPU               int    `json:"cpu"`
		Diskcount         int    `json:"diskCount"`
		Hostname          string `json:"hostName"`
		InMaintenanceMode bool   `json:"inMaintenanceMode"`
		MemoryMB          int    `json:"memoryMB"`
		Powerstate        string `json:"powerState"`
		Storagegb         int    `json:"storageGB"`
		Disks             []struct {
			ID             string        `json:"id"`
			SizeGB         int           `json:"sizeGB"`
			PartitionPaths []interface{} `json:"partitionPaths"`
		} `json:"disks"`
		Partitions []struct {
			SizeGB float64 `json:"sizeGB"`
			Path   string  `json:"path"`
		} `json:"partitions"`
		Snapshots []struct {
			Name  string `json:"name"`
			Links []Link `json:"links"`
		} `json:"snapshots"`
		Customfields []struct {
			ID           string `json:"id"`
			Name         string `json:"name"`
			Value        string `json:"value"`
			Displayvalue string `json:"displayValue"`
		} `json:"customFields"`
	} `json:"details"`
	Type        string `json:"type"`
	Storagetype string `json:"storageType"`
	ChangeInfo  struct {
		CreatedDate  string `json:"createdDate"`
		CreatedBy    string `json:"createdBy"`
		ModifiedDate string `json:"modifiedDate"`
		ModifiedBy   string `json:"modifiedBy"`
	} `json:"changeInfo"`
	Links []Link `json:"links"`
}

type ServerCreateResponse struct {
	Server   string `json:"server"`
	IsQueued bool   `json:"isQueued"`
	Links    []Link `json:"links"`
}

func (s *ServerCreateResponse) GetStatusId() (string, error) {
	for _, v := range s.Links {
		if v.Rel == "status" {
			return v.ID, nil
		}
	}
	return "", errors.New(fmt.Sprintf("No status ID found for server %s", s.Server))
}

type DatacenterResponse struct {
	ID    string `json:"id"`
	Name  string `json:"name"`
	Links []Link `json:"links"`
}

type Link struct {
	Rel   string   `json:"rel,omitempty"`
	Href  string   `json:"href,omitempty"`
	ID    string   `json:"id,omitempty"`
	Verbs []string `json:"verbs,omitempty"`
}
<commit_msg>refactored redundant call<commit_after>package clc

import (
	"fmt"
	"time"
)

type Server struct {
	Name           string `json:"name"`
	Description    string `json:"description"`
	GroupID        string `json:"groupId"`
	SourceServerID string `json:"sourceServerId"`
	IsManagedOS    bool   `json:"isManagedOS,omitempty"`
	PrimaryDNS     string `json:"primaryDns,omitempty"`
	SecondaryDNS   string `json:"secondaryDns,omitempty"`
	NetworkID      string `json:"networkId,omitempty"`
	IPaddress      string `json:"ipAddress,omitempty"`
	Password       string `json:"password,omitempty"`
	CPU            int    `json:"cpu"`
	MemoryGB       int    `json:"memoryGB"`
	Type           string `json:"type"`
	Storagetype    string `json:"storageType,omitempty"`
	Customfields   []struct {
		ID    string `json:"id"`
		Value string `json:"value"`
	} `json:"customFields,omitempty"`
	Additionaldisks []struct {
		Path   string `json:"path"`
		SizeGB int    `json:"sizeGB"`
		Type   string `json:"type"`
	} `json:"additionalDisks,omitempty"`
	TTL time.Time `json:"ttl,omitempty"`
}

func (s *Server) Valid() bool {
	return s.Name != "" && s.CPU != 0 && s.MemoryGB != 0 && s.GroupID != "" && s.SourceServerID != ""
}

type ServerResponse struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	GroupID     string `json:"groupId"`
	IsTemplate  bool   `json:"isTemplate"`
	LocationID  string `json:"locationId"`
	OStype      string `json:"osType"`
	Status      string `json:"status"`
	Details     struct {
		IPaddresses []struct {
			Internal string `json:"internal"`
		} `json:"ipAddresses"`
		AlertPolicies []struct {
			ID    string `json:"id"`
			Name  string `json:"name"`
			Links []Link `json:"links"`
		} `json:"alertPolicies"`
		CPU               int    `json:"cpu"`
		Diskcount         int    `json:"diskCount"`
		Hostname          string `json:"hostName"`
		InMaintenanceMode bool   `json:"inMaintenanceMode"`
		MemoryMB          int    `json:"memoryMB"`
		Powerstate        string `json:"powerState"`
		Storagegb         int    `json:"storageGB"`
		Disks             []struct {
			ID             string        `json:"id"`
			SizeGB         int           `json:"sizeGB"`
			PartitionPaths []interface{} `json:"partitionPaths"`
		} `json:"disks"`
		Partitions []struct {
			SizeGB float64 `json:"sizeGB"`
			Path   string  `json:"path"`
		} `json:"partitions"`
		Snapshots []struct {
			Name  string `json:"name"`
			Links []Link `json:"links"`
		} `json:"snapshots"`
		Customfields []struct {
			ID           string `json:"id"`
			Name         string `json:"name"`
			Value        string `json:"value"`
			Displayvalue string `json:"displayValue"`
		} `json:"customFields"`
	} `json:"details"`
	Type        string `json:"type"`
	Storagetype string `json:"storageType"`
	ChangeInfo  struct {
		CreatedDate  string `json:"createdDate"`
		CreatedBy    string `json:"createdBy"`
		ModifiedDate string `json:"modifiedDate"`
		ModifiedBy   string `json:"modifiedBy"`
	} `json:"changeInfo"`
	Links []Link `json:"links"`
}

type ServerCreateResponse struct {
	Server   string `json:"server"`
	IsQueued bool   `json:"isQueued"`
	Links    []Link `json:"links"`
}

func (s *ServerCreateResponse) GetStatusId() (string, error) {
	for _, v := range s.Links {
		if v.Rel == "status" {
			return v.ID, nil
		}
	}
	return "", fmt.Errorf("No status ID found for server %s", s.Server)
}

type DatacenterResponse struct {
	ID    string `json:"id"`
	Name  string `json:"name"`
	Links []Link `json:"links"`
}

type Link struct {
	Rel   string   `json:"rel,omitempty"`
	Href  string   `json:"href,omitempty"`
	ID    string   `json:"id,omitempty"`
	Verbs []string `json:"verbs,omitempty"`
}
<|endoftext|>


<commit_before>package cli_test

import (
	"github.com/qadium/plumber/cli"
	"io/ioutil"
	"reflect"
	"testing"
	"os"
)

// config with everything filled out
const goodConfig = `
language: python
name: foobar
inputs:
  - name: a
    description: none
    type: int
outputs:
  - name: b
    description: none
    type: int
env:
  - SECRET=1234
install:
  - make -j4
  - make install`

// config with all optional stuff left out
// includes an extra field which should just be ignored
const optConfig = `
language: python
name: foobar
inputs:
  - name: a
    type: string
outputs:
  - name: b
    type: string
extra: field`

// config with missing fields
const missingFields = `
language: go
name: foobar`

// config with missing name on inputs
const missingInputsAndOutputs = `
language: go
name: foobar
inputs:
  - type: foo
outputs:
  - foobar: a`

func writeConfig(t *testing.T, config string) string {
	configFile, err := ioutil.TempFile("", "plumberTest")
	if err != nil {
		t.Errorf("Had an issue creating the temp file to test, '%v'", err.Error())
	}

	if _, err := configFile.WriteString(config); err != nil {
		t.Errorf("Had an error writing the config file to test, '%v'", err.Error())
	}

	return configFile.Name()
}

// if the expectedContext is nil, then we'll check if an error is thrown
func parseConfig(t *testing.T, expectedContext *cli.Context, config string) {
	configFile := writeConfig(t, config)
	defer func() {
		if err := os.RemoveAll(configFile); err != nil {
			t.Errorf("Had an issue removing the temp file, '%v'", err.Error())
		}
	}()

	ctx, err := cli.ParseConfig(configFile)

	if expectedContext == nil {
		if err == nil {
			t.Errorf("Expected an error, but got '%v'", ctx)
		}
	} else {
		if err != nil {
			t.Errorf("Had an issue parsing the config file, '%v'", err.Error())
		}

		if !reflect.DeepEqual(ctx, expectedContext) {
			t.Errorf("Got '%v', expected '%v'", ctx, expectedContext)
		}
	}
}

func TestParseConfig(t *testing.T) {
	ctx := &cli.Context{
		Language: "python",
		Name: "foobar",
		Inputs: []cli.Field{
			cli.Field{
				Name: "a",
				Description: "none",
				Type: "int",
			},
		},
		Outputs: []cli.Field {
			cli.Field{
				Name: "b",
				Description: "none",
				Type: "int",
			},
		},
		Env: []string{"SECRET=1234"},
		Install: []string{"make -j4", "make install"},
	}
	parseConfig(t, ctx, goodConfig)
}

func TestParseOptConfig(t *testing.T) {
	ctx := &cli.Context {
		Language: "python",
		Name: "foobar",
		Inputs: []cli.Field{
			cli.Field{
				Name: "a",
				Description: "",
				Type: "string",
			},
		},
		Outputs: []cli.Field {
			cli.Field{
				Name: "b",
				Description: "",
				Type: "string",
			},
		},
		Env: nil,
		Install: nil,
	}
	parseConfig(t, ctx, optConfig)
}

func TestParseMissingFields(t *testing.T) {
	parseConfig(t, nil, missingFields)
}

func TestParseMissingInputsAndOutputs(t *testing.T) {
	parseConfig(t, nil, missingInputsAndOutputs)
}

func TestParseConfigFromDir(t *testing.T) {

}
<commit_msg>updated context test to get more coverage<commit_after>package cli_test

import (
	"github.com/qadium/plumber/cli"
	"io/ioutil"
	"reflect"
	"testing"
	"os"
	"fmt"
)

// config with everything filled out
const goodConfig = `
language: python
name: foobar
inputs:
  - name: a
    description: none
    type: int
outputs:
  - name: b
    description: none
    type: int
env:
  - SECRET=1234
install:
  - make -j4
  - make install`

// config with all optional stuff left out
// includes an extra field which should just be ignored
const optConfig = `
language: python
name: foobar
inputs:
  - name: a
    type: string
outputs:
  - name: b
    type: string
extra: field`

// config with missing fields
const missingFields = `
language: go
name: foobar`

// config with missing name on inputs
const missingInputName = `
language: go
name: foobar
inputs:
  - type: foo
outputs:
  - foobar: a`

const missingOutputName = `
langauge: go
name: foobar
outputs:
	- hello: 1
inputs:
	- name: a`

func writeConfig(t *testing.T, config string) string {
	configFile, err := ioutil.TempFile("", "plumberTest")
	if err != nil {
		t.Errorf("Had an issue creating the temp file to test, '%v'", err.Error())
	}

	if _, err := configFile.WriteString(config); err != nil {
		t.Errorf("Had an error writing the config file to test, '%v'", err.Error())
	}

	return configFile.Name()
}

// if the expectedContext is nil, then we'll check if an error is thrown
func parseConfig(t *testing.T, expectedContext *cli.Context, config string) {
	configFile := writeConfig(t, config)
	defer func() {
		if err := os.RemoveAll(configFile); err != nil {
			t.Errorf("Had an issue removing the temp file, '%v'", err.Error())
		}
	}()

	ctx, err := cli.ParseConfig(configFile)

	if expectedContext == nil {
		if err == nil {
			t.Errorf("Expected an error, but got '%v'", ctx)
		}
	} else {
		if err != nil {
			t.Errorf("Had an issue parsing the config file, '%v'", err.Error())
		}

		if !reflect.DeepEqual(ctx, expectedContext) {
			t.Errorf("Got '%v', expected '%v'", ctx, expectedContext)
		}
	}
}

func TestParseConfig(t *testing.T) {
	ctx := &cli.Context{
		Language: "python",
		Name: "foobar",
		Inputs: []cli.Field{
			cli.Field{
				Name: "a",
				Description: "none",
				Type: "int",
			},
		},
		Outputs: []cli.Field {
			cli.Field{
				Name: "b",
				Description: "none",
				Type: "int",
			},
		},
		Env: []string{"SECRET=1234"},
		Install: []string{"make -j4", "make install"},
	}
	parseConfig(t, ctx, goodConfig)
}

func TestParseOptConfig(t *testing.T) {
	ctx := &cli.Context {
		Language: "python",
		Name: "foobar",
		Inputs: []cli.Field{
			cli.Field{
				Name: "a",
				Description: "",
				Type: "string",
			},
		},
		Outputs: []cli.Field {
			cli.Field{
				Name: "b",
				Description: "",
				Type: "string",
			},
		},
		Env: nil,
		Install: nil,
	}
	parseConfig(t, ctx, optConfig)
}

func TestParseMissingFields(t *testing.T) {
	parseConfig(t, nil, missingFields)
}

func TestParseMissingInputName(t *testing.T) {
	parseConfig(t, nil, missingInputName)
}

func TestParseMissingOutputName(t *testing.T) {
	parseConfig(t, nil, missingOutputName)
}

func TestParseMissingLanguage(t *testing.T) {
	parseConfig(t, nil, `name: hello`)
}

func TestParseMissingName(t *testing.T) {
	parseConfig(t, nil, `language: python`)
}

func TestParseConfigFromDir(t *testing.T) {
	tempDir, err := ioutil.TempDir("", "plumberTest")
	if err != nil {
		t.Errorf("Could not make temp dir; got error '%v'", err.Error())
	}
	defer func() {
		if err := os.RemoveAll(tempDir); err != nil {
			t.Errorf("Had an issue removing the temp file, '%v'", err.Error())
		}
	}()

	filename := fmt.Sprintf("%s/.plumb.yml", tempDir)

	if err := ioutil.WriteFile(filename, []byte(goodConfig), 0644); err != nil {
		t.Errorf("Could not write .plumb.yml; got error '%v'", err.Error())
	}

	expected := &cli.Context{
		Language: "python",
		Name: "foobar",
		Inputs: []cli.Field{
			cli.Field{
				Name: "a",
				Description: "none",
				Type: "int",
			},
		},
		Outputs: []cli.Field {
			cli.Field{
				Name: "b",
				Description: "none",
				Type: "int",
			},
		},
		Env: []string{"SECRET=1234"},
		Install: []string{"make -j4", "make install"},
	}

	ctx, err := cli.ParseConfigFromDir(tempDir)
	if err != nil {
		t.Errorf("Should have parsed properly, got error '%v'", err.Error())
	}

	if !reflect.DeepEqual(ctx, expected) {
		t.Errorf("Got '%v', expected '%v'", ctx, expected)
	}
}
<|endoftext|>


<commit_before>package gdw

import (
	"fmt"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"

	"github.com/eapache/channels"
)

// Worker is used to define a goroutine pool whose results and/or execution are of interest, thus awaitable through WaitGroup.
type WorkerPool struct {
	jobQueue   *channels.InfiniteChannel
	limiter    *channels.ResizableChannel
	queueDepth int64
	wgMap      map[string]*sync.WaitGroup
}

// Random string utilities - START
// source - http://stackoverflow.com/a/31832326/1112789

var src = rand.NewSource(time.Now().UnixNano())

const (
	letterIdxBits = 6
	letterIdxMask = 1<<letterIdxBits - 1
	letterIdxMax  = 63 / letterIdxBits
	letterBytes   = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)

func generateToken() string {
	b := make([]byte, 20)
	for i, cache, remain := 20-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}
	return string(b)
}

// Random string utilities - END

func NewWorkerPool(size int) *WorkerPool {
	jobQueue := channels.NewInfiniteChannel()
	limiter := channels.NewResizableChannel()
	limiter.Resize(channels.BufferCap(size))
	worker := &WorkerPool{
		jobQueue:   jobQueue,
		limiter:    limiter,
		queueDepth: 0,
		wgMap:      make(map[string]*sync.WaitGroup),
	}
	worker.wgMap["gdw_main_pool"] = &sync.WaitGroup{}

	go func() {
		jobQueueOut := jobQueue.Out()
		limiterIn := limiter.In()
		limiterOut := limiter.Out()
		for jobs := range jobQueueOut {
			switch jt := jobs.(type) {

			case Job:
				limiterIn <- true
				atomic.AddInt64(&worker.queueDepth, -1)
				go func(j Job) {
					j.DoWork()
					<-limiterOut
					worker.wgMap["gdw_main_pool"].Done()
				}(jt)

			case []Job:
				for _, job := range jt {
					limiterIn <- true
					atomic.AddInt64(&worker.queueDepth, -1)
					go func(j Job) {
						j.DoWork()
						<-limiterOut
						worker.wgMap["gdw_main_pool"].Done()
					}(job)
				}

			case *batchedJob:
				limiterIn <- true
				atomic.AddInt64(&worker.queueDepth, -1)
				go func(bj *batchedJob) {
					bj.batched.DoWork()
					<-limiterOut
					worker.wgMap[bj.name].Done()
					worker.wgMap["gdw_main_pool"].Done()
				}(jt)

			case []*batchedJob:
				for _, job := range jt {
					limiterIn <- true
					atomic.AddInt64(&worker.queueDepth, -1)
					go func(bj *batchedJob) {
						bj.batched.DoWork()
						<-limiterOut
						worker.wgMap[bj.name].Done()
						worker.wgMap["gdw_main_pool"].Done()
					}(job)
				}

			}
		}
	}()

	return worker
}

func (w *WorkerPool) NewBatch(name string) (*Batch, error) {
	if _, ok := w.wgMap[name]; ok {
		return nil, fmt.Errorf("Batch named %s already exists.", name)
	}
	w.wgMap[name] = &sync.WaitGroup{}
	return &Batch{
		worker: w,
		name:   name,
	}, nil
}

func (w *WorkerPool) NewTempBatch() *Batch {
	token := generateToken()
	w.wgMap[token] = &sync.WaitGroup{}
	return &Batch{
		worker: w,
		name:   token,
	}
}

func (w *WorkerPool) LoadBatch(name string) (*Batch, error) {
	if _, ok := w.wgMap[name]; !ok {
		return nil, fmt.Errorf("No batch named %s exists.", name)
	}
	return &Batch{
		worker: w,
		name:   name,
	}, nil
}

func (w *WorkerPool) SetPoolSize(size int) {
	w.limiter.Resize(channels.BufferCap(size))
}

func (w *WorkerPool) GetPoolSize() int {
	return int(w.limiter.Cap())
}

func (w *WorkerPool) GetQueueDepth() int {
	return int(atomic.LoadInt64(&w.queueDepth))
}

func (w *WorkerPool) Add(job Job, amount int) {
	w.add(job, amount, "gdw_main_pool")
}

func (w *WorkerPool) add(job Job, amount int, batch string) {
	w.wgMap["gdw_main_pool"].Add(amount)
	atomic.AddInt64(&w.queueDepth, int64(amount))
	switch batch {

	case "gdw_main_pool":
		jobs := make([]Job, amount)
		for i := 0; i < amount; i++ {
			jobs[i] = job
		}
		w.jobQueue.In() <- jobs

	default:
		bjs := make([]*batchedJob, amount)
		for i := 0; i < amount; i++ {
			bj := &batchedJob{
				batched: job,
				name:    batch,
			}
			bjs[i] = bj
		}
		w.jobQueue.In() <- bjs

	}
}

func (w *WorkerPool) AddOne(job Job) {
	w.addOne(job, "gdw_main_pool")
}

func (w *WorkerPool) addOne(job Job, batch string) {
	w.wgMap["gdw_main_pool"].Add(1)
	atomic.AddInt64(&w.queueDepth, 1)
	switch batch {

	case "gdw_main_pool":
		w.jobQueue.In() <- job

	default:
		bj := &batchedJob{
			batched: job,
			name:    batch,
		}
		w.jobQueue.In() <- bj

	}
}

func (w *WorkerPool) Wait() {
	w.wgMap["gdw_main_pool"].Wait()
}

func (w *WorkerPool) WaitBatch(batch string) error {
	wg, ok := w.wgMap[batch]
	if !ok {
		return fmt.Errorf("No batch named %s exists.", batch)
	}
	wg.Wait()
	return nil
}

func (w *WorkerPool) CleanBatch(batch string) error {
	if _, ok := w.wgMap[batch]; !ok {
		return fmt.Errorf("No batch named %s exists.", batch)
	}
	delete(w.wgMap, batch)
	return nil
}

func (w *WorkerPool) Close() {
	w.jobQueue.Close()
	w.limiter.Close()
}
<commit_msg>lock map for sync writes<commit_after>package gdw

import (
	"fmt"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"

	"github.com/eapache/channels"
)

// Worker is used to define a goroutine pool whose results and/or execution are of interest, thus awaitable through WaitGroup.
type WorkerPool struct {
	jobQueue   *channels.InfiniteChannel
	limiter    *channels.ResizableChannel
	queueDepth int64
	wgMap      map[string]*sync.WaitGroup
	mapSync    sync.Mutex
}

// Random string utilities - START
// source - http://stackoverflow.com/a/31832326/1112789

var src = rand.NewSource(time.Now().UnixNano())

const (
	letterIdxBits = 6
	letterIdxMask = 1<<letterIdxBits - 1
	letterIdxMax  = 63 / letterIdxBits
	letterBytes   = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
)

func generateToken() string {
	b := make([]byte, 20)
	for i, cache, remain := 20-1, src.Int63(), letterIdxMax; i >= 0; {
		if remain == 0 {
			cache, remain = src.Int63(), letterIdxMax
		}
		if idx := int(cache & letterIdxMask); idx < len(letterBytes) {
			b[i] = letterBytes[idx]
			i--
		}
		cache >>= letterIdxBits
		remain--
	}
	return string(b)
}

// Random string utilities - END

func NewWorkerPool(size int) *WorkerPool {
	jobQueue := channels.NewInfiniteChannel()
	limiter := channels.NewResizableChannel()
	limiter.Resize(channels.BufferCap(size))
	worker := &WorkerPool{
		jobQueue:   jobQueue,
		limiter:    limiter,
		queueDepth: 0,
		wgMap:      make(map[string]*sync.WaitGroup),
	}
	worker.wgMap["gdw_main_pool"] = &sync.WaitGroup{}

	go func() {
		jobQueueOut := jobQueue.Out()
		limiterIn := limiter.In()
		limiterOut := limiter.Out()
		for jobs := range jobQueueOut {
			switch jt := jobs.(type) {

			case Job:
				limiterIn <- true
				atomic.AddInt64(&worker.queueDepth, -1)
				go func(j Job) {
					j.DoWork()
					<-limiterOut
					worker.wgMap["gdw_main_pool"].Done()
				}(jt)

			case []Job:
				for _, job := range jt {
					limiterIn <- true
					atomic.AddInt64(&worker.queueDepth, -1)
					go func(j Job) {
						j.DoWork()
						<-limiterOut
						worker.wgMap["gdw_main_pool"].Done()
					}(job)
				}

			case *batchedJob:
				limiterIn <- true
				atomic.AddInt64(&worker.queueDepth, -1)
				go func(bj *batchedJob) {
					bj.batched.DoWork()
					<-limiterOut
					worker.wgMap[bj.name].Done()
					worker.wgMap["gdw_main_pool"].Done()
				}(jt)

			case []*batchedJob:
				for _, job := range jt {
					limiterIn <- true
					atomic.AddInt64(&worker.queueDepth, -1)
					go func(bj *batchedJob) {
						bj.batched.DoWork()
						<-limiterOut
						worker.wgMap[bj.name].Done()
						worker.wgMap["gdw_main_pool"].Done()
					}(job)
				}

			}
		}
	}()

	return worker
}

func (w *WorkerPool) NewBatch(name string) (*Batch, error) {
	if _, ok := w.wgMap[name]; ok {
		return nil, fmt.Errorf("Batch named %s already exists.", name)
	}
	w.mapSync.Lock()
	w.wgMap[name] = &sync.WaitGroup{}
	w.mapSync.Unlock()
	return &Batch{
		worker: w,
		name:   name,
	}, nil
}

func (w *WorkerPool) NewTempBatch() *Batch {
	token := generateToken()
	w.mapSync.Lock()
	w.wgMap[token] = &sync.WaitGroup{}
	w.mapSync.Unlock()
	return &Batch{
		worker: w,
		name:   token,
	}
}

func (w *WorkerPool) LoadBatch(name string) (*Batch, error) {
	if _, ok := w.wgMap[name]; !ok {
		return nil, fmt.Errorf("No batch named %s exists.", name)
	}
	return &Batch{
		worker: w,
		name:   name,
	}, nil
}

func (w *WorkerPool) SetPoolSize(size int) {
	w.limiter.Resize(channels.BufferCap(size))
}

func (w *WorkerPool) GetPoolSize() int {
	return int(w.limiter.Cap())
}

func (w *WorkerPool) GetQueueDepth() int {
	return int(atomic.LoadInt64(&w.queueDepth))
}

func (w *WorkerPool) Add(job Job, amount int) {
	w.add(job, amount, "gdw_main_pool")
}

func (w *WorkerPool) add(job Job, amount int, batch string) {
	w.wgMap["gdw_main_pool"].Add(amount)
	atomic.AddInt64(&w.queueDepth, int64(amount))
	switch batch {

	case "gdw_main_pool":
		jobs := make([]Job, amount)
		for i := 0; i < amount; i++ {
			jobs[i] = job
		}
		w.jobQueue.In() <- jobs

	default:
		bjs := make([]*batchedJob, amount)
		for i := 0; i < amount; i++ {
			bj := &batchedJob{
				batched: job,
				name:    batch,
			}
			bjs[i] = bj
		}
		w.jobQueue.In() <- bjs

	}
}

func (w *WorkerPool) AddOne(job Job) {
	w.addOne(job, "gdw_main_pool")
}

func (w *WorkerPool) addOne(job Job, batch string) {
	w.wgMap["gdw_main_pool"].Add(1)
	atomic.AddInt64(&w.queueDepth, 1)
	switch batch {

	case "gdw_main_pool":
		w.jobQueue.In() <- job

	default:
		bj := &batchedJob{
			batched: job,
			name:    batch,
		}
		w.jobQueue.In() <- bj

	}
}

func (w *WorkerPool) Wait() {
	w.wgMap["gdw_main_pool"].Wait()
}

func (w *WorkerPool) WaitBatch(batch string) error {
	wg, ok := w.wgMap[batch]
	if !ok {
		return fmt.Errorf("No batch named %s exists.", batch)
	}
	wg.Wait()
	return nil
}

func (w *WorkerPool) CleanBatch(batch string) error {
	if _, ok := w.wgMap[batch]; !ok {
		return fmt.Errorf("No batch named %s exists.", batch)
	}
	w.mapSync.Lock()
	delete(w.wgMap, batch)
	w.mapSync.Unlock()
	return nil
}

func (w *WorkerPool) Close() {
	w.jobQueue.Close()
	w.limiter.Close()
}
<|endoftext|>


<commit_before>package ircstats

import (
	"encoding/json"
	"fmt"
	"sort"
	"strconv"
	"time"
)

//
// This view data struct will contain all the data that will be injected into the view template. Ideally this will be
// done as a JSON export so that JavaScript within the view can transform it in any way it sees fit.
//
type ViewData struct {
	PageTitle       string   // Page title from configuration
	PageDescription string   // Page description from configuration
	JsonData        JsonData // Json data for exporting to page
}

// Data mapping for front end JSON
type UserData struct {
	Username           string
	Url                string
	Avatar             string
	FirstSpoke         int64
	LastSpoke          int64
	TotalWords         int64            // Count of words
	Averages           Averages         // Used for words/day
	Vocabulary         int64            // Number of different words used
	Words              map[string]int64 // World cloud
	DaysActiveInPeriod int64
	TotalWordsInPeriod int64
	WordsByDayInPeriod float64
	ActivityPercentage float64          // Overall % contribution to Channel.WordCount
}

// Data mapping for passing timezone data to front end JSON
type TimeZone struct {
	Name   string
	Offset int
}

// Data mapping for passing date data to front end JSON
type GraphDay struct {
	Date  string
	Value int64
}

func (tz TimeZone) Format() string {
	var output string

	if tz.Offset > 0 {
		output = "GMT +" + strconv.Itoa(tz.Offset)
	} else {
		output = "GMT -" + strconv.Itoa(tz.Offset)
	}
	return output
}

type JsonData struct {
					      // Configurable options
	HeatMapInterval   uint                // HeatMap Interval from configuration
	ActivityPeriod    uint                // Activity Period from configuration

					      // Dates
	GeneratedAt       int64               // Timestamp of last generated at
	FirstSeen         int64               // Timestamp of first message
	LastSeen          int64               // Timestamp of last message
	TotalDaysSeen     int64               // Number of days between FirstSeen and LastSeen
	TimeZone          TimeZone

					      // Averages
	Averages          Averages            // Calculated Averages

					      // Counters
	MaxDay            MaxDay              // Calculated Max Day
	MaxHour           MaxHour             // Calculated Max Hour
	MaxWeek           MaxWeek             // Calculated Max Week
	TotalLines        int64               // Lines parsed in total
	TotalWords        int64               // Total Words (all words multiplied by times used)
	TotalUsers        int64               // Number of unique users
	TotalActiveUsers  int64               // Number of active users within activity period (default 30 days)

					      // Graph Data
	Days              []GraphDay

					      // Misc
	Users             map[string]UserData // Users list
	SortedActiveUsers []string            // Sorted Users map by "activity"
	SortedTopUsers    []string            // Sorted Users map by words
}

type sortedMap struct {
	m map[string]int
	s []string
}

func (sm *sortedMap) Len() int {
	return len(sm.m)
}

func (sm *sortedMap) Less(i, j int) bool {
	return sm.m[sm.s[i]] > sm.m[sm.s[j]]
}

func (sm *sortedMap) Swap(i, j int) {
	sm.s[i], sm.s[j] = sm.s[j], sm.s[i]
}

func sortedKeys(m map[string]int) []string {
	sm := new(sortedMap)
	sm.m = m
	sm.s = make([]string, len(m))
	i := 0
	for key, _ := range m {
		sm.s[i] = key
		i++
	}
	sort.Sort(sm)
	return sm.s
}

func NewViewData(c Config) *ViewData {
	j := JsonData{
		HeatMapInterval: c.HeatMapInterval,
		ActivityPeriod:  c.ActivityPeriod,
		GeneratedAt:     time.Now().Unix(),
	}

	// Set timezone data for frontend
	j.TimeZone.Name, j.TimeZone.Offset = time.Now().Zone()
	j.TimeZone.Offset = (j.TimeZone.Offset / 60) / 60 // We want the zone offset in hours

	return &ViewData{
		PageTitle:       c.PageTitle,
		PageDescription: c.PageDescription,
		JsonData:        j,
	}
}

func (j JsonData) Debug() {
	fmt.Println("==================================================")
	fmt.Println("Json Data Debug:")
	fmt.Println("= [ Dates ] ======================================")
	fmt.Printf("First line date\t\t\t\t\t%d\n", j.FirstSeen)
	fmt.Printf("Last line date\t\t\t\t\t%d\n", j.LastSeen)
	fmt.Printf("Total Days Seen:\t\t\t\t%d\n", j.TotalDaysSeen)
	fmt.Println("= [ Averages ] ===================================")
	fmt.Printf("Mean Lines/Hr:\t\t\t\t\t%f\n", j.Averages.Hour)
	fmt.Printf("Mean Lines/Day:\t\t\t\t\t%f\n", j.Averages.Day)
	fmt.Printf("Mean Lines/Week:\t\t\t\t%f\n", j.Averages.Week)
	fmt.Printf("Mean Lines/Week Day:\t\t\t%f\n", j.Averages.WeekDay)
	fmt.Println("= [ Counters ] ===================================")
	fmt.Printf("Total Lines Parsed:\t\t\t\t%d\n", j.TotalLines)
	fmt.Printf("Total Unique Users:\t\t\t\t%d\n", j.TotalUsers)
	fmt.Printf("Users Active in past %d days:\t%d\n", j.ActivityPeriod, j.TotalActiveUsers)

	fmt.Printf("Peak Day Date:\t\t\t\t\t%s\n", j.MaxDay.Day)
	fmt.Printf("Peak Day Lines:\t\t\t\t\t%d\n", j.MaxDay.Lines)

	fmt.Printf("Peak Hour:\t\t\t\t\t\t%d\n", j.MaxHour.Hour)
	fmt.Printf("Peak Hour Lines:\t\t\t\t%d\n", j.MaxHour.Lines)

	fmt.Printf("Peak Week:\t\t\t\t\t\t%d\n", j.MaxWeek.Week)
	fmt.Printf("Peak Week Lines:\t\t\t\t%d\n", j.MaxWeek.Lines)

	fmt.Println("==================================================")
}

// Calculate stats for View
func (vd *ViewData) Calculate(db Database) {
	// Dates
	vd.JsonData.FirstSeen = db.Channel.FirstSeen
	vd.JsonData.LastSeen = db.Channel.LastSeen
	vd.JsonData.TotalDaysSeen = db.Channel.TotalDaysSeen()

	// Calculate Counters
	vd.JsonData.TotalUsers = db.CountUsers()
	vd.JsonData.MaxDay.Day, vd.JsonData.MaxDay.Lines = db.Channel.FindPeakDay()
	vd.JsonData.MaxHour.Hour, vd.JsonData.MaxHour.Lines = db.Channel.FindPeakHour()
	vd.JsonData.MaxWeek.Week, vd.JsonData.MaxWeek.Lines = db.Channel.FindPeakWeek()
	vd.JsonData.TotalLines = db.Channel.LineCount
	vd.JsonData.TotalWords = db.Channel.WordCount

	// Calculate Averages
	vd.JsonData.Averages.Hour = db.Channel.FindHourAverage()
	vd.JsonData.Averages.Week = db.Channel.FindWeekAverage()
	vd.JsonData.Averages.Day = db.Channel.FindDayAverage()

	// Calculate Users
	vd.calculateUsers(db)

	// @todo Format data for Graph Usage
	// loop one day each between vd.JsonData.FirstSeen and vd.JsonData.LastSeen
	//vd.JsonData.Days = append(vd.JsonData.Days, GraphDay{Date: dt, Value: 0})

}

func (vd *ViewData) calculateUsers(db Database) {
	var (
		timePeriod  map[string]bool
		users       map[string]UserData
		activeUsers map[string]int
		topUsers    map[string]int

		userWordCount int64
		userDaysActive int64
	)

	timePeriod = make(map[string]bool)
	activeUsers = make(map[string]int)
	topUsers = make(map[string]int)
	users = make(map[string]UserData)

	for i := 1; i < int(vd.JsonData.ActivityPeriod); i++ {
		timePeriod[time.Now().AddDate(0, 0, -i).Format("2006-02-01")] = true
	}

	for nick, u := range db.Users {
		userWordCount = 0
		userDaysActive = 0

		for timePeriodDate := range timePeriod {
			if _, ok := u.Days[timePeriodDate]; ok {
				userDaysActive++
				userWordCount += u.Days[timePeriodDate]
			}
		}

		viewUserData := UserData{
			Username:           u.Username,
			Url:                u.Url,
			Avatar:             u.Avatar,
			FirstSpoke:         u.FirstSeen,
			LastSpoke:          u.LastSeen,
			TotalWords:         u.WordCount,
			Vocabulary:         int64(len(u.Words)),
			Words:              u.Words,
			DaysActiveInPeriod: userDaysActive,
			TotalWordsInPeriod: userWordCount,
			ActivityPercentage: (float64(u.WordCount) / float64(db.Channel.WordCount)) * 100,
		}

		viewUserData.Averages.Hour = u.FindHourAverage()
		viewUserData.Averages.Week = u.FindWeekAverage()
		viewUserData.Averages.Day = u.FindDayAverage()

		if userDaysActive > 0 {
			viewUserData.WordsByDayInPeriod = float64(userWordCount) / float64(userDaysActive)
			activeUsers[nick] = int(userDaysActive)
		}
		topUsers[nick] = int(viewUserData.TotalWords)
		users[nick] = viewUserData
	}

	vd.JsonData.Users = users
	vd.JsonData.SortedTopUsers = sortedKeys(topUsers)
	vd.JsonData.SortedActiveUsers = sortedKeys(activeUsers)
	vd.JsonData.TotalActiveUsers = int64(len(vd.JsonData.SortedActiveUsers))
}

// Returns a json string of the JsonData, good for debugging.
func (vd ViewData) GetJsonString() (j []byte, err error) {
	j, err = json.Marshal(vd.JsonData)
	return
}
<commit_msg>Removed Words from being passed to JSON. It's almost 1MB of data thats useless to the front end<commit_after>package ircstats

import (
	"encoding/json"
	"fmt"
	"sort"
	"strconv"
	"time"
)

//
// This view data struct will contain all the data that will be injected into the view template. Ideally this will be
// done as a JSON export so that JavaScript within the view can transform it in any way it sees fit.
//
type ViewData struct {
	PageTitle       string   // Page title from configuration
	PageDescription string   // Page description from configuration
	JsonData        JsonData // Json data for exporting to page
}

// Data mapping for front end JSON
type UserData struct {
	Username           string
	Url                string
	Avatar             string
	FirstSpoke         int64
	LastSpoke          int64
	TotalWords         int64            // Count of words
	Averages           Averages         // Used for words/day
	Vocabulary         int64            // Number of different words used
	DaysActiveInPeriod int64
	TotalWordsInPeriod int64
	WordsByDayInPeriod float64
	ActivityPercentage float64          // Overall % contribution to Channel.WordCount
}

// Data mapping for passing timezone data to front end JSON
type TimeZone struct {
	Name   string
	Offset int
}

// Data mapping for passing week date data to front end JSON
// This is used for the by week bar graph
type SvgGraphWeek struct {
	First string // Week beginning date e.g June 12
	Last  string // Week ending date e.g June 18
	Value int64  // e.g. Lines
}

// Data mapping for passing day date data to front end JSON
// This is used for the heatmap
type SvgGraphDay struct {
	Date  string
	Value int64 // e.g. Lines
}

//type GraphData struct {
//	Days  []SvgGraphDay
//Weeks []SvgGraphWeek
//}

func (tz TimeZone) Format() string {
	var output string

	if tz.Offset > 0 {
		output = "GMT +" + strconv.Itoa(tz.Offset)
	} else {
		output = "GMT -" + strconv.Itoa(tz.Offset)
	}
	return output
}

type JsonData struct {
					      // Configurable options
	HeatMapInterval   uint                // HeatMap Interval from configuration
	ActivityPeriod    uint                // Activity Period from configuration

					      // Dates
	GeneratedAt       int64               // Timestamp of last generated at
	FirstSeen         int64               // Timestamp of first message
	LastSeen          int64               // Timestamp of last message
	TotalDaysSeen     int64               // Number of days between FirstSeen and LastSeen
	TimeZone          TimeZone

					      // Averages
	Averages          Averages            // Calculated Averages

					      // Counters
	MaxDay            MaxDay              // Calculated Max Day
	MaxHour           MaxHour             // Calculated Max Hour
	MaxWeek           MaxWeek             // Calculated Max Week
	TotalLines        int64               // Lines parsed in total
	TotalWords        int64               // Total Words (all words multiplied by times used)
	TotalUsers        int64               // Number of unique users
	TotalActiveUsers  int64               // Number of active users within activity period (default 30 days)

					      // Graph Data
	Days              []SvgGraphDay

					      // Misc
	Users             map[string]UserData // Users list
	SortedActiveUsers []string            // Sorted Users map by "activity"
	SortedTopUsers    []string            // Sorted Users map by words
}

type sortedMap struct {
	m map[string]int
	s []string
}

func (sm *sortedMap) Len() int {
	return len(sm.m)
}

func (sm *sortedMap) Less(i, j int) bool {
	return sm.m[sm.s[i]] > sm.m[sm.s[j]]
}

func (sm *sortedMap) Swap(i, j int) {
	sm.s[i], sm.s[j] = sm.s[j], sm.s[i]
}

func sortedKeys(m map[string]int) []string {
	sm := new(sortedMap)
	sm.m = m
	sm.s = make([]string, len(m))
	i := 0
	for key, _ := range m {
		sm.s[i] = key
		i++
	}
	sort.Sort(sm)
	return sm.s
}

func NewViewData(c Config) *ViewData {
	j := JsonData{
		HeatMapInterval: c.HeatMapInterval,
		ActivityPeriod:  c.ActivityPeriod,
		GeneratedAt:     time.Now().Unix(),
	}

	// Set timezone data for frontend
	j.TimeZone.Name, j.TimeZone.Offset = time.Now().Zone()
	j.TimeZone.Offset = (j.TimeZone.Offset / 60) / 60 // We want the zone offset in hours

	return &ViewData{
		PageTitle:       c.PageTitle,
		PageDescription: c.PageDescription,
		JsonData:        j,
	}
}

func (j JsonData) Debug() {
	fmt.Println("==================================================")
	fmt.Println("Json Data Debug:")
	fmt.Println("= [ Dates ] ======================================")
	fmt.Printf("First line date\t\t\t\t\t%d\n", j.FirstSeen)
	fmt.Printf("Last line date\t\t\t\t\t%d\n", j.LastSeen)
	fmt.Printf("Total Days Seen:\t\t\t\t%d\n", j.TotalDaysSeen)
	fmt.Println("= [ Averages ] ===================================")
	fmt.Printf("Mean Lines/Hr:\t\t\t\t\t%f\n", j.Averages.Hour)
	fmt.Printf("Mean Lines/Day:\t\t\t\t\t%f\n", j.Averages.Day)
	fmt.Printf("Mean Lines/Week:\t\t\t\t%f\n", j.Averages.Week)
	fmt.Printf("Mean Lines/Week Day:\t\t\t%f\n", j.Averages.WeekDay)
	fmt.Println("= [ Counters ] ===================================")
	fmt.Printf("Total Lines Parsed:\t\t\t\t%d\n", j.TotalLines)
	fmt.Printf("Total Unique Users:\t\t\t\t%d\n", j.TotalUsers)
	fmt.Printf("Users Active in past %d days:\t%d\n", j.ActivityPeriod, j.TotalActiveUsers)

	fmt.Printf("Peak Day Date:\t\t\t\t\t%s\n", j.MaxDay.Day)
	fmt.Printf("Peak Day Lines:\t\t\t\t\t%d\n", j.MaxDay.Lines)

	fmt.Printf("Peak Hour:\t\t\t\t\t\t%d\n", j.MaxHour.Hour)
	fmt.Printf("Peak Hour Lines:\t\t\t\t%d\n", j.MaxHour.Lines)

	fmt.Printf("Peak Week:\t\t\t\t\t\t%d\n", j.MaxWeek.Week)
	fmt.Printf("Peak Week Lines:\t\t\t\t%d\n", j.MaxWeek.Lines)

	fmt.Println("==================================================")
}

// Calculate stats for View
func (vd *ViewData) Calculate(db Database) {
	// Dates
	vd.JsonData.FirstSeen = db.Channel.FirstSeen
	vd.JsonData.LastSeen = db.Channel.LastSeen
	vd.JsonData.TotalDaysSeen = db.Channel.TotalDaysSeen()

	// Calculate Counters
	vd.JsonData.TotalUsers = db.CountUsers()
	vd.JsonData.MaxDay.Day, vd.JsonData.MaxDay.Lines = db.Channel.FindPeakDay()
	vd.JsonData.MaxHour.Hour, vd.JsonData.MaxHour.Lines = db.Channel.FindPeakHour()
	vd.JsonData.MaxWeek.Week, vd.JsonData.MaxWeek.Lines = db.Channel.FindPeakWeek()
	vd.JsonData.TotalLines = db.Channel.LineCount
	vd.JsonData.TotalWords = db.Channel.WordCount

	// Calculate Averages
	vd.JsonData.Averages.Hour = db.Channel.FindHourAverage()
	vd.JsonData.Averages.Week = db.Channel.FindWeekAverage()
	vd.JsonData.Averages.Day = db.Channel.FindDayAverage()

	// Calculate Users
	vd.calculateUsers(db)

	// @todo Format data for Graph Usage
	// loop one day each between vd.JsonData.FirstSeen and vd.JsonData.LastSeen

	// Calculate Date Data for graphs
	vd.calculateDateData(db);

	//vd.JsonData.Days = append(vd.JsonData.Days, GraphDay{Date: dt, Value: 0})

}

func (vd *ViewData) calculateDateData(db Database) {
	var (
		timeNow time.Time
		elementTime time.Time
		date string
		lines int64
		i int64
		days []SvgGraphDay
	)

	timeNow = time.Now()
	days = make([]SvgGraphDay, (vd.JsonData.TotalDaysSeen));

	for i = 0; i < vd.JsonData.TotalDaysSeen; i++ {
		elementTime = timeNow.AddDate(0, 0, int(-(vd.JsonData.TotalDaysSeen - i)))
		date = elementTime.Format("2006-02-01");

		if db.Channel.HasDay(date) {
			lines = db.Channel.Days[date]
		} else {
			lines = 0
		}

		days[i] = SvgGraphDay{Date: date, Value: lines }
	}

	vd.JsonData.Days = days;
}

func (vd *ViewData) calculateUsers(db Database) {
	var (
		timePeriod  map[string]bool
		users       map[string]UserData
		activeUsers map[string]int
		topUsers    map[string]int

		userWordCount int64
		userDaysActive int64
	)

	timePeriod = make(map[string]bool)
	activeUsers = make(map[string]int)
	topUsers = make(map[string]int)
	users = make(map[string]UserData)

	for i := 1; i < int(vd.JsonData.ActivityPeriod); i++ {
		timePeriod[time.Now().AddDate(0, 0, -i).Format("2006-02-01")] = true
	}

	for nick, u := range db.Users {
		userWordCount = 0
		userDaysActive = 0

		for timePeriodDate := range timePeriod {
			if _, ok := u.Days[timePeriodDate]; ok {
				userDaysActive++
				userWordCount += u.Days[timePeriodDate]
			}
		}

		viewUserData := UserData{
			Username:           u.Username,
			Url:                u.Url,
			Avatar:             u.Avatar,
			FirstSpoke:         u.FirstSeen,
			LastSpoke:          u.LastSeen,
			TotalWords:         u.WordCount,
			Vocabulary:         int64(len(u.Words)),
			DaysActiveInPeriod: userDaysActive,
			TotalWordsInPeriod: userWordCount,
			ActivityPercentage: (float64(u.WordCount) / float64(db.Channel.WordCount)) * 100,
		}

		viewUserData.Averages.Hour = u.FindHourAverage()
		viewUserData.Averages.Week = u.FindWeekAverage()
		viewUserData.Averages.Day = u.FindDayAverage()

		if userDaysActive > 0 {
			viewUserData.WordsByDayInPeriod = float64(userWordCount) / float64(userDaysActive)
			activeUsers[nick] = int(userDaysActive)
		}
		topUsers[nick] = int(viewUserData.TotalWords)
		users[nick] = viewUserData
	}

	vd.JsonData.Users = users
	vd.JsonData.SortedTopUsers = sortedKeys(topUsers)
	vd.JsonData.SortedActiveUsers = sortedKeys(activeUsers)
	vd.JsonData.TotalActiveUsers = int64(len(vd.JsonData.SortedActiveUsers))
}

// Returns a json string of the JsonData, good for debugging.
func (vd ViewData) GetJsonString() (j []byte, err error) {
	j, err = json.Marshal(vd.JsonData)
	return
}
<|endoftext|>


<commit_before>package wright

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/programminh/wright/qpx"
)

var endpoint = fmt.Sprintf("https://www.googleapis.com/qpxExpress/v1/trips/search?key=%", APIKey)

type Trip struct {
	AdultCount  int
	Origin      string
	Destination string
	Date        time.Time
}

func (t Trip) MarshalJSON() (b []byte, err error) {
	req := qpx.Request{}
	slice := qpx.Slice{
		Kind:                  "qpxexpress#sliceInput",
		Origin:                t.Origin,
		Destination:           t.Destination,
		Date:                  t.Date.Format("2006-01-02"),
		MaxStop:               3,
		MaxConnectionDuration: 300,
	}

	pass := qpx.Passengers{
		Kind:       "qpxexpress#passengerCounts",
		AdultCount: t.AdultCount,
	}

	req.Request.Solutions = 10
	req.Request.Slice = []qpx.Slice{slice}
	req.Request.Passengers = pass

	b, err = json.Marshal(req)

	return
}

func Search(t *Trip) (err error) {
	var (
		b   []byte
		res *http.Response
	)

	if b, err = json.Marshal(t); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	prettyprint(t)

	if res, err = http.Post(endpoint, "application/json", bytes.NewBuffer(b)); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	io.Copy(os.Stdout, res.Body)
	res.Body.Close()

	return
}

func prettyprint(v interface{}) {
	b, _ := json.MarshalIndent(v, "", "    ")
	fmt.Println(string(b))
}
<commit_msg>Fix typo in endpoint<commit_after>package wright

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"time"

	"github.com/programminh/wright/qpx"
)

var endpoint = fmt.Sprintf("https://www.googleapis.com/qpxExpress/v1/trips/search?key=%s", APIKey)

type Trip struct {
	AdultCount  int
	Origin      string
	Destination string
	Date        time.Time
}

func (t Trip) MarshalJSON() (b []byte, err error) {
	req := qpx.Request{}
	slice := qpx.Slice{
		Kind:                  "qpxexpress#sliceInput",
		Origin:                t.Origin,
		Destination:           t.Destination,
		Date:                  t.Date.Format("2006-01-02"),
		MaxStop:               3,
		MaxConnectionDuration: 300,
	}

	pass := qpx.Passengers{
		Kind:       "qpxexpress#passengerCounts",
		AdultCount: t.AdultCount,
	}

	req.Request.Solutions = 10
	req.Request.Slice = []qpx.Slice{slice}
	req.Request.Passengers = pass

	b, err = json.Marshal(req)

	return
}

func Search(t *Trip) (err error) {
	var (
		b   []byte
		res *http.Response
	)

	if b, err = json.Marshal(t); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	prettyprint(t)

	fmt.Println(endpoint)

	if res, err = http.Post(endpoint, "application/json", bytes.NewBuffer(b)); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	io.Copy(os.Stdout, res.Body)
	res.Body.Close()

	return
}

func prettyprint(v interface{}) {
	b, _ := json.MarshalIndent(v, "", "    ")
	fmt.Println(string(b))
}
<|endoftext|>


<commit_before>// Copyright 2014 Myung Sun Kim. All rights reserved.
// Use of this source code is governed by a MIT License
// that can be found in the LICENSE file
package mapcsv

import (
	"encoding/csv"
	"sort"
)

type Writer struct {
	*csv.Writer
	Fields []string
	Line   int
}

func NewWriter(w *csv.Writer) *Writer {
	return &Writer{w, []string{}, 0}
}

func (w *Writer) Write(record map[string]string) (err error) {
	numKeys := len(record)
	var keys []string
	for key, _ := range record {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	numFields := len(w.Fields)
	arrRecords := make([]string, numKeys)
	// Recording fields at the very first line
	if w.Line == 0 && numFields != 0 {
		w.Line++
		return w.Writer.Write(w.Fields)
	}

	// There is no Designated field, so convert key value as integer and fill out out array
	if numFields == 0 {
		for i, val := range keys {
			arrRecords[i] = record[val]
		}
	} else {
		for i, val := range w.Fields {
			arrRecords[i] = record[val]
		}
	}
	w.Line++
	return w.Writer.Write(arrRecords)
}

func (w *Writer) Error() error {
	return w.Writer.Write(nil)
}

func (w *Writer) Flush() {
	w.Writer.Flush()
}

func (w *Writer) WriteAll(records []map[string]string) (err error) {
	for _, record := range records {
		err = w.Write(record)
		if err != nil {
			return err
		}
	}
	w.Flush()
	return nil
}
<commit_msg>Writer now has comments<commit_after>// Copyright 2014 Myung Sun Kim. All rights reserved.
// Use of this source code is governed by a MIT License
// that can be found in the LICENSE file
package mapcsv

import (
	"encoding/csv"
	"sort"
)

// A Writer writes records to a CSV encoded file.
//
// A Writer operates exactly same as encoding/csv/Writer except that it takes
// map[string]string instead of []string
//
// It also takes Fields name as []string if one wants to write fields name
// at the top of the stream
type Writer struct {
	*csv.Writer
	Fields []string
	Line   int
}

// NewWriter returns a new Writer that writes to csv.Writer
func NewWriter(w *csv.Writer) *Writer {
	return &Writer{w, []string{}, 0}
}

// Writer writes a single CSV record to csv.Writer by converting given map
// into []string
func (w *Writer) Write(record map[string]string) (err error) {
	numKeys := len(record)
	var keys []string
	for key, _ := range record {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	numFields := len(w.Fields)
	arrRecords := make([]string, numKeys)
	// Recording fields at the very first line
	if w.Line == 0 && numFields != 0 {
		w.Line++
		return w.Writer.Write(w.Fields)
	}

	// There is no Designated field, so convert key value as integer and fill out out array
	if numFields == 0 {
		for i, val := range keys {
			arrRecords[i] = record[val]
		}
	} else {
		for i, val := range w.Fields {
			arrRecords[i] = record[val]
		}
	}
	w.Line++
	return w.Writer.Write(arrRecords)
}

// Error reports any error that has occurred during a previous Write or Flush.
func (w *Writer) Error() error {
	return w.Writer.Write(nil)
}

// Flush writes any buffered data to the underlying io.Writer.
// To check if an error occured during the Flush, call Error.
func (w *Writer) Flush() {
	w.Writer.Flush()
}

// WriteAll writes multiple CSV reports to csv.Writer an then calls Flush.
func (w *Writer) WriteAll(records []map[string]string) (err error) {
	for _, record := range records {
		err = w.Write(record)
		if err != nil {
			return err
		}
	}
	w.Flush()
	return w.Error()
}
<|endoftext|>


<commit_before>// Package: fileLogger
// File: writer.go
// Created by: mint(mint.zhao.chiu@gmail.com)_aiwuTech
// Useage:
// DATE: 14-8-24 12:40
package fileLogger

import (
	"log"
	"fmt"
	"time"
	"runtime"
)

const (
	DEFAULT_PRINT_INTERVAL = 300
)

// Receive logStr from f's logChan and print logstr to file
func (f *FileLogger) logWriter() {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("FileLogger's LogWritter() catch panic: %v\n", err)
		}
	}()

	//TODO let printInterVal can be configure, without change sourceCode
	printInterval := DEFAULT_PRINT_INTERVAL

	seqTimer := time.NewTicker(time.Duration(printInterval) * time.Second)
	for {
		select {
		case str := <- f.logChan:

			f.p(str)
		case <- seqTimer.C:
			log.Printf("================ LOG SEQ SIZE:%v ==================\n", len(f.logChan))
		}
	}
}

// print log
func (f *FileLogger) p(str string) {
	f.mu.RLock()
	defer f.mu.RUnlock()

	f.lg.Output(2, str)
	f.pc(str)
}

// print log in console, default log string wont be print in console
// NOTICE: when console is on, the process will really slowly
func (f *FileLogger) pc(str string) {
	if f.logConsole {
		log.Println(str)
	}
}

// Printf throw logstr to channel to print to the logger.
// Arguments are handled in the manner of fmt.Printf.
func (f *FileLogger) Printf(format string, v ...interface {}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=2
	f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf(format, v...)
}

// Print throw logstr to channel to print to the logger.
// Arguments are handled in the manner of fmt.Print.
func (f *FileLogger) Print(v ...interface {}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=2
	f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprint(v...)
}

// Println throw logstr to channel to print to the logger.
// Arguments are handled in the manner of fmt.Println.
func (f *FileLogger) Println(v ...interface {}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=2
	f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintln(v...)
}

//======================================================================================================================
// Trace log
func (f *FileLogger) Trace(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(2) //calldepth=3
	if f.logLevel <= TRACE {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("\033[32m[TRACE] "+format+" \033[0m ", v...)
	}
}

// same with Trace()
func (f *FileLogger) T(format string, v ...interface{}) {
	f.Trace(format, v...)
}

// info log
func (f *FileLogger) Info(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(2) //calldepth=3
	if f.logLevel <= INFO {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("\033[1;35m[INFO] "+format, v...)
	}
}

// same with Info()
func (f *FileLogger) I(format string, v ...interface{}) {
	f.Info(format, v...)
}

// warning log
func (f *FileLogger) Warn(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(2) //calldepth=3
	if f.logLevel <= WARN {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("\033[1;33m[WARN] "+format+" \033[0m ", v...)
	}
}

// same with Warn()
func (f *FileLogger)W(format string, v ...interface{}) {
	f.Warn(format, v...)
}

// error log
func (f *FileLogger) Error(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(2) //calldepth=3
	if f.logLevel <= ERROR {
		f.logChan <- fmt.Sprintf("%v:%v]", shortFileName(file), line) + fmt.Sprintf("\033[1;4;31m[ERROR] "+format+" \033[0m ", v...)
	}
}

// same with Error()
func (f *FileLogger) E(format string, v ...interface{}) {
	f.Error(format, v...)
}
<commit_msg>add console log prefix<commit_after>// Package: fileLogger
// File: writer.go
// Created by: mint(mint.zhao.chiu@gmail.com)_aiwuTech
// Useage:
// DATE: 14-8-24 12:40
package fileLogger

import (
	"log"
	"fmt"
	"time"
	"runtime"
)

const (
	DEFAULT_PRINT_INTERVAL = 300
)

// Receive logStr from f's logChan and print logstr to file
func (f *FileLogger) logWriter() {
	defer func() {
		if err := recover(); err != nil {
			log.Printf("FileLogger's LogWritter() catch panic: %v\n", err)
		}
	}()

	//TODO let printInterVal can be configure, without change sourceCode
	printInterval := DEFAULT_PRINT_INTERVAL

	seqTimer := time.NewTicker(time.Duration(printInterval) * time.Second)
	for {
		select {
		case str := <- f.logChan:

			f.p(str)
		case <- seqTimer.C:
			log.Printf("================ LOG SEQ SIZE:%v ==================\n", len(f.logChan))
		}
	}
}

// print log
func (f *FileLogger) p(str string) {
	f.mu.RLock()
	defer f.mu.RUnlock()

	f.lg.Output(2, str)
	f.pc(str)
}

// print log in console, default log string wont be print in console
// NOTICE: when console is on, the process will really slowly
func (f *FileLogger) pc(str string) {
	if f.logConsole {
		if log.Prefix() != f.prefix {
			log.SetPrefix(f.prefix)
		}
		log.Println(str)
	}
}

// Printf throw logstr to channel to print to the logger.
// Arguments are handled in the manner of fmt.Printf.
func (f *FileLogger) Printf(format string, v ...interface {}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=2
	f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf(format, v...)
}

// Print throw logstr to channel to print to the logger.
// Arguments are handled in the manner of fmt.Print.
func (f *FileLogger) Print(v ...interface {}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=2
	f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprint(v...)
}

// Println throw logstr to channel to print to the logger.
// Arguments are handled in the manner of fmt.Println.
func (f *FileLogger) Println(v ...interface {}) {
	_, file, line, _ := runtime.Caller(1) //calldepth=2
	f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintln(v...)
}

//======================================================================================================================
// Trace log
func (f *FileLogger) Trace(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(2) //calldepth=3
	if f.logLevel <= TRACE {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("\033[32m[TRACE] "+format+" \033[0m ", v...)
	}
}

// same with Trace()
func (f *FileLogger) T(format string, v ...interface{}) {
	f.Trace(format, v...)
}

// info log
func (f *FileLogger) Info(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(2) //calldepth=3
	if f.logLevel <= INFO {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("\033[1;35m[INFO] "+format, v...)
	}
}

// same with Info()
func (f *FileLogger) I(format string, v ...interface{}) {
	f.Info(format, v...)
}

// warning log
func (f *FileLogger) Warn(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(2) //calldepth=3
	if f.logLevel <= WARN {
		f.logChan <- fmt.Sprintf("[%v:%v]", shortFileName(file), line) + fmt.Sprintf("\033[1;33m[WARN] "+format+" \033[0m ", v...)
	}
}

// same with Warn()
func (f *FileLogger)W(format string, v ...interface{}) {
	f.Warn(format, v...)
}

// error log
func (f *FileLogger) Error(format string, v ...interface{}) {
	_, file, line, _ := runtime.Caller(2) //calldepth=3
	if f.logLevel <= ERROR {
		f.logChan <- fmt.Sprintf("%v:%v]", shortFileName(file), line) + fmt.Sprintf("\033[1;4;31m[ERROR] "+format+" \033[0m ", v...)
	}
}

// same with Error()
func (f *FileLogger) E(format string, v ...interface{}) {
	f.Error(format, v...)
}
<|endoftext|>


<commit_before>package main

func writer(info wsInfo) {

	for {
		select {
		case wsMsg := <-info.messageChan:
			info.conn.Write([]byte("OK"))
			info.conn.Write(wsMsg.bytes)
		case <-info.closeChan:
			return
		}
	}
}
<commit_msg>removed test line<commit_after>package main

func writer(info wsInfo) {
	for {
		select {
		case wsMsg := <-info.messageChan:
			info.conn.Write(wsMsg.bytes)
		case <-info.closeChan:
			return
		}
	}
}
<|endoftext|>