ほげほげほげほ

ただのメモ置き場です。基本的にプログラム関連のメモ。

Ruby on Railsでサブクエリ(副問合せ)を書く

Rubyスキルがほぼ0なのですが、Rubyでサブクエリを書く必要があったので備忘録として…

どんなクエリ書きたいの?

select * from hoge_hoge where hoge_id in (select hoge_id from hoge_hoge where type = 'Hoge' and hoge_aaa_id = 100) and type = 'Hogeee';

というクエリですね。
hoge_hogeテーブルからhoge_idを取ってきて、取ってきたhoge_idHogeeeタイプを元にhoge_hogeテーブルからデータを取得するクエリです.

説明すると結構ややこしいですね!  
2回クエリ走らせりゃええやんけ と思う方もいるかと思いますが(自分もそう)
キャッシュ使ってないシステムなのでクエリ発行回数を減らしたくて・・・
キャッシュ入れりゃええやんけ と思う方が大多数だと思いますが(自分もそう)
とりあえず、まぁそんな感じのクエリを書く必要があったできたわけです。

どうやるのか?

載ってありました. http://tokyo.supersoftware.co.jp/code/7546

つまり、こういうことらしい。

hoge_ids = HogeHoge.where(type: 'Hoge').and(hoge_aaa_id: 1200)
hoge = HogeHoge.where(hoge_id: hoge_ids).and(type: 'Hogeee')

え?ちょっと待って。これ2回クエリ走らせてない?

と、書きながら思いました。しかし、log/development.logを見てみると...

SELECT `hogehoge`.* FROM `hoge_hoge` WHERE `hogehoge`.`hoge_id` IN (SELECT `hogehoge`.`hoge_id` FROM `hoge_hoge` WHERE `hogehoge`.`type` = 'Hoge' AND `hogehoge`.`hoge_aaa_id` = 100) AND `hogehoge`.`type` = 'Hogeee';

意図したクエリになっていました。。。不思議。。。

おわりに

Ruby初心者、ActiveRecord初心者なので内部でどうなってるのか調査しないといけないですが、
なんとかできました。
これってどういう仕組なんですかね?次くらいに調べたいと思います。。。

それでは〜

golangのテストについて

Go言語を調べている間、いろいろなライブラリとかサンプルコードとか見てるとテストファイルを同じ階層に入れていました。 goはそういうお作法なのかと思い今回実験してみました。

今回のぎもん

こんな感じになってる構成がおおいけど、同じ階層にテストファイルをいれないといけないのか

▶ tree
.
├── controller
│   ├── hogehoge.go
│   ├── hogehoge_test.go

試してみた

フォルダ構成

▶ tree
.
├── controller
│   └── hogehoge.go
├── test
│   └── hogehoge_test.go

実装

import (
    "fmt"
    "testing"
    "../controller"
)

func TestHogehoge(t *testing.T) {
    count := 1
    for i := 0; i < count; i++ {
        key := fmt.Sprintf("key_%d", i)
        value := fmt.Sprintf("value_%d", i)     
        println("---------------------")
        controller.Hogehoge(key, value)
    }
}

実行結果

---------------------  ← hogehoge_test.go での出力
/////////////////////  ← hogehoge.go での出力
PASS
ok      _/hogehogehoge/test 0.106s

結果

testファイルを同じ階層にいれないといけないということはありませんでした。
しかし、同じ階層(package)内に入れないとpublic function以外参照できないので同じ階層に入れている様でした。
ちょっとどうにかしたいけどこれはしょうがないんですね…うーん。

それでは〜

Goのフレームワークを調べてみた

前回、前々回はbeego, echo と実際触って見ましたが、全部触ると時間がかかるので今回はGoで使えるその他ウェブアプリケーションフレームワークを何点か調べてみました。

Goで使えるウェブフレームワーク

iris

特徴

  • 超高速webフレームワーク
  • すごくシンプル
  • githubで公開している速度の数値は良さげ

しかし、ぐぐると権利問題やらこのフレームワークだけは選んではいけないみたいなことが書かれていました…

Goji

特徴

  • 軽量フレームワーク
  • echoと同じ匂いを感じる
  • Middlewareという仕組みがあり、リクエスト毎に必要な処理を集約することが可能
  • Sinatra的高度な正規表現マッチも使えるルータ
  • 簡単に実装出来るミドルウェアスタック
  • グレースフルシャットダウン
  • マルチプレクサ
  • Goji自体にはほとんど機能はないらしい

ドキュメント

  • 英語でならある。日本語はちらほら

Gin

特徴

ドキュメント

  • 情報は豊富。日本語でも多い印象

Revel

特徴

  • JavaのPlay Frameworkを参考に設計されたフルスタックフレームワーク
  • MVCフレームワーク
  • Webアプリケーション開発に必要な機能が一式揃ってる
  • ホットリロード対応
  • Go Templatesを使用
  • ルーティング、 パラメータ解析、 検証、 セッション/フラッシュ、 テンプレート作成、 キャッシング、 ジョブ実行、 テストフレームワーク、国際化
  • devモードでロギングが可能
  • Filters機能がある(SpringのFileterと同じような機能。認証とかに便利)
  • ライブラリ管理はdepを使用

ドキュメント

公式サイトのドキュメントは豊富。
日本語の情報も色々とある。

Martini

特徴

  • モジュール形式のWebアプリケーションサービスフレームワーク
  • PythonのFlaskのような感覚でコーディング
  • ハンドラ、ルーティング、サービス、静的ファイル配信、ミドルウェアハンドラ(Filterのようなもの)、認証はある

ドキュメント

日本語の公式ドキュメントあるにはあるが、量が少ない。はじめのチュートリアルレベル。
深い情報は英語になっている。しかし知りたいことの情報は豊富にありそう。

Gorilla

特徴

  • httpパッケージなどが揃っている
  • パッケージを組み合わせて利用するイメージ

ドキュメントある?

  • 日本語のドキュメントはあまりない。

ここからはマイクロフレームワーク

go-kit

特徴

  • microservices用ツールキット
  • tracing packageで、ログなのか追いやすい
  • DNSやconsulなど様々なプラグインを追加可能
  • Go-MicroやGizmoでも使われている
  • go-kit用のCLIあり(kit)
  • github star 12841(2019/03/11)
  • パッケージを組み合わせて利用するイメージ
  • 他のWAFにある軽量系のFWとあまり違いがわからない…

ドキュメント

  • 日本語に訳された公式Docは無い
  • qiitaとかの記事は結構見かける

Go Micro

特徴

  • 一通り思想から理解しなければ使いこなすのが難しそう
  • RPCベースのライブラリ(proto-rpc. json-rpc)
  • microservices運用のpacageが一通り揃っている
    • サービスをトレースするためのtrace-web
    • Webサービスを展開するためのgo-web
    • モニタリングツールのmonitor-web

ドキュメント

  • 調べてない

goa

特徴

  • github star 3003(2019/03/11)
  • ビズネスロジックオンリーの開発ができる
  • swagger documentの自動生成
  • CLI goagen

ドキュメント

gizmo

特徴

  • NY Timesが開発したFW

ドキュメント

  • 調べてない

とまぁいろいろありまして、自分が今回使おうと思ったのは Gin でした。
Pairsでの実例もありなかなか良さげなので Gin を使おうかなと思います。

それでは!

Go echo を使ってみる

f:id:kusobutayarow:20190323205109j:plain

特徴

  • リソース量に比例して全体のスループットが向上する設計
  • スケーラブルなフレームワークまたREST API向け
  • Ginを超える程の高速処理
  • RESTAPI向けに最適化されているフレームワーク
  • スタンダードなHTTPサーバで動作する
  • URI(PATH)単位でなにか処理を入れられる
  • ミドルウェアに対して拡張性がある。
  • urlごとにログ出したり出さなかったりすることできる
  • JSON,XML,フォームペイロードなどへのデータバインディングのサポートがある
  • HTTPの様々なレスポンスを送るための使いやすい関数をサポート
  • どんなテンプレートエンジンででもテンプレートレンダリングできる
  • Loggerに対して任意の定義を行える

install

go get github.com/labstack/echo
go get github.com/dgrijalva/jwt-go

./main.go

goって package が main のやつがないと起動しないんですね…

package main

import (
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
    "./handler"
)

func main() {
    e := echo.New()

    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    e.GET("/", handler.MainPage())

    e.Start(":8080")
}

/handler/handler.go

package handler

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

func MainPage() echo.HandlerFunc {
    return func(c echo.Context) error {
        return c.String(http.StatusOK, "Hello World!!!!")
    }
}

起動

起動するとこんな感じ. echoって感じですね.

▶ go run main.go

   ____    __
  / __/___/ /  ___
 / _// __/ _ \/ _ \
/___/\__/_//_/\___/ v4.0.0
High performance, minimalist Go web framework
https://echo.labstack.com
____________________________________O/_______
                                    O\

アクセス

表示されます

▶ curl http://localhost:8080
Hello World!!!!%

ついでにDB周りも触って見る。

dbrというORMがあるらしい?

dbr

dbrって何ぞ?

golang の orm ですね。 サポートしているドライバーは下記の模様

今回は、というかいっつもMySQL使ってるのでMySQL使います。

dbr install

▶ go get -u github.com/gocraft/dbr

データ作成

mysql> create table user(
    -> id  varchar(36) not null,
    -> name varchar(50) not null,
    -> email varchar(128) not null,
    -> primary key(id));
Query OK, 0 rows affected (0.02 sec)

mysql> insert into user (id, name, email) values ('D7EEFCBD-CD03-4524-893A-4850D9828D57', 'kusobutayarow', 'kusobutayaro-w@hogehogehogeho.jp');
Query OK, 1 row affected (0.01 sec)

mysql> select * from user;
+------------------------------------------------+----------------+---------------------------------------+
| id                                                                               | name                  | email                                                          |
+------------------------------------------------+----------------+---------------------------------------+
| D7EEFCBD-CD03-4524-893A-4850D9828D57 | kusobutayarow | kusobutayaro-w@hogehogehogeho.jp  |
+------------------------------------------------+----------------+---------------------------------------+
1 row in set (0.00 sec)

main.go 修正

package main

import (
    "github.com/gocraft/dbr"
    "github.com/labstack/echo"
    "github.com/labstack/echo/middleware"
    "./handler"
    _ "github.com/go-sql-driver/mysql"
    "net/http"
)

type (
    user struct {
        ID    string `db:"id"`
        Email string `db:"email"`
        Name  string `db:"name"`
    }
)

var (
    tableName = "user"
    seq   = 1
    conn, _ = dbr.Open("mysql", "root:@tcp(127.0.0.1:3306)/hogehogehogeho", nil)
    sess = conn.NewSession(nil)
)


func selectUser(c echo.Context) error {
    var m user
    name := c.Param("name")
    sess.Select("*").From(tableName).Where("name = ?", name).Load(&m)
    return c.JSON(http.StatusOK, m)
}

func main() {
    e := echo.New()
    e.Use(middleware.Logger())
    e.Use(middleware.Recover())

    e.GET("/", handler.MainPage())
    e.GET("/user/:name", selectUser)

    e.Start(":8080")
}

実行.

取得成功

▶ curl http://localhost:8080/user/kusobutayarow | jq
{
  "ID": "D7EEFCBD-CD03-4524-893A-4850D9828D57",
  "Email": "kusobutayaro-w@hogehogehogeho.jp",
  "Name": "kusobutayarow"
}

無事取れた. ※jqコマンドに関してはこちら > https://stedolan.github.io/jq/

所感

  • 最初にbeegoを触ったからか、すごいシンプルだなぁという印象を受けました。

参考: refer

beego使ってみた

なんでbeego?

  • なんとなくGo言語使ってみようとなり、どのFWが良いか選定するため
  • echo, beego, ginはやってみた。

特徴

  • Webアプリ用のフルスタックフレームワーク
  • Rubyっぽい
  • beego用のORMがある
  • beego用のCLIツール(bee)がある
  • プロジェクト作るとcontrollerディレクトリ等がすでにあり、作り方がある程度決まっている
  • Rubyっぽい
  • scaffoldを使ってのコード自動生成ができる
  • MVCモデル
  • Viewを必要としないAPIモードあり
  • i18n対応
  • LiveReloadあり
  • ハチがかわいい f:id:kusobutayarow:20190323204407p:plain

CLIツール

beego専用のCLIツールがある.

bee new {PROJECT_NAME}
bee api {PROJECT_NAME}

LiveReload

beeで起動していればファイル編集時に検知して再コンパイルしてくれる。嬉しい.

Swaggerが自動生成される

実装したあとのAPIのテストが楽かと思った. が、本来の開発だと先にSwaggerを作らないといけないと思う.

起動

bee run -downdoc=true -gendoc=true

DB接続など

beego用のormがある.

実装

apiモード

  • bee api {PROJECT_NAME}でプロジェクトを作成するとapiモードになる。
  • apiモードで作るとviewが無い状態でテンプレが作られる
  • デフォルトで作ったやつはRooterの設定がアレなのでアクセスしても404になった.
    • 修正が必要

apiモードのRooter実装

http://localhost:8080/user/:name

func init() {
    ns := beego.NewNamespace("/api",
        beego.NSNamespace("/object",
            beego.NSInclude(
                &controllers.ObjectController{},
            ),
        ),
        beego.NSNamespace("/user",
            beego.NSRouter("/:name", &controllers.UserController{}), <- ここ
        ),
    )
    beego.AddNamespace(ns)
}

bee/orm

models

structをmodelに書いてたら起動時にテーブル作ってくれた.Rubyっぽい

create table `user`
-- --------------------------------------------------
--  Table Structure for `_/hogehogehogeho/models.User`
-- --------------------------------------------------
CREATE TABLE IF NOT EXISTS `user` (
    `id` varchar(255) NOT NULL PRIMARY KEY,
    `name` varchar(255) NOT NULL DEFAULT '' ,
    `email` varchar(255) NOT NULL DEFAULT ''
) ENGINE=InnoDB;

model内実装,

func init() {
    orm.RegisterModel(new(User))
}

type User struct {
    Id      string `orm:"pk" json:"id"`
    Name    string `json:"name"`
    Email   string `json:"email"`
}

func GetUserByName(name string) (u *User, err error) {
    o := orm.NewOrm()
    _ = o.Read(&name, "Name")
    if u, ok := UserList[name]; ok {
        return u, nil
    }
    return nil, errors.New("User not exists")
}

main

import (
    ...
    "github.com/astaxie/beego/orm"
)

func init() {
    ...

    dataSource := "root:@tcp(127.0.0.1:3306)/hogehogehogeho?charset=utf8"

    orm.RegisterDataBase("default", "mysql", dataSource, 30)
    orm.SetMaxIdleConns("default", 100)
    orm.SetMaxOpenConns("default", 100)

    ...
}

実行

これで取れると.

curl http://localhost:8080/user/DBにある適当な名前

所感

  • フレームワークディレクトリ構成が決まっているのはこれくらいじゃないでしょうか. あらかじめ決めてないとカオスになるので、これが決まっているのは嬉しいかも.
  • LiveReloadが嬉しい。コンパイルもはやい
  • RubyからGoに乗り換えるのであればこのFWなのかなと思いました。

iTerm2が起動しなくなった場合の対処

やりかた

bashに戻す

sudo chsh -s /bin/bash

再インストール

brew reinstall zsh && brew unlink zsh && brew link zsh

起動しないときは焦りましたが、自分の場合はこれで起動できました。

Thanks

bashへの戻し方
再インストールコマンド