Inspired by sinatra, swlkr made Osprey, more opinionated and stripped down than his earlier Joy.
Currently, only (use osprey) or (import osprey :prefix "") work, not (import osprey) due to macro issues.
(use osprey )
(GET "/" "osprey" )
(server 9001 )
If you import, things won't work properly.
While in Joy, you pass a dictionary to joy/app or call the relevant middleservice in the object passed to joy/server, in Osprey you call configuration functions like layout on your layout (used on any routes returning an array, presumed to be hiccup-html):
(layout
(doctype :html5 )
[:html
[:head
[:title (request :path )]]
[:body response ]])
(GET "/"
[:h1 "home" ])
With multiple layouts, you may name and call them with use-layout:
(layout :a (doctype :html5 ) [:html ])
(GET "/something"
(use-layout :a )
[:div "/a!" ])
Similarly, not-found sets what returns if route or file not found:
(not-found
(status :404 )
(content-type "text/html" )
(html/encode
[:h1 "404 Page not found" ]))
The enable func sets some 4 entries a la Joy's app:
(enable :logging (fn [s _ _ ]
(def dur (* 1000 (- (os/clock ) s )))
(print "Hey it took " dur "ms." (if (> 0.02 dur ) " Not bad!" ))))
(enable :static-files )
# see https://github.com/swlkr/osprey/blob/master/examples/csrf-tokens.janet
(enable :sessions {:secure false })
(enable :csrf-tokens )
before will execute code before any route:
(before # "/todo/" # with this optional arg, only for such routes
(header "X-Powered-By" "osprey" )) # header adds this to the response
In general, there is a hook system to run code before or after the handler. Something like layout or not-found add a function to the hook list, while GET or POST push to *routes* which the handler checks.
enable aids us on more complex tasks on both sides of a handler, e.g. :sessions descrypts the cookie into (dyn :session) before the handler and after the handler encrypts (dyn :session)'s current state into a Set-Cookie header.
But ultimately, this is all just a different way of handling the handler pipeline/threading macro running through middlewares or hooks, like Joy.
Swlkr wrote these:
(use osprey
)
# enable session cookies
(enable :sessions {:secure false })
# wrap all html responses in the html below
(layout
(doctype :html5 )
[:html {:lang "en" }
[:head
[:title (request :uri )]]
[:body response ]])
(GET "/"
[:main
(if (session :session? )
[:p "yes, there is a session!" ]
[:p "no, there is not a session" ])
# the form helper is only available in route macros
# it also automatically assigns method to POST
(form {:action "/create-session" }
[:input {:type "submit" :value "Sign in" }])
(form {:action "/delete-session" }
[:input {:type "submit" :value "Sign out" }])])
(POST "/create-session"
(session :session? true )
(redirect "/" ))
(POST "/delete-session"
(session :session? nil )
(redirect "/" ))
(server 9001 )
(use osprey )
(defn protected? [request ]
(or (= (request :path ) "/" )
(= (request :path ) "/protected" )))
# halt with a 401
(before
(unless (protected? request )
(halt {:status 401 :body "Nope." :headers {"Content-Type" "text/plain" }})))
# wrap all html responses with layout
(layout
(doctype :html5 )
[:html {:lang "en" }
[:head
[:title (request :path )]]
[:body response ]])
# returns 200
(GET "/"
[:h1 "welcome to osprey!" ])
# halt works in handlers as well
# try curl -v 'localhost:9001?bypass='
(GET "/protected"
(unless (params :bypass )
(halt {:status 401 :body "Nope." :headers {"Content-Type" "text/plain" }}))
[:h1 "Yep!" ])
(server 9001 )
(use osprey )
(import json )
# put the todos somewhere
# since there isn't a database
(def todos @[])
# before everything try to parse json body
(before
(content-type "application/json" )
(when (and body (not (empty? body )))
(update request :body json/decode ))
# before urls with :id
(when (params :id )
(update-in request [:params :id ] scan-number )))
# just a nice status message on root
# try this
# $ curl -v localhost:9001
(GET "/"
(json/encode {:status "up" }))
# here's the meat and potatoes
# return the todos array from earlier
# try this
# $ curl -v localhost:9001/todos
(GET "/todos"
(json/encode todos ))
# this appends todos onto the array
# try this
# $ curl -v -X POST -H "Content-Type: application/json" --data '{"todo": "buy whole wheat bread"}' localhost:9001/todos
(POST "/todos"
(json/encode (array/push todos body )))
# this updates todos in the array
# :id is assumed to be an integer
# since todos is an array
# try this
# $ curl -v -X PATCH -H "Content-Type: application/json" --data '{"todo": "buy whole grain bread"}' localhost:9001/todos/0
(PATCH "/todos/:id"
(json/encode (update todos (params :id ) merge body )))
# this deletes todos from the array
# :id is assumed to be an integer
# since todos is an array
# try this
# $ curl -v -X DELETE localhost:9001/todos/0
(DELETE "/todos/:id"
(json/encode (array/remove todos (params :id ))))
# start the server on port 9001
(server 9001 )