Merge pull request #1 from cryogen-project/master

Update cryogen-core to latest version
This commit is contained in:
Torsten Uhlmann 2016-12-14 21:00:26 +01:00 committed by GitHub
commit ca2513f954
14 changed files with 628 additions and 201 deletions

View file

@ -1,16 +1,17 @@
(defproject cryogen-core "0.1.24" (defproject cryogen-core "0.1.43"
:description "Cryogen's compiler" :description "Cryogen's compiler"
:url "https://github.com/cryogen-project/cryogen-core" :url "https://github.com/cryogen-project/cryogen-core"
:license {:name "Eclipse Public License" :license {:name "Eclipse Public License"
:url "http://www.eclipse.org/legal/epl-v10.html"} :url "http://www.eclipse.org/legal/epl-v10.html"}
:dependencies [[org.clojure/clojure "1.7.0"] :dependencies [[org.clojure/clojure "1.8.0"]
[clj-rss "0.1.9"] [clj-rss "0.2.3"]
[me.raynes/fs "1.4.6"] [me.raynes/fs "1.4.6"]
[crouton "0.1.2"] [crouton "0.1.2"]
[cheshire "5.5.0"] [cheshire "5.6.3"]
[clj-text-decoration "0.0.3"] [clj-text-decoration "0.0.3"]
[io.aviso/pretty "0.1.18"] [io.aviso/pretty "0.1.33"]
[hiccup "1.0.5"] [hiccup "1.0.5"]
[selmer "0.8.5"] [selmer "1.10.1"]
[pandect "0.5.2"] [pandect "0.6.1"]
[hawk "0.2.11"]
[clj-tagsoup "0.3.0" :exclusions [org.clojure/clojure]]]) [clj-tagsoup "0.3.0" :exclusions [org.clojure/clojure]]])

0
resources/.gitkeep Normal file
View file

View file

@ -11,51 +11,65 @@
[cryogen-core.sass :as sass] [cryogen-core.sass :as sass]
[cryogen-core.markup :as m] [cryogen-core.markup :as m]
[cryogen-core.io :refer [cryogen-core.io :refer
[get-resource find-assets create-folder wipe-public-folder copy-resources [get-resource find-assets create-folder create-file-recursive create-file wipe-public-folder
copy-resources-from-theme]] copy-resources copy-resources-from-theme path]]
[cryogen-core.sitemap :as sitemap] [cryogen-core.sitemap :as sitemap]
[cryogen-core.rss :as rss])) [cryogen-core.rss :as rss])
(:import java.util.Locale))
(cache-off!) (cache-off!)
(def public "resources/public") (defn root-uri
"Creates the uri for posts and pages. Returns root-path by default"
(defn root-path [k config]
"Creates the root path for posts, tags and pages" (if-let [uri (k config)]
[config k] uri
(if-let [root (k config)] (config (-> k (name) (s/replace #"-uri$" "") (keyword)))))
(str "/" root "/") "/"))
(defn re-pattern-from-ext (defn re-pattern-from-ext
"Creates a properly quoted regex pattern for the given file extension" "Creates a properly quoted regex pattern for the given file extension"
[ext] [ext]
(re-pattern (str (s/replace ext "." "\\.") "$"))) (re-pattern (str (s/replace ext "." "\\.") "$")))
(defn find-entries
"Returns a list of files under the templates directory according to the
implemented Markup protocol and specified root directory. It defaults to
looking under the implemented protocol's subdirectory, but fallsback to look
at the templates directory."
[root mu ignored-files]
(let [assets (find-assets (path "templates" (m/dir mu) root)
(m/ext mu)
ignored-files)]
(if (seq assets)
assets
(find-assets (path "templates" root)
(m/ext mu)
ignored-files))))
(defn find-posts (defn find-posts
"Returns a list of markdown files representing posts under the post root in templates/md" "Returns a list of markdown files representing posts under the post root."
[{:keys [post-root ignored-files]} mu] [{:keys [post-root ignored-files]} mu]
(find-assets (str "templates/" (m/dir mu) post-root) (m/ext mu) ignored-files)) (find-entries post-root mu ignored-files))
(defn find-pages (defn find-pages
"Returns a list of markdown files representing pages under the page root in templates/md" "Returns a list of markdown files representing pages under the page root."
[{:keys [page-root ignored-files]} mu] [{:keys [page-root ignored-files]} mu]
(find-assets (str "templates/" (m/dir mu) page-root) (m/ext mu) ignored-files)) (find-entries page-root mu ignored-files))
(defn parse-post-date (defn parse-post-date
"Parses the post date from the post's file name and returns the corresponding java date object" "Parses the post date from the post's file name and returns the corresponding java date object"
[file-name date-fmt] [^String file-name date-fmt]
(let [fmt (java.text.SimpleDateFormat. date-fmt)] (let [fmt (java.text.SimpleDateFormat. date-fmt)]
(.parse fmt (.substring file-name 0 10)))) (.parse fmt (.substring file-name 0 10))))
(defn post-uri
"Creates a post uri from the post file name"
[file-name {:keys [blog-prefix post-root]} mu]
(str blog-prefix post-root (s/replace file-name (re-pattern-from-ext (m/ext mu)) ".html")))
(defn page-uri (defn page-uri
"Creates a page uri from the page file name" "Creates a URI from file name. `uri-type` is any of the uri types specified in config, e.g., `:post-root-uri`."
[page-name {:keys [blog-prefix page-root]} mu] ([file-name params]
(str blog-prefix page-root (s/replace page-name (re-pattern-from-ext (m/ext mu)) ".html"))) (page-uri file-name nil params))
([file-name uri-type {:keys [blog-prefix clean-urls?] :as params}]
(let [page-uri (params uri-type)
uri-end (if clean-urls? (s/replace file-name #"(index)?\.html" "/") file-name)]
(path "/" blog-prefix page-uri uri-end))))
(defn read-page-meta (defn read-page-meta
"Returns the clojure map from the top of a markdown page/post" "Returns the clojure map from the top of a markdown page/post"
@ -68,9 +82,10 @@
(defn page-content (defn page-content
"Returns a map with the given page's file-name, metadata and content parsed from "Returns a map with the given page's file-name, metadata and content parsed from
the file with the given markup." the file with the given markup."
[page config markup] [^java.io.File page config markup]
(with-open [rdr (java.io.PushbackReader. (reader page))] (with-open [rdr (java.io.PushbackReader. (reader page))]
(let [page-name (.getName page) (let [re-root (re-pattern (str "^.*?(" (:page-root config) "|" (:post-root config) ")/"))
page-name (s/replace (str page) re-root "")
file-name (s/replace page-name (re-pattern-from-ext (m/ext markup)) ".html") file-name (s/replace page-name (re-pattern-from-ext (m/ext markup)) ".html")
page-meta (read-page-meta page-name rdr) page-meta (read-page-meta page-name rdr)
content ((m/render-fn markup) rdr config)] content ((m/render-fn markup) rdr config)]
@ -85,7 +100,8 @@
(update-in page-meta [:layout] #(str (name %) ".html")) (update-in page-meta [:layout] #(str (name %) ".html"))
{:file-name file-name {:file-name file-name
:content content :content content
:toc (if (:toc page-meta) (generate-toc content))})) :toc (if-let [toc (:toc page-meta)]
(generate-toc content :list-type toc))}))
(defn parse-page (defn parse-page
"Parses a page/post and returns a map of the content, uri, date etc." "Parses a page/post and returns a map of the content, uri, date etc."
@ -93,7 +109,7 @@
(let [{:keys [file-name page-meta content]} (page-content page config markup)] (let [{:keys [file-name page-meta content]} (page-content page config markup)]
(merge (merge
(merge-meta-and-content file-name page-meta content) (merge-meta-and-content file-name page-meta content)
{:uri (page-uri file-name config markup) {:uri (page-uri file-name :page-root-uri config)
:page-index (:page-index page-meta)}))) :page-index (:page-index page-meta)})))
(defn parse-post (defn parse-post
@ -102,13 +118,15 @@
(let [{:keys [file-name page-meta content]} (page-content page config markup)] (let [{:keys [file-name page-meta content]} (page-content page config markup)]
(merge (merge
(merge-meta-and-content file-name page-meta content) (merge-meta-and-content file-name page-meta content)
(let [date (parse-post-date file-name (:post-date-format config)) (let [date (if (:date page-meta)
archive-fmt (java.text.SimpleDateFormat. "yyyy MMMM" (java.util.Locale. "en")) (.parse (java.text.SimpleDateFormat. (:post-date-format config)) (:date page-meta))
(parse-post-date file-name (:post-date-format config)))
archive-fmt (java.text.SimpleDateFormat. (get config :archive-group-format "yyyy MMMM") (Locale/getDefault))
formatted-group (.format archive-fmt date)] formatted-group (.format archive-fmt date)]
{:date date {:date date
:formatted-archive-group formatted-group :formatted-archive-group formatted-group
:parsed-archive-group (.parse archive-fmt formatted-group) :parsed-archive-group (.parse archive-fmt formatted-group)
:uri (post-uri file-name config markup) :uri (page-uri file-name :post-root-uri config)
:tags (set (:tags page-meta))})))) :tags (set (:tags page-meta))}))))
(defn read-posts (defn read-posts
@ -119,7 +137,8 @@
(fn [mu] (fn [mu]
(->> (->>
(find-posts config mu) (find-posts config mu)
(map #(parse-post % config mu)))) (pmap #(parse-post % config mu))
(remove #(= (:draft? %) true))))
(m/markups)) (m/markups))
(sort-by :date) (sort-by :date)
reverse)) reverse))
@ -140,7 +159,7 @@
"Adds the uri and title of a post to the list of posts under each of its tags" "Adds the uri and title of a post to the list of posts under each of its tags"
[tags post] [tags post]
(reduce (fn [tags tag] (reduce (fn [tags tag]
(update-in tags [tag] (fnil conj []) (select-keys post [:uri :title :content :date]))) (update-in tags [tag] (fnil conj []) (select-keys post [:uri :title :content :date :enclosure])))
tags (:tags post))) tags (:tags post)))
(defn group-by-tags (defn group-by-tags
@ -161,11 +180,22 @@
(sort-by :parsed-group) (sort-by :parsed-group)
reverse)) reverse))
(defn group-for-author
"Groups the posts by author. If no post author if found defaults `default-author`."
[posts default-author]
(->> posts
(map #(select-keys % [:title :uri :date :formatted-archive-group :parsed-archive-group :author]))
(map #(update % :author (fn [author] (or author default-author))))
(group-by :author)
(map (fn [[author posts]]
{:author author
:posts posts}))))
(defn tag-info (defn tag-info
"Returns a map containing the name and uri of the specified tag" "Returns a map containing the name and uri of the specified tag"
[{:keys [blog-prefix tag-root]} tag] [config tag]
{:name (name tag) {:name (name tag)
:uri (str blog-prefix tag-root (name tag) ".html")}) :uri (page-uri (str (name tag) ".html") :tag-root-uri config)})
(defn add-prev-next (defn add-prev-next
"Adds a :prev and :next key to the page/post data containing the title and uri of the prev/next "Adds a :prev and :next key to the page/post data containing the title and uri of the prev/next
@ -184,66 +214,92 @@
sidebar-pages false} (group-by #(boolean (:navbar? %)) pages)] sidebar-pages false} (group-by #(boolean (:navbar? %)) pages)]
(map (partial sort-by :page-index) [navbar-pages sidebar-pages]))) (map (partial sort-by :page-index) [navbar-pages sidebar-pages])))
(defn write-html
"When `clean-urls?` is set, appends `/index.html` before spit; otherwise just spits."
[file-uri {:keys [clean-urls?]} data]
(if clean-urls?
(create-file-recursive (path file-uri "index.html") data)
(create-file file-uri data)))
(defn compile-pages (defn compile-pages
"Compiles all the pages into html and spits them out into the public folder" "Compiles all the pages into html and spits them out into the public folder"
[{:keys [blog-prefix page-root] :as params} pages] [{:keys [blog-prefix page-root-uri] :as params} pages]
(when-not (empty? pages) (when-not (empty? pages)
(println (blue "compiling pages")) (println (blue "compiling pages"))
(create-folder (str blog-prefix page-root)) (create-folder (path "/" blog-prefix page-root-uri))
(doseq [{:keys [uri] :as page} pages] (doseq [{:keys [uri] :as page} pages]
(println "\t-->" (cyan uri)) (println "\t-->" (cyan uri))
(spit (str public uri) (write-html uri
(render-file (str "/html/" (:layout page)) params
(merge params (render-file (str "/html/" (:layout page))
{:servlet-context "../" (merge params
:page page {:active-page "pages"
:uri uri})))))) :servlet-context (path "/" blog-prefix "/")
:page page
:uri uri}))))))
(defn compile-posts (defn compile-posts
"Compiles all the posts into html and spits them out into the public folder" "Compiles all the posts into html and spits them out into the public folder"
[{:keys [blog-prefix post-root disqus-shortname] :as params} posts] [{:keys [blog-prefix post-root-uri disqus-shortname] :as params} posts]
(when-not (empty? posts) (when-not (empty? posts)
(println (blue "compiling posts")) (println (blue "compiling posts"))
(create-folder (str blog-prefix post-root)) (create-folder (path "/" blog-prefix post-root-uri))
(doseq [post posts] (doseq [post posts]
(println "\t-->" (cyan (:uri post))) (println "\t-->" (cyan (:uri post)))
(spit (str public (:uri post)) (write-html (:uri post)
(render-file (str "/html/" (:layout post)) params
(merge params (render-file (str "/html/" (:layout post))
{:servlet-context "../" (merge params
:post post {:active-page "posts"
:disqus-shortname disqus-shortname :servlet-context (path "/" blog-prefix "/")
:uri (:uri post)})))))) :post post
:disqus-shortname disqus-shortname
:uri (:uri post)}))))))
(defn compile-tags (defn compile-tags
"Compiles all the tag pages into html and spits them out into the public folder" "Compiles all the tag pages into html and spits them out into the public folder"
[{:keys [blog-prefix tag-root] :as params} posts-by-tag] [{:keys [blog-prefix tag-root-uri] :as params} posts-by-tag]
(when-not (empty? posts-by-tag) (when-not (empty? posts-by-tag)
(println (blue "compiling tags")) (println (blue "compiling tags"))
(create-folder (str blog-prefix tag-root)) (create-folder (path "/" blog-prefix tag-root-uri))
(doseq [[tag posts] posts-by-tag] (doseq [[tag posts] posts-by-tag]
(let [{:keys [name uri]} (tag-info params tag)] (let [{:keys [name uri]} (tag-info params tag)]
(println "\t-->" (cyan uri)) (println "\t-->" (cyan uri))
(spit (str public uri) (write-html uri
(render-file "/html/tag.html" params
(merge params (render-file "/html/tag.html"
{:servlet-context "../" (merge params
:name name {:active-page "tags"
:posts posts :servlet-context (path "/" blog-prefix "/")
:uri uri}))))))) :name name
:posts posts
:uri uri})))))))
(defn compile-tags-page [{:keys [blog-prefix] :as params}] (defn compile-tags-page [{:keys [blog-prefix] :as params}]
(println (blue "compiling tags page")) (println (blue "compiling tags page"))
(spit (str public blog-prefix "/tags.html") (let [uri (page-uri "tags.html" params)]
(render-file "/html/tags.html" (write-html uri
(merge params params
{:uri (str blog-prefix "/tags.html")})))) (render-file "/html/tags.html"
(merge params
{:active-page "tags"
:servlet-context (path "/" blog-prefix "/")
:uri uri})))))
(defn content-until-more-marker
[^String content]
(let [index (.indexOf content "<!--more-->")]
(if (pos? index)
(let [s (subs content 0 index)]
(->> ((tagsoup/parse-string s) 2)
(drop 2)
hiccup/html)))))
(defn create-preview (defn create-preview
"Creates a single post preview" "Creates a single post preview"
[blocks-per-preview post] [blocks-per-preview post]
(merge (select-keys post [:title :author :date :uri]) (merge post
{:content (or (re-find #".+?(?=<!--more-->)" (:content post)) {:content (or (content-until-more-marker (:content post))
(->> ((tagsoup/parse-string (:content post)) 2) (->> ((tagsoup/parse-string (:content post)) 2)
(drop 2) (drop 2)
(take blocks-per-preview) (take blocks-per-preview)
@ -253,18 +309,18 @@
"Returns a sequence of vectors, each containing a set of post previews" "Returns a sequence of vectors, each containing a set of post previews"
[posts-per-page blocks-per-preview posts] [posts-per-page blocks-per-preview posts]
(->> posts (->> posts
(reduce (fn [v post] (conj v (create-preview blocks-per-preview post))) []) (map #(create-preview blocks-per-preview %))
(partition-all posts-per-page) (partition-all posts-per-page)
(map-indexed (fn [i v] {:index (inc i) :posts v})))) (map-indexed (fn [i v] {:index (inc i) :posts v}))))
(defn create-preview-links (defn create-preview-links
"Turn each vector of previews into a map with :prev and :next keys that contain the uri of the "Turn each vector of previews into a map with :prev and :next keys that contain the uri of the
prev/next preview page" prev/next preview page"
[previews blog-prefix] [previews params]
(mapv (fn [[prev target next]] (mapv (fn [[prev target next]]
(merge target (merge target
{:prev (if prev (str blog-prefix "/p/" (:index prev)) nil) {:prev (if prev (page-uri (path "p" (str (:index prev) ".html")) params) nil)
:next (if next (str blog-prefix "/p/" (:index next)) nil)})) :next (if next (page-uri (path "p" (str (:index next) ".html")) params) nil)}))
(partition 3 1 (flatten [nil previews nil])))) (partition 3 1 (flatten [nil previews nil]))))
(defn compile-preview-pages (defn compile-preview-pages
@ -272,54 +328,95 @@
[{:keys [blog-prefix posts-per-page blocks-per-preview] :as params} posts] [{:keys [blog-prefix posts-per-page blocks-per-preview] :as params} posts]
(when-not (empty? posts) (when-not (empty? posts)
(let [previews (-> (create-previews posts-per-page blocks-per-preview posts) (let [previews (-> (create-previews posts-per-page blocks-per-preview posts)
(create-preview-links blog-prefix)) (create-preview-links params))
previews (if (> (count previews) 1) (assoc-in previews [1 :prev] (str blog-prefix "/index.html")) previews)] previews (if (> (count previews) 1) (assoc-in previews [1 :prev] (page-uri "index.html" params)) previews)]
(create-folder (str blog-prefix "/p/")) (create-folder (path "/" blog-prefix "p"))
(doseq [{:keys [index posts prev next]} previews] (doseq [{:keys [index posts prev next]} previews
(spit (if (= 1 index) (str public blog-prefix "/index.html") (str public blog-prefix "/p/" index)) :let [index-page? (= 1 index)]]
(render-file "/html/previews.html" (write-html (if index-page? (page-uri "index.html" params) (page-uri (path "p" (str index ".html")) params))
(merge params params
{:servlet-context (if (= 1 index) "" "../") (render-file "/html/previews.html"
:posts posts (merge params
:prev-uri prev {:active-page "preview"
:next-uri next}))))))) :home (when index-page? true)
:servlet-context (path "/" blog-prefix "/")
:posts posts
:prev-uri prev
:next-uri next})))))))
(defn compile-index (defn compile-index
"Compiles the index page into html and spits it out into the public folder" "Compiles the index page into html and spits it out into the public folder"
[{:keys [blog-prefix disqus?] :as params}] [{:keys [disqus?] :as params}]
(println (blue "compiling index")) (println (blue "compiling index"))
(spit (str public blog-prefix "/index.html") (let [uri (page-uri "index.html" params)]
(render-file "/html/home.html" (write-html uri
(merge params params
{:home true (render-file "/html/home.html"
:disqus? disqus? (merge params
:post (get-in params [:latest-posts 0]) {:active-page "home"
:uri (str blog-prefix "/index.html")})))) :home true
:disqus? disqus?
:post (get-in params [:latest-posts 0])
:uri uri})))))
(defn compile-archives (defn compile-archives
"Compiles the archives page into html and spits it out into the public folder" "Compiles the archives page into html and spits it out into the public folder"
[{:keys [blog-prefix] :as params} posts] [{:keys [blog-prefix] :as params} posts]
(println (blue "compiling archives")) (println (blue "compiling archives"))
(spit (str public blog-prefix "/archives.html") (let [uri (page-uri "archives.html" params)]
(render-file "/html/archives.html" (write-html uri
(merge params params
{:archives true (render-file "/html/archives.html"
:groups (group-for-archive posts) (merge params
:uri (str blog-prefix "/archives.html")})))) {:active-page "archives"
:archives true
:groups (group-for-archive posts)
:servlet-context (path "/" blog-prefix "/")
:uri uri})))))
(defn compile-authors
"For each author, creates a page with filtered posts."
[{:keys [blog-prefix author-root-uri author] :as params} posts]
(println (blue "compiling authors"))
(create-folder (path "/" blog-prefix author-root-uri))
;; if the post author is empty defaults to config's :author
(doseq [{:keys [author posts]} (group-for-author posts author)]
(let [uri (page-uri (str author ".html") :author-root-uri params)]
(println "\t-->" (cyan uri))
(write-html uri
params
(render-file "/html/author.html"
(merge params
{:author author
:groups (group-for-archive posts)
:servlet-context (path "/" blog-prefix "/")
:uri uri}))))))
(defn tag-posts (defn tag-posts
"Converts the tags in each post into links" "Converts the tags in each post into links"
[posts config] [posts config]
(map #(update-in % [:tags] (partial map (partial tag-info config))) posts)) (map #(update-in % [:tags] (partial map (partial tag-info config))) posts))
(defn- template-dir?
"Checks that the dir exists in the templates directory."
[dir]
(.isDirectory (file (str "resources/templates/" dir))))
(defn- markup-entries [post-root page-root]
(let [entries (for [mu (m/markups)
t (distinct [post-root page-root])]
[(str (m/dir mu) "/" t) t])]
(apply concat entries)))
(defn copy-resources-from-markup-folders (defn copy-resources-from-markup-folders
"Copy resources from markup folders" "Copy resources from markup folders. This does not copy the markup entries."
[config] [{:keys [post-root page-root] :as config}]
(copy-resources (let [folders (->> (markup-entries post-root page-root)
(merge config (filter template-dir?))]
{:resources (for [mu (m/markups) (copy-resources
t ["posts" "pages"]] (str (m/dir mu) "/" t)) (merge config
:ignored-files (map #(re-pattern-from-ext (m/ext %)) (m/markups))}))) {:resources folders
:ignored-files (map #(re-pattern-from-ext (m/ext %)) (m/markups))}))))
(defn read-config (defn read-config
"Reads the config file" "Reads the config file"
@ -330,6 +427,9 @@
slurp slurp
read-string read-string
(update-in [:blog-prefix] (fnil str "")) (update-in [:blog-prefix] (fnil str ""))
(update-in [:page-root] (fnil str ""))
(update-in [:post-root] (fnil str ""))
(update-in [:tag-root-uri] (fnil str ""))
(update-in [:rss-name] (fnil str "rss.xml")) (update-in [:rss-name] (fnil str "rss.xml"))
(update-in [:rss-filters] (fnil seq [])) (update-in [:rss-filters] (fnil seq []))
(update-in [:sass-src] (fnil str "css")) (update-in [:sass-src] (fnil str "css"))
@ -339,9 +439,8 @@
(update-in [:ignored-files] (fnil seq [#"^\.#.*" #".*\.swp$"])))] (update-in [:ignored-files] (fnil seq [#"^\.#.*" #".*\.swp$"])))]
(merge (merge
config config
{:page-root (root-path :page-root config) {:page-root-uri (root-uri :page-root-uri config)
:post-root (root-path :post-root config) :post-root-uri (root-uri :post-root-uri config)}))
:tag-root (root-path :tag-root config)}))
(catch Exception _ (catch Exception _
(throw (IllegalArgumentException. "Failed to parse config.edn"))))) (throw (IllegalArgumentException. "Failed to parse config.edn")))))
@ -349,22 +448,24 @@
"Generates all the html and copies over resources specified in the config" "Generates all the html and copies over resources specified in the config"
[] []
(println (green "compiling assets...")) (println (green "compiling assets..."))
(let [{:keys [site-url blog-prefix rss-name recent-posts sass-src sass-dest keep-files ignored-files previews?] :as config} (read-config) (let [{:keys [^String site-url blog-prefix rss-name recent-posts sass-src sass-dest keep-files ignored-files previews? author-root-uri] :as config} (read-config)
posts (add-prev-next (read-posts config)) posts (add-prev-next (read-posts config))
pages (add-prev-next (read-pages config)) pages (add-prev-next (read-pages config))
[navbar-pages sidebar-pages] (group-pages pages) [navbar-pages sidebar-pages] (group-pages pages)
posts-by-tag (group-by-tags posts) posts-by-tag (group-by-tags posts)
posts (tag-posts posts config) posts (tag-posts posts config)
params (merge config params (merge config
{:title (:site-title config) {:today (java.util.Date.)
:title (:site-title config)
:active-page "home"
:tags (map (partial tag-info config) (keys posts-by-tag)) :tags (map (partial tag-info config) (keys posts-by-tag))
:latest-posts (->> posts (take recent-posts) vec) :latest-posts (->> posts (take recent-posts) vec)
:navbar-pages navbar-pages :navbar-pages navbar-pages
:sidebar-pages sidebar-pages :sidebar-pages sidebar-pages
:archives-uri (str blog-prefix "/archives.html") :archives-uri (page-uri "archives.html" config)
:index-uri (str blog-prefix "/index.html") :index-uri (page-uri "index.html" config)
:tags-uri (str blog-prefix "/tags.html") :tags-uri (page-uri "tags.html" config)
:rss-uri (str blog-prefix "/" rss-name) :rss-uri (path "/" blog-prefix rss-name)
:site-url (if (.endsWith site-url "/") (.substring site-url 0 (dec (count site-url))) site-url) :site-url (if (.endsWith site-url "/") (.substring site-url 0 (dec (count site-url))) site-url)
:theme-path (str "file:resources/templates/themes/" (:theme config))})] :theme-path (str "file:resources/templates/themes/" (:theme config))})]
@ -383,16 +484,19 @@
(compile-preview-pages params posts) (compile-preview-pages params posts)
(compile-index params)) (compile-index params))
(compile-archives params posts) (compile-archives params posts)
(when author-root-uri
(println (blue "generating authors views"))
(compile-authors params posts))
(println (blue "generating site map")) (println (blue "generating site map"))
(spit (str public blog-prefix "/sitemap.xml") (sitemap/generate site-url ignored-files)) (create-file (path "/" blog-prefix "sitemap.xml") (sitemap/generate site-url ignored-files))
(println (blue "generating main rss")) (println (blue "generating main rss"))
(spit (str public blog-prefix "/" rss-name) (rss/make-channel config posts)) (create-file (path "/" blog-prefix rss-name) (rss/make-channel config posts))
(println (blue "generating filtered rss")) (println (blue "generating filtered rss"))
(rss/make-filtered-channels public config posts-by-tag) (rss/make-filtered-channels config posts-by-tag)
(println (blue "compiling sass")) (println (blue "compiling sass"))
(sass/compile-sass->css! (sass/compile-sass->css!
{:src-sass sass-src {:src-sass sass-src
:dest-sass (str "../public" blog-prefix "/" sass-dest) :dest-sass (path ".." "public" blog-prefix sass-dest)
:ignored-files ignored-files :ignored-files ignored-files
:base-dir "resources/templates/"}))) :base-dir "resources/templates/"})))

View file

@ -25,7 +25,7 @@
(catch Exception e {:error (.getMessage e)}))] (catch Exception e {:error (.getMessage e)}))]
(when-not (:error git-resp) (when-not (:error git-resp)
(if-let [git-src (json/parse-string git-resp)] (if-let [git-src (json/parse-string git-resp)]
{:content (String. (Base64/decodeBase64 (get git-src "content")) "UTF-8") {:content (String. ^bytes (Base64/decodeBase64 ^String (get git-src "content")) "UTF-8")
:name (get git-src "name") :name (get git-src "name")
:uri (get (get git-src "_links") "html")})))) :uri (get (get git-src "_links") "html")}))))

View file

@ -1,9 +1,18 @@
(ns cryogen-core.io (ns cryogen-core.io
(:require [clojure.java.io :as io] (:require [clojure.java.io :as io]
[clojure.string :as s]
[me.raynes.fs :as fs])) [me.raynes.fs :as fs]))
(def public "resources/public") (def public "resources/public")
(defn path
"Creates path from given parts, ignore empty elements"
[& path-parts]
(->> path-parts
(remove s/blank?)
(s/join "/")
(#(s/replace % #"/+" "/"))))
(defn re-filter [bool-fn re & other-res] (defn re-filter [bool-fn re & other-res]
(let [res (conj other-res re)] (let [res (conj other-res re)]
(reify java.io.FilenameFilter (reify java.io.FilenameFilter
@ -17,7 +26,7 @@
(-> resource io/resource io/file)) (-> resource io/resource io/file))
(defn ignore [ignored-files] (defn ignore [ignored-files]
(fn [file] (fn [^java.io.File file]
(let [name (.getName file) (let [name (.getName file)
matches (map #(re-find % name) ignored-files)] matches (map #(re-find % name) ignored-files)]
(not (some seq matches))))) (not (some seq matches)))))
@ -27,19 +36,26 @@
extension (ext) ignoring any files that match the given (ignored-files). extension (ext) ignoring any files that match the given (ignored-files).
First make sure that the root directory exists, if yes: process as normal; First make sure that the root directory exists, if yes: process as normal;
if no, return empty vector." if no, return empty vector."
[f ext ignored-files] [f ^String ext ignored-files]
(if-let [root (get-resource f)] (if-let [root (get-resource f)]
(->> (get-resource f) (->> (get-resource f)
file-seq file-seq
(filter (ignore ignored-files)) (filter (ignore ignored-files))
(filter (fn [file] (-> file .getName (.endsWith ext))))) (filter (fn [^java.io.File file] (-> file .getName (.endsWith ext)))))
[])) []))
(defn create-folder [folder] (defn create-folder [folder]
(let [loc (io/file (str public folder))] (let [loc (io/file (path public folder))]
(when-not (.exists loc) (when-not (.exists loc)
(.mkdirs loc)))) (.mkdirs loc))))
(defn create-file [file data]
(spit (path public file) data))
(defn create-file-recursive [file data]
(create-folder (.getParent (io/file file)))
(create-file file data))
(defn wipe-public-folder [keep-files] (defn wipe-public-folder [keep-files]
(let [filenamefilter (reify java.io.FilenameFilter (accept [this _ filename] (not (some #{filename} keep-files))))] (let [filenamefilter (reify java.io.FilenameFilter (accept [this _ filename] (not (some #{filename} keep-files))))]
(doseq [path (.listFiles (io/file public) filenamefilter)] (doseq [path (.listFiles (io/file public) filenamefilter)]
@ -47,9 +63,9 @@
(defn copy-dir [src target ignored-files] (defn copy-dir [src target ignored-files]
(fs/mkdirs target) (fs/mkdirs target)
(let [filename-filter (apply reject-re-filter ignored-files) (let [^java.io.FilenameFilter filename-filter (apply reject-re-filter ignored-files)
files (.listFiles (io/file src) filename-filter)] files (.listFiles (io/file src) filename-filter)]
(doseq [f files] (doseq [^java.io.File f files]
(let [out (io/file target (.getName f))] (let [out (io/file target (.getName f))]
(if (.isDirectory f) (if (.isDirectory f)
(copy-dir f out ignored-files) (copy-dir f out ignored-files)
@ -58,7 +74,7 @@
(defn copy-resources [{:keys [blog-prefix resources ignored-files]}] (defn copy-resources [{:keys [blog-prefix resources ignored-files]}]
(doseq [resource resources] (doseq [resource resources]
(let [src (str "resources/templates/" resource) (let [src (str "resources/templates/" resource)
target (str public blog-prefix "/" (fs/base-name resource))] target (path public blog-prefix (fs/base-name resource))]
(cond (cond
(not (.exists (io/file src))) (not (.exists (io/file src)))
(throw (IllegalArgumentException. (str "resource " src " not found"))) (throw (IllegalArgumentException. (str "resource " src " not found")))

View file

@ -1,4 +1,5 @@
(ns cryogen-core.markup (ns cryogen-core.markup
(:require [clojure.string :as s])
(:import java.util.Collections)) (:import java.util.Collections))
(defonce markup-registry (atom [])) (defonce markup-registry (atom []))
@ -13,10 +14,11 @@
(defn rewrite-hrefs (defn rewrite-hrefs
"Injects the blog prefix in front of any local links "Injects the blog prefix in front of any local links
ex. <img src='/img/cryogen.png'/> becomes <img src='/blog/img/cryogen.png'/>"
ex. <img src='/img/cryogen.png'/> becomes <img src='/blog/img/cryogen.png'/>"
[blog-prefix text] [blog-prefix text]
(clojure.string/replace text #"href=.?/|src=.?/" #(str (subs % 0 (dec (count %))) blog-prefix "/"))) (if (s/blank? blog-prefix)
text
(clojure.string/replace text #"href=.?/|src=.?/" #(str (subs % 0 (dec (count %))) blog-prefix "/"))))
(defn markups (defn markups
"Return a vector of Markup implementations. This is the primary entry point "Return a vector of Markup implementations. This is the primary entry point
@ -24,3 +26,13 @@
Markups." Markups."
[] []
@markup-registry) @markup-registry)
(defn register-markup
"Add a Markup implementation to the registry."
[mu]
(swap! markup-registry conj mu))
(defn clear-registry
"Reset the Markup registry."
[]
(reset! markup-registry []))

View file

@ -4,7 +4,7 @@
[clojure.string :as s] [clojure.string :as s]
[text-decoration.core :refer :all])) [text-decoration.core :refer :all]))
(defn load-plugin [url] (defn load-plugin [^java.net.URL url]
(let [{:keys [description init]} (edn/read-string (slurp url))] (let [{:keys [description init]} (edn/read-string (slurp url))]
(println (green (str "loading module: " description))) (println (green (str "loading module: " description)))
(-> init str (s/split #"/") first symbol require) (-> init str (s/split #"/") first symbol require)
@ -12,7 +12,5 @@
(defn load-plugins [] (defn load-plugins []
(let [plugins (.getResources (ClassLoader/getSystemClassLoader) "plugin.edn")] (let [plugins (.getResources (ClassLoader/getSystemClassLoader) "plugin.edn")]
(loop [] (doseq [plugin (enumeration-seq plugins)]
(load-plugin (.. plugins nextElement openStream)) (load-plugin (. ^java.net.URL plugin openStream)))))
(when (.hasMoreElements plugins)
(recur)))))

View file

@ -1,18 +1,21 @@
(ns cryogen-core.rss (ns cryogen-core.rss
(:require [clj-rss.core :as rss] (:require [clj-rss.core :as rss]
[clojure.xml :refer [emit]] [text-decoration.core :refer :all]
[text-decoration.core :refer :all]) [cryogen-core.io :refer [create-file path]])
(:import java.util.Date)) (:import java.util.Date))
(defn posts-to-items [site-url posts] (defn posts-to-items [^String site-url posts]
(map (map
(fn [{:keys [uri title content date]}] (fn [{:keys [uri title content date enclosure author description]}]
(let [link (str (if (.endsWith site-url "/") (apply str (butlast site-url)) site-url) uri)] (let [link (str (if (.endsWith site-url "/") (apply str (butlast site-url)) site-url) uri)
enclosure (if (nil? enclosure) "" enclosure)]
{:guid link {:guid link
:link link :link link
:title title :title title
:description content :description (or description content)
:author author
:enclosure enclosure
:pubDate date})) :pubDate date}))
posts)) posts))
@ -26,8 +29,8 @@
:lastBuildDate (Date.)}) :lastBuildDate (Date.)})
(posts-to-items (:site-url config) posts))) (posts-to-items (:site-url config) posts)))
(defn make-filtered-channels [public {:keys [rss-filters blog-prefix] :as config} posts-by-tag] (defn make-filtered-channels [{:keys [rss-filters blog-prefix] :as config} posts-by-tag]
(doseq [filter rss-filters] (doseq [filter rss-filters]
(let [uri (str public blog-prefix "/" (name filter) ".xml")] (let [uri (path "/" blog-prefix (str (name filter) ".xml"))]
(println "\t-->" (cyan uri)) (println "\t-->" (cyan uri))
(spit uri (make-channel config (get posts-by-tag filter)))))) (create-file uri (make-channel config (get posts-by-tag filter))))))

View file

@ -15,19 +15,22 @@
(= 0 (:exit (sh "sass" "--version")))) (= 0 (:exit (sh "sass" "--version"))))
(defn compass-installed? (defn compass-installed?
"Checks for the installation of Compass." "Checks for the installation of Compass."
[] []
(= 0 (:exit (sh "compass" "--version")))) (try
(= 0 (:exit (sh "compass" "--version")))
(catch java.io.IOException _
false)))
(defn find-sass-files (defn find-sass-files
"Given a Diretory, gets files, Filtered to those having scss or sass "Given a Diretory, gets files, Filtered to those having scss or sass
extention. Ignores files matching any ignored regexps." extention. Ignores files matching any ignored regexps."
[base-dir dir ignored-files] [base-dir dir ignored-files]
(let [filename-filter (match-re-filter #"(?i:s[ca]ss$)")] (let [^java.io.FilenameFilter filename-filter (match-re-filter #"(?i:s[ca]ss$)")]
(->> (.listFiles (io/file base-dir dir) filename-filter) (->> (.listFiles (io/file base-dir dir) filename-filter)
(filter #(not (.isDirectory %))) (filter #(not (.isDirectory ^java.io.File %)))
(filter (ignore ignored-files)) (filter (ignore ignored-files))
(map #(.getName %))))) (map #(.getName ^java.io.File %)))))
(defn compile-sass-file! (defn compile-sass-file!
"Given a sass file which might be in src-sass directory, "Given a sass file which might be in src-sass directory,
@ -37,10 +40,9 @@
dest-sass dest-sass
base-dir]}] base-dir]}]
(shell/with-sh-dir base-dir (shell/with-sh-dir base-dir
(sh "sass" (if (compass-installed?)
"--update" (sh "sass" "--compass" "--update" (str src-sass ":" dest-sass))
(when (compass-installed?) "--compass") (sh "sass" "--update" (str src-sass ":" dest-sass)))))
(str src-sass ":" dest-sass))))
(defn compile-sass->css! (defn compile-sass->css!
"Given a directory src-sass, looks for all sass files and compiles them into "Given a directory src-sass, looks for all sass files and compiles them into
@ -55,7 +57,7 @@ the command. Shows you any problems it comes across when compiling. "
;; I found sass files, ;; I found sass files,
;; If sass is installed ;; If sass is installed
(do (do
(println "Compiling Sass Files:" src-sass dest-sass) (println "\t" (cyan src-sass) "-->" (cyan dest-sass))
(let [result (compile-sass-file! opts)] (let [result (compile-sass-file! opts)]
(if (zero? (:exit result)) (if (zero? (:exit result))
;; no problems in sass compilation ;; no problems in sass compilation

View file

@ -10,7 +10,7 @@
(let [fmt (java.text.SimpleDateFormat. "yyyy-MM-dd")] (let [fmt (java.text.SimpleDateFormat. "yyyy-MM-dd")]
(.format fmt date))) (.format fmt date)))
(defn loc [f] (defn loc [^java.io.File f]
(-> f (.getAbsolutePath) (.split "resources/public/") second)) (-> f (.getAbsolutePath) (.split "resources/public/") second))
(defn generate [site-url ignored-files] (defn generate [site-url ignored-files]
@ -19,7 +19,7 @@
{:tag :urlset {:tag :urlset
:attrs {:xmlns "http://www.sitemaps.org/schemas/sitemap/0.9"} :attrs {:xmlns "http://www.sitemaps.org/schemas/sitemap/0.9"}
:content :content
(for [f (find-assets "public" ".html" ignored-files)] (for [^java.io.File f (find-assets "public" ".html" ignored-files)]
{:tag :url {:tag :url
:content :content
[{:tag :loc [{:tag :loc

View file

@ -1,9 +1,11 @@
(ns cryogen-core.toc (ns cryogen-core.toc
(:require [crouton.html :as html] (:require [clojure.zip :as z]
[crouton.html :as html]
[hiccup.core :as hiccup])) [hiccup.core :as hiccup]))
(def _h [:h1 :h2 :h3 :h4 :h5 :h6]) (def _h [:h1 :h2 :h3 :h4 :h5 :h6])
(defn- compare_index [i1 i2] (- (.indexOf _h i2) (.indexOf _h i1)))
(defn- compare-index [i1 i2] (- (.indexOf ^clojure.lang.APersistentVector _h i2) (.indexOf ^clojure.lang.APersistentVector _h i1)))
(defn- get-headings (defn- get-headings
"Turn a body of html content into a vector of elements whose tags are "Turn a body of html content into a vector of elements whose tags are
@ -18,35 +20,96 @@
headings))) headings)))
[] content)) [] content))
(defn make-links (defn- zip-toc-tree-to-insertion-point
"Create a table of contents from the given headings. This function will look "Given a toc-tree zipper and a header level, navigate
for either: the zipper to the appropriate parent of the level for that header
(1) headings with a child anchor with a non-nil name attribute, e.g. to be inserted and return the zipper."
<h1><a name=\"reference\">Reference Title</a></h1> [toctree h-tag]
or (if-let [current-tag (-> toctree first :value :tag)]
(2) headings with an id attribute, e.g. <h1 id=\"reference\">Reference Title</h1> (let [direction (compare-index h-tag current-tag)]
In both cases above, the anchor reference becomes \"#reference\" and the (cond (zero? direction) (z/up toctree) ; Tag belongs at current level
anchor text is \"Reference Title\"." (neg? direction) toctree ; Tag belongs below this level
(pos? direction) (recur (z/up toctree) h-tag))) ; Keep looking up
; This level is the root list, return it
toctree))
(defn- insert-toc-tree-entry
"Inserts a toc-tree (zipper) entry for the given entry at the appropriate place.
Obeys the invariant that the toc-tree (zipper) is always moved to the inserted loc."
[tree entry]
(let [{htag :tag} entry
tree (zip-toc-tree-to-insertion-point tree htag)]
(-> tree (z/append-child {:children [] :value entry}) z/down z/rightmost)))
(defn- build-toc-tree
"Given a sequence of header nodes, build a toc tree using zippers
and return it."
[headings] [headings]
(loop [items headings acc nil _last nil] (loop [zp (z/zipper
map?
:children
(fn [node children] (assoc node :children (apply vector children)))
{:value :root :children []})
items headings]
(if-let [{tag :tag {id :id} :attrs [{{name :name} :attrs} title :as htext] :content} (first items)] (if-let [{tag :tag {id :id} :attrs [{{name :name} :attrs} title :as htext] :content} (first items)]
(let [anchor (or id name)] (let [anchor (or id name)]
(if (nil? anchor) (if (nil? anchor)
(recur (rest items) acc nil) (recur zp (rest items))
(let [entry [:li [:a {:href (str "#" anchor)} (or title (first htext))]] (recur (insert-toc-tree-entry zp
jump (compare_index _last tag)] {:tag tag
(cond (> jump 0) (recur (rest items) (str acc "<ol>" (hiccup/html entry)) tag) :anchor anchor
(= jump 0) (recur (rest items) (str acc (hiccup/html entry)) tag) :text (or
(< jump 0) (recur (rest items) (str acc (apply str (repeat (* -1 jump) "</ol>")) (if (string? title) title (-> title :content first))
(hiccup/html entry)) tag))))) (first htext))})
(str acc "</ol>")))) (rest items))))
(z/root zp))))
(defn generate-toc [html]
(-> html (defn- make-toc-entry
(.getBytes "UTF-8") "Given an anchor link and some text, construct a toc entry
(java.io.ByteArrayInputStream.) consisting of link to the anchor using the given text, wrapped
(html/parse) in an <li> tag."
:content [anchor text]
(get-headings) (when (and anchor text)
(make-links) [:li [:a {:href (str "#" anchor)} text]]))
(clojure.string/replace-first #"ol" "ol class=\"contents\"")))
(defn- build-toc
"Given the root of a toc tree and either :ol or :ul,
generate the table of contents and return it as a hiccup tree."
[toc-tree list-open & {:keys [outer-list?] :or {outer-list? true}}]
(let [{:keys [children], {:keys [anchor text]} :value} toc-tree
li (make-toc-entry anchor text)
first-list-open (if outer-list?
(keyword (str (name list-open) ".content"))
list-open)]
; Create hiccup sequence of :ol/:ul tag and sequence of :li tags
(if (seq children)
(let [sublist [first-list-open (map build-toc children
(repeat list-open)
(repeat :outer-list?)
(repeat false))]]
(if-let [li li] ; The root element has nothing so ignore it
(seq [li sublist]) ; Use seq to lazily concat the li with the sublists
sublist))
li))) ; Or just return the naked :li tag
(defn generate-toc
"Reads an HTML string and parses it for headers, then returns a list of links
to them.
Optionally, a map of :list-type can be provided with value :ul, :ol, or true.
:ol and true will result in an ordered list being generated for the table of
contents, while :ul will result in an unordered list. The default is an
ordered list."
[^String html & {:keys [list-type] :or {list-type :ol}}]
(let [list-type (if (true? list-type) :ol list-type)]
(-> html
(.getBytes "UTF-8")
(java.io.ByteArrayInputStream.)
(html/parse)
:content
(get-headings)
(build-toc-tree)
(build-toc list-type)
(hiccup/html))))

View file

@ -1,14 +1,15 @@
(ns cryogen-core.watcher (ns cryogen-core.watcher
(:require [clojure.java.io :refer [file]] (:require [clojure.java.io :refer [file]]
[cryogen-core.io :refer [ignore]] [cryogen-core.io :refer [ignore]]
[pandect.core :refer [md5]] [pandect.algo.md5 :refer [md5]]
[hawk.core :as hawk]
[clojure.set :as set])) [clojure.set :as set]))
(defn get-assets [path ignored-files] (defn get-assets [path ignored-files]
(->> path (->> path
file file
file-seq file-seq
(filter #(not (.isDirectory %))) (filter #(not (.isDirectory ^java.io.File %)))
(filter (ignore ignored-files)))) (filter (ignore ignored-files))))
(defn checksums [path ignored-files] (defn checksums [path ignored-files]
@ -21,15 +22,15 @@
(when-some [changes (set/difference new-sum-set old-sum-set)] (when-some [changes (set/difference new-sum-set old-sum-set)]
(vals (select-keys new-sums changes))))) (vals (select-keys new-sums changes)))))
(defn watch-assets [root ignored-files action] (defn watch-assets [sums root ignored-files action]
(loop [sums (checksums root ignored-files)] (let [new-sums (checksums root ignored-files)]
(Thread/sleep 300) (when (find-changes @sums new-sums)
(let [new-sums (checksums root ignored-files)] (action)
(when (find-changes sums new-sums) (reset! sums new-sums))))
(action))
(recur new-sums))))
(defn start-watcher! [root ignored-files action] (defn start-watcher! [root ignored-files action]
(doto (Thread. #(watch-assets root ignored-files action)) (let [sums (atom (checksums root ignored-files))
(.setDaemon true) handler (fn [ctx e]
(.start))) (watch-assets sums root ignored-files action))]
(hawk/watch! [{:paths [root]
:handler handler}])))

View file

@ -0,0 +1,118 @@
(ns cryogen-core.compiler-test
(:require [clojure.test :refer :all]
[cryogen-core.compiler :refer :all]
[cryogen-core.io :refer [path]]
[cryogen-core.markup :as m]
[me.raynes.fs :as fs])
(:import [java.io File]))
; Test that the content-until-more-marker return nil or correct html text.
(deftest test-content-until-more-marker
; text without more marker, return nil
(is (nil? (content-until-more-marker "<div id=\"post\">
<div class=\"post-content\">
this post does not have more marker
</div>
</div>")))
; text with more marker, return text before more marker with closing tags.
(is (= (content-until-more-marker "<div id='post'>
<div class='post-content'>
this post has more marker
<!--more-->
and more content.
</div>
</div>")
"<div id=\"post\"><div class=\"post-content\">
this post has more marker
</div></div>")))
(defn- markdown []
(reify m/Markup
(dir [this] "md")
(ext [this] ".md")))
(defn- asciidoc []
(reify m/Markup
(dir [this] "asc")
(ext [this] ".asc")))
(defn- create-entry [dir file]
(fs/mkdirs (File. dir))
(fs/create (File. (str dir File/separator file))))
(defn- reset-resources []
(fs/delete-dir "resources")
(create-entry "resources" ".gitkeep"))
(defn- check-for-pages [mu]
(find-pages {:page-root "pages"} mu))
(defn- check-for-posts [mu]
(find-posts {:post-root "posts"} mu))
(deftest test-find-entries
(reset-resources)
(let [mu (markdown)]
(testing "Finds no files"
(is (empty? (check-for-posts mu))
(is (empty? (check-for-pages mu))))
(let [dir->file
[[check-for-posts "resources/templates/md/posts" "post.md"]
[check-for-posts "resources/templates/posts" "post.md"]
[check-for-pages "resources/templates/md/pages" "page.md"]
[check-for-pages "resources/templates/pages" "page.md"]]]
(doseq [[check-fn dir file] dir->file]
(testing (str "Finds files in " dir)
(create-entry dir file)
(let [entries (check-fn mu)]
(is (= 1 (count entries)))
(is (= (.getAbsolutePath (File. (str dir File/separator file)))
(.getAbsolutePath (first entries)))))
(reset-resources)))))))
(defmacro with-markup [mu & body]
`(do
(m/register-markup ~mu)
(try
~@body
(finally
(m/clear-registry)))))
(defn- copy-and-check-markup-folders
"Create entries in the markup folders. If `with-dir?` is set to true, include
the Markup implementation's `dir` in the path. Check that the folders exist
in the output folder."
[[pages-root posts-root :as dirs] mu with-dir?]
(doseq [dir dirs]
(let [path (if with-dir?
(str (m/dir mu) "/" dir)
dir)]
(create-entry (str "resources/templates/" path)
(str "entry" (m/ext mu)))))
(with-markup mu
(copy-resources-from-markup-folders
{:post-root posts-root
:page-root pages-root
:blog-prefix "/blog"}))
(doseq [dir dirs]
(is (.isDirectory (File. (str "resources/public/blog/" dir))))))
(deftest test-copy-resources-from-markup-folders
(reset-resources)
(testing "No pages or posts nothing to copy"
(copy-resources-from-markup-folders
{:post-root "pages"
:page-root "posts"
:blog-prefix "/blog"})
(is (not (.isDirectory (File. (str "resources/public/blog/pages")))))
(is (not (.isDirectory (File. (str "resources/public/blog/posts"))))))
(reset-resources)
(doseq [mu [(markdown) (asciidoc)]]
(testing (str "Test copy from markup folders (" (m/dir mu) ")")
(let [dirs ["pages" "posts"]]
(copy-and-check-markup-folders dirs mu true)
(reset-resources)
(copy-and-check-markup-folders dirs mu false))))
(reset-resources))

View file

@ -0,0 +1,109 @@
(ns cryogen-core.toc-test
(:require [clojure.test :refer :all]
[clojure.string :refer [join]]
[clojure.zip :as z]
[crouton.html :as html]
[hiccup.core :as hiccup]
[cryogen-core.toc :refer :all]))
; Reimport private functions
(def get-headings #'cryogen-core.toc/get-headings)
(def make-toc-entry #'cryogen-core.toc/make-toc-entry)
(def zip-toc-tree-to-insertion-point #'cryogen-core.toc/zip-toc-tree-to-insertion-point)
(def insert-toc-tree-entry #'cryogen-core.toc/insert-toc-tree-entry)
(def build-toc-tree #'cryogen-core.toc/build-toc-tree)
(def build-toc #'cryogen-core.toc/build-toc)
(defn parse-to-headings
[hiccup-seq]
(-> hiccup-seq hiccup/html html/parse-string :content get-headings))
; Test that the get-headings function properly filters non-headers
(deftest test-get-headings
(let [noisy-headers [:div [:h1 "First H1"]
[:p "Ignore..."]
[:h2 "First H2"]]]
(is (= (parse-to-headings noisy-headers)
[{:tag :h1 :attrs nil :content ["First H1"]}
{:tag :h2 :attrs nil :content ["First H2"]}]))))
; Test that the make-toc-entry ignores invalid input
(deftest test-make-toc-entry
(is (nil?
(make-toc-entry nil "Text")))
(is (nil?
(make-toc-entry "anchor" nil)))
(is (= [:li [:a {:href "#anchor"} "Text"]]
(make-toc-entry "anchor" "Text"))))
; Test that the built table of contents always treats later
; headers as being at the same level as earlier headers, even
; if the later headers are strictly greater in value.
; E.G.
; * h2
; * h3
; * h1
(deftest test-build-toc
(let [simplest-header [:div [:h2 [:a {:name "test"}] "Test"]]
no-headers [:div [:p "This is not a header"]]
closing-header-larger-than-opening-1
[:div [:h2 [:a {:name "starting_low"}]
"Starting Low"]
[:h1 [:a {:name "finishing_high"}]
"Finishing High"]]
closing-header-larger-than-opening-2
[:div [:h2 [:a {:name "starting_low"}]
"Starting Low"]
[:h4 [:a {:name "jumping_in"}]
"Jumping Right In"]
[:h3 [:a {:name "pulling_back"}]
"But then pull back"]
[:h2 [:a {:name "to_the_top"}]
"To the top"]]]
(is (= [:ol.content (seq [[:li [:a {:href "#test"} "Test"]]])]
(-> simplest-header parse-to-headings build-toc-tree
(build-toc :ol))))
(is (nil?
(-> no-headers parse-to-headings build-toc-tree
(build-toc :ol))))
(is (= [:ol.content (seq [[:li [:a {:href "#starting_low"} "Starting Low"]]
[:li [:a {:href "#finishing_high"} "Finishing High"]]])]
(-> closing-header-larger-than-opening-1
parse-to-headings
build-toc-tree
(build-toc :ol)))
"No outer header should be less indented than the first header tag.")
(is (= [:ul.content
(seq [
(seq [
[:li [:a {:href "#starting_low"} "Starting Low"]]
[:ul
(seq [
[:li [:a {:href "#jumping_in"} "Jumping Right In"]]
[:li [:a {:href "#pulling_back"} "But then pull back"]]
])
] ])
[:li [:a {:href "#to_the_top"} "To the top"]]
])]
(-> closing-header-larger-than-opening-2
parse-to-headings
build-toc-tree
(build-toc :ul)))
(join "" ["Inner headers can be more indented, "
"but outer headers cannot be less indented "
"than the original header."]))
))
(deftest test-generate-toc
(let [htmlString "<div><h2><a name=\"test\"></a>Test</h2></div>"]
(is (= "<ol class=\"content\"><li><a href=\"#test\">Test</a></li></ol>"
(generate-toc htmlString)))
(is (= "<ol class=\"content\"><li><a href=\"#test\">Test</a></li></ol>"
(generate-toc htmlString :list-type true)))
(is (= "<ol class=\"content\"><li><a href=\"#test\">Test</a></li></ol>"
(generate-toc htmlString :list-type :ol)))
(is (= "<ul class=\"content\"><li><a href=\"#test\">Test</a></li></ul>"
(generate-toc htmlString :list-type :ul)))))