Category Archives: Go

handler クロージャからの生成

package app

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

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

func init() {
    r := gin.Default()
    r.GET("/", newHandler("hi!"))
    http.Handle("/", r)
}

// handlerを引数から生成する
func newHandler(str string) func(*gin.Context) {
    return func(c *gin.Context) {
        c.String(200, str)
    }
}

構造体の配列

type Users []*User

func (us Users) IDs() []string {
    ids := make([]string, len(us))
    for i, u := range us {
        ids[i] = u.ID
    }
    return ids
}

func main() {
    us := Users{&User{ID: "1"}, &User{ID: "2"}}
    log.Print(us.IDs()) // ["1","2"]にが戻る
}

配列のdiff取得

main.go

package main

import "log"

func main() {
    array1 := []string{"1", "2"}
    array2 := []string{"2", "3"}
    d := diffArray(array1, array2)
    log.Print(d.Add)
    log.Print(d.Delete)
}

type diff struct {
    Add    []string
    Delete []string
}

func diffArray(baseArray, compareArray []string) *diff {
    var d diff
    for _, b := range baseArray {
        if !findStr(compareArray, b) {
            d.Delete = append(d.Delete, b)
        }
    }
    for _, c := range compareArray {
        if !findStr(baseArray, c) {
            d.Add = append(d.Add, c)
        }
    }
    return &d
}

func findStr(args []string, str string) bool {
    for _, v := range args {
        if v == str {
            return true
        }
    }
    return false
}

テストコード

package main

import (
    "testing"
)

func TestFindStr(t *testing.T) {
    if !findStr([]string{"1", "2"}, "1") {
        t.Error()
    }
}

func TestDiffArray(t *testing.T) {
    d := diffArray([]string{"1", "2"}, []string{"2", "3", "4"})
    if d.Add[0] != "3" {
        t.Error()
    }
    if d.Add[1] != "4" {
        t.Error()
    }
    if d.Delete[0] != "1" {
        t.Error()
    }
}

select2 test

app.yaml

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

handlers:
- url: /css
  static_dir: static/css
- url: /js
  static_dir: static/js
- url: /.*
  script: _go_app

handler.go

package app

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

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

func handler(w http.ResponseWriter, r *http.Request) {
    t, err := template.ParseFiles("index.tmpl")
    if err != nil {
        log.Print("error", err)
    }
    t.ExecuteTemplate(w, "index", nil)
}

api_handler.go

package app

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"
)

func init() {
    http.HandleFunc("/api", apiHandler)
    http.HandleFunc("/api2", apiHandler2)
}

type item struct {
    ID   int    `json:"id"`
    Text string `json:"text"`
    Name string `json:"name"`
}

func apiHandler(w http.ResponseWriter, r *http.Request) {
    q := r.URL.Query().Get("q")
    var its []*item
    switch q {
    case "1":
        it := item{ID: 1, Text: "one", Name: "Yohei"}
        its = append(its, &it)
    case "2":
        it := item{ID: 2, Text: "two", Name: "Miyamoto"}
        its = append(its, &it)
    }
    b, _ := json.Marshal(its)
    fmt.Fprint(w, string(b))
}

func apiHandler2(w http.ResponseWriter, r *http.Request) {
    var it item
    if s := r.URL.Query().Get("id"); s != "" {
        id, _ := strconv.Atoi(s)
        switch id {
        case 1:
            it.ID = 1
            it.Text = "one!"
            it.Name = "1"
        case 2:
            it.ID = 2
            it.Text = "two!"
            it.Name = "2"
        }
    }
    b, _ := json.Marshal(it)
    log.Print(string(b))
    fmt.Fprint(w, string(b))
}

index.tmpl

{{define "index"}}
<html>
<head>
  <script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
  <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet">
  <script type="text/javascript" src="js/app.js"></script>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
</head>
<body>
  <select id="select2-1"></select>
  <select id="select2-2"></select>
  <form>
    <input name="text"></input>
  </form>
</body>
</html>
{{end}}

app.js

$(function(){
  "use strict";
  $('#select2-1').select2({
    width: "400px",
    data: [{id: 0, text: "hello"}],
    ajax: {
      url: "api",
      dataType: 'json',
      delay: 250,
      minimumInputLength: 4,
      data: function (params) {
        return {
          q: params.term
        };
      },
      processResults: function (data) {
        return {
          results: $.map(data, function(obj) {
            var id = obj.id
            var text = obj.text
            return { id: id, text: text };
          })
        };
      },
      cache: false
    }
  }).on("select2:select", function(e){
    if (e.params.data.text == "one"){
      var str = "hello one!"
    }else{
      var str = "hello two!"
    }
    $.ajax({
      url: "/api2",
      type: 'GET',
      data: {id:e.params.data.id},
      timeout: 10000,
      dataType: 'json'
    }).done(function (data) {
      $("input[name='text']").val(data.name)
    });
    var newOption = new Option(str,0);
    $('#select2-2 option').remove();
    $('#select2-2').append(newOption).trigger('change');
    console.log("change!")
  });

  $('#select2-2').select2({
    width: "400px",
  });
});

gorouting バッファ

チャンネルに保持しないのでバッファなしで問題なし

func main() {
    ch := make(chan int)
    go func() {
        ch <- 1
    }()
    log.Print(<-ch)
}

バッファが必要なケース

wait groupを使う場合はバッファが必要

func main() {
    w := new(sync.WaitGroup) // バッファがないとエラー
    ch := make(chan int)
    w.Add(1)
    go func() {
        ch <- 1 // ここで一度、チャンネルに保持するため
        w.Done()
    }()
    w.Wait()
    log.Print(<-ch)
}

同じくバッファが必要なケース

func main() {
    ch := make(chan int, 5) // バッファ付きチャンネル

    w := new(sync.WaitGroup)
    for i := 0; i < 5; i++ {
        w.Add(1)
        go func(i int) {
            ch <- i
            w.Done()
        }(i)
    }
    w.Wait()
    close(ch)

    for {
        if v, ok := <-ch; ok {
            log.Print(v)
        } else {
            break
        }
    }
}

クローズする方法の方が、スマート

func main() {
    w := new(sync.WaitGroup)
    ch := make(chan int, 1)
    w.Add(1)
    go func() {
        ch <- 1
        w.Done()
    }()
    w.Wait()
    close(ch)
    for v := range ch {
        log.Print(v)
    }
}

gorouting エラーハンドリング

// User ...
type User struct {
    Name string
    Age  int
}

func getUser() (*User, error) {
    return &User{Name: "yohei"}, fmt.Errorf("error!")
}

func main() {
    type item struct {
        User *User
        err  error
    }
    ch := make(chan item)
    go func() {
        var it item
        it.User, it.err = getUser()
        ch <- it
    }()

    it := <-ch
    if it.err != nil {
        log.Print(it.err)
    } else {
        log.Print(it.User)
    }
}

無名関数

func main() {
    s1 := func() string { return "hello" }()
    log.Print(s1)
    s2 := func(s string) string { return fmt.Sprintf("%s!", s) }("hello")
    log.Print(s2)
}

結果

2017/09/19 11:58:17 hello
2017/09/19 11:58:17 hello!

derer

パターン1

func main() {
    deferTest()
}

func deferTest() {
    defer log.Print("defer!")
    log.Print("hello")
}

出力結果

2017/09/19 09:32:47 hello
2017/09/19 09:32:47 defer!

パターン2

func main() {
    deferTest(1)
}

func deferTest(i int) {
    defer log.Print("defer!")
    if i == 1 {
        log.Print("one!")
        return
    }
    log.Print("not one!")
    return
}

結果

2017/09/19 09:35:52 one!
2017/09/19 09:35:52 defer!

agouti

スクレイピング対象ページ

app/main.go

package app

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

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

func handler(w http.ResponseWriter, r *http.Request) {
    t, err := template.ParseFiles("app/index.tmpl")
    if err != nil {
        log.Print("error", err)
    }
    t.ExecuteTemplate(w, "content", nil)
}

app/index.tmpl

{{define "content"}}
  <html>
    <div id="content">
      <div>one</div>
      <div>two</div>
    </div>
    <div>
      <label>価格</label>
      <div>100</div>
      <div>200</div>
    </div>
    <div>
      <div>900</div>
      <label>住所</label>
    </div>
    <form action="" method="post">
      <input type="text" name="name">
    </form>
  </html>
{{end}}

app.yaml

application: agouti-test
version: 1
runtime: go
api_version: go1.8

skip_files:
  - scraping.go
handlers:
- url: /.*
  script: _go_app
  secure: always

ページ作成

driver := agouti.PhantomJS()
if err := driver.Start(); err != nil {
    log.Fatalf("Failed to start driver:%v", err)
    return
}
defer driver.Stop()

p, err := driver.NewPage()
if err != nil {
    log.Fatal(err)
}

scraping.go

package main

import (
    "log"

    "github.com/sclevine/agouti"
)

const (
    targetURL = "https://agouti-test.appspot.com"
)

func main() {
    driver := agouti.PhantomJS()
    if err := driver.Start(); err != nil {
        log.Fatalf("Failed to start driver:%v", err)
        return
    }
    defer driver.Stop()

    p, err := driver.NewPage()
    if err != nil {
        log.Fatal(err)
    }

    err = p.Navigate(targetURL)
    if err != nil {
        log.Fatal(err)
    }

    t, err := p.FindByID("content").All("div").At(0).Text()
    if err != nil {
        log.Fatal(err)
    }
    log.Print(t)

    err = p.FirstByName("name").Fill("hello")
    if err != nil {
        log.Fatal(err)
    }
    p.Screenshot("capture.jpg")

    // 以降の兄弟を取得
    t, err = p.FirstByXPath("//label[contains(text(),'価格')]/following-sibling::div[2]").Text()
    if err != nil {
        log.Fatal(err)
    }
    log.Print(t)

    // 以前の兄弟を取得
    t, err = p.FirstByXPath("//label[contains(text(),'住所')]/preceding-sibling::div[1]").Text()
    if err != nil {
        log.Fatal(err)
    }
    log.Print(t)

    // 親から下の階層を取得
    t, err = p.FirstByXPath("//div[contains(text(),'900')]/parent::div[1]/label").Text()
    if err != nil {
        log.Fatal(err)
    }
    log.Print(t)
}

参考

http://qiita.com/rllllho/items/cb1187cec0fb17fc650a