[Go] Ubuntu20.04にインストール

GoのDownloadsページから最新版のバージョンを確認します。
https://golang.org/dl/

今回はgo1.17.1

$ sudo wget -c https://dl.google.com/go/go1.17.1.linux-amd64.tar.gz -O – | sudo tar -xz -C /usr/local
$ export PATH=$PATH:/usr/local/go/bin
$ source ~/.profile
$ go version
go version go1.17.1 linux/amd64

うむ、OK

[Go] MySQLに接続する

まずMySQLにデータを作ります。
mysql
create database go;
use go
create table users (
id int auto_increment primary key,
name varchar(255),
age int,
address varchar(255),
update_at datetime
);
mysql> describe users;
+———–+————–+——+—–+———+—————-+
| Field | Type | Null | Key | Default | Extra |
+———–+————–+——+—–+———+—————-+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| age | int | YES | | NULL | |
| address | varchar(255) | YES | | NULL | |
| update_at | datetime | YES | | NULL | |
+———–+————–+——+—–+———+—————-+

package main

import (
	"fmt"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
)

func main(){
	_, err := sqlConnect()
	if err != nil {
		panic(err.Error())
	} else {
		fmt.Println("DB connect success!")
	}
}

func sqlConnect()(database *gorm.DB, err error){
	DBMS := "mysql"
	USER := "hoge"
	PASS := "fuga"
	PROTOCOL := "tcp(localhost:3306)"
	DBNAME := "go"

	CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
	return gorm.Open(DBMS, CONNECT)
}

$ /home/vagrant/go/bin/dep ensure
$ go run main.go
DB connect success!

### データ挿入

import (
	"fmt"
	"time"

	_ "github.com/go-sql-driver/mysql"
	"github.com/jinzhu/gorm"
)

func main(){
	db, err := sqlConnect()
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	error := db.Create(&Users{
		Name: "山田太郎",
		Age: 20,
		Address: "東京都港区赤坂1丁目1-1",
		UpdateAt: getDate(),
	}).Error
	if error != nil {
		fmt.Println(error)
	} else {
		fmt.Println("data insert success!")
	}

}

func sqlConnect()(database *gorm.DB, err error){
	DBMS := "mysql"
	USER := "hoge"
	PASS := "fuga"
	PROTOCOL := "tcp(localhost:3306)"
	DBNAME := "go"

	CONNECT := USER + ":" + PASS + "@" + PROTOCOL + "/" + DBNAME + "?charset=utf8&parseTime=true&loc=Asia%2FTokyo"
	return gorm.Open(DBMS, CONNECT)
}

type Users struct {
	ID int
	Name string `json:"name"`
	Age int `json:"age"`
	Address string `json:"address"`
	UpdateAt string `json:"updateAt" sql:"not null;type:date"`
}

func getDate() string {
	const layout = "2006-01-01 15:05:30"
	now := time.Now()
	return now.Format(layout)
}

$ go run main.go
data insert success!

mysql> select * from users;
+—-+————–+——+———————————+———————+
| id | name | age | address | update_at |
+—-+————–+——+———————————+———————+
| 1 | 山田太郎 | 20 | 東京都港区赤坂1丁目1-1 | 2021-09-09 14:23:20 |
+—-+————–+——+———————————+———————+
1 row in set (0.00 sec)

### データの取得

func main(){
	db, err := sqlConnect()
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	result := []*Users{}
	error := db.Find(&result).Error
	if error != nil || len(result) == 0 {
		return
	}
	for _, user := range result {
		fmt.Println(user.Name)
	}
}

$ go run main.go
山田太郎

### データの更新

func main(){
	db, err := sqlConnect()
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	result := []*Users{}
	error := db.Find(&result).Error
	if error != nil || len(result) == 0 {
		return
	}
	for _, user := range result {
		fmt.Println(user.Name)
	}

	fmt.Println("update")

	error = db.Model(Users{}).Where("id = ?", 1).Update(&Users{
		Name: "佐藤はじめ",
		UpdateAt: getDate(),
	}).Error

	if error != nil {
		fmt.Println(error)
	}

	result = []*Users{}
	db.Find(&result)
	for _, user := range result {
		fmt.Println(user.Name)
	}
}

$ go run main.go
山田太郎
update
佐藤はじめ

### DELETE

	fmt.Println("Delete")

	error = db.Where("id = ?", 1).Delete(Users{}).Error
	

	if error != nil {
		fmt.Println(error)
	}

	result = []*Users{}
	db.Find(&result)
	for _, user := range result {
		fmt.Println(user.Name)
	}

$ go run main.go
佐藤はじめ
Delete

mysql> select * from users;
Empty set (0.00 sec)

OK、CRUDは一通りわかった🈴

[Go] HTMLテンプレートを使って表示

public/view/hello.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>
	{{define "hello"}}Hello, {{.}}!{{end}}
</body>
</html>

main.go

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

	"github.com/labstack/echo"
)

type Template struct {
	templates *template.Template
}

func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error {
	return t.templates.ExecuteTemplate(w, name, data)
}

func Hello(c echo.Context) error {
	return c.Render(http.StatusOK, "hello", "World")
}

func main(){
	e := echo.New()
	t := &Template{
		templates: template.Must(template.ParseGlob("public/views/*.html")),
	}
	e.Renderer = t
	e.GET("/hello", Hello)

	e.Logger.Fatal(e.Start(":8000"))
}

うーむ、出来たのはわかったけど、イマイチ仕組みがわからん

[Go] Anacondaでtwitterのつぶやき取得

/home/vagrant/go/bin/dep init

twitterのconsumer key, access-tokenなどを用意します。

package main

import (
	"github.com/ChimeraCoder/anaconda"
)

func main() {
    anaconda.NewTwitterApiWithCredentials("your-access-token", "your-access-token-secret", "your-consumer-key", "your-consumer-secret")
}

$ /home/vagrant/go/bin/dep ensure

作成されています。

twitterAccount.json

{
  "accessToken": "",
  "accessTokenSecret": "",
  "consumerKey": "",
  "consumerSecret": ""
}
package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"

	"github.com/ChimeraCoder/anaconda"
)

func main(){

	raw, error := ioutil.ReadFile("twitterAccount.json")

	if error != nil {
		fmt.Println(error.Error())
		return
	}

	var twitterAccount TwitterAccount
	json.Unmarshal(raw, &twitterAccount)

	api := anaconda.NewTwitterApiWithCredentials(twitterAccount.AccessToken, twitterAccount.AccessTokenSecret, twitterAccount.ConsumerKey, twitterAccount.ConsumerSecret)

	searchResult, _ := api.GetSearch(`スタートアップ`, nil)
	for _, tweet := range searchResult.Statuses {
		fmt.Println(tweet.Text)
	}
}

type TwitterAccount struct {
	AccessToken string `json:"accessToken"`
	AccessTokenSecret string `json:"accessTokenSecret"`
	ConsumerKey string `json:"consumerKey"`
	ConsumerSecret string `json:"consumerSecret"`
}

$ go run main.go
“厄介者”火山灰で排水処理を手助け、
巨大企業も目?
RT @ecoecoecho: 何をやっているか全く分からないが藤原竜也のイ
RT @Herlipto_info: 𝖲𝗁𝖺𝗋𝗂𝗇𝗀 𝗈𝗎𝗋 𝗅𝗈𝗏𝖾𝗅𝗒 𝗇𝖾𝗐 𝗉𝗂𝖾𝖼𝖾𝗌.

9/20(mon)20:00
RT @Herlipto_info: 𝖲𝗁𝖺𝗋𝗂𝗇𝗀 𝗈𝗎𝗋 𝗅𝗈𝗏𝖾𝗅𝗒 𝗇𝖾𝗐 𝗉𝗂𝖾𝖼𝖾𝗌.

// 省略

おおおおおおおおおおお

package main

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

	"github.com/labstack/echo"
	"github.com/ChimeraCoder/anaconda"
)

func main(){

	e := echo.New()
	e.Post("/tweet", search)
	e.Logger.Fatal(e.start(":1323"))
}

func search(c echo.Context) error {
	keyword := c.FormValue("keyword")
	api := connectTwitterApi()

	searchResult, _ := api.GetSearch(`"` +keyword+ `"`, nil)

	tweets := make([]*Tweet, 0)

	for _, data := range searchResult.Statuses {
		tweet := new(Tweet)
		tweet.Text = data.FullText
		tweet.User = data.User.Name

		tweets = append(tweets, tweet)
	}

	return c.JSON(http.StatusOK, tweets)
}

func connectTwitterApi() *anaconda.TwitterApi {
	raw, error := ioutil.ReadFile("twitterAccount.json")

	if error != nil {
		fmt.Println(error.Error())
		return
	}

	var twitterAccount TwitterAccount
	json.Unmarshal(raw, &twitterAccount)

	return anaconda.NewTwitterApiWithCredentials(twitterAccount.AccessToken, twitterAccount.AccessTokenSecret, twitterAccount.ConsumerKey, twitterAccount.ConsumerSecret)

}



type TwitterAccount struct {
	AccessToken string `json:"accessToken"`
	AccessTokenSecret string `json:"accessTokenSecret"`
	ConsumerKey string `json:"consumerKey"`
	ConsumerSecret string `json:"consumerSecret"`
}

type Tweet struct {
	User string `json:"user"`
	Text string `json:"text"`
}

type Tweets *[]Tweet

$ go build
go: inconsistent vendoring in /home/vagrant/go/src/github.com/me/twitter:
github.com/ChimeraCoder/anaconda@v2.0.0+incompatible: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
github.com/ChimeraCoder/tokenbucket@v0.0.0-20131201223612-c5a927568de7: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
github.com/azr/backoff@v0.0.0-20160115115103-53511d3c7330: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
github.com/dustin/go-jsonpointer@v0.0.0-20160814072949-ba0abeacc3dc: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
github.com/dustin/gojson@v0.0.0-20160307161227-2e71ec9dd5ad: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
github.com/garyburd/go-oauth@v0.0.0-20180319155456-bca2e7f09a17: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
github.com/labstack/echo/v4@v4.5.0: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt
golang.org/x/net@v0.0.0-20210917221730-978cfadd31cf: is explicitly required in go.mod, but not marked as explicit in vendor/modules.txt

run ‘go mod vendor’ to sync, or use -mod=mod or -mod=readonly to ignore the vendor directory

なんでやろ
まあ 取得できるって事まではわかった。

[Go] Echoでルーティングを実装

### Get

import (
	"net/http"
	"github.com/labstack/echo/v4"
)

func main() {
	e := echo.New()
	e.GET("/users/:name", getUserName)

	e.Logger.Fatal(e.Start(":1323"))
}

func getUserName(c echo.Context) error {
	name := c.Param("name")
	return c.JSON(http.StatusOK, name)
}

### QueryParamを使う場合

func main() {
	e := echo.New()
	e.GET("/show", show)

	e.Logger.Fatal(e.Start(":1323"))
}

func show(c echo.Context) error {
	team := c.QueryParam("team")
	member := c.QueryParam("member")
	return c.String(http.StatusOK, "team:"+team+", member:"+member)
}

http://192.168.33.10:1323/show?team=test&member=hpscript

### Post
FormValueで受け取る

func main() {
	e := echo.New()
	e.GET("/save", save)

	e.Logger.Fatal(e.Start(":1323"))
}

func save(c echo.Context) error {

	name := c.FormValue("name")
	email := c.FormValue("email")
	return c.String(http.StatusOK, "name:"+name+", email:"+email)
}

### JSONで返却

type User struct {
	Name string `json:"name"`
	Email string `json:"email"`
}

func main() {
	e := echo.New()
	e.GET("/users", saveUser)

	e.Logger.Fatal(e.Start(":1323"))
}

func saveUser(c echo.Context) error {

	u := new(User)
	if err := c.Bind(u); err != nil {
		return err
	}
	return c.JSON(http.StatusOK, u)
}
type Message struct {
	Name string `json:"name"`
	Email string `json:"email"`
	Message string `json:"message"`
}

type Response struct {
	Name string `json:"name"`
	Email string `json:"email"`
	Message string `json:message`
	Status string `json:status`
}

func main() {
	e := echo.New()
	e.GET("/send", sendMessage)

	e.Logger.Fatal(e.Start(":1323"))
}

func sendMessage(c echo.Context) error {

	m := new(Message)
	if error := c.Bind(m); error != nil {
		return error
	}
	r := new(Response)
	r.Name = m.Name
	r.Email = m.Email
	r.Message = m.Message
	r.Status = "success"
	return c.JSON(http.StatusOK, r)
}

なるほど、ルーティングの機能についてはある程度わかりました。
MVCに分かれているのではなく、1枚のファイルの中に書いていくのね。

[Go] Firebaseとの連結

Firebaseで Firestoreを作成する

import (
	"fmt"

	firebase "firebase.google.com/go"
    "google.golang.org/api/option"
)

func main(){
	fmt.Printf("Hello Firestore")
}

必要な依存関係を追加
$ go get firebase.google.com/go

あれ? 上手くいかんな。。。

[Go] パッケージ管理のdepを使う

公式のドキュメント: dep
git hub: https://github.com/golang/dep

### Binary installation
$ curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 5230 100 5230 0 0 7623 0 –:–:– –:–:– –:–:– 7612
ARCH = amd64
OS = linux
Will install into /home/vagrant/go/bin
Fetching https://github.com/golang/dep/releases/latest..
Release Tag = v0.5.4
Fetching https://github.com/golang/dep/releases/tag/v0.5.4..
Fetching https://github.com/golang/dep/releases/download/v0.5.4/dep-linux-amd64..
Setting executable permissions.
Moving executable to /home/vagrant/go/bin/dep

どうやら、私のケースでは /home/vagrant/go/bin/depにあるよう

$ /home/vagrant/go/bin/dep version
dep:
version : devel
build date :
git hash :
go version : go1.15.14
go compiler : gc
platform : linux/amd64
features : ImportDuringSolve=false
$ mkdir -p /home/vagrant/go/src/github.com/me/example
$ cd /home/vagrant/go/src/github.com/me/example
$ /home/vagrant/go/bin/dep init
$ ls
Gopkg.lock Gopkg.toml vendor

main.go

package main

import (
	"fmt"
	
	"github.com/carlescere/scheduler"
	"runtime"
)

func main(){
	scheduler.Every(3).Seconds().Run(printSuccess)
	runtime.Goexit()
}

func printSuccess(){
	fmt.Printf("Success!! \n")
}

$ go run main.go
main.go:6:2: cannot find package “github.com/carlescere/scheduler” in any of:
/home/vagrant/go/src/github.com/me/example/vendor/github.com/carlescere/scheduler (vendor tree)
/usr/lib/golang/src/github.com/carlescere/scheduler (from $GOROOT)
/home/vagrant/go/src/github.com/carlescere/scheduler (from $GOPATH)

このままでは 以下のようにcannot findとなるのでensureする
$ /home/vagrant/go/bin/dep ensure

$ go run main.go
Success!!
Success!!
Success!!
Success!!
Success!!
Success!!

なるほど、phpでいうcomposerみたいなものか
素晴らしい

[Go] Echoを使いたい

$ go version
go version go1.15.14 linux/amd64

$ mkdir myapp && cd myapp
$ go mod init myapp
$ ls
go.mod

module myapp

go 1.15

$ go get github.com/labstack/echo/v4
$ ls
go.mod go.sum

server.go

package main

import (
	"net/http"
	"github.com/labstack/echo/v4"
)

func main() {
	e := echo.New()
	e.GET("/", func(c echo.Context) error{
		return c.String(http.StatusOK, "Hello, Echo")
	})
	e.Logger.Fatal(e.Start(":1323"))
}

$ go run server.go

type User struct {
	Name string `json:"name"`
	Email string `json:"email"`
}

func main() {
	e := echo.New()
	e.GET("/", func(c echo.Context) error{
		return c.String(http.StatusOK, "Hello, Echo")
	})
	e.GET("/user", show)

	e.Logger.Fatal(e.Start(":1323"))
}

func show(c echo.Context) error {
	u := new(User)
	if err := c.Bind(u); err != nil {
		return err
	}
	return c.JSON(http.StatusOK, u)
}

http://192.168.33.10:1323/user?name=hpscript&email=info@hpscript.com

ん? なんかオカシイ

[Go] Concurrency

### Goroutines
A goroutine is a lightweight thread managed by Go runtime.

func say(s string){
	for i := 0; i < 5; i++ {
		time.Sleep(100 * time.Millisecond)
		fmt.Println(s)
	}
}

func main(){
	go say("world")
	say("hello")
}

### Channels
Channels are a typed conduit through which you can send and receive values with the channel operator

func sum(s []int, c chan int){
	sum := 0
	for _, v := range s {
		sum += v
	}
	c <- sum
}

func main(){
	s := []int{7, 2, 8, -9, 4, 0}

	c := make(chan int)
	go sum(s[:len(s)/2], c)
	go sum(s[len(s)/2:], c)
	x, y := <-c, <-c

	fmt.Println(x, y, x+y)
}

Buffered Channels

func main(){
	ch := make(chan int, 2)
	ch <- 1
	ch <- 2

	fmt.Println(<-ch)
	fmt.Println(<-ch)
}

– Range and close
A sender can close a channel to indicate that no more values will sent. Receivers can test whether a channel has been closed by assigning a second parameter to the receive expression: after

func fibonacci(n int, c chan int){
	x, y := 0, 1
	for i := 0; i < n; i++ {
		c <- x
		x, y = y, x+y
	}
	close(c)
}

func main(){
	c := make(chan int, 10)
	go fibonacci(cap(c), c)
	for i := range c{
		fmt.Println(i)
	}
}

### Select
The select statement lets a goroutine wait on multiple communication operations.

func fibonacci(c, quit chan int){
	x, y := 0, 1
	for {
		select {
		case c <- x:
			x, y = y, x+y
		case <-quit:
			fmt.Println("quit")
			return
		}
	}
}

func main(){
	c := make(chan int)
	quit := make(chan int)
	go func(){
		for i := 0; i < 10; i++ {
			fmt.Println(<-c)
		}
		quit <- 0
	}()
	fibonacci(c, quit)
}

Default Selection

func main(){
	tick := time.Tick(100 * time.Millisecond)
	boom := time.After(500 * time.Millisecond)
	for {
		select {
		case <-tick:
			fmt.Println("tick.")
		case <-boom:
			fmt.Println("BOOM!")
			return
		default:
			fmt.Println("   .")
			time.Sleep(50 * time.Millisecond)
		}
	}
}

– sync.Mutex

type SafeCounter struct {
	mu sync.Mutex
	v map[string]int
}

func (c *SafeCounter) Inc(key string){
	c.mu.Lock()
	c.v[key]++
	c.mu.Unlock()
}

func(c *SafeCounter) Value(key string) int {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.v[key]
}

func main(){
	c := SafeCounter{v: make(map[string]int)}
	for i := 0; i < 1000; i++ {
		go c.Inc("somekey")
	}

	time.Sleep(time.Second)
	fmt.Println(c.Value("somekey"))
}

それではEchoでもやりますか

[Go] Methods and Interfaces その2

– Interface values with nil underlying values
If the concrete value inside the interface itself is nil, the method will be called with a nil receiver.

type I interface {
	M()
}

type T struct {
	S string
}

func (t *T) M(){
	if t == nil {
		fmt.Println("<nil>")
		return
	}
	fmt.Println(t.S)
}

func main(){
	var i I

	var t *T
	i = t
	describe(i)
	i.M()

	i = &T{"hello"}
	describe(i)
	i.M()
}

func describe(i I){
	fmt.Printf("(%v, %T)\n", i, i)
}

– Nil interface values
A nil interface value holds neither value nor concrete type.

type I interface {
	M()
}

func main(){
	var i I

	describe(i)
	i.M()
}

func describe(i I){
	fmt.Printf("(%v, %T)\n", i, i)
}

(, )
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x49abcf]

goroutine 1 [running]:
main.main()
/home/vagrant/next/go/hello.go:14 +0x8f

– The empty interface
The interface type that specifies zero methods is known as the empty interface: interface{}

func main(){
	var i interface{}
	describe(i)

	i = 42
	describe(i)

	i = "hello"
	describe(i)
}

func describe(i interface{}){
	fmt.Printf("(%v, %T)\n", i, i)
}

– Type assertions
A type assertion provides access to an interface value’s underlying concrete value.

func main(){
	var i interface{} = "hello"
	
	s := i.(string)
	fmt.Println(s)

	s, ok := i.(string)
	fmt.Println(s, ok)

	f, ok := i.(float64)
	fmt.Println(f, ok)

	f = i.(float64)
	fmt.Println(f)
}

– Type switches
A type switch is a construct that permits several type assertions in series.

func do(i interface{}){
	switch v := i.(type){
	case int:
		fmt.Printf("Twice %v is %v\n", v, v*2)
	case string:
		fmt.Printf("%q is %v bytes long\n", v, len(v))
	default:
		fmt.Printf("I don't know about type %T!\n", v)
	}
}

func main(){
	do(21)
	do("hello")
	do(true)
}

– Stringers
A stringer is a type that can describe itself as a string. The fmt package look for this interface to print values.

type Person struct {
	Name string
	Age int
}

func (p Person) String() string {
	return fmt.Sprintf("%v (%v years)", p.Name, p.Age)
}

func main(){
	a := Person{"Arthur Dent", 42}
	z := Person{"Zaphod Beeblebrox", 9001}
	fmt.Println(a, z)
}

### Errors
Go programs express error state with error values.

type MyError struct {
	When time.Time
	What string
}

func (e *MyError) Error() string{
	return fmt.Sprintf("at %v, %s", e.When, e.What)
}

func run() error {
	return &MyError {
		time.Now(),
		"it didn't work",
	}
}

func main(){
	if err := run(); err != nil {
		fmt.Println(err)
	}
}

### Readers
The io package specifies the io.Reader interface, which represents the read end of a stream of data.

import (
	"fmt"
	"io"
	"strings"
)

func main(){
	r := strings.NewReader("Hello, Reader!")

	b := make([]byte, 8)
	for {
		n, err := r.Raed(b)
		fmt.Printf("n = %v err = %v b= %v\n", n, err, b)
		fmt.Printf("b[:n] = %q\n", b[:n])
		if err == io.EOF {
			break
		}
	}
}

### Images

import (
	"fmt"
	"image"
)

func main(){
	m := image.NewRGBA(image.Rect(0, 0, 100, 100))
	fmt.Println(m.Bounds())
	fmt.Println(m.At(0, 0).RGBA())
}

Methods と Interfaceを使いこなすには 少し時間がかかりそうだ