はじめに
Rubyは毎年12月25日にアップデートされます。
今年はまだpreview版がリリースされていませんが(2017年10月10日時点)、今年もそろそろリリースの日が近づいてきました。
Ruby 2.5については2017年10月10日にpreview1がリリースされました。
そこでこの記事ではこの2.5.0-preview1を参考にして、おそらくこんな感じでリリースされるであろうRuby 2.5の新機能や変更点をまとめてみました。
2017.12.25追記: Part 2もあります!
この記事を公開したあとにも多数新機能が追加されました。この記事に追記すると長くなってしまうので、Part 2として公開しています。こちらもあわせてご覧ください。
サンプルコードでわかる!Ruby 2.5の主な新機能と変更点 Part 2 - Qiita
本記事の情報源
本記事は以下のNEWSページに掲載されている情報から、個人的に注目したい新機能をピックアップしたものです。
この記事に掲載していない変更点もあるので、詳細はNEWSページをご覧ください。
また、説明している内容に間違いがあれば、コメントや編集リクエスト等で優しく指摘してやってください。
ここで紹介していない新機能に関する編集リクエストも大歓迎です!
動作確認したRubyのバージョン
本記事は以下の環境で実行した結果を記載しています。
$ ruby -v
ruby 2.5.0preview1 (2017-10-10 trunk 60153) [x86_64-darwin16]
2017.12.26追記: 正式リリース版でも動作確認しました
正式リリースされたRuby 2.5.0でも動作確認を行いました。
$ ruby -v
ruby 2.5.0p0 (2017-12-25 revision 61468) [x86_64-darwin16]
また、Ruby 2.5.0の最終的なNEWSページはこちらになります。
コード例
本記事のコード例は以下のGitHubリポジトリに置いています。
それでは以下が本編です!
言語仕様上の変更点
do/endブロック内でbegin/endなしのrescue/else/ensureが書けるようになった
Ruby2.5ではブロック内でbegin/endなしのrescue/else/ensureが書けるようになりました。
これによりネストの深さと行数を節約できます。
# Ruby 2.4
[1].each do |n|
begin
n / 0
rescue
# rescue
else
# else
ensure
# ensure
end
end
# Ruby 2.5
[1].each do |n|
n / 0
rescue
# rescue
else
# else
ensure
# ensure
end
ただし、ブロックを{}
で書いた場合は構文エラーになります。
[1].each { |n|
n / 0
rescue
# rescue
else
# else
ensure
# ensure
}
#=> SyntaxError: (irb):3: syntax error, unexpected keyword_rescue, expecting '}'
# rescue
# ^~~~~~
do/endと{}で挙動が変わるの?と思ってしまったんですが、むしろ{}を除外することで提案が通ったそうです。
今までも何回か提案されていたけれど、{…rescue…}の違和感からrejectされ続けてきたモノなので、むしろ{}を除外したというのが今回通ったポイント https://t.co/eDNlrtlUwE
— †ꁐ𐨥ᐠ†-̱̏ (@n0kada) August 8, 2017
トップレベルの定数探索のルールが変更された
下のような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の標準添付は急きょ延期されたとのことです(参考)。
Bundlerが標準ライブラリに取り込まれました。なので、gem install bundler
なしでBundlerが使えるようになります。
ERBのローカル変数をHashで渡せる ERB#result_with_hash メソッド
これまでERBでテンプレート内で使われるローカル変数を渡すには、以下のようにbindingを渡したり、あるいは渡す変数を絞るためにstructを定義する必要がありました。
require 'erb'
require 'ostruct'
namespace = OpenStruct.new(a: 2, b: 3)
template = 'Result: <%= a * b %>'
ERB.new(template).result(namespace.instance_eval { binding }) #=> "Result: 6"
これが、ERB#result_with_hash
の導入により、最低限の変数だけ渡すのが以下のように簡単に書けるようになりました。
require 'erb'
ERB.new('Result: <%= a * b %>').result_with_hash(a: 2, b: 3) #=> "Result: 6"
実験中の変更点
バックトレースの表示順が逆になる?
(この仕様変更はこの記事を公開したあとにいくつか変更された点があるので、Part 2であらためて説明しています。)
サンプルコードでわかる!Ruby 2.5の主な新機能と変更点 Part 2 - Qiita
オブジェクト全般の新機能
ブロックの実行結果がそのまま戻り値になるyield_self
Ruby 2.5ではレシーバがブロックの引数になり、ブロックの結果がそのまま戻り値になるyield_self
が追加されました。
Kernelモジュールのメソッドなので、すべてのオブジェクト(BasicObjectを除く)で使用できます。
# レシーバ(= 2)を受け取り、ブロックの戻り値(= 2 * 10)がメソッド全体の戻り値になる
2.yield_self { |n| n * 10 } #=> 20
以下は配列に含まれる文字列をカンマで連結し、さらにそれを丸括弧で囲むコード例です。
names = ['Alice', 'Bob']
names.join(', ').yield_self { |s| "(#{s})" } #=> "(Alice, Bob)"
ちなみに、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
数値に関する新機能
round/floor/ceil/truncateでレシーバをFloatに変換しなくなった
Ruby 2.4まではround
メソッドを使うと、レシーバをFloatに変換してから四捨五入をしていました。
2.round(2) #=> 2.0
そのため、大きな数になると丸め誤差の関係で数学的におかしな結果が返ってきていました。
(10**25).round(2) #=> 1.0e+25
(10**25).round(2).to_i #=> 10000000000000000905969664
Ruby 2.5では無理にFloatに変換しないので、数学的に正しい結果が返ります。
2.round(2) #=> 2
(10**25).round(2) #=> 10000000000000000000000000
これはfloor
/ceil
/truncate
についでも同様です。
(10**25).floor(2) #=> 10000000000000000000000000
(10**25).ceil(2) #=> 10000000000000000000000000
(10**25).truncate(2) #=> 10000000000000000000000000
参考: Feature #13420: Integer#{round,floor,ceil,truncate} should always return an integer, not a float
正しい精度で平方根を返すInteger.sqrt
round
メソッドと同様、Math.sqrt
メソッドも引数をFloatとして扱うために、大きな数では精度が狂うケースがありました。
n = 10**46
# 数学的には 100000000000000000000000 が正
Math.sqrt(n).to_i #=> 99999999999999991611392
このようなケースではRuby 2.5で追加されたInteger.sqrt
を使うと、正しい値が返ってきます。
n = 10**46
Integer.sqrt(n) #=> 100000000000000000000000
なお、平方根の値が小数になる場合は小数点以下が切り捨てられます。
引数が整数以外の数値であれば、最初に整数に変換されてから平方根を計算します。
Math.sqrt(3) #=> 1.7320508075688772
Integer.sqrt(3) #=> 1
Integer.sqrt(9.9) #=> 3
日時に関する新機能
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
続きはPart 2でどうぞ!
これ以外の新機能についてはPart 2で紹介しているので、こちらもあわせてご覧ください。
サンプルコードでわかる!Ruby 2.5の主な新機能と変更点 Part 2 - Qiita
まとめ
というわけで、本記事ではRuby 2.5の新機能や変更点をまとめてみました。
do/endブロックの中にbegin/endなしでrescueが書ける点や、定数探索のルールが変わって予期せぬ不具合が発生しにくくなった点は日常的な開発で嬉しいポイントだと思います。
一方で、バックトレースの並びが逆順になるのは「うーん、ちょっと」という気がしました。
ただ、これはまだ「実験中」の仕様変更ですので、フィードバック次第では従来のままになる可能性もあります。
その他にもいろいろと興味深い新機能が追加されています。
yield_self
なんかはこれから面白い使い方を研究していきたくなるメソッドですね。
さあ、みなさんもぜひRuby 2.5の新機能を試してみてください!
あわせて読みたい
Ruby 2.3、2.4の新機能は以下の記事にまとめてあります。
こちらもあわせてどうぞ。