2021/07/29 今週の気になった bugs.ruby のチケット

Integer.try_convert の追加や Struct.new.keyword_init? の追加などがありました。

[Feature #10473] Change Date#to_datetime to use local time

require "date"

# ローカルのタイムゾーンを使用しない
p Date.new(2014,1,1).to_datetime.to_time
# => 2014-01-01 00:00:00 +0000

# to_date や to_time はローカルのタイムゾーンを参照している
p Date.new(2014,1,1).to_date.to_time
# => 2014-01-01 00:00:00 +0900
p Date.new(2014,1,1).to_time
# => 2014-01-01 00:00:00 +0900
p Time.parse('2021-07-23')
# => 2021-07-23 00:00:00 +0900

p DateTime.parse('2021-07-23')
# => #<DateTime: 2021-07-23T00:00:00+00:00 ((2459419j,0s,0n),+0s,2299161j)>
  • また、 to_datetime でローカルのタイムゾーンを使用するようにすると次の結果が変わってしまう
DateTime.parse(d.to_s) == d.to_datetime
  • DateTime 自体、互換性のために残されているので非互換にするのは望ましくない、との事
  • 互換性を保ちつつ対応する場合は to_datetime に使用するタイムゾーンを指定するようにするのはどうか、と提案されている

[Bug #18018] Float#floor / truncate sometimes result that is too small.

p 291.4.floor(1) # => 291.4 (ok)
p 291.4.floor(2) # => 291.39 (not ok)
p 291.4.floor(3) # => 291.4 (ok)
p 291.4.floor(4) # => 291.4 (ok)
p 291.4.floor(5) # => 291.39999 (not ok)
p 291.4.floor(6) # => 291.4 (ok)

[Bug #18044] unusual behavior with Arabic string in a Hash

  • アラビア語の文字列を含む Hash でキーを検索すると常に nil が返ってくる、というバグ報告
foo = {"arabic" => "ٱلتَّوْبَة"}
p foo.keys # => ["arabic"]
p foo["arabic"] # => nil
p foo.values_at("arabic") => [nil]
foo.fetch "arabic" # Raises error with - did you mean "arabic" ?
  • どうして…、と思ったら上記の再現コードのキーに U200e が紛れているのが原因だったらしい
    • U200e は Left-to-Right Mark (LRM) と呼ばれる制御文字
  • 実際に Vim でコードを貼り付けるとこんな感じ

  • 制御文字を取り除いたら問題なく動作した
foo = {"arabic" => "ٱلتَّوْبَة"}
p foo.keys # => ["arabic"]
p foo["arabic"] # => "ٱلتَّوْبَة"
p foo.values_at("arabic") => {["ٱلتَّوْبَة"]=>[nil]}

[Feature #18040] Why should foo(1 if true) be an error?

  • foo(1 if true) がエラーになるのはなぜか?というバグ報告
  • パーサ的には 1 if true は式ではなくてステートメントなので現状は意図する挙動だとコメントされている
  • その後は 1 if true が式なのかステートメントなのかで議論が続いている
  • 最終的には『現実的に修正できない』という理由で Reject されている
  • ちなみに p foo((1 if true)) みたいに () でくくると動作する

[Feature #15211] Integer.try_convert

Regexp.try_convert(/re/)      # => /re/
# 失敗したら nil を返す
Regexp.try_convert("re")      # => nil
  • Integer.try_convert は最新版では実装済
p Integer.try_convert(10)
# => 10
p Integer.try_convert("10")
# => nil

# ちなみに内部で to_int を呼び出しているので Float だとこうなる
p Integer.try_convert(1.23)
# => 1

[Feature #18008] keyword_init? method for Struct

  • Struct.newkeyword_init: true したかどうかを判定するメソッドを追加する提案
  • 次のように Struct.new の戻り値に対して判定できる
S1 = Struct.new(:a, :b)
S2 = Struct.new(:a, :b, keyword_init: true)
S3 = Struct.new(:a, :b, keyword_init: false)

# 指定しなかった場合は nil を返す
pp S1.keyword_init?  # => nil
pp S2.keyword_init?  # => true
pp S3.keyword_init?  # => false
  • これは既に最新版で対応済み

[Feature #17724] Make the pin operator support instance/class/global variables

  • 元々はパターンマッチで ^@n を使用すると『ローカル変数かメソッドを期待する』とエラーメッセージになっていた
# ローカル変数かメソッドを期待する、というエラーメッセージが出力される
case 10
in ^@n
end
# => error: syntax error, unexpected instance variable, expecting local variable or method
# in ^@n
#     ^~
  • しかし、メソッドの場合でもエラーになるのでエラーメッセージを修正しよう、という内容のチケットだった
def n = 10

# メソッドを使用してもエラーになる
case 10
in ^n
end
# => error: n: no such local variable
  • これの対応として ^インスタンス/クラス/グローバル変数 が使えるように対応された
class X
  @a  = 1
  @@b = 2
  $c  = 3

  # インスタンス、クラス、グローバル変数が使える
  case 1
  in ^@a
  in ^@@b
  in ^$c
  end
end