defx で絞り込みしつつ dentie-file/rec を起動する
defx で絞り込みしつつ dentie-file/rec を起動する
defx で find っぽいことをしたかったので設定した。
単に :Denite file/rec -input=ファイル名
を起動しているだけ。
:Defx -auto-cd
で起動しておく必要があるので注意。
" NOTE: auto-cd を有効にしておく必要があるので注意 call defx#custom#option('_', { 'auto_cd': 1, }) function! s:start_find() let find_word = input("Find Pattern: ") call denite#start([{ "name": "file/rec", "args": []}], { "input": find_word }) endfunction autocmd FileType defx call s:defx_my_settings() function! s:defx_my_settings() abort " 起動するキーマップ nnoremap <silent><buffer> gd :<C-u>call <SID>start_find()<CR> endfunction
denite.nvim の denite-menu を使って簡単にファイルを開いたりする
dentie-menu を使うことで簡単に自分好みのショートカットを設定する source を定義することができます。
" denite-menu の設定 let s:menus = {} " Denite menu:dotfile をすると開くファイルを登録する let s:menus.dotfile = { \ 'description': 'Edit your dotfile' \ } let s:menus.dotfile.file_candidates = [ \ ['vimrc', '~/.vimrc'], \ ['gvimrc', '~/.gvimrc'], \ ['bashrc', '~/.bashrc'], \ ['bash_aliases', '~/.bash_aliases'], \ ] " Denite menu:my_denites をすると開くファイルを登録する " コマンドを呼ぶ let s:menus.my_denites = { \ 'description': 'Denite list' \ } let s:menus.my_denites.command_candidates = [ \ ['file/old', 'Denite file/old'], \ ['menu:dotfile', 'Denite menu:dotofile'], \ ['grep', 'Denite grep'], \] " 登録 call denite#custom#var('menu', 'menus', s:menus) " 呼び出し nnoremap <Space>ll :Denite menu<CR>
べんりべんり。
2021/03/25 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[PR irb #212] Complete require and require_relative
- irb で
require
のファイル名補完をする機能の追加
irb(main):001:0" require "irb/<Tab> # <- Tab を押すとファイル名を補完してくれる
- これは普通に便利そう
[PR irb #204] Add whereami command
irb
にpry
のwhereami
コマンドを追加する PRwhereami
するとbinding.irb
した付近のコードを確認することができる
$ ruby /tmp/a.rb From: /tmp/a.rb @ line 3 : 1: a = 1 2: @b = 2 => 3: binding.irb irb(main)[01:0]> a => 1 irb(main)[02:0]> @b => 2 irb(main)[03:0]> whereami From: /tmp/a.rb @ line 3 : 1: a = 1 2: @b = 2 => 3: binding.irb => nil
- pry の
whereami
コマンド知らなかった - これはめっちゃ便利そう…
[PR irb #203] Implement pry-like ls command
irb
にpry
のls
コマンドを追加する PRls
すると引数オブジェクトのメソッド一覧やインスタンス一覧を出力してくれる
$ irb irb(main):001:0> require "erb" => true irb(main):002:0> ls ERB.new('test') ERB#methods: def_class def_method def_module encoding filename filename= lineno lineno= location= make_compiler result result_with_hash run set_eoutvar src instance variables: @_init @encoding @filename @frozen_string @lineno @src => nil
- これも知らなかった
- めっちゃ便利そう…
[Feature #17411] Allow expressions in pattern matching
- 以下のように
^
を使ってパターンマッチに式をかけるようにする提案
user = { name: "homu", age: 14 } case user # 式を書く場合は ^() を使う in { age: ^(7 + 7) } end
- これが実装されてマージされた :tada:
- 早くパターンマッチ使いたい…
[Bug #17738] Ruby can still freeze ENV
ENV
は普通はfreeze
できないががんばればfreeze
できるという報告
# これは error ENV.freeze # こうすると freeze できる Kernel.instance_method(:freeze).bind(ENV).call() p ENV.frozen? #=> true
- Ruby むずかしい…
[Bug #17735] Hash#transform_keys!
drops non evaluated keys
- 以下のように
Hash#transform_valies!
のブロック内でraise
するとレシーバは変わらない
# transform_values! はレシーバの要素を保持する hash = {a: 1, b: 2, c: 3} a = hash.transform_values! { raise } rescue p hash #=> {:a=>1, :b=>2, :c=>3}
- しかし
Hash#transform_keys!
の場合はレシーバが空になる
# transform_keys! はレシーバの要素が空になる hash = {a: 1, b: 2, c: 3} hash.transform_keys!(){ raise } rescue p hash #=> {}
[Bug #17736] Destructive methods inconsistently handle receiver frozen state
in given block
Array#select!
のブロック内でレシーバをfreeze
するとレシーバが空の配列になる
array = [1, 2, 3, 42] array.select! do array.freeze false end p array #=> []
Array#uniq!
の場合は例外になりレシーバはそのままになる
array = [1, 2, 3, 42, 2, 3] begin array.uniq! do |item| array.freeze item end rescue => err p err #=> #<FrozenError: can't modify frozen Array: [1, 2, 3, 42, 2, 3]> end p array #=> [1, 2, 3, 42, 2, 3]
Array#select!
の方はバグということで修正された
array = [1, 2, 3, 42] # Ruby 3.1.0-dev だとエラー # error: `select!': can't modify frozen Array: [1, 2, 3, 42] (FrozenError) array.select! do array.freeze false end
[Bug #17739] Array#sort! changes the order even if the receiver raises FrozenError in given block
Array#sort!
のブロック内でレシーバをfreeze
すると例外が発生するがソート済みになっているというバグ報告- [Bug #17736] と同じようなバグ
array = [1, 2, 3, 4, 5] begin array.sort! do |a, b| array.freeze if a == 3 1 end rescue => err # 例外が発生する p err #=> #<FrozenError: can't modify frozen Array: [5, 4, 3, 2, 1]> end # 例外が発生してもソート済みになっている p array #=> [5, 4, 3, 2, 1]
- ちなみに
break
した場合はそこまでのソートになっている
array = [1, 2, 3, 4, 5] array.sort! do |a, b| break if a == 3 1 end # 途中までソートされた状態 p array #=> [3, 4, 2, 1, 5]
- これはまだ未修正
[Feature #17743] Show argument types in backtrace
- バックトレースに引数情報も追加する提案
- 例えば以下のようなコードを実行すると
def say_hi(person) puts message(person) end def message(person) "hi: #{person.name}" end say_hi(nil)
- 以下のようなバックトレースが出力される
/tmp/vTaZxJg/70:6:in `message': undefined method `name' for nil:NilClass (NoMethodError) from hi.rb:2:in `say_hi' from hi.rb:9:in `<main>'
- これを以下のようにする提案
hi.rb:6:in `message': undefined method `name' for nil:NilClass (NoMethodError) from hi.rb:2:in `say_hi' called with NilClass from hi.rb:9:in `<main>' called with NilClass
- どのような表記にするのか(クラスだけ?値は必要がない?複数の引数の場合は?)やパフォーマンス的な懸念点がないか議論されている
[Feature #17744] Accumulate Enumerable#tally
results
- 以下のように
Enumerable#tally
に引数を渡してそれに結果を集計していくような提案- 元々は今月の開発者会議で提案されていたっぽい
h = {} [:a,:b,:c].tally(h) [:a,:b,:d].tally(h) # 2個の tally の結果が h に蓄積される p h #=> {:a=>2, :b=>2, :c=>1, :d=>1}
- 便利な気がするけど引数が変更されてしまうのはなんかちょっと気持ち悪い…
2021/03/18 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[PR irb #202] process multi-line pastes as a single entity
- 現行の
irb
だと以下のようなコードをペーストするとエラーになる
class A def a; self; end def b; true; end end a = A.new a .a .b
irb(main):001:1* class A irb(main):002:1* def a; self; end irb(main):003:1* def b; true; end irb(main):004:0> end = > :b irb(main):005:0> irb(main):006:0> a = A.new = > #<A:0x00005588503197b8> irb(main):007:0> irb(main):008:0> a = > #<A:0x00005588503197b8> irb(main):009:0> .a /home/worker/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.4/lib/irb/workspace.rb:116:in `eval': (irb):9: syntax error, unexpected '.' (SyntaxError) from /home/worker/.rbenv/versions/3.0.0/lib/ruby/gems/3.0.0/gems/irb-1.3.4/exe/irb:11:in `<top (required)>' from /home/worker/.rbenv/versions/3.0.0/bin/irb:23:in `load' from /home/worker/.rbenv/versions/3.0.0/bin/irb:23:in `<main>' irb(main):010:0> .b
- これはペーストしたタイミングで Ruby のコードが逐次的に評価され
.a
を呼び出した時にエラーになってしまっているため- 先に
a
のコードが評価され
- 先に
- これを回避するためにペーストして『最後に』コードを評価するようにする PR
- 個人的には落とし所としてはいいとは思う
[Bug #17571] prependしたArray#[] が反映されない
- 以下のように
Array
にprepend
してる#[]
が呼び出されないことがある
module TestMod def [](*) :called end end Array.prepend TestMod # これは Array#[] が呼ばれる p [1, 2, 3][1] # => 2 # これは TestMod#[] が呼ばれる p [1, 2, 3][] # => :called # Method オブジェクトは TestMod を指している p [1, 2, 3].method(:[]) # => #<Method: Array(TestMod)#[](*) /tmp/vud3mdg/27:2>
- これはおそらく
Array#[]
を事前にメソッドキャッシュしておりそちらを優先して呼び出しているのが原因ぽい?- なので
prepend
しているメソッドは呼ばれなくなっている - 引数がない場合は
Array#[]
とシグネチャが異なるのでキャッシュされたメソッドではなくてprepend
されたメソッドを呼び出しているので意図する挙動になっているみたい?
- なので
- これは Ruby 2.7 では問題なくて Ruby 3.0 から問題になっている
- Ruby 3.0 に上げるタイミングでなにか壊れるかもしれないので注意したい
- 原因っぽいコミット(と PR)
[Bug #17725] Prepend breaks ability to override optimized methods
- 以下のように
String.prepend
するとString#+
が上書きされたりされなかったりする
# これは上書きされる class String def + other 'blah blah' end end p 'a' + 'b' # => "blah blah"
# これは上書きされない String.prepend(Module.new) class String def + other 'blah blah' end end p 'a' + 'b' # => "ab"
[Bug #16996] Hash should avoid doing unnecessary rehash
Hash#dup
よりもHash#merge
の方が高速だというチケット- 3.5倍早いと書かれている
- これは
Hash#dup
は内部で再ハッシュを行っているがHash#merge
は行っていないのでその差になる - Ruby なにもわからない
Hash#replace/dup/initialize_copy
で再ハッシュを行わないようにする PR- 開発者会議でも承認されたっぽいのでマージされそう
- ちなみに
hash.select
よりもhash.merge.select!
のほうが高速らしいHash#select/reject
で再ハッシュしないようにする PR- https://github.com/ruby/ruby/pull/4273
[Bug #17719] Irregular evaluation order in hash literals
Hash
リテラルでキーが重複している場合に以下のような評価順になる
# 1個目と2個目の foo の要素が先に評価される $ ruby -e '{foo:p(1), bar:p(2), foo:p(3)}' -e:1: warning: key :foo is duplicated and overwritten on line 1 1 3 2
- これはバグのようなので修正するチケット
- 以下のような問題もあるしもうエラーにしちゃっていいんじゃないかなあ…。
Ruby の Hash リテラルでキーが重複している場合の奇妙な動作
最近こういう動作を見つけたので覚書。
Hash リテラルでキーが重複しているとどうなるのか
Hash リテラルで Hash を定義した場合、キーの順番は『記述した順番』になります。
# ここで定義した順番になる hash = { age: 14, name: "homu" } pp hash # => {:age=>14, :name=>"homu"} hash = { name: "homu", age: 14 } pp hash # => {:name=>"homu", :age=>14}
では、この Hash リテラルでキーが重複しているとどうなるのかというと次のようになります。
# age が重複している場合 hash = { id: 1, age: 14, name: "homu", age: 20, age: 10 } # キーの順番は age が先にくるが、値は最後に定義した値になる pp hash # => {:id=>1, :age=>10, :name=>"homu"}
こんな感じで
- キーの位置は『最初に定義された位置』
- キーの値は『最後に定義された値』
という挙動になります。
キーの位置と値が異なるのでちょっと混乱しますね。
このような書き方をすると警告もでますし、基本的に Hash リテラルではキーを重複して書かないようにしましょう。
# warning: key :age is duplicated and overwritten on line 2 hash = { id: 1, age: 14, name: "homu", age: 20, age: 10 }
関連
2021/03/11 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[Bug #16908] Strange behaviour of Hash#shift when used with default_proc
.
- 以下のように
default_proc
が設定されている状態でHash#shift
を呼ぶといと意図しない値が返ってくるよっていうチケットHash
が空の時にshift
を呼ぶとdefault_proc
の値が返ってくる
hash = Hash.new{|k,v| k[v] = 0} hash.shift # => 0 hash.shift # => [nil, 0]
- 意図としては両方共
[nil, 0]
が返ってきてほしい - もうちょっと詳細に説明するとこんな感じ
hash = Hash.new{|k,v| k[v] = 0} # 空 p hash # => {} # ここは default_proc 値を返す # ここが意図していないというチケット p hash.shift # => 0 # hash,shift 後は中身が入ってる状態になる p hash # => {nil=>0} # ので、これは [nil, 0] を返す p hash.shift # => [nil, 0]
- この挙動は確かに奇妙
- チケットだと
nil
を返すほうがいい、みたいな意見もある
[Feature #17674] Proposal: Method#source_location
or Method#owner
for refined methods
- https://bugs.ruby-lang.org/issues/15504#note-17 からの派生
- https://github.com/AlexWayfer/gorilla_patch/blob/master/lib/gorilla_patch/cover.rb#L8 こんな実装を書いてある場合に
range.method(:cover?)
でsource_location
が取れなくてそれを対応させてほしいっぽい?- Ruby 2.6 だと
refine + using
しててもrange.method(:cover?) # => nil
になってしまう
- Ruby 2.6 だと
- これは Ruby 2.7 だと既に対応済み
# test.rb module Cover refine Range do def cover?(value) return super unless value.is_a?(Range) super(value.first) && super(value.last) end end end using Cover pp (1..10).method(:cover?).source_location # Ruby 2.6 => nil # Ruby 2.7 => ["/test.rb", 4] pp (1..10).method(:cover?).owner # Ruby 2.6 => Range # Ruby 2.7 => #<refinement:Range@Cover>
2021/03/05 今週の気になった bugs.ruby のチケット
内容は適当です。
今週と言っても今週みかけたチケットなだけでチケット自体は昔からあるやつもあります。
あくまでも『わたしが気になったチケット』で全ての bugs.ruby のチケットを載せているわけではありません。
[Feature #17660] Expose information about which basic methods have been redefined
RubyVM.redefined_methods
というメソッドを追加する提案- これは再定義されてた標準的なメソッドの一覧を返すようなメソッドになる
class Integer def +(x); x ** self; end end # 再定義された Integer#+ メソッドの情報を返す p RubyVM.redefined_methods # => {Integer=>[:+]}
- これによって再定義されてしまうことを防ぐことができるらしい
Minitest.after_run { fail "Basic methods have been redefine" if RubyVM.redefined_methods.any? }
[Bug #17652] GC compaction crash on mprotect
- GC compaction でクラッシュするというバグ報告
- 以下のコードで再現
GC.auto_compact = true times = 20_000_000 arr = Array.new(times) times.times do |i| arr[i] = "#{i}" end arr = Array.new(1_000_000, 42) GC.start puts "ok"
- こんなコードで再現するんだ… GC なんもわからねえ…
[Feature #17663] Enumerator#with, an alternative to Enumerator#with_object
Enumerator#with
を追加する提案#with
に渡した引数を#with
のブロックの引数として受け取るEnumerator#with
と似ているが戻り値がちょっと違う
class Enumerator def with(*options) return to_enum(:with, *options) unless defined? yield each do |entry| yield entry, *options end end end # each_with_object は each_with_object の引数を返す pp (1..5).map.each_with_object(2) { |it, n| p it.to_s(n) } # => 2 # with はそのままイテレーションの結果を返す pp (1..5).map.with(2) { |it, n| it.to_s(n) } # => ["1", "10", "11", "100", "101"] # こうもかける pp (1..5).map.with(2, &:to_s) # => ["1", "10", "11", "100", "101"]
#each_with_object
を#map
などでチェーンしたい場合はあるのでこれは普通に便利そう- まあ今なら素直にナンパラを使えばいいじゃん、という気もするが…
(1..5).map { _1.to_s(2) }
[Misc #17662] The heredoc pattern used in tests does not syntax highlight correctly in many editors
- ヒアドキュメントを使って以下のような記述を行っていることがある
assert_ruby_status([], "#{<<-"begin;"}\n#{<<-'end;'}", bug) begin; exit("1" == Thread.start(1, &:to_s).value) end;
- これは以下のような文字列になる
str = "#{<<-"begin;"}\n#{<<-'end;'}" begin; exit("1" == Thread.start(1, &:to_s).value) end; pp str # => "\n" + " exit(\"1\" == Thread.start(1, &:to_s).value)\n"
- この書き方だとエディタでハイライトされないことが多いので以下のように書き直そう、という提案
assert_ruby_status([], <<~'RUBY', bug) exit("1" == Thread.start(1, &:to_s).value) RUBY
- 手元の Vim だとうまくハイライトされなかった…うーん…
[Bug #17661] IO#each will segfault when if file is closed inside an each_byte
block
- 以下のように
File#each_byte
内でFile#close
すると segv するというバグ報告
file = File.open(__FILE__) file.each_byte do |byte| file.close end
- なぜ…
- PR があるのでそのうち対応はされそう
[Bug #17667] Module#name needs synchronization
Module#name
は非同期処理に対応していないので以下のようにすると segv するというバグ報告
class C @iv = 1 end Ractor.new { loop { C.name } } class C 0.step { |i| instance_variable_set("@iv#{i}", i) } end
Module#name
内でインスタンス変数にアクセスしたりしているんですかね?普通に踏みそう