2016年にやったこと
クックパッドで働くのは4年目、社会人としては2年目になった。2015年にやったことと同じフォーマットでまとめておく。
発表
今年は6本発表した。去年RubyKaigi前後にいろいろ集中してて死にかけたので、2か月に1回というのが僕にとってはちょうど良いペースだと思う。
RubyConf 2016
今年は海外のカンファレンスで登壇してきたというのが一番大きいと思う。英語は一応どうにかなったけど、うまい表現ができずもどかしいことがあるのでもうちょっとマシにしたい。あとこの成果で初めてクックパッドの業務にmrubyが導入されたように思う。
RubyKaigi 2016
100%クックパッドの業務時間で作ったOSSを題材に、今年は1人でRubyKaigiに登壇した。Barbeque自体はまだまだ改善点があるものの、ECSを活用してジョブ単位のオートスケールができ、マルチテナントで運用コストが低いシステムを作って実際に導入されるところまで持っていけたのはよかった。
Cookpad TechConf 2016
今年から弊社も社の技術カンファレンスをやるようになったので、アルバイト時代からずっとやりたいと思っていた仕事とその成果について話した。クックパッドの開発基盤グループにいると、難しい問題のデバッグや高速化にじっくり取り組めるのが楽しいと思う。
Rails Upgrade Casual Talks
大変ありがたいことに、発表しませんかというお誘いを初めていただいた。Rails 5が出る前だったので、cookpad.comのRails 4.1→4.2の話をした。
How to safely upgrade Rails // Speaker Deck
TokyuRuby会議10
Rails 5が出たのに合わせて、僕が送っていたパッチの紹介をした。東急は高速で喋っても誰にも怒られないのが楽しい。
My patches for Rails 5 // Speaker Deck
Roppongi.rb #2
toshimaruさんにお誘いいただいてMItamaeについて話した。何かOSSを作って、便利さを発表して、いろんな人に使ってもらう、という体験はとても楽しいので今後も続けたい。
MItamae Hacking Guide // Speaker Deck
ホッテントリ
ブログは1ヵ月に1記事くらい書くのを意識しているけど、はてなブログが11記事、Qiitaが6記事という感じだった。「発表」と重複があるけどブクマを貰えたのは以下の通り。
いまだに400 users越えたことないので何かもっとバーンて感じの成果欲しい。
OSS活動
今年リリースしたOSS
Starだけ見ると去年に比べるとウーンという感じ。mitamaeを使ってくださっているみなさまありがとうございます。
Star | リポジトリ |
---|---|
★65 | k0kubun/mitamae |
★64 | cookpad/barbeque |
★48 | k0kubun/xkremap |
★46 | k0kubun/itamae-go |
今年は↑でmruby, Docker, Go, Xlibあたりを触っていて、他にhescapeでSIMDプログラミングで少し遊んだのと、もうちょっとC言語に強くなるためにCでCコンパイラを実装するのを途中までやり、そこでLLVMも触った。
今年活発にメンテしていた既存OSS
Star | リポジトリ |
---|---|
★558 | k0kubun/md2key |
★407 | k0kubun/Nocturn |
md2keyは、ずっと欲しかったけど実装できなかったnested listが作れたのが大きい。あとプルリをいただいて、シーケンス図、表、発表者ノートなどがサポートされた。 Nocturnはデザインを変えReact/Redux化してから300くらいStarが増えた。既存の設計を真似たことでメンテが楽になった。
毎年新しいプロダクトをリリースしていきたいと思っているけど、既存のものを改善していくのも大切にしたい。
contribution
今年はmrubyを触り始めていろいろ踏んだのでmruby周りにいろいろ貢献した気がする。RubyとかRailsはもうそこまで不満がないけど、mrubyの方はまだまだやれることがたくさんありそう。mruby本体にも1回だけ貢献できた。
@k0kubun さんがmrubyにfreeze実装してくれた。圧倒的仕事の速さ
— Yukihiro Matsumoto (@yukihiro_matz) 2016年12月12日
ポッドキャスト
timakinさんに誘われてboot.fmというポッドキャストを始めた。4話配信済み。2人でやっていても、ネタと収録する時間とゲストを呼ぶ人脈とコミュ力がなくてかなり大変で、miyagawaさんはすごい!!!!という気持ちになる。一方、自分がホストをやると、話したいことを自由に話せる楽しさがある。
2017年は
- Ruby以外で手になじむ言語を増やしたい
- 今後もコードを書き続けたいが、このままだとRuby以外で仕事できなくなり失職しそう
- 業務で使って困ることがないレベルまで英語の語彙や表現力を向上したい
- ドキュメント読むのとOSS活動をする時に使うし、いつか海外でも働けるようにしたい
- 執筆活動をしたい
- というかやってるんだけど、ちゃんと出すところまで持っていけるようにしたい
私生活と両立して楽しくやれればいいなと思っています。来年もろしくお願いします。
Linux向けの最強のキーリマッパーを作った
X Window Systemで動作するキーリマッパー「xremap」を作った
- 2017/1/9追記: xkremap→xremapにリネームしました
- 2021/12/21: Rust化に伴いアーキテクチャを刷新し、より多くの機能と環境がサポートされました: Linux用キーリマッパーxremapをRustで書き直した - k0kubun's blog
僕はKarabiner用のRuby DSLを作ったりそれを使って大量の設定を既述する程度にはKarabinerのヘビーユーザーなんだけど、デスクトップ環境にLinuxを使い始めてからもう1年以上経つ今でもLinux環境で使えるKarabiner並にリッチなキーリマッパーを見つけられずずっと不便していたので、ユースケースを満たす最低限のものを自分で作った。
ちなみにX用であって別にLinuxの何かに依存しているわけではないので、タイトルは釣りである。
これは何
RubyのDSLかつシンプルなキーの指定方法によりキーリマップを設定することができる。例えば、以下の設定ファイルを書いてxremapに渡す*1とターミナルやEmacsの外でもEmacsライクなキーバインドが使えるようになる。
remap 'C-b', to: 'Left' remap 'C-f', to: 'Right' remap 'C-p', to: 'Up' remap 'C-n', to: 'Down' remap 'C-a', to: 'Home' remap 'C-e', to: 'End' remap 'C-k', to: ['Shift-End', 'Ctrl-x']
また、xbindkeys等のツールより優れている点はアプリケーション*2ごとのキーバインドを設定できるところにある。普段Emacsバインディングを使っていると、Slackが割りあてるC-k
とコンフリクトするわけだけど、例えば以下のように書くとSlackでのみ下記の設定を適用し問題を回避することができる。
window class_only: 'slack' do remap 'Alt-n', to: 'Ctrl-k' end
あと、任意のキーからシェルを起動することができるので、ランチャーとしても使える。
remap 'C-o', to: execute('nocturn') remap 'C-u', to: execute('google-chrome-stable') remap 'C-h', to: execute('urxvt')
補足だけど、ある単一のキーを別のキーにリマップするみたいなことはXmodmapとかでやったほうがいいと思う。その方が多分速いので。僕はErgoDoxのファームウェアでそういうリマップはできるので不要なんだけど、xremapはEmacsバインディングみたいな何らかのキーの組み合わせをキーの組み合わせやシーケンスに変換したい時に使う。
なぜ作ったのか
GitHub等でキーバインドが奪われるし、その度にJavaScriptを読みたくない
GitHubというサイトはC-k
, C-b
を奪ってくる。社員は誰もEmacsを使ったことがないに違いない。
僕はいままでEmacsバインディングはinclude "/usr/share/themes/Emacs/gtk-2.0-key/gtkrc"
によって設定していたんだけど、これだと普通にブラウザに奪われるため、user.jsを書く必要があって面倒だった。
xremapを使うとXのルートウィンドウレベルでキー入力イベントをフックしてリマップするので、例えばGitHubのコメント欄とかでブラウザにキーバインドが奪われない点が便利。
rbindkeysがRuby >= 2.2で動かない & wineを使うとSEGVする
類似のツールにrbindkeysというものがあり、というかほぼ要件を満たしていたので長い間使っていた。が、上述したようなランチャーの用途には使えず別のツールで解決していたのとか、単純に不安定なのとか、DSLが複雑などの理由でずっと自分向けの奴を作りたいと思っていた。仕様も内部実装も仕組みも全て僕の好みに変えたrbindkeysがxremapといえる。いままでお世話になりました。
rbindkeysと比較して、xremapは下記のような違いがある。
- キーイベントの取得やリマップにLinux Input Subsystemを使わないので、Linuxじゃなくても(多分)動く & rootじゃなくても実行できる
- キーボードを抜き差ししてもそのまま使える
- DSLの違いにより、キーの指定がシンプルに書ける
C-k
とかのリマップの挙動が安定している
あと、この場合別にRubyでもmrubyでもどっちでもいいなあとは思ったんだけど、最近慣れてきたmrubyを採用している点も異なる。
macOSと違ってCommandキーがないから色々コンフリクトするのを解決したい
コピーやペーストがC-c
, C-v
などControlに割りあてられているため、例えばEmacsのC-f
とブラウザの検索のC-f
が被ってしまっていた*3。
アプリケーションごとにキーバインドを変えられる程度にはリッチかつ十分に安定したキーリマッパーがあると、macOSみたいにCommandキー相当のキーを作ることができる。会社で貸与されてるノートPCがMacBookなので今でもmacOSは使うのだけど、できれば複数の環境の差異をなくしたいというのがあった。
気持ち
デスクトップ環境でLinuxを使っている人はNocturnとxremapをよろしくお願いします。
*1:systemdとかで起動するのがよい xremap/xremap.service at 6e8e1f21285ecedfa7ac88d703ad80d25a2699dd · k0kubun/xremap · GitHub
*2:正確にはウィンドウのclass
*3:gtkrcによる設定だと、テキスト入力欄かどうかで挙動が変わっていたが、検索とかは常に使えて欲しかった
RubyConf 2016 で話してきた & MItamae v1.0.0をリリースした
RubyConf 2016で登壇してきた
2016/11/10〜11/12にアメリカのオハイオ州シンシナティでRubyConfというイベントがあって、Ruby DSLによって設定できるCLIツールをRubyインタプリタやgemの存在に依存しないシングルバイナリとして実装するための知見を「Evaluate Ruby Without Ruby」というタイトルで発表してきた。
発表資料
発表動画
RubyConfってどうなの
RubyConfはRubyKaigi並に規模が大きいもののあまりRubyのDeepな部分には期待できないカンファレンスなんだけど、当時行ったことがなかったアメリカに行ってみたいという思いがあって去年も参加していた。あと、RubyKaigiとは違った層の海外のエンジニアと話せる *1 のも良い点だと思う。
去年はRubyKaigi 2015で話したものと同じ内容のCFPをRubyConfに出してリジェクトされ悔しい思いをしたが、今年はRubyKaigi 2016で話したものと今回の新作トークの2つのCFPを出したら新作の方が通って話せることになったので、今回は海外で登壇するという目的もあった。
海外で登壇した感想
英語むずい。
僕は基本的に発表スクリプトを書かないで発表するんだけど、英語の勉強が不十分なので、英語で話すことに脳のリソースがかなりもっていかれるのと緊張でスライドに書いてあること以外に何を話そうと思っていたのか忘れてしまい、発表練習していた時に比べ発表時間が短くなった。慣れが必要なのでまたやりたい。
一方、英語ネイティブの人たちを相手に英語でトークをするという点での精神的障壁に関しては、過去のRubyConfやRailsConfの動画を見ているとそこまで流暢でない英語を話す人もいたので開き直ったら意外と大丈夫だった。やっていきがあれば何でもできる。
MItamae v1.0.0をリリースしました
上記の発表でモチベーションや実装方法を紹介しているMItamaeはItamaeという構成管理ツールの別実装なんだけど、RubyConfから一週間以上経った今、本家の機能のうちMItamaeにいれようと思っていた機能が一通り揃ったのでv1.0.0をリリースした。ついでに使われてるmrubyも最新にしてある。
初めて知った人向けに書いておくと、MItamaeはitamae ssh
などItamaeの全ての機能を置き替えるものではなく、ツールの特性や用途は少し異なる。MItamaeについて日本語で紹介している記事はこちら:
Itamaeのmruby実装「MItamae」が大体いい感じになった話 - k0kubun's blog。
現在の導入実績について
社内での導入はWIPなんだけど、既に社外での利用事例がある模様。ありがとうございます。
packerのビルドのためにmitamaeが投入されてた。
— FUJI Goro (@__gfx__) 2016年11月14日
お気持ち
というわけで機能も揃って導入実績も増えてきたMItamaeをよろしくお願いします。
mitamaeを使ってみたまえ
— k0kubun (@k0kubun) 2016年11月18日
Itamaeのmruby実装「mitamae」が大体いい感じになった話
Roppongi.rb #2 で「mitamae」について話してきた
Roppongi.rb #2が "Infrastructure x Ruby" をテーマに開催され、そこで RubyなしでItamaeレシピを実行できる「itamae-go」を作った - k0kubun's blog 話と pure mrubyで実装されたItamae「itamae-mruby」を作った - k0kubun's blog 話をしてきた。
いいたかったことはスライドの通りだけど、枠が15分でいろいろ漏れた話を書いておく。
mitamaeの現状について
なんでitamae-mrubyからMItamaeに変えたの
というか一昨日までmitamaeはitamae-mrubyという名前だった。エエー。変えた理由は真面目な奴がいくつかあるんだけど、あえて不真面目な奴だけ書くと、名前が微妙なソフトウェアは流行らない気がしていて、itamae-goよりitamae-mrubyの方が筋が良いのにitamae-goの方がウケが良かったので、名前を変えてみることにした。
ちなみに僕はリネームの常習犯で、Hamlitは元々違う名前だったし、activerecord-precountは最初にrubygemsにプッシュしてから3回リネームしているし、他にもいろいろやらかしているので、mitamaeもあと2回くらい変身の可能性を残している。
原案はmrubyで何かとお世話になっているksssさんの発言なんだけど、mitamaeだと「見給え感」が強すぎるのでMItamaeということにしている。 v1.6.3 からmrubyと合わせてログやドキュメント上の表記をMItamaeからmitamaeに変更にした。*1
mitamae、一見すると自己顕示欲の塊みたいな名前ですごい
— ホームページビルダー (@r7kamura) 2016年11月1日
プラグイン機構について
resourceプラグインをサポートした。社内でItamaeになってる奴をスッとmitamaeにしようとした時に、portageとかはプラグイン化したいみたいなことを言われたのでサポートした。
で、まあmrubyを使う以上rubygemsは利用できないので考える必要があって、普通にmrbgemを使うとプラグインを組み込む度にmitamaeをコンパイルし直すという話になるのだけど、シングルバイナリで配布してる意味が薄れるので、pluginsディレクトリにmrbgemと同じディレクトリ構造 *2 でRubyのスクリプトを置いておくと動的にロードされるようにした。
なので、リポジトリにgit submoduleでプラグインをいれるか、最悪vendoringするというのが想定された使い方になっている。これに関してはいろんな人の意見を聞いてみたい。
来週の話
今日はItamaeを軸に話をしたけど、来週の金曜にitamae-go, mitamaeを題材にしつつmrubyを使う側を軸にRubyConfで登壇するので、興味がある人はシンシナティで僕と握手。
SSEを使ってHTMLエスケープを高速化してみた
高速なHTMLエスケープをするライブラリを作った
ある日HTMLエスケープを速くしたくなって、hescapeというライブラリを作った。
とにかく速いHTMLエスケープがしたい
Railsアプリのビューのレンダリングにおいて、CGI.escapeHTML
を高速化*1することでRailsのデフォルトのテンプレートエンジンが大きく高速化されたり*2、GitHubでもHTMLエスケープが全体のパフォーマンスに影響が大きかった事例もある*3など、常に自動でHTMLエスケープが行なわれるRailsの環境ではHTMLエスケープの速度が割と大きな意味を持っている。
従って、Hamlitの最速性を維持するためにHTMLエスケープのパフォーマンスを極めておきたかった。
vmg/houdini を倒したい
前述したGitHubの人が既にhoudiniというかなり速いエスケープライブラリを作っていて、escape_utilsというgemを使うとHamlやSlimでそれが使え、HamlitやFamlはデフォルトでそれが使われるようになっている。Rubyにおいてこれより速くHTMLエスケープを行う手段を知らないので、これを越えることが目標になる。
Streaming SIMD Extensions
とはいえhoudiniはよくできているので、一緒に協力してテンプレートエンジンを作っている*4 id:eagletmt さんと去年「(HTMLエスケープは)まああれより速くしようがないですよね」みたいな話をしたのだけど、この前Tokyo RubyKaigi 11の懇親会で id:nurse さんが「HTMLエスケープはSSE使えば速くできるよ」とおっしゃっていた。
h2oのHTTP/1パーサに使われているpicohttpparserもSSE命令(PCMPESTRI)で速くなっていて *5、ループで1文字ずつチェックしていたのが1命令で16文字同時にチェックされるようになっている。HTMLエスケープでも、エスケープすべき文字がない時特に速くなりそうな感じがしたので、試してみることにした。
一人チューニング大会
SSEを使う以前にそもそもCで速いコードを書くのがあまり得意ではないので、事前にテストとベンチマークを用意して、ひたすらベンチマークの結果をよくする、という一人ISUCONみたいなことをやっていた。結構面白いので、やってみたい人は以下を読まずにHTMLエスケープ(CGI.escapeHTML
コンパチのもの)を実装してみると良いかもしれない。
ベンチマークには、一切エスケープをやらないもの、全部エスケープするもの、あとライバルのhoudiniが使っているものを用意し、CIでhoudiniの何倍速いかというのを出していた*6。以下は実装ログとwercker上でのベンチマークの結果(houdiniより何倍速いか)の推移である。
コミット | 概要 | no escape | all escape | houdini bench |
---|---|---|---|---|
Implement HTML escape | 素直に実装した奴。遅い。 | 0.04x | 0.43x | 0.08x |
Reduce allocation times | エスケープが発生した時のみreallocするようにした | 0.53x | 0.43x | 0.85x |
Lazily copy unescaped characters | 非エスケープ文字はエスケープが発生した時にまとめてコピーするようにした | 0.62x | 0.35x | 0.93x |
Skip allocation when nothing is escaped | エスケープが行なわれない時はmallocが起きないようにした | 1.13x | 0.34x | 0.91x |
Increase allocation size by 1.5 | houdiniのパクリ。2回目以降のエスケープ時、メモリが足りない時に必要分の1.5倍メモリを確保している。いいのか…と思うけど対houdiniなら公平。 | 1.12x | 0.61x | 1.01x |
Optimized strlen of escaped string | strlen対象が限られてるのを利用してstrlenを四則演算に変えている。やんちゃチューニング感ある。 | 1.08x | 0.99x | 1.11x |
Change ensure_allocated to be static | ある関数をstaticに変えただけ | 1.08x | 1.16x | 1.13x |
Skip non-escaped characters fast | この辺で語られてるテクニック(をやっているつもり)。エスケープしない時に局所的にループを回している。 | 1.17x | 1.16x | 1.14x |
Optimize by pcmpestri intrinsics | ここでSSEを使った。全部エスケープだと遅くなるが、エスケープがない場合は本当に速い。 | |
0.83x | 1.63x |
結果
普通のWebアプリのほとんどのケースでは実際のエスケープは走らないわけだけど、そのケースにおいて PCMPESTRIを使っただけで6〜7倍になった(下記の追記を参照)。ただ、あまり何も考えないで作った非現実的なベンチなので、参考にするなら例えばURLくらいの長さの文字列のエスケープを見た方が良いと思う。
本当はHamlitにいれようと思ってたけど、保守性が下がる割に全てのケースで速くなるわけではない*7のと、ビルドの時に考えることが増えるので、HamlitでSSEを使うのは一旦見送ることにした。
08/16 23:35 追記
shinhさんににご指摘いただいた致命的なバグを直したことによりエスケープなしのケースが 7.29x→2.78x くらいに変化している。いろいろ指摘をいただいたので直している最中で、それが終わってみないと実際どのくらいの速さになるものなのか不明。
感想
最初どうやって使うのかが全然わからず難しいなと思ったけど、その難しさに見合う結果は得られたのでよかった。picohttpparserのコードを参考にしてたのでSSE4を使っているけれど、後で後継のAVXも試したい。
*1:https://github.com/ruby/ruby/pull/1164
*2:http://k0kubun.hatenablog.com/entry/2016/05/29/215851
*3:https://github.com/blog/1475-escape-velocity
*4:http://k0kubun.hatenablog.com/entry/2015/12/12/000037
*5:http://blog.kazuhooku.com/2014/12/improving-parser-performance-using-sse.html
*6:10%エスケープというのも見てたけど、houdiniのベンチとあんま変わらんのと横幅の関係でこの記事からはカット
*7:これもうちょっとなんとかならないかな…
pure mrubyで実装されたItamae「itamae-mruby」を作った
itamae-goを作り直してitamae-mrubyを作った
先週Goからmrubyを使ってRubyなしでItamaeレシピを実行できる「itamae-go」を作ったんだけど、全く同じコンセプトの、RubyなしでItamaeレシピを実行できる「itamae-mruby」を作った。
itamae-goの問題点
mrubyは組み込み言語だしこれは本来想定された使い方であり、go-mrubyの実用的な例として普通に作ってよかったと思っているけど、ことItamaeを実装することに関しては以下のような問題があった。
- レシピを読む部分以外をGoで実装していたので、specinfraのコードの移植に手間がかかる
- 主にstandaloneなバイナリを吐く目的にGoが使われているが、mruby-cliでもできるのでGoを使っているメリットがそれほどなく、2つの言語をブリッジするコードを書く労力に見合わない
僕としてはRubyなしで動くItamaeは環境構築の用途にやっぱり欲しいし、ちゃんとメンテし続けたいので保守がしやすいように作り直した。
mruby-cliとは
Goと違ってmruby自体には自分が書いたスクリプトをstandaloneなバイナリにする手段はない(多分)。そこで、mrubyのC APIを使ってmrubyで書いたコードの__main__
メソッドにargvを渡して呼び出すCのコードと、それをクロスコンパイルするDocker環境を提供しているのがmruby-cliである。 なので、mruby-cliを使えばRubyの存在に依存しないバイナリをRubyで書くことができる。
itamae-mrubyの現状
- 現時点で基本的な機能はitamae-goのスーパーセット
- mruby-yamlのクロスコンパイルがうまくいかないので一旦
--node-yaml
は外している
- mruby-yamlのクロスコンパイルがうまくいかないので一旦
- OSX, Arch, Debian, Ubuntuに加えCentOS, Gentooとかもサポートするようにした
- 多分動くけどまだちゃんと検証できてない
- core mrbgemのバグを踏み、eachの中だとresourceが動かなかった
itamae-mrubyの設計
ryotaraiさんがmrubyで実装した奴をItamae 2にしようとか言ってたので微妙に手間をかけて作っている。まあそんな簡単には置き替えられないと思うけど。
- resourceからresourceを実行する部分を分離し役割を減らしている
- ryotaraiさんのアイデアで、actionを実行するのではなく事前状態と事後状態を受けて適用するようにしてみている
- specinfraは移植しやすいよう完全にそのまま
mrubyだけでCLIを実装した知見
mrubyはまだまだ未整備な部分が多くフロンティア感がある。普通にRubyで実装するのとはいろいろ違うところがあったので、そのへんの感想を書いておく。
Rubyにはあるがmrubyに存在しなかったもの
rubygemsが使えないというのがやっぱり大きい。そして標準ライブラリは大体ないし、本家だと言語機能になっている部分がないこともあり、やっていく必要がある。以下に私のやっていきにより生まれたものを書いておく。
mruby-hashie
ItamaeはHashie::Mash
をガッツリ使っているし、社内のレシピでも使われてるのでmrbgemにしておいた。元のコードがMIT Licenseなので、ライセンス表記やauthorsとともに実装をいただいたらほとんどそのまま *1 で動いた。なお、Hashie::Mash
しかない。
@k0kubun ないぞ
— r7kamura (@r7kamura) 2016年7月30日
mruby-shellwords
標準ライブラリ。かなりコマンドをガチャガチャやるので普通に必要だった。Itamaeで必要だったメソッドはそのままのコードで動いた。
mruby-open3
標準ライブラリなんだけど、これを作るのはコピペでは全然動かないし結構大変。まず既存のmrbgemにspawn
がないので、それを自分で書く必要がある。spawn
は本来mruby-processとかで実装すべきだけど、spawn
のオプションをいろいろサポートするのは実装するのは割と大変なので、outとerrオプションを必ずredirectするインチキspawnを内蔵している。
意外とmrubyにも存在したもの
Rubyに比べてしまうともの足りなく感じるけど、mrbgem自体は結構ある。個人的にはmruby-threadとかmruby-tempfileがあるのが助かった。
GitHub - mruby/mgem-list: A list of all GEMs for mruby to be managed by mgem
ファイル名順にソースが読み込まれる
Dir.glob("#{dir}/mrblib/**/*.rb").sort
の順に読まれるので、主に継承が必要な時にファイル名の先頭に数字をつけるみたいなことをやる必要がある。
mruby-requireを使うと綺麗に書けるんだけど、実行時にファイルを探しにいくのでワンバイナリを作る用途では使えなそう。
bundle gemコマンドみたいな奴
あった。便利。
まとめ
Rubyなしで実行したいコマンド作る用途にもmrubyは便利なので、この知見を参考にmrubyを使う人が増えてmrubyのエコシステムがよりよくなってほしい。あとitamae-mrubyもよろしくね
*1:&:sym でto_procする奴がなんか動かなかった。mirbでは動くので多分サポートはされてるんだけどmrbgemの中だとなぜか…。あとでちゃんと調査する
RubyなしでItamaeレシピを実行できる「itamae-go」を作った
Goとmrubyを使ってitamae-goを作った
Pokemon Goが流行っていたので流行に乗じてItamae Goを作った。
というのは冗談で、手元の開発環境のセットアップにitamaeを使っているのだけど、まっさらな環境でitamaeを実行したい時にRubyやitamaeをどういれるかについて考えるのが面倒なので、Rubyなしで実行できるitamaeを作った。Goで実装し、mrubyでレシピを読むことによりRubyなしでの実行を実現した。
インストール方法
Releasesにバイナリを置いてあるのでこれをダウンロードする。基本的には環境セットアップ用のシェルスクリプトからこれをcurlなりwgetなりでダウンロードして使うことを想定している。
なんか動かなかったらgit clone
してmake
すればその環境用のバイナリが作れるはず。 *1
使い方
普通にitamaeレシピを書いて、
# recipe.rb package 'vim' do action :install end service 'sshd' do action [:start, :enable] end
itamae-go localを実行する。
$ sudo ./itamae-go local recipe.rb INFO : Starting itamae... INFO : Recipe: recipe.rb INFO : package[vim] executed will change from 'false' to 'true' INFO : service[sshd] executed will change from 'false' to 'true' INFO : service[sshd] executed will change from 'false' to 'true'
CLIは大体本家itamaeと同じになるように作っていて、Thorじゃなくてmitchellh/cliを使ってる都合でitamae help
がitamae-go --help
になってるところだけ違う。itamae-go sshはなくて、itamae-go localのみ。
実装ステータス
開発環境によく使われそうなOSXとUbuntu(Debian)と、僕が使うArch Linuxをサポートしている。
実装している機能のリストはREADMEに書いてあって、_exampleに入ってるような機能は動く。 けど、一見実装されてそうな機能をいろいろサボってたり、まだ僕もそんな使ってない状況なので、自分で直しながら使うつもりの人以外は使わない方が良さそうなステータス。
Goからmrubyを使った感想
これを実装するために一昨日あたりに初めてmrubyを触った。あとプログラムに別の言語を組み込むのも初めてだったので、その辺の感想を書いておく。
良かった点
両方の言語の良いとこ取りができる
Goの豊富な標準ライブラリや高い性能を生かしつつ、RubyのDSLやmrbgemsを活用した機能を持つことができ、両方の言語の良いところがそなわり最強に見える。
mrubyが思ったよりいろいろできる
mrubyはIOがないとかrequireがないとか、あんまり普通にプログラミングできるイメージではなかったけど、mrbgemsを追加してmrubyをビルドすれば割と目欲しい機能は揃う。やんちゃコードのミルフィーユみたいな奴も動いた。
悪かった点
Goとmrubyを行ったり来たりするコードを書くのが面倒
少なくともmitchellh/go-mrubyにあるAPIでは、Rubyで作ったデータ構造をいい感じにそのままGoの世界に持ってくる手段がない。具体的にはGoでJSONを読み込む時みたいにstructにtagを書いておくとRubyのstructがそのまま読めるみたいなのをやりたい気がするが、まあなかったのでRuby側にHashをとっておくコードを書いてkey一つごとにメソッド呼び出しをすることで取得していた。*2
あとは、そもそもRubyのコードをGoの文字列として持つしかないのでその時点で汚ないコードだし、それをどこに配置するかみたいな設計も難しくなる。
クロスコンパイルが大変
というか僕がcgo有効なGoのクロスコンパイルに慣れてなくてまだできてない*3。Goとmrubyそれぞれではクロスコンパイルができるので多分できるんだろうけど、普通のGoやmrubyのクロスコンパイルに比べるとちょっと面倒そう。
気持ち
mruby-cliでもワンバイナリにできるし、mrubyになくて困ったのは__FILE__
くらい*4だったので、mrubyだけで作っても良かったんじゃないかと思ったけど、組み込み言語に関する知見は結構あったので良かった。