はじめに
Rubyは毎年12月25日にアップデートされます。
Ruby 2.5のpreview1がリリースされた際に新機能をまとめた記事を書いたのですが、2017年12月14日にリリースされたrc1ではさらに多くの新機能が導入されていました。
これをそのままpreview1の記事に追記すると、記事のボリュームがかなり大きくなるため、rc1で導入された新機能は今回別記事(Part 2)として公開することにします。
ちなみにPart 1の記事はこちらです。
サンプルコードでわかる!Ruby 2.5の主な新機能と変更点 Part 1 - Qiita
本記事の情報源
本記事は以下のNEWSページに掲載されている情報から、個人的に注目したい新機能(なおかつPart 1に載っていないもの)をピックアップしたものです。
この記事に掲載していない変更点もあるので、詳細はNEWSページをご覧ください。
また、説明している内容に間違いがあれば、コメントや編集リクエスト等で優しく指摘してやってください。
ここで紹介していない新機能に関する編集リクエストも大歓迎です!
動作確認したRubyのバージョン
本記事は以下の環境で実行した結果を記載しています。
$ ruby -v
ruby 2.5.0dev (2017-12-24 trunk 61431) [x86_64-darwin16]
ご覧のとおり確認したのは12月24日時点のdev版なので、Ruby 2.5が正式リリースされたらあらためて内容をチェックしようと思います。
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リポジトリに置いています。
それでは以下が本編です!
文字列やファイルの読み書きに関する新機能や変更点
String#start_with?
メソッドに正規表現が渡せるようになった
Ruby 2.5ではString#start_with?
メソッドに正規表現が渡せるようになりました。
s = '123abc'
s.start_with?(/\d+/) #=> true
s.start_with?(/9\d+/) #=> false
IO#write
やStringIO#write
に複数の引数を渡せるようになった
Ruby 2.5ではIO#write
やStringIO#write
に複数の引数を渡せるようになりました。
path = '/tmp/sample.txt'
File.open(path, 'w') do |f|
# writeメソッドに複数の引数を渡す
f.write 'abc', '1234'
end
File.read(path) #=> "abc1234"
require "stringio"
a = StringIO.new("hoge", 'r+')
# writeメソッドに複数の引数を渡す
a.write "abc", "1234"
a.string #=> "abc1234"
Pathnameクラスにインスタンスメソッドのglob
が追加された
Ruby 2.5ではPathnameクラスにインスタンスメソッドのglob
が追加されました。これにより、「Railsアプリのspecディレクトリ以下にある全rbファイルを、Pathnameオブジェクトの配列として取得する」というようなコードが簡潔に書けるようになります。
# Ruby 2.4の場合
pathnames = Pathname.glob(Rails.root.join('spec/**/*.rb'))
# Ruby 2.5の場合
pathnames = Rails.root.glob('spec/**/*.rb')
配列やハッシュに関する新機能や変更点
Railsでお馴染みのHash#slice
がRuby標準のメソッドになった
これまでRails(ActiveSupport)の拡張機能だったHash#slice
メソッドがRuby標準のメソッドになりました。slice
メソッドは指定したキーに合致するキーと要素からなる、新しいハッシュを返します。
hash = { a: 'Alice', b: 'Bob', c: 'Carol' }
hash.slice(:a, :c) #=> { a: 'Alice', c: 'Carol' }
ただし、slice!
やexcept
/except!
は今回導入されなかったようです。
- 参考: Feature #8499: Importing Hash#slice, Hash#slice!, Hash#except, and Hash#except! from ActiveSupport
Enumerable#any?/all?/none?/one?にパターンオブジェクトを渡せるようになった
Ruby 2.5ではEnumerableのany?
/all?
/none?
/one?
メソッドにパターンオブジェクトが渡せるようになりました。言葉ではちょっと説明しづらいので、先にコードを見てもらった方が分かりやすいと思います。
arr = ['abc', 123]
# Ruby 2.4の場合
# 整数が1つでも含まれるか?
arr.any? { |obj| Integer === obj } #=> true
# 実数が一つでも含まれるか?
arr.any? { |obj| Float === obj } #=> false
# Ruby 2.5の場合
arr.any?(Integer) #=> true
arr.any?(Float) #=> false
arr = ['123-4567', '789-0123']
# Ruby 2.4の場合
# 全要素が/\d+-\d+/にマッチするか?
arr.all? { |s| /\d+-\d+/ === s } #=> true
# 全要素が/123-\d+/にマッチするか?
arr.all? { |s| /123-\d+/ === s } #=> false
# Ruby 2.5の場合
arr.all?(/\d+-\d+/) #=> true
arr.all?(/123-\d+/) #=> false
# none?やone?も同じような考え方で引数を渡せる
つまり、メソッドの引数に渡されたオブジェクトと配列(正確にはEnumerableモジュールをincludeしているオブジェクト)の各要素を===
で比較し、その戻り値をany?
/all?
/none?
/one?
の判定に使う、という挙動になります。
ちなみに===
はcase文などで内部的に使用される演算子です。詳しくは公式ドキュメントを参照してください。
例外やデバッグに関する新機能や変更点
バックトレースの表示順が逆になった
(この内容はPart 1でも書きましたが、若干内容が変わっている点もあるのであらためてここでまとめます)
Ruby 2.5ではエラー発生時のバックトレースの出力順が逆になっています。
たとえば次のような例外が発生するスクリプトがあったとします。
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を読むと、「バックトレースが長大になるとき、ターミナル上で上スクロールせずにエラーメッセージが確認できる」「上から下に実行過程が読める」というメリットがある、とのことです。
ただし、今のところ逆順に表示されるのは「組み込み定数であるSTDERR
が変更されておらず、なおかつ、tty(標準入出力となっている端末デバイス)に出力する場合」と「IRBで実行した場合」になっています。
そのため、上記の条件に合致しない場合は今までどおりの出力順になっています。
また、例外オブジェクトのbacktrace
で返される配列もこれまでどおりの順番です。
def method_1
method_2
rescue => e
# Ruby 2.4も2.5も中身の順序は同じ
puts e.backtrace
end
この仕様変更は「実験中(experimental)」としてリリースされます。Ruby 2.6以降ではまた扱いが変わるかもしれません。
例外クラス名、例外メッセージ、バックトレースが1つの文字列として返るException#full_message
Ruby 2.5では例外クラス名、例外メッセージ、バックトレースが1つの文字列として返るException#full_message
メソッドが追加されました。
begin
1 / 0
rescue => e
puts e.full_message
end
#=> Traceback (most recent call last):
#=> 1: from full_msg.rb:2:in `<main>'
#=> full_msg.rb:2:in `/': divided by 0 (ZeroDivisionError)
さくっとエラー内容をログに残したり、デバッグ時にエラーの詳細を確認したりするのに便利そうです。
freezeしたオブジェクトに破壊的変更を加えようとするとFrozenErrorが発生するようになった
freezeしたオブジェクトに破壊的変更を加えようとするとRuby 2.4まではRuntimeErrorが発生していました。
s = 'abc'.freeze
s.upcase! #=> RuntimeError: can't modify frozen String
Ruby 2.5ではRuntimeErrorではなく、FrozenErrorが発生します。
s.upcase! #=> FrozenError: can't modify frozen String
なお、FrozenErrorはRuntimeErrorクラスのサブクラスです。
FrozenError.superclass #=> RuntimeError
requireなしでpp
メソッドが使えるようになった
pp
メソッドを使う場合、これまではrequire 'pp'
が必要でしたが、Ruby 2.5ではrequire
なしで使えます。
ちなみにpp
は"pretty print"の略で、複雑なオブジェクトを表示したりする場合にp
メソッドよりも読みやすく出力されます。
data = [false, 42, %w{fourty two}, {:now => Time.now, :class => Time.now.class, :distance => 42e42}]
p data
#=> [false, 42, ["fourty", "two"], {:now=>2017-12-24 06:21:05 +0900, :class=>Time, :distance=>4.2e+43}]
# Ruby 2.5ではrequire 'pp'が不要
pp data
#=> [false,
# 42,
# ["fourty", "two"],
# {:now=>2017-12-24 06:21:05 +0900, :class=>Time, :distance=>4.2e+43}]
binding.irb
の機能改善
Ruby 2.4ではプログラムの実行中にirbが開けるbinding.irb
メソッドが追加されました。
Ruby 2.5ではこのbinding.irb
に関して以下のような機能改善が行われました。
-
require 'irb'
を明示的に書かなくても自動的にirbライブラリが読み込まれる - 実行が停止した行の周辺コードが表示される
以下はbinding.irb
でコードを停止したときの表示例です。
$ ruby ./test/binding_irb_sample.rb
From: ./test/binding_irb_sample.rb @ line 6 :
1: class Test
2: attr_accessor :x, :y, :z
3: def initialize(x, y, z)
4: @x = x
5: @y = y
=> 6: binding.irb
7: @z = z
8: end
9: end
10:
11: Test.new(1, 2, 3)
irb(#<Test:0x00007f9f5f02fe08>):001:0>
- 参考: Bug #13099: Binding#irb does not work outside of irb
- 参考: Feature #14124: Show source around binding.irb on irb startup
パフォーマンス改善
メソッドの引数に&block
を書いた場合の速度が改善された
Ruby 2.4まではメソッドの引数に&block
を書いたときと書かないときで速度に違いがありました。
# block を引数で受け取って、call で呼ぶ => 遅い
def block_call_with_block_arg(&block)
block.call
end
# 引数で受け取った block を捨てて yield で呼ぶ => これも遅い
def yield_with_block_arg(&block)
yield
end
# block を引数で受け取らずに yield で呼ぶ => 速い
def yield_without_block_arg
yield
end
Ruby 2.5では上記コードの2つめのパターンが、一番下のパターンと同じぐらい速くなりました。
詳しい内容は以下のブログ記事をご覧ください(上記のサンプルコードもこちらのブログ記事から引用させてもらいました)。
Ruby 2.5 は引数に &block を書いても速い!!! - onk.ninja
ERBテンプレートからのコード生成が2倍速くなった
NEWSには"ERB now generates code from a template which runs 2 times faster than Ruby 2.4"とあるので、ERBテンプレートからのコード生成がRuby 2.4と比較して2倍速くなったそうです。
その他の新機能や変更点
Bundlerの標準ライブラリ化はいったん延期
もともとRuby 2.5ではBundlerが標準ライブラリに取り込まれることになっていましたが、リリース直前に大きな問題が見つかったため、この対応は延期されることになったようです。
キーワード引数でStructを初期化できるようになった
Structで作ったクラスを初期化する場合、これまでは必ず引数の順番を覚えておく必要がありました。
# Ruby 2.4の場合
OldPoint = Struct.new(:x, :y)
# 必ずx, yの順で引数を指定する
p1 = OldPoint.new(1, 2)
p1.x #=> 1
p1.y #=> 2
Ruby 2.5ではStruct.new
でkeyword_init
というオプションを指定できます。これをtrue
にすると、キーワード引数で初期化できます。
# キーワード引数で初期化可能なオプションを付けてクラスを作る
NewPoint = Struct.new(:x, :y, keyword_init: true)
# キーワード引数で初期化できる(キーワード引数なので順番は変わってもかまわない)
p2 = NewPoint.new(y: 20, x: 10)
p2.x #=> 10
p2.y #=> 20
Methodオブジェクトを===
で呼び出せるようになった
Ruby 2.5ではMethodオブジェクトを===
で呼び出せるようになりました。
def hello(name)
"Hello, #{name}!"
end
method(:hello) === 'Alice' #=> "Hello, Alice!"
これにより、case文のwhen節に直接Methodオブジェクトを書いたりできるようになります。
def adult?(age)
age > 20
end
def child?(age)
age < 20
end
def adult_or_child(age)
case age
when method(:adult?)
'大人です'
when method(:child?)
'子どもです'
else
'はたちです'
end
end
adult_or_child(21) #=> 大人です
adult_or_child(19) #=> 子どもです
adult_or_child(20) #=> はたちです
attr_accessor
やdefine_method
がpublicメソッドになった
Ruby 2.5ではModuleクラスの以下のメソッドがpublicメソッドになりました。
attr
attr_accessor
attr_reader
attr_writer
define_method
alias_method
undef_method
remove_method
これにより、メタプログラミングが少し楽に書けるようになります。
user_class = Class.new
user_class.attr_accessor :name
user_class.define_method(:hello, -> { "Hello, I am #{name}." })
user = user_class.new
user.name = 'Alice'
user.name #=> "Alice"
user.hello #=> "Hello, I am Alice."
- 参考: Feature #14132: Module#attr{|_reader|_writer} should be public
- 参考: Feature #14133: Module#{define|alias|undef}_method should be made public
英数字のみで構成されるランダムな文字列を生成するSecureRandom.alphanumeric
Ruby 2.5では英数字のみで構成されるランダムな文字列を生成するSecureRandom.alphanumeric
メソッドが追加されました。
require 'securerandom'
SecureRandom.alphanumeric #=> "cr12XFfO3FbtAHB1"
SecureRandom.alphanumeric #=> "Rg96LmqVWQ2DoNzu"
Thread.report_on_exception
のデフォルト値がtrue
になった
Ruby 2.4では別スレッドの例外を報告するかどうかを決めるThread.report_on_exception
フラグが導入されました。
Ruby 2.4では明示的にtrue
をセットしないとこの機能が有効になりませんでしたが、Ruby 2.5ではデフォルトでtrue
になっています。
# Ruby 2.5ではtrueがデフォルト値
Thread.report_on_exception #=> true
まとめ
というわけで、Ruby 2.5の新機能をあれこれ紹介してみました。
Rubyコミッタの方々をはじめ、Ruby 2.5の開発に携わってくださった開発者のみなさまに感謝します。
Part 1の記事とあわせて読んで、Ruby 2.5を使ったRubyプログラミングを楽しんでください!