2021/09/16 今回の気になった bugs.ruby のチケット
今週は { a: a, b: b }
を { a:, b: }
とかけるショートハンドが入りました。
[Feature #18168] Add ActiveSupport deep_transform_values to Ruby
- ActiveSupport の
#deep_transform_values
を Ruby 本体に追加する提案
require "active_support/all" hash = { person: { name: 'Rob', age: '28' } } # ネストした Hash すべてに value.to_s.upcase を適用させる pp hash.deep_transform_values { |value| value.to_s.upcase } # => {:person=>{:name=>"ROB", :age=>"28"}}
- 再帰的に参照している場合は
SystemStackError
になっちゃうみたいですね
require "active_support/all" hash = { a: 1 } hash[:hash] = hash # 再帰的に参照してる場合はエラー # error: stack level too deep (SystemStackError) pp hash.deep_transform_values{ |value| value.to_s.upcase }
- チケットの内容が虚無なのでこのままだと流れていきそうな雰囲気
[Bug #18160] IndexError raised from MatchData#{offset,begin,end} does not keep the encoding of the argument
MatchData#{offset,begin,end}
で発生したIndexError
がエンコーディングを保持してないバグ報告
pp RUBY_VERSION # => "3.0.2" m = /.*/.match("foo") m.offset("\u{3042}") rescue p $!.message # => "undefined group name reference: \xE3\x81\x82"
- これは最新版では修正されて正しい意図する文字コードで出力される
pp RUBY_VERSION # => "3.1.0" m = /.*/.match("foo") m.offset("\u{3042}") rescue p $!.message # => "undefined group name reference: あ"
- 便利
[Feature #14579] Hash value omission
{ x:, y: }
を{ x: x, y: y }
のショートハンドにする提案- JavaScript にあるような奴
- これ以外にも似たような提案は無限にされていたけど matz を説得できずに長年入っていなかった
- 先日の RubyKaigi の感想戦でこれに関する議論がされて、matz を説得して無事に accepts された :tada:
- これは以下のように
x:
をx: x
に展開するようなシンタックスシュガーになる
name = "homu" age = 14 # { name: name, age: age } のシンタックスシュガー { name:, age: } # => {:name=>"homu", :age=>14} # 一部だけ値を割り当てることもできる { name:, age: 16 } # => {:name=>"homu", :age=>16} def tokyo "東京" end # メソッド呼び出しも可能 { tokyo: } # => {:tokyo=>"東京"} def user(name:, age:) { name:, age: } end # user(name: name, age: age) になる user(name:, age:) # 現時点では定数も展開できる X = 42 { X: } # => {:X=>42}
- 現状は
{ X: }
みたいな定数も展開されるがもしかしたらリリース時点でまた仕様が変わっている可能性があるので注意 - ちなみにこれを利用すると
if
変数に対して高速にアクセスする事ができる
def foo(if:) # if はキーワードなので変数にアクセスする時は Binding#local_variable_get を使う必要があった if_ = binding.local_variable_get(:if) # このショートハンドを使うとこうかけるようになる if_ = {if:}[:if] end
[Feature #17355] Using same set of names in or-patterns (pattern matching with Foo(x) | Bar(x))
- 以下のようにパターンマッチで束縛名が重複していると今はエラーになる
case [1, 2] in [1, a] | [a, 3] then a end
- これをエラーではなくて各パターンごとで束縛できるようにする提案
- これがあると例えば以下のようなコードが
def user_email(user) case user in User(email:) then email in Admin(email:) then email in Moderator(email:) then email end end
- 以下のように1つのパターンで定義する事ができる
def user_email(user) case user in User(email:) | Admin(email:) | Moderator(email:) then email end end
- 今はまだ実装中?らしい
- これは普通にほしいなあ
[Misc #18150] Proposal: Deprecate leading zero syntax to declare octals, since it's extremely confusing (and Python 3 removed it too)
- 8進数リテラルについて議論するチケット
- 現状の Ruby は
0
が先頭に付いていると8進数として解釈される012 # => 10
- また
0o
が付いている場合も8進数として解釈される0o12 # => 10
- なのでうっかり次のようなコードを書いてしまうとこれはエラーになってしまう
# "2021-09-01" のつもりで 09 と書いてしまった # これはエラーになる START_DATE = Date.new(2021, 09, 01) # error: Invalid octal digit
- このようなケースがあるので
0
を使った8進数リテラルを Ruby 3.x では非推奨にし、Ruby 4.0 では0011 => # 11
にしよう、という提案 - ちなみに先頭
0
が8進数リテラルになっている言語はJavaScript
Go
Java
C
がある - 逆にそうでない言語は
Rust
とElixir
とのこと - また
Python3+
はエラーになる - 個人的には 8進数リテラルを使ったことがないので気にはならないけど、既存のコードを非互換にしてまで入れる必要があるのかと言われると難しいところ
[Feature #18146] Add delete_prefix
and delete_suffix
to Pathname
Pathname
に#delete_prefix
とdelete_suffix
を追加する提案- 元々は RuboCop が誤検知していたのが起因らしい
- https://github.com/rubocop/rubocop-performance/issues/245
Pathname.new("path/to/some/ruby/file.rb").sub(/\.rb\z/, "")
- が
Pathname.new("path/to/some/ruby/file.rb").delete_suffix(".rb")
- を推奨するようになってしまっていたらしい
- 誤検知を本体に実装しようとするアイディアがすごい(褒めてる)
[ruby/un] Add colorize command
- 普通に便利そう
[Feature #16182] Should expr in a, b, c
be allowed or not?
- 元々は Ruby 2.7 時点での
expr in [a, b, c]
という1行 in の[]
を省略できるようにするかどうかを議論するチケット - Ruby 2.7 (3.0) では
[]
や{}
は省略できないようになっていた
# これらはシンタックスエラー { name: "homu" } in name: [1, 2] in a, b
- Ruby 3.1 からはこの条件が緩和されて
[]
や{}
が省略してかけるようになる
# OK { name: "homu" } in name: [1, 2] in a, b
- ちなみに右代入も許容されている
# OK { name: "homu" } => name: [1, 2] => a, b
- また、メソッドの引数に渡す場合は明示的に
()
を付ける必要があるので注意する
# これはエラー puts([1] in String) # () をつけると OK puts(([1] in String))