こんにちは。エンジニアの木村です。
早いもので 「Fenrir Advent Calendar 2016」 も半分を過ぎ、折り返しの 13 日目となりました。
「Fenrir Advent Calendar 2016」 ラストとなる 12 月 25 日はクリスマスでもあり、毎年恒例の Ruby のマイナーバージョンアップのリリース日でもあります。
Ruby エンジニアな方々は、毎年新機能に心を踊らせているのでしょうか。あるいは、既存システムへの対応に追われることになるのでしょうか。Ruby 2.4 でもたくさんのアップデートがありますが、今日はその中からいくつかピックアップしてご紹介したいと思います!
Refinements
Ruby 2.1 から登場した Refinements については以前にも本ブログで紹介したことがありました。Ruby 2.3 までと比べ Refinements のスコープが変更となり、Symbol#to_proc、Kernel#send、BasicObject#send についても refine の対象となりました。また、クラスだけでなくモジュールに対しても Refinements による拡張が可能となりました。
Refinements のスコープの変更
module Echo refine String do def echo puts self * 2 end end end class Hoge using Echo "abc".send(:echo) end #=> 'acbabc'
Ruby 2.3 まではモンキーパッチでしかできなかった呼び出し方が Refinements でできるようになっています。
モジュールの拡張
module HelloWorld def greet 'hello' end end module SuperHelloWorld refine HelloWorld do def greet "say #{super}!" end end end using SuperHelloWorld class Greet include HelloWorld def say "I #{greet}!" end end #=> I say hello!
クラスだけでなく、モジュールも拡張できていますね。
モジュールの拡張によってメソッド探索のルートが従来と変わってくる点についても注意が必要です。
Enumerable へのメソッド追加
Array や Hash、Range などに include されている組み込みクラス Enumerable に Enumerable#sum、Enumerable#uniq が追加されました。
Enumerable#sum
ActiveSupport を使用されていれば Enumerable を拡張した sum メソッドが既に用意されていますが、Ruby 単体でも sum メソッドが扱えるようになります。
[1, 1, 1].sum #=> 3
もちろん数値だけでなく文字列に対しても sum メソッドを用いて連結処理を行うことができます。
['a', 'b', 'c'].sum('') #=> abc
Enumerable#uniq
Array クラスには重複した要素を取り除く Array#uniq、Array#uniq! が用意されていましたが、Hash に対しても重複削除ができるようになります。
olimpics = { 1896 => 'Athens', 1900 => 'Paris', 1904 => 'Chikago', 1906 => 'Athens', 1908 => 'Rome' } p olimpics.uniq{|k, v| v} #=> [[1896, "Athens"], [1900, "Paris"], [1904, "Chikago"], [1908, "Rome"]]
Array#uniq と同じようにブロックを評価した結果が重複しているものを削除します。ただし、Array クラスに存在した破壊的メソッド Array#uniq! は Enumerable には存在しないので注意が必要です。
Kernel#clone
Ruby 2.3 までの Kernel#clone、Kernel#dup の違いはコピー対象に freeze 状態と特異メソッドを含めるかどうかでした。
Ruby 2.4 では Karnel#clone のキーワード引数により freeze 状態を含めるかどうか選択できるようになりました。
obj1 = Object.new obj1.taint def obj1.singleton_method; end obj1.freeze obj2 = obj1.dup obj3 = obj1.clone obj4 = obj1.clone(freeze: true) obj5 = obj1.clone(freeze: false) p obj1.tainted? #=> true p obj1.respond_to?(:singleton_method) #=> true p obj1.frozen? #=> true p obj2.tainted? #=> true p obj2.respond_to?(:singleton_method) #=> false p obj2.frozen? #=> false p obj3.tainted? #=> true p obj3.respond_to?(:singleton_method) #=> true p obj3.frozen? #=> true p obj4.tainted? #=> true p obj4.respond_to?(:singleton_method) #=> true p obj4.frozen? #=> true p obj4.tainted? #=> true p obj4.respond_to?(:singleton_method) #=> true p obj4.frozen? #=> false
キーワード引数に false を渡すことにより Karnel#clone で汚染状態と特異メソッドのみをコピーできていることがわかります。
条件式による多重代入
Ruby 自体既に多重代入はサポートしていましたが、以下のような条件式を用いた多重代入は SyntaxError が発生していました。
# Ruby ~2.3 の場合 if (a, b, c = ['a', 'b', 'c']) p a p b p c end #=> SyntaxError
Ruby 2.4 では条件式による多重代入がサポートされ、上記のコードを実行してもエラーが発生しないようになりました。
# Ruby 2.4 の場合 if (a, b, c = ['a', 'b', 'c']) p a p b p c else p 'hogehoge' end #=> a #=> b #=> c
多重代入の引数に nil を指定すると else ブロックが評価されます。
# Ruby 2.4 の場合 if (a, b, c = nil) p a p b p c else p 'hogehoge' end #=> hogehoge
最後に
あっと驚くような新機能や変更点はないかもしれませんが、これまで自力で実装していた機能が標準でサポートされるとコーディングが益々捗りますね!
ここでご紹介した新機能はほんの一握りですが、まだまだたくさんの修正がコミッタさん達によって加えられています。ruby-trunk を眺めているとたくさんのコミッタによって議論されながら修正が加えられていく様子がわかって興味深いものがあります。
クリスマスのイベントの一つとして「Ruby 2.4 の正式版を試す」というタスクをくわえてみてはいかがでしょうか。
フェンリルのオフィシャル Twitter アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!