Last active
December 20, 2015 22:19
-
-
Save theJohnnyBrown/6204094 to your computer and use it in GitHub Desktop.
How the anabolic website works
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| ;; so I know you're probably curious what I'm up to that could possibly | |
| ;; take as long as it has to get a simple site running with a few pages and a | |
| ;; contact form, especially when you have essentially the same site already | |
| ;; running on your laptop. I wrote this document to explain why. | |
| ;; I will explain these things by showing you some code. I'm going to show the | |
| ;; interesting bits and leave out quite a bit. In other words, if you want this | |
| ;; stuff to actually work on your computer, clone the anabolic-website | |
| ;; repository and "use the source, Luke". | |
| ;; Anyway, here's what took up a load of my time today. Every page of the | |
| ;; template we are using has a div that looks like below. | |
| <div id="content">... lots of stuff here </div> | |
| ;; this div#content (as we would refer to it in CSS) has everything that goes | |
| ;; in between the header and the footer. It's really convenient that the | |
| ;; designer organized things this way. | |
| ;; Now for some background. The most basic clojure site, in the way I write | |
| ;; them, would work like below. | |
| ;; BASIC EXAMPLE SITE | |
| ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
| ;; This deftemplate means, "when I say '(base :content my-content)', I want an | |
| ;; HTML page with the content I give in 'my-content' and everything else taken | |
| ;; from index.html" | |
| ;; It's also possible to leave :content blank, and then we'll just get the | |
| ;; straight index.html page. That's what maybe-substitute means. So just | |
| ;; (base) means "show the index.html page with nothing changed" | |
| (html/deftemplate base "index.html" [& {:keys [content]}] | |
| [:#content] (maybe-substitute content)) | |
| ;; this defsnippet means, "when I say (other-page-content), give me the | |
| ;; <div id="content"> ... </div> from other-page.html | |
| (html/defsnippet other-page-content "other-page.html" [:#content] []) | |
| (defn home-page-view [req] | |
| (base)) | |
| ;; what happens in this view is we call base with a different content. | |
| ;; maybe-substitute sees that there is something to substitute, and | |
| ;; substitutes the other-page-content in place of whatever is in the index.html | |
| ;; content | |
| (defn other-page-view [req] | |
| (base :content (other-page-content))) | |
| ;; this sets up the URLs, so that example.com shows home-page-view, and | |
| ;; example.com/other-page/ shows the results of other-page-view | |
| (def routes | |
| (app | |
| [""] home-page-view | |
| ["other-page" ""] other-page-view)) | |
| ;; So that's a basic site. Although there's some more code involved to make sure | |
| ;; all the libraries are in place, handle forms, and do a few other things, | |
| ;; this is the idea. But what if there's more than two pages? what if there's | |
| ;; a design with 10 html pages? 50? For that, we can use macros. Macros are | |
| ;; a tool that automates the writing of code. Of course, I could do something | |
| ;; like write 50 defsnippets, 50 views, and an app statement with 50 URL lines. | |
| ;; We only have 10 or 20 or something, so it wouldn't be that hard to write each | |
| ;; one. | |
| ;; Instead of that, here's what I did for anabolic. | |
| ;; This "defmacro" takes the name of the page, and automatically writes code | |
| ;; like the defsnippet and "defn whatever-page-view" above. As a benefit of | |
| ;; writing all the snippet and view definitions in one place, when I noticed | |
| ;; some broken URLs on the page, I could fix them for all the pages at once | |
| ;; that's what's going on with absify-url and dirify-url below. | |
| (defmacro def-page-snippet [page] | |
| `(let [snip-sym# (symbol (str ~page "-content"))] | |
| (do | |
| (intern *ns* snip-sym# | |
| (html/snippet (str ~page ".html") [:#content] [] | |
| [[:img (relative-url :src)]] | |
| (absify-url :src) | |
| [[:a (html-url :href)]] (dirify-url :href))) | |
| (intern *ns* (symbol (str ~page "-view")) | |
| (render-request | |
| (fn [] | |
| (base | |
| :content ((eval (symbol "anabolic-website.core" | |
| (name snip-sym#)))) | |
| :nav (nav-snippet (str "/" ~page "/"))))))))) | |
| ;; Now that the macro is written, It's possible to just do this | |
| ;; list the names of all the html pages | |
| (def static-pages [ "about-us" "app-development" "online-marketing" | |
| "our-team" "projects" "services" "web-development" "website-hosting"]) | |
| ;; give each name in turn to def-page snippet. After this bit of code runs, | |
| ;; we will have available about-us-content and about-us-view, services-content | |
| ;; and services-view, etc. | |
| (doseq [pg static-pages] | |
| (def-page-snippet pg)) | |
| ;; last thing is to set up the URLs. This macro creates an app definition where | |
| ;; example.com/about-us/ goes to about-us-view, example.com/projects/ goes to | |
| ;; projects-view, and so on. If we get a new static page it's as easy as | |
| ;; dropping in the HTML file, and adding its name to the static-pages list. | |
| (defmacro static-page-routes [] | |
| `(app | |
| ~@(apply concat | |
| (for [sp static-pages] | |
| [[sp ""] (symbol (str sp "-view"))])))) | |
| (def routes (static-page-routes)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment