Skip to content
/ blog Public

Beyond the Frame: blog and personal website of David Schmudde

Notifications You must be signed in to change notification settings

schmudde/blog

Repository files navigation

Beyond the Frame

This is the code for my blog and personal website, Beyond the Frame.

This README is also the build.boot file as a literate Babel document (aka org-babel). CTRL-c-v-t in Emacs will tangle code and generate the build.boot.

  • Install
    • boot
    • Pandoc and pandoc-sidenote: sudo apt-get install pandoc, sudo apt-get install pandoc-sidenote
  • To build the website, run boot build.
  • To start a development server, run boot dev.

Non-Clojure Dependencies

  • To run boot-livereload a JavaScript file named livereload.js must be saved under resources/js/livereload.js. More information is available from the LiveReload website, but the script is pretty simple: document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js?snipver=1"></' + 'script>').
  • Install CSS
  • Included CSS

Pandoc

  • Tufte Pandoc CSS
  • perun/pandoc builds the HTML from markdown in Perun.
    • sudo apt install pandoc
    • Supports footnotes, unlinke the default Perun plugin.
      • The default is equivalent to: pandoc 2020-04-11-perun-blog-2.md -f markdown -t html5 -o test.html
    • Pandoc Sidenote for creating sidenotes rather than footnotes.
      • Called by pandoc --filter pandoc-sidenote.
      • In Perun, default ["-f" "markdown" "-t" "html5"] &rarr; ["--from" "markdown" "--to" "html5" "--filter" "pandoc-sidenote"]
      • sudo apt-set install pandoc-sidenote
    • :cmd-opts — CMD line options to pass to pandoc, (default ["-f" "markdown" "-t" "html5"], which converts markdown files to html5).

Tufte CSS

Erik came up with a great way to embed videos in Tufte and keep them 16x9.

<figure>
    <div class="iframe-wrapper">
        <iframe src="https://www.youtube.com/embed/dQw4w9WgXcQ" frameborder="0" allowfullscreen></iframe>
    </div>
</figure>

But on mobile it doesn’t reach its full width.

figure {
    max-width: 90%;
}

So it can be centered.

figure {
    max-width: 90%;
    display: block;
    margin-left: auto;
    margin-right: auto;
}

The latest tufte.css, “1.8.0”, adds support for dark mode. I don’t want to deal with that so I’m sticking with “1.7.2”.

https://github.com/edwardtufte/tufte-css/commit/957e9c6dc3646ab1847ebe41fdb853e59ecf8579

/* Adds dark mode */
@media (prefers-color-scheme: dark) {
    body {
        background-color: #151515;
        color: #ddd;
    }
}

/* Adds dark mode */
@media (prefers-color-scheme: dark) {
    a:link, .tufte-underline, .hover-tufte-underline:hover {
        text-shadow: 0.03em 0 #151515, -0.03em 0 #151515, 0 0.03em #151515, 0 -0.03em #151515, 0.06em 0 #151515, -0.06em 0 #151515, 0.09em 0 #151515, -0.09em 0 #151515, 0.12em 0 #151515, -0.12em 0 #151515, 0.15em 0 #151515, -0.15em 0 #151515;
    }
}

The Boot Build File

Create the Environment

;; DO NOT EDIT
;; This file is tangled from README.org.

(set-env!
 :source-paths #{"src" "content"}
 :resource-paths #{"resources"}
 :dependencies '[[perun "0.4.3-SNAPSHOT" :scope "test"]
                 [nrepl "0.7.0" :scope "test"]
                 [hiccup "1.0.5" :exclusions [org.clojure/clojure]]
                 [javax.xml.bind/jaxb-api "2.3.0"] ;; new requirement after local system update
                 [pandeiro/boot-http "0.8.3" :exclusions [org.clojure/clojure]]
                 [deraen/boot-livereload "0.2.1"]
                 [time-literals "0.1.4"]
                 [ring/ring-mock "0.4.0"]
                 [cljc.java-time "0.1.11"]])

A few quick notes on the configuration.

  • :resource-paths #{"resources" "content"}: I keep my raw files in two places. My original writing is under content while resources has all of the necessary images, css, js, etc....
  • [deraen/boot-livereload "0.2.1"] : boot-livereload is an important part of any boot flow involving a browser. It reloads the browser when any files are changed.

TODO:

  • The [javax.xml.bind/jaxb-api "2.3.0"] requirement?
  • The two time requirements?
(require '[io.perun :as perun]
         '[pandeiro.boot-http :refer [serve]]
         '[deraen.boot-livereload :refer [livereload]])

Helper Functions

Is it a blog post, a webpage, or a book review? Has it been published?

  • post? and book? is based on the path.
  • program? pulls from posts, books, and programs but only selects things tagged #clojure.
(defn page? [{:keys [original-path] :as meta}]
  (if original-path
    (.startsWith original-path "pages/")
    false))

(defn post? [{:keys [original-path] :as meta}]
  (if original-path
    (.startsWith original-path "posts/")
    false))

(defn archive? [{:keys [original-path] :as meta}]
  (when original-path
    (clojure.string/includes? original-path "archive")))

(defn book?
  "In: {:original-path \"books\"}"
  [{:keys [original-path] :as meta}]
  (if original-path
    (.startsWith original-path "books/")
    false))

(defn program?
  "In: {:original-path \"programs\"}"
  [{:keys [original-path] :as meta}]
  (if original-path
    (.startsWith original-path "programs/")
    false))

(defn tagged-clojure?
  "Note: an essay tagged #clojure could exist in /books, /posts, /program"
  [{:keys [tags original-path] :as meta}]
  (when (some #(= "clojure" %) tags)
    true))

(defn published?
  "In: {:date-published \"yes\"}"
  [{:keys [date-published] :as meta}]
  (if date-published true false))

The Main Build Task

This is the build task that builds the static site.

TODO: why do all css/ dirs update every time I render, but not the *.css files

(deftask build []
  (comp (perun/global-metadata :filename "site.base.edn")
        (perun/pandoc :cmd-opts ["--from" "markdown" "--to" "html5" "--filter" "pandoc-sidenote"])
        (perun/collection :renderer 'site.core/render-index-page :page "index.html"
                          :filterer (apply every-pred [post? published?]))
        (perun/collection :renderer 'site.core/render-index-page :page "books.html"
                          :filterer (apply every-pred [book? published?]))
        (perun/collection :renderer 'site.core/render-programs-index-page :page "programs.html"
                          :filterer (apply every-pred [tagged-clojure? published?]))

        (perun/render :renderer 'site.core/render-post-pages
                      :filterer (apply every-pred [post? published?])
                      :meta {:type "post"})
        (perun/render :renderer 'site.core/render-book-pages
                      :filterer (apply every-pred [book? published?])
                      :meta {:type "book"})
        (perun/render :renderer 'site.core/render-post-pages
                      :filterer (apply every-pred [program? published?])
                      :meta {:type "program"})

        (perun/tags :renderer 'site.core/render-tag-pages
                    :filterer (apply every-pred [(some-fn book? post? program?) published?])
                    :out-dir "public/tags")

        (perun/render :renderer 'site.core/render-post-pages
                      :filterer page?
                      :meta {:type "page"})
        (perun/static :renderer 'site.cv/render
                      :page "cv.html"
                      :meta {:type "page"})
        (perun/static :renderer 'site.timeline/render
                      :page "timeline.html"
                      :meta {:type "page"})
        (perun/collection :renderer 'site.previous-entries/render
                          :page "previous-entries.html"
                          :filterer (apply every-pred [(some-fn book? post? program?) published?]))
        (perun/rss :filterer (apply every-pred [post? published? #((comp not archive?) %)]))
        (perun/rss :site-title "Beyond the Frame: Clojure" :description "Essays about the Clojure programming language"
                   :filterer (apply every-pred [tagged-clojure? published?]) :filename "btf-clojure-feed.rss")
        (target)))

It’s complex task, so I broke out a few details below.

This site must render through pandoc to render tufte css-style sidenotes from the Markdown source. The pandoc-sidenote plugin does the heavy lifting.

The collection task renders links to all previous posts in index.html. render actually does the rendering.

(perun/pandoc :cmd-opts ["--from" "markdown" "--to" "html5" "--filter" "pandoc-sidenote"])
(perun/collection :renderer 'site.core/render-index-page :page "index.html"
                  :filterer (apply every-pred [post? published?]))
(perun/render :renderer 'site.core/render-post-pages
              :filterer (apply every-pred [post? published?])
              :meta {:type "post"})

The Development Task

The dev task sandwiches the build function between watch and serve. The former watches for any changes to your files and automatically recompiles. The latter serves those files to a web browser.

Perun offers an elegant way to inject the script into every page in the development environment. (livereload :asset-path "public" :filter #"\.(css|html|js)$") lets livereload know what to look for, while (perun/inject-scripts :scripts #{"js/livereload.js"}) loads the actual script.

(deftask dev []
  (comp (watch)
        (build)
        (perun/inject-scripts :scripts #{"js/livereload.js"})
        (livereload :asset-path "public" :filter #"\.(css|html|js)$")
        (serve :resource-root "public")))

Development Environment

There is no nrepl present. The current process:

  1. boot dev or boot dev repl if you want a REPL.
  2. Open a project file, *.clj, and cider-jack-in.

Updating the file will update the website.

Customize the jack-in command by tweaking the shell command it runs to include a dev profile.

C-u M-x cider-jack-in to specify the exact command for cider-jack-in. Specifically, add the dev profile. The current Cider Boot Parameters look like this: repl -s -b localhost wait. Just prefix the dev command and run: dev repl -s -b localhost wait. But refresh-on-save does not seem to work.

I need to customize the command line CIDER uses for cider-jack-in by modifying the following string options:

  • cider-boot-global-options: these are passed to the command directly, in first position (e.g., -o to lein enables offline mode).
  • cider-boot-parameters: these are usually task names and their parameters (e.g., dev for launching boot’s dev task instead of the standard repl -s wait).

Appendix

Plugins I would like to add someday:

  • (perun/sitemap :filename "sitemap.xml")
  • (perun/ttr)
  • (perun/word-count)
  • (perun/build-date)
  • (perun/paginate :renderer 'io.perun.example.paginate/render)
  • (perun/sitemap)
  • (perun/atom-feed :filterer :original)

Troubleshooting

Insert (perun/print-meta) into the (deftask dev [] ...) command to troubleshoot the build process.

boot --verbose build

boot show -f perun/markdown show -f: To inspect the files and metadata that is passed from task to task, there are two tasks we can use. The Boot built-in task show includes a convenient option to display a tree of all files in the fileset. To see how a task changes the fileset, you can use it like this: https://perun.io/guides/getting-started/

Regular build bug: [inject-scripts] - copied unchanged file public/posts/2020-04-29-info-to-knowledge.html

Improvements

  • elisp fiction to autocomplete keywords (vs. tags)

Tachyons measures widths using the border-box model. Tufte uses the content-box model. I experimented with simply switching the model in my custom css file, btf.css, but it broke the reflow (as expected).

body {
    -moz-box-sizing: content-box;
    -webkit-box-sizing: content-box;
    box-sizing: content-box;
}

TODO: The ideal solution is to use a custom Tachyons build.

lftp

lftp uses Transport Layer Security (TLS). So it’s essential to first grab the certificate from the FTP server.

openssl s_client -connect schmud.de:21 -starttls ftp
  1. I include the certificate chain in a new file called mycert.crt in the local ~/.lftp folder.
  2. I create a file called rc in the local ~/.lftp folder and add the lines
    • set ssl:ca-file "mycert.crt"
    • set ssl:check-hostname no (this prevents Fatal error: Certificate verification: certificate common name doesn't match requested host name ‘<ftp-hostname>’ when running a command like ls remotely)

Further reading:

Alternatively, it may be possible to use the Ubuntu certificates in some cases:

  • Grab the latest certificates: sudo update-ca-certificates
  • Update the /etc/lftp.conf by pointing to the certificate file set ssl:ca-file "/etc/ssl/certs/ca-certificates.crt"

Alternatively, alternatively certificate errors can be temporarily suppressed using set ssl:verify-certificate false at the lftp prompt

lftp commands include

  • local ls: run command locally
  • lcd: local change directory

Comment Log

(comment

  (published? {:date-published nil})
  (published? {:date-published "avril 14th"})

  (def path-data [{:original-path "posts/fefe"} {:original-path nil} {:original-path "po"} {:original-path "fee/fefef"} {:original-path "posts/zzz"} ])

  (def pub-data [{:date-published "avril 14th"} {:date-published nil} {:date-published "may 14th"}])

  (def pub-path-data [{:original-path "posts/fefe" :date-published "avril 14th"} {:original-path nil :date-published "date"} {:original-path "po" :date-published "may 14th"} {:original-path "fee/fefef" :date-published nil} {:original-path "posts/zzz" :date-published "may 14th"} ])

  (filter post? path-data)
  (filter published? pub-path-data)
  (filterv (and post? published?) pub-path-data)
  ; > ({:original-path "posts/fefe", :date-published "avril 14th"}
  ;    {:original-path nil, :date-published "date"}
  ;    {:original-path "po", :date-published "may 14th"}
  ;    {:original-path "posts/zzz", :date-published "may 14th"})
  (filter (or post? published?) pub-path-data)
  ; > ({:original-path "posts/fefe", :date-published "avril 14th"}
  ;    {:original-path "posts/zzz", :date-published "may 14th"})

  (filter (apply every-pred [post? published?]) pub-path-data)
  ; > ({:original-path "posts/fefe", :date-published "avril 14th"}
  ;    {:original-path "posts/zzz", :date-published "may 14th"})

  (map #(and (post? %) (published? %)) pub-path-data) ; (true false false false true)
  (map #(or (post? %) (published? %)) pub-path-data) ; (true true true false true)

  )

Editing Org Mode

  • <s &rarr; TAB: write a code block in a .org file.
    • C-c-v-t: tangle the file and produce
    • C-c: evaluate the Clojure code
    • C-c-e h: export to HTML, C-c-e b see it immediately in a browser window
    • Run these commands with
      • C-c C-c
      • C-c C-o: results in a separate buffer.
  • #+BEGIN_SRC shell :results code: the #+RESULTS: certificate must be as :results code rather than drawer, otherwise it will not render correctly in GitHub.
  • Clojure + Literate Programming originally inspired by Literate Clojure Ants

About

Beyond the Frame: blog and personal website of David Schmudde

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published