https://docs.ruby-lang.org/en/trunk/NEWS.html を元に Ruby 2.5 の変更内容を調べてみました。
長くなったので3つにわけてます。
いろいろ便利になっていますが、個人的には Hash#slice
, Kernel#pp
が嬉しいです。
- 配列/ハッシュ/Enumerable
- 数値
- 文字列
- IO/ファイル
- Dir.children, Dir.each_child 追加
- Dir.glob :base オプション追加
- Dir.chdir, .open, .new, .mkdir, .rmdir, .empty? が GVL を解放する
- File.open の :newline オプションが指定されたらtextモードになる
- File::TMPFILE オプションで開いたファイルに対する File#path が IOError になる
- File のメソッドのいくつかが GVL を解放する
- File::Stat#atime, #mtime, #ctime が Windows 8以降で小数点以下の秒数をサポート
- File::Stat#ino, File.identical? が Windows 8.1 以降の ReFS 128bit ino をサポート
- File.lutime 追加
- IO.copy_stream が copy_file_range(2) を使って高速化
- IO.pread, IO.pwrite 追加
- IO#write が複数文字列を受けつける
- 例外系
- その他
- Kernel#pp 追加
- Kernel#warn に uplevel オプション追加
- Method#=== 追加
- Module#attr, attr_accessor, attr_reader, attr_writer, define_method, alias_method, undef_method, remove_method が public
- Object#yield_self 追加
- Process.times の精度向上
- Process.last_status 追加
- RubyVM::InstructionSequence#each_child, trace_point 追加
- Struct.new に :keyword_init オプション追加
- Thread.name= が Windows でも有効
- Thread#fetch 追加
- Thread.report_on_exception がデフォルトで true
- Time.at で秒以下の単位を指定可能
- Random.raw_seed が Random.urandom に名前変更
配列/ハッシュ/Enumerable
Array#append, #prepend 追加
append, prepend は push, unshift と同じなんですが、同じことをするにも複数の名前があるってのがRubyっぽいですね。
a = [1, 2, 3] a.append 4 a #=> [1, 2, 3, 4] a.prepend 0 a #=> [0, 1, 2, 3, 4]
Enumerable#any?, #all?, #none?, #one? が引数で判定
今まではブロックの評価結果が真かどうかで判定していたのですが、単純な値の比較の場合は引数で与えられるようになりました。
引数を与えた場合は 引数 === 要素
の結果が真かどうかで判定されます。
[0, 1, 2].any?{|n| n == 1} #=> true [0, 1, 2].any?(1) #=> true
Hash#transform_keys, transform_keys! 追加
ハッシュ中の値を変換する transform_values メソッドは 2.4 からありましたが、キーを変更する transform_keys メソッドが追加されました。
{a: 1, b: 2, c: 3}.transform_keys(&:upcase) #=> {A: 1, B: 2, C: 3} {a: 1, b: 2, c: 3}.transform_values{|v| v*2} #=> {a: 2, b: 4, c: 6} ←これは 2.4 から
Hash#slice 追加
Hashオブジェクトの指定したキーから成る新たなHashオブジェクトを返します。
h = {a: 1, b: 2, c: 3} h.slice(:a, :c) #=> {a: 1, c: 3}
数値
Integer.sqrt 追加
平方根を整数で返す。
Integer.sqrt(9) #=> 3 Integer.sqrt(15) #=> 3 Integer.sqrt(16) #=> 4
Integer#allbits?, #anybits?, #nobits? 追加
すべてのビットが立っている? いずれかのビットが立っている? いずれのビットも立っていない? を調べるメソッドです。
11.allbits?(10) #=> true 11.allbits?(12) #=> false 11.anybits?(12) #=> true 11.anybits?(4) #=> false 11.nobits?(4) #=> true 11.nobits?(4) #=> false
Integer#pow 追加
2.pow(5) #=> 32 2**5 と同じ 2.pow(5, 7) #=> 4 2**5%7 と同じ
Integer#round, floor, ceil, truncate が整数を返す
# Ruby 2.4 123.round(1) #=> 123.0 123.round(-1) #=> 120 # Ruby 2.5 123.round(1) #=> 123 123.round(-1) #=> 120
Numeric: coerce内の例外
数値とオブジェクトを比較した時に、coerce メソッドが呼ばれてるんですが、その際に発生した例外が ArgumentError となって隠蔽されてしまっていたのですが、そのまま例外が上がるようになりました。
class A def coerce(other) raise 'hoge' end end # Ruby 2.4 1 < A.new #=> in `<': comparison of Integer with A failed (ArgumentError) # Ruby 2.5 1 < A.new #=> in `coerce': hoge (RuntimeError)
Range: <=>内の例外
これも同上です。
class A def <=>(x) raise 'hoge' end end # Ruby 2.4 Range.new(A.new, A.new) #=> in `initialize': bad value for range (ArgumentError) # Ruby 2.5 Range.new(A.new, A.new) #=> in `<=>': hoge (RuntimeError)
文字列
String#delete_prefix, delete_suffix, delete_prefix!, delete_suffix! 追加
"abcdefg".delete_prefix("abc") #=> "defg" "abcdefg".delete_suffix("efg") #=> "abcd"
String#grapheme_clusters, each_grapheme_cluster 追加
Unicodeの合成文字単位で処理
gaga = "がが" # 1文字目は「か」と「゙」の合成文字 gaga.chars #=> ["か", "゙", "が"] gaga.grapheme_clusters #=> ["が", "が"]
String#undump 追加
String#dump の逆を行うメソッドです。
"a\nあ\t".dump #=> "\"a\\n\\u3042\\t\"" "a\nあ\t".dump.undump #=> "a\nあ\t"
String#casecmp, casecmp? が例外を発生しない
# Ruby 2.4 "hoge".casecmp(123) #=> TypeError # Ruby 2.5 "hoge".casecmp(123) #=> nil
String#start_with? が正規表現でも指定可
"123abc".start_with?("123") #=> true "123abc".start_with?(/\d/) #=> true
IO/ファイル
Dir.children, Dir.each_child 追加
ディレクトリ内のエントリを .
, ..
を除いて返すメソッドが追加されました。
Dir.entries("/tmp/x") #=> ["..", "abc", "."] Dir.children("/tmp/x") #=> ["abc"]
Dir.foreach("/tmp/x"){...} # 「.」「..」を含む Dir.each_child("/tmp/x"){...} # 「.」「..」を含まない
Dir.glob :base オプション追加
Dir.glob にカレントディレクトリの代わりのパスを指定できるオプションが追加されました。
Dir.glob("/tmp/x/*") #=> ["/tmp/x/abc"] Dir.glob("*", base: "/tmp/x") #=> ["abc"]
Dir.chdir, .open, .new, .mkdir, .rmdir, .empty? が GVL を解放する
マルチスレッドでの実行性能があがるかもしれません。
File.open の :newline オプションが指定されたらtextモードになる
今まではtextモードを指定しない場合は :newline オプションを指定しても効果がありませんでしたが、:newline オプションを指定すると自動的にtextモードになるようになったようです。
File.write("hoge", "a\r\n") # Ruby 2.4 File.open("hoge", "rt", newline: :universal).gets #=> "a\n" File.open("hoge", newline: :universal).gets #=> "a\r\n" # Ruby 2.5 File.open("hoge", "rt", newline: :universal).gets #=> "a\n" File.open("hoge", newline: :universal).gets #=> "a\n"
この :newline オプションはドキュメントに載っていないようですが、newline: :universal
は universal_newline: true
と同じみたいです。
File::TMPFILE オプションで開いたファイルに対する File#path が IOError になる
File::TMPFILE は open(2) の O_TPMFILE と同じです。このオプションは指定したディレクトリに名前無しファイルを作成するためのオプションなのでファイル名はありませんが、今までは #path でディレクトリ名が返ってきていました。2.5 では IOError になります。
# Ruby 2.4 File.open("/tmp", File::WRONLY|File::TMPFILE).path #=> "/tmp" # Ruby 2.5 File.open("/tmp", File::WRONLY|File::TMPFILE).path #=> File is unnamed (TMPFILE?) (IOError)
File のメソッドのいくつかが GVL を解放する
File.rename, File.readable?, File.readable_real?, File.writable?, File.writable_real?, File.executable?, File.executable_real?, File.mkfifo, File.readlink, File.truncate, File#truncate, File.chmod, File.lchmod, File.chown, File.lchown, File.unlink, File.utime, File.stat, File.lstat, File.exist?, その他 stat系メソッド
マルチスレッドでの実行性能があがるかもしれません。
File::Stat#atime, #mtime, #ctime が Windows 8以降で小数点以下の秒数をサポート
Windows 使ってないのでわかりません。Linux では以前から取得できました。
File.stat("hoge").mtime.strftime("%F %T.%N") #=> "2017-12-26 01:37:26.252224601"
File::Stat#ino, File.identical? が Windows 8.1 以降の ReFS 128bit ino をサポート
Windows 使ってないので何のことだかわかりません…
File.lutime 追加
リンク先ではなくリンクファイルそのもののタイムスタンプを更新するメソッドです。lutimes(3) 関数を使ってるようです。
IO.copy_stream が copy_file_range(2) を使って高速化
最近のLinuxにはcopy_file_rangeなんてシステムコールがあるんですな。カーネルレベルでファイルのコピーをして、ユーザー空間でデータを転送しないで済むので、オーバーヘッドがなくて速いらしいです。
IO.pread, IO.pwrite 追加
ファイルの特定の位置から read, write するメソッド追加。POSIXの同名システムコールと同じ。 ファイルポインタは変更されません。
File.open("/tmp/x/abc") do |f| f.read(5) #=> "abcde" f.pread(10, 3) #=> "defghijklm" f.read(5) #=> "fghij" end
IO#write が複数文字列を受けつける
引数に複数の文字列を指定した場合は writev(2) を使うので、事前に文字列を結合するよりも効率が良さそうです。
$stdout.write('abc', 'def', 'ghi')
例外系
Exception#full_message
例外を rescue しなければ標準エラー出力にエラー内容が出力されますが、それと同じ文字列を取り出すことが出来ます。
class A def initialize raise 'hoge' end end A.new
% ruby a.rb Traceback (most recent call last): 2: from a.rb:7:in `<main>' 1: from a.rb:7:in `new' a.rb:3:in `initialize': hoge (RuntimeError)
class A def initialize raise 'hoge' end end begin A.new rescue => e p e.full_message end
% ruby a.rb "\e[1mTraceback \e[m(most recent call last):\n\t2: from a.rb:8:in `<main>'\n\t1: from a.rb:8:in `new'\na.rb:3:in `initialize': \e[1mhoge (\e[4;1mRuntimeError\e[m\e[1m)\n\e[m"
FrozenError 例外追加
frozen オブジェクトを更新するような操作で発生します。今までは RuntimeError が発生してました。 FrozenError は RuntimeError のサブクラスなので、通常はプログラムを変更する必要はないはずです。
# Ruby 2.4 "abc".freeze.upcase! #=> can't modify frozen String (RuntimeError) # Ruby 2.5 "abc".freeze.upcase! #=> can't modify frozen String (FrozenError)
IOError のメッセージ "closed stream" が "stream closed in another thread" に変更
# Ruby 2.4 $stdin.close; $stdin.gets #=> closed stream (IOError) Thread.new{$stdin.close}; $stdin.gets #=> stream closed (IOError) # Ruby 2.5 $stdin.close; $stdin.gets #=> closed stream (IOError) Thread.new{$stdin.close}; $stdin.gets #=> stream closed in another thread (IOError)
KeyError#receiver, key 追加
例外が発生したオブジェクトとキーが例外に保持されるようになりました。
begin hash = {a: 123} hash.fetch(:b) rescue KeyError => e e.receiver #=> {a: 123} e.key #=> :b end
その他
Kernel#pp 追加
require 'pp'
しなくても pp メソッドが使えるようになりました。便利。
Kernel#warn に uplevel オプション追加
uplevel を指定すると warn が発生したファイル名と行番号を出力します。
def a warn('a', uplevel: 1) # 1つ上の階層のファイル名と行番号を表示 end def b a end def c b end c #=> a.rb:5: warning: a
Method#=== 追加
Method#call と同じです。Proc#=== が Proc#call であるのに合わせたようです。
Module#attr, attr_accessor, attr_reader, attr_writer, define_method, alias_method, undef_method, remove_method が public
今まで Klass.class_eval{define_method(:hoge){}}
みたいにしないといけなかったのが、Klass.define_method(:hoge){}
と書けるようになりました。
Object#yield_self 追加
# tap は前からある (1.8 ?) 123.tap{|x| x*2} #=> 123 # 2.5 から 123.yield_self{|x| x*2} #=> 246
Process.times の精度向上
# Ruby 2.4 % ruby -e 'p Process.times' #<struct Process::Tms utime=0.06, stime=0.01, cutime=0.0, cstime=0.0> # Ruby 2.5 ruby -e 'p Process.times' #<struct Process::Tms utime=0.071065, stime=0.015792, cutime=0.0, cstime=0.0>
Process.last_status 追加
$?
と同じ
RubyVM::InstructionSequence#each_child, trace_point 追加
よく知らないのでパス…
Struct.new に :keyword_init オプション追加
クラス生成時に :keyword_init を指定することでオブジェクト生成時にキーワード引数で値を設定することができます。
Hoge = Struct.new(:a, :b) Hoge.new(123, 456) #=> #<struct Hoge a=123, b=456> Hoge = Struct.new(:a, :b, keyword_init: true) Hoge.new(a: 123, b: 456) #=> #<struct Hoge a=123, b=456>
Thread.name= が Windows でも有効
スレッドに名前をつけることができる機能が Ruby 2.3からあったんですが、Windows 10 でも有効になったらしいです。Windows 使ってないので未確認です。
Thread.new do Thread.current.name = "hogehoge" end
% ps -L -o pid,lwp,comm -p 14655 PID LWP COMMAND 14655 14655 ruby 14655 14656 ruby-timer-thr 14655 14657 hogehoge
Thread#fetch 追加
key が無い場合に Hash#[key]
は nil を返して、Hash#fetch(key)
は例外を発生させるんですが、それと同じような振る舞いをする Thread#fetch
が追加されました。
Thread.current[:hoge] #=> nil Thread.current.fetch(:hoge) #=> KeyError "key not found: hoge" Thread.current.fetch(:hoge, 123) #=> 123
Thread.report_on_exception がデフォルトで true
2.4 ではスレッドが例外が終了してもデフォルトでは何も出力されませんでした。
Thread.report_on_exception=true
とすることで標準エラー出力に出力されましたが、2.5 ではこれがデフォルトで true になってます。
thr = Thread.new{ raise "hoge" } # この時点でエラーが出力される thr.join #=> 例外発生 RuntimeError: hoge
Time.at で秒以下の単位を指定可能
Time.at(1511056368, 123456).nsec #=> 123456000 Time.at(1511056368, 123456.789).nsec #=> 123456789 Time.at(1511056368, 123456789, :nsec).nsec #=> 123456789