Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
†Ruby黒魔術経典†
Search
Tomohiro Hashidate
June 08, 2019
Programming
6.7k
15
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
†Ruby黒魔術経典†
名古屋Ruby会議04 発表資料
Tomohiro Hashidate
June 08, 2019
More Decks by Tomohiro Hashidate
See All by Tomohiro Hashidate
Ruby::Boxでできること、Refinementsでできること
joker1007
3
420
Do Ruby::Box dream of Modular Monolith?
joker1007
1
950
ReproでのicebergのStreaming Writeの検証と実運用にむけた取り組み
joker1007
0
750
マイクロサービスへの5年間 ぶっちゃけ何をしてどうなったか
joker1007
23
10k
Quarkusで作るInteractive Stream Application
joker1007
0
290
今改めてServiceクラスについて考える 〜あるRails開発者の10年〜
joker1007
25
23k
rubygem開発で鍛える設計力
joker1007
5
1.4k
実践Kafka Streams 〜イベント駆動型アーキテクチャを添えて〜
joker1007
3
1.4k
本番のトラフィック量でHudiを検証して見えてきた課題
joker1007
2
1.3k
Other Decks in Programming
See All in Programming
なぜ型を書くのか? TSKaigi2026で改めて考える #tskaigi_smarthr
kajitack
0
140
Javaの型とAI時代に型が大事な理由 / java types and type in AI era
kishida
2
150
「AIで開発し、AIを届ける」をEvalでつなぐ 〜AIネイティブに始めるプロダクト開発の実践〜 / Connecting "Develop with AI, deliver AI" with Eval
rkaga
4
5.4k
Creating Composable Callables in Contemporary C++
rollbear
0
160
1B+ /day規模のログを管理する技術
broadleaf
0
110
Webフレームワークの ベンチマークについて
yusukebe
0
180
AIを活用したE2Eテスト実装効率化のあゆみ / ebisu-mobile-14-kotetu
kotetuco
0
130
ECSアプリログをFireLensでコスト削減しようとしたけど諦めた話 in Fargate×Node.js
akihisaikeda
2
4.2k
代数的データ型って何が嬉しいの? #frontend_phpcon_do
kajitack
8
3.8k
軽量Java基盤の設計 DIコンテナに頼らない、長期保守と1秒起動の実現 JJUG CCC 2026 Spring
macha64
0
570
依存関係から依存物へ―Dependencyという言葉の歴史をひも解く
j_lee
0
130
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4.5k
Featured
See All Featured
How GitHub (no longer) Works
holman
316
150k
For a Future-Friendly Web
brad_frost
183
10k
[RailsConf 2023] Rails as a piece of cake
palkan
59
6.7k
Balancing Empowerment & Direction
lara
6
1.2k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.2k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Navigating Weather and Climate Data
rabernat
0
230
Exploring the relationship between traditional SERPs and Gen AI search
raygrieselhuber
PRO
2
4k
The SEO Collaboration Effect
kristinabergwall1
1
490
Designing for Performance
lara
611
70k
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.3k
What's in a price? How to price your products and services
michaelherold
247
13k
Transcript
†Ruby 黒魔術経典† @joker1007 (Repro inc. CTO) 名古屋Ruby 会議04
self.inspect @joker1007 Repro inc. CTO Data Engineering, Architect Ruby で悪事を働く⼈と⾒做されている
We provide a web service as ... Analytics of Mobile
Apps, Web Apps. Marketing Automation. We're hiring!!
https://youtu.be/1mauqP9zWbM?t=31
⼤いなる⼒には ⼤いなる責任が 伴う
メタプログラミングや魔術的な挙動をする コードは強⼒だが 侵してはならないこともある
今⽇の議題 以下の内容について話す。 黒魔術において守るべきルール 実際に魔術を編み出すために使えるパーツ 考え⽅の具体例
Part1 守りの黒魔術
守りの黒魔術 その1 組込みクラスを壊さない 特に require しただけで挙動を変えない 上書きさせたい場合は、必ずユーザーに指定させる 組込みのクラス・メソッドは全てのRuby ユーザーが期待している挙動が ある。(
当たり前の話) 暗黙的に弄って挙動を変えたら、何が起こるのか分からなくなる。
守りの黒魔術 その2 スタックが追える様にすること eval を使う場合に定義場所のロケーションを適切に設定する。 でないと例外が発⽣した時にどこで定義されたコードなのか分からない。 実はRuby の例外のバックトレースは⾃分で上書きできるので、ノイズに なりそうな情報を隠すこともできる。
守りの黒魔術 その3 パフォーマンスを意識すること eval やbinding は負荷が⾼い。 呼び出し回数を少なくするために以下の様な⽅法が使える。 クラスレベルでソースコードをキャッシュする クラス定義時や初回実⾏時のみ動作する様にする メソッド定義にだけ利⽤する
初回実⾏時にメソッドを上書きする
守りの黒魔術 その4 TracePoint の利⽤は明⽰的に そして、絶対にensure でdisable すること。 途中で例外が発⽣するとtrace が有効のままになる。 trace
が有効のままになるとマジで何が起きるか分からない。 フックが暴⾛してstack level too deep になるのはまだ良い⽅。
Part2 黒魔術に使えるAPI の探し⽅
基本はリファレンスをひたすら読むこと それだけだと雑過ぎるので、もうちょい解説する。
るりまのここを読むべし BasicObject Object Module/Class Method Proc Kernel (ObjectSpace)
⼀回読んでも忘れるから、 とりあえずこの辺りを読み返す癖を付けておく。 ちなみに、 RubyVM::AbstractSyntaxTree はまだるりまが無いです。 ( プルリクチャンス)
使えそうな機能の例 評価コンテキスト操作 eval 系 トリガー/ フック メソッドフック, TracePoint, included, inherited,
method_missing, trace_var, trap, finalizer ⼤域脱出 throw/catch, Fiber
使えそうな機能の例 ( 続き) オブジェクト参照 _variable_get 系, const_get, ObjectSpace 変数/ 定数操作
_variable_set 系, const_set メタデータ取得 Method やProc から取れる情報 メソッドの動的定義 define_method, module_eval
メタプログラミングパターン メソッド定義 DSL 動的解析 静的解析 ⾃動的/ 暗黙的処理の追加 ⾔語拡張 ↓ に向かう程魔術度が増す
Part3 考え⽅の具体例 暗黙のブロック引数 it を作ってみよう see. https://bugs.ruby-lang.org/issues/15897
proc の中で暗黙の内に it でパラメータを参照できるとは、 Ruby の動作に置き換えるとどういうことかを考えてみる。 評価コンテキスト内で it というローカル変数に値が⼊っている proc
のself に it というメソッドが定義されていて、パラメータを取 得できる proc の外側で it が定義されている。 ローカル変数 or 引数 これらのどれかが実現できれば良さそう。
ローカル変数追加⽅式について検討 local_variable_set が使えそう local_variable_set は変数書き換えは簡単だが、新規に追加するのは難 しい binding が毎回新しく⽣成されるため binding を固定してeval
しなければならない そもそもブロック呼び出しに実際に使われている値をどうやって事前 に取得する? なんか無理っぽい
メソッド追加⽅式について検討 評価コンテキストにおけるself は取得できる Proc#binding やTracePoint で可能 単純にメソッドを定義するとあるクラスのインスタンス全てが影響す る 特異メソッドとして定義すれば可能かも しかしスコープを抜けた後も参照できてしまう
Refinements は使えないか ブロックの定義が別の場所なのでeval が必要 ローカル変数⽅式と同様の問題がある
外側でit を定義する⽅法について検討 新しいproc でラップして追加できる ブロックに渡される引数を事前に知る必要が無い やはりeval する必要がある
結論: 恐らくeval が必須 そしてproc でwrap ⽅式が現実的 eval するためにはソースコード断⽚が必要
ブロックのソースコードを取る⽅法 RubyVM::AST.of or parser gem で位置を特定し読む ( またお前か)
特定のメソッドを対象にPoC を書く module Ext def map(*args, &block) source = File.readlines(block.source_location[0])
proc_binding = block.binding ast = RubyVM::AbstractSyntaxTree.of(block) args_tbl = ast.children[0] block_node = ast.children[2] if args_tbl.empty? extracted = extract_source( source, block_node.first_lineno, block_node.first_column, block_node.last_lineno, block_node.last_column) new_block = proc_binding.eval("proc { |it| #{extracted} }") super(*args, &new_block) else super(*args, &block) end end end Array.prepend(Ext) n = 3 [1, 2, 3].map { p it + n } # => [4, 5, 6]
出来た! 後はgem にするだけ
こんな感じで、⾃分の場合はゴールから逆算して考える。 やりたい事が出来るとはRuby においてオブジェクトの状態や変数のスコ ープ、メソッドの定義がどうなっていればいいかを想像し、そこに⾄る⽅ 法を逆向きに辿って実現可能な⽅法を考える。
ちなみに、実はこれ RubyKaigi2019 で話したものと同じ パターンを使っている
最後に 黒魔術を使うためにはRuby の挙動や各オブジェクトが何なのかというこ とを詳しく知る必要がある。 魔術的な挙動を起こす⽅法を知ることは、安全なコードの書き⽅を知るこ とにも繋がる。 いざという時の選択肢も増える。 Ruby より深く楽しみ、より良いコードに繋げよう