きょこみのーと

技術に関係ないほうのブログ

Twitterでお気に入りした画像をランダムで返すGAE/Goアプリをつくった

ランダムで返す部分が雑にキャッシュしてるのはご愛嬌・・・

https://koha-api.appspot.com/v1/api/image

画像はこちらを利用させていただきました。(経験値先生最高です!さっちん絵がほしかった・・・)

注意!!!営利目的でのご利用はやめてくださいね!!!

お気に入りは適当にアカウント作って作りました。

twitter.com

GAE/Goの実装の方ですが、かなり雑ですが以下になります。

github.com

使い方はREADME.mdに書きましたが、 環境変数だけで一応どのアカウントにも対応できるようにしてます。

需要があれば、もうちょっとなんとかします!

主な使い方ですが、LGTMとかみたいな感じにAPIクライアント作ってしまえばいいかと思います。 自分はGo製のSlackbotでAPIクライアントを実装しました。

github.com

シルバーウィークにいよいよUnity入門した

はじめに

最近Goばかり書いてるのですが、今作ろうとしてるゲームをCocos2d-xで作るのは流石にもう辛いので、コレを期にUnity入門することにしました。

シルバーウィークの月〜水の3日間でサクッと1冊本読み切ってExampleを2,3個作るとこまでやりましたので、その所感とか。

準備したもの

細かいミスだけど、正誤表あるので先に見ておくと良いです。 今の最新版のUnity5.2で挫折しないで最後までいけたので、すごいと思います。 自分みたいなガチ入門者向けかと。

正誤表はこちら => http://www.socym.co.jp/support/s-967#ttlWrong

LOGICOOL ワイヤレストラックボール M570t

LOGICOOL ワイヤレストラックボール M570t

トラックパッド派なんだけど、流石に三次元の動きをトラックパッドで操作するの限界を感じて以前Unity挫折したので先に買っておいた。 買ってよかった・・・普通のマウスでも良かったかもしれないけどUnityで全然使えました。

環境

  • Unityv5.2.0f3

はるか昔にUnity4になる前に触った頃に比べてサクサク動いてる気もするし、色々わかりやすくなった。 買った書籍がよかったのかもしれないけど、ストレスはほとんどなかった。

あと、マウス買ってよかった。以前にMacのタッチパッドでUnity操作しようとしたの無謀だった。 クリックしたまま引っ張るとか、回すとかかなりのテクニックがいるのでおとなしくマウスにしよう。

トラックボールだとマウス移動自体がないので場所も取らなくて楽だった。

Editorやばい

  • Xamarin Studio: 日本語入力できるけど、日本語変換後の入力がスルーされることがあり結構ストレスだが、軽さや補完など平均点は維持してるのでコレに決めた

困ったこと

「変換が」を入力したつもりが「変換あ」になる

「変換」の時点でEnterをして確定してから「が」を入力すればいいのだが、「変換」の確定を「g」の入力でやる癖がもう抜けなくて辛い。(これ文章で説明してわかるのかな・・・)

他にも試したEditorたち

  • MonoDevelop: まだ日本語入力できなくて消耗してるので没
  • ReSharper: JetBrains製品好きとしては・・・と思ったがまさかのMac版なくて/(^o^)\
  • VisualStudioCode: 一番ナウい感じなんだけど、Unity5.2での補完のきかせ方が分からず挫折
  • MonoDevelop: 惜しい所まできていたが、Unityちゃんの描画のためか重くなり没

色々教わったこと・学んだこと

こんな感じでやってた

  • まず章を1つざーっと目を通す
  • 何作るかと何するかをざっくり把握してから、実際にExample作成の手を動かす
  • とにかく作りきる
  • 気が向いたら、ちょこっと改造する

こんな感じにキャラをUnityちゃんにしてみたり。

本読みながら、ポチポチ入力してくのもいいけど、結局図の方ばっかりみちゃって説明読み飛ばしちゃう気がする・・・(図だけで十分作れてしまうのもすごい)

なので、ざっと1回読んでから戻って作って行く感じでやっていくと、頭の中で整理もできて丁度良かったです。

C#なんとなく書ける

なんとなく書けるの良くない。もうちょいUnity自体の理解が深まったら、改めてC#も入門する。

(学生の時になんとなくC#書いてたから、軽く8年ぶりくらいか・・・)

所感

ようやくUnity入門してスタートに立てた感じ。Asset周りがなんか有料ばっかでゾッとしてるんだけど、まあ基本的な機能は揃ってるので、しばらくは手抜きしない方向でいこう・・・(素材周り以外は)

UnityちゃんかわいいよUnityちゃんかわいいよ

GitHubのREADME.mdに貼ったBadgeが更新されない時

なんかRefreshするタイミングとかミスすると更新されないときがあるので、手動でなんとかする方法。

こんなとき↓

f:id:kyokomi:20150823220925p:plain

↑のBadgeの画像URLをコピペして、こうする

$ curl -X PURGE https://camo.githubusercontent.com/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
{"status": "ok", "id": "xxx-xxxxxxxxxx-xx"}

そして、ブラウザを更新すると反映される。

f:id:kyokomi:20150823221033p:plain

参考

Aggressive image caching breaks image badges · Issue #224 · github/markup · GitHub

Goでlotteryという抽選ライブラリを作った

単なるmath/randのラッパーですが。

もしかしたら他にも似たようなやつあるかもしれませんが、見つからなかったので作りました。

github.com

指定した確率

20%の確率で〜みたいな処理をしたいときです。

lot := lottery.New(rand.New(rand.NewSource(time.Now().UnixNano())))

if lot.Lot(20) {
    // 20%の時の処理
}

抽選リストから1件抽選

以下のような複数の抽選対象から1件だけ抽選するときのinterfaceもあります。

  • A: 10%
  • B: 20%
  • C: 30%
  • D: 40%

Prob() intというインターフェースを実装すればOKです。

type DropItem struct {
    ItemName string
    DropProb int
}

func (d DropItem) Prob() int {
    return d.DropProb
}

var _ lottery.Interface = (*DropItem)(nil)

抽選はこんな感じにやる。

lot := lottery.New(rand.New(rand.NewSource(time.Now().UnixNano())))

// 抽選対象リスト
dropItems := []lottery.Interface{
    DropItem{ItemName: "エリクサ", DropProb: 10},      // 10%
    DropItem{ItemName: "エーテル", DropProb: 20},      // 20%
    DropItem{ItemName: "ポーション", DropProb: 30},    // 30%
    DropItem{ItemName: "ハズレ", DropProb: 40},       // 40%
}

// 抽選
lotIdx := lot.Lots(dropItems...)
if lotIdx == -1 {
    // errorです(抽選リストの指定ミスかと)
}

// 抽選結果のItem
dropItem := dropItems[lotIdx].(DropItem)

以下のテストコードも参考にしていただければと。

lottery/lottery_test.go at 2c0264f14a47bf1be830ae12d2c84a90ce9f1ffa · kyokomi/lottery · GitHub

200万回実行した結果は以下になります。

f:id:kyokomi:20150726150526p:plain

2015/07/26 15:51 追記

@tenntenn さんが「袋の中に球が3種類入っていてそれを順次取り出して行く」ような使い方を見つけてくれたので、紹介させていただきます。

ありがとうございます!

GoConでGoで作った拡張しやすいSlack botについてLTしてきた

拡張しやすいはず・・・

最初はkyokomi/gomaの話にしようかなと思ったけど、LT駆動開発キメたろうと思って、slack bot作ることにした。

LTであまり時間無く、ざくっと概要とデモ見せる感じで終わったので、exampleとか詳細を書こうと思います。

実際に実行するbotのサンプル

tokenの取得

slack botのtokenは以下で取得してください。

f:id:kyokomi:20150621211458p:plain

f:id:kyokomi:20150621211555p:plain

exampleを実行する

<token>は、一つ前の節で取得したもの。環境変数SLACK_BOT_TOKENでも可。

$ go get github.com/kyokomi/slackbot
$ cd $GOPATH/src/github.com/kyokomi/slackbot/example
$ go run main.go -token <token>

これで実行終わり。起動時のログはこんな感じ。

f:id:kyokomi:20150621212112p:plain

Slackで発言してみる。(botが居る部屋で)

f:id:kyokomi:20150621212144p:plain

echoプラグインが動いていることがわかる。

pluginの仕組み

_ importするだけでpluginを追加できているのは、この辺をみればわかる。

init()でplugins.AddPluginを読んでいる箇所

slackbot/echo.go at v1.1 · kyokomi/slackbot · GitHub

func init() {
    plugins.AddPlugin(pluginKey("naruhodoMessage"), NaruhodoMessage{})
}

SlackのMessage受信時に呼び出される処理している箇所

pluginsに突っ込まれたものを順番に呼び出している。

slackbot/plugin.go at v1.1 · kyokomi/slackbot · GitHub

func ExecPlugins(ctx context.Context, message string) {
    for _, p := range plugins {
        ok, m := p.CheckMessage(ctx, message)
        if !ok {
            continue
        }

        next := p.DoAction(ctx, m)
        if !next {
            break
        }
    }
}

実は、とても原始的な実装。

割とウケた「なるほどですぞ」プラグインのソースは以下だけ。(ウケてよかった・・・)

package naruhodo

import (
    "math/rand"
    "strings"
    "time"

    "github.com/kyokomi/slackbot/plugins"
    "golang.org/x/net/context"
)

type pluginKey string

var naruhodoList = []string{
    "なるほどなるほどですぞ!",
    "なるほど!",
    "なるほど?",
    "なーるほど!",
    "それはなるほどですね",
    "なるほど!!",
    "なるほど!!!",
}

var rd = rand.New(rand.NewSource(time.Now().UnixNano()))

func init() {
    plugins.AddPlugin(pluginKey("naruhodoMessage"), NaruhodoMessage{})
}

type NaruhodoMessage struct {
}

func (r NaruhodoMessage) CheckMessage(ctx context.Context, message string) (bool, string) {
    return strings.Index(message, "なるほど") != -1, message
}

func (r NaruhodoMessage) DoAction(ctx context.Context, message string) bool {
    idx := int(rd.Int() % len(naruhodoList))
    plugins.SendMessage(ctx, naruhodoList[idx])
    return false // next ng
}

var _ plugins.BotMessagePlugin = (*NaruhodoMessage)(nil)

ほとんどgenerateしたコードのまま。

  • CheckMessageでなるほどという文字がmessageに含まれていたらtrueを返す
  • CheckMessagetrueを返した時にDoActionが実行される
  • DoActioncontextに突っ込んでいたSendMessageを使ったSlackへ返信する

たったこれだけ。簡単なのでぜひ使ってみてください! そしてプラグイン作ったら報告してくれると嬉しいです!! (特定の環境に依存してないプラグインじゃなければ、PullRequestでもOK)

GWにGo言語で作ったMeetAppというサービスの開発記録

GWに2〜3日くらい本気だして、MeetAppというサービスをリリースしました。

f:id:kyokomi:20150518233235p:plain

フロントエンド&企画をやっていただいた@tejitakさんのブログに大体の概要が書いてありますので、こちらを併せてご覧いただければと。

GWハッカソンでMeetAppという趣味アプリ開発者のためのサービス作りました - TEJI TECH BLOG

自分の方は、Goの構成や使っているライブラリや開発中のTipsなどをまとめようかと思います。

Goの構成

使ったライブラリ

今回の開発過程で作ったライブラリ

kyokomi/goroku

herokuの以下アドオンをx/net/contextベースで利用できるライブラリ

作ったといっても、他のライブラリをラップしてherokuの環境変数を使うようにしているだけですが。

開発時のTipsとか

心がけたこと

  • 汎用性 vs 開発速度(2対8くらいの割合)
  • 読みやすいコード vs 捨てやすい/改修しやすいコード(4対6くらいの割合)
  • パフォーマンス考慮できるならやるけど、少しでも複雑になるなら// TODO:残してサクッとスルーする
  • おれおれフレームワークとか作らない
  • テスト書いたほうが開発が早くなるor画面上で動作確認が難しいテストのみ書くようにした

良かった点

  • mongolab便利だった(管理画面でデータいじったりするの楽)
  • Cloudinary便利だった(画像リサイズとかURLベースでやってくれる)
  • CircleCIで自動herokuデプロイを組んでから開発速度が加速した(最初からやってもよさそう)
  • 開発後半で、リファクタしたりよく使うコードとかを別パッケージにしたりする余裕があった
  • 開発はほぼ2人だったのでコミュニケーションロスもほとんど無く、スピード感良い感じでスムーズに開発できた

おわり

引き続き改善していくつもりですが、x/net/contextベースで開発したため結構使いまわせるようになっているので次はもっと早く開発できそうですので新しく別サービス作るのもアリかなと思ってます。

一緒に作るのも面白かったので、またやりたい。

CircleCI上でMySQLとPostgreSQLを扱ったtestを行う

先日Gunosy.go#12でLTしたkyokomi/gomaですが、 CircleCI上でDBを利用してgo generateとtestの実行を行っています。

github.com

たぶん公式ドキュメント見ればわかると思いますが、 サクッとやりたい人向けにCircleCI上でMySQLPostgreSQLを利用する方法をご紹介しようかと思います。

CircleCIでCREATE DATABASE等のsqlを実行

この辺みたら大体わかると思いますが、普通にsqlファイルをmysqlpsqlコマンドで実行してます。

https://github.com/kyokomi/goma/blob/master/circle.yml#L7

  • $HOME/$CIRCLE_PROJECT_REPONAMEは、CircleCIでgit checkoutされるリポジトリのPATH
  • 今回は例として、sample_dbという名前のDatabaseを作成する
  • data.sqlには、CREATE TABLE文やINSERT文が入っているイメージ

MySQL

/* ddl/mysql/setup.sql */

CREATE DATABASE sample_db;

circle.yamldatabase:に以下を追加します。

# circle.yaml

database:
  post:
    - mysql -u root < $HOME/$CIRCLE_PROJECT_REPONAME/ddl/mysql/setup.sql
    - mysql sample_db -u root < $HOME/$CIRCLE_PROJECT_REPONAME/ddl/mysql/data.sql

PostgreSQL

念のためSUPERUSERとLOGINのROLEを設定してますが不要かもです。

/* ddl/postgres/setup.sql */

CREATE DATABASE sample_db;
CREATE ROLE postgres SUPERUSER;
ALTER ROLE postgres WITH LOGIN;

mysqlと書き方が違うので注意。

# circle.yaml

database:
  post:
    - psql -U postgres -f $HOME/$CIRCLE_PROJECT_REPONAME/ddl/postgres/setup.sql
    - psql -d sample_db -U postgres -f $HOME/$CIRCLE_PROJECT_REPONAME/ddl/postgres/data.sql

CircleCI実行の順番

databaseフェーズは、dependenciesフェーズの後になりますので、databasepostもしくは、testフェーズでDBを使ったtest等を実行しましょう。

kyokomi/gomaの実行時のログですがこんな感じになります。

f:id:kyokomi:20150418193629p:plain

f:id:kyokomi:20150418193636p:plain

おわり

CircleCI困ったらsshして入れるので色々試せるのが便利ですね。(どっかでもいいましたが)

※go generateして生成したファイルもgo testしてるのがポイント。