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

goroutine 並行処理 userモデル

シンプルな書き方

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

func getName() string {
    time.Sleep(3 * time.Second)
    return "yohei"
}

func getAge() int {
    time.Sleep(2 * time.Second)
    return 10
}

func newUser() *User {
    ch1 := make(chan string)
    ch2 := make(chan int)
    go func() {
        ch1 <- getName()
    }()
    go func() {
        ch2 <- getAge()
    }()
    u := new(User)
    u.Name = <-ch1 // メインスレッドはch1の値が取れるまで待ってくれる
    u.Age = <-ch2 // メインスレッドはch2の値が取れるまで待ってくれる
    return u
}

func main() {
    u := newUser()
    log.Print(u)
}

selectを使った書き方

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

func getName() string {
    time.Sleep(3 * time.Second)
    return "yohei"
}

func getAge() int {
    time.Sleep(2 * time.Second)
    return 10
}

func newUser() *User {
    ch1 := make(chan string)
    ch2 := make(chan int)
    go func() {
        ch1 <- getName()
    }()
    go func() {
        ch2 <- getAge()
    }()
    u := new(User)
    for i := 0; i < 2; i++ {
        select {
        case v1 := <-ch1:
            u.Name = v1
        case v2 := <-ch2:
            u.Age = v2
        }
    }
    return u
}

func main() {
    u := newUser()
    log.Print(u)
}

goroutin 並列、並行処理

方法1

並列処理

func main() {
    // log.Print(u)
    ch := make(chan string)
    go func() {
        ch <- "hello1"
        ch <- "hello2"
        close(ch)
    }()
    for {
        v, ok := <-ch
        if !ok {
            break
        }
        log.Print(v)
    }
}

方法2

並列処理

func main() {
    ch := make(chan string)
    go func() {
        ch <- "hello1"
        ch <- "hello2"
    }()
    for i := 0; i < 2; i++ {
        log.Print(<-ch)
    }
}

並行処理

func main() {
    ch := make(chan string)
    names := []string{"yohei", "miyamoto"}
    for _, n := range names {
        go func(n string) {
            ch <- sayName(n)
        }(n)
    }
    for range names { // for i := 0; i < 2; i++ { と同じ意味
        log.Print(<-ch)
    }
}

func sayName(name string) string {
    time.Sleep(3 * time.Second)
    return fmt.Sprintf("hello! %s!\n", name)
}

ajax golang

app.yaml

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

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

main.go

package app

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

func init() {
    http.HandleFunc("/", handler)
    http.HandleFunc("/ajax", ajaxHandler)
    http.HandleFunc("/hello", helloHandler)
}

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

func ajaxHandler(w http.ResponseWriter, r *http.Request) {
    log.Print(r.URL.RawQuery)
    fmt.Fprintf(w, "Hello, ajax!")
}

func helloHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World")
}

index.tmpl

{{define "index"}}
<html>
<head>
<script src="https://code.jquery.com/jquery-3.2.1.min.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</head>
<body>
<div class="container">
  <select>
    <option value=""></option>
    <option value="1">one</option>
    <option value="2">two</option>
  </select>
  <div id="ajax"></div>
</div>
</body>
</html>
{{end}}

static/html/ajax.html

<p>ajax!</p>

static/js/app.js

$(function() {
  $('select').change(function() {
    $.ajax({
        url: "ajax",
        type: 'GET',
        data: {val:"test"},
        timeout: 10000,
        dataType: 'html'
    }).done(function (data) {
      $('#ajax').html(data)
    }).fail(function (data) {
    }).always(function (data) {
   });
  })
});

配列の分割

func sliceIDs(ids []string, n int) (output [][]string) {
    for i := 0; i < (len(ids)/n)+1; i++ {
        fromIndex := i * n
        toIndex := fromIndex + n
        if toIndex > len(ids) {
            toIndex = len(ids)
        }
        output = append(output, ids[fromIndex:toIndex])
    }
    return
}