サンプルコードでわかる！Ruby 2.7の主な新機能と変更点 Part 3 - 新機能と変更点の総まとめ

## はじめに
Rubyは毎年12月25日にアップデートされます。
Ruby 2.7については2019年12月21日にrc2がリリースされました。

[Ruby 2\.7\.0\-rc2 リリース](https://www.ruby-lang.org/ja/news/2019/12/21/2-7-0-rc2-released/)

この記事ではRuby 2.7で導入される変更点や新機能について、サンプルコード付きでできるだけわかりやすく紹介していきます。

ただし、Ruby 2.7は多くの新機能や変更点があり、1つの記事に収まらないのでいくつかの記事に分けて書いていきます。
番号指定パラメータ、パターンマッチ構文、キーワード引数に関する仕様変更についてはすでに他の記事で説明したので、本記事ではそれ以外の変更点を説明しています。

### すでに説明したRuby 2.7の新機能や変更点はこちら

Ruby 2.7の新機能や変更点は非常に多いので、いくつかの記事に分けて説明しています。
以下の記事はすでに公開済みです。

- [サンプルコードでわかる！Ruby 2\.7の主な新機能と変更点 Part 1 \- 番号指定パラメータ（numbered parameter） \- Qiita](https://qiita.com/jnchito/items/79f0172e60f237e2c542)
- [サンプルコードでわかる！Ruby 2\.7の新機能・パターンマッチ（前編） \- Qiita](https://qiita.com/jnchito/items/9bb4aa1dcefa00257815)
- [サンプルコードでわかる！Ruby 2\.7の新機能・パターンマッチ（後編） \- Qiita](https://qiita.com/jnchito/items/a9c7030cb5697f992cb7)
- [サンプルコードでわかる！Ruby 2\.7の主な新機能と変更点 Part 2 \- キーワード引数に関する仕様変更 \- Qiita](https://qiita.com/jnchito/items/c15ac23791e0320e0fc2)

### 本記事の情報源
本記事は以下のような情報源をベースにして、記事を執筆しています。

- [Ruby 2\.7\.0\-rc2 リリース](https://www.ruby-lang.org/ja/news/2019/12/21/2-7-0-rc2-released/)
- [rc2のNEWSページ](https://github.com/ruby/ruby/blob/v2_7_0_rc2/NEWS)
- [プロと読み解くRuby 2\.7 NEWS \- クックパッド開発者ブログ](https://techlife.cookpad.com/entry/2019/12/25/121834)
- リリースノートやNEWSに記載されている各種issue

また、issueを追いかけてもピンと来なかった内容については、ブログ「[ruby trunk changes](https://ruby-trunk-changes.hatenablog.com/)」のコミット解説を参考にさせてもらいました（nagachikaさん、どうもありがとうございます！）。

### 動作確認したRubyのバージョン

本記事は以下の環境で実行した結果を記載しています。

```console
$ ruby -v
ruby 2.7.0rc2 (2019-12-22 master 75acbd5f00) [x86_64-darwin19]
```

（2019.12.26追記）正式リリースされたRuby 2.7.0でも動作確認済みです。

```console
$ ruby -v
ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]
```

### フィードバックお待ちしています
本文の説明内容に間違いや不十分な点があった場合は、コメント欄や編集リクエスト等で指摘 or 修正をお願いします🙏

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

## 構文や言語機能上の変更点

### パターンマッチ構文が試験的に導入された（別記事にて説明済み）

Ruby 2.7ではパターンマッチ（またはパターンマッチング）構文が試験的に導入されました。

> 関数型言語で広く使われているパターンマッチという機能が実験的に導入されました。 渡されたオブジェクトの構造がパターンと一致するかどうかを調べ、一致した場合にその値を変数に代入するといったことができるようになります。
> 
> [Ruby 2\.7\.0\-rc2 リリース](https://www.ruby-lang.org/ja/news/2019/12/21/2-7-0-rc2-released/)

以下はパターンマッチ構文の使用例です。

```ruby
case {status: :error, message: 'User not found.'}
in {status: :success}
  puts "Success!"
in {status: :error, message: message}
  puts "Error: #{message}"
end
#=> Error: User not found.
```

パターンマッチについては以下の記事で詳しく説明しています。

- [サンプルコードでわかる！Ruby 2\.7の新機能・パターンマッチ（前編） \- Qiita](https://qiita.com/jnchito/items/9bb4aa1dcefa00257815)
- [サンプルコードでわかる！Ruby 2\.7の新機能・パターンマッチ（後編） \- Qiita](https://qiita.com/jnchito/items/a9c7030cb5697f992cb7)

### キーワード引数と普通の引数の自動変換が非推奨になった（別記事にて説明済み）

Ruby 2.7ではキーワード引数と普通の引数の自動変換が非推奨になり、警告が発生します。

以下は警告が発生するようになったコードの例です。

```ruby
def foo(key: 0)
  p key
end
foo({key: 42})
#=> The last argument is used as the keyword parameter
#=> 42
```

警告が発生するコードはRuby 3で動かなくなります。

詳しい内容はこちらの記事をご覧ください。

- [サンプルコードでわかる！Ruby 2\.7の主な新機能と変更点 Part 2 \- キーワード引数に関する仕様変更 \- Qiita](https://qiita.com/jnchito/items/c15ac23791e0320e0fc2)

### ブロックの仮引数として番号指定パラメータが導入された（別記事にて説明済み）

Ruby 2.7ではブロックの仮引数として番号指定パラメータ（numbered parameter）が
試験的に
導入されました。

（2019.12.27追記）
試験的な導入ではなく、Ruby 2.7.0で正式に導入されたようです。（[参考](https://github.com/ruby/ruby/commit/360f153fcaab637486aa2669d354d53e0eb1c3d2)）

これにより、`|s|` のように明示的に引数名を指定する代わりに、`_1` のような連番でブロックの仮引数を受け取ることができます。

```ruby
# 番号指定パラメータを使わない場合
%w(1 20 300).map { |s| s.rjust(3, '0') }
#=> ["001", "020", "300"]

# 番号指定パラメータを使う場合
%w(1 20 300).map { _1.rjust(3, '0') }
#=> ["001", "020", "300"]
```

```ruby
# 番号指定パラメータを使わない場合
[1, 2, 3, 4].inject(0) { |memo, n| memo + n }
#=> 10

# 番号指定パラメータを使う場合
[1, 2, 3, 4].inject(0) { _1 + _2 }
#=> 10
```

詳しい内容はこちらの記事をご覧ください。

- [サンプルコードでわかる！Ruby 2\.7の主な新機能と変更点 Part 1 \- 番号指定パラメータ（numbered parameter） \- Qiita](https://qiita.com/jnchito/items/79f0172e60f237e2c542)

### メソッドの内のブロックを伴わないProc.new/procとlambdaが警告またはエラー扱いとなった

Ruby 2.6まではメソッド内でブロックなしのProc.new/procやlambdaを呼ぶと、暗黙的にメソッド呼び出し時に引き渡したブロックが割り当てられていました。

```ruby
def proc_without_block
  # このprocにはこのメソッドを呼びだしたときに
  # 引き渡したブロックが暗黙的に割り当てられる
  proc.call * 100

  # Proc.newを使った場合も同様
  # Proc.new.call * 100
end

proc_without_block { 123 }
#=> 12300
```

```ruby
def lambda_without_block
  # lambdaの場合も同様
  lambda.call * 100
end

lambda_without_block { 123 }
#=> 12300
```

Ruby 2.7ではProc.new/procのときに警告が、lambdaのときは例外が発生するようになりました。

```ruby
def proc_without_block
  proc.call * 100
end

# Ruby 2.7では警告が出る
proc_without_block { 123 }
#=> warning: Capturing the given block using Kernel#proc is deprecated; use `&block` instead
```

```ruby
def lambda_without_block
  lambda.call * 100
end

# Ruby 2.7では例外が発生する
lambda_without_block { 123 }
#=> ArgumentError (tried to create Proc object without a block)
```

### 開始値省略範囲式(beginless range)が試験的に導入された

Ruby 2.7では開始値省略範囲式(beginless range)が試験的に導入されました。

```ruby
numbers = [10, 20, 30, 40, 50]

# beginless rangeを使って配列の最初の3要素を取得する
numbers[..2]
#=> [10, 20, 30]

# 従来通り次のように書いても同じ
numbers[0..2]
#=> [10, 20, 30]
```

Ruby 2.6で導入されたendless rangeと組み合わせると次のようなコードも書けます。

```ruby
n = -5
ret =
  case n
  when ..-1
    'minus'
  when 1..
    'plus'
  else
    'zero'
  end
ret
#=> minus
```

### 特殊変数の`$;`と`$,`に非nil値を設定すると警告が出るようになった

Ruby 2.7ではsplitメソッドのデフォルトの区切り文字を表す特殊変数`$;`と、デフォルトの出力フィールド区切り文字列（joinメソッドなどで使われる）を表す`$,`で、nil以外の値を設定すると警告が出るようになりました。
また、その状態でsplitメソッドやjoinメソッドを呼び出した際も警告が出ます。

```ruby
# デフォルト値はnil
$; #=> nil

# 引数なしでsplitメソッドを呼ぶと空白文字で分割される
'ab,c d'.split #=> ["ab,c", "d"]

# 非nil値を設定すると警告が出る
$; = ','
#=> warning: non-nil $; will be deprecated

# splitメソッドを呼んだときも警告が出る
'ab,c d'.split #=> ["ab", "cd"]
#=> warning: $; is set to non-nil value
```

```ruby
# デフォルト値はnil
$, #=> nil

# 引数なしでjoinメソッドを呼ぶと空文字で連結される
%w(a b c).join #=> "abc"

# 非nil値を設定すると警告が出る
$, = '-'
#=> warning: non-nil $, will be deprecated

# joinメソッドを呼んだときも警告が出る
%w(a b c).join #=> "a-b-c"
#=> warning: $, is set to non-nil value
```

### ヒアドキュメント識別子に付ける`'`や`"`が同じ行にないと構文エラーが出るようになった

下のコードのようにヒアドキュメントの識別子に付ける`'`や`"`が同じ行にない場合、Ruby 2.6までは警告が出ていましたが、Ruby 2.7からはsyntax errorが発生するようになりました。

```ruby
s = <<~"TEXT
"
  Hello!
TEXT
#=> Ruby 2.6では以下の警告が発生する
#   warning: here document identifier ends with a newline

#=> Ruby 2.7ではsyntax errorが発生する
#   unterminated here document identifier
#   s = <<~"TEXT
#   syntax error, unexpected end-of-input
#   s = <<~"TEXT
```

### フリップフロップ構文の警告が撤回された

Ruby 2.6からフリップフロップ構文を使うと警告が発生するようになっていましたが、Ruby 2.7では（正確にはRuby 2.6.4以降では）この変更が撤回され、警告が出なくなりました。

```ruby
numbers = 1..10
ret =
  numbers.map do |n|
    # この下の行がフリップフロップ構文
    if (n % 3 == 0)..(n % 2 == 0)
      n * 10
    end
  end
#=> Ruby 2.6.0〜2.6.3では以下の警告が発生していたが、
#   Ruby 2.6.4以降とRuby 2.7では発生しなくなった
#   warning: flip-flop is deprecated


ret.compact
#=> [30, 40, 60, 90, 100]
```

フリップフロップ構文って何？という方は以下の記事をご覧ください。

[範囲式を使ったフリップフロップ \- Qiita](https://qiita.com/jnchito/items/e7aa147e340311e94a4d)

### 改行を伴うメソッドチェーンにコメント行を挟み込めるようになった

Ruby 2.7では、以下のように改行を伴うメソッドチェーンにコメント行を挟み込めるようになりました。

```ruby
200
  # 次の値（つまり201）を得る
  .then(&:succ)
  # 文字列に変換する
  .then(&:to_s)
  # 逆順にする
  .then(&:reverse)
#=> "102"
```

ドットが後に来るパターンでも大丈夫です。

```ruby
200.
  # 次の値（つまり201）を得る
  then(&:succ).
  # 文字列に変換する
  then(&:to_s).
  # 逆順にする
  then(&:reverse)
```

ちなみに、Ruby 2.6では以下のように構文エラーが発生していました。

```
syntax error, unexpected '.', expecting end-of-input
  .then(&:succ)
```

### privateメソッドをself付きで呼び出せるようになった

これまでRubyのprivateメソッドは「レシーバを指定して呼び出すことができないメソッド」とされてきました。
そのためクラス内でも`self`付きでprivateメソッドを呼び出すと例外が発生していました。

```ruby
class Foo
  def hello
    # nameはprivateメソッドなのでself付きで呼び出すことはできない
    "I am #{self.name}"
  end

  private

  def name
    'Alice'
  end
end

foo = Foo.new

# self.nameと書いていたので、例外が発生する
foo.hello
#=> `hello': private method `name' called for #<Foo:0x00007fb0488b24c8> (NoMethodError)
```

Ruby 2.7ではself付きでprivateメソッドを呼び出せるようになりました。

```ruby
class Foo
  def hello
    # Ruby 2.7ではself付きでprivateメソッドを呼び出せる
    "I am #{self.name}"
  end

  private

  def name
    'Alice'
  end
end

foo = Foo.new
foo.hello
#=> "I am Alice"

# もちろん、クラスの外からはレシーバ付きでprivateメソッドを呼び出すことはできない
foo.name
#=> private method `name' called for #<Foo:0x00007f91049094e8> (NoMethodError)
```

ちなみに、これまでのRubyでもセッターメソッドだけはprivateでも例外的にself付きで呼び出すことができていました。（[こちらの記事](https://qiita.com/jnchito/items/451018811842c2631e1e)も参考）

```ruby
class Foo
  def initialize
    @name = 'Alice'
  end

  def hello
    "I am #{self.name}"
  end

  def change_name
    # Ruby 2.6以前でも、セッターメソッドだけはprivateでもself付きで呼び出せた
    self.name = 'Bob'
  end

  private

  def name=(value)
    @name = value
  end

  def name
    @name
  end
end

foo = Foo.new

foo.hello
#=> I am Alice

foo.change_name

foo.hello
#=> I am Bob
```

今回のRuby 2.7の変更により、セッターメソッドか、そうでないかに関わらず、一貫してprivateメソッドをself付きで呼び出せるようになります。

### rescue修飾子を伴う多重代入の挙動が変わった

Ruby 2.6では以下のようにrescue修飾子と多重代入を行うと次のような結果になっていました。

```ruby
# ZeroDivisionErrorが発生するが、rescueする
a, b = [1/0, 2/0] rescue [10, 20]
a #=> nil
b #=> nil
```

これは次のようにパースされていたためです。

```ruby
(a, b = [1/0, 2/0]) rescue [10, 20]
```

Ruby 2.7ではパースのされ方が以下のように変更されました。

```ruby
a, b = ([1/0, 2/0] rescue [10, 20])
```

これにより、rescue修飾子から返される配列を多重代入できるようになりました。

```ruby
a, b = [1/0, 2/0] rescue [10, 20]
a #=> 10
b #=> 20
```

### 特異クラス構文内でyieldを使うと警告が出るようになった

Ruby 2.7では、以下のように特異クラス構文内でyieldを呼び出すと警告が出るようになりました。（こんなコードを書くことは、まれだと思いますが・・・）

```ruby
def yield_in_class_syntax
  class << Object.new
    yield
  end
end

yield_in_class_syntax { puts 'nihao!' }
#=> warning: `yield' in class syntax will not be supported from Ruby 3.0. [Feature #15575]
#   nihao!
```

### 全引数を別のメソッドに引き渡す`...`引数が導入された

Ruby 2.7ではあらゆる引数を受け取って、別のメソッドに引き渡す`...`引数が導入されました。

```ruby
def add(a, b)
  a + b
end

def add_with_description(...)
  # 受け取った引数をすべてそのままaddメソッドに引き渡す
  answer = add(...)
  "answer is #{answer}"
end

add_with_description(2, 3)
#=> answer is 5
```

別のメソッドを呼び出すときは丸かっこ（`()`）が必須です。丸かっこがないと開始と終了のない範囲オブジェクト（`nil...nil`）と見なされます。

```ruby
# (...)と書かなかった場合は、nil...nilをaddメソッドを渡したことになる（警告も出る）
answer = add ...
#=> warning: ... at EOL, should be parenthesized?
```

メソッドの仮引数も別メソッドに渡す引数も、いずれも`...`になっている必要があります。
片方だけが通常の引数だったり、引数の一部だけが`...`になっていたりすると、構文エラーになります。

```ruby
def add_with_description(a, b)
  # 仮引数は通常の引数で、別メソッドの呼び出しが...になっていると構文エラー
  answer = add(...)
  "answer is #{answer}"
end
```

```ruby
# 引数の一部が通常の引数で、残りが...になっていると構文エラー
def add_with_description(a, ...)
  answer = add(a, ...)
  "answer is #{answer}"
end
```

### セキュリティモデル関連の特殊変数やメソッドのサポートが縮小された

RubyにはCGIプログラミングをサポートするためのセキュリティ機能（セキュリティモデル）が用意されています。（[参考](https://docs.ruby-lang.org/ja/2.6.0/doc/spec=2fsafelevel.html)）
しかし、近年のエコモデルではこの仕組みはあまり役に立たないため、サポートが縮小されつつあります。

特殊変数の`$SAFE`はRubyの「セーフレベル」を設定するために使われていましたが、Ruby 2.7ではこの変数を変更したり、参照したりすると警告が表示されます。

```ruby
# 参照すると警告が出る
$SAFE
#=> warning: $SAFE will become a normal global variable in Ruby 3.0
#   0

# 変更しても警告が出る
$SAFE = 1
#=> warning: $SAFE will become a normal global variable in Ruby 3.0
```

また、taint, untaint, trust, untrust といった、セキュリティモデルに関連するメソッドも無効化されています。（オブジェクトは汚染状態にはならない）

```ruby
some = "puts '@&%&(#!'"

some.tainted?
#=> false

# Ruby 2.7では呼びだしても何も変化がない
some.taint

some.tainted?
#=> falseのままになる（Ruby 2.6ではtrueになる）
```

### シンタックスハイライトや自動インデントなど、irbが大きく進化した

Ruby 2.7ではirbが大きく進化しました。

**2019.12.26追記：正式リリースされた2.7.0では警告は表示されません（ここから↓）**

それに関連して、irbを起動すると、以下のようなメッセージが表示されます。

```console
$ irb
This version of IRB is drastically different from the previous version.
If you hit any issues, you can use "irb --legacy" to run the old version.
If you want to just erase this message, please use "irb --multiline" or
add `IRB.conf[:USE_MULTILINE] = true` to your ~/.irbrc file.
irb(main):001:0>
```

メッセージを翻訳すると次のようになります。

> 本バージョンのIRBは以前のバージョンから大幅に変わっています。
> もし何か問題があれば、"irb --legacy"というコマンドで以前のバージョンに戻すことができます。
> このメッセージを消したい場合は、"irb --multiline"というコマンドを使うか、~/.irbrcファイルに`IRB.conf[:USE_MULTILINE] = true`を追加してください。

**2019.12.26追記：正式リリースされた2.7.0では警告は表示されません（ここまで↑）**

従来のirbに戻したり、新しいirbのままメッセージの表示をなくしたいときは、上のメッセージにあるとおり、`irb --legacy`や`irb --multiline`といったコマンドで起動します。

続いて、新しいirbの変更点を以下で紹介します。

**進化その1) シンタックスハイライトされる**

irb内に打ち込んだコードが自動的にシンタックスハイライトされます。
<img width="599" alt="Screen Shot 2019-12-24 at 5.23.44.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/dbace7cd-1abc-de96-e749-27ec9fd732d0.png">

**進化その2) 自動的にインデントしてくれる**

irb内に打ち込んだコードが自動的にインデントされます。
![6fMleap5ii.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/9d7b2c78-0502-2485-f212-d1d87d0a577b.gif)

**進化その3) 上下キーで複数行の入力履歴をまとめて行き来できる**

上下キーを押すと複数行の入力履歴をまとめて行き来できます。
![rC6CYRF7Tt.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/02c0f2bd-b0d7-ec64-c603-2620d07cb70c.gif)

ただし、長いメソッドやif文を入力したあとだと、履歴をさかのぼるのがちょっと大変かもしれません。
![9risyF6Apm.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/88018596-4f21-cf35-ed0a-e2883c280109.gif)

**進化その4) TABキーを押すと入力候補を表示してくれる**

メソッドを途中まで入力してTABキーを押すとクラス名やメソッド名、変数名などの入力候補を表示してくれます。
![2Q0QKdMev3.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/7ae665fa-e03d-08ac-4ec0-66bc39d236a2.gif)

候補が1つしかない場合は自動補完してくれます。
![3poUVqlEcW.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/2dbe6157-05f6-5e13-5ea4-2a06aeaaf2b0.gif)

**進化その5) TABキーを2回押すとクラスやメソッドのドキュメントを表示してくれる**

メソッド名を入力してTABキーを2回押すとクラスやメソッドのドキュメントを表示してくれます。
表示を戻すときは`q`を押します。
![YqJmzQnBb0.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/7465/cc3ca568-d3a7-4edd-721c-174e24531dc6.gif)

**~/.irbrcを編集してirbの設定を変更する**

冒頭のメッセージにもあったように、`~/.irbrc`に以下の設定を追加するとデフォルトの起動モードを変えられます。

```~/.irbrc
# trueなら新しいバージョン、falseなら以前のバージョンで起動
IRB.conf[:USE_MULTILINE] = true
```

自動インデントを無効化する場合は、以下の設定を追加します。

```~/.irbrc
IRB.conf[:AUTO_INDENT] = false
```

入力履歴はデフォルトで1000件保存されます。（`~/.irb_history`という履歴ファイルが作成されます）
履歴をもっと増やしたい、または減らしたい、という場合は`~/.irbrc`で好みの件数を設定できます。（nilを設定すると入力履歴が無効化されます）

```~/.irbrc
IRB.conf[:SAVE_HISTORY] = 2000
```

参考: [What's new in Interactive Ruby Shell \(IRB\) with Ruby 2\.7 – Saeloun Blog](https://blog.saeloun.com/2019/09/23/new-irb-features.html)

**2020.5.8追記：Ruby 2.7のirbがペーストすると遅すぎる問題について**

すごく便利になったRuby 2.7のirbですが、その副作用として（？）長い複数行テキストをペーストしたときにめちゃくちゃ遅くなる（最悪ハングする）という問題があります（Ruby 2.7.1で確認）。

[pasting in multiline irb is slow · Issue \#43 · ruby/irb](https://github.com/ruby/irb/issues/43)

これは`rails console`を経由した場合も同じです。

この問題を回避するためには、`--legacy`や`--nomultiline`のオプションを付けて、以前のバージョンで起動する必要があります。

```
$ irb --legacy
```

ただし、`rails console`ではこの方法が使えないため、上で紹介したように`~/.irbrc`に以下の設定を入れてください。

```~/.irbrc
IRB.conf[:USE_MULTILINE] = true
```

### `Object#method`と`Module#instance_method`がRefinementsを考慮するようになった

Ruby 2.6ではRefinementsによって定義されたメソッドを、`method`メソッドや`instance_method`メソッドを使って取り出すことができませんでした。（オーバーライドされていた場合は、オーバーライド前のメソッド定義を取り出していました）

```ruby
# Ruby 2.6の場合

# Refinementsを使ってStringクラスにshuffleメソッドを追加する
module StringShuffle
  refine String do
    def shuffle
      chars.shuffle.join
    end
  end
end
using StringShuffle

# Ruby 2.6ではメソッドオブジェクトとしてshuffleメソッドを取り出せない
m = 'abcdef'.method(:shuffle)
#=> NameError: undefined method `shuffle' for class `String'

# instance_methodを使った場合も同様
m = String.instance_method(:shuffle)
#=> NameError: undefined method `shuffle' for class `String'
```

Ruby 2.7では`method`メソッドや`instance_method`メソッドを使った場合でも、Refinementsによる拡張が考慮されるようになりました。

```ruby
# Ruby 2.7ではshuffleメソッドが取り出せる
m = 'abcdef'.method(:shuffle)
m.call
#=> 'dabefc'

# instance_methodを使った場合も同様
m = String.instance_method(:shuffle)
m.arity
#=> 0
```

### カテゴリ別に警告の表示・非表示を制御できるコマンドラインオプションが追加された

Ruby 2.7では `-W` オプションが拡張され、カテゴリ別に警告の表示・非表示が制御できるようになりました。

**非推奨（deprecation）警告を非表示にする**

```
ruby -W:no-deprecated your_code.rb
```

**試験的機能（experimental feature）警告を非表示にする**


```
ruby -W:no-experimental your_code.rb
```

**RUBYOPT環境変数を使って制御する**

RUBYOPT環境変数を使って制御することもできます。

```
RUBYOPT=-W:no-deprecated bundle exec rspec
```

複数のオプションを指定したい場合は次のようにスペースで区切ります。

```
RUBYOPT='-W:no-deprecated -W:no-experimental' bundle exec rspec
```

### Warning[(カテゴリ)]を使ってカテゴリ別に警告の表示・非表示を制御できるようになった

これは「構文や言語機能上の変更点」ではないのですが、上で説明した`-W`オプションと内容が近いので、このタイミングで一緒に説明しておきます。

Ruby 2.7では以下のようにWarning[カテゴリ]を使ってRubyのコード内で警告の表示・非表示を制御することも出来ます。

```ruby
# 非推奨警告を非表示にする
Warning[:deprecated] = false

def buy_burger(menu, drink: true, potato: true)
end

params = { drink: true, potato: false }

# 以下のコードは非推奨警告の対象になるが、Warning[:deprecated]にfalseをセットしたので警告が表示されない
buy_burger('fish', params)
```

コマンドラインや`RUBYOPT`環境変数で`-W:no-deprecated`のようなオプションを設定している場合は最初からfalseになっています。

```ruby
# -W:no-deprecatedオプションを付けて起動した場合は最初からfalse
Warning[:deprecated]
#=> false
```

指定可能なカテゴリは`:deprecated`と`:experimental`の2つだけです。
それ以外のカテゴリを指定すると例外が発生します。

```ruby
# 無効なカテゴリを指定すると例外が発生する
Warning[:hoge] = false
#=> ArgumentError: unknown category: hoge
```

警告によってはコード実行時ではなく、コードを読み込んだタイミングで警告が発生するものがあります。（パターンマッチ構文など）
その場合はWarning[(カテゴリ)]を使っても警告を非表示にはできません。（コマンドラインオプションや`RUBYOPT`環境変数を使って制御してください）

```ruby
# 試験的機能の警告を非表示にする
Warning[:experimental] = false

# しかし、パターンマッチ構文の警告は実行時ではなく、コードを読み込んだタイミングで警告が表示される
case 0
in n
  # ...
end
#=> warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
```

続いて、クラス単位でRuby 2.7の新機能や変更点を紹介していきます。

## Array

### `&`と同じ振る舞いをするintersectionメソッドが追加された

Ruby 2.7では両方の配列に含まれる要素を重複なく返す`intersection`メソッドが追加されました。
これは従来の`&`演算子と同じ挙動になります。

```ruby
a = [1, 1, 3, 5]
b = [3, 2, 1]

# 2つの配列に共通して含まれる要素は1と3（重複なしで返す）
a.intersection(b)
#=> [1, 3]

# Ruby 2.6以前では&演算子を使う方法しかなかった
a & b
#=> [1, 3]
```

ちなみにRuby 2.6では`|`演算子と同じ`union`メソッドと、`-`演算子と同じ`difference`メソッドが追加されました。

```ruby
a = [1, 2, 3]
b = [3, 4, 5]

# a | bと同じ
a.union(b)
#=> [1, 2, 3, 4, 5]
```

```ruby
a = [1, 2, 3, 4, 5]
b = [0, 1, 2]

# a - bと同じ
a.difference(b)
#=> [3, 4, 5]
```

`|`と`-`に名前付きのメソッドが追加されたのであれば、`&`にもあった方がいいのでは？ということで`intersection`メソッドが追加されたみたいです。

[Feature \#16155: Add an Array\#intersection method \- Ruby master \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/16155)

## Array / Range

### minmaxメソッドが高速化した

これまでArrayクラスの`minmax`メソッドはEnumerableモジュールによって提供されていましたが、Ruby 2.7では高速化のためArrayクラス自身に実装されました。

```ruby
# Ruby 2.6
[].method(:minmax)
#=> #<Method: Array(Enumerable)#minmax>

# Ruby 2.7
[].method(:minmax)
#=> #<Method: Array#minmax()>
```

Rangeクラスについても同様に`minmax`メソッドがRangeクラス自身に実装されました。

```ruby
# Ruby 2.6
(1..3).method(:minmax)
#=> #<Method: Range(Enumerable)#minmax>

# Ruby 2.7
(1..3).method(:minmax)
#=> #<Method: Range#minmax()>
```

### Range#minmaxがRange#maxの値を最大値として返すようになった

後方互換性のない変更点です。

上で説明したように、Ruby 2.7では`Range#minmax`はEnumerableモジュールからRangeクラス自身に実装されました。
この関係で`Range#minmax`が返す最大値は`Range#max`に対応する値になります。

以下はRuby 2.6でRuby 2.7で実行結果が異なるコード例です。

```ruby
('a'..'aa').max
#=> "aa"

# Ruby 2.6ではminmaxの最大値として"z"が返る
('a'..'aa').minmax
#=> ["a", "z"]

# Ruby 2.7では"aa"が返る（Range#maxの値と一致する）
('a'..'aa').minmax
#=> ["a", "aa"]


(1..(5.5)).max
#=> 5.5

# Ruby 2.6ではminmaxの最大値として5が返る
(1..(5.5)).minmax
#=> [1, 5]

# Ruby 2.7では5.5が返る（Range#maxの値と一致する）
(1..(5.5)).minmax
#=> [1, 5.5]
```

## Comparable

### clampメソッドに範囲オブジェクトを渡せるようになった

Ruby 2.7ではclampメソッドに範囲オブジェクトが渡せるようになりました。

```ruby
# Ruby 2.6以前
-1.clamp(0, 2) #=> 0
1.clamp(0, 2)  #=> 1
3.clamp(0, 2)  #=> 2

# Ruby 2.7からは範囲オブジェクトも渡せる
-1.clamp(0..2) #=> 0
1.clamp(0..2)  #=> 1
3.clamp(0..2)  #=> 2
```

ちなみに、clampメソッドはRuby 2.4で追加された比較的新しいメソッドです。

参考 [指定された範囲内の値を返すようにするComparable\#clamp](https://qiita.com/jnchito/items/9f9d45581816f121af07)

## Complex（複素数オブジェクト）

### `<=>`メソッドが追加され、条件付きで大小比較ができるようになった

Ruby 2.7では複素数オブジェクトに`<=>`メソッドが追加され、以下のような比較をしても例外が発生しなくなりました。

```ruby
# Ruby 2.6
0 <=> 0i
#=> NoMethodError (undefined method `<=>' for (0+0i):Complex)

# Ruby 2.7
0 <=> 0i
#=> 0
```

ただし、以下の条件を満たさない場合はnilが返ります。

- 複素数オブジェクトの虚部がゼロである
- 比較対象のオブジェクトが実数、または虚部がゼロの複素数オブジェクトである

```ruby
# 虚部がゼロでなければnil
Complex(2, 3) <=> Complex(2, 3) #=> nil
Complex(2, 3) <=> 1             #=> nil

# 比較対象のオブジェクトが実数でないのでnil
Complex(2) <=> :a #=> nil

# 虚部がゼロ、かつ比較対象のオブジェクトが実数なので比較可能
Complex(5) <=> 2  #=> 1

# 虚部がゼロ、かつ比較対象のオブジェクトが虚部ゼロの複素数なので比較可能
Complex(5) <=> Complex(2) #=> 1
```

## Dir

### globと`[]`にヌル文字で区切るパターンを渡すと例外が発生するようになった

Ruby 2.7では`Dir.glob`と`Dir.[]`にヌル文字で区切るパターンを渡すと例外が発生するようになりました。

```ruby
Dir.glob("foo\0bar")
#=> ArgumentError (nul-separated glob pattern is deprecated)

Dir["foo\0bar"]
#=> ArgumentError (nul-separated glob pattern is deprecated)
```

ちなみにRuby 2.6がリリースされたときには警告が出るようになっていて、段階的にこの機能が削除されたことになります。

```ruby
# Ruby 2.6
Dir.glob("foo\0bar")
#=> warning: use glob patterns list instead of nul-separated patterns
```

[Feature \#14643: Remove problematic separator '\\0' of Dir\.glob and Dir\.\[\] \- Ruby master \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/14643)

## Encoding

### CESU-8エンコーディングが追加された

Ruby 2.7ではCESU-8エンコーディング（[Wikipedia](https://en.wikipedia.org/wiki/CESU-8)）が使えるようになりました。

```ruby
s = "いろは"
encoded = s.encode("CESU-8")
encoded.encoding == Encoding::CESU_8
#=> true
```

## Enumerable

### select(filter)とmapを同時に行うfilter_mapメソッドが追加された

Ruby 2.7ではselect(filter)とmapを同時に行うfilter_mapメソッドが追加されました。
このメソッドを使うとブロックの戻り値が真であるものだけがmapの結果として返されます。

```ruby
numbers = [1, 2, 3, 4, 5]
# filter_mapを利用して偶数の要素だけ値を10倍する（奇数は要素から除外）
numbers.filter_map { |n| n * 10 if n.even? }
#=> [20, 40]

# Ruby 2.6以前だと下のどちらかの書き方になる
numbers.select(&:even?).map { |n| n * 10 }
numbers.map { |n| n * 10 if n.even? }.compact
```

ちなみに、`filter_map`では偽の要素（`nil`または`false`）が除外されますが、`compact`は`nil`の要素だけが除外されるところが違うので少し注意が必要です。

```ruby 
[1, nil, false].filter_map(&:itself) 
#=> [1]

[1, nil, false].compact 
#=> [1, false]
```


破壊的な`filter_map!`メソッドは用意されていないようです。

```ruby
numbers.filter_map! { |n| n * 10 if n.even? }
#=> NoMethodError (undefined method `filter_map!' for [1, 2, 3, 4, 5]:Array)
```


### 要素ごとの個数をカウントするtallyメソッドが追加された

Ruby 2.7では要素ごとの個数をカウントするtallyメソッドが追加されました。

```ruby
order = ['ピザ', 'パスタ', 'ピザ', 'ドリア', 'ドリア']
order.tally
#=> {'ピザ' => 2, 'パスタ' => 1, 'ドリア' => 2}

# Ruby 2.6以前で同等のことを実現するコード例
order.each_with_object(Hash.new(0)) { |o, h| h[o] += 1 }
#=> {'ピザ' => 2, 'パスタ' => 1, 'ドリア' => 2}
```

ちなみに、tallyという名前は線の本数で数を表現する、[tally marks](https://en.wikipedia.org/wiki/Tally_marks)（[画線法](https://ja.wikipedia.org/wiki/%E7%94%BB%E7%B7%9A%E6%B3%95)）に由来するそうで

![](https://upload.wikimedia.org/wikipedia/commons/thumb/6/66/Tally_marks_3.svg/240px-Tally_marks_3.svg.png)
![](https://upload.wikimedia.org/wikipedia/commons/thumb/5/5a/Tally_marks.svg/240px-Tally_marks.svg.png)

## Enumerator

### 自由なデータ変更を伴いつつ、無限のシーケンスを生成できるproduceメソッドが追加された

Ruby 2.7では自由なデータ変更を伴いつつ無限のシーケンスを生成できるproduceメソッドが追加されました。

```ruby
# 1を順番にインクリメントさせるシーケンスから最初の5つを取り出す
# （第1引数の1はシーケンスの初期値）
Enumerator.produce(1, &:succ).take(5)
#=> [1, 2, 3, 4, 5]

# 0から999までのランダムな数字を5つ作成する（初期値を与えないパターン）
Enumerator.produce { rand(1000) }.take(5)
#=> [110, 725, 554, 755, 861]
```

[こちらの記事](https://blog.saeloun.com/2019/11/27/ruby-2-7-enumerator-produce)にはproduceメソッドを使ってフィボナッチ数列を作成するコード例が載っていました。

```ruby
# https://blog.saeloun.com/2019/11/27/ruby-2-7-enumerator-produce
Enumerator.produce([0, 1]) { |base_1, base_2|
  [base_2, base_1 + base_2]
}.take(10).map(&:first)
#=> [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]
```

### lazyなenumeratorを非lazyなenumeratorに変換するeagerメソッドが追加された

Ruby 2.7ではlazyなenumeratorを非lazyなenumeratorに変換するeagerメソッドが追加されました。

```ruby
# lazyなenumeratorを作成する
lazy = [1, 2, 3].lazy.map { |x| x * 2 }

# 非lazyなenumeratorに変換する
enum = lazy.eager

# Enumeratorのインスタンスになっている
# （eagerを呼ばなければEnumerator::Lazyのインスタンス）
enum.class #=> Enumerator

# 非lazyなので普通に配列で返ってくる
# （lazyのままだと、またEnumerator::Lazyが返るので、to_aする必要がある）
enum.map { |x| x / 2 }
#=> [1, 2, 3]
```

[Feature \#15901: Enumerator::Lazy\#eager \- Ruby master \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/15901)


### Enumerator::Yielderオブジェクトにto_procメソッドが実装された

Enumerator::Yielderオブジェクトが`to_proc`メソッドを実装したため、`<<`や`.yield`を使わなくても`(&y)`のような記法で済むようになりました。

```ruby
# Ruby 2.6以前
# （ブロック引数のyがEnumerator::Yielderオブジェクト）
enum = Enumerator.new { |y|
  # y << i の代わりに y.yield i と書いても良い
  (1..3).each { |i| y << i }
}
enum.map { |i| i * 10 }
#=> [10, 20, 30]

# Ruby 2.7
enum = Enumerator.new { |y|
  # (&y)でOK
  (1..3).each(&y)
}
enum.map { |i| i * 10 }
#=> [10, 20, 30]
```

[Feature \#15618: Implement Enumerator::Yielder\#to\_proc \- Ruby master \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/15618)

## Fiber

### resumeすると同時にresumeされたfiber内で例外を発生させるraiseメソッドが追加された

Ruby 2.7はresumeすると同時にresumeされたfiber内で例外を発生させるraiseメソッドが追加されました。

```ruby
fib = Fiber.new do
  counter = 0
  loop { counter += Fiber.yield }
  counter
end
fib.resume
fib.resume(10)
fib.resume(100)

# raiseメソッドを使ってfiber内でStopIteration例外を発生させ、ループを終了させる
# ループが終了するとcounterの値が返ってくる
fib.raise(StopIteration)
#=> 110
```

[Feature \#10344: \[PATCH\] Implement Fiber\#raise \- Ruby master \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/10344)

## File

### ドットで終わる文字列をextnameメソッドに渡すとドットを返すようになった

Ruby 2.7ではドットで終わる文字列をextnameメソッドに渡すとドットを返すようになりました。

```ruby
# Ruby 2.6
File.extname('foo.')
#=> ""

# Ruby 2.7
File.extname('foo.')
#=> "."
```

## FrozenError

### 例外の発生原因となった凍結されたオブジェクトをreceiverメソッドで受け取れるようになった

Ruby 2.7では例外の発生原因となった凍結されたオブジェクトをreceiverメソッドで受け取れるようになりました。

```ruby
frozen_str = 'abc'.freeze
begin
  frozen_str.upcase!
rescue FrozenError => err
  # receiverメソッドで、例外の発生原因となったオブジェクトを取得できる
  err.receiver
  #=> "abc"

  # 元のオブジェクトと同一のオブジェクトが返るので、equal?で比較するとtrueが返る
  err.receiver.equal?(frozen_str)
  #=> true
end
```

FrozenErrorオブジェクトをnewする際は、`receiver:`オプションで`receiver`メソッドが返すオブジェクトを指定できます。

```ruby
frozen_str = 'abc'.freeze
e = FrozenError.new('test', receiver: frozen_str)
e.receiver.equal?(frozen_str)
#=> true
```

## GC

### 断片化したメモリをデフラグするGC.compactメソッドが導入された

Ruby 2.7では断片化したメモリをデフラグするGC.compactメソッドが導入されました。

```ruby
# 断片化したメモリをデフラグする
GC.compact
```

技術的な解説は[Ruby 2.7.0.preview3](https://www.ruby-lang.org/ja/news/2019/11/23/ruby-2-7-0-preview3-released/)のリリースノートより引用します。

> 一部のマルチスレッドなRubyプログラムを長期間動かし、マーク&スイープ型GCを何度も実行していると、メモリが断片化してメモリ使用量の増大や性能の劣化を招くことが知られています。

> Ruby 2.7ではGC.compact というメソッドを導入し、ヒープをコンパクションすることが出来るようになります。ヒープ内の生存しているオブジェクトを他のページに移動し、不要なページを解放できるようになるとともに、ヒープをCoW (Copy on Write) フレンドリーにすることが出来ます。 [[Feature #15626](https://bugs.ruby-lang.org/issues/15626)]

## IO

### BOMを見て外部エンコーディングを判別できるset_encoding_by_bomメソッドが導入された

Ruby 2.7ではBOMを見て外部エンコーディングを判別できるset_encoding_by_bomメソッドが導入されました。
ただし、ファイルをバイナリモードで開いていなかったり、すでに外部エンコーディングが設定されていたりした場合はArgumentErrorが発生します。

```ruby
# BOMが付いているファイルをバイナリモード("rb"の"b")で開く
io = File.open("test/fixtures/bom.txt", "rb")

# デフォルトの外部エンコーディングはASCII_8BIT
io.external_encoding
#=> Encoding::ASCII_8BIT

# encodingを判別する
io.set_encoding_by_bom
#=> Encoding::UTF_8

# 外部エンコーディングがUTF-8に変わる
io.external_encoding
#=> Encoding::UTF_8
```

以下はBOMが付いていないUTF-8のテキストファイルを開いた場合の挙動です。

```ruby
# BOMが付いていないファイルをバイナリモードで開く
io = File.open("test/fixtures/no_bom.txt", "rb")

# デフォルトの外部エンコーディングはASCII_8BIT
io.external_encoding
#=> Encoding::ASCII_8BIT

# encodingを判別する（BOMがないのでnilが返る）
io.set_encoding_by_bom
#=> nil

# 外部エンコーディングはASCII_8BITのまま
io.external_encoding
#=> Encoding::ASCII_8BIT
```

[Bug \#15210: UTF\-8 BOM should be removed from String in internal representation \- Ruby master \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/15210)

## Integer

### `[]`メソッドがビットの範囲を指定できるようになった

Ruby 2.7では`[]`メソッドを使って指定した範囲のビットの値を取り出せるようになりました。

```ruby
# 0番目のビットから4桁ぶん取得する
0b01001101[0, 4]
#=> 0b1101 (10進数では13)

# 0番目から3番目までのビットを取得する（[0, 4]と同じ）
0b01001101[0..3]
#=> 0b1101

# 整数値を指定すると、その位置のビットの値を返す（Ruby 2.6以前からの仕様）
0b01001101[0] #=> 1
0b01001101[1] #=> 0
0b01001101[2] #=> 1
0b01001101[3] #=> 1
```

## Kernel

### PathnameメソッドにPathnameオブジェクトを引数として渡すと引数そのものを返すようになった

Ruby 2.7では`Kernel#Pathname`メソッドにPathnameオブジェクトを引数として渡すと引数そのものを返すようになりました。

```ruby
pathname = Pathname.new('/tmp')

# Kernel#PathnameにPathnameオブジェクトを渡すと引数自身が返る
Pathname(pathname).equal?(pathname)
#=> true
```

なお、上のコードでは大文字で始まる`Pathname`が2回出てきますが、前者がクラス名（定数）で、後者はメソッドである点に注意してください。

```ruby
# Pathnameはクラス名（定数）
Pathname.new('/tmp')

# Pathnameは（大文字で始まっているが）メソッド
Pathname(pathname)
```

## Method

### inspectメソッドがメソッドの引数や定義場所の情報も返すようになった

Ruby 2.7では`Method#inspect`メソッドが、メソッドの引数や定義場所の情報も返すようになりました。

```ruby
class SampleClass
  def example_for_inspect(a, b=nil, *c, d:, e: nil, **rest, &block)
  end
end

# Ruby 2.6
SampleClass.new.method(:example_for_inspect).inspect
#=> "#<Method: SampleClass#example_for_inspect>"

# Ruby 2.7
SampleClass.new.method(:example_for_inspect).inspect
#=> "#<Method: SampleClass#example_for_inspect(a, b=..., *c, d:, e: ..., **rest, &block) (your path)/sample_class.rb:2>"
```

## Module

### 定数の定義場所を返すconst_source_locationメソッドが追加された

Ruby 2.7では`Module#const_source_location`メソッドを使って、定数の定義場所を確認できるようになりました。

```ruby
module ConstantExample
  FOO = 123
end

# const_source_locationメソッドを呼ぶと、定義場所のパスと行番号が返る
ConstantExample.const_source_location(:FOO)
#=> ["(your path)/const_example.rb", 2]
```

### autoload?メソッドがinheritフラグを受け取れるようになった（未執筆）

（僕は使い方がいまいちよくわからなかったので、@tmtmsさんの以下のブログ記事を参照してください🙏）

- [Ruby 2\.7 の変更点 \- Module \- @tmtms のメモ](https://tmtms.hatenablog.com/entry/201912/ruby27-module)

### nameメソッドが常に凍結された文字列を返すようになった（試験的な変更）

Ruby 2.7では`Module#name`メソッドが常に凍結された文字列を返すようになりました。
ただし、これは試験的な変更です。

```ruby
name = Time.name
p name #=> "Time"

# Ruby 2.6
name.frozen? #=> false

# Ruby 2.7
name.frozen? #=> true
```

## Module / Proc

### ruby2_keywordsメソッドが追加された（未執筆）

（僕は使い方がいまいちよくわからなかったので、@tmtmsさんの以下のブログ記事を参照してください🙏）

- [Ruby 2\.7 の変更点 \- Module \- @tmtms のメモ](https://tmtms.hatenablog.com/entry/201912/ruby27-module)
- [Ruby 2\.7 の変更点 \- 2\.7\.0\-rc2 \- @tmtms のメモ](https://tmtms.hatenablog.com/entry/201912/ruby27-rc2)

## NilClass / TrueClass / FalseClass

### to_sメソッドを呼ぶと凍結された文字列を返すようになった（試験的な変更）

Ruby 2.7では`nil`/`true`/`false`に対して`to_s`メソッドを呼ぶと、凍結された文字列を返すようになりました。
ただし、これは試験的な変更です。

```ruby
s = nil.to_s
p s       #=> ""
# Ruby 2.7（Ruby 2.6ではfalse。以下同様）
s.frozen? #=> true

s = true.to_s
p s       #=> "true"
s.frozen? #=> true

s = false.to_s
p s       #=> "false"
s.frozen? #=> true
```

## ObjectSpace::WeakMap

### true/falseやシンボルなど、特別なオブジェクトをキーや値に指定できるようになった

Ruby 2.7ではtrue/falseやシンボルなど、特別なオブジェクトをWeakMapのキーや値に指定できるようになりました。

```ruby
wm = ObjectSpace::WeakMap.new
x = 'Hello'

wm[true] = x
wm[true] #=> Hello

wm[nil] = x
wm[nil]  #=> Hello

wm[1] = x
wm[1]    #=> Hello

wm[:foo] = x
wm[:foo] #=> Hello

# Ruby 2.6ではエラーになる
wm[true] = x
#=> ArgumentError (cannot define finalizer for TrueClass)
```

## Proc

### to_sメソッドの戻り値に`@`が含まれなくなった

後方互換性のない変更点です。

Ruby 2.7では`Proc#to_s`の戻り値に`@`の文字が含まれなくなりました。

```ruby
# Ruby 2.6
->{}.to_s 
#=> "#<Proc:0x00007fba7c952db8@(irb):36 (lambda)>"

# Ruby 2.7
->{}.to_s
#=> "#<Proc:0x00007fda4c934c38 (irb):41 (lambda)>"
```

なお、この変更は`@`ではなくスペースで区切られていれば、ターミナルでobject_idをダブルクリックしたときにobject_idだけをきれいに選択できるから、という理由で導入されたようです。

参考 [Feature \#16101: Proc\#to\_s returns "\.\.\. file:line" instead of "\.\.\.@file:line" \- Ruby master \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/16101)

## Range

### `Range#===`メソッドが文字列に対しても`Range#cover?`を使うようになった

後方互換性のない変更点です。

Ruby 2.6（2.7ではなく、2.6です）では`===`を呼びだしたとき、`Range#include?`メソッドで判定不能だった場合は`Range#cover?`メソッドで範囲内かどうかをチェックするように変更されました。

参考：[サンプルコードでわかる！Ruby 2\.6の主な新機能と変更点 \- Qiita](https://qiita.com/jnchito/items/ce11cb7fccdf4b66af24#range%E3%81%AE%E3%81%8C%E5%88%A4%E5%AE%9A%E6%99%82%E3%81%ABinclude%E3%81%A0%E3%81%91%E3%81%A7%E3%81%AA%E3%81%8Fcover%E3%82%82%E5%88%A9%E7%94%A8%E3%81%99%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%9F)

しかし、文字列に対して`===`を呼びだしたときは`Range#include?`が使われていました。
Ruby 2.7では文字列の場合でも`Range#cover?`が使われるようになります。

```ruby
('A'..'Z').cover? 'ANA'
#=> true

('A'..'Z').include? 'ANA'
#=> false

# Ruby 2.6ではinclude？が使われるのでfalse
('A'..'Z') === 'ANA'
#=> false

# Ruby 2.7ではcover？が使われるのでtrue
('A'..'Z') === 'ANA'
#=> true
```

参考：[Bug \#15449: Range\#=== is not using cover in Ruby 2\.6 \- Ruby master \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/15449)

## RubyVM

### `RubyVM.resolve_feature_path`が`$LOAD_PATH.resolve_feature_path`に移動した（未執筆）

（僕は詳しい内容がよくわからなかったので、@tmtmsさんの以下のブログ記事を参照してください🙏）

- [Ruby 2\.7 の変更点 \- GC / Method / NilClass / TrueClass / FalseClass / Proc / RubyVM \- @tmtms のメモ](https://tmtms.hatenablog.com/entry/201912/ruby27-misc5)

## String

### Unicodeバージョンが12.1.0に、Unicide Emojiバージョンが12.1になった

Ruby 2.7ではUnicodeバージョンが12.1.0に、Unicide Emojiバージョンが12.1になりました。

```ruby
RbConfig::CONFIG['UNICODE_VERSION']       #=> 12.1.0
RbConfig::CONFIG['UNICODE_EMOJI_VERSION'] #=> 12.1
```

ちなみにUnicode 12.1では「令和」の合字が追加されています。

> 12.1 では日本の新年号「令和」の合字一文字のみを加えており、総文字数は 137,929 となりました。
>
> [The Unicode Blog: Unicode コンソーシアムは「令和」をサポートする Unicode 12\.1 を正式リリースしました](http://blog.unicode.org/2019/05/unicode-12-1-jp.html)

## Symbol

### start_with?メソッドとend_with?メソッドが追加された

Ruby 2.7ではSymbolクラスに`start_with?`メソッドと`end_with?`メソッドが追加されました。
文字通りレシーバであるシンボルが指定された文字列で始まっていれば（または終わっていれば）trueを、そうでなければfalseを返します。

```ruby
:foo_bar.start_with?('foo') #=> true
:foo_bar.start_with?('oo')  #=> false

:foo_bar.end_with?('bar')   #=> true
:foo_bar.end_with?('ba')    #=> false
```

ちなみに引数は文字列で指定します。シンボルで指定すると例外が発生します。

```ruby
:foo_bar.start_with?(:foo)
#=> TypeError (no implicit conversion of Symbol into String)
```

## Time

### ミリ秒以下を切り上げ/切り下げできるceil/floorメソッドが追加された

Ruby 2.7ではTimeクラスにミリ秒以下を切り上げ/切り下げできるceil/floorメソッドが追加されました。

```ruby
# ミリ秒以下に10桁の値を持つTimeオブジェクトを作成する
t = Time.utc(2010, 3, 30, 5, 43, "25.0123456789".to_r)
t.iso8601(10)
#=> 2010-03-30T05:43:25.0123456789Z

# ミリ秒以下を切り上げる
t.ceil(0).iso8601(10)
#=> 2010-03-30T05:43:26.0000000000Z
t.ceil(5).iso8601(10)
#=> 2010-03-30T05:43:25.0123500000Z

# ミリ秒以下を切り下げる
t.floor(0).iso8601(10)
#=> 2010-03-30T05:43:25.0000000000Z
t.floor(5).iso8601(10)
#=> 2010-03-30T05:43:25.0123400000Z
```

### inspectメソッドがミリ秒の情報も返すようになった

Ruby 2.7では`Time#inspect`メソッドがミリ秒の情報も返すようになりました。

```ruby
t = Time.new(2010, 3, 30, 5, 43, "25.0123456789".to_r, '+09:00')

# Ruby 2.6ではinspectもto_sも返ってくる文字列は同じ
t.to_s
#=> 2010-03-30 05:43:25 +0900
t.inspect
#=> 2010-03-30 05:43:25 +0900

# Ruby 2.7ではinspectメソッドがミリ秒以下の情報も返す
t.inspect
#=> 2010-03-30 05:43:25 123456789/10000000000 +0900
```

## UnboundMethod

### bindとcallを同時に行うbind_callメソッドが追加された

Ruby 2.7ではbindとcallを同時に行う`UnboundMethod#bind_call`メソッドが導入されました。

```ruby
# https://github.com/ruby/ruby/blob/v2_7_0_rc2/NEWS から引用
class Foo
  def add_1(x)
    x + 1
  end
end
class Bar < Foo
  def add_1(x) # override
    x + 2
  end
end

obj = Bar.new
p obj.add_1(1) #=> 3
p Foo.instance_method(:add_1).bind(obj).call(1) #=> 2
p Foo.instance_method(:add_1).bind_call(obj, 1) #=> 2
```

## 標準ライブラリ関連のアップデート

## Date

### 令和の和暦を"R01"のように表示したりパースしたりできるようになった

Ruby 2.7では令和の和暦を"R01"のように表示したりパースしたりできるようになりました。

```ruby
require 'date'

reiwa_date = Date.new(2019, 5, 1)

reiwa_date.jisx0301
#=> R01.05.01

Date.parse("R01.05.01")
#=> #<Date: 2019-05-01 ((2458605j,0s,0n),+0s,2299161j)>
```

## ERB

### ERBインスタンスをマーシャリングしようとすると例外が発生するようになった

Ruby 2.7ではERBインスタンスをマーシャリングしようとすると例外が発生するようになりました。

```ruby
require 'erb'

str = "hoge"
erb = ERB.new("value = <%= str %>")

# ERBインスタンスをマーシャリングすると例外が発生する
Marshal.dump(erb)
#=> TypeError (singleton class can't be dumped)
```

## open-uri

### Kernelのopenメソッドを使うと警告が出るようになった

Ruby 2.7では`open-uri`ライブラリが提供している`open`メソッドを使うと警告が出るようになります。

```ruby
require 'open-uri'

# open-uriが提供しているopenメソッドを使うと警告が出る
open("http://www.ruby-lang.org/")
#=> warning: calling URI.open via Kernel#open is deprecated, call URI.open directly

# 代わりにURI.openメソッドを使う
# （この場合も事前に'open-uri'ライブラリをrequireしておく）
URI.open("http://www.ruby-lang.org/")
```

なお、この変更はRuby 2.5のリリース時に予告されていました。

> URI.open method defined as an alias to open-uri's Kernel.open. open-uri's Kernel.open will be deprecated in future.
>
> https://github.com/ruby/ruby/blob/ruby_2_5/NEWS

### Webページを開いた際のデフォルトの文字コードがUTF-8になった

Ruby 2.7ではWebページを開いた際のデフォルトの文字コードがUTF-8になりました。

```ruby
require 'open-uri'

URI.open("http://www.ruby-lang.org/") do |f|
  puts f.charset #=> utf-8
end
```

## OptionParser

### 不明なオプションが渡されると"Did you mean?"が表示されるようになった

Ruby 2.7のOptionParserでは不明なオプションが渡されると"Did you mean?"が表示されるようになりました。

```option_parser_sample.rb
require 'optparse'

OptionParser.new do |opts|
  opts.on("-f", "--foo", "foo") {|v| }
  opts.on("-b", "--bar", "bar") {|v| }
  opts.on("-c", "--baz", "baz") {|v| }
end.parse!
```

```console
$ option_parser_sample.rb --fooo
Traceback (most recent call last):
option_parser_sample.rb:7:in `<main>': invalid option: --fooo (OptionParser::InvalidOption)
Did you mean?  foo
```

## Pathname

### globメソッドにオプション引数としてbaseが渡せるようになった

Ruby 2.7では`Pathname.glob`メソッドのオプション引数として`base:`が渡せるようになりました。
この引数は`Dir.glob`メソッド（`Dir.[]`メソッド）の`base:`オプションに委譲されます。

> [PARAM] base:
> カレントディレクトリの代わりに相対パスの基準にするベースディレクトリを指定します。指定した場合、結果の頭にはベースディレクトリはつかないので、絶対パスが必要な場合はベースディレクトリを追加する必要があるでしょう。
>
> [singleton method Dir\.\[\] \(Ruby 2\.6\.0 リファレンスマニュアル\)](https://docs.ruby-lang.org/ja/latest/method/Dir/s/=5b=5d.html)


```ruby
require 'pathname'

path = '/tmp'

# /tmpディレクトリ以下のパスを表すPathnameオブジェクトの配列が返る
Pathname.glob("*", File::FNM_DOTMATCH, base: path)
#=> [#<Pathname:.>, #<Pathname:..>, ...]
```

## その他

Ruby 2.7ではこれ以外にもさまざまな変更点がありますが、全部は説明しきれないので[NEWS](https://github.com/ruby/ruby/blob/v2_7_0_rc2/NEWS)のページに載っていた内容で、ここまでに説明していない情報を箇条書きにしておきます。

- RubyGemsのバージョンが3.1.2になった（[参考](https://github.com/rubygems/rubygems/blob/3.1/History.txt#L3)）
- Bundlerのバージョンが2.1.2になった（[参考](https://github.com/bundler/bundler/releases/tag/v2.1.2)）
- エスケープ文字が含まれる場合のCGI.escapeHTMLメソッドの呼び出しが2倍から5倍速くなった（[参考](https://github.com/ruby/ruby/pull/2226)）
- CSVのバージョンが3.1.2になった（[参考](https://github.com/ruby/csv/blob/master/NEWS.md)）
- Object#DelegateClass accepts a block and module_evals it in the context of the returned class, similar to Class.new and Struct.new.
- JSONのバージョンが2.3.0になった
- Add Net::FTP#features to check available features, and Net::FTP#option to enable/disable each of them. [Feature #15964]
- Add ipaddr optional parameter to Net::HTTP#start to replace the address for TCP/IP connection [Feature #5180]
- Net::IMAP / Add Server Name Indication (SNI) support. [Feature #15594]
- Racc / Merge 1.4.15 from upstream repository and added cli of racc.
- Reline / New stdlib that is compatible with readline stdlib by pure Ruby and also has a multiline mode.
- REXMLのバージョンが3.2.3になった（[参考](https://github.com/ruby/rexml/blob/master/NEWS.md)）
- RSSのバージョンが0.2.8になった（[参考](https://github.com/ruby/rss/blob/master/NEWS.md)）
- StringScannerのバージョンが1.0.3になった（[参考](https://github.com/ruby/strscan/blob/master/NEWS.md)）
- 以下のライブラリがbundled gemでなくなり、明示的なgemのインストールが必要になった
    - CMath (cmath gem)
    - Scanf (scanf gem)
    - Shell (shell gem)
    - Synchronizer (sync gem)
    - ThreadsWait (thwait gem)
    - E2MM (e2mmap gem)
- 以下のライブラリがdefault gemとしてrubygems.orgに公開された
    - benchmark
    - cgi
    - delegate
    - getoptlong
    - net-pop
    - net-smtp
    - open3
    - pstore
    - readline
    - readline-ext
    - singleton
- 以下のライブラリもdefault gemになった。ただし、rubygems.orgにはまだ公開されていない
    - monitor
    - observer
    - timeout
    - tracer
    - uri
    - yaml
- `did_you_mean` gemがbundled gemからdefault gemに昇格した
- `Profiler__`モジュールとprofile.rbが標準ライブラリから削除された（Ruby 2.0から誰もメンテナンスしていないため）
- パフォーマンス、もしくは実装上の改善
    - Fiber
    - File.realpath
    - Hash
    - MonitorとMonitorMixinのパフォーマンスが改善された
    - Thread
    - JIT
- その他の変更
    - RubyのビルドにC99に対応したコンパイラが必要になった
    - IA64アーキテクチャのサポートが削除された
    - 開発リポジトリがSubversionからGitに変更された（ただし、GitHubではなく https://git.ruby-lang.org/ruby.git でホストされている）
    - RUBY_REVISIONが数値ではなく文字列になった（リポジトリがSubversionからGitに変わった影響）
    - RUBY_DESCRIPTIONにSubversionのrevisionではなく、Gitのrevisionが含まれるようになった
    - Support built-in methods in Ruby with `_builtin` syntax. 

## まとめ

というわけで、この記事ではRuby 2.7で導入されたさまざまな新機能や変更点を説明しました。

いやあ、今年は例年以上に変更点が多いですね！！
「Rubyは死んだ」とか言ってるのはいったい誰なんでしょうか？

個人的には`Enumerable#tally`や`Enumerable#filter_map`の使用頻度が高くなりそうな予感がします。
Rubyって配列やハッシュ周りのメソッドがめちゃくちゃ豊富なのが好きです、僕は。

irbの進化もすさまじくて、簡単な動作確認がすこぶる効率アップしそうですね。

そして、今年も楽しいクリスマスプレゼントを届けてくれたMatzさんやコミッタのみなさんに感謝したいと思います。どうもありがとうございました！
みなさんもぜひRuby 2.7の新機能を試してみてください😉

### あわせて読みたい

本記事の中でもいくつか紹介しましたが、@tmtmsさんも毎年アドベントカレンダーでRubyの新機能を解説されています。
僕が説明を端折った機能も丁寧に解説されているので、こちらも一緒にチェックすることをお勧めします。

[Ruby 2\.7 Advent Calendar 2019 \- Qiita](https://qiita.com/advent-calendar/2019/ruby27)

こちらの記事では、Rubyコミッタの笹田さんと遠藤さんが「Ruby 2.7でなぜそのような変更が行われたのか」という背景を解説されています。こちらもあわせて読むと、Ruby 2.7の新機能をより深く理解できるはずです。

[プロと読み解くRuby 2\.7 NEWS \- クックパッド開発者ブログ](https://techlife.cookpad.com/entry/2019/12/25/121834)

Ruby 2.3〜2.6の新機能は以下の記事にまとめてあります。
こちらもあわせてどうぞ。

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

### PR: 本記事を読んでもよくわからなかったRuby初心者の方へ

「本文に一通り目を通してみたけど、なんかよくわからない用語がたくさん出てきて、イマイチちゃんと理解できなかった😣」というRuby初心者の方は、拙著「プロを目指す人のためのRuby入門」（通称チェリー本）を読んでみてください。
本書の内容を一通り理解すれば、この記事の内容も問題なく読みこなせるはずです！

[プロを目指す人のためのRuby入門｜技術評論社](https://gihyo.jp/book/2017/978-4-7741-9397-7)
[![9784774193977.jpg](https://qiita-image-store.s3.amazonaws.com/0/7465/67b8854c-a86e-caa8-64d3-7fa5724a1bf0.jpeg)](https://gihyo.jp/book/2017/978-4-7741-9397-7)

ちなみに本書の対象バージョンはRuby 2.4.1ですが、Ruby 2.5以降で発生する記述内容との差異は、それぞれ以下の記事にまとめてあります。なので、多少バージョンが古くても安心して読んでいただけます😊

- [Ruby 2\.5で発生する「プロを目指す人のためのRuby入門」との差異について \- Qiita](https://qiita.com/jnchito/items/761db22aec84910fe116)
- [Ruby 2\.6で発生する「プロを目指す人のためのRuby入門」との差異について \- Qiita](https://qiita.com/jnchito/items/a2a833e3bd80a452fd61)
- [Ruby 2\.7で発生する「プロを目指す人のためのRuby入門」との差異について \- Qiita](https://qiita.com/jnchito/items/aa4ebf6f0e363a1b8098)