gin func 共通化

package app

import (
    "net/http"

    "github.com/gin-gonic/gin"
)

func init() {
    r := gin.Default()
    baseFunc := BaseFunc(baseFunc)
    r.GET("/new", baseFunc.new)
    http.Handle("/", r)
}

// BaseFunc ...
type BaseFunc func(*gin.Context) string

// 共通メソッド
func baseFunc(*gin.Context) string {
    return "hello"
}

func (f BaseFunc) new(c *gin.Context) {
    s := f(c)
    c.String(200, s)
}

gae go パッケージ構成 dotenvについて

フォルダ構成

(project root)
├── app
│   ├── app.yaml
│   ├── key.env
│   └── main.go (userパッケージ呼び出し)
├── models [サービス共通で使うパッケージ]
│   ├── model.go(dotenv初期化)
│   └── user.go
└── vendor

app/main.go

func init() {
    os.Setenv("GO_ENV", "dev")
    err := godotenv.Load("key.env")
    if err != nil {
        log.Fatal("Error loading .env file")
    }
}

models/model.go

func init() {
        // modelsパッケージ単体テストの時のみ、app/key.envを参照する
    if os.Getenv("GO_ENV") != "dev" {
        err := godotenv.Load("../app/key.env")
        if err != nil {
            log.Fatal("Error loading .env file")
        }
    }
}

gae go helloworld

ソース

package app

import (
    "fmt"
    "net/http"
)

func init() {
    http.HandleFunc("/", handler)
}

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprint(w, "Hello, world!")
}

app.yaml

application: {アプリケーション名}
version: 1
runtime: go
api_version: go1.8

handlers:
- url: /.*
  script: _go_app

golang メソッドチェーン

package main

import (
    "fmt"
    "log"
    "strings"
)

// Repo ...
type Repo struct {
    RawQuery []string
}

// User ...
func (r *Repo) User(id string) *Repo {
    r.RawQuery = append(r.RawQuery, fmt.Sprintf(`user=%s`, id))
    return r
}

// Group ...
func (r *Repo) Group(id string) *Repo {
    r.RawQuery = append(r.RawQuery, fmt.Sprintf(`group=%s`, id))
    return r
}

// Query ...
func (r *Repo) Query() string {
    return strings.Join(r.RawQuery, " and ")
}

func main() {
    r := new(Repo)
    log.Print(r.User("1").Group("2").Query()) // user=1 and group=2 が返る
}

func 共通化

func newHandlerFunc(handleFunc func(w http.ResponseWriter, r *http.Request)) http.Handler {
    handler := http.HandlerFunc(handleFunc)
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "common!")
        handler.ServeHTTP(w, r)
    })
}

func testFunc(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test!") }

func main() {
    http.Handle("/", newHandlerFunc(testFunc))
    http.ListenAndServe(":8080", nil)
}

gin

func init() {
    err := godotenv.Load(fmt.Sprintf("kintone.env.%s", "dev"))
    if err != nil {
        log.Fatal("Error loading .env file")
    }
    r := gin.Default()
    h := newHandleFunc(testFunc)
    r.GET("/", h)
    http.Handle("/", r)
}

func newHandleFunc(handleFunc func(*gin.Context, *kintone.Client)) func(*gin.Context) {
    return func(c *gin.Context) {
        c.String(200, "base!")
        ctx := appengine.NewContext(c.Request)
        client, _ := newClient(ctx, os.Getenv("KINTONE_APPID"))
        handleFunc(c, client)
    }
}

func testFunc(c *gin.Context, client *kintone.Client) {
    c.String(200, "test!!")
    r, _ := client.GetRecord("39")
    c.String(200, r.ID)
}

gin wrap

func init() {
    r := gin.Default()
    r.GET("/", gin.WrapF(newHandlerFunc(testFunc)))
    http.Handle("/", r)
}

func newHandlerFunc(handleFunc func(w http.ResponseWriter, r *http.Request)) http.HandlerFunc {
    handler := http.HandlerFunc(handleFunc)
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "common!")
        handler.ServeHTTP(w, r)
    })
}

func testFunc(w http.ResponseWriter, r *http.Request) { fmt.Fprint(w, "test!") }

funcを返すfunc

package main

import "log"

func newFunc(str string) func() string {
    return func() string {
        return str
    }
}

func main() {
    f := newFunc("hello")
    log.Print(f()) // "hello"が返る
}

funcにfunc追加

type BaseFunc func(string)

func (f BaseFunc) sayHello(s string) {
    f(s) // sayHelloメソッドの中でレシーバーのbasefuncのメソッドも実行
    log.Printf("sayHelloFunc: %s", s)
}

func main() {
    f := BaseFunc(func(s string) { log.Printf("basefunc: %s", s) }) // BaseFunc初期化
    f.sayHello("test!")
}

結果

f.sayHello(“test!”) を実行すると、sayHelloメソッドが、親のbasefuncも実行する形になる。

basefunc: test!
sayHelloFunc: test!

interfaceと構造体の埋め込み

type Person interface {
    Name() string
    Title() string
}

type person struct {
    firstame string
}

// 共通メソッドをperson structに持たせる
func (p *person) Name() string {
    return p.firstame
}

type male struct {
    *person // 埋め込み
}

func (m male) Title() string {
    return "Mr."
}

type female struct {
    *person
}

// Person interface作成メソッド
func newPerson(gender, firstname string) Person {
    switch gender {
    case "male":
        // p := person{firstame: firstname}
        p := male{&person{firstame: firstname}} // maleは、person structをラップしたもの
        return p
    }
    return nil
}

func fullName(p Person) string {
    return p.Title() + p.Name()
}

func main() {
    p := newPerson("male", "yohei")
    log.Print(fullName(p))
}

参考

https://qiita.com/tenntenn/items/e04441a40aeb9c31dbaf

branch 差分

// branch差分確認
git diff master branch

// masterの修正をブランチに取り込む
git merge master

// 差分はなくなってることを確認できる
git diff master branch