Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Reifying the Call Stack
Search
Norbert Wójtowicz
January 16, 2018
Programming
120
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Reifying the Call Stack
Norbert Wójtowicz
January 16, 2018
More Decks by Norbert Wójtowicz
See All by Norbert Wójtowicz
Datalog: Data All Truth · Append Log · Obtain Graph
pithyless
2
930
Datalog: Biting the Silver Bullet
pithyless
0
300
Data-Oriented Architecture (LambdaDays 2016)
pithyless
3
940
ClojureScript + React.js: How I learned to stop worrying and love the browser.
pithyless
1
560
Other Decks in Programming
See All in Programming
[2026-09-26]空論ジェネリックプロセス~テスト資産とAIで紡ぐ、再現可能なパフォーマンスチューニングの話~
tosite
0
110
スマート反転とウェブアクセシビリティ
camiha
0
210
GitHubハンズオン講座 — 実務レベルのチーム開発のフローを身につけよう
junhat6
0
110
[Rails World 2026] Durable orchestration on Rails: from continuation to workflow
palkan
1
320
setup-vp GitLab対応の裏側
naokihaba
0
120
AHC070解法紹介
eijirou
0
140
モジュールの視点からSwiftを読み解く #iosdc
s_shimotori
0
200
KiroのSpecで「五目並べ」を作ってみる
satoshi256kbyte
1
220
ソニーのクラウド共通基盤の変遷とAI時代の開発スタイルに合わせた進化 / The Journey of Sony’s Common Cloud Platform and Its Evolution for AI-Native Development
kenjiyoneyama
0
100
MVNOの申込からeSIM開通までをiOSアプリでつなぐ- 本人確認・MNP・通信事業者基盤をまたぐ実装
satotakeshi
0
480
技術的負債の返済は、AI時代の複利で効く投資 — 経営としての意思決定とその遂行
curekoshimizu
0
1.9k
AgentCore CLI で進化した AWS での AI エージェントの作り方 : 必要な機能を必要な時に
icoxfog417
PRO
3
380
Featured
See All Featured
Rails Girls Zürich Keynote
gr2m
96
14k
Everyday Curiosity
cassininazir
0
330
Design of three-dimensional binary manipulators for pick-and-place task avoiding obstacles (IECON2024)
konakalab
0
600
DevOps and Value Stream Thinking: Enabling flow, efficiency and business value
helenjbeal
1
390
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
The Invisible Side of Design
smashingmag
301
52k
How to train your dragon (web standard)
notwaldorf
97
6.8k
How STYLIGHT went responsive
nonsquared
100
6.3k
コードの90%をAIが書く世界で何が待っているのか / What awaits us in a world where 90% of the code is written by AI
rkaga
63
46k
Unsuck your backbone
ammeep
672
58k
Are puppies a ranking factor?
jonoalderson
2
3.9k
VelocityConf: Rendering Performance Case Studies
addyosmani
331
25k
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