Skip to content

Instantly share code, notes, and snippets.

@theJohnnyBrown
Last active December 20, 2015 22:19
Show Gist options
  • Select an option

  • Save theJohnnyBrown/6204094 to your computer and use it in GitHub Desktop.

Select an option

Save theJohnnyBrown/6204094 to your computer and use it in GitHub Desktop.
How the anabolic website works
;; 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