はじめに
2016年9月にRuby 2.4のpreview2が、11月にpreview3が、12月11日にrc1がリリースされました(参考1、参考2、参考3)。
僕は早速インストールして新機能を試してみましたが、みなさんはいかがでしょうか?
というわけで、この記事では僕が実際に動かして確認したRuby 2.4の変更点や新機能をサンプルコード付きでまとめます。
この記事は大きく分けて次のセクションに分かれています。
- 文法上の変更点
- 後方互換性が失われる変更点
- パフォーマンス改善
- オブジェクト全般の新機能
- 主に数値に関連する新機能
- 主に文字列・正規表現に関連する新機能
- 主に配列・ハッシュに関連する新機能
- ディレクトリやファイルに関連する新機能
- その他の標準ライブラリに関連する新機能
- スレッドに関連する新機能
なお、Ruby 2.4は2016年12月25日にリリースされる予定です。
本記事はrc1時点のものなので、正式リリースまでに内容が変わる可能性もあります。
2016.12.25追記:正式リリース版でも動作検証しました
予定通り、2016年12月25日にRuby 2.4.0が正式リリースされました。
下記の内容のほとんどは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
主要な変更点は本記事の内容でカバーできているはずですが、マイナーな変更点まではカバーできていないかもしれません。
また、説明している内容に間違いがあれば、コメントや編集リクエスト等で優しく指摘してやってください。
本記事で使ったサンプルコードのリポジトリ
本記事で使ったサンプルコード(テストコード)は以下のリポジトリにあります。
Minitestの簡単な説明
本文中ではMinitestを使ったテストコードが登場します。
Minitestのコードの意味がよくわからない、という人は最低限、以下の検証メソッドの意味を理解していれば、なんとなく内容がつかめるかと思います。
# 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
を返すメソッドを条件式内で使うコード例です。
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
後方互換性が失われる変更点
FixnumクラスとBignumクラスがIntegerクラスに統合された
Ruby 2.3までは整数は基本的にFixnumクラスで、極端に大きいまたは小さい数になるとBignumクラスに自動的に変わっていましたが、Ruby 2.4では両者の親クラスであるIntegerクラスにまとめられます。
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
よって以下のテストコードはすべてパスします。
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で同名のメソッドを定義していた場合は、後勝ちになるようです。
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
Enumerableモジュールにsumが追加された
Ruby 2.4ではEnumerableモジュールにsum
メソッドが追加されました。
これにより、配列の中身の合計値を求めたりできます。
[1, 2, 3, 4].sum
#=> 10
デフォルトの初期値は0ですが、引数で変更できます。
[].sum
#=> 0
[1, 2, 3, 4].sum(5)
#=> 15
小数の合計値は丸め誤差が発生するので注意してください。
[0.1, 0.1, 0.1].sum
#=> 0.30000000000000004
ただし、sum
メソッドでは内部的にカハンの加算アルゴリズムが使われているため、+
演算子で加算したときよりも高速で誤差が少なくなりやすいです。
# 要素が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
文字列の配列を連結する場合は初期値に何らかの文字列を渡します。
['foo', 'bar'].sum('')
#=> 'foobar'
['foo', 'bar'].sum('>>')
#=> '>>foobar'
配列の配列を連結することも可能です。
[[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
非ASCII文字でも小文字化、大文字化されるようになった
Ruby 2.4では非ASCII文字(たとえばウムラウトが含まれるような文字)でも小文字化、大文字化されるようになりました。
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
オプションを付けてください。
'Türkiye'.upcase(:ascii)
#=> "TüRKIYE"
参考: Feature #10085: Add non-ASCII case conversion to String#upcase/downcase/swapcase/capitalize
Symbol#matchがMatchDataを返すようになった(不具合修正)
Ruby 2.3まではSymbol#match
はマッチした位置を返す不具合がありました。
:"".match(//)
# => 0
Ruby 2.4ではString#match
と同じようにMatchDataオブジェクトを返します。
m = :"".match(//)
#=> #<MatchData "">
m[0]
#=> ""
参考: Bug #11991: `Symbol#match` returns the match position, unlike `String#match` and `Regexp#match`
DateTimeとTimeのto_timeでタイムゾーンを保持するようになった(不具合修正)
Ruby 2.4ではDateTimeクラスとTimeクラスのto_time
メソッドを呼びだした際、元のオブジェクトのタイムゾーンを保持するようになりました。
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
メソッドを呼び出すと、実行環境のタイムゾーンに変わる不具合がありました。
# 実行環境が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
Ruby/Tkが標準ライブラリから外された
Ruby 2.4ではRuby/Tkが標準ライブラリから外されて外部gemになりました。
Rubyインストール時には一緒にインストールされないため、必要であれば別途インストールする必要があります。
参考: Feature #8539: Unbundle ext/tk
パフォーマンス改善
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
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
オブジェクト全般の新機能
指定された範囲内の値を返すようにするComparable#clamp
Ruby 2.4ではclamp
メソッドを使って、指定した範囲の値を返すことができます。
「指定した範囲の値を返す」というのは厳密に言うと、下限値と上限値を引数で指定し、レシーバの値が、
- その範囲内であればレシーバを返す
- 下限を超えていれば下限値を返す
- 上限を超えていれば上限値を返す
というものです。
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
メソッドに渡す引数は必ず下限値、上限値の順で渡す必要があります。逆で渡すとエラーになります。
2.clamp(3, 1)
#=> ArgumentError: min argument must be smaller than max argument
なお、clampというメソッド名は電子回路で使われる「クランプ回路」に由来するようです。
過電流や短絡電流の対策に使える電流クランプ回路 - EDN Japan
参考: Feature #10594: Comparable#clamp
freeze状態までcloneするかどうかをオプションで指定できるようになった
freezeしたオブジェクトをcloneした場合、Ruby 2.3まではcloneしたオブジェクトまでfreezeされていましたが、Ruby 2.4ではfreezeするかどうかをfreeze
オプションで指定できます。
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
主に数値に関連する新機能
切り上げ、切り下げ、切り捨てで小数点の位置を指定できるようになった
Ruby 2.4ではceil
、floor
、truncate
の各メソッドに引数を渡して、切り上げ、切り下げ、切り捨てを行う小数点の位置を指定できるようになりました。
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
整数を1桁ずつ配列に入れて返すInteger#digits
Ruby 2.4では整数を1桁ずつ配列に入れて返すInteger#digits
メソッドが追加されました。値は1桁目、2桁目・・・の順番で格納されます。
123.digits
#=> [3, 2, 1]
基数を指定するとn進数の1桁目、2桁目・・・と値を格納することもできます。
0x7b.digits(16)
#=> [11, 7]
123.digits(16)
#=> [11, 7]
マイナスの値に対してdigits
を呼び出すとエラーになります。
-123.digits
#=> Math::DomainError: out of domain
参考: Feature #12447: Integer#digits for extracting digits of place-value notation in any base
主に文字列・正規表現に関連する新機能
正規表現に文字列がマッチしたかどうかだけを返すRegexp#match?、String#match?、Symbol#match?
Ruby 2.4では正規表現に文字列がマッチしたかどうかだけを返すRegexp#match?
メソッドが追加されました。
このメソッドはmatch
メソッドよりも高速で、なおかつグローバル変数の$~
を変更しない、という特徴もあります。
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
なお、match
メソッドとは異なり、Stringクラスにはmatch?
メソッドは定義されていません。
rc1にてStringクラス、Symbolクラスにもmatch?
メソッドが実装されました。
'2016-09-01'.match?(/\d+-\d+-\d+/)
#=> true
:hello_world.match?(/\w+_\w+/)
#=> true
参考: Feature #8110: Regex methods not changing global variables
名前付きキャプチャをハッシュとして返すMatchData#named_captures
Ruby 2.4では正規表現にマッチした名前付きキャプチャをハッシュとして返すMatchData#named_captures
メソッドが追加されました。
ハッシュのキーはシンボルではなく文字列になります。
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
MatchData#values_atで名前付きキャプチャの名前を指定できるようになった
Ruby 2.3まではMatchData#values_at
メソッドで指定できるのは、キャプチャした文字列のインデックスだけでしたが、Ruby 2.4では名前付きキャプチャの名前を指定できるようになりました。
名前は文字列、またはシンボルで指定できます。
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
Stringのcapacityを指定できるようになった
Ruby 2.4ではString.new
にcapacity
オプションを渡せるようになりました。
長い文字列を追加することが事前に分かっている場合は、このオプションを指定することでパフォーマンスを向上させることができます。
以下のサイトの計測結果によると、capacity
を指定すると2.32倍速くなったそうです。
New Features in Ruby 2.4 - BlockScore Blog
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
を指定してもそれ以上文字が追加できなくなるわけではありません。
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
StringクラスとRegexpクラスのUnicodeバージョンが9.0にアップデート
Ruby 2.4ではStringクラスとRegexpクラスのUnicodeバージョンが8.0から9.0にアップデートされました。
これにより、たとえば正規表現で西夏文字(せいかもじ)を表すTangut
プロパティ(Unicode 9.0で追加されたプロパティ)を使えたりするようになります。
def test_unicode_9_0
assert "A\u{17000}B".match(/\p{Tangut}/)
end
ちなみに\u{17000}
というのはこんな文字です。(大半のブラウザでは表示できないはずです)
参考: Feature #12513: Update Unicode data to Unicode Version 9.0
主に配列・ハッシュに関連する新機能
指定した条件で重複をなくすEnumerable#uniq
Ruby 2.4ではEnumerableモジュールにuniq
メソッドが追加されたため、配列だけでなく、ハッシュでもuniq
が使えるようになります。
重複をなくす条件はブロックで指定します。
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!
は用意されません。
olimpics.uniq! { |k, v| v }
#=> undefined method `uniq!' for #<Hash:0x007fa9f505bb48>
Enumerator::Lazy
でもuniq
が使えます。
# 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
ハッシュの値を特定のルールで変更するHash#transform_values
Ruby 2.4ではHashクラスにtransform_values
メソッドが追加され、ハッシュの値を特定のルールで変更できるようになりました。
破壊的な変更を行うtransform_values!
メソッドもあります。
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本体にインポートしたものです。
参考: Feature #12512: Import Hash#transform_values and its destructive version from ActiveSupport
ディレクトリやファイルに関連する新機能
ディレクトリが空かどうかを判定するDir.empty?
Ruby 2.4ではDir.empty?
というメソッドが追加されました。
その名の通り、ディレクトリが空であればtrueを返すメソッドです。
def test_dir_empty?
Dir.mktmpdir do |dir|
assert Dir.empty?(dir)
end
end
参考: Feature #10121: Dir.empty?
ファイルが空かどうかを判定するFile.empty?
Ruby 2.4ではFile.zero?
のエイリアスメソッドとして、File.empty?
が追加されました。
その名の通り、ファイルが空であればtrueを返すメソッドです。
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?
その他の標準ライブラリに関連する新機能
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
オプションを付けてパースするコード例です。
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
Logger.newのキーワード引数追加
Ruby 2.4ではLogger.new
のキーワード引数としてlevel
、progname
、formatter
、datetime_format
が追加されました。
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以前ではインスタンス作成後にプロパティをセットする必要がありました。
logger = Logger.new($stdout)
logger.level = :info
参考: Feature #12224: logger: Allow specifying log level in constructor
ログのローテーションファイルの拡張子書式を指定できるようになった
Ruby 2.3以前では、ログのローテーションファイルの拡張子書式は"%Y%m%d"固定になっていましたが、Ruby 2.4ではshift_period_suffix
オプションを使って書式を指定できるようになりました。
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
optparseで起動時引数をハッシュに格納するintoオプション
optparseで起動時引数をパースする際、into
オプションを使ってパースした結果をハッシュに格納できるようになりました。
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
参考: Feature #11191: Add #to_h method to OptionParser
OpenSSLが外部gemとして切り出された
Ruby 2.4ではOpenSSLが標準ライブラリではなく、外部のgemになりました。
ただし、Rubyインストール時にOpenSSL gemも一緒にインストールされるため、通常の使用では特に変化はありません。
また、機能面ではOpenSSL 1.1.0をサポートするようになったとのことです。
参考: Feature #9612: Gemify OpenSSL
スレッドに関連する新機能
別スレッドの例外を報告するかどうかを決めるThread.report_on_exceptionフラグ
Ruby 2.4では別スレッドで発生した例外を報告するかどうかを決めるThread.report_on_exception
フラグが追加されました。
このフラグをtrueにすると、別スレッドで例外が発生した場合にその内容が報告されます。
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
参考: Feature #6647: Exceptions raised in threads should be logged
デッドロック発生時に全スレッドのバックトレースが出力されるようになった
Ruby 2.4ではデッドロック発生時に全スレッドのバックトレースが出力されるようになりました。
以下はデッドロックが発生するコードの例です。
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: デッドロックチェックに全スレッドのバックトレースダンプの追加
Preview 2の主な変更点は以上です。続いてPreview 3の変更点を載せていきます。
文法上の変更点(これ以降はPreview 3とrc1)
rescue修飾子付きのコードを ( ) で囲んでメソッドの引数として渡せるようになった
以下のようなコードはこれまで構文エラーになっていましたが、Ruby 2.4では実行可能になりました。
# 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
後方互換性が失われる変更点
roundメソッドがデフォルトで偶数丸めをするようになった => halfオプションの追加のみになりました(rc1)
roundメソッドで四捨五入する際のルールが「偶数丸め」になりました。
preview3の時点では偶数丸めがデフォルトになっていましたが、その後の議論によりrc1ではデフォルトの挙動は従来通り(つまり0.5なら切り上げ)になりました。
Bug #12958: Breaking change in how `#round` works - Ruby trunk - Ruby Issue Tracking System
結果として、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
以下はデフォルトのround
メソッドの結果と、half: :even
を指定した場合の挙動の違いです。
# デフォルト(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 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
参考:Bug #12958: Breaking change in how `#round` works - Ruby trunk - Ruby Issue Tracking System
パフォーマンス改善
ハッシュテーブルの高速化
こちらのアナウンスによると「従来Rubyのハッシュテーブルの内部実装 (st_table) ではに双方向連結リストとチェイン法を用いた実装が使われていましたが、挿入順の配列にオープンアドレス法を用いることによる高速化が行われました」とのことです。
以下のIssueではかなり専門的な議論が繰り広げられているので、興味がある方はチェックしてみてください。
参考: Feature #12142: Hash tables with open addressing
インスタンス変数へのアクセスの高速化
内部実装の改善により、インスタンス変数のアクセスが高速化したそうです。
以下のIssueでは10%ほど速くなった、というような話が載っています
参考: Bug #12274: accessing to instance variable should be fast.
irbの変更点
プログラムの実行中にirbが開けるbinding.irbメソッドの追加
Pryを使っているとbinding.pry
というコマンドをコードに埋め込むことで、プログラムの実行中にPryを開くことができます。
これと同じように、irbでもbinding.irb
というコマンドでirbが開けるようになりました。
# irbをrequireする
require 'irb'
s = 'hello'
puts s
# ここでirbを開く
binding.irb
puts 'bye'
上のコードを実行すると、次のように途中でirbが起動します。
参考:Revision 56624 - irb.rb: Binding#irb * lib/irb.rb (Binding#irb): new method like Binding#pry.
数値に関する新機能
infinite?/finite?メソッドがFloat以外の数値クラスにも追加された
値が無限大かどうか(+∞ or -∞)をチェックするためのinfinite?
メソッド(逆はfinite?
メソッド)が、Floatメソッドだけでなく、Integerクラス、Rationalクラス、Complexクラスにも追加されました。
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
配列・ハッシュ・Setに関連する新機能
concatメソッドが複数の配列を引数に取れるようになった
配列のconcat
メソッドが複数の配列を引数に取れるようになりました。
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
ハッシュにcompact
/compact!
メソッドが追加された
値がnil
の要素を削除するcompact
メソッドがハッシュに追加されました。
破壊的に変更するcompact!
メソッドも用意されています。
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
メソッドと同等です。
参考:Feature #12333: `String#concat`, `Array#concat`, `String#prepend` to take multiple arguments
ブロック無しのchunkがEnumeratorを返すようになった
Enumerable
モジュールのchunk
メソッドはこれまでブロックが必須でしたが、Ruby 2.4からはブロック無しで呼び出すとEnumeratorオブジェクトを返すようになります。
def test_numerable_chunk
assert_instance_of Enumerator, [1, 2, 3].chunk
end
参考:Feature #2172: Enumerable#chunk with no block
Setに同じインスタンスかどうかで重複を判断するモードが追加された
Ruby 2.4ではSetクラスにcompare_by_identity
メソッドが追加されました。
このメソッドを呼び出すと、同じインスタンスかどうか(つまりeql?
ではなくequal?
の結果)で重複を判断します。
また、compare_by_identity?
メソッドを使うと、どちらのモードになっているのかを確認できます。
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
メソッドが複数の文字列を引数として受け取れるようになりました。
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
大文字小文字を無視して同一かどうかを返すcasecmp?メソッドが追加された(rc1)
Ruby 2.4では大文字小文字を無視して同一かどうかをtrue
/false
で返すcasecmp?
メソッドが追加されました。
このメソッドは文字列とシンボルで使えます。
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
メソッドで比較する必要がありました。
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
unpackの1要素目を返すunpack1メソッドが追加された(rc1)
Ruby 2.4ではunpack
の戻り値から1要素目だけを返すunpack1
メソッドが追加されました。
def test_unpack1
# unpackは配列を返す
assert_equal [65, 66, 67], "ABC".unpack("C*")
# unpackは1要素目だけを返す
assert_equal 65, "ABC".unpack1("C*")
end
ディレクトリやファイルに関連する新機能
Pathnameクラスにempty?メソッドが追加された
Ruby 2.4ではPathnameクラスにempty?
メソッドが追加されました。
このメソッドを使うとディレクトリやファイルの中身が空かどうかを確認できます。
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?
テキストファイルを行単位で読み込む際に、chompするかどうかを指定できるようになった
IO#gets
, IO#readline
, IO#each_line
, IO#readlines
, IO#foreach
でchomp
オプションを指定できるようになりました。
このオプションを指定すると、最初からchomp
した状態(つまり改行文字を削除した状態)で各行のテキストを取得できます。
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できるようになりました。
# 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
"&:to_s"のようにシンボルをProc化した場合もrefineされるようになった
Ruby 2.4では&:to_s
のようにシンボルをProc化した場合もrefineされるようになりました。
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)
sendメソッド経由で呼びだしたメソッドにもrefineされるようになった
Ruby 2.4ではsend
メソッド経由で呼びだしたメソッドもrefineされるようになりました。
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
Refinementsで使われているモジュールを取得するModule.used_modules
メソッドが追加された
Ruby 2.4では現在のスコープのRefinementsで使われているモジュールを取得するModule.used_modules
メソッドが追加されました。
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
その他
警告発生時の振る舞いをカスタマイズできるようになった
Ruby 2.4ではWarningというモジュールが組み込みライブラリに追加されました。
このモジュールにはwarn
というクラスメソッドを持っています。
Rubyの実行中に警告が出力された際はこのWarning.warn
メソッドが呼び出されるので、このメソッドをオーバーライドすると、警告の出力を取得したり加工したりすることができます。
以下は発生した警告を配列として保持し、後から取得するコード例です。
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 2.4
class Warning
end
#=> TypeError: Warning is not a class
参考:Feature #12299: Add Warning module for customized warning handling
Shellwords.shellsplitがバックスラッシュを正しく解釈するようになった
Shellwords.shellsplit
メソッドが引数に渡された文字列のバックスラッシュを正しく解釈するようになりました。
# Ruby 2.3
require 'shellwords'
# この結果はおかしい
Shellwords.shellsplit(%q|printf "%s\\n"|)
#=> ["printf", "%sn"]
# これはOK
Shellwords.shellsplit(%q|printf '%s\\n'|)
#=> ["printf", "%s\\n"]
# Ruby 2.4
require 'shellwords'
# どちらもOK
Shellwords.shellsplit(%q|printf "%s\\n"|)
#=> ["printf", "%s\\n"]
Shellwords.shellsplit(%q|printf '%s\\n'|)
#=> ["printf", "%s\\n"]
IPAddrオブジェクトを==で比較する際、右辺に無効なオブジェクトが来てもエラーが起きなくなった
IPAddrクラスのオブジェクトを==
で比較する際、右辺に単純な文字列等を置いたりするとエラーが起きていましたが、Ruby 2.4ではエラーではなくfalse
を返すようになりました。
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
XMLRPCが外部gemとして切り出された
Ruby 2.4ではXMLRPCが標準ライブラリではなく、外部のgemになりました。
ただし、Rubyインストール時にXMLRPC gemも一緒にインストールされるため、通常の使用では特に変化はありません。
参考:Feature #12160: Extract XMLRPC library to bundled gem
FreeBSD 4未満はサポートされなくなった
FreeBSD 4未満(FreeBSD < 4)はサポートされなくなったとのことです。
今回確認しきれなかったPreview 3の変更点
以下の変更点は今回自分で確認できなかったので、こちらのブログに書かれている記載内容を引用させてもらいます。
TracePoint#callee_id [Feature #12747]
r56592 の続きで TracePoint に callee_id を追加しています。 alias されたメソッドを呼んだ時に呼び元で呼んだメソッド名のほうを返すようにしています。実体のメソッド名(method_id)と呼び元でのメソッド名(callee_id)を明確に区別するようにしたのですね。
New method: Net::HTTP.post [Feature #12375]
標準添付ライブラリ net/http に POST のリクエストを送信する Net::HTTP.post メソッドを追加しています。 Net::HTTP.post_form というメソッドもありますが、こちらはパラメータを application/x-www-form-urlencoded でエンコードする前提ですが、こちらはリクエストボディをそのまま指定するので JSON エンコードしたリクエストボディを渡すこともできます。また header の指定もできるようになっているので、より便利になりました。
Readline.quoting_detection_proc and Readline.quoting_detection_proc= [Feature #12659]
拡張ライブラリ readline で Readline の rl_char_is_quoted_p というコールバック関数を登録する変数に対応して Readline.quoting_detection_proc= で call メソッドを呼べるオブジェクトを登録できるようにしています。これは入力する文字がクオート内でエスケープされているかどうかを判定するものだそうです。
CGI WEBrick Don't allow , as a separator [Bug #12791]
標準添付ライブラリ webrick と cgi で HTTP ヘッダの Cookie をパースする時に複数の Cookie を "," で区切って並べる記法を受け付けないようにしています。
RubyVM::Env was removed.
ユーザは直接使ってなかっただろうと思いますが RubyVM::Env クラスは削除して、内部的なオブジェクトをまとめている T_IMEMO オブジェクトの仲間入りをしています。
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 を追加しています。
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
本文には何度か正規表現の話が出てきましたが、そもそも正規表現って何?という方はこちらの記事を読んでみてください。
初心者歓迎!手と目で覚える正規表現入門・その1「さまざまな形式の電話番号を検索しよう」 - Qiita
Ruby 2.4ではTimeとDateTimeの不具合が修正されましたが、TimeとDateTimeって何が違うの?という方はこちらの記事が参考になるかもしれません。