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

内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。

[Bug #17838] Set#intersect? and enumerables

  • 現状だと Set#intersect? には Set しか渡せないんですがそれを Enumerable を渡せるようにしようという提案っぽい
[1, 2, 3].intersect?(Set[2, 3, 4]) # => true
Set[2, 3, 4].intersection([1, 2, 3]) # => Set[2, 3]
# これを渡せるようにしたいぽい?
Set[2, 3, 4].intersect?([1, 2, 3]) # => ArgumentError

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

内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。

[Feature #17830] Add Integer#previous and Integer#prev

  • Integer#next の逆の Integer#previous を追加する提案
  • 現状は Integer#nextエイリアスとして Integer#succ があり、それの逆の Integer#pred がある
  • Integer#succ <-> Integer#pred はわかりやすいが、 Integer#next <-> Integer#pred は分かりづらいので Integer#next <-> Integer#previous を追加しよう、というモチベーションらしい
  • Integer#pred 自体使ったことなかったけど実際どういうケースで使うんですかね?
  • コメントには (number - 1).times { ... })number.pred.times { ... }) みたいに書くとはかかれていた

[Feature #17837] Add support for Regexp timeouts

# この処理はいつまで経っても終わらない…
/A(B|C+)+D/ =~ "A" + "C" * 100 + "X"

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

内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。

[Bug #9542] Delegator does not delegate protected methods

  • 以下のコードが Ruby 2.0 と 2.1 で差異がある、というバグ報告
require "delegate"
class Cow
  def unprotected_moo
    "mooooo!"
  end

  protected

  def protected_moo
    "guarded mooooo!"
  end
end
my_cow = Cow.new
puts SimpleDelegator.new(my_cow).unprotected_moo
# => "mooooo!"

# protected_moo は protected なのに呼び出すことができる
puts SimpleDelegator.new(my_cow).protected_moo
# 2.0      => "guarded mooooo!"
# 2.1 以降 => undefined method `protected_moo' (NoMethodError)

puts my_cow.unprotected_moo
# => "mooooo!"
puts my_cow.protected_moo
# 2.0      => "guarded mooooo!"
# 2.1 以降 => protected method `protected_moo' called (NoMethodError)
  • これ自体は期待する挙動なので Reject されている
    • そもそもチケット自体が7年前のやつだった

[Bug #17814] inconsistent Array.zip behavior

  • 以下のように Array#zip だとイテレーションが1回余計に呼ばれているというバグ報告
i = 0
# 1 ずつ増えるカウンタ
e = Enumerator.produce { i += 1 }

# 1つ余計にイテレーションが発生する
p [0, 0, 0, 0].zip e
# => [[0, 1], [0, 2], [0, 3], [0, 4]]
p i
# => 5

# Enumerable#zip だと再現しない
p [0, 0, 0, 0].each.zip e
# => [[0, 6], [0, 7], [0, 8], [0, 9]]
p i
# => 9

[Feature #17785] Allow named parameters to be keywords

  • 先週も書いてた キーワード_ という名前で変数参照できるようにしよう、という提案
def check(arg, class:)
  # _ を付けて参照できるようにする
  arg.is_a?(class_)
end

check(42, class: Integer) # => true
  • __params__ みたいな値で受け取れるようにしようという提案があってこれは普通にほしいと思った
def check(arg, class:)
  arg.is_a?(__params__[:class])
end

check(42, class: Integer) # => true
  • ただし、キーワード引数以外の引数をどうするかの課題がある
    • __params__ というよりかは __keyword_params__ みたいな?

[Feature #17718] a method paramaters object that can be pattern matched against

  • 次のようにキーワード引数を一括で返すメソッドを追加する提案
    • パターンマッチで利用することを想定している
def get_perdiem(city: nil, state: nil, zip:nil)

  case parameters_match  # (return an object of the parameters we can pattern match on)
  in {zip: zip}
     find_perdiem_by_zip(zip)
  in {state: s, city: c}
     find_perdiem_by_state_and_city(s, c)
  in { state: s}
     find_perdiem_by_state(s)
  else
     raise 'need combination of zip, city,state'
  end
end
def getParam(**args)
  case args
  in {zip: zip}
    p "zip"
  in {state: s, city: c}
    p "state+city"
  in { state: s}
    p "state"
  else
    raise 'need combination of zip, city, state'
  end
end

[Bug #4443] odd evaluation order in a multiple assignment

def foo
  p :foo
  []
end
def bar
  p :bar
end

# bar -> foo という順に評価される
x, foo[0] = bar, 0
# output:
# :bar
# :foo

# これは foo -> bar という順になる
foo[0] = bar
# output:
# :foo
# :bar
def foo
  p :foo
  []
end
def bar
  p :bar
end

# 最新版だと foo -> bar と評価されるようになった
x, foo[0] = bar, 0
# output:
# :foo
# :bar

[Feature #17398] SyntaxError in endless method

  • 以下のようにエンドレスメソッド定義の本体が statement だった場合にエラーになる、というチケット
# OK
def foo() = puts("bar")

# syntax error, unexpected string literal, expecting `do' or '{' or '('
def hoge() = puts "bar"
# current: SyntaxError
# patch: Allow
def foo() = puts "bar"

# current: SyntaxError
# patch: SyntaxError
private def foo() = puts "bar"

[Feature #15198] Array#intersect?

  • Array#intersect? を追加する提案
  • これは以下の動作と同じ意味
    • Array#&
    • [1, 1, 2, 3] & [3, 1, 4] #=> [1, 3]
(a1 & a2).any?

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

内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。

[Feature #17790] Have a way to clear a String without resetting its capacity

  • 文字列の中身を空にする String#clear メソッドがある
str = "abc"
str.clear
p str     # => ""
  • この String#clear メソッドは capacity を含めて解放する
require "objspace"

str = String.new("homuhomu", capacity: 1024)
puts ObjectSpace.dump(str)
# => {"address":"0x55d9bdae3e80", "type":"STRING", "class":"0x55d9bd96a888", "bytesize":8, "capacity":1024, "value":"homuhomu", "encoding":"UTF-8", "memsize":1065, "flags":{"wb_protected":true}}

# clear 後は capacity も含めて解放されている
str.clear
puts ObjectSpace.dump(str)
# => {"address":"0x55d9bdae3e80", "type":"STRING", "class":"0x55d9bd96a888", "embedded":true, "bytesize":0, "value":"", "encoding":"UTF-8", "memsize":40, "flags":{"wb_protected":true}}
  • これは例えば次のように同じバッファを使い回す時に解放されないほうがよいケースもある
buffer = String.new(encoding: Encoding::BINARY, capacity: 1024)

10.times do
  build_next_packet(buffer)
  udp_socket.send(buffer)
  buffer.clear
end
  • なので clear では capacity を開放しないようにする、という提案
  • 議論が進んでいて clear(shrink: true/false) みたいに clear 時に制御するのがいいんじゃない?みたいなコメントもでてる

[Feature #17785] Allow named parameters to be keywords

  • 次のようにキーワード引数に『言語のキーワード』を指定すると参照するのが難しい
def check(arg, class:)
  # ここで class 引数を使いたいが class はキーワードなので参照できない
  arg.is_a?(class)
end

check(42, class: Integer) # => true
  • これを キーワード_ という名前で変数参照できるようにしよう、という提案
def check(arg, class:)
  # _ を付けて参照できるようにする
  arg.is_a?(class_)
end

check(42, class: Integer) # => true
def check(arg, class:)
  # local_variable_get で動的に変数を取得する
  class_ = binding.local_variable_get(:class)
  arg.is_a?(binding.local_variable_get(:class))
end

check(42, class: Integer) # => true
def check(arg, class:)
  arg.is_a?(\class)
end

# キーワード引数以外も適用可能
def diff(start, \end)
  \end - start
end

[Feature #17786] Proposal: new "ends" keyword

  • 複数の endends という1つのキーワードで定義できるようにしようという提案
def render(scene, image, screenWidth, screenHeight)
  screenHeight.times do |y|
    screenWidth.times do |x|
      color = self.traceRay(....)
      r, g, b = Color.toDrawingColor(color)
      image.set(x, y, StumpyCore::RGBA.from_rgb(r, g, b))
    end
  end
end
  • これを以下のように記述する
def render(scene, image, screenWidth, screenHeight)
  screenHeight.times do |y|
    screenWidth.times do |x|
      color = self.traceRay(....)
      r, g, b = Color.toDrawingColor(color)
      image.set(x, y, StumpyCore::RGBA.from_rgb(r, g, b))
ends
  • いろいろとコメントされているが流石に否定的な内容が目立つ…
  • 例えば次のように途中に ends がある場合に class Aend が不用意に閉じられて SyntaxError になる
class A
  def b
    c do
      d do
        e
  ends     # ここで class A のスコープが閉じられる

  def c
  end
end        # なのでここで SyntaxError になる

jq コマンドを使って Vim で選択した範囲の JSON をフォーマッティングする

書いてみた。

" 選択した範囲を jq コマンドを使ってフォーマッティングする
" 範囲選択しない場合は現在の行を対象とする
command! -range JSONFormatter :<line1>,<line2>!jq .

べんり。
別途 jq 外部コマンドが必要なので注意。

defx.nvim でカーソル移動をするたびに preview ウィンドウを開く

defx.nvim で auto-preview 的なことをやってみた。

augroup my-defx
    autocmd!
    autocmd FileType defx call s:defx_my_settings()
augroup END

function! s:defx_my_settings() abort
    augroup ftplugin-my-denite
        autocmd! * <buffer>
        autocmd CursorMoved <buffer> call defx#call_action("preview", [])
    augroup END
endfunction

単に CursorMoved 時に preview ウィンドウを開くようにしているだけですね。
実際にやってみるとそんなに重くないのでありっちゃありな気がする。

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

内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。

[PR irb #219] Add pry-like show_source command

  • irb に pry のような show_source コマンドが実装された
  • これは以下のようにメソッドなどの定義を表示してくれるコマンド
$ RBENV_VERSION=3.1.0-dev irb
irb(main):001:0> require "erb"
 => true
irb(main):002:0> show_source "ERB#result"

From: /home/worker/.rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/erb.rb:901

  def result(b=new_toplevel)
    unless @_init.equal?(self.class.singleton_class)
      raise ArgumentError, "not initialized"
    end
    eval(@src, b, (@filename || '(erb)'), @lineno)
  end

 => nil
irb(main):003:0> show_source "ERB.new('<%= hello %>').src"

From: /home/worker/.rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/erb.rb:843

  attr_reader :src

 => nil
irb(main):004:0> show_source "IRB::VERSION"

From: /home/worker/.rbenv/versions/3.1.0-dev/lib/ruby/3.1.0/irb/version.rb:14

  VERSION = "1.3.5"

 => nil
irb(main):005:0>
  • どんどん irb がよくなっていって便利

[PR irb #202] process multi-line pastes as a single entity

class A
  def b; self; end
  def c; true; end
end

a = A.new

a
  .b
  # aaa
  .c

(a)
  &.b()


class A def b; self; end; def c; true; end; end;
a = A.new
a
  .b
  # aaa
  .c
(a)
  &.b()
$ irb
irb(main):001:1* class A
irb(main):002:1*   def b; self; end
irb(main):003:1*   def c; true; end
irb(main):004:0> end
 => :c
irb(main):005:0>
irb(main):006:0> a = A.new
 => #<A:0x00007fe44496c078>
irb(main):007:0>
irb(main):008:0> a
irb(main):009:0>   .b
irb(main):010:0>   # aaa
irb(main):011:0>   .c
 => true
irb(main):012:0>
irb(main):013:0> (a)
irb(main):014:0>   &.b()
 => #<A:0x00007fe44496c078>
irb(main):015:0>
irb(main):016:0>
irb(main):017:0> class A def b; self; end; def c; true; end; end;
 => :c
irb(main):018:0> a = A.new
 => #<A:0x00007fe444c7c880>
irb(main):019:0> a
irb(main):020:0>   .b
irb(main):021:0>   # aaa
irb(main):022:0>   .c
 => true
irb(main):023:0> (a)
irb(main):024:0>   &.b()
 => #<A:0x00007fe444c7c880>
irb(main):025:0>
  • こんな感じで評価できるようになって初めて irb 上でコードが実行されて次のコードがペーストされる
  • 途中のコードは途中で実行される点には注意する

[Feature #17773] Alias Numeric#zero? and Float#zero? as Numeric#empty? and Float#empty?

  • Numeric#zero?Float#zero?Numeric#empty?Float#empty?エイリアスにしようという提案
  • モチベーションとしては Web などの入力フィールドで 文字列などと同じように 0 を無効値として扱うために共通のメソッドとして #empty? がほしいという感じ
  • 0 自体は集合ではないし、 0 が意図的に入力された可能性もあるなど全体的には否定的なコメントが目立つ
  • ちなみに ActiveSupport#blank?0.blank? == false になる
  • 共有のメソッド名にするならもっと別の名前の方がよい気はするな〜

[Bug #17777] 2.6.7 fails to build on macOS: implicit declaration of function 'rb_native_mutex_destroy' is invalid in C99

  • macOSRuby 2.6.7 をビルドすると失敗するという報告
  • C99 で無効なコードを書いているのが原因ぽい?
  • ビルド前に export warnflags=-Wno-error=implicit-function-declaration をしておくことで回避することは可能
  • macOSRuby 2.6.7 をビルドする場合はご注意を