はじめに
Rubyは毎年12月25日にアップデートされます。
まだ今年はまだpreview版がリリースされていませんが(2017年10月10日時点)、今年もそろそろリリースの日が近づいてきました。
そこでこの記事では現在開発中のdev版を参考にして、おそらくこんな感じでリリースされるであろうRuby 2.5の新機能や変更点をまとめてみました。
本記事の情報源
本記事は以下のNEWSページに掲載されている情報から、個人的に注目したい新機能をピックアップしたものです。
この記事に掲載していない変更点もあるので、詳細はNEWSページをご覧ください。
また、説明している内容に間違いがあれば、コメントや編集リクエスト等で優しく指摘してやってください。
動作確認したRubyのバージョン
本記事は以下の環境で実行した結果を記載しています。
$ ruby -v
ruby 2.5.0dev (2017-10-08 trunk 60140) [x86_64-darwin16]
コード例
本記事のコード例は以下のGitHubリポジトリに置いています。
https://github.com/JunichiIto/ruby-2-5-sandbox
それでは以下が本編です!
言語仕様上の変更点
do/endブロック内でbegin/endなしのrescue/else/ensureが書けるようになった
Ruby2.5ではブロック内でbegin/endなしのrescue/else/ensureが書けるようになりました。
これによりネストの深さと行数を節約できます。
# Ruby 2.4
[1].each do |n|
begin
1 / 0
rescue
# rescue
else
# else
ensure
# ensure
end
end
# Ruby 2.5
[1].each do |n|
1 / 0
rescue
# rescue
else
# else
ensure
# ensure
end
ただし、ブロックを{}
で書いた場合は構文エラーになります。
[1].each { |n|
1 / 0
rescue
# rescue
else
# else
ensure
# ensure
}
#=> SyntaxError: (irb):3: syntax error, unexpected keyword_rescue, expecting '}'
# rescue
# ^~~~~~
do/endと{}で挙動が変わるの?と思ってしまったんですが、むしろ{}を除外することで提案が通ったそうです。
トップレベルの定数探索のルールが変更された
下のような3つのクラスがあったとします。
# Ruby 2.4
class Item; end
class Staff; end
class ItemsController; end
さらにこの状態から、次のクラスを参照します。
Staff::ItemsController
ぱっと見、「こんなクラスは定義してないよ!」と思うかもしれませんが、Ruby 2.4では(警告付きで)ItemsControllerを参照したことになります。
# Ruby 2.4
Staff::ItemsController
#=> warning: toplevel constant ItemsController referenced by Staff::ItemsController
#=> ItemsController
なぜなら、トップレベルのクラス定義はObjectクラス以下にクラスが定義され、さらにRubyはStaffクラス、Objectクラスと順に継承関係をさかのぼってItemsControllerが定義されていないか探しに行っていたためです。
しかし、この仕様はプログラマが本来求めているものと異なるクラスが返される恐れがあり、わかりづらい不具合の原因になっていました。
(こちらの記事で詳しく説明しています。)
そこでRuby 2.5では、継承関係をさかのぼらなくなりました(Staffクラス以下にItemsControllerが定義されていなければエラー)。
# Ruby 2.5
Staff::ItemsController
#=> NameError: uninitialized constant Staff::ItemsController
# Did you mean? ItemsController
ItemsControllerを参照するためには、以下のいずれかの方法をとることになります(この書き方はいずれもRuby 2.4でも有効です)。
ItemsController
Object::ItemsController
::ItemsController
refinementsでto_sを書き換えたときに、文字列の式展開でもrefinements側のto_sが使われるようになった
次のようなrefinementsを使ったクラス定義があったとします。
class A
end
module B
refine A do
def to_s
'b'
end
end
end
class C
using B
def initialize
@a = A.new
end
def c1
@a.to_s
end
def c2
"#{@a}"
end
end
さらにクラスCのc1メソッドとc2メソッドを次のように呼び出します。
C.new.c1
C.new.c2
明示的にto_s
メソッドを呼びだしているc1メソッドはもちろん、式展開で暗黙的にto_s
が使われるc2メソッドもどちらも同じ結果になる(module Bのrefinementsが有効になっている)と思いますが、Ruby 2.4ではこのようになっていました。
# Ruby 2.4
C.new.c1 #=> "b"
C.new.c2 #=> "#<A:0x00007f870a852d50>"
ごらんのとおり、c2メソッド(式展開を使った場合)はrefinementsが有効になっていません。
ですが、Ruby 2.5ではどちらもrefinementsが有効になりました。
# Ruby 2.5
C.new.c1 #=> "b"
C.new.c2 #=> "b"
標準ライブラリ関連の変更点
Bundlerが標準ライブラリに取り込まれた
Bundlerが標準ライブラリに取り込まれました。
なので、gem install bundler
なしでBundlerが使えるようになります。
この記事の執筆時点ではBundler 1.15.4がインストールされるようです。
$ ruby -v
ruby 2.5.0dev (2017-10-08 trunk 60140) [x86_64-darwin16]
$ bundler -v
Bundler version 1.15.4
実験中の変更点
バックトレースの表示順が逆になる?
これはあくまで「実験段階」の変更点ですが、エラー発生時のバックトレースの出力順が逆になっています。
たとえば次のような例外が発生するスクリプトがあったとします。
def method_1
method_2
end
def method_2
# ZeroDivisionErrorを発生させる
1 / 0
end
method_1
Ruby 2.4までは次のようにバックトレースが出力されていました。
$ ruby ./test/error_example.rb
./test/error_example.rb:7:in `/': divided by 0 (ZeroDivisionError)
from ./test/error_example.rb:7:in `method_2'
from ./test/error_example.rb:2:in `method_1'
from ./test/error_example.rb:10:in `<main>'
Ruby 2.5ではこれが逆順で表示されます。
$ ruby ./test/error_example.rb
Traceback (most recent call last):
3: from ./test/error_example.rb:10:in `<main>'
2: from ./test/error_example.rb:2:in `method_1'
1: from ./test/error_example.rb:7:in `method_2'
./test/error_example.rb:7:in `/': divided by 0 (ZeroDivisionError)
元のIssueを読むと、「バックトレースが長大になるとき、ターミナル上で上スクロールせずにエラーメッセージが確認できる」「上から下に実行過程が読める」というメリットがある、とのことです。
ただし、今のところ逆順に表示されるのは標準エラー出力に出力されるバックトレースだけで、それ以外の場合(irb)などは今までの出力順になっています。
例外オブジェクトのbacktrace
で返される配列もこれまでどおりの順番です。
def method_1
begin
method_2
rescue => e
# Ruby 2.4も2.5も中身の順序は同じ
puts e.backtrace
end
end
ただし、繰り返しになりますが、これはまだ「実験中」の仕様変更です。
フィードバックを集めてからこの変更を適用するかどうかを決定するとのことです(参考)。
僕個人の意見としては「今さら導入するにはユーザーへのインパクトが大きそうな割に、そこまで嬉しいメリットもないのでどちらかといえば反対」ですね。
オブジェクト全般の新機能
ブロックの実行結果がそのまま戻り値になるyield_self
Ruby 2.5ではレシーバがブロックの引数になり、ブロックの結果がそのまま戻り値になるyield_self
が追加されました。
Kernelモジュールのメソッドなので、すべてのオブジェクト(BasicObjectを除く)で使用できます。
2.yield_self { |n| n * 10 } #=> 20
'hello'.yield_self { |s| s.upcase } #=> "HELLO"
ちなみに、yield_self
についてはこちらの記事でも紹介されています。
Ruby2.5で導入されるyield_selfについて - Qiita
文字列/正規表現に関する新機能
接頭辞や接尾辞を削除するdelete_prefix/delete_suffix
Ruby 2.5では文字列から接頭辞や接尾辞を削除するdelete_prefix
とdelete_suffix
が追加されました。
'invisible'.delete_prefix('in') #=> "visible"
'pink'.delete_prefix('in') #=> "pink"
'worked'.delete_suffix('ed') #=> "work"
'medical'.delete_suffix('ed') #=> "medical"
- 参考: Feature #12694: Want a String method to remove heading substr
- 参考: Feature #13665: String#delete_suffix
casecmp/casecmp?に文字列以外の引数を渡したときに例外ではなくnilを返すようになった
Ruby 2.5ではcasecmp/casecmp?
メソッドに文字列以外の引数(数値など)を渡したときにnil
を返すようになりました。
# Ruby 2.4
'abc'.casecmp(1) #=> TypeError
'abc'.casecmp?(1) #=> TypeError
# Ruby 2.5
'abc'.casecmp(1) #=> nil
'abc'.casecmp?(1) #=> nil
シンボルでは以前からnil
を返していたいので、それに挙動を合わせたようです。
# Ruby 2.4, 2.5
:abc.casecmp(1) #=> nil
:abc.casecmp?(1) #=> nil
正規表現で非包含オペレータが使えるようになった(Ruby 2.4.1以降)
これはRuby 2.5ではなく、Ruby 2.4.1からの新機能ですが、正規表現エンジンが鬼雲6.1.1にアップデートされ、非包含オペレータ((?~)
)が使えるようになりました。
以下は非包含オペレータを使って、DEBUGとINFOを含まない行を抜き出すコード例です。
text = <<LOG
10:00 [INFO] Lorem ipsum dolor sit amet
10:10 [WARN] Lorem ipsum dolor sit amet
10:20 [INFO] Lorem ipsum dolor sit amet
10:25 [DEBUG] Lorem ipsum dolor sit amet
10:30 [ERROR] Lorem ipsum dolor sit amet
10:40 [INFO] Lorem ipsum dolor sit amet
LOG
puts text.scan(/^(?~DEBUG|INFO)$/)
#=> 10:10 [WARN] Lorem ipsum dolor sit amet
# 10:30 [ERROR] Lorem ipsum dolor sit amet
非包含オペレータの詳細については以下の記事を参照してください。
Unicode 10をサポートするようになった
Ruby 2.5ではUnicode 10をサポートするようになりました。
これにより、たとえば正規表現で変体仮名を表すIn_Kana_Extended_A
プロパティ(Unicode 10で追加されたプロパティ)を使えたりするようになります。
"A\u{1B10A}B".match?(/\p{In_Kana_Extended_A}/) #=> true
ちなみに\u{1B10A}
というのはこんな文字です。(大半のブラウザでは表示できないはずです)
http://www.unicode.org/charts/PDF/Unicode-10.0/U100-1B100.pdf
配列に関する新機能
unshift/pushのエイリアスメソッドとしてprepend/appendが追加された
Ruby 2.5ではunshift
/push
のエイリアスメソッドとしてprepend
/append
が追加されました。
array = [3, 4]
array.prepend(1, 2) #=> [1, 2, 3, 4]
array #=> [1, 2, 3, 4]
array = [1, 2]
array.append(3, 4) #=> [1, 2, 3, 4]
array #=> [1, 2, 3, 4]
個人的にはunshift
/push
よりも直感的に理解しやすいメソッド名だなと思います
ハッシュに関する新機能
キーを特定のルールで変換するtransform_keys
/transform_keys!
Ruby 2.5ではハッシュのキーを特定のルールで変換するtransform_keys
が追加されました。
hash = { a: 1, b: 2 }
hash.transform_keys { |k| k.to_s }
#=> { 'a' => 1, 'b' => 2 }
transform_keys!
はレシーバのハッシュ自身を変更させます(破壊的メソッド)。
hash = { a: 1, b: 2 }
hash.transform_keys! { |k| k.to_s }
#=> { 'a' => 1, 'b' => 2 }
hash
#=> { 'a' => 1, 'b' => 2 }
ちなみにRuby 2.4ではハッシュの値を変換するtransform_values
メソッドが追加されていました。
hash = {a: 1, b: 2, c: 3}
hash.transform_values {|v| v ** 2 } #=> {a: 1, b: 4, c: 9}
KeyErrorにreceiverメソッドとkeyメソッドが追加された
Ruby 2.5ではfetch
メソッドなどでキーが見つからなかったときに発生するKeyErrorにreceiver
メソッドとkey
メソッドが追加されました。
receiver
はエラーが起きたハッシュ自身を、key
は見つからなかったキーを返します。
begin
h = {foo: 1, bar: 2}
h.fetch(:bax)
rescue KeyError => e
e.receiver #=> {:foo=>1, :bar=>2}
e.key #=> :bax
end
数値に関する新機能
整数値として平方根を返すInteger.sqrt
これまでRubyでは数値の平方根を求めるためのMath.sqrt
メソッドがありました。
Ruby 2.5ではInteger.sqrt
が追加されました。
このメソッドは整数値として平方根を返します。
Math.sqrt(9) #=> 3.0
Math.sqrt(2) #=> 1.4142135623730951
Integer.sqrt(9) #=> 3
Integer.sqrt(2) #=> 1
Integer.sqrt
はMath.sqrt
よりも速い、というメリットがあるようです。
詳しくは元のissueを参照してください。
日時に関する新機能
Time#atメソッドでミリ秒/ナノ秒を指定できるようになった
Time#at
メソッドでは第1引数にエポック秒を、第2引数にマイクロ秒を指定することができます。
def format_time(t)
t.strftime('%Y-%m-%d %H:%M:%S.%N')
end
t = Time.at(1514127600, 1)
format_time(t) #=> "2017-12-25 00:00:00.000001000"
Ruby 2.5では第3引数を指定することでミリ秒やマイクロ秒も設定できるようになりました。
# ミリ秒
format_time Time.at(1514127600, 1, :millisecond)
#=> "2017-12-25 00:00:00.001000000"
# マイクロ秒
format_time Time.at(1514127600, 1, :usec)
#=> "2017-12-25 00:00:00.000001000"
format_time Time.at(1514127600, 1, :microsecond)
#=> "2017-12-25 00:00:00.000001000"
# ナノ秒
format_time Time.at(1514127600, 1, :nsec)
#=> "2017-12-25 00:00:00.000000001"
format_time Time.at(1514127600, 1, :nanosecond)
#=> "2017-12-25 00:00:00.000000001"
ファイル/ディレクトリ操作に関する新機能
Dir#globメソッドに起点となるディレクトリを指定できるbaseオプションが追加された
Ruby 2.5ではDir#glob
メソッドに起点となるディレクトリを指定できるbaseオプションが追加されました。
# ./test/dir_aディレクトリを起点とし、".rb"で終わるファイルを探す
Dir.glob('./*.rb', base: './test/dir_a')
"."や".."を返さないDir#children/each_childメソッド
Rubyには指定されたパス内のファイルエントリ名を返すDir#entries
メソッドがあります。
Dir.entries('./test/dir_a')
#=> ['.', '..', 'code_a.rb', 'text_a.txt']
ただし、上の結果を見ると分かるようにentries
メソッドでは"."や".."もファイルエントリとして返却されます。
Ruby 2.5で追加されたDir#children
メソッドを使うと、"."や".."が含まれなくなります。
Dir.children('./test/dir_a')
#=> ['code_a.rb', 'text_a.txt']
Dir#each_child
メソッドは配列ではなく、Enumeratorオブジェクトを返します。
Dir.each_child('./test/dir_a')
#=> #<Enumerator: Dir:each_child(\"./test/dir_a\")>"
Dir.each_child('./test/dir_a').to_a
#=> ['code_a.rb', 'text_a.txt']
Setクラスに関する新機能
to_sがinspectのエイリアスメソッドになった
Ruby 2.5ではSetクラスのto_s
メソッドがinspect
メソッドのエイリアスメソッドになりました。
これにより、要素の情報が表示されるようになります。
require 'set'
s1 = Set.new
s1 << 'tic' << 'tac'
# Ruby 2.4
s1.to_s #=> #<Set:0x00007f83b98357e0>
# Ruby 2.5
s1.to_s #=> #<Set: {"tic", "tac"}>
===がinclude?のエイリアスメソッドになった
Ruby 2.5ではSetクラスの===
がinclude?
のエイリアスメソッドになりました。
require 'set'
Set[1, 2, 3].include?(2) #=> true
Set[1, 2, 3].include?(5) #=> false
Set[1, 2, 3] === 2 #=> true
Set[1, 2, 3] === 5 #=> false
Threadクラスに関する新機能
スレッドに指定したキーのデータが格納されていなければデフォルト値を返すThread#fetch
メソッド
RubyではThread#[]
/[]=
を使ってスレッド固有のデータを読み書きすることができます。
Thread.current[:foo] = 'bar'
Thread.current[:foo] #=> "bar"
Ruby 2.5ではThread#fetch
メソッドが追加され、Hash#fetch
メソッドと同じように指定されたキーが見つからないときのデフォルト値を指定することができます。
Thread.current[:foo] = 'bar'
Thread.current.fetch(:foo, 'baz') #=> "bar"
Thread.current.fetch(:hoge, 'baz') #=> "baz"
# 第2引数を指定しない場合はキーが見つからないとエラーになる
Thread.current.fetch(:foo) #=> "bar"
Thread.current.fetch(:hoge) #=> KeyError: key not found: hoge
まとめ
というわけで、本記事ではRuby 2.5の新機能や変更点をまとめてみました。
do/endブロックの中にbegin/endなしでrescueが書ける点や、定数探索のルールが変わって予期せぬ不具合が発生しにくくなった点は日常的な開発で嬉しいポイントだと思います。
一方で、バックトレースの並びが逆順になるのは「うーん、ちょっと」という気がしました。
ただ、これはまだ「実験中」の仕様変更ですので、フィードバック次第では従来のままになる可能性もあります。
その他にもいろいろと興味深い新機能が追加されています。
yield_self
なんかはこれから面白い使い方を研究していきたくなるメソッドですね。
さあ、みなさんもぜひRuby 2.5の新機能を試してみてください!
あわせて読みたい
Ruby 2.3、2.4の新機能は以下の記事にまとめてあります。
こちらもあわせてどうぞ。