ポクポク

ポクッとしてツナッ

Perl でテンプレートの変数に型を書く in 2017

この記事ははてなエンジニアAdvent Calendar 2017の22日目の記事です。前日は id:takuya-a さんの『Bing検索の裏側―BitFunnelのアルゴリズム』でした。たいへん興味深いですね。


はてなPerl アプリケーションが多い会社ですが、今年のアドベントカレンダーは全く Perl の話が出てきてない......。でも僕は Perl 大好き少年なので、このまえ作った CPAN モジュール Text::Xslate::Bridge::TypeDeclaration の話をします。

一般的な Web アプリケーションではテンプレートエンジンに変数を渡して html を組み立てますよね。 はてなでは Text::XslateTTerse Syntax を利用しているプロジェクトが多いです。 例えばユーザ情報を表示するサイドバーの部分テンプレートはこんな雰囲気になるでしょう。

<!-- _sidebar.html -->

<div id="sidebar">
  <div id="profile">
    <img src="[% user_account.icon_url %]" />
    <a href="[% user_account.profile_page_path %]">[% user_account.name %]</a>
    <p>[% user_account.profile_description %]</p>
  </div>

  [% IF show_recent_entries %]
    <ul id="recent-entries">
      [% FOR entry IN entries %]
        <li><a href="[% entry.permalink_path %]">[% entry.title %]</a></li>
      [% END %]
    </ul>
  [% END %]
</div>

このぐらいのテンプレートなら簡単に把握できるけど、実際はこの30倍ぐらい複雑だぞ!!

  • どういう変数があるのかぱっと見よくわからない
  • それぞれの変数はどのようなオブジェクトなのかよくわからない
  • 分割されたテンプレートは再利用可能で良いけどエンドポイントによって変数の渡し忘れが起きがち
    • 一見ちゃんと動いているように見えたりする

などなど気になりますね。

テンプレート中の変数のコメントの歴史

過去のプロジェクトのテンプレートを見てみると、テンプレート中の変数に立ち向かう人類の努力の跡をみることができます。

暗黒時代

<div id="sidebar">
  ...
</div>
  • ただテンプレートがあるだけ
  • 表示のためにどういう変数が必要かは毎回頑張って読み解く、体力が必要
  • よく壊れるし壊れても気づきにくい

冒頭にコメントを書く時代

[% # user_account:  閲覧対象のユーザオブジェクト
   # show_recent_entries: 最近のエントリを表示するオプション
   # recent_entries: 最近のエントリの配列
%]

<div id="sidebar">
  ...
</div>
  • どういう変数を期待しているかは分かる
  • どういうオブジェクトかは変数名やコメントから読み解く
  • 修正時に同時にコメントを修正するのを見落としがち、レビューで気をつける

コメントに型を書く時代

Smart::Args が流行ってコメントに型を書くようになった頃です。

[% # user_account: 閲覧対象のユーザオブジェクト(Model::UserAccount)
   # show_recent_entries: 最近のエントリを表示するオプション(Bool)
   # recent_entries: 最近のエントリの配列(Maybe[ArraRef[Model::Entry]])
%]

<div id="sidebar">
  ...
</div>
  • 型の表記方法に合意が取れた状態
  • 変数のオブジェクトが何か分かるのでどうアクセスすれば良いか分かる
  • レビューで気をつける世界観はそのまま

型をコメントしつつ動くコードにする時代

型を書く期の少し後はこういうのがありました。

[% SET user_acocunt        = visiting_user_account    # 閲覧対象のユーザオブジェクト(Model::UserAccount) %]
[% SET show_recent_entries = show_recent_entries || 0 # 最近のエントリを表示するオプション(Bool) %]
[% SET recent_entries      = recenet_entries || []    # 最近のエントリの配列(Maybe[ArraRef[Model::Entry]] %]

<div id="sidebar">
  ...
</div>
  • 単なるコメントではなく動くコードではある
  • 変数のデフォルト値を設定したりもやる
  • そこまで何かが良くなっている感はない

などなどいろんな工夫で変数に立ち向かってきたものの、これだ! という方法はありませんでした。

こうしたかった

  • テンプレートに型注釈を書きたい
  • コメントではなく動くコードになってほしい
  • おかしければエラーを出したいし、テストでもチェックしたい

という要求を叶えるために、Text::Xslate::Bridge::TypeDeclaration というモジュールを作りました。

こういう感じにつかえるぞ

[% declare(
    user_account        => 'Model::UserAccount',
    show_recent_entries => 'Bool',
    recent_entries      => 'Maybe[ArraRef[Model::Entry]]',
) %]

<div id="sidebar">
  ...
</div>

変数と宣言している型が合わなければ以下のような感じでエラーがテンプレートに挿入されます。

f:id:pokutuna:20171222015047p:plain

テストでは型エラーの要素があればテストが落ちるようにしています。本番環境ではエラーメッセージから情報が漏れないよう、レンダリングのパフォーマンスを落とさないように型チェックを無効にもできます。 これで複雑なテンプレートを書き換えることになってもヘトヘトにならずに済むぜ!!!

どういう感じか

Type::Tiny を利用して型チェックを行っています。
型ライブラリを利用していないプロジェクトでも導入できるようにデフォルトでは Types::Standard の型を利用可能にし、未定義の型名は isa として扱います。(Smart::Args のような敷居の低さを目指した)

Type::Tiny は特に Tiny な感じはしないけど、{MouseX, MooseX}::Types::Base の型を lookup できるのでプロジェクトの型ライブラリを制限しにくくて良さそうなので採用しました。グローバルな型レジストリが無い点は戸惑ったけどアプリケーションとテンプレートで共通の Type::Registry を使うというのは状態が隠れていなくて良い気もします。

単機能なプラグインを Text::Xslate::Bridge:: 以下に作るのは迷いましたが、Xslate->current_var にアクセスしたかったり、エラーをテンプレートに注入したかったので Bridge として作りました。 Text::Xslate::HashWithDefault と組み合わせて未定義変数への参照も行えばより堅牢なテンプレートを作ることができるでしょう。

ぜひご利用ください!!!

そして明日は id:wtatsuru さんです。たのしみですね。

Mackerel で仕事のがんばり具合を見える化する

この記事は Mackerel Advent Calendar 2017 の21日目の記事です。 前日は id:heleeen さんの ハムスターのために監視導入したら人間のための監視もしたほうが良さそうなことがわかりました でした。湿度が上がってよかったですね。


突然ですが僕の今日の仕事の様子です。

f:id:pokutuna:20171221174736p:plain

10:05 ぐらいから仕事を始めてますね。そこから 10:40 ぐらいまで Chrome で GHE 上のレビューをしてました。10:50 分ぐらいまでの空白時間はたぶんトイレでしょう。そこから Emacs と iTerm2 をばりばり触ってるのでコードを書いていた気がします。12:30 ぐらいに一段落して、ブラウザでの動作確認やレビュー依頼を書いています。13~14:00 が昼休みなのですが、勤勉極まりない僕は 13:10 ほどまで仕事をしていたようです。午後は溜まっていたレビューを片付けていたので Chrome と iTerm2 にほどほどにタイプしてますね。この記事は 16:40 頃から Emacs で書き始めました。

というようなメトリックを Mackerel に記録できるぞ!
このアプリケーションを入れるんだ!! Mac 専用です!!! https://github.com/pokutuna/MackerelAppActivity.app

やっていることは簡単で、その時点でアクティブなアプリケーション名をメトリック名として、タイプ数を値として定期的に Mackerel に送信しているだけです。

つかいかた

インストール

なんと id:aereal さんのおかげで Homebrew で入れることができます。

# https://github.com/aereal/homebrew-mackerelappactivity
brew tap aereal/mackerelappactivity
brew cask install mackerelappactivity

設定ファイルをおく

こういう JSON~/.mackerel-app-activity.json においてください。 コメントは消してくれ!!

{
    // Mackerel の API キー, 書き込み権限が必要です
    "ApiKey": "********",

    // Mackerel 上の Service 名です
    "ServiceName": "clients",

    // メトリックの prefix です, PC の名前とか入れると個別のグラフにできるぞ
    "MetricPrefix": "activity.types.",

    // メトリック名には BundlerIdentifier(アプリ識別子のようなやつ)の末節を使いますが
    // アプリによっては分かりづらいのでメトリック名の対応を指定できます
    "NameMapping": { 
        "slackmacgap": "Slack",
        "LimeChat-AppStore": "LimeChat",
        "keychainaccess": ""  // メトリック名が空だと送信しない
    },

    // Mackerel への API リクエスト間隔(分)です
    "PostIntervalMinutes": 1
}

アクセシビリティに追加する

キー入力を検出するのに権限が必要なので "アクセシビリティ" への追加が必要です。 僕には悪意はないですが信用もないので、各自の責任でアクセシビリティに追加してください。しくみはキーロガーと同じですからね。

あとはアプリを起動すれば動くはず!

アイコンについて

このアイコンは鯖の正面顔をイメージして僕が書いたものです。 メニューバーに常駐するので悲惨、プルリクお待ちしております。 コードも勘で Swift を書いたという風情なのでそちらもお待ちしております。

紅葉おにゅう峠

前回は紅葉まだだったので 10/31 にまたおにゅう峠に行った。だいぶん紅葉しているけどまだ色づく余地は残っていそう。

image

image

頂上の分水嶺で、日本海側はパッキリ落葉していて世界が変わってる。
このルートだいたい150kmあってほどほどにキツくて良い。雰囲気も静かで道に沿う川もやたらと透明感がある。来週ぐらいにもまた行って紅葉の様子を確かめたい(が週末雨予報である)。あと 11/2 でロードバイク納車からちょうど2年経ってた。

おにゅう峠成功

3月末に雪で失敗した「おにゅう峠」に行ってきた。 pokutuna.hatenablog.com

この雪に埋まってた道が

通れるようになってた。

雪で通れなかったふもとから峠まで長くて、6km ほどガレた道が続く。稜線を登れる。

よく写真で見る場所、登ってきた道が見える。紅葉まだだった。

福井側と京都側

朽木~おにゅう峠~能見峠(久多峠) は静かでいいルート、おにゅう峠の周りでしか人と会わない。 朽木のコンビニから、久多の集落手前まで飲み物を買うところがないから夏は行きづらそう。

帰りに花脊の手前でボトルを落としたことに気づいた。6500km ぐらい使ってたやつなので哀愁がある。すぐ 1 Click で注文した。


おきにいりの川です。雪が残るぐらいのほうがきれい。

3月

10月

シャロちゃんに生活費あげたい

[12:14] pokutuna: シャロちゃんに生活費あげたい
[12:14] pokutuna: たんに生活費あげるだけだと気を使うだろうから
[12:14] pokutuna: 晩ごはん作ってもらうかわりに2人分食費を払うとかいう形に落ち着けたい
[12:15] pokutuna: 毎日一緒にご飯食べつつ、ついでに大学受験の塾代とか払いたい
[12:15] pokutuna: 最近そういうことばかり考えている

もちろんちょっと多めに渡します

で、ORTLIEB サドルバッグのネジはどう改造すればよいのか

サドルバッグの定番 OLTLIEB(オルトリーブ)。
防水で、特に L サイズは大容量なのでブルベや旅行の定番になっている。そしてバッグ内側に出っ張っているネジ(ナット)がいくらなんでもデカすぎて、交換する改造も定番になってる。僕も交換してみたんだけど何回か失敗してようやく安定するようになった。

当初僕はは自転車で2泊するために L サイズを買って、サドル側のアタッチメントがサイズ間で共通なので Micro も買った。普段はパンク修理道具入れの Micro を使ってて、旅行の時は L サイズに Micro を入れて持っていってる。L サイズはやっぱり重いし、両方あると滞在先に L サイズを置いて Micro だけで行動もできて便利。年末の Festive 500 でも、このまえの 乗鞍岳も滞在先では Micro だけで過ごした。

L サイズの場合は容量全体に対するネジの割合は微々たるものだけど、Micro はいくらなんでもネジのせいでめちゃくちゃ使いづらい。ネジ自体の大きさは大したことなくても、でっぱりのせいでパッキングしにくくて容量を食われてしまう。これは1本だけ低頭ネジに交換した状態、他は元のネジ。

image

一度適当に交換したところ、走行中にネジがゆるんで外れてしまった。なんとか持ってたビニール袋を紐状にしてサドルにくくりつけて帰った。じわじわと緩んでくるのでワッシャのはさみ方をいろいろ試したところ、以下の形に落ち着いた。

(内側) ナット - 平ワッシャ - [内板] - [布] - [台座]- バネワッシャ - ネジ頭 (外側)

ポイントは台座とネジ頭の間にバネワッシャ(スプリングワッシャ)を挟むこと。
ワッシャがプラの台座に食いついてネジが緩んでこなくなった。10ヶ月(3000km~)増し締めしてないけど無事に固定できてる。外してみたところ、台座にくっきり跡がついていて固定力を感じる。内側の平ワッシャはとりあえずつけてるけど意味あるかは分からない、内板よりは滑らなさそうではある。

image

ネジは錆びてほしくないからステンレスで、内容量のためなのだから超低頭が良い...良いのは良いけどめっちゃ高い。材質がステンレスになると値段がグッと上がって、超低頭になるとさらに上がる。これなんか4本で860円する。ステンレス超低頭小ねじ 4x12 4本 トラスや低頭のナベ頭でもまあいいのではないかと思う。ネジの頭についてはこの 頭部形状 のサイトが勉強になる。

六角だと自転車工具で留めやすくていいかも...とおもいきや、短く回しやすいものが無いのでサドルバッグの中で回すのに苦労しそうなのでやめた。ネジを内側から回すならスタビドライバーが必要になる。十分に小さいラチェットドライバーがあると良さそうだけどやや高い(1000円~)。スタビで良いと思う。もともとのネジを外すには内側からモンキーレンチでナットを固定して、外側からドライバーで回すと良い。

太さは 4mm (4M) でぴったり。長さは、内側にもワッシャをはさみたいなら 12mm, なくてもよいなら 10mm だと思う。長いほうが多少固定しやすく作業性は良い。ただ Micro を交換する場合、一番奥のネジがアタッチメントに干渉してしまう!! のでここだけネジの頭を外側にして、内側を袋ナットで固定することにした。ボルトの先端が内側に出ていると、振動によって中身がじわじわと削られてしまう。いつのまにかチューブを入れているジップロックに穴が空いていて、こりゃパンクした時に交換チューブにも穴が空いてるパターンになるな、と思って袋ナットにした。

image

今現在の Micro の改造結果はこちら。

image

まとめ

  • 台座側のプラスチックにバネワッシャを入れるとがっちり止まるぞ
  • 頭部は 超低頭 が良いが高い、トラスや低頭(ナベ)でも良いかも
  • 内側からネジを回すのは難しいからスタビドライバー(安い)かラチェットドライバー(高い)を買おう
  • ネジの長さは 10mm か 12mm、太さは 4mm (4M) がよい

策を弄せず、ロックタイト塗ったらいいんじゃないでしょうか。

オルトリーブのサドルバッグ、最近どこも在庫切れでそろそろ刷新されるんじゃないか... という気がする。