AquaSKK 4.2.1 をインストールする Homebrew Cask を書いた

以前に書いた AquaSKK をインストールする Cask を書いた - Sexual Knowing の続き。

Homebrew Cask で AquaSKK をインストールするには

brew tap aereal/homebrew-aereal_casks
brew install aquaskk

Cask DSL 1.0 をサポート

Homebrew Cask 0.40.0 で DSL の語彙が変更された。0.40.0 以降で brew cask install aquaskk が失敗するようになっていたので修正した。

AquaSKK 4.2.1 をインストールするように

OS X Yosemite で動作するよう修正が加えられている fork より AquaSKK 4.2.1 が配布されているのでこちらをインストールするようにした。

どうぞご利用ください

テンプレートエンジン Night (#tenight) で「テンプレートの静的解析とリファクタリング」について発表した

テンプレートエンジンNight というイベントで Text::Xslate を使ってテンプレートの静的解析とリファクタリングを行った知見について発表した。

テンプレートエンジンを使ってこういうことができる、というような内容から、テンプレートエンジン作った・作ってますというような内容まで、トークの内容に幅があっておもしろかった。

トークで話さなかったこと

使っている変数の宣言コメントはどうやって抽出するのか

正規表現です。

変数として定義することも考えられるし、実際にそうすることも考えてみた。

[% SET INHERITED_VARS = ['post'] %]

しかし既にあるコメントによる宣言を書き換える手間や、現状を把握したいという目的からは逸れてしまうことから見送った。

テンプレートのモジュール化 (分割) の指針

早すぎた抽象化という問題がある (あった) ということは認識しているし、今が最高の状態だと思っているわけではない。

既存のテンプレートのコードを書き換える前に現状を把握するために静的解析を行おう、というモチベーションがあってトークの内容に繋がる、というかんじです。

Mackerel で QNAP も監視したいのでする

最近、MacBookSSD の空き容量がいつの間にか無くなっていて、スワップを作れなくなった結果、フリーズするということがあって反省したのでちゃんと管理しようという気持ちになった。

ひとまず家にある中で壊れたりトラブルが起きると一番困るのは QNAP なので、これを監視することにした。

Mackerel は Go で書かれた agent を入れるだけでよい割にアラートの閾値をいい感じに定義することができるので便利。

QNAP で mackerel-agent を動かす

僕の持っている QNAP は TS-220 というモデルで CPU は ARM v5 で OS は Linux ベースらしい。

mackerel-agent は最近の CentOSDebian しか動作することを保証されていない:

For now, mackerel-agent is guaranteed to run only on CentOS 5/6 and Debian 6/7.

https://github.com/mackerelio/mackerel-agent#readme

とはいえ Go で書かれているし Linux ベースなら取り付く島が無いなんてことはないだろうと思ってちょっと試したら動いた。

テストは書いていないしかなりいい加減だがこれで動いた。

  • QTS (QNAP の OS) に入っている uname-o (Operating System) をサポートしていない
    • どのような意図でもって実装されていないのかは不明だけれどもさして重要な情報ではないので決め打ちで "Linux" とする
  • QTS に入っている df-P オプションをサポートしていないし出力の形式が少し違う
    • 1024-block1k-block だった
    • それ以外は特に書式に大きな違いはなかったので正規表現を修正する

Docker でビルドする

mackerel-agent は OS X ではビルドに失敗するので Docker を使うことにした。

Dockerfile:

FROM golang:cross

RUN mkdir -p /go/src/github.com/mackerelio
ADD ./mackerel-agent /go/src/github.com/mackerelio/mackerel-agent
WORKDIR /go/src/github.com/mackerelio/mackerel-agent
RUN make deps
CMD bash -c "GOOS=linux GOARCH=arm GOARM=5 CGO_ENABLED=0 make build && cp ./build/mackerel-agent /host/_mackerel-agent"

これで docker build -t mackerel-agent . && docker run -v "$(pwd)":/host mackerel-agent とすると _mackerel-agent が作られる。

Docker Hub Registry には golang が登録されているが、そのうちクロスコンパイルするための環境がセットアップされているタグを選ぶ。最新 (1.3) でよければ cross.

ビルドの際に指定している環境変数Optional environment variables を参照した。 また GOARM 変数については GoArm - go-wiki が詳しい。

ルート証明書が見つからないエラー

ビルドはできたものの実行時エラーが出る:

x509: failed to load system roots and no roots provided

意味がわからなかったが調べたところ x509 は SSL 通信のために使われているらしい。“roots” がよくわからなかったがルート証明書のことらしい。

ルート証明書が無いなら配置すればよいかと思ったもののどこに配置すればよいのかわからない。

Go において CertPool は参照すべき証明書を保持するオブジェクトの型で、SSL 通信などを扱うライブラリ (e.g. net/http) ではデフォルトではシステムの証明書を参照するようだった。

ではシステムの証明書の配置場所はどこかというと root_unix.go というファイルに定義されている。

var certFiles = []string{
        "/etc/ssl/certs/ca-certificates.crt",     // Debian/Ubuntu/Gentoo etc.
        "/etc/pki/tls/certs/ca-bundle.crt",       // Fedora/RHEL
        "/etc/ssl/ca-bundle.pem",                 // OpenSUSE
        "/etc/ssl/cert.pem",                      // OpenBSD
        "/usr/local/share/certs/ca-root-nss.crt", // FreeBSD/DragonFly
    }

QNAP では /etc/ssl/certs/myroots.crt に配置されていたので symlink を作った。

おわり

これで QNAP 上で mackerel-agent を動かすことができるようになったはず。

あとは init スクリプトを書いたりしてデーモン化しておけばよい。

ディスク容量だけではなく netin/netout や CPU 使用状況も見れるので並列にコピーを走らせて無茶を走らせているときなど、余裕があるかなど見れて便利。

だいたい普通の Linux という感じでかなり手軽に運用できるので、次は nasne あたりも監視したい。

Shipped Text-Xslate-AST-Walker @ 0.01

これはなに?

Text::Xslate::Parser が返す AST を文字通り「歩きまわる」ためのモジュールです。

0.01 現在では条件に一致するノードのみを返すメソッド (#search_descendants) が実装されています。

使い方

SYNOPSIS より:

use Text::Xslate::Parser;
use Text::Xslate::AST::Walker;

my $template = <<EOF;
: my $first_name = 'Hanae';
Hello, <: $last_name :>, <: $first_name :>.
EOF
my $parser = Text::Xslate::Parser->new;
my $nodes = $parser->parse($template);
my $tw = Text::Xslate::AST::Walker->new(nodes => $nodes);
my $undeclared_vars = $tw->search_descendants(sub {
  my ($node) = @_;
  ($node->arity eq 'variable') && !$node->is_defined && !$node->is_reserved;
});

printf "Undeclared var: %s @ Line %d\n", $_->id, $_->line for @$undeclared_vars;

簡単ですね。

#search_descendants に渡すサブルーチンには Text::Xslate::Symbolインスタンスが渡されます。

上記例のように未定義の変数を列挙することなどができます。

Text::Xslate は便利

Text::Xslate は高速なテンプレートエンジンとして既に有名で実績がありますが、一方で構文を選ぶことのできる柔軟性も持ち合わせています。

構文それぞれについて Parser が用意されており、抽象構文木を得ることができます。

今後

などを目指しています。

機能追加、バグ報告

GitHub で開発しているので Issues or Pull Request をお待ちしております。

どうぞご利用ください

cpanm Text::Xslate::AST::Walker

HTML::Selector::XPath @ 0.17 で normalize-space() するようになった


HTML::Selector::XPath@0.16 は CSS における class セレクタと等価な XPath を出力しない - Sexual Knowing

Normalize space characters of the class attribute by aereal · Pull Request #5 · Corion/HTML-Selector-XPath · GitHub がマージされた 0.17 がリリースされた。

0.17 以降に更新すると @normalize-space()@ を使うようになっているので、class 属性値の中に改行などの空白 (U+0020) 以外の空白文字が含まれていても正しくマッチするようになった。

めでたい。

HTML::Selector::XPath@0.16 は CSS における class セレクタと等価な XPath を出力しない

再現コード


class_with_lf.t

class 属性の値における「空白文字」

class 属性の値は:

The attribute, if specified, must have a value that is a set of space-separated tokens representing the various classes that the element belongs to.

HTML Standard

とあるように、a set of space-separated tokens と定義されている。

では a set of space-separated tokens の定義を参照すると:

A set of space-separated tokens is a string containing zero or more words (known as tokens) separated by one or more space characters, where words consist of any string of one or more characters, none of which are space characters.

HTML Standard

とある。

space characters について合意を持たないといけなさそう。

The space characters, for the purposes of this specification, are U+0020 SPACE, U+0009 CHARACTER TABULATION (tab), U+000A LINE FEED (LF), U+000C FORM FEED (FF), and U+000D CARRIAGE RETURN (CR).

HTML Standard

とある。つまり改行 (line feed, carriage return) は「空白文字」として定義されている。

HTML::Selector::XPath のバグ

class 属性の値の前後に空白 (U+0020 SPACE) を追加して contains() 関数でマッチするか試しているが、前述の通り class 属性の値は U+0020 以外の文字も空白文字として許容する。

たとえば改行 (U+000A LINE FEED) があった場合には contains() 関数は偽を返すので、CSS における class セレクタと等価ではない。

対策と修正

XPath には normalize-space() 関数がある。この関数は、連続する空白文字をひとつの空白 (U+0020) に置き換える。

この Pull Request では normalize-space() を使うよう変更している。

YAPC::Asia Tokyo 2014,プログラマブル Mac OS X

プログラマブル Mac OS X - YAPC::Asia Tokyo 2014

プログラマブル Mac OS X」というタイトルで YAPC::Asia Tokyo 2014 のトークに応募した。

ひとつの仕事をうまくやるプログラムの組み合わせでより大きな仕事をこなす、という UNIX 的な考え方は Mac OS X にも継承されており、設定を取得・変更したり普段 GUI から操作する機能と同等の仕事をこなすコマンドが種々用意されている。

pkgutil, defaults, launchctl といったツールおよびそれらの背後にある技術 (e.g. Property List, launchd, Installer Package) についての簡単な紹介や実際にそれらのツールを組み合わせた簡単なスクリプトの紹介をする予定。

Mac OS X ユーザがより効率的に「大きな仕事」をこなす助けとなればよいと思う。

npm でモジュールのバージョンを固定する

A. npm shrinkwrap

npm shrinkwrap を実行すると package.json に列挙されている依存を読みとり、実際にインストールされるべきモジュールのバージョンが列挙された npm-shrinkwrap.json というファイルが作られる。

npm install はこのファイルが存在すればこちらを優先して読むようになっている (--no-shrinkwrap オプションを与えることで無視することもできる)。

いくつか気をつける点があって:

  • devDependencies も固定するには npm shrinkwrap を実行する際に --dev オプションを与える必要がある
  • npm-shrinkwrap.json が存在しない場合、package.json の依存関係を読み取る。つまり、Bundler などのように固定された依存関係が保存されたファイルが存在しない場合にエラーないし警告を出す仕組みは (現在のところ) ない

特に後者は気を利かせてフォールバックしてほしくない時に npm だけではどうしようもないという問題がある。

aws-cli とファイルのパス

aws route53 change-resource-record-sets--change-batch 引数の値にファイルパスを渡すには file スキームの URI を渡す必要がある。

aws route53 change-resource-record-sets --hosted-zone-id /hostedzone/XXXX --change-batch file://${PWD}/batch.json

ドキュメント読んでも file スキームが書いてあるのを無意識の内に無視してずっと Invalid JSON と言われ続けていた。

✘╹◡╹✘ < aws --version
aws-cli/1.3.9 Python/2.7.6 Darwin/13.2.0

work tree の外から git pull する

バッチスクリプトなどで work tree の外から git pull を行いたい、ということはままあるシチュエーションであると思います。

git コマンドは --git-dir オプションでコマンドで操作する .git ディレクトリを指定できるので、これを指定すれば済むと思うかもしれませんが、これではうまくいきません:

cd $HOME
git --git-dir=$HOME/repos/@aereal/dotfiles/.git pull
# Cannot pull with rebase: You have unstaged changes.
# Please commit or stash them.

work tree も指定する必要があります:

git --git-dir=$HOME/repos/@aereal/dotfiles/.git --work-tree=$HOME/repos/@aereal/dotfiles pull

あるいは Git 1.8.5 以降であれば -C オプションが使えます:

git -C $HOME/repos/@aereal/dotfiles pull

git-sh-setup.shrequire_clean_work_tree() 関数が git diff-files --quiet --ignore-submodules を実行して終了ステータスが非ゼロだと上記のようなエラーメッセージを出力して終了するようです。

Kyoto.なんかで“Introduce browserify”という発表をした

Kyoto.なんかという Kyoto.js の潮流を汲んだような汲んでいないような、勉強会のようななんなのかよくわからないイベントで発表した。

browserify という Common JS Modules/1.0 などに互換のあるモジュールを、Common JS Modules/1.0 などモジュール機構に対応していないブラウザで動かせるようにするツールについて簡単な紹介をした。

browserify-shim を使って jQuery plugin も browserify で扱えるようにするのがとても便利。

実際には browserify-shim が require() を解決し展開するやりかたはけっこう凝っている (出力されたコードとちょっと実装を読んだくらいで、僕も詳しい実装はあまり追っていない)。

作者の substack さんは browserify という比較的大きなツールを作る際に、細かく部品化してそれぞれ独立した npm モジュールとして公開されていたりする。

unix philosopher.

substack in cyberspace

というポートフォリオの一文が実にしっくりくる人だと思います。

anyenv-exec を作った

anyenvプラグイン anyenv-exec を作った。

使い方

たとえばインストールされている *env のバージョンを出力する例:

anyenv exec --version
# plenv 2.1.1-9-g26dbef7
# pyenv 0.4.0-20140404-10-g601ac4b
# rbenv 0.4.0-97-gfe0b243

rehash する例:

anyenv exec rehash

*env 系は大抵のサブコマンドが共通していることから、出力を比較したいとか、いちいち for 文を書くのはめんどうであるとか、そういった思いから作ってみた。

実装は大したものではないけれどけっこう便利。

インストール

mkdir -p $(anyenv root)/plugins
git clone git://github.com/aereal/anyenv-exec.git $(anyenv root)/plugins/anyenv-exec
aereal/anyenv-exec · GitHub

chrome.contextMenus.create() に parentId とコンテキストを指定するプロパティは同時に指定できない?


ドキュメントには特に何も書いていない。

子メニュー (= parentId を指定されている) は親メニューより狭いコンテキストを指定することはできない?

Vim で現在カーソルがあるテストメソッドを実行する

Perl には Test::Class という JUnit 風にテストを書けるモジュールがある。すなわち:

ことができる。

Test::Class はテスト実行時に環境変数 TEST_METHOD に与えられた値で実行するテストメソッドをフィルタリングすることができる。

Sometimes you just want to run a single test. Commenting out other tests or writing code to skip them can be a hassle, so you can specify the TEST_METHOD environment variable. The value is expected to be a valid regular expression and, if present, only runs test methods whose names match the regular expression.

Test::Class - search.cpan.org

テストコードが存在するソフトウェアに修正を加える場合、大抵、いつかのタイミングでテストコードを修正することになる。そうなるとまず修正したテストのみを実行し、最小限度のフィードバックを得たい。

つまり、TEST_METHOD を指定してテストを走らせたいのだが、いちいちテストメソッド名を入力するのも面倒である。

そこでテストの実行には vim-quickrun を、現在カーソルのあるメソッド名を取得するために current-func-info.vim を使う。

let g:quickrun_config['prove/carton'] = {
      \ 'exec'    : 'carton exec -- %c %o %s',
      \ 'command' : 'prove',
      \ }
let g:quickrun_config['prove/carton/contextual'] = extend(g:quickrun_config['prove/carton'], {
      \ 'exec' : 'TEST_METHOD=%a ' . g:quickrun_config['prove/carton'].exec,
      \ })
command! ProveThis call s:prove_this()
function! s:prove_this()
  let func_name = cfi#format('%s', '')
  if func_name == ''
    QuickRun prove/carton
  else
    execute 'QuickRun prove/carton/contextual -args ' . func_name
  endif
endfunction

メソッド名を得ることができればその値を引数として渡し prove/carton/contextual の設定を使って QuickRun を実行する。得られなければ prove/carton を実行する。