2022/08/11 今回の気になった bugs.ruby のチケット

今週は **nil を許容する提案などがありました。

[Feature #18959] Handle gracefully nil kwargs eg. **nil

  • 以下のように『値が存在している時のみ』 Hash に値を追加したいケースがある
{
  some: 'value',
  **({ id: id } if id.present?),   # id の値が存在していれば `{ id: id }` を Has に定義したい
}
  • しかし以下のように **nil は呼び出すことができないので上記のコードは意図する挙動をしない
def qwe(a: 1) end

qwe(**nil) #=> fails with `no implicit conversion of nil into Hash (TypeError)` error

{ a:1, **nil } #=>  fails with `no implicit conversion of nil into Hash (TypeError)` error
  • 以下のようにして回避する事もできるが冗長である
h = { some: 'value' }
h[:id] = id if id.present?
h
# または以下
{
  some: 'value',
  **(id.present? ? { id: id } : {}),
}
  • *nil は動作しており、以下のように書くことができるので **nil も許容してほしいという提案
    • ちなみに *nil[] になる
    • Rails でよく使うらしい
content_tag :div, class: [*('is-hero' if hero), *('is-search-page' if search_page)].presence
  • ちなみに *obj**objobj.to_aobj.to_hash が内部で呼び出される
class X
  def to_a
    ["default"]
  end

  def to_hash
    { default: 0 }
  end
end

x = X.new
pp(*x)    # => "default"
pp(**x)   # => {:default=>0}
  • なので以下のように nil.to_hash を定義して対応する事は可能
# to_hash を定義しておく
def nil.to_hash = {}

def qwe(a: 1) end

qwe(**nil) #=> OK

{ a:1, **nil } #=> OK
h = {"y"=>"!"}
"xyz".sub(/y/, h) #=> "x!z"

h = nil
"xyz".sub(/y/, h) #=> TypeError (no implicit conversion of nil into String)

def nil.to_hash; {}; end
"xyz".sub(/y/, h) #=> "xz" (segfault in 2.6!)

[Feature #18961] Introduce support for pattern matching all elements of an array

  • 以下のように配列の全ての要素が特定のパータンの場合のパターンマッチを書きたいことがある
class Robot; end
class User; end

case [User.new, User.new]
in [User, *] => u if u.all? { _1 in User }
  puts 'array of users!'
end
# => array of users!

case [User.new, User.new, Robot.new]
in [User, *] => u if u.all? { _1 in User }
  puts 'array of users!'
end
# => Error: guard clause does not return true (NoMatchingPatternError)
  • これを以下のような構文で対応させいたいという提案
case [User.new, User.new]
in [User*]
  puts 'array of users!'
end
  • これだけだと u.all? { _1 in User } でよさそうに見えるんですがパターンの一部だけに記述したい、みたいな要求はありそう

[Bug #18960] Module#using raises RuntimeError when called at toplevel from wrapped script

# using.rb
using Module.new
# OK
load "./using.rb"

# NG
load "./using.rb", true
# raises RuntimeError (main.using is permitted only at toplevel)
  • こんなバグが

マージされた機能

[Bug #18946] Time#to_date returns incorrect date

  • 以下のように 1499-12-27TimeDate に変換すると 1499-12-18 になるというバグ報告
require "time"

time = Time.local(1499, 12, 27)
pp time
# => 1499-12-27 00:00:00 +091859

# 変換した日付は -9日されている
pp time.to_date
# => #<Date: 1499-12-18 ((2268919j,0s,0n),+0s,2299161j)>
# 1582-10-15 の場合
require "time"

time = Time.local(1582, 10, 15)
pp time
# => 1582-10-15 00:00:00 +091859

# 変換前と変換後は同じ日付
pp time.to_date
# => #<Date: 1582-10-15 ((2299161j,0s,0n),+0s,2299161j)>
# 1582-10-15 の場合
require "time"

time = Time.local(1582, 10, 14)
pp time
# => 1582-10-14 00:00:00 +091859

# この日付だと変換後はユリウス暦の日付に置き換わっている
pp time.to_date
# => #<Date: 1582-10-04 ((2299160j,0s,0n),+0s,2299161j)>
require "date"

p Time.local(1499, 12, 27).to_datetime
# => #<DateTime: 1499-12-27T00:00:00-07:52 ((2268928j,28378s,0n),-28378s,2299161j)>

p Time.local(1499, 12, 27).to_date
# => #<Date: 1499-12-18 ((2268919j,0s,0n),+0s,2299161j)>
require "date"

p RUBY_VERSION   # => "3.2.0"

p Time.local(1499, 12, 27).to_datetime
# => #<DateTime: 1499-12-18T00:00:00+09:18 ((2268918j,52861s,0n),+33539s,2299161j)>

p Time.local(1499, 12, 27).to_date
# => #<Date: 1499-12-18 ((2268919j,0s,0n),+0s,2299161j)>