Category Archives: Go

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

interface 実装パターン

package main

import "log"

type Worker interface {
    Hello() string
}

type User struct {
    Name string
}

func (u User) Hello() string {
    return u.Name
}

func Hello(w Worker) string {
    return w.Hello()
}

func main() {
    u := User{Name: "yohei"}    
    w := Worker(u)

    // 以下のやり方でも良い
    // var w Worker
    // w = u

    log.Print(Hello(w))
}

go wrap

package main

import "log"

// Isao ...
func Isao(input string) string {
    switch input {
    case "hello":
        return "hello!"
    case "hi":
        return "hi!"
    }
    return "何をいってるかわからねえよ!このやろう!!!"
}

func IsaoWrapper(input string) string {
    output := Isao(input)
    if output == "何をいってるかわからねえよ!このやろう!!!" {
        output = "何をいってるのかわかりません。"
    }
    return output
}

func main() {
    log.Print(IsaoWrapper("hello!"))
}

gin test

package app

import (
    "html/template"
    "log"
    "net/http"

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

func init() {
    r := gin.Default()
    r.GET("/", newHandler)
    r.POST("/create", createHandler)
    http.Handle("/", r)
}

func newHandler(c *gin.Context) {
    t, err := template.ParseFiles("form.tmpl")
    if err != nil {
        log.Print("error", err)
    }
    t.ExecuteTemplate(c.Writer, "form", nil)
}

func createHandler(c *gin.Context) {
    key := "name"
    if str, ok := c.GetPostForm(key); ok { // GetPostFormがあれば存在しないKeyの場合はエラーハンドリングできる
        c.String(200, "%s created!", str)
    } else {
        c.String(200, "%sフィールドがありません", key)
    }
}

func testHandler(str string) func(*gin.Context) {
    return func(c *gin.Context) {
        c.String(200, str)
    }
}

Github

https://github.com/YoheiMiyamoto/gin-test