Developer's Blog

まもなくリリース!グッとくる Ruby 2.4 の新機能

adventcalendar2016_13

こんにちは。エンジニアの木村です。

早いもので 「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 アカウントでは、フェンリルプロダクトの最新情報などをつぶやいています。よろしければフォローしてください!

Copyright © 2019 Fenrir Inc. All rights reserved.