Helper code blocks constituting the project's *"Library of Babel"* are defined here. This code blocks work in *Emacs/Org* environment and are not supposed to be called from Clojure or ClojureScript. * time-is-now Returns current time as a string in a given format. *Examples:* #+BEGIN_EXAMPLE <> <> <> <> #+CALL: get-table-param(table=../project.org:project-specification, title="Project name") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{10cm}| :float nil | Name | Default value | Description | |-----------+---------------+---------------------------------------------------------------------------------------| | table | (quote ()) | Table reference. Reference format is ~[table containing org-file path:]table-name~, | | | | for example ~../file.org:table_name~ or just ~table_name~ if a table is given | | | | in the same file the call is done from. Default value simply allows return nil if | | | | parameter is not given, since internally code block receives a table as a list. | |-----------+---------------+---------------------------------------------------------------------------------------| | title | "" | Parameter name. Path-like parameter names are supported (i.e. names containing ~'/'~. | | | | If such /complex/ parameter is given, than it'll be splited, and each resulting part | | | | will be considered as a separate parameter name. The following parameter name search | | | | will be started from a row where the preceding parameter name has been found, a value | | | | of a preceding parameter is ignored. A value of the last parameter will be returned. | |-----------+---------------+---------------------------------------------------------------------------------------| | [splitby] | "" | Parameter value splitter. Optional. Works in pair with ~joinby~. If parameter values | | | | a listed in a cell as comma-separated list, for example, than, giving ~splitby=","~, | | | | will split'em, and splitted values will be joined with ~joinby~ value. | |-----------+---------------+---------------------------------------------------------------------------------------| | [joinby] | "" | Parameter value combinator. Optional. Works in pair with ~splitby~. | #+NAME: get-table-param #+BEGIN_SRC emacs-lisp :var table=(quote ()) :var title="" :var splitby="" :var joinby="" :hlines yes :results value silent (let* ((table-adopted (mapcar (lambda (x) (if (equal x 'hline) '(hline hline hline) x)) table)) (titles (mapcar #'car table-adopted)) (path (split-string title (regexp-quote "/"))) (last-index (reduce (lambda (index title) (cl-position title titles :start index :test #'equal)) path :initial-value nil))) (if last-index (let* ((result (nth 1 (nth last-index table-adopted)))) (if (and result splitby joinby) (mapconcat #'identity (split-string result (regexp-quote splitby) t "\s+") joinby) result)))) #+END_SRC * get-specification-param Returns parameter value given in [[#project-specification][the project specification table]] inside ~../project.org~ file. *Examples:* #+BEGIN_EXAMPLE <> <> #+CALL: get-specification-param(title="Project name") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{10cm}| :float nil | Name | Default value | Description | |-----------+---------------+------------------------------------------------------| | title | "" | Parameter name (see [[#lob-get-table-param][(get-table-param)]]). | | [splitby] | "" | Parameter value separator (see [[#lob-get-table-param][(get-table-param)]]). | | [joinby] | "" | Parameter value combinator (see. [[#lob-get-table-param][(get-table-param)]]). | #+NAME: get-specification-param #+BEGIN_SRC emacs-lisp :var title="" :var splitby="" :var joinby="" :results value silent (save-excursion (let ((project-org-dir (locate-dominating-file (buffer-file-name) "project.org"))) (if project-org-dir (let ((project-spec-ref (concat project-org-dir "project.org:project-specification"))) (org-babel-execute-src-block nil (cdr (assoc 'get-table-param org-babel-library-of-babel)) (list (cons :var (format "table=%s" project-spec-ref)) (cons :var (format "title=\"%s\"" title)) (cons :var (format "splitby=\"%s\"" splitby)) (cons :var (format "joinby=\"%s\"" joinby)))))))) #+END_SRC * in-some-path Returns an absolute path to a file in one of the project directories given in [[#project-specification][the project specification table]], will take project name into account and insert it as intermediate directory if requested. *Requirements*: Project specification must have following parameters defined: - Project name - Parameter given in ~param~ code block parameter. *Examples:* #+BEGIN_EXAMPLE #+BEGIN_SRC clojure :tangle (org-sbe in-some-path (param="Clojure/Sources path" path \"core.clj\")) ... #+END_SRC <> #+CALL: in-some-path(param="Clojure/Sources path", path="core.clj") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{8cm}| :float nil | Name | Default value | Description | |---------------+------------------------+-------------------------------------------------------------------------| | param | "Clojure/Sources path" | Parameter name defining the base directory (relative to project root) | | [path] | "" | Path relative to built directory name | | [projectvise] | t | Whether to insert project name between base directory path and the path | | | | part given in ~path~ parameter | #+NAME: in-some-path #+BEGIN_SRC emacs-lisp :var param="Clojure/Sources path" :var path="" :var projectvise='t :results value silent (save-excursion (let* ((project-org-dir (locate-dominating-file (buffer-file-name) "project.org")) (src-path (org-babel-execute-src-block nil (cdr (assoc 'get-specification-param org-babel-library-of-babel)) (list (cons :var (format "title=\"%s\"" param))))) (project-name (org-babel-execute-src-block nil (cdr (assoc 'get-specification-param org-babel-library-of-babel)) (list (cons :var "title=\"Project name\"") (cons :var "splitby=\".\"") (cons :var "joinby=\"/\"")))) (ns-path (replace-regexp-in-string "\-" "_" project-name))) (concat project-org-dir src-path "/" (if projectvise (concat ns-path "/") "") path))) #+END_SRC * in-clj-path Return an absolute path to a file in project's exported *Clojure* sources directory with respect to project name. *Requirements*: Project specification must have following parameters defined: - Project name - Clojure/Sources path *Examples:* #+BEGIN_EXAMPLE #+BEGIN_SRC clojure :tangle (org-sbe in-clj-path (path \"core.clj\")) ... #+END_SRC <> #+CALL: in-clj-path(path="core.clj") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{10cm}| :float nil | Name | Default value | Description | |------+---------------+------------------------------------------------------------------------------| | path | "" | Path relative to project's exported Clojure sources directory to be appended | #+NAME: in-clj-path #+BEGIN_SRC emacs-lisp :var path="" :results value silent (save-excursion (org-babel-execute-src-block nil (cdr (assoc 'in-some-path org-babel-library-of-babel)) (list (cons :var "param=\"Clojure/Sources path\"") (cons :var (format "path=\"%s\"" path))))) #+END_SRC * in-cljs-path Return an absolute path to a file in project's exported *Clojure Script* sources directory with respect to project name. *Requirements*: Project specification must have following parameters defined: - Project name - ClojureScript/Sources path *Examples:* #+BEGIN_EXAMPLE #+BEGIN_SRC clojure :tangle (org-sbe in-cljs-path (path \"core.clj\")) ... #+END_SRC <> #+CALL: in-cljs-path(path="core.clj") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{10cm}| :float nil | Name | Default value | Description | |------+---------------+-------------------------------------------------------------------------------------| | path | "" | Path relative to project's exported Clojure Script sources directory to be appended | #+NAME: in-cljs-path #+BEGIN_SRC emacs-lisp :var path="" :results value silent (save-excursion (org-babel-execute-src-block nil (cdr (assoc 'in-some-path org-babel-library-of-babel)) (list (cons :var "param=\"ClojureScript/Sources path\"") (cons :var (format "path=\"%s\"" path))))) #+END_SRC * in-tests-path Return an absolute path to a file in project's exported tests sources directory with respect to project name. *Requirements*: Project specification must have following parameters defined: - Project name - Tests path *Examples:* #+BEGIN_EXAMPLE #+BEGIN_SRC clojure :tangle (org-sbe in-tests-path (path \"core.clj\")) ... #+END_SRC <> #+CALL: in-tests-path(path="core.clj") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{10cm}| :float nil | Name | Default value | Description | |------+---------------+--------------------------------------------------------------------| | path | "" | Path relative to project's exported tests directory to be appended | #+NAME: in-tests-path #+BEGIN_SRC emacs-lisp :var path="" :results value silent (save-excursion (org-babel-execute-src-block nil (cdr (assoc 'in-some-path org-babel-library-of-babel)) (list (cons :var "param=\"Tests path\"") (cons :var (format "path=\"%s\"" path))))) #+END_SRC * in-resources-path Return an absolute path to a file in project's exported resources directory. *Requirements*: Project specification must have following parameters defined: - Project name - Resource path *Examples:* #+BEGIN_EXAMPLE #+BEGIN_SRC clojure :tangle (org-sbe in-resources-path (path \"index.html")) ... #+END_SRC <> #+CALL: in-resources-path(path="index.html") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{10cm}| :float nil | Name | Default value | Description | |------+---------------+--------------------------------------------------------------------------| | path | "" | A path relative to project's exported resources directory to be appended | #+NAME: in-resources-path #+BEGIN_SRC emacs-lisp :var path="" :results value silent (save-excursion (org-babel-execute-src-block nil (cdr (assoc 'in-some-path org-babel-library-of-babel)) (list (cons :var "param=\"Resource path\"") (cons :var (format "path=\"%s\"" path)) (cons :var "projectvise=()")))) #+END_SRC * in-assets-path Return an absolute path to a file in project's exported assets directory. *Requirements*: Project specification must have following parameters defined: - Project name - Assets path *Examples:* #+BEGIN_EXAMPLE #+BEGIN_SRC clojure :tangle (org-sbe in-resources-path (path \"index.html")) ... #+END_SRC <> #+CALL: in-resources-path(path="index.html") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{10cm}| :float nil | Name | Default value | Description | |------+---------------+-----------------------------------------------------------------------| | path | "" | A path relative to project's exported assets directory to be appended | #+NAME: in-assets-path #+BEGIN_SRC emacs-lisp :var path="" :results value silent (save-excursion (org-babel-execute-src-block nil (cdr (assoc 'in-some-path org-babel-library-of-babel)) (list (cons :var "param=\"Assets path\"") (cons :var (format "path=\"%s\"" path)) (cons :var "projectvise=()")))) #+END_SRC * in-target-path Returns an absolute path to a file in project's target directory. *Requirements*: Project specification must have following parameters defined: - Project name - Target path *Examples:* #+BEGIN_EXAMPLE #+BEGIN_SRC clojure :tangle (org-sbe in-target-path (path \"project.jar")) ... #+END_SRC <> #+CALL: in-target-path(path="project.jar") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{10cm}| :float nil | Name | Default value | Description | |------+---------------+--------------------------------------------------------------| | path | "" | A path relative to project's target directory to be appended | #+NAME: in-target-path #+BEGIN_SRC emacs-lisp :var path="" :results value silent (save-excursion (org-babel-execute-src-block nil (cdr (assoc 'in-some-path org-babel-library-of-babel)) (list (cons :var "param=\"Target path\"") (cons :var (format "path=\"%s\"" path)) (cons :var "projectvise=()")))) #+END_SRC * render-project-dependencies Renders (returns as a string) project dependencies, defined in [[#project-dependencies][the project dependencies table]], as a list of *Clojure* vectors. *Examples:* #+BEGIN_EXAMPLE <> #+CALL: render-project-dependencies() (org-sbe render-project-dependencies) #+END_EXAMPLE #+NAME: render-project-dependencies #+BEGIN_SRC emacs-lisp :results value silent ; Full dependency definition specification is given here ; https://github.com/cemerick/pomegranate/blob/master/src/main/clojure/cemerick/pomegranate/aether.clj ; in resolve-dependencies function (require 'subr-x) (require 'seq) (let ((project-org-dir (locate-dominating-file (buffer-file-name) "project.org"))) (if project-org-dir (let* ((project-spec-ref (concat project-org-dir "project.org:project-dependencies")) ; deps-table is a list of lists and hlines (deps-table (org-babel-ref-resolve project-spec-ref)) ; Dependency representing hash-map key traversing sequence (serialize-key-traversing-seq '(name version scope optional classifier extension exclusions))) (cl-labels ( ; dependency hash map has following keys ; - name - dependency artifact name ; - version - dependency artifact version ; - scope - dependency scope ; - optional - flag showing whether a dependency is optional, any value but "" and "no" is considered to be true ; - classifier - dependency Maven-classifier ; - extension - dependency Maven-extension ; - exclusions - list of transient dependency exclusions for a dependency (make-dependency (name version scope optional classifier extension exclusions) (let ((new-dep (make-hash-table))) (puthash 'name name new-dep) (puthash 'version (format "%s" version) new-dep) (puthash 'scope scope new-dep) (puthash 'optional (if (or (string-empty-p optional) (string= "no" (downcase optional))) nil 't) new-dep) (puthash 'classifier classifier new-dep) (puthash 'extension extension new-dep) (puthash 'exclusions exclusions new-dep) new-dep)) ; Dependency serializing function (serialize-dependency (dependency) (concat "[" ; Traversing every key in the dependency hash table and building dependency definition string (mapconcat (lambda (key) (let ((value (gethash key dependency))) (cond ; name is always given ((equal key 'name) value) ; I'm not sure but maybe version might be empty ((and (equal key 'version) (not (string-empty-p value))) (format "\"%s\"" value)) ; Scope is optional ((and (equal key 'scope) (not (string-empty-p value))) (format ":scope \"%s\"" value)) ; Optional flag should be set only if it's true ((and (equal key 'optional) value) ":optional true") ; Classifier is optinal ((and (equal key 'classifier) (not (string-empty-p value))) (format ":classifier \"%s\"" value)) ; Extension is optional ((and (equal key 'extension) (not (string-empty-p value))) (format ":extension \"%s\"" value)) ; Exclusions should be given if there're any ((and (equal key 'exclusions) (not (seq-empty-p value))) (concat ":exclusions [" (mapconcat #'serialize-dependency value " ") "]"))))) serialize-key-traversing-seq " ") "]"))) (let ( ; deps-adopted is a list of hash tables representing dependencies (deps-adopted (reduce (lambda (deps-adopted dependency) ; Skip any hlines and empty rows (if (or (equal dependency 'hline) (every #'string-empty-p dependency)) deps-adopted ; Else destructuring the dependency given (destructuring-bind (name version scope optional classifier extension exclusions ex-classifier ex-extension _) dependency (cond ; Append new dependency hash map in case there's a name given ((not (string-empty-p name)) (append deps-adopted (list (make-dependency name version scope optional classifier extension (if (not (string-empty-p exclusions)) (list (make-dependency exclusions "" "" "" ex-classifier ex-extension nil))))))) ; Append another exclusion in case there's no name but exclusions given ((and (string-empty-p name) (not (string-empty-p exclusions))) (let* ((last-dep (car (last deps-adopted)))) (puthash 'exclusions (append (gethash 'exclusions last-dep nil) (list (make-dependency exclusions "" "" "" ex-classifier ex-extension nil))) last-dep) deps-adopted)))))) ; Skipping title line (cdr deps-table) :initial-value nil))) ; So now I have list of dependencies (as hash maps) which I'm to transform into Clojure's vector of vectors ; in Clojure syntax (mapconcat #'serialize-dependency deps-adopted "\n")))))) #+END_SRC * render-code-block-if Renders a code block if [[#project-specification][the project specification table]] contains a parameter with a given name and a value passing an equality condition. *Requirements*: - A code block with a given name must be defined in file the call is done from. *Examples:* #+BEGIN_EXAMPLE <> #+CALL: render-codeblock-if(name="project-clojurescript-dependencies" if="ClojureScript=yes") #+END_EXAMPLE #+CAPTION: *Parameters:* #+ATTR_LATEX: :width \textwidth :align |l|l|p{10cm}| :float nil | Name | Default value | Description | |-----------+---------------+------------------------------------------------------------------------------------| | name | "" | A code block name to be rendered, the one given in it's ~#+NAME:~ dirrective. | | condition | "" | Rendering condition given as ~"parameter = value"~, where ~=~ is string comparison | | | | operation. | #+NAME: render-codeblock-if #+BEGIN_SRC emacs-lisp :var name="" :var condition="" :results value silent (save-excursion (destructuring-bind (&optional (param-name "") &optional (cond-value "")) (split-string condition "=" t "\s+") (let ((param-value (org-babel-execute-src-block nil (cdr (assoc 'get-specification-param org-babel-library-of-babel)) (list (cons :var (format "title=\"%s\"" param-name)))))) (if (string= param-value cond-value) (progn (org-babel-goto-named-src-block name) (let ((tangle-result (org-babel-tangle-single-block ""))) (nth 5 tangle-result))) "")))) #+END_SRC