ElixirのProtocolを使ってみたくてActiveSupportっぽいライブラリを作ってる

2 min読了の目安(約1800字TECH技術記事

Railsの世界で有名を馳せるActiveXXX系ライブラリ。
その中でも特にお世話になるのがActiveRecordとActiveSupportだと思います。
Elixirの作者はRailsのコミッターとしても有名な方でElixirの標準モジュールにもActiveSupportで実装されている便利な実装が多分に含まれています。

ただいくつかほしいなと思ったときに無いメソッドがあったのでProtocolの学びを兼ねてライブラリ制作に手を出してみました。

例によってQiita向けに書いたやつをこっちにも

ほしいときに無いやつ

個人的にRails書いてて(よく)使うメソッドいくつか

  • #compact
  • #blank?
  • #pluck
  • #include?

とかとか。
この内compactとblank?は実装されてないことが分っており、compactは過去にElixirのメーリングリストで議論されたことはあるもののEnum.rejectで十分でしょってことで却下されているということも分った。
他にblank?についてはBlankableというライブラリが存在しガッツリ実装されている。

逆に each_sliceとか(これはRuby標準だけど)はElixir標準モジュールにしっかり実装されており、Rubyのメソッド名と == ではないが調べるごとにElixirの標準モジュールの手厚さを実感するばかりである。

blankable見たんだけど

先のblankableの実装は実に興味深いもので このへんとか見ると defprotocolを使って実装されていたりする。

ほしいときに無いやつも多いし、hex.pmで調べたらActiveSupport系の実装は多いけど最終更新が4年前とかなのでせっかくなので自前で実装してみることにした。

作ってるやつ

だいたいまだ実装途上なんですがgr8distance/etnaでヒソヒソ開発進めています。
開発途中なんでWIPのコミットとか多いですがhex.pmに公開する前にはきちんと整理しておきます...

基本的な実装方針

先人(blankable)に倣って基本的には lib/etna.exにプロトコルを定義しています。
protocolの下に defimplしていこうかと思ったんですが実装が膨大になることは目に見えていたので lib/etna/XXX.exに書くことにしました。
現時点では lib.exがprotocolを満たした実装になっています(多分もっと処理増えますが...)

テスト側はどうしても分割できなかったので describeでモジュールごとに切ってその中に処理を実装しています。

もしも実装するべきでない処理があったら?

Protocol側には stringify_keysというのを定義しているんですがLISTにstringify_keysなんてあったものじゃないのでそういう時はElxir(本家)のEnumの実装を参考に {:error, __MODULE__}を返すようにしています。

使い方

まだテストでしか使ってないですがこんな感じになる予定です。

iex(1)> Etna.sum(["hello", "world", "!!"])
"helloworld!!"
iex(2)> Etna.sum([%{age: 20}, %{age: 30}, %{age: 40}], fn m -> m.age end)
90
iex(3)> Etna.include?([1,2,3], 2)
true
iex(4)> Etna.include?([1,2,3], 7)
false
iex(5)> Etna.compact([1,nil, nil, 5, 6])
[1, 5, 6]

気が向いたときに実装進めていきたいと思います。