日頃の行い

個人的な日頃の行いをつらつら書いてます\\\\ ٩( 'ω' )و ////

今更ながらRetryするGolangのpackage書いてみた

何番煎じかわからないですが、かいてみました。
呼び出し方がretry.Retryってなってなんかびみょ・・・

github.com

使い方

retry.Retryの第三引数の実装にリトライがありえる処理を書く感じです。
errorが返った場合にはretry.Retryの第一引数に与えた回数分だけリトライします。
リトライの間の待ち時間は第二引数で設定できます。
第二引数の関数は、今のリトライ回数と前回の結果を受け取って待ち時間を返す関数にする必要があります。
retry.Retryの返り値は、第三引数に与えた関数の返り値と一致します。
第一引数分リトライをしてもerrorが返った場合はerrorが返ります。

main.go

package main

import (
    "fmt"
    "log"
    "time"

    "github.com/ara-ta3/retry"
)

func main() {
    c := someClient{
        n: 0,
    }
    // retry.Retry returns the return values of "func" at third args
    r, err := retry.Retry(
        10,
        func(n int, r interface{}) time.Duration {
            // waiting time
            return 0 * time.Second
        },
        func() (interface{}, error) {
            // your codes
            // if this returns error, this will retry this "func"
            return c.DoSomething()
        },
    )
    if err != nil {
        log.Fatalln(err)
    }

    res, ok := r.(*result)

    if !ok {
        log.Fatalf("result cannot be cast to Result struct. r: %+v", r)
    }

    fmt.Printf("%+v\n", res)
}

type someClient struct {
    n int
}

type result struct {
    Message string
}

func (c *someClient) DoSomething() (*result, error) {
    if c.n >= 5 {
        return &result{
            Message: fmt.Sprintf("some message. n: %d", c.n),
        }, nil
    }
    c.n++
    fmt.Printf("count: %d\n", c.n)
    return nil, fmt.Errorf("return error for retry")
}

これを実行すると下記のような感じになります。
待ち時間は0秒にしているので一瞬で終わります。

$time go run main.go
count: 1
count: 2
count: 3
count: 4
count: 5
&{Message:some message. n: 5}
go run main.go  0.14s user 0.08s system 98% cpu 0.231 total

感想

  • retry色んな所でやりたくなったりするので、書いてみました。
  • 便利なので使っていきたい。

GolangのsqlxでIN句が使いたかったので調べたメモ

Golangでバッチを最近書いてるんですが、
sqlxを使っていて、
IN句のprepared statmentどうやって使うんだろうと思ったので調べたときのメモです。

準備

とても適当なテーブルを用意します。
idとnameをカラムにもつhogeというテーブルです。
3件適当なデータを突っ込んでおきます。

((none))> use test;
Database changed
(test)> create table hoge (id int auto_increment, name varchar(255), primary key (id));
Query OK, 0 rows affected (0.03 sec)

(test)> insert into hoge (name) values ("hoge");
Query OK, 1 row affected (0.01 sec)

(test)> insert into hoge (name) values ("fuga");
Query OK, 1 row affected (0.00 sec)

(test)> insert into hoge (name) values ("piyo");
Query OK, 1 row affected (0.00 sec)

(test)> select * from hoge;
+----+------+
| id | name |
+----+------+
|  1 | hoge |
|  2 | fuga |
|  3 | piyo |
+----+------+
3 rows in set (0.00 sec)

書いたコード

idが2, 3のレコードを取ってきてstructにbindさせています。
structにbindさせるやつはこのへんで試したりしました。

golangでSQLを投げて返ってきた結果をstructにbindしたい - 日頃の行い

書いたコードはこんな感じです。
この辺のドキュメントを参考にしました。

Illustrated Guide to SQLX

gist.github.com

動いたっぽい。

感想

  • テーブルからIN句を使ってデータを取ってstructにbindできました。
  • IN句も使えそう。
  • sqlx.Inを使うと第二引数に与えたsliceの長さ分だけ ? を増やしたクエリを返してくれるみたいですね。
  • そのクエリを利用してSelectやらRebindやらすればいいみたいです。便利。

PHPStormを使っている時にCannot resolve file 'xxxxx.js' と怒られる

PHPStormでテンプレートをいじっている時に、
scriptファイルのパスを指定したらタイトルの通りに怒られてしまったのでその対処方法を探しました。
イメージはこんな感じです。

f:id:arata3da4:20170523201229p:plain

検証に使ったレポジトリはこちらです。

github.com

とりあえず Cannot resolve file phpstorm js ってぐぐってみたらここにたどり着きました。

stackoverflow.com

PHPStormはデフォルトだとルートディレクトリから見たパスにファイルがなかったら
Cannot resolve file … と言うみたいですね。
確かにscriptのパスを /js/index.js からルートからみたパスの
/web/js/index.js に変えたらなにもしなくても解決できるようになりました。

# ディレクトリ構成
$tree
.
├── LICENSE
├── Makefile
├── README.md
├── composer.json
├── composer.lock
├── composer.phar
├── templates
│   └── index.html
└── web
    ├── index.php
    └── js
        └── index.js

当然、他にも方法はあって、
今回で言えば web ディレクトリを Resource Root として扱うようにPHPStormに設定すればよいみたいです。
Resource Root にしたいディレクトリを右クリック -> Mark Directory as -> Resource Root で設定できるみたいです。

f:id:arata3da4:20170523202007p:plain

設定したら、解決されるようになりました。

f:id:arata3da4:20170523202131p:plain

しかも、(macなら)cmd + クリックでファイルまで辿れるようになったので、
ファイルが大量になったりしたときにはとても便利そうですね。

感想

  • ファイルまで辿れるのとても便利
  • 最高に便利になったので絶対やったほうがいいと思いました。
  • もうPHPStormじゃないとphp書けない体になりました。

golangでSQLを投げて返ってきた結果をstructにbindしたい

クエリを投げた結果のデータをいい感じのstructにbindしたいなと思ってやり方を探してました。
いい感じのないかなと思って探したら下記のライブラリを見つけたのでそれを触ったメモです。

github.com

テーブル準備

適当なテーブルを作ります。
idとnameしかない適当なテーブルです。

((none))> use test;
Database changed
(test)> show tables;
Empty set (0.00 sec)

(test)> create table hoge(id int primary key, name varchar(255));
Query OK, 0 rows affected (0.01 sec)

(test)> show tables;
+----------------+
| Tables_in_test |
+----------------+
| hoge           |
+----------------+
1 row in set (0.00 sec)

(test)> desc hoge;
+-------+--------------+------+-----+---------+-------+
| Field | Type         | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+-------+
| id    | int(11)      | NO   | PRI | NULL    |       |
| name  | varchar(255) | YES  |     | NULL    |       |
+-------+--------------+------+-----+---------+-------+
2 rows in set (0.01 sec)

(test)> insert into hoge values(1, "aaa");
Query OK, 1 row affected (0.00 sec)

(test)> insert into hoge values(2, "bbb");
Query OK, 1 row affected (0.00 sec)

(test)> select * from hoge;
+----+------+
| id | name |
+----+------+
|  1 | aaa  |
|  2 | bbb  |
+----+------+
2 rows in set (0.00 sec)

コードを書く

事前に go get github.com/jmoiron/sqlx を実行してます。

main.go

package main

import (
    "fmt"

    _ "github.com/go-sql-driver/mysql"
    "github.com/jmoiron/sqlx"
)

type Hoge struct {
    ID   int    `db:"id"`
    Name string `db:"name"`
}

func main() {

    db, err := sqlx.Connect(
        "mysql",
        fmt.Sprintf(
            "%s:%s@tcp(%s:%s)/%s",
            "root",
            "",
            "localhost",
            "3306",
            "test",
        ),
    )
    if err != nil {
        panic(err)
    }

    hs := []Hoge{}
    err = db.Select(&hs, "SELECT id, name FROM hoge")
    if err != nil {
        panic(err)
    }

    fmt.Printf("%+v\n", hs)
}

実行してみる

$ls -l
total 8
-rw-r--r--  1 arata  staff  486  5 18 21:48 main.go
$go run main.go
[{ID:1 Name:aaa} {ID:2 Name:bbb}]

ちゃんとデータがbindされてました。
便利。

PHPのビルトインサーバっぽく開発しつつhttpsアクセスをしたくなったときにしたこと

最近Mockアプリケーションを作るときにAPIサーバを一旦雑に作りたくて、
phpを使っているんですが、ローカルの開発環境をhttpsにしているため、XMLHttpRequestを投げようとした際に、

Mixed Content: The page at 'https://xxx' was loaded over HTTPS, but requested an insecure XMLHttpRequest endpoint 'http://yyy'. This request has been blocked; the content must be served over HTTPS.

と怒られました。
何個か方法はあると思うんですが、phpのビルトインサーバで出来ないのかなぁ
とふと思って検索したらライブラリがあったのでそれを使ってみました。
その時のメモです。

使ったライブラリはこちらです。

github.com

雑に検証したファイル群はこちらです。

gist.github.com

実際に動かしてみました。
hyper-run -S localhost:8080 -s localhost:8081 ./index.php を実行して、curlでアクセスしただけです。

$make server
./composer.phar install
Loading composer repositories with package information
Installing dependencies (including require-dev) from lock file
Nothing to install or update
Generating autoload files
./vendor/bin/hyper-run -S localhost:8080 -s localhost:8081 ./index.php
[Tue May 16 19:40:20 2017] 127.0.0.1:50957 [200]: /
[Tue May 16 19:40:27 2017] 127.0.0.1:50960 [200]: /

...
# 別ターミナル
$curl -i http://localhost:8080
HTTP/1.1 200 OK
Host: localhost:8080
Date: Tue, 16 May 2017 10:40:38 +0000
Connection: close
X-Powered-By: PHP/7.1.2
Content-type: text/html; charset=UTF-8

Hello World

$curl -i --insecure https://localhost:8081
HTTP/1.1 200 OK
Host: localhost:8081
Date: Tue, 16 May 2017 10:40:33 +0000
Connection: close
X-Powered-By: PHP/7.1.2
Content-type: text/html; charset=UTF-8

Hello World

ビルトインサーバっぽく使えて、httpsでつなげて便利でした。
nginxやapache立てるのもめんどいくらい初期のときには使っていきたい。

Slack内でActiveなユーザ数をMackerelのサービスメトリックで表示してみた

コミュニティでSlackを使ってるんですが、どれくらいの人が普段見てるんだろうと思って可視化してみました。
計測したい数は今User Presenceがactiveなユーザ数です。

api.slack.com

Mackerelのサービスメトリックがカジュアルに使えてとても便利なので今回はそれを利用しました。

mackerel.io

作ったコードはこんな感じです。
とても素朴なコードです。
これをcronで毎分動かすことで、毎分activeなユーザ数をMackerelのサービスメトリックに投げています。
Slackのライブラリを使おうとしたのですが、Presenceがなぜか取れなかったりしたのでとりあえず自前で書きました。

package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "time"

    "github.com/joho/godotenv"
    mkr "github.com/mackerelio/mackerel-client-go"
)

type response struct {
    OK      bool   `json:"ok"`
    Members []user `json:"members"`
}

type user struct {
    ID                string      `json:"id"`
    Name              string      `json:"name"`
    Deleted           bool        `json:"deleted"`
    Color             string      `json:"color"`
    RealName          string      `json:"real_name"`
    TZ                string      `json:"tz,omitempty"`
    TZLabel           string      `json:"tz_label"`
    TZOffset          int         `json:"tz_offset"`
    Profile           interface{} `json:"profile"`
    IsBot             bool        `json:"is_bot"`
    IsAdmin           bool        `json:"is_admin"`
    IsOwner           bool        `json:"is_owner"`
    IsPrimaryOwner    bool        `json:"is_primary_owner"`
    IsRestricted      bool        `json:"is_restricted"`
    IsUltraRestricted bool        `json:"is_ultra_restricted"`
    Has2FA            bool        `json:"has_2fa"`
    HasFiles          bool        `json:"has_files"`
    Presence          string      `json:"presence"`
}

func main() {
    err := godotenv.Load()
    if err != nil {
        log.Fatal(err.Error())
    }
    client := mkr.NewClient(os.Getenv("MACKEREL_TOKEN"))
    token := os.Getenv("SLACK_TOKEN")
    resp, err := http.Get(
        fmt.Sprintf("https://slack.com/api/users.list?token=%s&presence=1", token),
    )
    if err != nil {
        log.Fatal(err.Error())
    }
    body, _ := ioutil.ReadAll(resp.Body)
    defer resp.Body.Close()
    r := response{}
    json.Unmarshal(body, &r)
    c := 0
    for _, u := range r.Members {
        if u.Presence == "active" {
            c++
        }
    }
    err = client.PostServiceMetricValues("Dark", []*mkr.MetricValue{
        &mkr.MetricValue{
            Name:  "dark.slack.active_rate",
            Time:  time.Now().Unix(),
            Value: c,
        },
    })
    if err != nil {
        log.Fatal(err.Error())
    }
}

github.com

実際の画面がこんな感じでした。
深夜帯に少しいるのはbotも数えてしまっているからかもしれないので、
botを除く処理を挟もうとこの記事を書いてる際に思いました。
ブログ書くのは整理にもなって良いですね。

f:id:arata3da4:20170324185031p:plain

まとめ

2016年振り返りと2017年どうしようかなみたいな雑記

なんとなく振り返って、来年の自分が一年前に何を思っていたかをまとめるだけの記事です。
去年の日記みてとりあえず大変な一年だったとか言ってるけど結局今年も大変だった気がする。
人生がカラフルに色づいていて楽しい😇

arata.hatenadiary.com

■去年書いてた今年の抱負

  • 4コマんま動かしたい
  • Goについて強くなりたい
  • Scalaについて強くなりたい
  • JavaScriptのBabel周りに慣れたい
  • 本をたくさん読もう

4コマんまは諦めちゃったなw
Go, Scala, Babel周り慣れた気がする。
Scalaはもうちょっと強くなりたい。
本はすごいH本, Pythonからはじめる数学入門はちゃんと読んだけど他は読み終わらなかったなぁ
来年も読んでいきたい。

Darkでやったイベント

Dark のイベントで参加したものあたり。
今まで通り最低限LT会は頑張るとして、結構もくもく会で新しい人が来てくれるので
publicにちょくちょくやっていきたいなぁと思った。
色々なイベントは雑な気持ちでカジュアルにやっていきたい。

  • もくもく会
    • 16回
      • connpassで募集したのが3回かな
    • 去年が12回らしいので頑張ったなという感じ。
    • 来年もやっていきたい
    • 12/24に行ったのは20/24(≒ 83%)の参加率ですごかった
  • LT会
    • 3, 6, 9月に開催しました
    • 12月に開催失敗したので1月に開催予定です
  • 野良イベント

その他

■ 参加したイベント

  • プライベート
    • ScalaMatsuri
    • PyLadies Meetup(手伝い) ✕ 2
    • Hadoop / Spark Conference Japan 2016
    • Jagmo
    • Gotandajs #3, #4
    • 雑兵Meetup
    • PyCon JP
    • Mackerel User Group Meetup
    • 秋のJavaScript祭り
    • RustのLT会
  • 会社関係のイベント
    • CTOからの挑戦状1st, 2nd
    • Treasure 2016
    • ギークフェスタ(夏)
    • サポーターズ1 on 1イベント n回

去年より減った気がする
Darkのイベントが増えたせいか、やる気がなくなったせいか
来年はまたphp触りそうなのでphp勉強会とか行きたいなぁ
あとCTOからの挑戦状の記事かけてよかった。

techlog.voyagegroup.com

■ 見たアニメ

グリムガル良かった
リゼロ良かった。レム・・・小説読まないとなぁー
デュラララもよかった。池袋楽しそう。
ReLifeのEDがクリティカルヒットするのでやばい

■ 弾けるようになった曲

電子ピアノ買ったので結構弾いてたらなんだかんだ弾けるようになって楽しい。
今は、ドラクエの序曲、海の見える街、天体観測、エピローグ ~親しき仲間へ~ を練習中

感想と抱負

voyarockのおかげでたくさん色んな曲弾けるようになってよかった。voyarock最高!
DarkでISUCONやらSECCONやら色んなイベントできてよかった!Dark最高!
来年はたくさん色んな曲を弾けるようにしたいなぁ
目標は楽譜見ずに10曲
Darkは内輪な人を増やしたいなぁ。
入り込んで来やすい環境作りたい。

■来年の抱負

  • 本を3冊は完走したい
    • FP in Scala
    • すごいE本
    • ゼロから作るDeep Learning
    • DDDの鈍器っぽいやつ
    • SICP
  • ピアノで10曲暗譜
  • Darkのpublicなもくもく会の回数を増やす
    • 今年は3回だったので5回くらいは最低限やりたいなぁ

来年は笑いながら未来の話をしていこうと思いまっす。
今年もありがとうございました。