2021/02/11 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[Bug #17424] Interactive Ruby で Object#method を再定義して任意の文字を入力するとエラーが発生する
- 表題の通りでなんですが
irb
上で#method
を再定義するとクラッシュするというバグ報告です - これは
irb
上で#method
を定義するとKernel#method
よりも優先順位が高いObject#method
が定義されてしまい、Reline でそのObject#method
を使用しているためです - これは仕様でチケットは閉じられています
- まあしょうがなさそう
[Feature #7394] Enumerable#find ifnone parameter could be non-callable
Enumerable#find
は要素が見つからなかった場合の処理をフォールバックすることができる
# 見つからなかった場合に第一引数の proc を呼び出す p [1, 3, 5].find(-> { "none" }, &:even?) # => "noge"
- 現状は
Proc
オブジェクトを渡すがこれをそれ以外のオブジェクトを渡せるようにする提案
# 第一引数の値がそのまま返ってくる p [1, 3, 5].find("none", &:even?) # => "noge"
- コメントでは以下のような既存の書き方と比較して何がよいのか指摘されている
p [1, 3, 5].find(&:even?) || "none"
[Feature #17608] Compact and sum in one step
- 次のように
Array#sum
を行う場合に#compact
を介して行うことがある
a = [1, nil, 2, 3] a.sum # !> TypeError a.compact.sum # => 6 a.sum{_1 || 0} # => 6
- これをワンステップで行うために
#sum
ではnil
の要素をスキップする、またはArray#filter_sum
のようなメソッドを追加する提案です - 最終的には
a.sum{_1 || 0}
で問題ないということで Reject されています
Vim の oldfiles の最大数を変更する
Twitter でいろいろとやり取りしていたので覚書。
Vim の oldfiles の最大数を変更する
Vim oldfiles
の保存数は 'viminfo'
の '
の設定に依存しています。
この保存数を変更する場合は vimrc に以下のような設定を追加することで変更することができます。
" v:oldfiles で保存するファイル数を設定 " vimrc に書いておく必要がある set viminfo+='10000 set viminfo-='100 " デフォルトの設定を削除しておかないt反映されない
また、oldfiles
で保存するファイルパスは Vim を再起動しないと反映されないので注意する必要があります。
ちなみに denite-file/old
はこの oldfiles
のデータを参照しているのでこの設定をすることで denite-file/old
の保存数を変更することもできます。
2021/02/04 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[Bug #10593] Emoji is been considered as comment
#️⃣
絵文字以降がコメントアウトとして扱われてしまうというバグ報告
#️⃣ これはコメントアウトです #️⃣ Ruby のコードは実行されません puts "hello, world" #️⃣ コメント
- これは
#️⃣
が囲み文字と呼ばれている絵文字で# + 特殊なコードポイント
で表現されているためです#️⃣
のバイト列がU+0023 + U+FE0F + U+20E3
となる- 参照:めくるめくEmojiの世界/emoji-world
- 実際に字句解析するとこんな感じ
require "ripper" # コメントとして字句解析される p Ripper.lex("#️⃣") # => [[[1, 0], :on_comment, "#️⃣", BEG]] # こっちは普通の文字になる p Ripper.lex("😀") # => [[[1, 0], :on_ident, "😀", CMDARG]]
- これは仕様ということで閉じられている
[Bug #7877] E::Lazy#with_index should be lazy
Enumerable::Lazy#with_index
が Ruby 2.6 と 2.7 で挙動が変わっていたので調べていたらこのチケットを見つけました
# Ruby 2.6 だとブロックの中身が呼ばれるが、2.7 だと呼ばれない pp [1, 2, 3].lazy.with_index { |it, i| l} # 2.6 => [1, 2, 3] # 2.7 => #<Enumerator::Lazy: ...>
- チケット自体は
Enumerable::Lazy#with_index
もlazy
化してほしいというバグチケット- 内容的には
feature
っぽいが
- 内容的には
- この影響で既存の
Enumerable::Lazy#with_index
の挙動がぶっ壊れてしまったぽい - 上記のコードを Ruby 2.7 でも同じようにどうさせる場合は
#force
か#each
するのが無難かなあ
pp [1, 2, 3].lazy.with_index { |it, i| p [it, i] }.force pp [1, 2, 3].lazy.with_index.each { |it, i| p [it, i] }
- ちなみにこの変更、Ruby 2.7 の
NEWS
にも載ってなくてチケット調べるのに手間取りましたgit blame
で該当するコミットを調べるなどしていた- commit: https://github.com/ruby/ruby/commit/e94ac03eb0d07af3cbff20194acf479bf8851c39
denite.nvim で絞り込みにマッチしたワードをハイライトする
ちょっと前に denite.nvim を試してみたら思ったよりも使えるようになったのでいろいろと試してみた覚書。
denite.nvim で絞り込みにマッチしたワードをハイライトする
denite.nvim
ではデフォルトでは絞り込んだワードでハイライトできなくて困ってたんですが以下のオプションの仕方を教えてもらいハイライトできるようになりました。
" Search を好きなハイライトにする Denite line -highlight-matched-char=None -highlight-matched-range=Search -match-highlight
これでまた unite.vim からの移行に近づいた…。
2021/01/28 今週の気になった bugs.ruby のチケット
内容は適当です。 今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。 あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[PR 4113] Add --warning=category option the same as -W:category
-W:category
と同じ意味を持つ--warning=category
を追加する PR- チケット自体は特になさそう?
--warning=category
のほうが意味的にはわかりやすそうなのであると便利そう
[Feature #17576] Partial Functions (procs, lambdas)
- ブロック構文に
in
を使ったガード節を追加する提案 - 以下のように
in
を使ってガード節を書くin x if x.odd?
の部分がガード節の条件
partial_proc = proc do |arg| in x if x.odd? "#{x} is odd" end
- このような定義をして以下のような操作を行う
# `defined?` で引数が有効かどうかを判定する partial_proc.defined?(42) #=> false partial_proc.defined?(41) #=> true # call で実際に呼び出す # 該当するガード節がない場合は例外が発生する partial_proc.call(42) #=> raises NoMatchingPatternError (42) partial_proc.call(41) #=> 41 is odd # call_or_else で見つからなかった場合の挙動をブロックに記述する partial_proc.call_or_else(42) { "fallback value" } #=> 'fallback value' partial_proc.call_or_else(41) { "fallback value" } #=> 41 is odd
- これを用いて
FizzBuzz
を書くと多分以下のような感じ?
p (1..20).map do |n| in Integer if n % 15 == 0 "fizzbuzz" in Integer if n % 3 == 0 "fizz" in Integer if n % 5 == 0 "buzz" in Integer n end
- 最初のコードを見たときは『どういうこっちゃ?』と思ったけどガード節と見れば納得
- カード節は普通に便利なのでほしい
- ただ
defined?
やcall_or_else
は微妙なのでこれは別に議論すべきじゃないかなーと思わなくもない
[Bug #17557] IRB array returned in multiple lines
- irb 1.3.0 で
irb
のデフォルトの出力がinspect
からpretty_inspect
に変わったけど元の挙動に戻せる?みたいなチケット
# 以前のバージョン 's'.methods = > [:unicode_normalize, :unicode_normalize!, :ascii_only?, :to_r, :unpack, ..., :!=, :equal?, :__id__, :instance_eval, :instance_exec] [2021-01-28 00:26]
# irb 1.3.0 's'.methods = > [2021-01-28 00:26] [:unicode_normalize, :unicode_normalize!, :ascii_only?, :to_r, :unpack, ..., :!=, :equal?, :__id__, :instance_eval, :instance_exec]
- 元の
inspect
を呼び出す挙動に戻す場合は~/.irbrc
とかに以下の設定を書いておけば OK
IRB.conf[:INSPECT_MODE] = :inspect
- ちなみにこの変更はバックポートされているので Ruby 3.0.1 から反映されそう
Ruby で UTF-8 の文字列を SJIS で文字化けさせたり復元したりする
以下の記事を知人と話していたら思ったよりも盛り上がったので覚書。
ちなみにクイズのネタバレがあるので見たくない人は読まないでね!!
縺ゅ¢縺セ縺励※縺翫a縺ァ縺ィ縺
みたいな文字化けした文字列を Ruby で生成する
言及されている文字化けは『 UTF-8
な文字を SJIS
で表示すると文字化ける』っていうような話になります。
では、Ruby でこのような文字化けを行う場合どうするのがいいのでしょうか。
これは要するに『 UTF-8
を SJIS
として扱い UTF-8
で変換する』という処理で実現することができます。
まず、Ruby のデフォルトのエンコーディングは utf-8 なので単に文字列リテラルを定義した場合は utf-8
の文字列になります。
# 文字列リテラルは utf-8 utf8 = "やばたにえん" pp utf8.encoding # => #<Encoding:UTF-8>
次に String#force_encoding
を使って『内部のデータはそのままでエンコーディング情報のみ』を変更します。
NOTE: ちなみに #force_encoding
は #encode
と違い #valid_encoding
でチェックは行いません(thanks id:imaizumimr :)
utf8 = "やばたにえん" # 内部のエンコーディング情報のみ書き換える # 内部データはそのまま sjis = utf8.force_encoding(Encoding::SJIS) # エンコーディング情報のみ SJIS として扱われる # ちなみに SJIS は Windows-31J のエイリアス pp sjis.encoding # => #<Encoding:Windows-31J> # バイトコードは同じ pp utf8.bytes == sjis.bytes # => true
最後に『内部データは UTF-8
だけど文字コード情報 SJIS
』な文字列に対して UTF-8
でエンコードします。
この時に変換できない文字があるので無理やり変換されるように invalid: :replace, undef: :replace
オプションを指定しています。
utf8 = "やばたにえん" sjis = utf8.force_encoding(Encoding::SJIS) # SJIS だけど実データは UTF-8 な文字列を無理やり UTF-8 で変換させる pp sjis.encode(Encoding::UTF_8, invalid: :replace, undef: :replace) # => "繧��縺溘↓縺医s"
これでいわゆる『文字化け』した文字列が生成できました。
文字化けしたコードを戻す
では、次は文字化けしたコードを復元してみましょう。
現状はこんな感じです。
utf8 = "やばたにえん" sjis = utf8.force_encoding(Encoding::SJIS) bake = sjis.encode(Encoding::UTF_8, invalid: :replace, undef: :replace) pp bake # => "繧��縺溘↓縺医s" pp bake.encoding # => #<Encoding:UTF-8>
これを元に戻すのは比較的簡単で(全然簡単ではなかったけど…)先程の処理と逆のことをします。
utf8 = "やばたにえん" sjis = utf8.force_encoding(Encoding::SJIS) bake = sjis.encode(Encoding::UTF_8, invalid: :replace, undef: :replace) # まず SJIS でエンコードする。その後に文字コードを UTF-8 に指定する pp bake.encode(Encoding::SJIS, invalid: :replace, undef: :replace).force_encoding(Encoding::UTF_8) # => "\xE3\x82??たにえん"
これで復元することができます。
できるんですが文字化けさせるときのエンコードを無理やり行っているので上の手順で復元した場合は完璧に復元することはできません、うぐぅ…。
まとめ
文字コードなんもわからんが Ruby の String
は文字コード周りの実装がしっかりしててすごいなぁ。
普段書かないような処理なのでいろいろと知らない機能やオプションなんかの知見がしれてよかったです。
あと人とわいわいしながらコード書くのたのしいですね!!!
久々に書いてて楽しい Ruby のコードだった。
2021/01/21 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[Bug #17530] irb handles << incorrectly with variable as identifier
irb
で以下のように入力したら意図しないエラーになったというバグ報告
irb(main):001:0' s1 = 'testing' = > "testing" [2021-01-20 23:11] irb(main):002:0' s2 = 'this' = > "this" irb(main):003:0" s2 <<s1 irb(main):004:0" adding text here does not work irb(main):005:0" s1 Traceback (most recent call last): 3: from /home/centos/.rubies/ruby-3.0.0/bin/irb:23:in `<main>' 2: from /home/centos/.rubies/ruby-3.0.0/bin/irb:23:in `load' 1: from /home/centos/.rubies/ruby-3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.0/exe/irb:11:in `<top (required)>' SyntaxError ((irb):4: syntax error, unexpected local variable or method, expecting '(') adding text here does not work ^~~~ irb(main):006:0>
- 意図としては 3行目の
s2 <<s1
はs2.<<(s1)
のようにString#<<
を呼んでほしい - しかし、
irb
上ではヒアドキュメントと解釈されてしまっており 4〜5行目はヒアドキュメントの複数行として扱われてしまう - なんですが 5行目で Ruby のコードが実行される場合はヒアドキュメント
s2.<<(s1)
と解釈されてしまいおかしいことになっています- 4行目がそのまま Ruby のコードとして実行されている
- この問題の本質としては
s2 <<s1
というコードは『s2
という変数が定義されているかどうか』で意味が変わってしまう点です- 変数が定義されていれば
s2.<<(s1)
と解釈され、そうでなければヒアドキュメントとして扱われる
- 変数が定義されていれば
- なので 3行目のコードを解析する際に変数の有無も考慮する必要があるのですが irb の実装としては1行ずつコードを解析しておりその前の行で
s2
変数が定義されていてもs2 <<s1
がディアドキュメントとして解釈されてしまっている、って感じです - これ、直してみたいんだけどどう治すのがいいのかなあ…
[Bug #17547] Fix Ripper.lex("a <<b")
Ripper.lex("a <<b")
が意図する結果を返してなかったというバグ報告- 必要なパース結果が含まれていなかった
- これですが先程の [Bug #17530] を調べている時に見つけたバグで直せそうだったので直してパッチ投げました
- Ripper なので実装が難しいのかなあ、と思っていたんですが問題になっていた箇所は Ruby で書かれていたので比較的簡単でした
require "ripper" p Ripper.lex("a <<b") # 期待する挙動 => [[[1, 0], :on_ident, "a", CMDARG], [[1, 1], :on_sp, " ", CMDARG], [[1, 2], :on_heredoc_beg, "<<b", CMDARG]] # 実際の挙動 => [[[1, 2], :on_heredoc_beg, "<<b", CMDARG]]
- この修正は既にマージ済みです
[Bug #17556] ruby 2.7.2 ::YAML.dump ArgumentError: invalid value for Integer(): "20210101_"
YAML.dump '20210101_'
するとInteger
と解釈されてエラーになるというバグ報告
require "yaml" p YAML::VERSION # => "3.1.0" # error: `Integer': invalid value for Integer(): "20210101_" (ArgumentError) p YAML.dump '20210101_'
- このバグは既に修正済みで手元だと
YAML::VERSION
が3.2.0
では問題なく動作していました
require "yaml" p YAML::VERSION # => "3.2.0" p YAML.dump '20210101_' # => "--- '20210101_'\n"
[Bug #17554] [PATCH] Fix ObjectSpace.dump to include singleton class name
- 次のように
ObjectSpace.dump
に特異クラスを渡した場合に Ruby 3.0 だと"name"
が含まれなくなっているというバグ報告
require "objspace" puts ObjectSpace.dump(Object.new.singleton_class) # 2.7 => {"address":"0x55adae76b630", "type":"CLASS", "class":"0x55adae7a76d0", "name":"Object", "references":["0x55adae7a9250", "0x55adae76b720"], "memsize":464, "flags":{"wb_protected":true}} # 3.0 => {"address":"0x55d9048e80e0", "type":"CLASS", "class":"0x55d90476d738", "references":["0x55d90476e8b8", "0x55d9048e8158"], "memsize":472, "flags":{"wb_protected":true}}
- なるほどね?
[Bug #17423] Prepend
should prepend a module before the class
- Ruby 3.0 では継承リスト周りの処理が色々と改善された結果
#prepend
が意図する挙動をしていないケースがありました
module M; end module A; end class B; include A; end A.prepend M B.prepend M # B に prepend M しているのに反映されてないように見える p B.ancestors # 2.7 => [M, B, A, Object, Kernel, BasicObject] # 3.0 => [B, M, A, Object, Kernel, BasicObject]
- これの対処として
[M, B, M, A, Object, Kernel, BasicObject]
を返す提案がされました - この場合は
M
が重複してしまいますが、次のようなコードでも重複するので問題ないとの判断
module M; end class A; end class B<A; end A.prepend M B.prepend M # これは Ruby 3.0 以前からこのような挙動になっている p B.ancestors # => [M, B, M, A, Object, Kernel, BasicObject]
- この変更は既にマージされました
- 今後は以下のような挙動になる予定です
module M; end module A; end class B; include A; end A.prepend M B.prepend M p B.ancestors # 2.7 => [M, B, A, Object, Kernel, BasicObject] # 3.0 => [B, M, A, Object, Kernel, BasicObject] # 3.1 => [M, B, M, A, Object, Kernel, BasicObject]
- このあたりはちょっと注意して使う必要がありそうですねえ