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

今週は String 周りでメソッドを追加するチケットの話などがありました。

[Feature #18564] Add Exception#detailed_message

class MyClass < StandardError
  def message = "my error!"
  def detailed_message(highlight: false, **opt)
    super + "\nThis is\nan additional\nmessage"
  end
end

raise MyClass
$ ./ruby test.rb
test.rb:8:in `<main>': my error! (MyClass)
This is
an additional
message
  • Exception#detailed_message(highlight: false) は内部で Exception#message を呼び出し highlight: true だとエスケープシーケンスでハイライトされる
e = RuntimeError.new("my error!")
p e.detailed_message                  #=> "my error! (RuntimeError)"
p e.detailed_message(highlight: true) #=> "\e[1mmy error! (\e[1;4mRuntimeError\e[m\e[1m)\e[m"

[Feature #13110] Byte-based operations for String

  • String にバイトベースの操作をするメソッドを追加するチケット
s = "あああいいいあああ"
p s.byteindex(/ああ/, 4) #=> 18
x, y = Regexp.last_match.byteoffset(0) #=> [18, 24]
s.bytesplice(x...y, "おおお")
p s #=> "あああいいいおおおあ"
lexington:ruby$ cat bench.rb
require "benchmark"

s = File.read("README.ja.md") * 10

Benchmark.bmbm do |x|
  x.report("index") do
    pos = 0
    n = 0
    loop {
      break unless s.index(/\p{Han}/, pos)
      n += 1
      _, pos = Regexp.last_match.offset(0)
    }
  end
  x.report("byteindex") do
    pos = 0
    n = 0
    loop {
      break unless s.byteindex(/\p{Han}/, pos)
      n += 1
      _, pos = Regexp.last_match.byteoffset(0)
    }
  end
end
lexington:ruby$ ./ruby bench.rb
Rehearsal ---------------------------------------------
index       1.060000   0.010000   1.070000 (  1.116932)
byteindex   0.000000   0.010000   0.010000 (  0.004501)
------------------------------------ total: 1.080000sec

                user     system      total        real
index       1.050000   0.000000   1.050000 (  1.080099)
byteindex   0.000000   0.000000   0.000000 (  0.003814)

[Feature #18563] Add "graphemes" and "each_grapheme aliases

  • String#each_grapheme_cluster メソッドのエイリアスとして #graphemes#each_grapheme を追加するチケット
  • 他の言語でも graphemes という名前になっている事が多いらしい

    • JavaScript/TypeScript grapheme-splitter library: splitGraphemes
    • PHP: grapheme_extract
    • Zig ziglyph library: GraphemeIterator
    • Golang uniseg library: NewGraphemes
    • Matlab: splitGraphemes
    • Python grapheme library: grapheme
    • Elixir: graphemes
    • Crystal uni_text_seg library: graphemes
    • Nim nim-graphemes library: graphemes
    • Rust unicode-segmentation library: graphemes
  • 以下、matz のコメント

https://bugs.ruby-lang.org/issues/13780#note-10

grapheme sounds like an element in the grapheme cluster. How about each_grapheme_cluster? If everyone gets used to the grapheme as an alias of grapheme cluster, we'd love to add an alias each_grapheme.

Matz.

[Bug #11064] #singleton_methods for objects with special singleton_class returns an empty array

  • 以下のように nil に特異メソッドを追加すると nil.singleton_methods には追加されてないように見えるというバグ報告
# nil に特異メソッドを追加する
def nil.bla
  42
end

# 以下は動作してるが
nil.bla #=> 42
nil.singleton_method(:bla) #=> #<Method: NilClass#bla>

# 以下は動作していない
nil.singleton_methods #=> []
  • これは nil#singleton_classNilClass を返しているため
    • nil には特異クラスは存在しておらず NilClass がその役割を果たしている
# NilClass を返す
p nil.singleton_class
# => NilClass

# なので nil の特異メソッドは NilClass のインスタンスメソッドとして定義される
def nil.bla
end
p NilClass.instance_methods.include? :bla
# => true