mmag

ハマったことメモなど

ExUnitAssertMatchっていうパッケージを書いた

hex.pm

伝わる人には伝わる言い方をすると、https://github.com/r7kamura/rspec-json_matcherみたいなことがしたい。

例えばPhoenixのコントローラのテストとかで、レスポンスのJSONの形式をテストしたいときに

assert json["user"]["name"] == "John"
assert json["user"]["age"] == 28
assert json["user"]["orgs"][0]["name"] |> is_binary()

とか全部書くのダルいし、もうちょいどんなJSONを求めてるのかわかるようにしたかった。あとElixirのパッケージ最近書いてなくて、リハビリしたかったというのもモチベーションの1つ。最初はマクロで実装しようと考えて書き始めたけど、そのまま機能追加してメンテしていく自信がなくなったのでやめて、以下のようになりました。

alias ExUnitAssertMatch, as: Match

expected = Match.map(%{
  "user" => Match.map(%{
    "name" => Match.literal("John"),
    "age" => Match.literal(28),
    "orgs" => Match.list_of(
      Match.map(%{
        "name" => Match.binary(),
      })
    ),
  }),
})

Match.assert expected, json

気をつけた点としては

  • useさせない
  • Configつかわない

です。マクロやめたのでuseはまぁしないっしょというのはありつつ、他のパッケージのコードにuseが入ってると結構コード読みづらくなるなーという実感があるので、自分がつくるものではやらんようにしよーというもの。Configややこしい問題はhttps://elixirforum.com/t/proposal-moving-towards-discoverable-config-files/14302とかhttps://elixiroutlaws.com/1でも言われていて、徐々に解決されていきそうだけど避けるようにしている。今回のパッケージはテストのときのためのものなので大して関係ないけど、設定っぽいものは引数で渡せるようにしてます。ex_unit_assert_matchって名前は長いなと思ったけど、https://hex.pm/docs/publish#naming-your-packageに従ってこうしてます。

趣味プロジェクトで使いながら、色々追加してこうと思います。

Gettextってよくできてるんだな、という感想

Elixir経由でしか使ったこと無いけど、Wikipediaを読むとどれでも大体同じっぽい。

joe-noh.hatenablog.com

なんでよくできてると思ったかというと、仕事でRails書いててtranslation missingをチラホラ出してしまい「直してもまだ漏れがあるのかないのかわからんからコードから抽出してくれんか」と思ったところで、そういえばGettextはこれやってくれてたんだな、となったのでした。最初に触ったときはワンステップ多い印象でしたが、意味あったんすね。

dev.toコードリーディング会に参加した

smarthr.connpass.com

行ってきました。十数人で各々が好きなところから読み始めて、最後に見所や気づきを喋る流れでした。initializersを読んでいく人やモデル中心に見ていく人など様々。近日中に全員のメモが公開されるとのことですので、そのときはリンク追記します。会場を提供してくださったSmartHRさんありがとうございました。


追記

追記ここまで


以下は自分のメモ。やはりdev.toと言えば爆速なので、CSSをheadタグに埋め込んでいたり、キャッシュってどうやってんだろうなーといったところを中心に読みました。最後の所感にも書きましたが、爆速サイトをつくるためのイケてる最強プラクティスが詰まったコードを期待していた割に、泥臭さ100%のコードやら結構ひどいメソッドたちやらが溢れていて、歴史に勝てるものはおらんのじゃ...という気持ちになりました。usersテーブル辺りを見ると共感が得られると思います。やはり銀の弾丸は無く、歯を食いしばってひとつずつ問題をなぎ倒していくしか道は無いのです...。

なお、ハイライトは http://sushi.to というサイトが発見された瞬間でした。


headタグへのCSS埋め込み

Service Worker

  • serviceworker-rails使ってる
  • service-worker.js.erb
    • インストール時に最小限必要なjs, css, 画像類, offline.htmlを取得してキャッシュに入れてる
    • GET以外、通知の取得はスルーしてネットワークへ
    • 割とキャッシュに入れてない
  • serviceworker-companion.js
    • 割と普通
    • Homeにインストールの結果をGAに送っていた

どうやってCDNキャッシュしてるか

  • bodyのdata属性にユーザ情報を埋め込んでいるが、これを非同期にやっているのでCDNにキャッシュできる

initializePage.js.erb

function initializePage(){
  initializeLocalStorageRender();
  initializeStylesheetAppend();
  initializeFetchFollowedArticles();
  callInitalizers();
}

function callInitalizers(){
  initializeLocalStorageRender();
  initializeBodyData();

initializeBodyData/async_info/base_dataを叩いてbodyのdataに入れてる。入れたらlocalStorageにも入れてる。次回からはlocalStoreageから読み出してbodyのdataに入れてる。getUserData()は15秒間待ってる。

base.js.erb

function mouseoverListener(e) {
  if ($lastTouchTimestamp > (+new Date - 500)) {
    return // Otherwise, click doesn't fire
  }

  var a = getLinkTarget(e.target)

  if (!a || !isPreloadable(a)) {
    return
  }

  a.addEventListener('mouseout', mouseoutListener)

  if (!$delayBeforePreload) {
    preload(a.href)
  }
  else {
    $urlToPreload = a.href
    $preloadTimer = setTimeout(preload, $delayBeforePreload)
  }
  getImageForLink(a);
}

マウスホバーでの先読みしてる。スマホtouchstartListener

所感

CSS埋め込みとか、CDNエッジキャッシュするための工夫とか、もうちょっと高度な謎テクノロジーであのパフォーマンスを実現していると思っていたけど、読んでみたら想像を絶する泥臭さだった。総じて、この量のjsをテストほとんど無しで本当に書いたの...という気持ち。