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
Running Rings Around Rack
Search
Alex Wheeler
November 16, 2017
0
84
Running Rings Around Rack
Clojure NYC
Alex Wheeler
November 16, 2017
Tweet
Share
More Decks by Alex Wheeler
See All by Alex Wheeler
Golang Concurrency
alexwheeler
0
88
Rewriting Rack: A Functional Approach
alexwheeler
2
150
Man Computer Symbiosis
alexwheeler
0
120
Flipper
alexwheeler
0
130
Transducers
alexwheeler
0
67
Character Encodings
alexwheeler
0
120
Featured
See All Featured
Making Projects Easy
brettharned
120
6.4k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4k
Faster Mobile Websites
deanohume
310
31k
No one is an island. Learnings from fostering a developers community.
thoeni
21
3.5k
Cheating the UX When There Is Nothing More to Optimize - PixelPioneers
stephaniewalter
285
14k
Typedesign – Prime Four
hannesfritz
42
2.8k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
XXLCSS - How to scale CSS and keep your sanity
sugarenia
249
1.3M
RailsConf 2023
tenderlove
30
1.3k
Docker and Python
trallard
46
3.6k
Designing Experiences People Love
moore
142
24k
Practical Orchestrator
shlominoach
190
11k
Transcript
Running Rings Around Rack Alex Wheeler
• @askwheeler • github.com/alexwheeler
• value-oriented programming • web server interfaces Ring (clojure), Rack
(ruby) • middleware
Clojure data structures • immutable • read-able • manipulated via
interfaces
• manipulated via interfaces
• “It is better to have 100 functions operate on
one data structure than to have 10 functions operate on 10 data structures” - Alan Jay Perlis
• “It is better to have 100 functions operate on
one data abstraction than 10 functions on 10 data structures.” - Rich Hickey
• Collection • Sequence • Associative • Indexed • Stack
• Set • Sorted
• Collection • Associative
(conj [1 2] 3) => [1 2 3] (conj '(1
2) 3) => '(3 1 2) (conj #{1 2} 3) => #{1 3 2} (conj {1 2} {3 4}) => {1 2, 3 4}
(assoc {} :name "Alex" :city "NYC") => {:name "Alex" :city
"NYC"} (assoc [1 2] 2 3) => [1 2 3] (assoc nil :name "Alex") => {:name "Alex"}
(def company {:employee-count 24}) (def employees-per-company [1 20 500]) (update
company :employee-count inc) => {:employee-counts 25} (update employees-per-company 0 inc) => [2 20 500] (:employee-count company) => 24 (first employees-per-company) => 1
None
None
None
None
None
None
None
None
(defn handle [req])
["/features" "sort-by=recent" {:content-type "application/json"} ""]
{ :uri "/features" :query-string "sort-by=recent" :headers { "content-type" "application/json"} :body
"" }
"/featuressort-by=recent{:content-type \"application/json\"}\"\""
Request { :uri "/features" :query-string "sort-by=recent" :headers { "content-type" "application/json"}
:body "" } Response { :status 200 :headers {} :body {} }
(defn handler [request] {:status 200 :headers {"Content-Type" "text/json"} :body ""})
(run-jetty handler {:port 3000}) class App def call(env) ['200', {'Content-Type' => 'application/json'}, []] end end Rack::Handler::WEBrick.run App.new
None
None
(defn handler [req] { :status 200 :headers {} :body ""
}) (defn timestamp [handler] (fn [request] (handler (assoc request :time (java.util.Date.))))) (def app (timestamp handler)) (run-jetty app {:port 3000})
(defn handler [req] { :status 200 :headers {} :body ""
}) (defn remove-sensitive-data [handler] (fn [request] (handler (dissoc request :bank-number)))) (defn timestamp [handler] (fn [request] (handler (assoc request :time (java.util.Date.))))) (def app (remove-sensitive-data (timestamp handler)))
(def sensitive-data [:foo]) (apply dissoc {:foo "2342345"} sensitive-data) => {}
(def sensitive-data [:foo]) (apply dissoc {:foo "2342345"} sensitive-data) => {}
SENSITIVE_DATA = [:foo] {foo: 'bar'}.delete_if {|k, v| SENSITIVE_DATA.include?(k)} => {}
(def sensitive-data [“bank-account-num“ “social-security”]) (defn remove-sensitive-data [handler] (fn [request] (handler
(update-in request [:params] #(apply dissoc % sensitive-data)))))
class RemoveSensitiveData SENSITIVE_DATA = [‘bank-account-num’, ‘social-security’] def initialize(app) @app =
app end def call(env) req = Rack::Request.new(env) req['params'].delete_if { |k, v| SENSITIVE_DATA.include?(k) } @app.call(env) end end
req['params'] => {bank-account-num: ‘1234324234’} req['params'].class => Hash
None
def call(env) req = Rack::Request.new(env) SENSITIVE_DATA.each do |bad_word| req.delete_param(bad_word) end
end
req.delete_param(‘social-security') { email: ‘
[email protected]
’ }.delete('email') bank_account.remove(‘bank-account-number‘)
None
(dissoc request :name) (dissoc person :email) (dissoc bank-account :social-security-number)
def call(env) req = Rack::Request.new(env) req['params'].delete_if { |k, v| BAD_WORDS.include?(v)
} Analytics.update(req) Logging.log(req) Database.update(req) @app.call(env) end
def call(env) req = Rack::Request.new(env) Analytics.update(req) Logging.log(req) Database.update(req) req['params'].delete_if {
|k, v| BAD_WORDS.include?(v) } @app.call(env) end
(defn remove-bad-words [handler] (fn [request] (analytics/update request) (logging/log request) (database/update
request) (handler (update-in request [:params] #(apply dissoc % bad-words)))))
app = Rack::Builder.new do use RemoveSensitiveData use AddTimestamp use LogAnalytics
use WriteDatabase run App.new end run app
(def app(m1(m2(m3(m4(m5(m6(m7 handler)))))))) (run-jetty app {:port 3000})
(def app (-> handler (remove-sensitive-data) (add-timestamp) (log-analytics) (write-database) (add-timestamp) (wrap-params)))
(run-jetty app {:port 3000})
Thanks! • alexwheeler.io • @askwheeler • github.com/alexwheeler