$30 off During Our Annual Pro Sale. View Details »
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Reifying the Call Stack
Search
Norbert Wójtowicz
January 16, 2018
Programming
0
97
Reifying the Call Stack
Norbert Wójtowicz
January 16, 2018
Tweet
Share
More Decks by Norbert Wójtowicz
See All by Norbert Wójtowicz
Datalog: Data All Truth · Append Log · Obtain Graph
pithyless
2
870
Datalog: Biting the Silver Bullet
pithyless
0
280
Data-Oriented Architecture (LambdaDays 2016)
pithyless
3
910
ClojureScript + React.js: How I learned to stop worrying and love the browser.
pithyless
1
530
Other Decks in Programming
See All in Programming
Media Capture and Streams: W3C仕様と現場での知見
nowaki28
0
130
Evolving NEWT’s TypeScript Backend for the AI-Driven Era
xpromx
0
260
TypeScript 5.9 で使えるようになった import defer でパフォーマンス最適化を実現する
bicstone
1
1k
大体よく分かるscala.collection.immutable.HashMap ~ Compressed Hash-Array Mapped Prefix-tree (CHAMP) ~
matsu_chara
1
210
CSC305 Lecture 15
javiergs
PRO
0
240
ソフトウェア設計の課題・原則・実践技法
masuda220
PRO
24
21k
堅牢なフロントエンドテスト基盤を構築するために行った取り組み
shogo4131
6
1.9k
Google Antigravity and Vibe Coding: Agentic Development Guide
mickey_kubo
2
130
WebRTC と Rust と8K 60fps
tnoho
2
1.9k
AIコーディングエージェント(Gemini)
kondai24
0
120
Combinatorial Interview Problems with Backtracking Solutions - From Imperative Procedural Programming to Declarative Functional Programming - Part 1
philipschwarz
PRO
0
120
TypeScriptで設計する 堅牢さとUXを両立した非同期ワークフローの実現
moeka__c
6
2.9k
Featured
See All Featured
The Cult of Friendly URLs
andyhume
79
6.7k
Imperfection Machines: The Place of Print at Facebook
scottboms
269
13k
The Cost Of JavaScript in 2023
addyosmani
55
9.3k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
46
7.8k
Save Time (by Creating Custom Rails Generators)
garrettdimon
PRO
32
1.8k
Site-Speed That Sticks
csswizardry
13
990
Building a Modern Day E-commerce SEO Strategy
aleyda
45
8.3k
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
37
2.6k
Why Our Code Smells
bkeepers
PRO
340
57k
Reflections from 52 weeks, 52 projects
jeffersonlam
355
21k
The Power of CSS Pseudo Elements
geoffreycrofte
80
6.1k
KATA
mclloyd
PRO
32
15k
Transcript
Interceptors: Reifying the Call Stack Norbert Wójtowicz · @pithyless
Ruby · Clojure
Rack class HelloWorld def call(env) [200, {"Content-Type" => “text/plain”}, ["Hello
world!"]] end end
Rack Middleware - Enter class AddGUID def call(env) env["tx_id"] =
SecureRandom.uuid status, headers, body = @app.call(env) [status, headers, body] end end
Rack Middleware - Leave class NoCache def call(env) status, headers,
body = @app.call(env) headers["Cache-Control"] = "no-cache" [status, headers, body] end end
Rack Middleware - Around class ProfileRequest def call(env) before =
Time.now.to_f status, headers, body = @app.call(env) after = Time.now.to_f diff = after - before log_result(diff) [status, headers, body] end end
Rack Middleware - Error class CatchErrors def call(env) @app.call(env) rescue
StandardError => e log_error(e) [500, {...}, ["<html>Oh noes!</html>"]] end end
Rack - Application use CatchErrors use ProfileRequest use NoCache use
AddGUID run HelloWorld
Call Stack enter leave leave enter leave env resp env
resp CatchErrors ProfileRequest NoCache AddGUID HelloWorld
Call Stack - Stack Overflow def fib(num) num < 3
? 1 : fib(num - 2) + fib(num - 1) end fib(10) #=> 55 fib(100_000) #=> Stack Level Too Deep
Call Stack - Asynchronous class AsyncHelloWorld def call(env) Thread.new sleep(5)
response = [200, {...}, ["Hello world!"]] env['async.callback'].call(response) end throw :async end end
Call Stack - Asynchronous Middleware def call(env) response = @app.call
env throw :async if @throw_on.include? response.first response end def call(env) response = @async_response catch(:async) { response = @app.call env } response end
Call Stack - Streaming def call(env) stream = env['rack.stream'] stream.after_open
do stream.chunk "Hello" stream.chunk "World" stream.close end [200, {'Content-Type' => 'text/plain'}, []] end
Call Stack - Global Ordering @use << proc { |app|
middleware.new(app, *args, &block) } ... app = @use.reverse.inject(app) { |a,e| e[a] }
Call Stack enter leave leave enter leave env resp env
resp CatchErrors ProfileRequest NoCache AddGUID HelloWorld
Ring (defn hello-world [request] {:status 200 :headers {"Content-Type" "text/plain"} :body
"Hello World!”})
Ring - Streaming (defn hello-world [request] {:status 200 :headers {"Content-Type"
"text/plain"} :body "Hello World!”}) ;; Body: ;; - String ;; - ISeq ;; - File ;; - InputStream
Ring - Asynchronous (defn hello-world ;; Synchronous ([request] {:status 200
:headers {"Content-Type" "text/plain"} :body "Hello World!"}) ;; Asynchronous ([request respond raise] (respond (hello-world request))))
Ring - Application (def app (-> handler (add-guid) (no-cache) (profile-request)
(catch-errors)))
Reify verb: make (something abstract) more concrete or real
Pedestal - Interceptors 1. enter · leave · error 2.
reified call stack
Interceptor - Handler (defn hello-world [request] {:status 200 :headers {"Content-Type"
"text/plain"} :body "Hello World!”}) ;; Body: ;; - String ;; - ISeq ;; - File ;; - InputStream
Interceptor - Enter {:name ::attach-guid :enter (fn [context] (assoc context
::guid (java.util.UUID/randomUUID)))}
Interceptor - Leave {:name ::disable-cache :leave (fn [context] (assoc-in context
[:response :headers "Cache-Control"] "no-cache"))}
Interceptor - Around {:name ::profile-request :enter (fn [context] (let [before
(System/currentTimeMillis)] (assoc context ::start-time before))) :leave (fn [context] (let [before (get context ::start-time) after (System/currentTimeMillis) diff (- after before)] (log diff) context))}
Interceptor - Errors {:name ::handle-error :enter (fn [context] ...) :leave
(fn [context] ...) :error (fn [context ex] ;; 1. Handle error context ;; 2. Cannot handle error (assoc context ::interceptor/error ex) ;; 3. Should have handled error, but... (throw (ex-info "Oh noes!" {:extra :data})))}
Interceptors - Application enter leave leave enter leave ctx ctx
ctx CatchErrors ProfileRequest NoCache AddGUID HelloWorld ctx
Interceptor - Deferred Calls {:name ::third-party-auth :enter (fn [context] (go
(assoc context :account (call-auth-system context))))}
Interceptor - Parameterized (defn auth-system [env] (if (dev? env) {:name
::fake-auth :enter (fn [context] (assoc context :account (fake-account context)))} {:name ::third-party-auth :enter (fn [context] (go (assoc context :account (call-auth-system context))))}))
Interceptor - Stateful (defn attach-database [uri] (let [db (db-pool uri)]
{:name ::attach-database :enter (fn [context] (assoc context ::db db))}))
Interceptor - Custom Ordering (defroutes routes ["/api" [(requires rate-limiter :account)]
["/slack" [(provides slack-auth :account)] ...] ["/hipchat" [(provides hipchat-auth :account)] ...]])
Interceptor - Dynamic Ordering (keys context) [... :io.pedestal.interceptor.chain/queue :io.pedestal.interceptor.chain/stack ...]
Interceptor - Dynamic Routing GET /docs auth → params →
read-db → handler → to-json GET /docs/secret.pdf auth → File (streaming) POST /docs auth → params → upload → process file → write-db → SSE
Interceptor - Performance
Interceptor - Performance
Reify all the things! @pithyless