サンプルコードでわかる！Ruby 2.5の主な新機能と変更点 Part 2


## はじめに

Rubyは毎年12月25日にアップデートされます。
Ruby 2.5のpreview1がリリースされた際に新機能をまとめた記事を書いたのですが、2017年12月14日にリリースされたrc1ではさらに多くの新機能が導入されていました。

- [Ruby 2\.5\.0\-rc1 リリース](https://www.ruby-lang.org/ja/news/2017/12/14/ruby-2-5-0-rc1-released/)

これをそのままpreview1の記事に追記すると、記事のボリュームがかなり大きくなるため、rc1で導入された新機能は今回別記事（Part 2）として公開することにします。

ちなみにPart 1の記事はこちらです。

[サンプルコードでわかる！Ruby 2\.5の主な新機能と変更点 Part 1 \- Qiita](https://qiita.com/jnchito/items/f182b6f0093a6a3701a1)

### 本記事の情報源

本記事は以下のNEWSページに掲載されている情報から、個人的に注目したい新機能（なおかつPart 1に載っていないもの）をピックアップしたものです。

[NEWS（commit: 7361587）](https://github.com/ruby/ruby/commit/7361587fffcddef72b11e611cb12726be2772513)

この記事に掲載していない変更点もあるので、詳細は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ページはこちらになります。

- [NEWS](https://github.com/ruby/ruby/blob/v2_5_0/NEWS)

### コード例

本記事のコード例は以下のGitHubリポジトリに置いています。

https://github.com/JunichiIto/ruby-2-5-sandbox

それでは以下が本編です！

## 文字列やファイルの読み書きに関する新機能や変更点

### `String#start_with?`メソッドに正規表現が渡せるようになった

Ruby 2.5では`String#start_with?`メソッドに正規表現が渡せるようになりました。

```ruby
s = '123abc'
s.start_with?(/\d+/)  #=> true
s.start_with?(/9\d+/) #=> false
```

- 参考: [Feature \#13712: String\#start\_with? with regexp](https://bugs.ruby-lang.org/issues/13712)

### `IO#write`や`StringIO#write`に複数の引数を渡せるようになった

Ruby 2.5では`IO#write`や`StringIO#write`に複数の引数を渡せるようになりました。

```ruby
path = '/tmp/sample.txt'
File.open(path, 'w') do |f|
  # writeメソッドに複数の引数を渡す
  f.write 'abc', '1234'
end
File.read(path) #=> "abc1234"
```

```ruby
require "stringio"
a = StringIO.new("hoge", 'r+')
# writeメソッドに複数の引数を渡す
a.write "abc", "1234"
a.string #=> "abc1234"
```

- 参考: [Feature \#9323: IO\#writev](https://bugs.ruby-lang.org/issues/9323)

### Pathnameクラスにインスタンスメソッドの`glob`が追加された

Ruby 2.5ではPathnameクラスにインスタンスメソッドの`glob`が追加されました。これにより、「Railsアプリのspecディレクトリ以下にある全rbファイルを、Pathnameオブジェクトの配列として取得する」というようなコードが簡潔に書けるようになります。

```ruby
# Ruby 2.4の場合
pathnames = Pathname.glob(Rails.root.join('spec/**/*.rb'))

# Ruby 2.5の場合
pathnames = Rails.root.glob('spec/**/*.rb')
```

- 参考: [Feature \#7360: Adding Pathname\#glob](https://bugs.ruby-lang.org/issues/7360)

## 配列やハッシュに関する新機能や変更点

### Railsでお馴染みの`Hash#slice`がRuby標準のメソッドになった

これまでRails(ActiveSupport)の拡張機能だった`Hash#slice`メソッドがRuby標準のメソッドになりました。`slice`メソッドは指定したキーに合致するキーと要素からなる、新しいハッシュを返します。

```ruby
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](https://bugs.ruby-lang.org/issues/8499)

### Enumerable#any?/all?/none?/one?にパターンオブジェクトを渡せるようになった

Ruby 2.5ではEnumerableの`any?`/`all?`/`none?`/`one?`メソッドにパターンオブジェクトが渡せるようになりました。言葉ではちょっと説明しづらいので、先にコードを見てもらった方が分かりやすいと思います。

```ruby
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文などで内部的に使用される演算子です。詳しくは[公式ドキュメント](https://docs.ruby-lang.org/ja/latest/class/Module.html#I_--3D--3D--3D)を参照してください。

- 参考: [Feature \#11286: \[PATCH\] Add case equality arity to Enumerable's sequence predicates\.](https://bugs.ruby-lang.org/issues/11286)

## 例外やデバッグに関する新機能や変更点

### バックトレースの表示順が逆になった

（この内容はPart 1でも書きましたが、若干内容が変わっている点もあるのであらためてここでまとめます）

Ruby 2.5ではエラー発生時のバックトレースの出力順が逆になっています。

たとえば次のような例外が発生するスクリプトがあったとします。

```ruby
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](https://bugs.ruby-lang.org/issues/8661)を読むと、「バックトレースが長大になるとき、ターミナル上で上スクロールせずにエラーメッセージが確認できる」「上から下に実行過程が読める」というメリットがある、とのことです。

ただし、今のところ逆順に表示されるのは「組み込み定数である`STDERR`が変更されておらず、なおかつ、[tty](https://ja.wikipedia.org/wiki/Tty)（標準入出力となっている端末デバイス）に出力する場合」と「IRBで実行した場合」になっています。

そのため、上記の条件に合致しない場合は今までどおりの出力順になっています。

また、例外オブジェクトの`backtrace`で返される配列もこれまでどおりの順番です。

```ruby
def method_1
  method_2
rescue => e
  # Ruby 2.4も2.5も中身の順序は同じ
  puts e.backtrace
end
```

この仕様変更は「実験中（experimental）」としてリリースされます。Ruby 2.6以降ではまた扱いが変わるかもしれません。

- 参考: [Feature \#8661: Add option to print backstrace in reverse order\(stack frames first & error last\) \- CommonRuby \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/8661)

### 例外クラス名、例外メッセージ、バックトレースが1つの文字列として返る`Exception#full_message`

Ruby 2.5では例外クラス名、例外メッセージ、バックトレースが1つの文字列として返る`Exception#full_message`メソッドが追加されました。

```ruby
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)
```

さくっとエラー内容をログに残したり、デバッグ時にエラーの詳細を確認したりするのに便利そうです。

- 参考: [Feature \#14141: Add a method to Exception for retrieving formatted exception for logging purpose \(Exception\#\{formatted,display\}\)](https://bugs.ruby-lang.org/issues/14141)

### freezeしたオブジェクトに破壊的変更を加えようとするとFrozenErrorが発生するようになった

freezeしたオブジェクトに破壊的変更を加えようとするとRuby 2.4まではRuntimeErrorが発生していました。

```ruby
s = 'abc'.freeze
s.upcase! #=> RuntimeError: can't modify frozen String
```

Ruby 2.5ではRuntimeErrorではなく、FrozenErrorが発生します。

```ruby
s.upcase! #=> FrozenError: can't modify frozen String
```

なお、FrozenErrorはRuntimeErrorクラスのサブクラスです。

```ruby
FrozenError.superclass #=> RuntimeError
```

- 参考: [Feature \#13224: Add FrozenError as a subclass of RuntimeError](https://bugs.ruby-lang.org/issues/13224)

### requireなしで`pp`メソッドが使えるようになった

`pp`メソッドを使う場合、これまでは`require 'pp'`が必要でしたが、Ruby 2.5では`require`なしで使えます。

ちなみに`pp`は"pretty print"の略で、複雑なオブジェクトを表示したりする場合に`p`メソッドよりも読みやすく出力されます。

```ruby
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`メソッドが追加されました。

- [プログラムの実行中にirbが開けるbinding\.irbメソッドの追加 (Ruby 2.4)](https://qiita.com/jnchito/items/9f9d45581816f121af07#%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%A0%E3%81%AE%E5%AE%9F%E8%A1%8C%E4%B8%AD%E3%81%ABirb%E3%81%8C%E9%96%8B%E3%81%91%E3%82%8Bbindingirb%E3%83%A1%E3%82%BD%E3%83%83%E3%83%89%E3%81%AE%E8%BF%BD%E5%8A%A0)

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](https://bugs.ruby-lang.org/issues/13099)
- 参考: [Feature \#14124: Show source around binding\.irb on irb startup](https://bugs.ruby-lang.org/issues/14124)


## パフォーマンス改善

### メソッドの引数に`&block`を書いた場合の速度が改善された

Ruby 2.4まではメソッドの引数に`&block`を書いたときと書かないときで速度に違いがありました。

```ruby
# 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](https://blog.onk.ninja/2017/12/19/ruby_2_5_medama)

- 参考: [Feature \#14045: Lazy Proc allocation for block parameters](https://bugs.ruby-lang.org/issues/14045)

### ERBテンプレートからのコード生成が2倍速くなった

NEWSには"ERB now generates code from a template which runs 2 times faster than Ruby 2.4"とあるので、ERBテンプレートからのコード生成がRuby 2.4と比較して2倍速くなったそうです。

- 参考: [Inside Treasure Data API / CRuby and ERB optimization at TD Hackathon // Speaker Deck](https://speakerdeck.com/k0kubun/cruby-and-erb-optimization-at-td-hackathon?slide=53)

## その他の新機能や変更点

### Bundlerの標準ライブラリ化はいったん延期

もともとRuby 2.5ではBundlerが標準ライブラリに取り込まれることになっていましたが、リリース直前に大きな問題が見つかったため、この対応は延期されることになったようです。

- 参考: [Postponing the Bundler merge\. · ruby/ruby@7825e83](https://github.com/ruby/ruby/commit/7825e8363d4b2ccad8e2d3f5eeba9e26f6656911)

### キーワード引数でStructを初期化できるようになった

Structで作ったクラスを初期化する場合、これまでは必ず引数の順番を覚えておく必要がありました。

```ruby
# 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`にすると、キーワード引数で初期化できます。

```ruby
# キーワード引数で初期化可能なオプションを付けてクラスを作る
NewPoint = Struct.new(:x, :y, keyword_init: true)
# キーワード引数で初期化できる（キーワード引数なので順番は変わってもかまわない）
p2 = NewPoint.new(y: 20, x: 10)
p2.x #=> 10
p2.y #=> 20
```

- 参考: [Feature \#11925: Struct construction with kwargs](https://bugs.ruby-lang.org/issues/11925)

### Methodオブジェクトを`===`で呼び出せるようになった

Ruby 2.5ではMethodオブジェクトを`===`で呼び出せるようになりました。

```ruby
def hello(name)
  "Hello, #{name}!"
end

method(:hello) === 'Alice' #=> "Hello, Alice!"
```

これにより、case文のwhen節に直接Methodオブジェクトを書いたりできるようになります。

```ruby
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) #=> はたちです
```

- 参考: [Feature \#14142: Add Method\#===](https://bugs.ruby-lang.org/issues/14142)

### `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`

これにより、メタプログラミングが少し楽に書けるようになります。

```ruby
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](https://bugs.ruby-lang.org/issues/14132)
- 参考: [Feature \#14133: Module\#\{define\|alias\|undef\}\_method should be made public](https://bugs.ruby-lang.org/issues/14133)

### 英数字のみで構成されるランダムな文字列を生成する`SecureRandom.alphanumeric`

Ruby 2.5では英数字のみで構成されるランダムな文字列を生成する`SecureRandom.alphanumeric`メソッドが追加されました。

```ruby
require 'securerandom'
SecureRandom.alphanumeric #=> "cr12XFfO3FbtAHB1"
SecureRandom.alphanumeric #=> "Rg96LmqVWQ2DoNzu"
```

### `Thread.report_on_exception`のデフォルト値が`true`になった

Ruby 2.4では別スレッドの例外を報告するかどうかを決める`Thread.report_on_exception`フラグが導入されました。

- [別スレッドの例外を報告するかどうかを決めるThread\.report\_on\_exceptionフラグ (Ruby 2.4)](https://qiita.com/jnchito/items/9f9d45581816f121af07#%E5%88%A5%E3%82%B9%E3%83%AC%E3%83%83%E3%83%89%E3%81%AE%E4%BE%8B%E5%A4%96%E3%82%92%E5%A0%B1%E5%91%8A%E3%81%99%E3%82%8B%E3%81%8B%E3%81%A9%E3%81%86%E3%81%8B%E3%82%92%E6%B1%BA%E3%82%81%E3%82%8Bthreadreport_on_exception%E3%83%95%E3%83%A9%E3%82%B0)

Ruby 2.4では明示的に`true`をセットしないとこの機能が有効になりませんでしたが、Ruby 2.5ではデフォルトで`true`になっています。

```ruby
# Ruby 2.5ではtrueがデフォルト値
Thread.report_on_exception #=> true
```

- 参考: [Feature \#14143: Thread\.report\_on\_exception should be true by default](https://bugs.ruby-lang.org/issues/14143)

## まとめ

というわけで、Ruby 2.5の新機能をあれこれ紹介してみました。
Rubyコミッタの方々をはじめ、Ruby 2.5の開発に携わってくださった開発者のみなさまに感謝します。

Part 1の記事とあわせて読んで、Ruby 2.5を使ったRubyプログラミングを楽しんでください！

- [Part 1はこちら](https://qiita.com/jnchito/items/f182b6f0093a6a3701a1)