りばーさいだー

気ままに書いてみます

Rails5.0 から ActiveRecord::Relation#slice が使えなくなる話

tl;dr

  • 4.2 までは ActiveRecord::Relation#method_missing で一部メソッドを Arraydelegate していた
  • 5.0 からはその処理が削除され、その代わりに Enumerable を include するように修正された
  • そのため、ほとんどのメソッドはそのまま使えるが、 #sliceEnumerable に存在しないため、使えなくなった

きっかけ

某システムのバージョンアップ中、変な箇所で落ちた。

内容は ActionView::Template::Error : undefined method `slice' for <Hoge::ActiveRecord_Relation:0x0000> というもの。

最初は、そんなわけあるか? と思いながら rails のコードを読みにいったのでした。

ブラックリストは管理しづらい

早速 railsリポジトリに飛び、 compare しながら 該当のコミットを探しだし 、コメントを要約すると以下のようなものでした。

mutaion methods( #compact! とか #sort! とか )をブラックリストで管理しているのは非網羅的であり、 Ruby のバージョンごとに差分をキャッチアップしていく必要がある。

そう、4.2 までは Array のメソッドの中でも mutation methodsブラックリストとして定数で管理していたのでした。

  BLACKLISTED_ARRAY_METHODS = [
    :compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
    :shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
    :keep_if, :pop, :shift, :delete_at, :select!
  ].to_set # :nodoc:

当然運用が面倒で、Ruby のバージョンと Rails のバージョンの依存関係も複雑になることは容易に想像がつきます。

そして Enumerable を include したことにより(結果的に)複雑な運用から手を引けるようになった、と。なるほど。

深淵に落ちていった #slice メソッド

待て待て、いやいやおかしいじゃないかと、そう思いますよね。

うんだって、 ActiveRecord::Relation で普通に #[] メソッドは使っているはずだし、このメソッドは機能として落とされることは考えづらい。

にも関わらず #slice が使えないなんてどういうこと…? 、と。 ※ Array では #slice#[]エイリアス

では 見ていきましょう

  delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join,
    :[], :&, :|, :+, :-, :sample, :reverse, :compact, to: :to_a

... え?

いやこれ、#[]#to_adelegate されてますよね。 #to_a@records で、 Array ですよね??

もしかして忘れられてそのまま落っこちていった、そういうことなんですか…?

おわりに

とまあ、真相は闇の中ですがとにかく、 #slice は使えなくなっちまいましたよと、そういうことですはい。

僕からは「 delegate するメソッドも結局 Ruby のバージョンと完全に切り離して考えることもできないんじゃないの(管理むずいんじゃないの)」というツッコミだけ残して終わりにしておきます(とはいえ良さげな解決策もパッとは思いつかないけど)。

ではこのへんで、ありがとうございました。