[Go] Revelでmysqlを操作する

package導入
$ go get github.com/jinzhu/gorm
$ go get github.com/go-sql-driver/mysql

conf/app.conf

db.user = hoge
db.password = fuga
db.host = localhost
db.port = 3306
db.name = test
db.protocol = tcp

app/models/gorm.go

package models

import (
	"github.com/revel/revel"
	"github.com/jinzhu/gorm"
	"strings"
	"time"
	"fmt"
	_"github.com/go-sql-driver/mysql"
)

var DB **gorm.DB

func InitDB(){
	db, err := gorm.Open("mysql", getConnectionString())

	if err != nil {
		revel.ERROR.Println("FATAL", err)
		panic(err)
	}

	db.DB()
	DB = &db
}

type Model struct {
	gorm.Model
	ID unit `gorm:"primary_key"`
	CreatedAt time.Time
	UpdatedAt time.Time 
	DeletedAt *time.Time
}

type Validator interface {
	IsStatisfied(interface{}) bool
	DefaultMessage() String
}

func getParamString(param string, defaultValue string) string {
	p, found := revel.Config.String(param)
	if !found {
		if defaultValue == "" {
			revel.ERROR.Fatal("Could not find parameter: " + param)
		} else {
			return defaultValue
		}
	}
	return p
}


func getConnectionString() string {
	host := getParamString("db.host", "localhost")
	port := getParamString("db.port", "3306")
	user := getParamString("db.user", "hoge")
	pass := getParamString("db.password", "fuga")
	dbname := getParamString("db.name", "test")
	protocol := getParamString("db.protocol", "tcp")
	dbargs := getParamString("dbargs", "	")
	timezone := getParamString("db.timezone", "parseTime=true&loc=Asia%2FTokyo")

	if strings.Trim(dbargs, " ") != ""{
		dbargs = "?" + dbargs
	} else {
		dbargs = ""
	}

	return fmt.Sprintf("%s:%s@%s([%s]:%s)/%s%s?%s", user, pass, protocol, host, port, dbname, dbargs, timezone)
}

app/init.go

func init() {
	revel.OnAppStart(models.InitDB)
}

なんかエラーになりますね。。

[Go] Revelでcreate application

app/views/App/index.html

    <div class="span6">
      {{template "flash.html" .}}
      <form action="/App/Hello" method="GET">
        <input type="text" name="myName"/><br><br>
        <input type="submit" value="Say hello!"/>
      </form>
    </div>

app/controllers/app.go

func (c App) Hello(myName string) revel.Result {
	return c.Render(myName)
}

conf/route

GET	/App/Hello	App.Hello

$ /home/vagrant/go/bin/revel run -a myapp
コンパイルし直す
http://192.168.34.10:9000/App/Hello?myName=test

### validation

func (c App) Hello(myName string) revel.Result {
	c.Validation.Required(myName).Message("Your name is required")
	c.Validation.MinSize(myName, 3).Message("Your name is not long enough!")

	if c.Validation.HasErrors(){
		c.Validation.Keep()
		c.FlashParams()
		return c.Redirect(App.Index)
	}

	return c.Render(myName)
}
<div class="span6">
      {{template "flash.html" .}}
      <form action="/App/Hello" method="GET">
        {{with $field := field "myName" .}}
        <input type="text" name="{{$field.Name}}" value="{{$field.Flash}}"/>
        {{end}}
        <br><br>
        <input type="submit" value="Say hello!"/>
      </form>
    </div>

OK, 後はMySQL連携

[Go] Revelでroutingとテンプレート

### Request Flow
conf/route

GET     /                                       App.Index

App.IndexとはApp ControllerのIndexメソッドに飛ぶということ

### Controller
app/controllers/app.go

package controllers

import (
	"github.com/revel/revel"
)

type App struct {
	*revel.Controller
}

func (c App) Index() revel.Result {
	greeting := "Alopha World"
	return c.Render(greeting)
}

### Template
app/views/App/index.html

{{set . "title" "Home"}}
{{template "header.html" .}}

<header class="jumbotron" style="background-color:#A9F16C">
  <div class="container">
    <div class="row">
      <h1>{{.greeting}}</h1>
      <p></p>
    </div>
  </div>
</header>

<div class="container">
  <div class="row">
    <div class="span6">
      {{template "flash.html" .}}
    </div>
  </div>
</div>

{{template "footer.html" .}}

なるほど、だいたい掴めた。直感的に分かりやすい。
よし、Revelで書こう

[Go] Revelのinstallとrun -a myapp

Revelの特徴: MVCモデル, 自動コンパイル

ドキュメントを読んでも埒が明かないので、公式のTutorialを始めます。

$ go version
go version go1.15.14 linux/amd64

To get the Revel framework
$ go get github.com/revel/revel
$ go get github.com/revel/cmd/revel

$ export PATH=”/home/vagrant/go/bin”
$ revel
Usage:
revel [OPTIONS]
// 省略

$ /home/vagrant/go/bin/revel new -a myapp
Revel executing: create a skeleton Revel application
Your application has been created in:
/home/vagrant/go/src/github.com/me/revel/myapp

You can run it with:
revel run -a myapp

$ cd myapp
$ /home/vagrant/go/bin/revel run -a myapp
http://192.168.34.10:9000/

OK, keep going

[Go] HandleFuncのhandlerをswitchでまとめて実装する

ルーティングによって処理を変えたいが、handlerの中身はほとんど一緒なため、handlerの中でswitch文を使って切り分ける
L html.EscapeString(r.URL.Path) でパスの値を取得できる

func apiHandler(w http.ResponseWriter, r *http.Request){

	var code string // 変数として宣言
	switch html.EscapeString(r.URL.Path) {
	case "/btc":
		code = "BTC_JPY" // ビットコイン
	case "/eth":
		code = "ETH_JPY" // イーサリアム
	case "/xrp":
		code = "XRP_JPY" // リップル
	case "/xlm":
		code = "XML_JPY" // ステラルーメン
	case "/mona":
		code = "MONA_JPY" // モナコイン
	default:
		code = "BTC_JPY"
	}

	uri := "https://api.bitflyer.com/v1/getticker?product_code=" + code
	req, _ := http.NewRequest("GET", uri, nil)

	// 省略
}


func main() {
	http.HandleFunc("/btc", apiHandler)
	http.HandleFunc("/eth", apiHandler)
	http.HandleFunc("/xrp", apiHandler)
	http.HandleFunc("/xlm", apiHandler)
	http.HandleFunc("/mona", apiHandler)
	log.Fatal(http.ListenAndServe(":8080",nil))
}

code := “BTC_JPY” とすると定数になるので、 var code string と変数として宣言する必要がある
なるほど、後はテンプレート側の処理

bulma cssとtypescriptを使いたい

[Go] CoinMarketCapのAPIを操作しよう

CoinMarketCapとは?
-> 急速に成長している仮想通貨スペースにおける世界で最も参照されている価格追跡ウェブサイトです。

まずDeveloper向けのアカウントを作成し、管理画面でAPI_KEYをコピーして、Developer向けページのIntroductionを一通り目を通します。
https://pro.coinmarketcap.com/account
https://coinmarketcap.com/api/documentation/v1/#section/Introduction

$ curl -H “X-CMC_PRO_API_KEY: ${API_KEY}” -H “Accept: application/json” -d “start=1&limit=5000&convert=USD” -G https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest

おおおおおおおおおおお、なんかすげえ

go

package main

import (
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
)

func main(){
	client := &http.Client{}
	req, err := http.NewRequest("GET", "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest", nil)
	if err != nil {
		log.Print(err)
		os.Exit(1)
	}

	q := url.Values{}
	q.Add("start", "1")
	q.Add("limit", "5000")
	q.Add("convert", "USD")

	req.Header.Set("Accepts", "application/json")
	req.Header.Add("X-CMC_PRO_API_KEY", "${API KEY}")
	req.URL.RawQuery = q.Encode()

	resp, err := client.Do(req);
	if err != nil {
		fmt.Println("Error!")
		os.Exit(1)
	}
	fmt.Println(resp.Status)
	respBody, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(respBody));
}

取得する量が多すぎるな
1回のリクエストで25credit使うとなると、1日12回程度しかrequestできんな。流石にこれだと使い物にならんが、イメージは出来た。

[Go] Jsonで出力する

import (
	"encoding/json"
	"log"
	"net/http"
	"time"
)

func apiClockHandler(w http.ResponseWriter, r *http.Request){
	type ResponseBody struct {
		Time time.Time `json:"time"`
	}
	rb := &ResponseBody {
		Time: time.Now(),
	}

	w.Header().Set("Content-type", "application/json")

	if err := json.NewEncoder(w).Encode(rb); err != nil {
		log.Fatal(err)
	}
}

func main(){
	http.HandleFunc("/api/clock", apiClockHandler)
	log.Fatal(http.ListenAndServe(":8080",nil))
}

http://192.168.34.10:8080/api/clock

OK, ある程度のところまで来た

[Go] Bitflyerで取得した値をHTMLで表示する

import (
	"encoding/json"
	"io/ioutil"
	"net/http"
	"html/template"
	"log"
	"fmt"
)

func bitflyerHandler(w http.ResponseWriter, r *http.Request){

	uri := "https://api.bitflyer.com/v1/getticker"
	req, _ := http.NewRequest("GET", uri, nil)

	client := new(http.Client)
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	byteArray, _ := ioutil.ReadAll(resp.Body)

	var ticker Ticker
	json.Unmarshal([]byte(byteArray),&ticker)


	t := template.Must(template.ParseFiles("/home/vagrant/go/src/github.com/me/sample/src/bitflyer.html.tpl"))
	if err := t.ExecuteTemplate(w, "bitflyer.html.tpl", fmt.Sprintf("%.0f\n", ticker.Ltp)); err != nil {
		log.Fatal(err)
	}
}


func main() {
	// fmt.Printf("%.0f\n",ticker.Ltp)

	http.HandleFunc("/bitflyer/", bitflyerHandler)
	log.Fatal(http.ListenAndServe(":8080",nil))
}


type Ticker struct {
	Code string `json:"product_code"`
	Ltp float64 `json:"ltp"`
}

bitflyer.html.tpl

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>
<body>
	{{ . }} 
</body>
</html>

なるほど、これをcoin marketでやるか

[Go] サーバーを起動してHTMLファイルを表示

import (
	"log"
	"net/http"
)

func main(){
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/home/vagrant/go/src/github.com/me/sample/src"))))
	log.Fatal(http.ListenAndServe(":8080",nil))
}

L /home/vagrant/go/src/github.com/me/sample/src にhtmlファイルを配置する

index.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>
<body>
	Hello world
</body>
</html>

$ go run server.go

ほう、Echo使わなくても表示できるやん
これを動的に表示したい

### 動的に表示

import (
	"fmt"
	"log"
	"net/http"
	"time"
)

func clockHandler(w http.ResponseWriter, r *http.Request){
	fmt.Fprintf(w, `
		<!DOCTYPE html>
        <html>
        <body>
            It's %d o'clock now.
        </body>
        </html>
		`, time.Now().Hour())
}

func main(){
	http.HandleFunc("/clock/", clockHandler)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/home/vagrant/go/src/github.com/me/sample/src"))))
	log.Fatal(http.ListenAndServe(":8080",nil))
}

Printfで渡してるだけやな

### データの渡し方

func clockHandler(w http.ResponseWriter, r *http.Request){
	t := template.Must(template.ParseFiles("/home/vagrant/go/src/github.com/me/sample/src/clock.html.tpl"))

	if err := t.ExecuteTemplate(w, "clock.html.tpl", time.Now()); err != nil {
		log.Fatal(err)
	}
}

func main(){
	http.HandleFunc("/clock/", clockHandler)
	http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("/home/vagrant/go/src/github.com/me/sample/src"))))
	log.Fatal(http.ListenAndServe(":8080",nil))
}

echoでのtemplateと書き方はそんなに変わらんな

[Go] BitflyerのAPIを取得したい

### Bitflyerからの取得

package main

import (
	"fmt"
	"io/ioutil"
	"net/http"
)

func main() {
	uri := "https://api.bitflyer.com/v1/gethealth"
	req, _ := http.NewRequest("GET", uri, nil)

	client := new(http.Client)
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	byteArray, _ := ioutil.ReadAll(resp.Body)
	fmt.Println(string(byteArray))
}

$ go run gethealth.go
{“status”:”NORMAL”}

### Jsonの扱い
GoでJsonの受け取り方
1. 構造体を定義

type User struct {
	Name string `json:"name"`
	Age int `json:"age"`
	Job string `json:"job"`
}
func main() {
	userStr :=
	 `{
	    "name": "tokumaru",
	    "age": 20,
	    "job": "student"
	  }`

	var user User
	json.Unmarshal([]byte(userStr),&user)

	fmt.Println(user.Name)
}

// 構造体を定義
type User struct {
	Name string `json:"name"`
	Age int `json:"age"`
	Job string `json:"job"`
}

$ go run test.go
tokumaru

↓上記をつなげる
### Bitcoinの価格を取得
bitcoinは小数点なので、float64で定義する。
表示する際は、fmt.Printf(“%.0f\n”,ticker.Ltp)として、小数点以下を切り捨てる

package main

import (
	"fmt"
	"encoding/json"
	"io/ioutil"
	"net/http"
)

func main() {
	uri := "https://api.bitflyer.com/v1/getticker"
	req, _ := http.NewRequest("GET", uri, nil)

	client := new(http.Client)
	resp, _ := client.Do(req)
	defer resp.Body.Close()

	byteArray, _ := ioutil.ReadAll(resp.Body)

	var ticker Ticker
	json.Unmarshal([]byte(byteArray),&ticker)

	fmt.Printf("%.0f\n",ticker.Ltp)
}

// 構造体を定義
type Ticker struct {
	Code string `json:"product_code"`
	Ltp float64 `json:"ltp"`
}

$ go run test.go
5014562

これをHTMLで表示させたい