サンプルコードでわかる！Ruby 2.4の新機能と変更点

## はじめに

2016年9月にRuby 2.4のpreview2が、11月にpreview3が、12月11日にrc1がリリースされました（[参考1](https://www.ruby-lang.org/en/news/2016/09/08/ruby-2-4-0-preview2-released/)、[参考2](https://www.ruby-lang.org/en/news/2016/11/09/ruby-2-4-0-preview3-released/)、[参考3](https://www.ruby-lang.org/en/news/2016/12/12/ruby-2-4-0-rc1-released/)）。
僕は早速インストールして新機能を試してみましたが、みなさんはいかがでしょうか？

というわけで、この記事では僕が実際に動かして確認したRuby 2.4の変更点や新機能をサンプルコード付きでまとめます。

この記事は大きく分けて次のセクションに分かれています。

- 文法上の変更点
- 後方互換性が失われる変更点
- パフォーマンス改善
- オブジェクト全般の新機能
- 主に数値に関連する新機能
- 主に文字列・正規表現に関連する新機能
- 主に配列・ハッシュに関連する新機能
- ディレクトリやファイルに関連する新機能
- その他の標準ライブラリに関連する新機能
- スレッドに関連する新機能

なお、Ruby 2.4は2016年12月25日にリリースされる予定です。
本記事はrc1時点のものなので、正式リリースまでに内容が変わる可能性もあります。

### 2016.12.25追記：正式リリース版でも動作検証しました

予定通り、2016年12月25日にRuby 2.4.0が正式リリースされました。

[Ruby 2\.4\.0 リリース](https://www.ruby-lang.org/ja/news/2016/12/25/ruby-2-4-0-released/)

下記の内容のほとんどはMinitestで動作検証しています。
Ruby 2.4.0でも全部のテストがパスしたので、正式リリース版でも本記事の内容は有効だと言えると思います。

### 本記事の情報源

本記事は以下のページに記載されている変更点をまとめたものです。

- https://github.com/ruby/ruby/blob/v2_4_0_preview2/NEWS
- https://github.com/ruby/ruby/blob/v2_4_0_preview3/NEWS
- https://github.com/ruby/ruby/blob/v2_4_0_rc1/NEWS

また、サンプルコードやパフォーマンスの計測値については下記サイトを一部参考にさせてもらいました。

[New Features in Ruby 2\.4 \- BlockScore Blog](https://blog.blockscore.com/new-features-in-ruby-2-4/)

主要な変更点は本記事の内容でカバーできているはずですが、マイナーな変更点まではカバーできていないかもしれません。

また、説明している内容に間違いがあれば、コメントや編集リクエスト等で優しく指摘してやってください。

### 本記事で使ったサンプルコードのリポジトリ

本記事で使ったサンプルコード（テストコード）は以下のリポジトリにあります。

https://github.com/JunichiIto/ruby-2-4

### Minitestの簡単な説明

本文中ではMinitestを使ったテストコードが登場します。
Minitestのコードの意味がよくわからない、という人は最低限、以下の検証メソッドの意味を理解していれば、なんとなく内容がつかめるかと思います。

```ruby
# aはbに等しい
assert_equal b, a

# aはnil
assert_nil a

# aは真（true、またはnilでもfalseでもない値）
assert a

# aは偽（nilまたはfalse）
refute a

# a.foo() を実行するとFooErrorが発生する
assert_raises(FooError) { a.foo() }

# 呼び出された時点でテストを失敗させる
flunk
```

それでは以下が本編です。先にPreview 2での変更点を載せ、それからPreview 3とrc1の変更点を載せます。

## 文法上の変更点

### 条件式での多重代入が可能になった

Ruby 2.4では条件式で多重代入が使えるようになりました。
以下は引数によって配列または`nil`を返すメソッドを条件式内で使うコード例です。

```ruby
def some_method_returning_array_or_nil(flag)
  flag ? [1, 2] : nil
end

def test_multiple_assignment_in_conditional_expression
  # 配列が返ると真と判定される
  if (a, b = some_method_returning_array_or_nil(true))
    assert true
  else
    flunk 'should be true'
  end
  
  # nilが返ると偽と判定される
  if (a, b = some_method_returning_array_or_nil(false))
    flunk 'should be false'
  else
    assert true
  end
end
```

なお、Ruby 2.3以前では上記のコードは`multiple assignment in conditional (SyntaxError)`というエラーが発生して実行できませんでした。

参考: [Feature \#10617: Change multiple assignment in conditional from parse error to warning](https://bugs.ruby-lang.org/issues/10617)

## 後方互換性が失われる変更点

### FixnumクラスとBignumクラスがIntegerクラスに統合された

Ruby 2.3までは整数は基本的にFixnumクラスで、極端に大きいまたは小さい数になるとBignumクラスに自動的に変わっていましたが、Ruby 2.4では両者の親クラスであるIntegerクラスにまとめられます。

```ruby
def test_unify_Fixnum_and_Bignum_into_Integer
  assert_equal Integer, 1.class
  assert_equal Integer, -1.class
  assert_equal Integer, 10000000000000000000000000000.class
  assert_equal Integer, -10000000000000000000000000000.class
end
```

また、`Fixnum`と`Bignum`を定数として参照すると、`Integer`が返ってきます。

```
Fixnum
#=> Integer

Bignum
#=> Integer
```

よって以下のテストコードはすべてパスします。

```ruby
def test_unify_Fixnum_and_Bignum_into_Integer
  a = 1
  
  assert a.kind_of?(Integer)
  assert a.kind_of?(Fixnum)
  assert a.kind_of?(Bignum)
  
  assert a.is_a?(Integer)
  assert a.is_a?(Fixnum)
  assert a.is_a?(Bignum)
  
  assert Integer === a
  assert Fixnum === a
  assert Bignum === a
end
```

Ruby 2.4では「Fixnum = Bignum = Integer」なので、FixnumやBignumを独自に拡張していた場合は、Integerクラスを拡張したことと同じになります。
また、FixnumとBignumで同名のメソッドを定義していた場合は、後勝ちになるようです。

```ruby
class ::Fixnum
  def foo
    'foo'
  end
  def baz
    'baz'
  end
end

class ::Bignum
  def bar
    'bar'
  end
  def baz
    'baz!!'
  end
end

def test_extend_Integer
  a = 1
  assert_equal 'foo', a.foo
  assert_equal 'bar', a.bar
  assert_equal 'baz!!', a.baz
end
```

Preview 3からはBignumやFixnumを参照すると警告が出るようになっています。

```
irb(main):001:0> Fixnum
(irb):1: warning: constant ::Fixnum is deprecated
=> Integer
irb(main):002:0> Bignum
(irb):2: warning: constant ::Bignum is deprecated
=> Integer
```

参考: [Feature \#12005: Unify Fixnum and Bignum into Integer](https://bugs.ruby-lang.org/issues/12005)

### Enumerableモジュールにsumが追加された

Ruby 2.4ではEnumerableモジュールに`sum`メソッドが追加されました。
これにより、配列の中身の合計値を求めたりできます。

```ruby
[1, 2, 3, 4].sum
#=> 10
```

デフォルトの初期値は0ですが、引数で変更できます。

```ruby
[].sum
#=> 0

[1, 2, 3, 4].sum(5)
#=> 15
```

小数の合計値は丸め誤差が発生するので注意してください。 

```ruby
[0.1, 0.1, 0.1].sum
#=> 0.30000000000000004
```

ただし、`sum`メソッドでは内部的に[カハンの加算アルゴリズム](https://ja.wikipedia.org/wiki/%E3%82%AB%E3%83%8F%E3%83%B3%E3%81%AE%E5%8A%A0%E7%AE%97%E3%82%A2%E3%83%AB%E3%82%B4%E3%83%AA%E3%82%BA%E3%83%A0)が使われているため、`+`演算子で加算したときよりも高速で誤差が少なくなりやすいです。

```rb
# 要素が3の場合、誤差は同じ
[0.1, 0.1, 0.1].sum
#=> 0.30000000000000004
0.1 + 0.1 + 0.1
#=> 0.30000000000000004

# 要素が6の場合は+の方が誤差がない
[0.1, 0.1, 0.1, 0.1, 0.1, 0.1].sum
#=> 0.6000000000000001
0.1 + 0.1 + 0.1 + 0.1 + 0.1 + 0.1
#=> 0.6

# 要素が10の場合はsumの方が誤差がない
array = Array.new(10, 0.1)
array.sum
#=> 1.0
# 素直に+で書くとコードが長くなるのでinjectで代用
array.inject(:+)
#=> 0.9999999999999999

# 要素が99999の場合はsumの方が誤差が小さい
array = Array.new(99999, 0.1)
array.sum
#=> 9999.900000000001
array.inject(:+)
#=> 9999.900000018848
```

文字列の配列を連結する場合は初期値に何らかの文字列を渡します。

```ruby
['foo', 'bar'].sum('')
#=> 'foobar'

['foo', 'bar'].sum('>>')
#=> '>>foobar'
```

配列の配列を連結することも可能です。

```ruby
[[1, 2], [3, 1, 5]].sum([])
#=> [1, 2, 3, 1, 5]
```

Ruby単体で見たときは後方互換性の問題は生じませんが、ActiveSupport等のサードパーティgemでsumが実装されていた場合は完全に互換性があるとは限らないので注意が必要、とのことです。

参考: [Feature \#12217: Introducing Enumerable\#sum for precision compensated summation and revert r54237](https://bugs.ruby-lang.org/issues/12217)

### 非ASCII文字でも小文字化、大文字化されるようになった

Ruby 2.4では非ASCII文字（たとえばウムラウトが含まれるような文字）でも小文字化、大文字化されるようになりました。

```ruby
def test_non_ascii_upcase_downcase_swapcase_capitalize
  assert_equal 'TÜRKIYE', 'Türkiye'.upcase
  assert_equal 'türkiye', 'Türkiye'.downcase
  assert_equal 'tÜRKIYE', 'Türkiye'.swapcase
  assert_equal 'Ürkiye', 'ürkiye'.capitalize

  # 破壊的メソッドも同じ挙動になる
  a = 'Türkiye'
  a.upcase!
  assert_equal 'TÜRKIYE', a
end
```

この変更が望ましくない場合（Ruby 2.3と同じようにASCII文字だけを変換したい場合）は`:ascii`オプションを付けてください。

```ruby
'Türkiye'.upcase(:ascii)
#=> "TüRKIYE"
```

参考: [Feature \#10085: Add non\-ASCII case conversion to String\#upcase/downcase/swapcase/capitalize](https://bugs.ruby-lang.org/issues/10085)

### Symbol#matchがMatchDataを返すようになった（不具合修正）

Ruby 2.3までは`Symbol#match`はマッチした位置を返す不具合がありました。

```ruby
:"".match(//) 
# => 0
```

Ruby 2.4では`String#match`と同じようにMatchDataオブジェクトを返します。

```ruby
m = :"".match(//) 
#=> #<MatchData "">
m[0]
#=> ""
```

参考: [Bug \#11991: \`Symbol\#match\` returns the match position, unlike \`String\#match\` and \`Regexp\#match\`](https://bugs.ruby-lang.org/issues/11991)

### DateTimeとTimeのto_timeでタイムゾーンを保持するようになった（不具合修正）

Ruby 2.4ではDateTimeクラスとTimeクラスの`to_time`メソッドを呼びだした際、元のオブジェクトのタイムゾーンを保持するようになりました。

```ruby
def test_date_time_to_time
  require 'date'
  cet_date_time = DateTime.strptime('2015-11-12 CET', '%Y-%m-%d %Z')
  assert_equal '2015-11-12 00:00:00 +0100', cet_date_time.to_time.to_s
end

def test_time_to_time
  cet_time = Time.new(2005, 2, 21, 10, 11, 12, '+01:00')
  assert_equal '2005-02-21 10:11:12 +0100', cet_time.to_time.to_s
end
```

Ruby 2.3以前では`to_time`メソッドを呼び出すと、実行環境のタイムゾーンに変わる不具合がありました。

```ruby
# 実行環境がJSTだと、to_timeの結果もJSTになる（Ruby 2.3以前）
DateTime.strptime('2015-11-12 CET', '%Y-%m-%d %Z').to_time.to_s
#=> "2015-11-12 08:00:00 +0900"

Time.new(2005, 2, 21, 10, 11, 12, '+01:00').to_time.to_s
#=> "2005-02-21 18:11:12 +0900"
```

参考: [Bug \#12189: DateTime\#to\_time removes timezone information]
(https://bugs.ruby-lang.org/issues/12189)

参考: [Bug \#12271: \`Time\#to\_time\` removes timezone information](https://bugs.ruby-lang.org/issues/12271)

### Ruby/Tkが標準ライブラリから外された

Ruby 2.4ではRuby/Tkが標準ライブラリから外されて外部gemになりました。

https://github.com/ruby/tk

Rubyインストール時には一緒にインストールされないため、必要であれば別途インストールする必要があります。

参考: [Feature \#8539: Unbundle ext/tk](https://bugs.ruby-lang.org/issues/8539)

## パフォーマンス改善

### Arrayクラス独自のmaxとminが実装され高速化した

Ruby 2.4ではArrayクラス独自の`max`メソッドと`min`メソッドが実装され高速化しました。
Ruby 2.3までは`Enumerable`モジュールの`max`メソッドと`min`メソッドが使われていました。

下記サイトの計測結果によると、1.61倍ほど高速化したとのことです。

[New Features in Ruby 2\.4 \- BlockScore Blog](https://blog.blockscore.com/new-features-in-ruby-2-4/)

```ruby
require 'benchmark/ips'

Benchmark.ips do |bench|
  NUMS = 1_000_000.times.map { rand }

  # By binding the Enumerable method to our array
  # we can bench the previous speed in Ruby 2.3
  ENUM_MIN  = Enumerable.instance_method(:min).bind(NUMS)

  # Bind the `#min` method to our test array also
  # so our benchmark code is as similar as possible
  ARRAY_MIN = Array.instance_method(:min).bind(NUMS)

  bench.report('Array#min') do
    ARRAY_MIN.call
  end

  bench.report('Enumerable#min') do
    ENUM_MIN.call
  end

  bench.compare!
end

# >> Warming up --------------------------------------
# >>            Array#min     3.000  i/100ms
# >>       Enumerable#min     2.000  i/100ms
# >> Calculating -------------------------------------
# >>            Array#min     35.147  (± 2.8%) i/s -    177.000  in   5.039133s
# >>       Enumerable#min     21.839  (± 4.6%) i/s -    110.000  in   5.040531s
# >> Comparison:
# >>            Array#min:       35.1 i/s
# >>       Enumerable#min:       21.8 i/s - 1.61x slower
```

参考: [Feature \#12172: Array\#max and Array\#min](https://bugs.ruby-lang.org/issues/12172)

## オブジェクト全般の新機能

### 指定された範囲内の値を返すようにするComparable#clamp

Ruby 2.4では`clamp`メソッドを使って、指定した範囲の値を返すことができます。

「指定した範囲の値を返す」というのは厳密に言うと、下限値と上限値を引数で指定し、レシーバの値が、

- その範囲内であればレシーバを返す
- 下限を超えていれば下限値を返す
- 上限を超えていれば上限値を返す

というものです。

```ruby
def test_comparable_clamp
  # 下限が1、上限が3で2が渡された場合 => 範囲内なのでそのまま2を返す
  assert_equal 2, 2.clamp(1, 3)
  # 下限が1、上限が3で0が渡された場合 => 下限値の1を返す
  assert_equal 1, 0.clamp(1, 3)
  # 下限が-2、上限が-1で0が渡された場合 => 上限値の-1を返す
  assert_equal -1, 0.clamp(-2, -1)
  # 下限が'a'、上限が'b'で'c'が渡された場合 => 上限値の'b'を返す
  assert_equal 'b', 'c'.clamp('a', 'b')
end
```

`clamp`メソッドに渡す引数は必ず下限値、上限値の順で渡す必要があります。逆で渡すとエラーになります。

```ruby
2.clamp(3, 1)
#=> ArgumentError: min argument must be smaller than max argument
```

なお、clampというメソッド名は電子回路で使われる「クランプ回路」に由来するようです。

[過電流や短絡電流の対策に使える電流クランプ回路 \- EDN Japan](http://ednjapan.com/edn/articles/1208/28/news061.html)

参考: [Feature \#10594: Comparable\#clamp](https://bugs.ruby-lang.org/issues/10594)

### freeze状態までcloneするかどうかをオプションで指定できるようになった

freezeしたオブジェクトをcloneした場合、Ruby 2.3まではcloneしたオブジェクトまでfreezeされていましたが、Ruby 2.4ではfreezeするかどうかを`freeze`オプションで指定できます。

```ruby
def test_clone_with_freeze_keyword_option
  a = 'A'.freeze
  assert a.frozen?
  # デフォルトではfreeze状態もcloneされる
  assert a.clone.frozen?
  # cloneはするがfreezeはしない
  refute a.clone(freeze: false).frozen?
end
```

参考: [Feature \#12300: Allow Object\#clone to take freeze: false keyword argument to not freeze the clone](https://bugs.ruby-lang.org/issues/12300)

## 主に数値に関連する新機能

### 切り上げ、切り下げ、切り捨てで小数点の位置を指定できるようになった

Ruby 2.4では`ceil`、`floor`、`truncate`の各メソッドに引数を渡して、切り上げ、切り下げ、切り捨てを行う小数点の位置を指定できるようになりました。

```ruby
def test_ceil_floor_truncate
  # 切り上げ
  assert_equal 2, 1.11.ceil
  assert_equal -1, -1.11.ceil
  assert_equal 1.2, 1.11.ceil(1)
  assert_equal -1.1, -1.11.ceil(1)
  # 負の値も指定可能
  assert_equal 11120, 11111.ceil(-1)
  assert_equal -11110, -11111.ceil(-1)

  # 切り下げ
  assert_equal 1, 1.99.floor
  assert_equal -2, -1.99.floor
  assert_equal 1.9, 1.99.floor(1)
  assert_equal -2, -1.99.floor(1)
  # 負の値も指定可能
  assert_equal 11110, 11119.floor(-1)
  assert_equal -11120, -11119.floor(-1)

  # 切り捨て（0から自身までの数値で、自身にもっとも近い整数または小数を返す）
  assert_equal 1, 1.99.truncate
  assert_equal -1, -1.99.truncate
  assert_equal 1.9, 1.99.truncate(1)
  assert_equal -1.9, -1.99.truncate(1)
  # 負の値も指定可能
  assert_equal 190, 199.truncate(-1)
  assert_equal -190, -199.truncate(-1)
end
```

参考: [Feature \#12245: optional parameter ndigits to Integer\#floor, Integer\#ceil, Float\#floor, Float\#ceil](https://bugs.ruby-lang.org/issues/12245)

### 整数を1桁ずつ配列に入れて返すInteger#digits

Ruby 2.4では整数を1桁ずつ配列に入れて返す`Integer#digits`メソッドが追加されました。値は1桁目、2桁目・・・の順番で格納されます。

```ruby
123.digits
#=> [3, 2, 1]
```

基数を指定するとn進数の1桁目、2桁目・・・と値を格納することもできます。

```ruby
0x7b.digits(16)
#=> [11, 7]

123.digits(16)
#=> [11, 7]
```

マイナスの値に対して`digits`を呼び出すとエラーになります。

```ruby
-123.digits
#=> Math::DomainError: out of domain
```

参考: [Feature \#12447: Integer\#digits for extracting digits of place\-value notation in any base](https://bugs.ruby-lang.org/issues/12447)

## 主に文字列・正規表現に関連する新機能

### 正規表現に文字列がマッチしたかどうかだけを返すRegexp#match?、String#match?、Symbol#match?

Ruby 2.4では正規表現に文字列がマッチしたかどうかだけを返す`Regexp#match?`メソッドが追加されました。
このメソッドは`match`メソッドよりも高速で、なおかつグローバル変数の`$~`を変更しない、という特徴もあります。

```ruby
def test_regexp_match?
  # match?メソッドはグローバル変数の$~を変更しない
  assert /\d+-\d+-\d+/.match?('2016-09-01')
  assert_nil $~

  # 以下のメソッドは$~を変更する
  assert /\d+-\d+-\d+/.match('2016-09-01')
  assert_equal '2016-09-01', $~[0]

  assert /\d+-\d+-\d+/ =~ '2016-09-02'
  assert_equal '2016-09-02', $~[0]

  assert /\d+-\d+-\d+/ === '2016-09-03'
  assert_equal '2016-09-03', $~[0]
end
```

以下の記事では`match?`メソッドと既存のメソッドの速度比較が載っています。
たとえば`match`メソッドに比べると`match?`メソッドは4.88倍速いそうです。

[New Features in Ruby 2\.4 \- BlockScore Blog](https://blog.blockscore.com/new-features-in-ruby-2-4/)

~~なお、`match`メソッドとは異なり、Stringクラスには`match?`メソッドは定義されていません。~~
rc1にてStringクラス、Symbolクラスにも`match?`メソッドが実装されました。

```rb
'2016-09-01'.match?(/\d+-\d+-\d+/)
#=> true

:hello_world.match?(/\w+_\w+/)
#=> true
```

参考: [Feature \#8110: Regex methods not changing global variables](https://bugs.ruby-lang.org/issues/8110)

### 名前付きキャプチャをハッシュとして返すMatchData#named_captures

Ruby 2.4では正規表現にマッチした名前付きキャプチャをハッシュとして返す`MatchData#named_captures`メソッドが追加されました。
ハッシュのキーはシンボルではなく文字列になります。

```ruby
def test_match_data_named_captures
  m = /(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/.match('2016-09-01')
  assert_equal(
      {'year' => '2016', 'month' => '09', 'day' => '01'},
      m.named_captures
  )
end
```

参考: [Feature \#11999: MatchData\#to\_h to get a Hash from named captures](https://bugs.ruby-lang.org/issues/11999)

### MatchData#values_atで名前付きキャプチャの名前を指定できるようになった

Ruby 2.3までは`MatchData#values_at`メソッドで指定できるのは、キャプチャした文字列のインデックスだけでしたが、Ruby 2.4では名前付きキャプチャの名前を指定できるようになりました。
名前は文字列、またはシンボルで指定できます。

```ruby
def test_match_data_values_at
  m = /(?<year>\d+)-(?<month>\d+)-(?<day>\d+)/.match('2016-09-01')
  assert_equal(['2016', '01'], m.values_at(1, 3))
  # Ruby 2.4からは文字列またはシンボルで名前を指定可能
  assert_equal(['2016', '01'], m.values_at('year', 'day'))
  assert_equal(['2016', '01'], m.values_at(:year, :day))
end
```

参考: [Feature \#9179: MatchData\#values\_at should support named capture](https://bugs.ruby-lang.org/issues/9179)

### Stringのcapacityを指定できるようになった

Ruby 2.4では`String.new`に`capacity`オプションを渡せるようになりました。
長い文字列を追加することが事前に分かっている場合は、このオプションを指定することでパフォーマンスを向上させることができます。

以下のサイトの計測結果によると、`capacity`を指定すると2.32倍速くなったそうです。

[New Features in Ruby 2\.4 \- BlockScore Blog](https://blog.blockscore.com/new-features-in-ruby-2-4/)

```ruby
require 'benchmark/ips'

Benchmark.ips do |bench|
  bench.report("Without capacity") do
    append_me = ' ' * 1_000
    template  = String.new

    100.times { template << append_me }
  end

  bench.report("With capacity") do
    append_me = ' ' * 1_000
    template  = String.new(capacity: 100_000)

    100.times { template << append_me }
  end

  bench.compare!
end

# >> Warming up --------------------------------------
# >>     Without capacity     1.690k i/100ms
# >>        With capacity     3.204k i/100ms
# >> Calculating -------------------------------------
# >>     Without capacity     16.031k (± 7.4%) i/s -    160.550k in  10.070740s
# >>        With capacity     37.225k (±18.0%) i/s -    362.052k in  10.005530s
# >>
# >> Comparison:
# >>        With capacity:    37225.1 i/s
# >>     Without capacity:    16031.3 i/s - 2.32x slower
```

ちなみに、`capacity`を指定してもそれ以上文字が追加できなくなるわけではありません。

```ruby
def test_string_with_capacity
  a = String.new(capacity: 1)
  a << '12345'
  assert_equal '12345', a
end
```

参考: [Feature \#12024: Add String\.buffer, for creating strings with large capacities](https://bugs.ruby-lang.org/issues/12024)

### StringクラスとRegexpクラスのUnicodeバージョンが9.0にアップデート

Ruby 2.4ではStringクラスとRegexpクラスのUnicodeバージョンが8.0から9.0にアップデートされました。

これにより、たとえば正規表現で[西夏文字](https://ja.wikipedia.org/wiki/%E8%A5%BF%E5%A4%8F%E6%96%87%E5%AD%97)（せいかもじ）を表す`Tangut`プロパティ（Unicode 9.0で追加されたプロパティ）を使えたりするようになります。

```ruby
def test_unicode_9_0
  assert "A\u{17000}B".match(/\p{Tangut}/)
end
```

ちなみに`\u{17000}`というのはこんな文字です。（大半のブラウザでは表示できないはずです）

![u17000@5.50px.png](https://qiita-image-store.s3.amazonaws.com/0/7465/56045679-ef16-f261-7092-dd2f98352159.png "u17000@5.50px.png")
[u17000 \- GlyphWiki](http://glyphwiki.org/wiki/u17000)

参考: [Feature \#12513: Update Unicode data to Unicode Version 9\.0](https://bugs.ruby-lang.org/issues/12513)

## 主に配列・ハッシュに関連する新機能

### 指定した条件で重複をなくすEnumerable#uniq

Ruby 2.4ではEnumerableモジュールに`uniq`メソッドが追加されたため、配列だけでなく、ハッシュでも`uniq`が使えるようになります。
重複をなくす条件はブロックで指定します。

```ruby
def test_enumerable_uniq
  # 値に'Athens'が2回登場している
  olimpics = {
      1896 => 'Athens',
      1900 => 'Paris',
      1904 => 'Chikago',
      1906 => 'Athens',
      1908 => 'Rome'
  }
  # 同じ値なら重複していると見なす
  each_city_first_time = olimpics.uniq { |k, v| v }
  # 1896の'Athens'だけが残る
  assert_equal [
      [1896, "Athens"],
      [1900, "Paris"],
      [1904, "Chikago"],
      [1908, "Rome"]
  ], each_city_first_time
end
```

配列とは異なり、破壊的メソッドの`uniq!`は用意されません。

```ruby
olimpics.uniq! { |k, v| v }
#=> undefined method `uniq!' for #<Hash:0x007fa9f505bb48>
```

`Enumerator::Lazy`でも`uniq`が使えます。

```ruby
# 2乗して10で割った余りを重複の判定条件にする
(1..Float::INFINITY).lazy.uniq { |x| (x**2) % 10 }.first(6)
#=> [1, 2, 3, 4, 5, 10]
```

参考: [Feature \#11090: Enumerable\#each\_uniq and \#each\_uniq\_by](https://bugs.ruby-lang.org/issues/11090)

### ハッシュの値を特定のルールで変更するHash#transform_values

Ruby 2.4ではHashクラスに`transform_values`メソッドが追加され、ハッシュの値を特定のルールで変更できるようになりました。
破壊的な変更を行う`transform_values!`メソッドもあります。

```ruby
def test_hash_transform_values
  x = {a: 1, b: 2, c: 3}
  # ハッシュの値を2乗する
  y = x.transform_values {|v| v ** 2 }
  assert_equal({a: 1, b: 4, c: 9}, y)
  # 変数xの中身は変わっていない
  assert_equal({a: 1, b: 2, c: 3}, x)

  # 破壊的メソッドを適用すると変数xの中身が変わる
  x.transform_values! {|v| v ** 2 }
  assert_equal({a: 1, b: 4, c: 9}, x)
end
```

ちなみにこのメソッドはRuby on Rails（ActiveSupport）の同名メソッドをRuby本体にインポートしたものです。

http://api.rubyonrails.org/classes/Hash.html#method-i-transform_values

参考: [Feature \#12512: Import Hash\#transform\_values and its destructive version from ActiveSupport](https://bugs.ruby-lang.org/issues/12512)

## ディレクトリやファイルに関連する新機能

### ディレクトリが空かどうかを判定するDir.empty?

Ruby 2.4では`Dir.empty?`というメソッドが追加されました。
その名の通り、ディレクトリが空であればtrueを返すメソッドです。

```ruby
def test_dir_empty?
  Dir.mktmpdir do |dir|
    assert Dir.empty?(dir)
  end
end
```

参考: [Feature \#10121: Dir\.empty?](https://bugs.ruby-lang.org/issues/10121)

### ファイルが空かどうかを判定するFile.empty?

Ruby 2.4では`File.zero?`のエイリアスメソッドとして、`File.empty?`が追加されました。
その名の通り、ファイルが空であればtrueを返すメソッドです。

```ruby
def test_file_empty?
  require 'tempfile'
  Tempfile.create("foo") do |f|
    assert File.zero?(f)
    # empty?はzero?のエイリアスメソッド
    assert File.empty?(f)
  end
end
```

参考: [Feature \#9969: Add File\.empty? as alias to File\.zero?](https://bugs.ruby-lang.org/issues/9969)

## その他の標準ライブラリに関連する新機能

### CSVデータでダブルクオートの不正なフォーマットを許容するliberal_parsingオプション

Ruby 2.4ではCSVデータをパースする際に、ダブルクオートを使った不正なフォーマットのCSVデータを許容する`liberal_parsing`オプションが追加されました。

ダブルクオートを使った不正なフォーマットのCSVデータというのは、たとえばこんな文字列のことです。

```
"Johnson, Dwayne",Dwayne "The Rock" Johnson
```

本来であれば以下のような文字列にするのが正です。
（値にダブルクオートが含まれる場合は、ダブルクオートで囲んだ上で、ダブルクオートを重ねる）

```
"Johnson, Dwayne","Dwayne ""The Rock"" Johnson"
```

以下は不正なCSVデータを`liberal_parsing`オプションを付けてパースするコード例です。

```ruby
def test_csv_liberal_parsing_option
  require 'csv'
  
  # ダブルクオートを使った不正なフォーマットのCSVデータを用意する
  input = '"Johnson, Dwayne",Dwayne "The Rock" Johnson'
  
  # liberal_parsingオプションなしだとエラー
  assert_raises(CSV::MalformedCSVError) { CSV.parse_line(input) }
  
  # liberal_parsingオプションありだとパース可能
  assert_equal(
      ['Johnson, Dwayne', 'Dwayne "The Rock" Johnson'],
      CSV.parse_line(input, liberal_parsing: true)
  )
end
```

参考: [Feature \#11839: CSV: liberal\_parsing option](https://bugs.ruby-lang.org/issues/11839)

### Logger.newのキーワード引数追加

Ruby 2.4では`Logger.new`のキーワード引数として`level`、`progname`、`formatter`、`datetime_format`が追加されました。

```ruby
def test_logger_options
  require 'logger'
  formatter = proc { |severity, timestamp, progname, msg| "#{severity}:#{msg}\n\n" }
  logger = Logger.new(
      STDERR,
      level: :info,
      progname: :progname,
      formatter: formatter,
      datetime_format: "%d%b%Y@%H:%M:%S"
  )
  assert_equal Logger::INFO, logger.level
  assert_equal :progname, logger.progname
  assert_equal formatter, logger.formatter
  assert_equal "%d%b%Y@%H:%M:%S", logger.datetime_format
end
```

ちなみに、Ruby 2.3以前ではインスタンス作成後にプロパティをセットする必要がありました。

```ruby
logger = Logger.new($stdout)
logger.level = :info
```

参考: [Feature \#12224: logger: Allow specifying log level in constructor](https://bugs.ruby-lang.org/issues/12224)

### ログのローテーションファイルの拡張子書式を指定できるようになった

Ruby 2.3以前では、ログのローテーションファイルの拡張子書式は"%Y%m%d"固定になっていましたが、Ruby 2.4では`shift_period_suffix`オプションを使って書式を指定できるようになりました。

```ruby
require 'logger'
# development.log.2016-09-01
# development.log.2016-09-01.1
# development.log.2016-09-01.2
# のような名前のログファイルを出力する
logger = Logger.new('development.log', shift_period_suffix: '%Y-%m-%d')
```

参考: [Feature \#10772: Add ability to change rotated log file extention](https://bugs.ruby-lang.org/issues/10772)

### optparseで起動時引数をハッシュに格納するintoオプション

optparseで起動時引数をパースする際、`into`オプションを使ってパースした結果をハッシュに格納できるようになりました。

```ruby
def test_optparse_into_option
  require 'optparse'
  require 'optparse/date'
  require 'optparse/uri'

  cli =
      OptionParser.new do |options|
        options.define '--from=DATE',    Date
        options.define '--url=ENDPOINT', URI
        options.define '--names=LIST',   Array
      end

  config = {}

  args = %w[
    --from  2016-02-03
    --url   https://blog.blockscore.com/
    --names John,Daniel,Delmer
  ]

  # パースした結果を変数configに格納する
  cli.parse(args, into: config)

  assert_equal(
      {
          from: Date.parse('2016-02-03'),
          url: URI('https://blog.blockscore.com/'),
          names: %w(John Daniel Delmer)
      },
      config
  )
end
```

上記のコードは以下の記事を参考にしました。

[New Features in Ruby 2\.4 \- BlockScore Blog](https://blog.blockscore.com/new-features-in-ruby-2-4/)

参考: [Feature \#11191: Add \#to\_h method to OptionParser](https://bugs.ruby-lang.org/issues/11191)

### OpenSSLが外部gemとして切り出された

Ruby 2.4ではOpenSSLが標準ライブラリではなく、外部のgemになりました。

https://github.com/ruby/openssl

ただし、Rubyインストール時にOpenSSL gemも一緒にインストールされるため、通常の使用では特に変化はありません。

また、機能面ではOpenSSL 1.1.0をサポートするようになったとのことです。

参考: [Feature \#9612: Gemify OpenSSL](https://bugs.ruby-lang.org/issues/9612)

## スレッドに関連する新機能

### 別スレッドの例外を報告するかどうかを決めるThread.report_on_exceptionフラグ

Ruby 2.4では別スレッドで発生した例外を報告するかどうかを決める`Thread.report_on_exception`フラグが追加されました。
このフラグをtrueにすると、別スレッドで例外が発生した場合にその内容が報告されます。

```ruby
def without_flag
  puts 'Starting some parallel work'
  thread =
      Thread.new do
        sleep 0.1
        fail 'something very bad happened!'
      end
  sleep 0.2
  puts 'Done!'
end
# フラグ無しで実行すると、エラーが報告されない
without_flag
#=> Starting some parallel work
#=> Done!

def with_flag
  # フラグにtrueをセットする
  Thread.report_on_exception = true
  
  puts 'Starting some parallel work'
  thread =
      Thread.new do
        sleep 0.1
        fail 'something very bad happened!'
      end
  sleep 0.2
  puts 'Done!'
end
# フラグありで実行すると、エラーが報告される
with_flag
#=> Starting some parallel work
#=> #<Thread:0x007fe1731faf20@(irb):66 run> terminated with exception:
#=> (irb):68:in `block in bar': something very bad happened! (RuntimeError)
#=> Done!
```

上記のコードは以下の記事を参考にしました。

[New Features in Ruby 2\.4 \- BlockScore Blog](https://blog.blockscore.com/new-features-in-ruby-2-4/)

参考: [Feature \#6647: Exceptions raised in threads should be logged](https://bugs.ruby-lang.org/issues/6647)

### デッドロック発生時に全スレッドのバックトレースが出力されるようになった

Ruby 2.4ではデッドロック発生時に全スレッドのバックトレースが出力されるようになりました。
以下はデッドロックが発生するコードの例です。

```ruby
Thread.current.name = "MainThread!"
z = Thread.new{Thread.stop}
a, b = Thread.new { 1 until b; b.join }, Thread.new { 1 until a; a.join }
a.name = "aaaaa"
b.name = "bbbbb"
z.name = "zzzz"
a.join
```

上記のコードをRuby 2.4で実行すると、以下のように全スレッドのバックトレースが出力されます。

```
fatal: No live threads left. Deadlock?
4 threads, 4 sleeps current:0x007fd8c1c06b30 main thread:0x007fd8c1c06b30
* #<Thread:0x007fd8c207cce8@MainThread! sleep_forever>
   rb_thread_t:0x007fd8c1c06b30 native:0x007fff776aa000 int:0
   (irb):7:in `join'
   (irb):7:in `irb_binding'
   /Users/jit/.rbenv/versions/2.4.0-preview2/lib/ruby/2.4.0/irb/workspace.rb:87:in `eval'
   /Users/jit/.rbenv/versions/2.4.0-preview2/lib/ruby/2.4.0/irb/workspace.rb:87:in `evaluate'
   (省略)
* #<Thread:0x007fd8c3843070@zzzz@(irb):2 sleep_forever>
   rb_thread_t:0x007fd8c3029b10 native:0x00700000108000 int:0
   (irb):2:in `stop'
   (irb):2:in `block in irb_binding'
* #<Thread:0x007fd8c284a7e0@aaaaa@(irb):3 sleep_forever>
   rb_thread_t:0x007fd8c1f34170 native:0x0070000020b000 int:0
    depended by: tb_thread_id:0x007fd8c1c06b30
    depended by: tb_thread_id:0x007fd8c1de2060
   (irb):3:in `join'
   (irb):3:in `block in irb_binding'
* #<Thread:0x007fd8c284a6f0@bbbbb@(irb):3 sleep_forever>
   rb_thread_t:0x007fd8c1de2060 native:0x0070000030e000 int:0
    depended by: tb_thread_id:0x007fd8c1f34170
   (irb):3:in `join'
   (irb):3:in `block in irb_binding'

	from (irb):7:in `join'
	from (irb):7
	from /Users/jit/.rbenv/versions/2.4.0-preview2/bin/irb:11:in `<main>'
```

一方、Ruby 2.3で表示されるメッセージはこれだけでした。

```
fatal: No live threads left. Deadlock?
	from (irb):19:in `join'
	from (irb):19
	from /Users/jit/.rbenv/versions/2.3.1/bin/irb:11:in `<main>'
```

参考: [Feature \#8214: デッドロックチェックに全スレッドのバックトレースダンプの追加](https://bugs.ruby-lang.org/issues/8214)

Preview 2の主な変更点は以上です。続いてPreview 3の変更点を載せていきます。

----

## 文法上の変更点（これ以降はPreview 3とrc1）

### rescue修飾子付きのコードを ( ) で囲んでメソッドの引数として渡せるようになった

以下のようなコードはこれまで構文エラーになっていましたが、Ruby 2.4では実行可能になりました。

```ruby
# rescue修飾子付きのコードを ( ) で囲んでメソッドの引数として渡す
p (nil.split rescue nil)
#=> nil
```

なお、Ruby 2.3以前では`SyntaxError: syntax error, unexpected modifier_rescue, expecting ')'`というエラーが出て実行できませんでした。

参考： [Feature \#12686: Allowing a postposed rescue in a method argument](https://bugs.ruby-lang.org/issues/12686)

## 後方互換性が失われる変更点

### 
roundメソッドがデフォルトで偶数丸めをするようになった
 => halfオプションの追加のみになりました（rc1）


roundメソッドで四捨五入する際のルールが「偶数丸め」になりました。


preview3の時点では偶数丸めがデフォルトになっていましたが、その後の議論によりrc1ではデフォルトの挙動は従来通り（つまり0.5なら切り上げ）になりました。

[Bug \#12958: Breaking change in how \`\#round\` works \- Ruby trunk \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/12958)

結果として、Ruby 2.4では`round`メソッドに`half: :up`と`half: :even`のオプションが追加されただけになります。
というわけで以下はrc1の仕様に準拠した解説です。

`round`メソッドに`half`オプションが追加されました。
`half`オプションには`:up`と`:even`の2種類の値が渡せます。

- `:up` = 0.5は必ず切り上げ（デフォルト、2.3以前と同等）
- `:even` = 偶数丸め（2.4から登場した新しい仕様）

偶数丸めは「最近接丸め」「JIS丸め」「ISO丸め」とも呼ばれ、「端数がちょうど0.5なら切り捨てと切り上げのうち結果が偶数となる方へ丸める」という端数処理の考え方です。

参考： [端数処理 \- Wikipedia](https://ja.wikipedia.org/wiki/%E7%AB%AF%E6%95%B0%E5%87%A6%E7%90%86#.E6.9C.80.E8.BF.91.E6.8E.A5.E5.81.B6.E6.95.B0.E3.81.B8.E3.81.AE.E4.B8.B8.E3.82.81)

以下はデフォルトの`round`メソッドの結果と、`half: :even`を指定した場合の挙動の違いです。

```ruby
# デフォルト（0.5は必ず切り上げ）
12.5.round
# => 13
13.5.round
#=> 14

# 偶数丸め
12.5.round(half: :even)
#=> 12
13.5.round(half: :even)
#=> 14
```

なお、IntegerやRationalも`round`メソッドで`half`オプションを渡すことができます。

```ruby
# Ruby 2.4
125.round(-1, half: :even)
#=> 120
135.round(-1, half: :even)
#=> 140

(125r / 10).round(half: :even)
#=> 12
(135r / 10).round(half: :even)
#=> 14
```

参考：[Bug \#12548: Rounding modes inconsistency between round versus sprintf](https://bugs.ruby-lang.org/issues/12548)

参考：[Bug \#12958: Breaking change in how \`\#round\` works \- Ruby trunk \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/12958)

## パフォーマンス改善

### ハッシュテーブルの高速化

[こちらのアナウンス](https://www.ruby-lang.org/ja/news/2016/11/09/ruby-2-4-0-preview3-released/)によると「従来Rubyのハッシュテーブルの内部実装 (st_table) ではに双方向連結リストとチェイン法を用いた実装が使われていましたが、挿入順の配列にオープンアドレス法を用いることによる高速化が行われました」とのことです。

以下のIssueではかなり専門的な議論が繰り広げられているので、興味がある方はチェックしてみてください。

参考： [Feature \#12142: Hash tables with open addressing](https://bugs.ruby-lang.org/issues/12142)

### インスタンス変数へのアクセスの高速化

内部実装の改善により、インスタンス変数のアクセスが高速化したそうです。
以下のIssueでは10%ほど速くなった、というような話が載っています

参考： [Bug \#12274: accessing to instance variable should be fast\.](https://bugs.ruby-lang.org/issues/12274)

## irbの変更点

### プログラムの実行中にirbが開けるbinding.irbメソッドの追加

Pryを使っていると`binding.pry`というコマンドをコードに埋め込むことで、プログラムの実行中にPryを開くことができます。

これと同じように、irbでも`binding.irb`というコマンドでirbが開けるようになりました。

```ruby
# irbをrequireする
require 'irb'

s = 'hello'
puts s

# ここでirbを開く
binding.irb

puts 'bye'
```

上のコードを実行すると、次のように途中でirbが起動します。

![Screen Shot 2016-11-16 at 08.37.18.png](https://qiita-image-store.s3.amazonaws.com/0/7465/2e89060f-687c-929c-bc6b-01620869db9c.png "Screen Shot 2016-11-16 at 08.37.18.png")

参考：[Revision 56624 \- irb\.rb: Binding\#irb \* lib/irb\.rb \(Binding\#irb\): new method like Binding\#pry\.](https://bugs.ruby-lang.org/projects/ruby-trunk/repository/revisions/56624)

## 数値に関する新機能

### infinite?/finite?メソッドがFloat以外の数値クラスにも追加された

値が無限大かどうか（+∞ or -∞）をチェックするための`infinite?`メソッド（逆は`finite?`メソッド）が、Floatメソッドだけでなく、Integerクラス、Rationalクラス、Complexクラスにも追加されました。

```ruby
def test_finite_infinite
  assert 1.finite?
  refute 1.infinite?

  assert 1.0.finite?
  refute 1.0.infinite?

  assert 1r.finite?
  refute 1r.infinite?

  assert Complex(1).finite?
  refute Complex(1).infinite?
end
```

参考：[Feature \#12039: Fixnum\#infinite?/Bignum\#infinite or Numeric\#infinte, consistent with Float\#infinite? and BigDecimal\#infinite?](https://bugs.ruby-lang.org/issues/12039)

## 配列・ハッシュ・Setに関連する新機能

### concatメソッドが複数の配列を引数に取れるようになった

配列の`concat`メソッドが複数の配列を引数に取れるようになりました。

```ruby
def test_array_concat
  assert [1, 2, 3, 4, 5, 6], [1, 2].concat([3, 4], [5, 6])
end
```

参考：[Feature \#12333: \`String\#concat\`, \`Array\#concat\`, \`String\#prepend\` to take multiple arguments](https://bugs.ruby-lang.org/issues/12333)

### ハッシュに`compact`/`compact!`メソッドが追加された

値が`nil`の要素を削除する`compact`メソッドがハッシュに追加されました。
破壊的に変更する`compact!`メソッドも用意されています。

```ruby
def test_hash_compact
  hash = { a: 1, b: nil, c: 2 }
  
  assert_equal({ a: 1, c: 2 }, hash.compact)
  assert_equal({ a: 1, b: nil, c: 2 }, hash)
  
  assert_equal({ a: 1, c: 2 }, hash.compact!)
  assert_equal({ a: 1, c: 2 }, hash)
end
```

ちなみに、これはActiveSupportで提供されている`Hash#compact`メソッドと同等です。

http://api.rubyonrails.org/classes/Hash.html#method-i-compact

参考：[Feature \#12333: \`String\#concat\`, \`Array\#concat\`, \`String\#prepend\` to take multiple arguments](https://bugs.ruby-lang.org/issues/12333)

### ブロック無しのchunkがEnumeratorを返すようになった

`Enumerable`モジュールの`chunk`メソッドはこれまでブロックが必須でしたが、Ruby 2.4からはブロック無しで呼び出すとEnumeratorオブジェクトを返すようになります。

```ruby
def test_numerable_chunk
  assert_instance_of Enumerator, [1, 2, 3].chunk
end
```

参考：[Feature \#2172: Enumerable\#chunk with no block](https://bugs.ruby-lang.org/issues/2172)

### Setに同じインスタンスかどうかで重複を判断するモードが追加された

Ruby 2.4ではSetクラスに`compare_by_identity`メソッドが追加されました。
このメソッドを呼び出すと、同じインスタンスかどうか（つまり`eql?`ではなく`equal?`の結果）で重複を判断します。

また、`compare_by_identity?`メソッドを使うと、どちらのモードになっているのかを確認できます。

```ruby
def test_set_compare_by_identity
  require 'set'
  
  set = Set.new
  values = ['a', 'a', 'b', 'b']
  set.merge(values)
  refute set.compare_by_identity?
  assert_equal ['a', 'b'], set.to_a

  set = Set.new
  values = ['a', 'a', 'b', 'b']
  set.compare_by_identity
  set.merge(values)
  assert set.compare_by_identity?
  assert_equal ['a', 'a', 'b', 'b'], set.to_a
end
```

## 文字列・シンボルに関連する新機能

### concatメソッドとprependメソッドが複数の文字列を受け取れるようになった

Ruby 2.4では、`concat`メソッドと`prepend`メソッドが複数の文字列を引数として受け取れるようになりました。

```ruby
def test_string_concat
  s = 'a'
  assert_equal 'abc', s.concat('b', 'c')
  assert_equal 'abc', s

  s = 'z'
  assert_equal 'xyz', s.prepend('x', 'y')
  assert_equal 'xyz', s
end
```

参考：[Feature \#12333: \`String\#concat\`, \`Array\#concat\`, \`String\#prepend\` to take multiple arguments](https://bugs.ruby-lang.org/issues/12333)

### 大文字小文字を無視して同一かどうかを返すcasecmp?メソッドが追加された（rc1）

Ruby 2.4では大文字小文字を無視して同一かどうかを`true`/`false`で返す`casecmp?`メソッドが追加されました。
このメソッドは文字列とシンボルで使えます。

```ruby
def test_casecmp?
  assert 'abc'.casecmp?('ABC')
  refute 'abc'.casecmp?('DEF')
  
  assert :abc.casecmp?(:ABC)
  refute :abc.casecmp?(:DEF)
end
```

ちなみにRuby 2.3までは1, 0, -1を返す`casecmp`メソッドで比較する必要がありました。

```ruby
assert_equal 0, 'abc'.casecmp('ABC')
assert_equal -1, 'abc'.casecmp('DEF')
assert_equal 0, :abc.casecmp(:ABC)
assert_equal -1, :abc.casecmp(:DEF)
```

[Feature \#12786: String\#casecmp? \- Ruby trunk \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/12786)

### unpackの1要素目を返すunpack1メソッドが追加された（rc1）

Ruby 2.4では`unpack`の戻り値から1要素目だけを返す`unpack1`メソッドが追加されました。

```ruby
def test_unpack1
  # unpackは配列を返す
  assert_equal [65, 66, 67], "ABC".unpack("C*")
  
  # unpackは1要素目だけを返す
  assert_equal 65, "ABC".unpack1("C*")
end
```

[Feature \#12752: Unpacking a value from a binary requires additional '\.first' \- Ruby trunk \- Ruby Issue Tracking System](https://bugs.ruby-lang.org/issues/12752)

## ディレクトリやファイルに関連する新機能

### Pathnameクラスにempty?メソッドが追加された

Ruby 2.4ではPathnameクラスに`empty?`メソッドが追加されました。
このメソッドを使うとディレクトリやファイルの中身が空かどうかを確認できます。

```ruby
def test_pathname_empty?
  require 'tempfile'
  require 'pathname'
  
  Dir.mktmpdir do |dir|
    assert Pathname(dir).empty?
  end
  
  Tempfile.create("foo") do |f|
    assert Pathname(f).empty?
  end
end
```

なお、Preview 2では`Dir`クラスと`File`クラスにも`empty?`メソッドが追加されています。

参考：[Feature \#12596: Add Pathname\#empty? to be consistent with Dir\.empty? and File\.empty?](https://bugs.ruby-lang.org/issues/12596)

### テキストファイルを行単位で読み込む際に、chompするかどうかを指定できるようになった

`IO#gets`, `IO#readline`, `IO#each_line`, `IO#readlines`, `IO#foreach`で`chomp`オプションを指定できるようになりました。
このオプションを指定すると、最初から`chomp`した状態（つまり改行文字を削除した状態）で各行のテキストを取得できます。

```ruby
def test_io_readlines
  require 'tempfile'
  tf = Tempfile.new("foo")
  tf.print "abc\ndef\nghi\n"
  tf.close

  File.open(tf.path) do |f|
    # chompせずに読み込む
    assert_equal ["abc\n", "def\n", "ghi\n"], f.readlines
    
    f.rewind
    
    # chompして読み込む
    assert_equal ["abc", "def", "ghi"], f.readlines(chomp: true)
  end
ensure
  tf&.unlink
end
```

## Refinementsに関連する新機能

### モジュールもrefineできるようになった

Ruby 2.4ではクラスだけでなく、モジュールに対してもrefineできるようになりました。

```ruby
# refineされるモジュール
module Awesome
  def xxx
    'awesome'
  end
end

# モジュールをrefineするモジュール
module SuperAwesome
  refine Awesome do
    def xxx
      # superは元のモジュールのメソッドを呼び出す
      "not #{super} but super-awesome"
    end
  end
end

using SuperAwesome

class Person
  include Awesome

  def say
    "I am #{xxx}!"
  end
end

class RefinementsTest < Minitest::Test
  def test_module_refinements
    person = Person.new
    assert_equal 'I am not awesome but super-awesome!', person.say
  end
end
```

参考：[Feature \#12534: Refinements: refine modules as well](https://bugs.ruby-lang.org/issues/12534)

### "&:to_s"のようにシンボルをProc化した場合もrefineされるようになった

Ruby 2.4では`&:to_s`のようにシンボルをProc化した場合もrefineされるようになりました。

```ruby
module StringPugs
  refine String do
    def pugs
      "Pugs!"
    end
  end
end

using StringPugs

class RefinementsTest < Minitest::Test
  def test_symbol_to_proc
    assert_equal ['Pugs!', 'Pugs!'], ['a', 'b'].map(&:pugs)
  end
end
```

参考：[Feature \#9451: Refinements and unary & \(to\_proc\)](https://bugs.ruby-lang.org/issues/9451)

### sendメソッド経由で呼びだしたメソッドにもrefineされるようになった

Ruby 2.4では`send`メソッド経由で呼びだしたメソッドもrefineされるようになりました。

```ruby
require 'minitest/autorun'

module StringPugs
  refine String do
    def pugs
      "Pugs!"
    end
  end
end

using StringPugs

class RefinementsTest < Minitest::Test
  def test_send
    assert_equal 'Pugs!', 'a'.send(:pugs)
  end
end
```

参考：[Feature \#11476: Methods defined in Refinements cannot be called via send](https://bugs.ruby-lang.org/issues/11476)

### Refinementsで使われているモジュールを取得する`Module.used_modules`メソッドが追加された

Ruby 2.4では現在のスコープのRefinementsで使われているモジュールを取得する`Module.used_modules`メソッドが追加されました。

```ruby
module StringPugs
  refine String do
    def pugs
      "Pugs!"
    end
  end
end

# Refinementsで使った1つ目のモジュール
using StringPugs

module Awesome
  def xxx
    'awesome'
  end
end

module SuperAwesome
  refine Awesome do
    def xxx
      "not #{super} but super-awesome"
    end
  end
end

# Refinementsで使った2つ目のモジュール
using SuperAwesome

class Person
  include Awesome

  def say
    "I am #{xxx}!"
  end
end

class RefinementsTest < Minitest::Test
  def test_used_modules
    assert_equal [StringPugs, SuperAwesome], Module.used_modules
  end
end
```

参考：[Feature \#7418: Kernel\#used\_refinements](https://bugs.ruby-lang.org/issues/7418)

## その他

### 警告発生時の振る舞いをカスタマイズできるようになった

Ruby 2.4ではWarningというモジュールが組み込みライブラリに追加されました。
このモジュールには`warn`というクラスメソッドを持っています。
Rubyの実行中に警告が出力された際はこの`Warning.warn`メソッドが呼び出されるので、このメソッドをオーバーライドすると、警告の出力を取得したり加工したりすることができます。

以下は発生した警告を配列として保持し、後から取得するコード例です。

```ruby
module Warning
  # これはRuby 2.4で追加されたメソッドのオーバーライド
  def self.warn(msg)
    warnings << msg
    super
  end

  # これは独自に追加したクラスメソッド
  def self.warnings
    @warnings ||= []
  end
end

class WarningTest < Minitest::Test
  def setup
    Warning.warnings.clear
  end

  def test_warn
    # 警告を発生させるため、わざとFixnumクラスを参照する
    assert Fixnum == Integer
    
    # 出力された警告をちゃんと保持しているかどうかの検証
    assert_equal 1, Warning.warnings.size
    msg = Warning.warnings.first
    assert msg.include?('warning: constant ::Fixnum is deprecated')
  end
end
```

ちなみに、Warningというクラス名は普通の英単語なので、みなさん自身が既存のコードで独自に定義している可能性もあります。
既存のコードでWarningというクラスを定義していると、Ruby 2.4にアップデートしたタイミングで名前の衝突が起きてエラーになるので注意してください。

```ruby
# Ruby 2.4
class Warning
end
#=> TypeError: Warning is not a class
```

参考：[Feature \#12299: Add Warning module for customized warning handling](https://bugs.ruby-lang.org/issues/12299)

### Shellwords.shellsplitがバックスラッシュを正しく解釈するようになった

`Shellwords.shellsplit`メソッドが引数に渡された文字列のバックスラッシュを正しく解釈するようになりました。

```ruby
# Ruby 2.3
require 'shellwords'

# この結果はおかしい
Shellwords.shellsplit(%q|printf "%s\\n"|)
#=> ["printf", "%sn"]

# これはOK
Shellwords.shellsplit(%q|printf '%s\\n'|)
#=> ["printf", "%s\\n"]
```

```ruby
# Ruby 2.4
require 'shellwords'

# どちらもOK
Shellwords.shellsplit(%q|printf "%s\\n"|)
#=> ["printf", "%s\\n"]

Shellwords.shellsplit(%q|printf '%s\\n'|)
#=> ["printf", "%s\\n"]
```

参考：[Bug \#10055: Shellwords\.shellsplit\(\) does not match POSIX sh behavior for backslashes within double\-quoted strings](https://bugs.ruby-lang.org/issues/10055)

### IPAddrオブジェクトを==で比較する際、右辺に無効なオブジェクトが来てもエラーが起きなくなった

IPAddrクラスのオブジェクトを`==`で比較する際、右辺に単純な文字列等を置いたりするとエラーが起きていましたが、Ruby 2.4ではエラーではなく`false`を返すようになりました。

```ruby
def test_ipaddr_compare
  require 'ipaddr'
  # Ruby 2.3以前ではIPAddr::InvalidAddressErrorが発生していた
  refute IPAddr.new("1.1.1.1") == "sometext"
end
```

### RDocのバージョンが5.0.0に上がった

Ruby 2.4にバンドルされるRDocのバージョンが5.0.0に上がりました。

参考：[rdoc\-5\.0\.0 をリリースした - HsbtDiary](https://www.hsbt.org/diary/20161105.html)

### XMLRPCが外部gemとして切り出された

Ruby 2.4ではXMLRPCが標準ライブラリではなく、外部のgemになりました。

https://github.com/ruby/xmlrpc

ただし、Rubyインストール時にXMLRPC gemも一緒にインストールされるため、通常の使用では特に変化はありません。

参考：[Feature \#12160: Extract XMLRPC library to bundled gem](https://bugs.ruby-lang.org/issues/12160%5Bruby-core:74239%5D)

### FreeBSD 4未満はサポートされなくなった

FreeBSD 4未満（FreeBSD < 4）はサポートされなくなったとのことです。

### 今回確認しきれなかったPreview 3の変更点

以下の変更点は今回自分で確認できなかったので、[こちらのブログ](http://d.hatena.ne.jp/nagachika/)に書かれている記載内容を引用させてもらいます。

[TracePoint#callee_id [Feature #12747]](https://bugs.ruby-lang.org/issues/12747)

> r56592 の続きで TracePoint に callee_id を追加しています。 alias されたメソッドを呼んだ時に呼び元で呼んだメソッド名のほうを返すようにしています。実体のメソッド名(method_id)と呼び元でのメソッド名(callee_id)を明確に区別するようにしたのですね。 
>
> http://d.hatena.ne.jp/nagachika/20161106

[New method: Net::HTTP.post [Feature #12375]](https://bugs.ruby-lang.org/issues/12375)

> 標準添付ライブラリ net/http に POST のリクエストを送信する Net::HTTP.post メソッドを追加しています。 Net::HTTP.post_form というメソッドもありますが、こちらはパラメータを application/x-www-form-urlencoded でエンコードする前提ですが、こちらはリクエストボディをそのまま指定するので JSON エンコードしたリクエストボディを渡すこともできます。また header の指定もできるようになっているので、より便利になりました。 
>
> http://d.hatena.ne.jp/nagachika/20161106

[Readline.quoting_detection_proc and Readline.quoting_detection_proc= [Feature #12659]](https://bugs.ruby-lang.org/issues/12659)

> 拡張ライブラリ readline で Readline の rl_char_is_quoted_p というコールバック関数を登録する変数に対応して Readline.quoting_detection_proc= で call メソッドを呼べるオブジェクトを登録できるようにしています。これは入力する文字がクオート内でエスケープされているかどうかを判定するものだそうです。 
>
> http://d.hatena.ne.jp/nagachika/20161003

[CGI WEBrick Don't allow , as a separator [Bug #12791]](https://bugs.ruby-lang.org/issues/12791)

> 標準添付ライブラリ webrick と cgi で HTTP ヘッダの Cookie をパースする時に複数の Cookie を "," で区切って並べる記法を受け付けないようにしています。 
>
> http://d.hatena.ne.jp/nagachika/20160927

RubyVM::Env was removed.

> ユーザは直接使ってなかっただろうと思いますが RubyVM::Env クラスは削除して、内部的なオブジェクトをまとめている T_IMEMO オブジェクトの仲間入りをしています。
>
> http://d.hatena.ne.jp/nagachika/20160728

Support CLOCK_MONOTONIC_RAW_APPROX, CLOCK_UPTIME_RAW, and CLOCK_UPTIME_RAW_APPROX which are introduced by macOS 10.12.

> macOS 10.12 (sierra) で clock_gettime() が対応されたので Process.clock_gettime の clock_id 用の各定数のサポートするプラットフォームへ macOS 10.12 を追記しています。 さらに新たに追加されるフラグとして Process に定数 CLOCK_MONOTONIC_RAW_APPROX、CLOCK_UPTIME_RAW、CLOCK_UPTIME_RAW_APPROX を追加しています。
>
> http://d.hatena.ne.jp/nagachika/20160921

the extension library is removed.  Till 2.0 it was a pure ruby script "thread.rb", which has precedence over "thread.so", and has been provided in $LOADED_FEATURES since 2.1.

（こちらは該当する情報が確認できませんでした）

## まとめ

というわけで、本記事ではRuby 2.4の新機能や変更点をまとめてみました。

個人的に一番気になるのはFixnumとBignumの統合でしょうか。
普段自分が書いているコードではわざわざ両者を区別するようなコードは書いていませんでしたが、もしかするとよく使うgemでそういったコードが書いてあると動かなくなるかも？と思ったりします。
とはいえ、FixnumやBignumを定数として参照した場合はIntegerが返るようになっているので、Ruby 2.4に変えた瞬間に動かなくなる、というケースはほとんどないような気もします。

その他はそこまでびっくりするような新機能が追加されているわけではありませんが、Enumerableモジュールの`sum`メソッドや、小数点を指定できる`ceil`/`floor`など、派手さはないが実用性のある新機能が追加されている印象です。

さあ、みなさんもRuby 2.4をインストールして、12月25日の正式リリースまでに新機能をいろいろ試してみてください！

### あわせて読みたい

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

[サンプルコードでわかる！Ruby 2\.3の主な新機能 \- Qiita](http://qiita.com/jnchito/items/0faac073cb77417d61c7)

本文には何度か正規表現の話が出てきましたが、そもそも正規表現って何？という方はこちらの記事を読んでみてください。

[初心者歓迎！手と目で覚える正規表現入門・その１「さまざまな形式の電話番号を検索しよう」 \- Qiita](http://qiita.com/jnchito/items/893c887fbf19e17d3ff9)

Ruby 2.4ではTimeとDateTimeの不具合が修正されましたが、TimeとDateTimeって何が違うの？という方はこちらの記事が参考になるかもしれません。

[RubyとRailsにおけるTime, Date, DateTime, TimeWithZoneの違い \- Qiita](http://qiita.com/jnchito/items/cae89ee43c30f5d6fa2c)