20200206のMacに関する記事は7件です。

Elasticsearch 7.5 と Kibana 7.5 に Security を有効化してDockerで起動する(コピペ)

やりたいこと

ログの集計・可視化をElasticsearchとKibanaを使って行いたいと思い、色々調べてみたところ、どうやらAmazon Lightsailで動かすのが安そうということで、環境構築について検討した。
Elastic Cloudだと手軽に始められる反面、インスタンスコストが割高で、最小構成だとリサイズに失敗したりしてほとんど何もできなかった。
Lightsailの場合、4GB RAMのインスタンスが$20/月なので、2GBをElasticsearchに、1GBをKibanaに、という構成ができそう。
そして、比較的最近、無償ライセンスのBasicプランでもX-Pack Securityが使えるようになっているので、しっかり認証も付けておきたい。(Lightsailはポート制限しかできない)
開発はMacで行うので、できればローカルの開発環境と本番を同じように構成したい。

要点

  • Elasticsearch & Kibana を使う
  • X-Pack Securityを有効化する
  • なるべく安くそこそこの環境を整えたい
  • MacとLightsailで同じ環境を作りたい

前提

  • Dockerが使える状態
  • ホスト側がRAM 4GB以上で、Elasticsearchに2GB、Kibanaに1GBを割当てられる
  • 直近ではSSLオプション使わない

以下をコピペしとけば動く

1. データの保存先を作成

  • 必要に応じて権限を変える
mkdir -p ~/Development/Docker/Elasticsearch/data ~/Development/Docker/Elasticsearch/certs

2. Elasticsearchを起動

  • 環境変数 ELASTIC_PASSWORDelastic ユーザーのパスワードなので変更する
docker run --name Elasticsearch -d \
-m 2048m \
-p 9200:9200 \
-p 9300:9300 \
-e cluster.name=ES \
-e discovery.type=single-node \
-e network.host=0.0.0.0 \
-e bootstrap.memory_lock=true \
-e xpack.security.enabled=true \
-e xpack.monitoring.collection.enabled=true \
-e "ES_JAVA_OPTS=-Xms1024m -Xmx1024m" \
-e "ELASTIC_PASSWORD=iY69DxipKifV7utYA4t6jgxT" \
-v ~/Development/Docker/Elasticsearch/data:/usr/share/elasticsearch/data \
-v ~/Development/Docker/Elasticsearch/certs:/usr/share/elasticsearch/certs \
--ulimit nproc=4096:4096 \
--ulimit memlock=256000:256000 \
--ulimit nofile=65536:65536 \
docker.elastic.co/elasticsearch/elasticsearch:7.5.2

3. kibanaユーザーのパスワードを設定する

  • BASIC認証のパスワードは2で指定したものを使う
  • password の値がkibanaユーザーのパスワード
curl -XPUT --user elastic:iY69DxipKifV7utYA4t6jgxT 'localhost:9200/_xpack/security/user/kibana/_password' -H "Content-Type: application/json" -d '{
  "password" : "6ezji8D5jvceXUsTsvg8mAY4"
}'

4. Kibanaを起動する

  • ELASTICSEARCH_PASSWORD は3で設定した kibana ユーザーのパスワードを指定する
docker run --name Kibana -d \
--link Elasticsearch:elasticsearch \
-m 1024m \
-p 5601:5601 \
-e "ELASTICSEARCH_HOSTS=http://elasticsearch:9200" \
-e "ELASTICSEARCH_USERNAME=kibana" \
-e "ELASTICSEARCH_PASSWORD=6ezji8D5jvceXUsTsvg8mAY4" \
--ulimit nproc=4096:4096 \
--ulimit memlock=256000:256000 \
--ulimit nofile=65536:65536 \
docker.elastic.co/kibana/kibana:7.5.2

4. 諸々設定する

  • ブラウザでホストの 5601 番ポートに接続
  • elastic ユーザーとしてログイン
  • グループやユーザーを作成

セキュリティを強化する

SSLオプションを使いましょう。
セキュリティ機能のはじめ方 | Elastic Blog

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

UnityでiOS実機とmacエディタでBLEを使う

UnityでiOS実機とmacエディタでBLEを使う

UnityでiOS実機とmacエディタで、CoreBluetoothを使って、Bluetooth Low Energyのデバイスと通信するnative pluginを作成しました。
Unity Packageを配布しています。
リポジトリはこちら→https://github.com/fuziki/UnityCoreBluetooth

目的

UnityにはBluetoothで通信する機能がないです。
iOSでdaydreamのコントローラを使いたかったので、プラグインを自作しました。
Unity Editorにも対応しており、editorで実際に接続して実装して、実機は動作確認だけという開発が可能です。

導入

組み込む

Daydreamのコントローラの生データを取得します

Daydreamコントローラについて

下記の条件のcharacteristicに接続すると、コントローラの生データを取得可能です。
BLEデバイスは複数のserviceを持っており、serviceは複数のcharacteristicを持っています。characteristicごとに決められた機能が提供されており、今回はcharacteristic uuidは同名のidが複数存在するので、notifyのusageのcharacteristicを使って生データを受け取ります。

| 項目 | 接続する端末の条件 |
|--|--|
| デバイス名 | Daydream controller |
| service uuid | FE55 |
| characteristic usage | notify |

UnityCoreBluetoothを使う

1. シングルトンインスタンスの生成

UnityCoreBluetoothはシングルトンで使用します。
bluetooth機能がpowerOnになったら、BLEデバイスのスキャンを開始させます。
コールバックの設定が全て終了したら、StartCoreBluetoothを呼び出して開始します。

        UnityCoreBluetooth.CreateSharedInstance();
        UnityCoreBluetooth.Shared.OnUpdateState((string state) =>
        {
            Debug.Log("state: " + state);
            if (state != "poweredOn") return;
            UnityCoreBluetooth.Shared.StartScan();
        });
        //~~中略~~
        UnityCoreBluetooth.Shared.StartCoreBluetooth();

2. 接続したいデバイス名のデバイスを見つけたら、接続する

        UnityCoreBluetooth.Shared.OnDiscoverPeripheral((UnityCBPeripheral peripheral) =>
        {
            if (peripheral.name != "")
                Debug.Log("discover peripheral name: " + peripheral.name);
            if (peripheral.name != "Daydream controller") return;

            UnityCoreBluetooth.Shared.StopScan();
            UnityCoreBluetooth.Shared.Connect(peripheral);
        });

3. デバイスに接続したら、サービスを探す。

        UnityCoreBluetooth.Shared.OnConnectPeripheral((UnityCBPeripheral peripheral) =>
        {
            Debug.Log("connected peripheral name: " + peripheral.name);
            peripheral.discoverServices();
        });

4. 対象のuuidのサービスが見つかったら、characteristicを探す。

        UnityCoreBluetooth.Shared.OnDiscoverService((UnityCBService service) =>
        {
            Debug.Log("discover service uuid: " + service.uuid);
            if (service.uuid != "FE55") return;
            service.discoverCharacteristics();
        });

5. usage がnotifyのcharacteristicが見つかったら、通知を有効にする

通知を有効にすることで、daydreamコントローラから連続して生データを受け取ることが可能になります。

        UnityCoreBluetooth.Shared.OnDiscoverCharacteristic((UnityCBCharacteristic characteristic) =>
        {
            string uuid = characteristic.uuid;
            string usage = characteristic.propertis[0];
            Debug.Log("discover characteristic uuid: " + uuid + ", usage: " + usage);
            if (usage != "notify") return;
            characteristic.setNotifyValue(true);
        });

6. characteristicから通知があったら、データを受け取る

※ リアルタイムで受け取れるのですが、メインスレッド保証ではないです。

        UnityCoreBluetooth.Shared.OnUpdateValue((UnityCBCharacteristic characteristic, byte[] data) =>
        {
            this.value = data;
            this.flag = true;
        });

7. シングルトンインスタンスの破棄

    void OnDestroy()
    {
        UnityCoreBluetooth.ReleaseSharedInstance();
    }

終わりに

unityエディタとiOS実機で動くBLEのプラグインを作成しました。
動作確認のたびにiOSの実機を起動するのは手間がかかるので、エディタで開発可能になることで開発が数倍楽になった気がします。
このプラグインの構造などはこちらの記事に書いてあります。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ローカルでMysqlが急に動かなくなった

概要

いつも通りターミナルからmysqlコマンドでDBにログインしようとしたら以下のようなエラーがでた

$ mysql -u **** -D **** -h **.***.***.*** -p
dyld: Library not loaded: /usr/local/opt/openssl/lib/libssl.1.0.0.dylib
  Referenced from: /usr/local/bin/mysql
  Reason: image not found
Abort trap: 6

やったこと

ん~なんか調べてもよくわからん
結果いろんな記事見てったらbrewでインストールしなおすのが一番簡単で早いっぽい

1. アンインストール

$ brew uninstall mysql
Uninstalling /usr/local/Cellar/mysql/5.7.21... (323 files, 233.9MB)

2. インストール

$ brew install mysql
Updating Homebrew...


(以下略)

3. ログイン

$ mysql -u **** -D **** -h **.***.***.*** -p
Enter password:

動いた

事後に思ったこと

よくよく考えたらmysql起動しているかとか見るの忘れてた...
もしかしたらそれで解決したかも...

おわり

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Terminal で直前の出力内容をキーボードからコピーしたいだけだった、、、

Terminal でなにかを実行したら、実行内容が出力されて、つぎの入力受付に移るだけど、直前の出力をキーボードからコピーしたかった. で色々調べて見たんだけど、なぜか、ぱっとショートカットキーが出てこなかったんで、ここに記す.

⌘ Shift ↑

色々調べまくった末にたどり着いたサイト

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Go+Gin+Gorm】初心者だからちょっ早で超簡単webサービス作ってみる

なんとなくGo言語を書いてみたくなった。
Mac環境でやります。
劣化版Twitterアプリみたいなのを作ります。
ユーザー認証とかはないです。
今回は、

一覧・詳細表示、登録、削除、更新
バリデーション

を実装していきます。

これが今回作るwebサービスです。
つぶやき

Go言語とは?

これによると、

並列処理、ガベージコレクションを備え、軽快にコンパイルできる言語です。以下のような特徴を持っています:

- 一台のコンピュータ上であっという間に大型のGoプログラムをコンパイルすることができます。
- Goはソフトウェアの構造にモデルを与えます。分析をより簡単にこなすことができ、ファイルやライブラリのincludeといったCスタイルの書き出しにありがちな部分を大幅に省くことができます。
- Goは静的型付け言語です。型に階層の概念が無いのでユーザはその関係に気をとられることもなく、典型的なオブジェクト指向言語よりももっとライトに感じるくらいです。
- Goは完全なガベージコレクションタイプの言語です。また、基本的な並列処理とネットワークをサポートしています。
- Goはマルチプロセッサ対応のソフトウェアを作成できるようデザインされています。

完全に理解した。

ginとは

go言語のフレームワークです。railsとかと違って密結合なオールインワンではないです。
なので、railsやってた方は拍子抜けするかも。
また、これくらいの規模ならFW使わなくても手軽にwebサービス作れちゃうのがgo言語のいい所だと理解しています。

gormとは

GO言語用のORMフレームワークです。
DB周りの処理を実装する時にこのフレームワークを使うと便利です。
ORMを使わないと、SQL文をコード内に書かないといけなくなるので、ぜひORM使いましょう。
ちなみにrailsだとActive RecordがORMにあたります。

ORMの恩恵がいまいちピンとこない方はこちらの記事をみるといいでしょう。
RailsのORM機能について

GoをローカルPCに入れてみる。

ここ見てやってみてください。
ターミナルでgoって二文字を打って、以下のようになってたらOK。
おめでとう!

$ go

Go is a tool for managing Go source code.

Usage:

    go <command> [arguments]

The commands are:

    bug         start a bug report
    build       compile packages and dependencies
    clean       remove object files and cached files
    doc         show documentation for package or symbol
    env         print Go environment information
    fix         update packages to use new APIs
    fmt         gofmt (reformat) package sources
    generate    generate Go files by processing source
    get         add dependencies to current module and install them
    install     compile and install packages and dependencies
    list        list packages or modules
    mod         module maintenance
    run         compile and run Go program
    test        test packages
    tool        run specified go tool
    version     print Go version
    vet         report likely mistakes in packages

Use "go help <command>" for more information about a command.

Additional help topics:

    buildmode   build modes
    c           calling between Go and C
    cache       build and test caching
    environment environment variables
    filetype    file types
    go.mod      the go.mod file
    gopath      GOPATH environment variable
    gopath-get  legacy GOPATH go get
    goproxy     module proxy protocol
    importpath  import path syntax
    modules     modules, module versions, and more
    module-get  module-aware go get
    module-auth module authentication using go.sum
    module-private module configuration for non-public modules
    packages    package lists and patterns
    testflag    testing flags
    testfunc    testing functions

Use "go help <topic>" for more information about that topic.

MySQLをローカルにいれる。

railsとかと違って、MySQLは自分で用意しないといけない。はず。
以下のコードをターミナルで打ってMySQLに入れたらもうインストール済です。よかったですね。

$ mysql -uroot -p

入れなかった人は、インストールしましょう。
某はこちらの記事を見てインストールしました。
【超簡単】macへMySQLをインストール
ありがたやー。
インストールできたらターミナル画面はそのままで次へ

MySQLセットアップ

次はデータベースを作ったり、ユーザーを作ったりします。

データベース名:test

ユーザー名:test
パスワード:12345678

以下ターミナルに打ち込んでください。

// データベース作成
mysql> create database test;

// ユーザー作成
mysql> create user 'test'@'localhost' IDENTIFIED BY '12345678';

// データベースにアクセスする権限をユーザーに付与
mysql> grant all privileges on test.* to 'test'@'localhost';
mysql> flush privileges;
mysql> exit

テーブル作らなくていいの?と思った方。
後述のmain.go内のdbInit()でautoMigrateを使ってTweetsテーブルを自動で作ってます。
他にテーブルが必要な場合は、autoMigrateに都度書いていくという感じです。

ディレクトリ構成図

cd $GOPATH/srcして、以下のディレクトリやファイルを作ってください。
ファイルの中身は今は空で構いません。

mytweet/
┣ views/
┃  ┣ delete.html
┃  ┣ detail.html
┃  ┣ index.html
┗ main.go

外部ライブラリの導入

go getというコマンドを使います。
外部ライブラリを導入するには、go getコマンドを利用すると便利です。

go get パッケージ名
go getコマンドを発行すると、以下の処理が自動的に行われます。

・指定したパッケージのGitリモートリポジトリを$GOPATH/srcへダウンロード
・依存パッケージのGitリモートリポジトリを$GOPATH/srcへダウンロード
・ソースコードのビルド(go installコマンド相当)

参照:はじめてのGO言語
以下ターミナルで実行

// ginフレームワーク
$ go get github.com/gin-gonic/gin

// mysql用ドライバー
$ go get github.com/go-sql-driver/mysql

// gorm
$ go get github.com/jinzhu/gorm

go getした際にソースはGOPATH/src配下に、インストールされます。
ちなみに、実行ファイル(コンパイルされたやつ)はGOPATH/binにインストールされます。

完成版ソースコード一気見せ

完成したソースコードを載せます。いきなり雑になってごめんなさい。
さっき作成したファイルにコピペしてください。

main.go
package main

import (
    "log"
    "net/http"
    "strconv"

    "github.com/gin-gonic/gin"
    _ "github.com/go-sql-driver/mysql" //直接的な記述が無いが、インポートしたいものに対しては"_"を頭につける決まり
    "github.com/jinzhu/gorm"
)

// Tweetモデル宣言
// モデルはDBのテーブル構造をGOの構造体で表したもの
type Tweet struct {
    gorm.Model
    Content string `form:"content" binding:"required"`
}

func gormConnect() *gorm.DB {
    DBMS := "mysql"
    USER := "test"
    PASS := "12345678"
    DBNAME := "test"
    // MySQLだと文字コードの問題で"?parseTime=true"を末尾につける必要がある
    CONNECT := USER + ":" + PASS + "@/" + DBNAME + "?parseTime=true"
    db, err := gorm.Open(DBMS, CONNECT)

    if err != nil {
        panic(err.Error())
    }
    return db
}

// DBの初期化
func dbInit() {
    db := gormConnect()

    // コネクション解放解放
    defer db.Close()
    db.AutoMigrate(&Tweet{}) //構造体に基づいてテーブルを作成
}

// データインサート処理
func dbInsert(content string) {
    db := gormConnect()

    defer db.Close()
    // Insert処理
    db.Create(&Tweet{Content: content})
}

//DB更新
func dbUpdate(id int, tweetText string) {
    db := gormConnect()
    var tweet Tweet
    db.First(&tweet, id)
    tweet.Content = tweetText
    db.Save(&tweet)
    db.Close()
}

// 全件取得
func dbGetAll() []Tweet {
    db := gormConnect()

    defer db.Close()
    var tweets []Tweet
    // FindでDB名を指定して取得した後、orderで登録順に並び替え
    db.Order("created_at desc").Find(&tweets)
    return tweets
}

//DB一つ取得
func dbGetOne(id int) Tweet {
    db := gormConnect()
    var tweet Tweet
    db.First(&tweet, id)
    db.Close()
    return tweet
}

//DB削除
func dbDelete(id int) {
    db := gormConnect()
    var tweet Tweet
    db.First(&tweet, id)
    db.Delete(&tweet)
    db.Close()
}

func main() {
    router := gin.Default()
    router.LoadHTMLGlob("views/*.html")

    dbInit()

    //一覧
    router.GET("/", func(c *gin.Context) {
        tweets := dbGetAll()
        c.HTML(200, "index.html", gin.H{"tweets": tweets})
    })

    //登録
    router.POST("/new", func(c *gin.Context) {
        var form Tweet
        // ここがバリデーション部分
        if err := c.Bind(&form); err != nil {
            tweets := dbGetAll()
            c.HTML(http.StatusBadRequest, "index.html", gin.H{"tweets": tweets, "err": err})
            c.Abort()
        } else {
            content := c.PostForm("content")
            dbInsert(content)
            c.Redirect(302, "/")
        }
    })

    //投稿詳細
    router.GET("/detail/:id", func(c *gin.Context) {
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            panic(err)
        }
        tweet := dbGetOne(id)
        c.HTML(200, "detail.html", gin.H{"tweet": tweet})
    })

    //更新
    router.POST("/update/:id", func(c *gin.Context) {
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            panic("ERROR")
        }
        tweet := c.PostForm("tweet")
        dbUpdate(id, tweet)
        c.Redirect(302, "/")
    })

    //削除確認
    router.GET("/delete_check/:id", func(c *gin.Context) {
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            panic("ERROR")
        }
        tweet := dbGetOne(id)
        c.HTML(200, "delete.html", gin.H{"tweet": tweet})
    })

    //削除
    router.POST("/delete/:id", func(c *gin.Context) {
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            panic("ERROR")
        }
        dbDelete(id)
        c.Redirect(302, "/")

    })

    router.Run()
}

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>一覧ページ</title>
  </head>
  <body>
    <header>
      <h1>つぶやき</h1>
    </header>
    <div class="wrap">
      <div class="input">
        <p>{{.err}}</p>
        <form action="/new" method="post">
          <p>いま思っていること :<input type="text" name="content" size="30" placeholder="つぶやくこと"/></p>
          <p><input type="submit" value="つぶやく" /></p>
        </form>
      </div>
      <div class="indexGet">
        <ul>
          {{range.tweets}}
          <li>
            {{.Content}}
            <label><a href="/detail/{{.ID}}">編集</a></label>
            <label><a href="/delete_check/{{.ID}}">削除</a></label>
          </li>
          {{end}}
        </ul>
      </div>
    </div>
  </body>
</html>
detail.html
<body>
    <h2>詳細</h2>

    <form method="post" action="/update/{{.tweet.ID}}">
        <p>内容<input type="text" name="tweet" size="30" value="{{.tweet.Content}}" ></p>
        <p><input type="submit" value="Send"></p>
    </form>
</body>
delete.html
<body>
    <h1>削除確認</h1>
    <p>本当に削除しますか?</p>
    <ul>
        <li>内容: {{.tweet.Content}}</li>
        <li>作成時間: {{.tweet.CreatedAt}}</li>
    </ul>

    <form method="post" action="/delete/{{.tweet.ID}}">
        <p><input type="submit" value="削除"></p>
        <p><a href="/">戻る</a></p>
    </form>
</body>

起動

mytweetディレクトリに入って、以下を実行!

go run main.go

http://localhost:8080
を開いて、できてたらOK!

go runコマンドとは?

Go言語はコンパイル言語ですが、go runコマンドを用いると一時ファイルとしてコンパイルしたプログラムをその場で実行することができます。
実行ファイルの作り方などはここを参照しましょう。

最後に

バリデーション部分がまだイマイチだなーという感じです。
今度はログイン機能などを追加してみたいと思います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Go+Gin+Gorm】初心者だから超簡単webサービス作ってみる

なんとなくGo言語を書いてみたくなった。
Mac環境でやります。
劣化版Twitterアプリみたいなのを作ります。
ユーザー認証とかはないです。
今回は、

・ 一覧・詳細表示、登録、削除、更新
・ 空の投稿ができないようにするバリデーション

を実装していきます。

これが今回作るwebサービスです。
つぶやき

Go言語とは?

これによると、

並列処理、ガベージコレクションを備え、軽快にコンパイルできる言語です。以下のような特徴を持っています:

- 一台のコンピュータ上であっという間に大型のGoプログラムをコンパイルすることができます。
- Goはソフトウェアの構造にモデルを与えます。分析をより簡単にこなすことができ、ファイルやライブラリのincludeといったCスタイルの書き出しにありがちな部分を大幅に省くことができます。
- Goは静的型付け言語です。型に階層の概念が無いのでユーザはその関係に気をとられることもなく、典型的なオブジェクト指向言語よりももっとライトに感じるくらいです。
- Goは完全なガベージコレクションタイプの言語です。また、基本的な並列処理とネットワークをサポートしています。
- Goはマルチプロセッサ対応のソフトウェアを作成できるようデザインされています。

完全に理解した。

ginとは

go言語のフレームワークです。railsとかと違って密結合なオールインワンではないです。
なので、railsやってた方は拍子抜けするかも。
また、これくらいの規模ならFW使わなくても手軽にwebサービス作れちゃうのがgo言語のいい所だと理解しています。

gormとは

GO言語用のORMフレームワークです。
DB周りの処理を実装する時にこのフレームワークを使うと便利です。
ORMを使わないと、SQL文をコード内に書かないといけなくなるので、ぜひORM使いましょう。
ちなみにrailsだとActive RecordがORMにあたります。

ORMの恩恵がいまいちピンとこない方はこちらの記事をみるといいでしょう。
RailsのORM機能について

GoをローカルPCに入れてみる。

ここ見てやってみてください。
ターミナルでgoって二文字を打って、以下のようになってたらOK。
おめでとう!

$ go

Go is a tool for managing Go source code.

Usage:

    go <command> [arguments]

The commands are:

    bug         start a bug report
    build       compile packages and dependencies
    clean       remove object files and cached files
    doc         show documentation for package or symbol
    env         print Go environment information
    fix         update packages to use new APIs
    fmt         gofmt (reformat) package sources
    generate    generate Go files by processing source
    get         add dependencies to current module and install them
    install     compile and install packages and dependencies
    list        list packages or modules
    mod         module maintenance
    run         compile and run Go program
    test        test packages
    tool        run specified go tool
    version     print Go version
    vet         report likely mistakes in packages

Use "go help <command>" for more information about a command.

Additional help topics:

    buildmode   build modes
    c           calling between Go and C
    cache       build and test caching
    environment environment variables
    filetype    file types
    go.mod      the go.mod file
    gopath      GOPATH environment variable
    gopath-get  legacy GOPATH go get
    goproxy     module proxy protocol
    importpath  import path syntax
    modules     modules, module versions, and more
    module-get  module-aware go get
    module-auth module authentication using go.sum
    module-private module configuration for non-public modules
    packages    package lists and patterns
    testflag    testing flags
    testfunc    testing functions

Use "go help <topic>" for more information about that topic.

MySQLをローカルにいれる。

railsとかと違って、MySQLは自分で用意しないといけない。はず。
以下のコードをターミナルで打ってMySQLに入れたらもうインストール済です。よかったですね。

$ mysql -uroot -p

入れなかった人は、インストールしましょう。
某はこちらの記事を見てインストールしました。
【超簡単】macへMySQLをインストール
ありがたやー。
インストールできたらターミナル画面はそのままで次へ

MySQLセットアップ

次はデータベースを作ったり、ユーザーを作ったりします。

データベース名:test

ユーザー名:test
パスワード:12345678

以下ターミナルに打ち込んでください。

// データベース作成
mysql> create database test;

// ユーザー作成
mysql> create user 'test'@'localhost' IDENTIFIED BY '12345678';

// データベースにアクセスする権限をユーザーに付与
mysql> grant all privileges on test.* to 'test'@'localhost';
mysql> flush privileges;
mysql> exit

テーブル作らなくていいの?と思った方。
後述のmain.go内のdbInit()でautoMigrateを使ってTweetsテーブルを自動で作ってます。
他にテーブルが必要な場合は、autoMigrateに都度書いていくという感じです。

ディレクトリ構成図

cd $GOPATH/srcして、以下のディレクトリやファイルを作ってください。
ファイルの中身は今は空で構いません。

mytweet/
┣ views/
┃  ┣ delete.html
┃  ┣ detail.html
┃  ┣ index.html
┗ main.go

外部ライブラリの導入

go getというコマンドを使います。
外部ライブラリを導入するには、go getコマンドを利用すると便利です。

go get パッケージ名
go getコマンドを発行すると、以下の処理が自動的に行われます。

・指定したパッケージのGitリモートリポジトリを$GOPATH/srcへダウンロード
・依存パッケージのGitリモートリポジトリを$GOPATH/srcへダウンロード
・ソースコードのビルド(go installコマンド相当)

参照:はじめてのGO言語
以下ターミナルで実行

// ginフレームワーク
$ go get github.com/gin-gonic/gin

// mysql用ドライバー
$ go get github.com/go-sql-driver/mysql

// gorm
$ go get github.com/jinzhu/gorm

go getした際にソースはGOPATH/src配下に、インストールされます。
ちなみに、実行ファイル(コンパイルされたやつ)はGOPATH/binにインストールされます。

完成版ソースコード一気見せ

完成したソースコードを載せます。いきなり雑になってごめんなさい。
さっき作成したファイルにコピペしてください。

main.go
package main

import (
    "log"
    "net/http"
    "strconv"

    "github.com/gin-gonic/gin"
    _ "github.com/go-sql-driver/mysql" //直接的な記述が無いが、インポートしたいものに対しては"_"を頭につける決まり
    "github.com/jinzhu/gorm"
)

// Tweetモデル宣言
// モデルはDBのテーブル構造をGOの構造体で表したもの
type Tweet struct {
    gorm.Model
    Content string `form:"content" binding:"required"`
}

func gormConnect() *gorm.DB {
    DBMS := "mysql"
    USER := "test"
    PASS := "12345678"
    DBNAME := "test"
    // MySQLだと文字コードの問題で"?parseTime=true"を末尾につける必要がある
    CONNECT := USER + ":" + PASS + "@/" + DBNAME + "?parseTime=true"
    db, err := gorm.Open(DBMS, CONNECT)

    if err != nil {
        panic(err.Error())
    }
    return db
}

// DBの初期化
func dbInit() {
    db := gormConnect()

    // コネクション解放解放
    defer db.Close()
    db.AutoMigrate(&Tweet{}) //構造体に基づいてテーブルを作成
}

// データインサート処理
func dbInsert(content string) {
    db := gormConnect()

    defer db.Close()
    // Insert処理
    db.Create(&Tweet{Content: content})
}

//DB更新
func dbUpdate(id int, tweetText string) {
    db := gormConnect()
    var tweet Tweet
    db.First(&tweet, id)
    tweet.Content = tweetText
    db.Save(&tweet)
    db.Close()
}

// 全件取得
func dbGetAll() []Tweet {
    db := gormConnect()

    defer db.Close()
    var tweets []Tweet
    // FindでDB名を指定して取得した後、orderで登録順に並び替え
    db.Order("created_at desc").Find(&tweets)
    return tweets
}

//DB一つ取得
func dbGetOne(id int) Tweet {
    db := gormConnect()
    var tweet Tweet
    db.First(&tweet, id)
    db.Close()
    return tweet
}

//DB削除
func dbDelete(id int) {
    db := gormConnect()
    var tweet Tweet
    db.First(&tweet, id)
    db.Delete(&tweet)
    db.Close()
}

func main() {
    router := gin.Default()
    router.LoadHTMLGlob("views/*.html")

    dbInit()

    //一覧
    router.GET("/", func(c *gin.Context) {
        tweets := dbGetAll()
        c.HTML(200, "index.html", gin.H{"tweets": tweets})
    })

    //登録
    router.POST("/new", func(c *gin.Context) {
        var form Tweet
        // ここがバリデーション部分
        if err := c.Bind(&form); err != nil {
            tweets := dbGetAll()
            c.HTML(http.StatusBadRequest, "index.html", gin.H{"tweets": tweets, "err": err})
            c.Abort()
        } else {
            content := c.PostForm("content")
            dbInsert(content)
            c.Redirect(302, "/")
        }
    })

    //投稿詳細
    router.GET("/detail/:id", func(c *gin.Context) {
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            panic(err)
        }
        tweet := dbGetOne(id)
        c.HTML(200, "detail.html", gin.H{"tweet": tweet})
    })

    //更新
    router.POST("/update/:id", func(c *gin.Context) {
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            panic("ERROR")
        }
        tweet := c.PostForm("tweet")
        dbUpdate(id, tweet)
        c.Redirect(302, "/")
    })

    //削除確認
    router.GET("/delete_check/:id", func(c *gin.Context) {
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            panic("ERROR")
        }
        tweet := dbGetOne(id)
        c.HTML(200, "delete.html", gin.H{"tweet": tweet})
    })

    //削除
    router.POST("/delete/:id", func(c *gin.Context) {
        n := c.Param("id")
        id, err := strconv.Atoi(n)
        if err != nil {
            panic("ERROR")
        }
        dbDelete(id)
        c.Redirect(302, "/")

    })

    router.Run()
}

index.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="X-UA-Compatible" content="ie=edge" />
    <title>一覧ページ</title>
  </head>
  <body>
    <header>
      <h1>つぶやき</h1>
    </header>
    <div class="wrap">
      <div class="input">
        <p>{{.err}}</p>
        <form action="/new" method="post">
          <p>いま思っていること :<input type="text" name="content" size="30" placeholder="つぶやくこと"/></p>
          <p><input type="submit" value="つぶやく" /></p>
        </form>
      </div>
      <div class="indexGet">
        <ul>
          {{range.tweets}}
          <li>
            {{.Content}}
            <label><a href="/detail/{{.ID}}">編集</a></label>
            <label><a href="/delete_check/{{.ID}}">削除</a></label>
          </li>
          {{end}}
        </ul>
      </div>
    </div>
  </body>
</html>
detail.html
<body>
    <h2>詳細</h2>

    <form method="post" action="/update/{{.tweet.ID}}">
        <p>内容<input type="text" name="tweet" size="30" value="{{.tweet.Content}}" ></p>
        <p><input type="submit" value="Send"></p>
    </form>
</body>
delete.html
<body>
    <h1>削除確認</h1>
    <p>本当に削除しますか?</p>
    <ul>
        <li>内容: {{.tweet.Content}}</li>
        <li>作成時間: {{.tweet.CreatedAt}}</li>
    </ul>

    <form method="post" action="/delete/{{.tweet.ID}}">
        <p><input type="submit" value="削除"></p>
        <p><a href="/">戻る</a></p>
    </form>
</body>

起動

mytweetディレクトリに入って、以下を実行!

go run main.go

http://localhost:8080
を開いて、できてたらOK!

go runコマンドとは?

Go言語はコンパイル言語ですが、go runコマンドを用いると一時ファイルとしてコンパイルしたプログラムをその場で実行することができます。
実行ファイルの作り方などはここを参照しましょう。

最後に

バリデーションメッセージがrailsのように親切じゃない。
自分でメッセージも用意しないといけないのかな。

続きはこちら
【go + gin + gorm】webアプリにログイン機能を追加してみる

参考

https://blog.kannart.co.jp/programming/2235/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

DB設計

エンティティ

サービスの中で管理する必要のある概念(情報)。DBでいうテーブルの事だと考えていい。

エンティティの属性

エンティティが個別に持つ情報。DBでいうテーブルの列。

リレーション

エンティティとエンティティとの間に存在する関係性。DBでいうテーブル同士の関係のこと。

データベースを作成する手順

1.データベースで管理するデータ(エンティティ)を決める
2.それぞれのデータの持つ属性を決める
3.エンティティ同士の関係性を決める(リレーション)
4.データを実際にデータベースのテーブルとして定義する

キーの種類

レコードを識別するための特別なカラムの事。
二種類ある。主キーと外部キー。

主キー

テーブルの中で他のレコードとの区別をつける識別子となるカラム。同じ主キーの値を持つレコードがテーブル内に存在してはいけない。
以下の生徒テーブルのidカラムが主キーになる。このとき、鈴木次郎さんのレコードのidが1であってはいけない。
a.png

外部キー

異なるテーブルのレコードと関係性(リレーション)を持つ場合に必要なカラム。主キー同様に識別子の役割を持ちますが、他のテーブルのレコードを識別するために使う。
a.png
上記は生徒テーブル。下記は成績テーブル。
a.png
成績テーブルのidは主キー。その他にstudent_idという属性が存在し、これが外部キーに当たる。

制約

特定のデータの保存を許さないためのバリデーション。
※バリデーションとはデータを登録する際に、一定の制約をかけること。空のデータを許可しないなど。

制約の種類

主な種類は4つ。
NOT NULL制約
一意性制約
主キー制約
外部キー制約

NOT NULL制約

カラムに設定する制約。そのカラムの値にはNULL(空の値)を入れることができなくなる。絶対に値が必要のあるカラムに対して使う制約。
a.png
上記のようにnull:falseを設定する事で空のデータを許可しない。

一意性制約

一意性制約を設定したカラムには同じ値を設定できなくなる。
Railsでは、add_indexメソッドの中でunique: trueという引数を指定することで、一意性制約をかけるためのマイグレーションファイルを作成できる。
a.png
これでemailの値の重複を許可しなくなる。

主キー制約

主キーである属性値が必ず存在してかつ重複していないことを保証する制約。Railsでテーブルを作成する際、主キー制約は元々実装されている。Railsでは主キーはidカラムとして自動で作成される。つまりidカラムの値は重複しないようにできている。

外部キー制約

外部キーに対応するレコードが必ず存在することを保証する制約。例えばstudent_idが3のレコードを保存するためにはstudentsテーブルにidが3のレコードが存在していなくてはならない。
a.png

references型を使用することで、 user_id から_idを省略できる。
例えば、user_idで4を指定した場合、usersテーブルに4がなければ、外部キー制約でエラーが発生する。

インデックスでデータを高速化

インデックスはデータベースの機能の一つで、テーブル内のデータ検索を高速化することが可能。

実際にインデックスを設定

a.png
これでscoresテーブルのnameが高速化。
複数のカラムに対して実行したければ、以下のように記述。
add_index :テーブル名, [:カラム名, :カラム名]

第一正規化

第一正規形の原則は、テーブル内に繰り返されるレコード(例ではclassレコード)をなくすこと。
a.png

第三正規化

複数のレコードで重複している内容を別のテーブルとして管理すること
a.png
DBを設計する際の例
a.png
a.png

多対多

has_manyのthroughオプションを利用する。
a.png
上記のようなイメージ。

いよいよREADMEにDB設計

1.README編集のためのブランチを作成
a.png

a.png
作成後に指定したブランチに切り変わっているか確認。
2.READMEにデータベース設計を記述
テーブル名
カラム名
カラムの型
カラムのオプション(null false制約など)
アソシエーション
主に上記を記載。
3.プルリクエスト作成
プルリクエストを作成する前に確認することがある。ローカルファイルが全てリモート環境に全てプッシュされているかどうか。
1.git add.を実行し、変更をインデックスに追加する。
2.git commit -m "コミット"を実行しインデックスに追加されている変更修正をコミットする。
3.次にプッシュをする。git push origin #作成したブランチ名
以上の手順で全てのファイルをリモートにプッシュできた。
※GitHubDesktopでも同じことができる。
4.プルリクエストを作成
a.png
githubのアプリケーションの名前(ここではsample)をクリック
次にbranchというタブをクリックする。その後作業中のブランチの名前をクリックする。ブランチ名が切り替わり、隣にあるnew pullrequestを押す。そうするとnew pullrequestの作成ページに遷移する。
あ.png
以下がnew pullrequestの作成ページ
a.png
そこで結合先のブランチが正しいか確認する。masterブランチにマージする場合は、masterになっているかどうか。最後に右端にある緑色のボタンをクリックする。

LGTMになった後

1.GitHubのサイト(リモートリポジトリ)で行うこと
・Merge pull requestのボタンを押してマージを実行する
masterブランチになっているか確認。
Delete branchでいらなくなったブランチを削除することも可能。
トップに戻ってbranchを選択してマージされているか確認

GitHub Desktopで行うこと
・Current Branchを「Master」に変更する
※ターミナルでgit checkout master等でmasterブランチに切りかえれる。git pullでマスターブランチを更新。これでローカル環境にもマージしたのが反映。
・ Fetch originをクリックする
・上記のボタンが「Pull origin」に変わるのでクリックする
・「History」をクリックして表示を変更する
・Merge pull requestが1つ増え、その中に最新の変更が反映していることを確認する

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む