watchfiyがnfsマウントしたファイルの変更を検知してくれないとき

browserify + watchify + gulp

browserify + gulpでビルドするときに,よく差分ビルドをするためにwatchifyを使うことを推奨している記事は結構多い.そういう環境でビルドする方法は こちら あたりを参考にしてもらえると嬉しい.

これが,watchifyを入れておかないと,ファイルの変更を検知してくれないので,毎回自分でビルドコマンドを叩かなきゃいけないし,差分ビルドにならないので遅い.そんな開発環境は嫌だ.というわけで,gulpのビルドタスクにwatchifyを組み込んでおくことは,本当にいろんなところで紹介されている.

nfsマウントしたディスク上のファイル変更が検知されない

前述のような例に沿って同じようなgulpタスクを作ってみた.ただ,特に理由はないのだけれど,vagrantで動かしていた.

jsのソースファイルは,vagrantのshared folderを使って,ホストOS側の適当なディレクトリに放り込んでいる.vagrantはそれをnfsマウントして利用していた. この場合,jsのソースファイルの編集はホストOS側で行う.しかし,サーバはvagrant側で動かしているのでjsのビルドもvagrant側で行ってほしい.

そうすると,vagrant側のgulpはwatchifyを動かしていても,ファイル変更を検知してくれなかった.

これは,watchifyに poll オプションを与える必要がある.

var w = watchify(b, {
  poll: true
});

http://stackoverflow.com/a/30743525/4545174

これでホストOS側で変更したファイルの変更検知を,watchifyが行ってくれるので差分ビルドが走るようになった.

ちなみに遅い

ビルドが遅いのではなくて,変更検知にラグがある…….これはnfsマウントしているから仕方ないのか?

同じことを思う人はいたらしい.

Watchify polling very slow on Vagrant Machine

こういう記事,あまり出回っていないので,多分近年のフロントエンド開発をする人はvagrantとか使わないのかな……. *1

*1:ここに脚注を書きます

golangでテストをする

最近golangを結構書いています.
コンパイルも割と速いし,実行速度は言うまでもなく速い.

クラスがないとか,例外がないとか,色々と言いたいことはあるでしょう.思想的には結構独特ですが,なれると普通に書けます.

今日はそんな中でテストの話をしようと思う.

やっぱりBDDがいい

golangにはテストをするための,testingというパッケージがある.

golang.jp


使い方はこの辺の記事が参考になるでしょう.

qiita.com



assertがないというのが,結構主題かもしれない.

ただ,使い方を見てみればわかる通り,これはExampleのテストでしかない.

個人的な意見なのだけれど,やっぱりパターンが複雑になればなるほど,分岐が多くなり,前準備が複雑になる.
どうしてもBDDを導入したかったので,ginkgoを入れてみた.

github.com



matcherにはgomegaというものが使われている.

github.com




使い方は説明を読んでもらえればわかるだろう.

BeforeEachAfterEachのようなコールバックも用意していて,使いやすい.

BeforeEachAfterEachは各It節の直前・直後で実行されるが,BeforeSuiteなどはtest_suiteが実行される直前に呼ばれるだけである.



テスト用のDBを切り替える

前提

さて,ginkgoの準備はできた.

golangは必ずしもwebサービスを作るものではないが,とりあえずwebサーバーで,裏側にDBを持っているようなものを想定しよう.
その場合,テスト用のDBをどうするかというのが結構な問題になる.

たとえば,Railsのようなフレームワークであれば,database.ymlの設定を,Railsが勝手に読み込んでくれる.
だから,テスト時には接続されるDBが自動的にテスト用のDBになる.


通常開発しているDBをテスト用に使ってしまっても構わないが,やっぱりデータをクリアするのは面倒だし,なによりテストしながら開発することを考えると,入れておいたデータが消えてしまうのは嫌だ.
開発用のデータというのは,できるだけ低コストで準備したいから,テスト毎に消されるなんて面倒が増えてしょうがない.

というわけで,テスト用のDBに切り替えられるようにしよう.

DBの接続設定を書く

まず,DBに接続している部分のコードを見てみよう.
たぶん,

import (
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)
func main() {
    db, err := sql.Open("mysql", "root:hogehoge@/sample_project?charset=utf8")
    if err != nil {
        panic(err.Error())
    }
}

こんなコードがかいてあるんじゃないだろうか.

ここを,まず環境変数によって切り替えられるようにする.

package db
import (
    "os"
    "database/sql"
    _ "github.com/go-sql-driver/mysql"
)
func Database() *sql.DB {
    username := os.Getenv("DB_USER")
    password := os.Getenv("DB_PASSWORD")
    database := os.Getenv("DB_NAME")
    db, err := sql.Open("mysql", username + ":" + password + "@/" + database + "?charset=utf8")
    if err != nil {
        panic(err.Error())
    }
    return db
}


これで環境変数からDB接続情報が取れるようになった.
direnvあたりを使って環境変数を管理しておくと楽だろう.

テストのBefore節で環境変数を書き換える

上記の関数を使ってDB接続する限り,接続先は環境変数に記述されているものとなる.
つまり,環境変数で与えられるDB_NAMEさえ書き換えてしまえば,ローカルの好きなDBにつなぐことができる.

ただし,環境変数そのものを入れ替えるので,テスト実行中に別のプロセスで開発したりしていないことを前提としている.


model_test.goにて,

package model_test
import (
    "os"
    "database/sql"
    "./database"
    . "github.com/onsi/ginkgo"
    . "github.com/onsi/gomega"
)

var _ = Describe("List", func() {
    var (
        currentdb string
        table *sql.DB
    )
    BeforeEach(func() {
        testdb := os.Getenv("DB_TEST_NAME")
	currentdb = os.Getenv("DB_NAME")
        os.Setenv("DB_NAME", testdb)
    })
    AfterEach(func() {
        table = database.Database()
        table.Exec("truncate users;")
	table.Close()
        os.Setenv("DB_NAME", currentdb)
    })
}

こんな感じにしておく.
これで環境変数としてDB_TEST_NAMEにテスト用DBの名前を入れておけば大丈夫.
ちゃんと終わったら元の値に戻しているので,終わったら普段通りに開発用DBに繋がるようになっている.


ちなみに,このコードは,DBの接続を切り替えるだけなので,ちゃんとCREATE DATABASEや,必要なテーブルのmigrationは終わらせてある前提.

そのへんはgooseあたりが使いやすかった.

liamstask / goose — Bitbucket




mockとstub

もう一点,かなり悩ましい項目の一つにStubやMockがある.


structに包まれて,interfaceとして実装している,いわゆるインスタンスメソッド的な立ち位置の関数は,実はinterfaceごとを上書きしてやればmockできる.

こんな感じで.

qiita.com



そこまでしなくても,testifyを使うと,mockというpackageがあったりする.

github.com



ただ,どちらもstructのメンバー関数しか上書きできない.


グローバル関数をstubしたいときはどうするのか?


いいやり方はこのくらいしか見つからなかった.

matope.hatenablog.com


csrf = checkCSRF

func checkCSRF() bool {
     // hogehoge
}

まぁこんな感じで,関数ポインタを使うように本体の実装を書き換える必要がある.

で,テスト側では

    JustBeforeEach(func() {
        csrf = func() bool { return true }
    }
    // hogehoge

こうしてグローバル変数に入った関数ポインタを書き換えてやる.


もしテスト中にstubを解除したければ,関数ポインタの値を適当な変数に入れて持っておけば良さそう.


まだこのくらいしか書いていない

テストっていうのは状況を準備するのが大変だ.
これだけの複雑な状況をテストのたびに作りなおすのが結構だるくて….

RSpecでいうところのfactory_girlみたいなものも,探して,確かにあったんだけど,それじゃないような……感.github.com


ActiveRecordみたいなものをもたずに,自分でDBから必要なデータを取ってきて,オブジェクトを組み立てているので,factory_girlでデータそのものを作ってもらっても,結局組み立て部分のコードがかさむ…….


というところで,まだまだ悩み中です.

ただ,golangはgodocがかなり充実していて,適当なライブラリであってもREADMEよりgodocに詳しく書いてあって,ドキュメントに関してはあまり困らなくて,すごく嬉しい.

swift2.0への変更で詰まったところ

これをswift2.0対応させてみたので,詰まったところを書いておく.

h3poteto.hatenablog.com


Xcode7を入れたあと,Xcode6.4で開発していたプロジェクトを開くと,初回に古いシンタックスをすべて置換してくれる.
ちなみにこれ,一度閉じてしまった場合でも,Edit->Convert->To Latest Swift Syntax でいける.

だいたい,使ってない変数を消してくれたり,varだけど代入していないものをletになおしてくれたりする.

そういうのがひと通り終わっても,コンパイルが通らなかったので以下メモ.

CGBitmapInfo

let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.NoneSkipFirst.rawValue)
CGBitmapContextCreate(
    nil,
    Int(ceil(pixelSize.width)),
    Int(ceil(pixelSize.height)),
    CGImageGetBitsPerComponent(originalImageRef),
    0,
    colorSpace,
    bitmapInfo)

こんなコードを書いていたのだけれど,

error:Cannot convert value of type 'CGBitmapInfo' to expected argument of type UInt32

こんなことを言われる.

どうやらCGBitmapContextCreateの引数の型が変わったらしい.
幸いなことにCGBitmapInfoはrawValueというUInt32型を構造体が保持しているので,そいつを取り出してやれば良い.

let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.NoneSkipFirst.rawValue)
CGBitmapContextCreate(
    nil,
    Int(ceil(pixelSize.width)),
    Int(ceil(pixelSize.height)),
    CGImageGetBitsPerComponent(originalImageRef),
    0,
    colorSpace,
    bitmapInfo.rawValue)


同じことがstackoverflowに質問されていたので,回答しておいた.stackoverflow.com


Dictionaryの型

var shintani: String?
let params: Dictionary<String, String> = [
    "yes" : "asumiss"
]
shintani = "ryoko"
let dict: Dictionary<String, AnyObject> = [
    "asumi" : params,
    "shintani" : shintani
]

これはAnyObjectで怒られる.

Type of expression is ambiguous without more context

そもそもString?型を突っ込んでいることに問題があるので(nullが入る可能性がありDictonaryとしては良くない),どこかでunwrapする必要がある.

var shintani: String?
let params: Dictionary<String, String> = [
    "yes" : "asumiss"
]
shintani = "ryoko"
let dict: Dictionary<String, Any> = [
    "asumi" : params,
    "shintani" : shintani!
]

viewControllersで取ってきたものの型チェック

型チェックがきつくなったので,viewControllersでとってきたものの型をちゃんとキャストしておかないといけない.

例えばこんなコードを書いていたとする.

if let controllers = appDelegate.rootController.viewControllers {
  for navController in controllers {
    if let target = navController.topViewController {
      // targetに対する処理
    }
  }
}

これだとtopViewControllersの呼び出しでコンパイルエラーになる.

if let controllers = appDelegate.rootController.viewControllers {
  for navController in controllers {
    if let controller = navController as? UINavigationController {
      if let target = controller.topViewController {
        // targetに対する処理
      }
    }
  }
}

こんな感じで,as UIViewContrllersしないとtopViewControllerとかできない

ATSをdebugで無効化

これがなかなかやっかいで…….
自作のサーバープログラムと通信するようなアプリを作っている場合,開発環境ではテストサーバーにつなぐことがほとんどだと思う.

だけど,開発環境のサーバーなんて,もちろんSSLなんて通しているはずがない.
っていうかオレオレ証明書ですら挟むのめんどくさい.

そもそもローカルでrails sしただけのサーバーに繋ぎたいし.


というわけで,debugモードの時だけATSを無効化したいんです.


qiita.com


これを参考に,ドメイン指定によるATS無効化をやることにしよう.


/etc/hostsに

127.0.0.1  whalebird.localdomain

と書いておいて,このドメインだけATSを無効化しました.


@testable便利

以前はこういう問題があったんですけど,testableのおかげでこういうことせずにクラスを呼び出すことができました.h3poteto.hatenablog.com


割と楽です.

阿澄病に引き続き花澤病治療薬を作った

阿澄病の治療薬として,twitter botを作ってもう2年くらい経ちました.h3poteto.hatenablog.com


今回は,新たに花澤病の治療薬twitter botを作りました.

twitter.com



暇な方はぜひフォローしてみてください.


現状の仕様

  • 1時間に一回花澤香菜youtube動画をつぶやく
  • リプライを送るとランダムで花澤香菜youtube動画を返してくれる
  • ふぁぼ,RTは記録している
  • 花澤香菜ワードを含むツイートは記録している
  • youtube動画は1日に一回更新している


動画の取得先としてニコニコ動画も入れたいんですけど,そこはまだ対応していません.
ふぁぼやRT,ツイートの記録は,今のところなんの役にも立ちません.

が,asumi.chのときのようにウェブサービスになるときに有用な情報なので,全部とっておきます.


そんな感じなので,花澤香菜を見たいなって思った時は,話しかけるといいです.
実はgolangで作っているのですが,反応速度はさすがの速さでした.

あと,botの名前を,もっとこう「香菜ちゃんぺろぺろ」的な気持ち悪いのにしたいんですが,いいのが思いつきません.


ソース

このへんにあります.

github.com


実はこれ,golangで全部書いています.


今のところ割と適当な作りをしています.

やりたいこと

感想

花澤香菜の動画結構多い

まだyoutubeしか漁ってないんですが,あすみんのを作った時より多い感じがします(体感).

まぁ確かにあすみんよりちょい若いし,2年くらい前まで花澤香菜といえば,どの日にアニメを見ても声が聞けた人気声優です.
おまけにあすみんと違って独身.

あと,今のところラジオ出演が結構多いのと,ネタになるラジオ出演が多いから,動画が多いんですかね.

普通に見ていてもラジオ系が多い印象があります.


文学少女の一人ラジオを始めた時,「この子は流行る」と言った俺の予言が的中して,「やはり俺の目に狂いはなかった」という気分です.


golangのめんどくさいところ

巷で言われ尽くしているので,そこまで言及はしません.が,やっぱり書いてみるとイマイチな感じを受ける部分も多いので.

  • classがない
  • packageのバージョン管理ができない
  • 例外がない,panicさせてもハンドリングできない
  • package mainを一つしか許容していない

特に最後のがきつくて,今回のようにスクリプト的なものを複数配置して,cronで定時稼働させるようなものの場合,main関数をいろんなところに書きますよね.
でも,golangはディレクトリ内のpackage mainを一つしか許容してくれません.

一つのディレクトリにはひとつのプログラムだけを入れておけってことなんでしょう.
理解はできるけど,使いにくい.


packageのバージョン管理については,結局gomというgemみたいなものを入れています.github.com


ただ,gomを通すとクロスコンパイルができなかったり……不便なところはあります.

阿澄病治療薬SlackBotを作った

みなさんさようなら.
相変わらず阿澄病のみんなのために日々「世の中に不要そうなもの」を開発し続けています.


今回はSlackのBotを作りました.

とりあえず画像をくれる

f:id:h3poteto:20150808005206p:plain

asumiss image

で画像を表示してくれます.
ちなみに,表示する画像はもちろん阿澄佳奈のみです.


内部の話をすると,これは普通にgoogleのImageAPIを使っています.
そのため,実は画像件数がそんなに多くないです.
特別なスクリプトを作っていたり,クロールしたりはしていないので,パターンは少なめですね.


いぇす!あすみす!

f:id:h3poteto:20150808005202p:plain

いぇす

とつぶやくだけ!


単純に語句に反応してくるだけですね.

なんと動画も出してくれる!

これが目玉機能に近い.

f:id:h3poteto:20150808005200p:plain

asumiss youtube

で,youtubeの動画を,

asumiss niconico

で,ニコニコ動画の動画を出してくれます.

f:id:h3poteto:20150808005157p:plain


めんどいときは,

asumiss movie

で,youtubeかニコニコ,どちらかの動画をランダムに出してくれます.

f:id:h3poteto:20150808005154p:plain



これは,もちろん動画をリアルタイム検索しているわけではなくて…….

実はhttp://asumi.chAPI用の穴を開けてあり,そこにアクセスしています.
つまり,http://asumi.ch で日夜クロールしている動画が,ここでの対象となります.


それらを選び出してつぶやいてくれているわけです.


今まで作っていたものがこんなところで有効活用!!!脅威のコラボレーション!!!


しかしここまで役に立たないSlackBotもあるまい…….


ソース

このへんに置いてあります.
阿澄病の方は是非使ってみてください.

github.com



ちなみに,個人Slackを開設するの,こういうことできてなかなか楽しいですよ.
癒やしになるし.