TL;DR
~/.pryrc
に以下を足すだけでOK
unless defined?(SCRIPT_LINES__)
SCRIPT_LINES__ = {}
end
Pry.config.hooks.add_hook(:before_eval, :"ast<3") do |code, _pry|
SCRIPT_LINES__["(pry)"] = code.lines
code
end
例
% pry
[1] pry(main)> RubyVM::AbstractSyntaxTree.of(-> { puts :hi })
=> (SCOPE@1:32-1:45
tbl: []
args:
(ARGS@1:32-1:32
pre_num: 0
pre_init: nil
opt: nil
first_post: nil
post_num: 0
post_init: nil
rest: nil
kw: nil
kwrest: nil
block: nil)
body: (FCALL@1:35-1:43 :puts (ARRAY@1:40-1:43 (LIT@1:40-1:43 :hi) nil)))
[2] pry(main)>
eval上書きではだめなのはなぜ
結論から言うとrubyには数々のeval属があってpryが使っているのはBinding#eval。
なのでKernel#evalを上書きしても動かない。
神速さんのエントリを読んだ。
byebugやpryもevalを使っているだろうし、evalを上書きすれば他も簡単に対応できる....と思っていた。 世の中はそんなに甘くなかった。
https://sinsoku.hatenablog.com/entry/2019/05/04/032007
これはすごい! evalだけ書き換えればよいとかなりべんりじゃん、マーベラス!と思って一応eval上書きも試したがうまく動かなかった。
def pry_eval(*eval_strs)
b =
if eval_strs.first.is_a?(String)
Pry.toplevel_binding
else
Pry.binding_for(eval_strs.shift)
end
pry_tester(b).eval(*eval_strs)
end
一応動かなかった実装例をあげておきます。
こうしてCで実装されているメソッドにパッチを当てておくとc_callじゃなくcallイベントで取れるようになる。
targetで指定できるようになるしbindingからローカル変数取れるようになってべんり。
戻り値を書き換えるときにも使えてべんりで詳しくは過去記事参照されたし。
https://qiita.com/hanachin_/items/d5e243048900344b2146
unless defined?(SCRIPT_LINES__)
SCRIPT_LINES__ = {}
end
eval_patch = Module.new do
def eval(string, binding = nil, filename = nil, lineno = nil)
super
end
end
Kernel.prepend(eval_patch)
ast_happier = TracePoint.new(:call) do |tp|
filename = tp.binding.local_variable_get(:filename)
next unless filename
next if File.exist?(filename)
SCRIPT_LINES__[filename] = tp.binding.local_variable_get(:string)
end
ast_happier.enable(target: eval_patch.instance_method(:eval))
ならばBinding#eval
上書きだ!
byebugとかだとbyebug -r ast.rb foo.rb
みたいな感じで-r
オプションで読み込ませておいてあげるとうまく動く
unless defined?(SCRIPT_LINES__)
SCRIPT_LINES__ = {}
end
eval_patch = Module.new do
def eval(string, filename = nil, lineno = nil)
SCRIPT_LINES__[filename] = string.lines
super
end
end
Binding.prepend(eval_patch)
pryの場合
ところでpry
にはモンキーパチらなくていいbefore_eval hookがあるのだった