Look at the content as well as the config to infer clojure eval

Before only the config was looked at to determine whether to include
the js minified of not, now look at the html as well. This makes it
more convenient to set up all klipse stuff once in config.edn without
having to manually specify which js to include.
This commit is contained in:
Aleksander Madland Stapnes 2017-01-27 02:57:33 -03:00
parent f7f02f7433
commit 70c95b41d8
3 changed files with 105 additions and 41 deletions

View file

@ -113,7 +113,7 @@
(merge-meta-and-content file-name page-meta content) (merge-meta-and-content file-name page-meta content)
{:uri (page-uri file-name :page-root-uri config) {:uri (page-uri file-name :page-root-uri config)
:page-index (:page-index page-meta) :page-index (:page-index page-meta)
:klipse (klipse/emit (:klipse config) (:klipse page-meta))}))) :klipse (klipse/merge-configs (:klipse config) (:klipse page-meta))})))
(defn parse-post (defn parse-post
"Return a map with the given post's information." "Return a map with the given post's information."
@ -131,7 +131,7 @@
:parsed-archive-group (.parse archive-fmt formatted-group) :parsed-archive-group (.parse archive-fmt formatted-group)
:uri (page-uri file-name :post-root-uri config) :uri (page-uri file-name :post-root-uri config)
:tags (set (:tags page-meta)) :tags (set (:tags page-meta))
:klipse (klipse/emit (:klipse config) (:klipse page-meta))})))) :klipse (klipse/merge-configs (:klipse config) (:klipse page-meta))}))))
(defn read-posts (defn read-posts
"Returns a sequence of maps representing the data from markdown files of posts. "Returns a sequence of maps representing the data from markdown files of posts.
@ -477,11 +477,15 @@
(println (green "compiling assets...")) (println (green "compiling assets..."))
(let [{:keys [^String site-url blog-prefix rss-name recent-posts sass-dest keep-files ignored-files previews? author-root-uri theme] (let [{:keys [^String site-url blog-prefix rss-name recent-posts sass-dest keep-files ignored-files previews? author-root-uri theme]
:as config} (read-config) :as config} (read-config)
posts (add-prev-next (read-posts config)) posts (map (fn [{:keys [klipse content] :as post}]
(assoc post :klipse (klipse/emit klipse content)))
(add-prev-next (read-posts config)))
posts-by-tag (group-by-tags posts) posts-by-tag (group-by-tags posts)
posts (tag-posts posts config) posts (tag-posts posts config)
latest-posts (->> posts (take recent-posts) vec) latest-posts (->> posts (take recent-posts) vec)
pages (read-pages config) pages (map (fn [{:keys [klipse content] :as page}]
(assoc page :klipse (klipse/emit klipse content)))
(read-pages config))
home-page (->> pages home-page (->> pages
(filter #(boolean (:home? %))) (filter #(boolean (:home? %)))
(first)) (first))

View file

@ -1,7 +1,8 @@
(ns cryogen-core.klipse (ns cryogen-core.klipse
(:require (:require [clojure.string :as str]
[camel-snake-kebab.core :refer [->snake_case_string ->camelCaseString]] [camel-snake-kebab.core :refer [->snake_case_string ->camelCaseString]]
[cheshire.core :as json])) [cheshire.core :as json]
[net.cgrand.enlive-html :as enlive]))
;;;;;;;;;;; ;;;;;;;;;;;
;; utils ;; utils
@ -28,6 +29,25 @@
v2)) v2))
ms)) ms))
(defn filter-html-elems
"Recursively walks a sequence of enlive-style html elements depth first
and returns a flat sequence of the elements where (pred elem)"
[pred html-elems]
(reduce (fn [acc {:keys [content] :as elem}]
(into (if (pred elem) (conj acc elem) acc)
(filter-html-elems pred content)))
[] html-elems))
(defn code-block-classes
"Takes a string of html and returns a sequence of
all the classes on all code blocks."
[html]
(->> html
enlive/html-snippet
(filter-html-elems (comp #{:code} :tag))
(keep (comp :class :attrs))
(mapcat #(str/split % #" "))))
;;;;;;;;;;;; ;;;;;;;;;;;;
;; klipse ;; klipse
@ -46,10 +66,19 @@
"A set of selectors that imply clojure evaluation." "A set of selectors that imply clojure evaluation."
#{"selector" "selector_reagent"}) #{"selector" "selector_reagent"})
(defn clojure-eval? (defn clojure-eval-classes
"Do the settings include any keys that imply clojure eval?" "Takes settings and returns a set of the html classes that imply clojure eval."
[normalized-settings] [normalized-settings]
(some clojure-selectors (keys normalized-settings))) (reduce (fn [classes selector]
(if-let [klass (get normalized-settings selector)]
(conj classes (->> klass rest (apply str))) ;; Strip the leading .
classes))
#{} clojure-selectors))
(defn clojure-eval?
"Takes settings and html and returns whether there is any clojure eval."
[normalized-settings html]
(boolean (some (clojure-eval-classes normalized-settings) (code-block-classes html))))
(defn normalize-settings (defn normalize-settings
"Transform the keys to the correct snake-case or camelCase strings." "Transform the keys to the correct snake-case or camelCase strings."
@ -61,25 +90,24 @@
(defn merge-configs (defn merge-configs
"Merges the defaults, global config and post config, "Merges the defaults, global config and post config,
transforms lisp-case keywords into snake_case/camelCase strings transforms lisp-case keywords into snake_case/camelCase strings
and infers whether to use minified or non-minified js Returns nil if there's no post-config.
(this can be overridden by supplying a :js key). A post-config with the value true counts as an empty map."
If the post config is just true, uses only the global config.
Returns nil if either there's no post-config or if there's no settings after merging."
[global-config post-config] [global-config post-config]
(when post-config (when post-config
(let [post-config (if (true? post-config) {} post-config) (let [post-config (if (true? post-config) {} post-config)]
merged-config (deep-merge defaults (deep-merge defaults
(update-existing global-config :settings normalize-settings) (update-existing global-config :settings normalize-settings)
(update-existing post-config :settings normalize-settings))] (update-existing post-config :settings normalize-settings)))))
;; It would make more sense to return nil if there's no selector specified
;; instead of no settings, but that would be too much of a (defn infer-clojure-eval
;; maintenance burden, as there are new selectors added all the time. "Infers whether there's clojure eval and returns the config with the
(when (:settings merged-config) appropriate value assoc'd to :js.
(if (:js merged-config) Returns the config untouched if :js is already specified."
merged-config [config html]
(assoc merged-config :js (if (clojure-eval? (:settings merged-config)) (if (:js config)
:non-min config
:min))))))) (assoc config :js
(if (clojure-eval? (:settings config) html) :non-min :min))))
(defn include-css [href] (defn include-css [href]
(str "<link rel=\"stylesheet\" type=\"text/css\" href=" (pr-str href) ">")) (str "<link rel=\"stylesheet\" type=\"text/css\" href=" (pr-str href) ">"))
@ -90,9 +118,9 @@
(defn emit (defn emit
"Takes the :klipse config from config.edn and the :klipse config from the "Takes the :klipse config from config.edn and the :klipse config from the
current post, and returns the html to include on the bottom of the page." current post, and returns the html to include on the bottom of the page."
[global-config post-config] [config html]
(when-let [{:keys [settings js-src js css-base css-theme]} (when-let [{:keys [settings js-src js css-base css-theme]}
(merge-configs global-config post-config)] (infer-clojure-eval config html)]
(assert (#{:min :non-min} js) (assert (#{:min :non-min} js)
(str ":js needs to be one of :min or :non-min but was: " js)) (str ":js needs to be one of :min or :non-min but was: " js))

View file

@ -14,6 +14,44 @@
(is (= {:a {:b 1}} (deep-merge {:a {:b 1}} {:a nil}))) (is (= {:a {:b 1}} (deep-merge {:a {:b 1}} {:a nil})))
(is (= {:a {:b 1 :c 3}} (deep-merge {:a {:b 1 :c 2}} {:a {:c 3}})))) (is (= {:a {:b 1 :c 3}} (deep-merge {:a {:b 1 :c 2}} {:a {:c 3}}))))
;; For testing convenience.
(defn elt
"Returns an enlive style html element."
([tag] (elt tag nil))
([tag attrs & content]
{:tag tag, :attrs attrs, :content content}))
(deftest filter-html-elems-test
(is (= [(elt :div {:class "x"} :content [(elt :div {:class "x"} "foo")])
(elt :div {:class "x"} "foo")])
(filter-html-elems (comp #{"x"} :class :attrs)
[(elt :h1 {:class "y"} "things!")
(elt :div {:class "x"} (elt :div {:class "x"} "foo"))])))
(deftest code-block-classes-test
(is (= ["clojure" "ruby"]
(code-block-classes
"<h1>stuff</h1>
<div class=\"not-code\"><pre><code class=\"clojure\">(def x 42)</code></pre></div>
<pre><code class=\"ruby\">123</code><pre>"))))
(deftest clojure-eval-classes-test
(is (= #{"eval-cljs" "eval-reagent"}
(clojure-eval-classes {"selector" ".eval-cljs"
"selector_reagent" ".eval-reagent"
"selector_eval_ruby" ".eval-ruby"}))))
(deftest clojure-eval?-test
(is (clojure-eval? {"selector" ".eval-cljs"}
"<h1>stuff</h1>
<div class=\"not-code\"><pre><code class=\"eval-cljs\">(def x 42)</code></pre></div>
<pre><code class=\"ruby\">123</code><pre>"))
(is (not (clojure-eval? {"selector" ".eval-cljs"
"selector_eval_ruby" ".eval-ruby"}
"<h1>stuff</h1>
<pre><code class=\"eval-ruby\">123</code><pre>"))))
(deftest normalize-settings-test (deftest normalize-settings-test
(is (= {"selector_reagent" ".reagent" (is (= {"selector_reagent" ".reagent"
"codemirror_options_in" {"lineNumbers" true}} "codemirror_options_in" {"lineNumbers" true}}
@ -22,22 +60,16 @@
:codemirror-options-in {:line-numbers true}})))) :codemirror-options-in {:line-numbers true}}))))
(deftest merge-configs-test (deftest merge-configs-test
(testing "Things are merged correctly, and :js :non-min is inferred from :selector." (testing "Things are merged correctly"
(is (= (merge defaults (is (= (merge defaults
{:settings {"selector" ".clojure" {:settings {"selector" ".clojure-eval"
"codemirror_options_in" {"lineNumbers" true}} "codemirror_options_in" {"lineNumbers" true}}})
:js :non-min})
(merge-configs {:settings {:codemirror-options-in {:line-numbers true}}} (merge-configs {:settings {:codemirror-options-in {:line-numbers true}}}
{:settings {:selector ".clojure"}})))) {:settings {:selector ".clojure-eval"}}))))
(testing "If it's all set up in config.edn, in the post it can be just :klipse true" (testing "If it's all set up in config.edn, in the post it can be just :klipse true"
(is (= (merge defaults (is (= (merge defaults {:settings {"selector_js" ".javascript"}})
{:settings {"selector_js" ".javascript"}
:js :min})
(merge-configs {:settings {:selector-js ".javascript"}} true)))) (merge-configs {:settings {:selector-js ".javascript"}} true))))
(testing "Returns nil if there's nothing in the blog post" (testing "Returns nil if there's nothing in the blog post"
(is (nil? (merge-configs {:settings {:selector ".clojure"}} nil)))) (is (nil? (merge-configs {:settings {:selector ".clojure-eval"}} nil)))))
(testing "If there's no :settings, returns nil"
(is (nil? (merge-configs {:css-base "/css/base.css"} {:css-theme "/css/theme.css"})))))