Skip to content
Permalink
Browse files

Merge pull request #3 from inaimathi/patreaon-integration

Patreaon integration
  • Loading branch information
inaimathi committed Jan 10, 2020
2 parents bfa0927 + 1d8f8b0 commit 5bad020314a2e30a12ac5c94b5cc0a7584497033
@@ -12,4 +12,5 @@ pom.xml.asc
/.lein-*
/.nrepl-port
.hgignore
.hg/
.hg/
src/langnostic/conf.clj
@@ -7,8 +7,8 @@

## ToDo

- Add a simple comment system

- Fixed FS signal problem. The reload of posts.json seems to hang at around 111 on the live system (not on the dev system, oddly). I suspect memory issues. See what you can do about it, but don't stress; this is going away once you move post metadata into the post markup itself.
- Cache static pages, instead of reading them each time

- Revisit old blogs. NOW AT: `id:93`
@@ -0,0 +1,130 @@
In various Lisps, there's a semi-common pattern of context wrapping with error handling. You have a situation in which you want to do something, and in the process bind some external resource, then free the resource afterwards.

```
(let* ((resource (open-resource foo))
(result (progn
(bar)
(baz)
(mumble))))
(close-resource resource)
(delete-resource-from-disk resource)
result)
```

You can write a relatively simple wrapper macro for this situation.

```
(defmacro with-resource ((name target) &body body)
(let ((result (gensym)))
`(let* ((,name (open-resource ,target))
(,result (progn ,@body)))
(close-resource resource)
(delete-resource-from-disk resource)
,result)))
```

With that definition, you can instead write.

```
(with-resource (resource foo)
(bar)
(mumble (baz resource)))
```

Ok, but what if the code you've written throws an error somehow?

```
(with-resource (resource foo)
(bar)
(error "Arbitrary Explosion")
(mumble (baz resource)))
```

Your routine doesn't complete, but also, the claimed resource never gets freed afterwards. You could fix this by just always wrapping the stuff you wrap with `with-resource` in some error-trapping. `ignore-errors`/`handler-case`/`handler-bind` depending on the specific situation.

```
(with-resource (resource foo)
(ignore-errors
(bar)
(error "Arbitrary Explosion")
(mumble (baz resource))))
```

However, it'd still be nice to be more responsible as a macro developer and do the right thing with the bound resource without depending on your user doing the right thing.

```
(defun resolve-resource (resource)
(close-resource resource)
(delete-resource-from-disk resource)
nil)
(defmacro with-resource ((name target) &body body)
(let ((result (gensym)))
`(let ((,name (open-resource ,target)))
(handler-case
(let ((,result (ignore-errors (progn ,@body))))
(resolve-resource resource)
,result)
(error (c)
(resolve-resource resource)
(error c))))))
```

With that change, your macro can close out the claimed resource, and still propagate the error to users that are more careful about their own code. There's probably an even more elegant way to do this with [`handler-bind`](http://clhs.lisp.se/Body/m_handle.htm), but showing you that would be beside the point here.

## And Now, For Something Completely Different

You can do something similar in Python too. For example

```
@contextmanager
def logged(tag):
print(f"Starting a procedure <{tag}>...")
yield
print(f"Finished the procedure <{tag}>...")
```

```
>>> with logged("MY TEST"):
... print("blah")
...
Starting a procedure <MY TEST>...
blah
Finished the procedure <MY TEST>...
>>>
```

And now, the point of this entire post, _yes_, you _can_ wrap that `yield` in a `try`/`catch` and have it do what you're expecting.

```
@contextmanager
def logged(tag):
print(f"Starting a procedure <{tag}>...")
try:
yield
print("IT WORKED!")
except:
print("OH NO! ETERNAL DARKNESS AWAITS YOU!")
print(f"Finished the procedure <{tag}>...")
```

Now you can do

```
>>> with logged("MY TEST"):
... print("blah")
...
Starting a procedure <MY TEST>...
blah
IT WORKED!
Finished the procedure <MY TEST>...
>>> with logged("MY TEST"):
... raise("EXPLOSIONS")
...
Starting a procedure <MY TEST>...
OH NO! ETERNAL DARKNESS AWAITS YOU!
Finished the procedure <MY TEST>...
>>>
```

Hopefully, you found that reassuring.
@@ -6,12 +6,16 @@
:dependencies [[org.clojure/clojure "1.8.0"]

[http-kit "2.1.18"]
[org.apache.httpcomponents/httpclient "4.3.5"]
[oauth-clj "0.1.16-SNAPSHOT"]
[compojure "1.5.1"]
[clj-time "0.12.0"]
[im.chit/hara.io.scheduler "2.3.6"]
[clojure-watch "0.1.11"]
[javax.servlet/servlet-api "2.5"]

[com.patreon/patreon "0.4.2"]

[cheshire "5.6.3"]
[markdown-clj "0.9.89"]
[hiccup "1.0.5"]]
@@ -1,10 +1,12 @@
# TipJar

So. You've read a bunch of my crazy-ass ramblings, and you feel a compulsion to throw spare change at me.
So. You've read a bunch of my crazy-ass ramblings, and you're abuzz with energy that you'd like to convert into action. Here's what you can do...

Well, don't worry about it.
### Support My [Patreon](https://www.patreon.com/langnostic)

I'm a freelancing Toronto programmer, whose writing is crafted for the dual purposes of a log stream and my own amusement. You've paid me quite enough by reading. If that's really not good enough, and you feel the need to engage in reciprocation...
I'm [langnostic](https://www.patreon.com/langnostic) there, because someone else got `inaimathi`.

Right now, it's just you handing me spare change. You can log into this blog as long as you have a Patreon account. Coming soon is a comment system for patrons, and there'll be more interesting projects happening that involve supporters thereafter.

### Discuss

@@ -11,6 +11,9 @@ body { width: 100%; margin:0px; padding: 0px; font-family: sans-serif serif; ove
.top-menu a { color: #FFF; }
.top-menu a:hover { color: #999; }

.auth-button { margin-left: 60px; float: right; }
.auth-button .user-thumbnail { height: 16px; padding: 0px; padding-left: 5px; }

h1, h1 a { color: #CC0606; margin-bottom: 0px; text-decoration: none; }
.posted { font-style: oblique; color: #AAA; display: block; font-size: small; text-align: right; margin-bottom: 10px;}
h2 a, h3 a, h4 a, h5 a, h6 a { text-decoration: none; color: black; }
@@ -0,0 +1,23 @@
(ns langnostic.auth
(:require [oauth.patreon :as pat]

[langnostic.conf :as conf]))


(defn redirect-url [provider]
(str conf/REDIRECT-URL provider))

(def login-url
{"patreon" (pat/oauth-authorization-url (conf/patreon :id) (redirect-url "patreon"))})

(defmulti authenticate! (fn [provider code] provider))
(defmethod authenticate! "patreon"
[provider code]
(let [client (new com.patreon.PatreonOAuth (conf/patreon :id) (conf/patreon :secret) (redirect-url "patreon"))
toks (.getTokens client code)
api-client (new com.patreon.PatreonAPI (.getAccessToken toks))
user (.get (.fetchUser api-client))]
{:site "patreon"
:name (.getFullName user) :email (.getEmail user) :url (.getUrl user)
:image (.getImageUrl user) :thumbnail (.getThumbUrl user)
:pledges (.getPledges user)}))
@@ -1,42 +1,50 @@
(ns langnostic.core
(:require [org.httpkit.server :as server]
[cheshire.core :as json]
[org.httpkit.client :as http]
[compojure.route :as route]
[clojure-watch.core :as watch]
[ring.middleware.params :refer [wrap-params]]
[ring.middleware.session :refer [wrap-session]]

[clojure.java.io :as io]

[langnostic.auth :as auth]
[langnostic.feed :as feed]
[langnostic.pages :as pages]
[langnostic.posts :as posts]
[langnostic.files :as fs])
(:use [compojure.core :only [defroutes GET POST DELETE ANY context]])
(:gen-class))

(def error-404
(defn error-404
[user]
{:status 404
:headers {"Content-Type" "text/html"}
:body (pages/template
name name
(fs/file-content
"resources/public/content/404.md"))})

(defn resource-page [title file]
(if (fs/file-in-resources? file)
{:status 200
:headers {"Content-Type" "text/html"}
:body (pages/template file (clojure.string/capitalize title) (fs/file-content file))}
error-404))
"resources/public/content/404.md")
:user user)})

(defn static-page [name]
(fn [req] (resource-page name (io/file "resources/public/content" (str name ".md")))))
(fn [req]
(let [file (io/file "resources/public/content" (str name ".md"))
user (get-in req [:session :user])]
(if (fs/file-in-resources? file)
{:status 200
:headers {"Content-Type" "text/html"}
:body (pages/template file (clojure.string/capitalize name) (fs/file-content file) :user user)}
(error-404 user)))))

(defn post [name]
(fn [req]
(if-let [post (posts/find-by-slug name)]
{:status 200
:headers {"Content-Type" "text/html"}
:body (pages/template "blog" (post :title) (pages/post post))}
error-404)))
(let [user (get-in req [:session :user])]
(if-let [post (posts/find-by-slug name)]
{:status 200
:headers {"Content-Type" "text/html"}
:body (pages/template "blog" (post :title) (pages/post post) :user user)}
(error-404 user)))))

(defn home [req]
{:status 200
@@ -46,27 +54,46 @@
[:div
(fs/file-content "resources/public/content/intro.md")
[:hr]
(pages/latest-post)])})
(pages/latest-post)]
:user (get-in req [:session :user]))})

(defn archive [posts]
(fn [req]
{:status 200
:headers {"Content-Type" "text/html"}
:body (pages/template
"archive" "Archive"
(pages/archive posts))}))
(pages/archive posts)
:user (get-in req [:session :user]))}))

(defn atom-feed [posts]
(fn [req]
{:status 200
:headers {"Content-Type" "application/atom+xml"}
:body (feed/atom-feed posts)}))

(defn authenticate [auth-type]
(fn
[req]
(let [user (auth/authenticate! auth-type (get-in req [:params "code"]))]
{:status 303
:headers {"Location" "/"}
:session {:user user}})))

(defn log-out
[req]
{:status 303
:headers {"Location" "/"}
:session nil})

(defroutes langnostic-routes
(GET "/" [] home)
(GET "/blog" [] home)
(GET "/posts/:name" [name] (post name))

(GET "/auth/log-out" [] log-out)
(GET "/auth/:auth-type" [auth-type] (authenticate auth-type))

(GET "/archive" [] (archive (posts/all-posts)))
(GET "/archive/by-tag/:tag" [tag] (archive (posts/find-by-tag tag)))

@@ -80,7 +107,7 @@
(GET "/feed/atom/by-tag/:tag" [tag] (atom-feed (posts/find-by-tag tag)))

(route/resources "/static/")
(route/not-found error-404))
(route/not-found (fn [req] (error-404 (get-in req [:session :user])))))

(defn -main
([] (-main "8000"))
@@ -108,4 +135,8 @@
(reset! (post :content) nil))))}])

(println "Listening on port" port "...")
(server/run-server langnostic-routes {:port (read-string port)})))
(server/run-server
(-> langnostic-routes
wrap-params
wrap-session)
{:port (read-string port)})))

0 comments on commit 5bad020

Please sign in to comment.
You can’t perform that action at this time.