Ruby 2.5

ながのRubyの会 #1

2017/12/02

とみたまさひろ

自己紹介

Rubyは毎年クリスマスにバージョンアップ

Ruby 2.5 2017/12/25

Ruby 2.5 新機能&変更

https://docs.ruby-lang.org/en/trunk/NEWS.html

ruby 2.5.0preview1 (2017-10-10 trunk 60153)

言語仕様

トップレベル定数参照

class Foo
end
class Bar
end

# Ruby 2.4
Foo::Bar  #=> Bar

# Ruby 2.5
Foo::Bar  #=> NameError 例外

rescue/else/ensure 節

def foo
  begin
     ...
  rescue
     ...
  end
end

これは従来から簡潔に書けた↓

def foo
  ...
rescue
  ...
end

2.5からはdo〜endブロックでも

array.each do
  begin
    ...
  rescue
    ...
  end
end

簡潔に書ける↓

array.each do
  ...
rescue
  ...
end

文字列内式のRefinement

class A; def to_s; "a"; end; end
module B
  refine A do
    def to_s; "b"; end
  end
end
class C
  using B
  def x
    A.new.to_s  #=> "b"
    "#{A.new}"  #=> "b" 2.4 では "a"
  end
end

Unicode バージョン 10.0.0

Ruby 2.4 は 9.0.0

組み込みライブラリ

Array#append, #prepend 追加

a = [1, 2, 3]
a.append 4
a #=> [1, 2, 3, 4]
a.prepend 0
a #=> [0, 1, 2, 3, 4]

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("/tmp/x/*")           #=> ["/tmp/x/abc"]

Dir.glob("*", base: "/tmp/x")  #=> ["abc"]

Hash#transform_keys, 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 から

IO.pread, IO.pwrite 追加

seek & read (write) をアトミックに行う

File.open("/tmp/x/abc") do |f|
  f.read          #=> "abcdefghijklmnopqrstuvwxyz"
  f.pread(10, 3)  #=> "defghijklm"
end

Integer.sqrt 追加

整数の平方根

Integer.sqrt(9)  #=> 3
Integer.sqrt(15) #=> 3
Integer.sqrt(16) #=> 4

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内の例外

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)

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>

String#delete_prefix, delete_suffix, delete_prefix!, delete_suffix! 追加

"abcdefg".delete_prefix("abc") #=> "defg"
"abcdefg".delete_suffix("efg") #=> "abcd"

String#casecmp, casecmp? が例外を発生しない

# Ruby 2.4
"hoge".casecmp(123)  #=> TypeError

# Ruby 2.5
"hoge".casecmp(123)  #=> nil

String#grapheme_clusters, each_grapheme_cluster 追加

Unicodeの合成文字単位で処理

gaga = "がが"           # 1文字目は「か」と「゙」の合成文字
gaga.chars              #=> ["か", "゙", "が"]
gaga.grapheme_clusters  #=> ["が", "が"]

Thread#fetch 追加

スレッド固有パラメータの取り出しをHash風に

Thread.current[:hoge]            #=> nil
Thread.current.fetch(:hoge)      #=> KeyError "key not found: hoge"
Thread.current.fetch(:hoge, 123) #=> 123

Thread.name=

Ruby 2.3からあった。Windows 10 でもスレッドに名前をつけられるようになったらしい。

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

Time.at で秒以下の単位を指定

Time.at(1511056368, 123456).nsec            #=> 123456000
Time.at(1511056368, 123456.789).nsec        #=> 123456789
Time.at(1511056368, 123456789, :nsec).nsec  #=> 123456789

KeyError#receiver, key 追加

例外が発生したオブジェクトとキーを返す

begin
  hash = {a: 123}
  hash.fetch(:b)
rescue KeyError => e
  e.receiver #=> {a: 123}
  e.key      #=> :b
end

BasicSocket#read_nonblock, #write_nonblock が O_NONBLOCK をセットしない

require 'io/nonblock'
s = TCPSocket.new("127.0.0.1", 80)
s.nonblock?        #=> false
s.read_nonblock(10)

# Ruby 2.4
s.nonblock?        #=> true

# Ruby 2.5
s.nonblock?        #=> false

Random.raw_seed が Random.urandom に名前変更

Socket::Ifaddr#vhid 追加

BSD?

標準添付ライブラリ

Bundler Gem 標準添付

ERB#result_with_hash 追加

require "erb"
name = "tmtms"
ERB.new("Hello <%=name%>").result #=> "Hello tmtms"
require "erb"
ERB.new("Hello <%=name%>").result_with_hash(name: "tmtms")

ERB: trim 指定時 CR LF を trim

# Ruby 2.4
require 'erb'
ERB.new("<%=123%>\r\n", nil, 1).result #=> "123\r\n"

# Ruby 2.5
require 'erb'
ERB.new("<%=123%>\r\n", nil, 1).result #=> "123"

Net::HTTP ステータス追加

    '102' => Net::HTTPProcessing,
    '208' => Net::HTTPAlreadyReported,
    '421' => Net::HTTPMisdirectedRequest,
    '451' => Net::HTTPUnavailableForLegalReasons,
    '506' => Net::HTTPVariantAlsoNegotiates,
    '508' => Net::HTTPLoopDetected,
    '510' => Net::HTTPNotExtended,

Net::HTTP::STATUS_CODES 追加

require 'net/http/status'
Net::HTTP::STATUS_CODES
#=> {100=>"Continue", 101=>"Switching Protocols",
#    102=>"Processing", 200=>"OK", 201=>"Created",
#    202=>"Accepted", 203=>"Non-Authoritative Information",
#    204=>"No Content", 205=>"Reset Content", 206=>"Partial Content",
#    ...}

Net::HTTP.new に no_proxy 引数追加

7番目の引数

no_proxy = "example.com,example.net:8080"
HTTP.new(address, port, proxy_addr, proxy_port,
         proxy_user, proxy_pass, no_proxy)

Net::HTTP: http_proxy環境変数を使用

Net::HTTP: http_proxy環境変数の認証情報を使用

http://user:password@proxy:port/

Windowsを除く

RbConfig::SIZE_OF, RbConfig::LIMITS 追加

require "rbconfig/sizeof"
RbConfig::SIZEOF
#=> {"int"=>4, "short"=>2, "long"=>8, "long long"=>8,
#    "__int128"=>16, "off_t"=>8, "void*"=>8, "float"=>4,
#    "double"=>8, "time_t"=>8, "clock_t"=>8, "size_t"=>8,
#    ...
RbConfig::LIMITS
#=> {"FIXNUM_MAX"=>4611686018427387903,
#    "FIXNUM_MIN"=>-4611686018427387904, "CHAR_MAX"=>127,
#    "CHAR_MIN"=>-128, "SCHAR_MAX"=>127, "SCHAR_MIN"=>-128,
#    "UCHAR_MAX"=>255, "WCHAR_MAX"=>2147483647,
#    ...

Ripper#state, Ripper::EXPR_BEG 追加

Set#to_s, #=== 追加

s = Set.new([1, 2, 3])
# Ruby 2.4
s.to_s   #=> "#<Set:0x00560b825243b8>"
s === 2  #=> false
s === s  #=> true

# Ruby 2.5
s.to_s   #=> "#<Set: {1, 2, 3}>"
s === 2  #=> true    Set#include? と同じ
s === s  #=> false

WEBrick: Serve Name Indication (SNI) サポート

標準ライブラリから mathn 削除

もうあまり使われてない?

標準ライブラリから ubygems 削除

rubygems を -rubygems コマンドラインオプションで使用できるようにするためのもの。

rubygems は標準で使用されるためもう不要。

その他

バックトレースの順番

標準エラー出力が端末の場合に表示が逆順 (EXPERIMENTAL)

# Ruby 2.4
% ruby /tmp/a.rb
/tmp/a.rb:8:in `c': unhandled exception
	from /tmp/a.rb:5:in `b'
	from /tmp/a.rb:2:in `a'
	from /tmp/a.rb:10:in `<main>'
# Ruby 2.5
% ruby /tmp/a.rb
Traceback (most recent call last):
	3: from /tmp/a.rb:10:in `<main>'
	2: from /tmp/a.rb:2:in `a'
	1: from /tmp/a.rb:5:in `b'
/tmp/a.rb:8:in `c': unhandled exception

バックトレースの順番

標準エラー出力が端末でなければ従来通り

% ruby /tmp/a.rb
Traceback (most recent call last):
	3: from /tmp/a.rb:10:in `<main>'
	2: from /tmp/a.rb:2:in `a'
	1: from /tmp/a.rb:5:in `b'
/tmp/a.rb:8:in `c': unhandled exception
% ruby /tmp/a.rb 2>&1 | cat
/tmp/a.rb:8:in `c': unhandled exception
	from /tmp/a.rb:5:in `b'
	from /tmp/a.rb:2:in `a'
	from /tmp/a.rb:10:in `<main>'

configure で拡張ライブラリを強制

  • 通常は環境に応じて自動判別
  • --with-ext オプションで強制できる
  • コンパイルできる環境でない場合は make でエラー
% ./configure --with-ext=openssl,+
% make
...
*** Following extensions are not compiled:
openssl:
	Could not be configured. It will not be installed.
	/misc/tmp/ruby-2.5.0-preview1/ext/openssl/extconf.rb:94: OpenSSL library could not be found. You might want to use --with-openssl-dir=<dir> option to specify the prefix where OpenSSL is installed.
	Check ext/openssl/mkmf.log for more details.
*** Fix the problems, then remove these directories and try again if you want.
exts.mk:1853: ターゲット 'note' のレシピで失敗しました
make[1]: *** [note] エラー 1
make[1]: ディレクトリ '/misc/tmp/ruby-2.5.0-preview1' から出ます
uncommon.mk:236: ターゲット 'build-ext' のレシピで失敗しました
make: *** [build-ext] エラー 2

以上