ファイルが変更されたら特定のコマンドを実行するentrというシンプルなツール

あるファイルが変更されたら特定のコマンドを実行したいというケースは良くある。例えばテストの自動実行とかブラウザの自動リロードとかサンプルコードを走らせたいとか。
これらを解決する手段としてguardが有名だしgulpで解決している人も多い。

entrはそんな「ファイルの変更を検知してコマンドを実行する」ツールの一つ。

こちらの動画で使用されているのを見て知った。entrの登場は開始50秒くらいから。


Goライブコーディング: go-switchgen(2倍速)

コンセプトは上記の公式サイトに書いてある。

entr is a zero-configuration tool with no external build or runtime dependencies.

guardやgulpと違い、entrは設定ファイルも依存ライブラリもいらない。実装はC言語で書かれている。

macならhomebrewで簡単に入る。他のOSはREADMEを読む限り普通にmake installだと思う。

$ brew install entr

説明が不要なくらい使い方はシンプル。変更を監視したいファイルをパイプで渡してあげるだけ。/_は変更があったファイルを指す。

f:id:kanno_kanno:20181208144744p:plain
(1ファイルなのでfindじゃなくechoで十分だった)


キャプチャでは1ファイルだけ指定しているけど、もちろんgrepによる複数指定が可能。ファイル一覧を渡せばいいのでgrepじゃなくてagとかackとかでももちろんOK。

// 公式サイトより
$ ag -l | entr make

使い方 - オプション

man entrで確認できるオプションについて僕なりの理解。

-c
デフォルトだと実行結果は標準出力にどんどん追加されていく。
f:id:kanno_kanno:20181208145848p:plain
-cを付けると実行のたびに画面がclearされる。watchコマンドの実行結果みたいなもの。

-d
指定したディレクトに新しい追加があった時にexitされる。
fooディレクトリを作ってfind foo | entr -d go run /_を実行したあと、新しくファイルを作ったら「entr: directory altered」というメッセージとともに確かにexitされた。
ちなみに追加ではなく削除してみたら「entr: cannot open 'foo/a.go': No child processes」というメッセージとともにexitされた。(落ちた)

-n
このオプションを付けると「非インタラクティブモード」で動く。デフォルトだと後述するようにentr実行中にキーボード入力を受け付けるが、-nだと無反応になる。どういう時に使うのかは分からない。

-p
デフォルトだとentrを実行した段階でまずコマンドを実行する。-pを付けるとファイルに変更があるまで実行しない。Postpone(後回し)のpだ。

-r
コマンド実行の度に子プロセスを作り直す。
デフォルトではプロセスツリーはこうなっていて、コマンド実行は同じプロセスで実行される。
f:id:kanno_kanno:20181208160324p:plain

それが-rを付けると子プロセスを作って実行され、再実行のたびに子プロセスは生まれ変わる。
f:id:kanno_kanno:20181208160452p:plain
examplesだとentr -r node app.jsのようなサーバー再起動などで使われているので、リソースをちゃんと解放させたいとかそういう時に指定する感じかな。

-s
引数で指定したコマンドを$SHELLに対して実行する。簡単に言えば指定したコマンドを実行するだけ。
公式の例にある$ ag -l | entr -s 'make && make test'のように、&&とかをちゃんと解釈させて動かしたい時に使うのだと思う。

使い方 - コマンド

entr実行中に受け付けているキーボード操作は2つ。

スペース
即座にコマンドを実行する。

q
entrから抜け出す。

例: SQLファイルを更新するたびにMySQLへ即実行

man entrに載っているexamplesを見ていたらこんなのがあった。

Clear the screen and run a query after the SQL script is updated:

      $ echo my.sql | entr -p psql -f /_

sqlファイルを編集したら即座にPostgreSQLに実行するというサンプル。面白いなと思った。
psqlだとサンプルそのままなので、MySQLで試してみた。

$ echo sample.sql | entr -c -p -s "mysql sample < sample.sql"
  • -cで実行の度に画面をクリア
  • -pで初回は実行しない。何か怖いので
  • -sでコマンドを指定
    • リダイレクト(<)を使うせいか、デフォルトのままでは動かなかった
    • -sを使っているため/_(変更があったファイル)の記述を使えなかった
      • コードを読む限り-sを使っていると無理っぽい

一応これで実行できた。Vimでファイルを編集したら即座に結果が出力される。ただ普段はGUIツールを使っているので実際に使うことはないだろう。

監視対象のファイルが多いとエラー

何も考えずディレクトリを指定したらエラーが発生。

Too many files listed; the hard limit for your login class is 4864

扱えるファイルディスクリプタのソフトリミットを超えたので当然。特に意識したことないけど、guardとか他のツールも同様の制御があるんだろうか。制御がなくてもソフトリミット超えたら落ちるはずだから一緒か。

おまけ

動画を見たのは少し前だったので今日導入したいと思った時にentrという名前を思い出せず、当初の目的はgoで書かれたcespare/reflexを入れて解決した。
そのあとに元動画を思い出して、entrのmanやリポジトリを見たら小さかったので勉強のために出来る範囲で読み解いた。C言語、雰囲気でしか分からないな。ファイルやプロセス周りだから、C言語というよりシステムプログラミングが分かっていないのか。でも「分からないけどちょっと分かる」みたいなレベルを追いかけている時って面白い。

Vim - 開いているファイルや選択しているコード行をGithubで開く

チーム開発をしている時など、「選択しているコード行のGithub URLが欲しい」ケースは多い。
URLで指定した行番号をハイライトしてくれる、こういうURL。
https://github.com/previm/previm/blob/12226fa0e3e807a6498cd6bd6de8af70278203fe/autoload/previm.vim#L64-L65
レビュー時やチャット上で共有する時に使う。

Vim上でサクッとやるには以下のどちらかのプラグインで実現できる。

vim-fugitive + vim-rhubarb

vim-fugitiveを入れるとgit管理下において:Gbrowseコマンドが使えるようになる。
このコマンドはg:fugitive_browse_handlersに設定されたハンドラを使用してURLを開く。
開きたいコードを行選択してから:GbrowseすればOK。もちろん行指定が不要なら引数なしでそのまま:Gbrowseを実行すればいい。

このg:fugitive_browse_handlersはもちろん自分で設定できるけど、vim-rhubarbを入れると勝手に設定してくれる。
(vim-rhubarbはhubを利用するためのプラグイン)

なお:Gbrowse実行時に「fugitive: "origin' is not a supported remote」というエラーが出る場合はプラグインが古いので更新すること。
g:fugitive_browse_handlersが空の場合は「fugitive: No Gbrowse handler found for '...'」といったエラーになる。

open-browser.vim + open-browser-github.vim

これは作者のtyruさんが紹介記事を書かれているのでそちらを参考に。
基本的な使い方は:Gbrowseと変わらない。
http://tyru.hatenablog.com/entry/20130125/useful_vim_commands_for_getting_along_with_github

どっちを使う?

どちらも素晴らしいのでお好きな方でいいと思う。
hubを良く使う人はvim-rhubarbを入れておくと便利なのかもしれない。僕はほとんど使っていないので便利度は分からない。
open-browser-github.vimのtyruさんは日本人なので、困った時に質問しやすいかもしれない。

この記事のきっかけ

今回のケースに対応するコードを、僕は.vimrcに書き捨てで設定していた。
Vim環境を整理するにあたってそのコードを消して、:Gbrowseが動くように四苦八苦した。
(最初はプラグインが古くて動かず、次にg:fugitive_browse_handlersがなくて動かなかった)

あとになって「そういえばopen-browser-github.vimがあったな」と思い出して結局そっちを使うことにしたのだけど、せっかくなので調べたことを残しておくことにした。

Laravelのセッションに関するテストでSession name cannot be emptyが出て落ちる場合の対応

軽い気持ちでcomposer updateしたらテストが落ちて、その調査と暫定対応をしたのでメモ。

エラーログ

PHPUnit\Framework\Exception: [2017-12-04 00:44:41] testing.ERROR: Session name cannot be empty, did you forget to call "parent::open()" in "Symfony\Component\HttpFoundation\Session\Storage\Handler\NullSessionHandler"?.

原因

AbstractSessionHandlerの2ヶ月前の変更による。

[HttpFoundation] Make sessions secure and lazy · symfony/http-foundation@55ca8d8 · GitHub

かつ、もう一つ。

テストケースにphpunit@runInSeparateProcessを付けていると落ちる。
ちゃんと原因を追っていないですが、別プロセスゆえにparent::open()parent::destory()がバラバラになっているのかな。分かりませんが。

なんでこのアノテーションを付けているかというと、依存ライブラリのStaticファサードをモックにしたかったから。
PHPUnitで静的(static)メソッドのモック - Qiita

なお StaticMethods をモッククラスとして定義するので、別のテストで StaticMethods を呼ぶと意図しない結果になります。例えば、次のようなテストケースだと test1 の実行時に StaticMethods がモックとして定義されるので test2 のテストは通りません。 @runInSeparateProcess アノテーションを使えば test1 は別プロセスになるので回避できます。

前も@runInSeparateProcessの挙動でハマったから、これ使いたくない。
その場合はStaticファサードをラップするクラスを自分で用意する必要がある。

対応

ちゃんとやっている余裕はないので暫定対応。
上記のコードを読むとsession.use_cookiesを使っていることが分かるので一時的にoffっている。

# これをテストコードの適当なところに書く
ini_set('session.use_cookies', false);

結論

テスト大事。
とはいえ、今回は「テストでしか落ちない」ケースではあるんだけど。

TRCとopenBDを利用した新刊一覧を表示するサイトを作った

こちらのニュースを今日知りました。

TRC新刊図書オープンデータを公開しました!!

数日前にアナウンスがあったようですが、見落としていました。
せっかくなので、無料かつ飽きない程度の実装で「新刊一覧」サイトを作りました。

サイトはこちらです。 https://trc-opendata-viewer.herokuapp.com/
よろしければご覧ください。

f:id:kanno_kanno:20170830234716p:plain

そんなアクセスされないと思いますが、無料枠なのでアクセスが多いとダメかもしれません。

サイトの概要

上記サイトにある「TRC新刊図書オープンデータ」を利用しています。
zipのtsvに書かれているISBNの一覧を元に表示しているだけです。数件ISBNがないデータがありますが、それは表示されません。

書影にはopenBDを使わせてもらっています。
よって、openBDに書影がない本は書影部分が表示されません。
(残念なことにファーストビューの本は全部ない)

検索等、凝った仕組みはありません。現状、追加開発する予定もありません。
それらを作り込もうとするとTRCのデータだけではダメで色々面倒だからです。
タイトルや書籍名の絞込実装ぐらいは出来ますが、個人的にその必要性をまだ感じていません。
(具体的な名前が分かっていればググるので、一覧でどうこうする必要がない)

zipの更新

zipは毎週更新されるようですが、現状はまだ1つしかないので自動更新は考えていません。
(ローカルのzipを読んでいます)

この辺りはどうしようか考え中です。
こちらの非公式アーカイブを利用させて頂こうかなと思っていますが、ひとまず様子見です。
https://github.com/takahashim/trc_opendata

現状の使い勝手

正直、自分でも使っていくかどうか微妙。
なんとなく書影を眺めているのは楽しいけど、リピーターになるにはもっとUIが洗練されないといけないと思う。
利用者としてページングが面倒なのだけど、インスタみたいなローディング方式にするのも微妙かなあと思ったり。どうなんだろう。

PHPのnull[0]はエラーにならないしnoticeも出ない

背景

null変数に添字アクセスするところで、落ちると思っていたら落ちなくてびっくりした。

環境

OSX 10.11.5

PHP 7.0.14 (cli) (built: Apr 1 2017 23:41:23) ( NTS )
Copyright © 1997-2016 The PHP Group
Zend Engine v3.0.0, Copyright © 1998-2016 Zend Technologies

再現

まずは普通の配列に対して、存在しない添字アクセスをするとどうなるか。

<?php
$a = [];
var_dump($a[1]);

上記コードを実行すると、以下のようにnoticeを出力する。

Notice: Undefined offset: 1 in /Users/kanno/workspace/sandbox/piece/2017/06/25-1036.php on line 4
PHP Notice:  Undefined offset: 1 in /Users/kanno/workspace/sandbox/piece/2017/06/25-1036.php on line 4
NULL

ではnullに対して添字アクセスしてみる。

<?php
var_dump(null[0]);
var_dump(null[1]);

上記コードを実行するとnoticeが出ない。というかエラーにすらならない。

NULL
NULL

わお…。

ちなみに勝手に配列になったりしないかどうかも確認したが、さすがにそれは大丈夫だった。

<?php
$a = null;
var_dump($a[1]);
var_dump($a);
NULL
NULL

ちなみにforeachもいけちゃうのかと思いきや、これはwarningだった。

<?php
foreach (null as $x) {
}
Warning: Invalid argument supplied for foreach() in /Users/kanno/workspace/sandbox/piece/2017/06/25-1036.php on line 2
PHP Warning:  Invalid argument supplied for foreach() in /Users/kanno/workspace/sandbox/piece/2017/06/25-1036.php on line 2

count(null)は0だから普通のfor文ならいけちゃうけど。

<?php
for ($i=0; $i < count(null); $i++) {
}