Merge tag 'adl-support-0.1.5'
This commit is contained in:
commit
3f5927847f
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -12,3 +12,5 @@ pom.xml.asc
|
|||
.lein-failures
|
||||
.nrepl-port
|
||||
.cpcache/
|
||||
|
||||
*.tar
|
||||
|
|
|
@ -1,7 +1,13 @@
|
|||
# Change Log
|
||||
All notable changes to this project will be documented in this file. This change log follows the conventions of [keepachangelog.com](http://keepachangelog.com/).
|
||||
|
||||
## [Unreleased]
|
||||
## 0.1.4 - 2018-0922
|
||||
|
||||
### Added
|
||||
Mainly documentation and tidy-up; Beta release.
|
||||
|
||||
## 0.1.4 - 2018-09-20
|
||||
This is not anticipated to be the actual Beta release; it's a dummy run to test the release and deployment process. Some required features are still missing.
|
||||
|
||||
## 0.1.0 - 2018-06-17
|
||||
### Added
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
# adl-support
|
||||
|
||||
A Clojure library designed to support auto-generated ADL code. This library should normally be included into projects generated by ADL, and consequently is licenced under the MIT license, which is less restrictive than the GNU General Public License which I normally use.
|
||||
A Clojure library designed to support auto-generated [ADL](https://github.com/simon-brooke/adl) code. This library should normally be included into projects generated by ADL, and consequently is licenced under the MIT license, which is less restrictive than the GNU General Public License which I normally use.
|
||||
|
||||
[](https://clojars.org/adl-support)
|
||||
|
||||
## Usage
|
||||
|
||||
FIXME
|
||||
You don't really use this; code auto-generated by ADL does. Some functions which may be more generally useful are in `adl-support.core`, q.v.
|
||||
|
||||
## License
|
||||
|
||||
Copyright © 2018 FIXME
|
||||
Copyright © 2018 Simon Brooke
|
||||
|
||||
Distributed under the MIT License.
|
||||
|
|
28
doc/adl-support.core.html
Normal file
28
doc/adl-support.core.html
Normal file
|
@ -0,0 +1,28 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>adl-support.core documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Adl-support</span> <span class="project-version">0.1.4-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to adl-support</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>adl-support</span></div></div></li><li class="depth-2 branch current"><a href="adl-support.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="adl-support.filters.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>filters</span></div></a></li><li class="depth-2 branch"><a href="adl-support.forms-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>forms-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.print-usage.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>print-usage</span></div></a></li><li class="depth-2 branch"><a href="adl-support.rest-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>rest-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.tags.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tags</span></div></a></li><li class="depth-2"><a href="adl-support.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="adl-support.core.html#var-*warn*"><div class="inner"><span>*warn*</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-compose-exception-reason"><div class="inner"><span>compose-exception-reason</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-compose-reason-and-log"><div class="inner"><span>compose-reason-and-log</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-do-or-log-and-return-reason"><div class="inner"><span>do-or-log-and-return-reason</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-do-or-log-error"><div class="inner"><span>do-or-log-error</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-do-or-return-reason"><div class="inner"><span>do-or-return-reason</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-do-or-warn"><div class="inner"><span>do-or-warn</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-do-or-warn-and-log"><div class="inner"><span>do-or-warn-and-log</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-massage-params"><div class="inner"><span>massage-params</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-massage-value"><div class="inner"><span>massage-value</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-raw-massage-params"><div class="inner"><span>raw-massage-params</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-raw-resolve-template"><div class="inner"><span>raw-resolve-template</span></div></a></li><li class="depth-1"><a href="adl-support.core.html#var-resolve-template"><div class="inner"><span>resolve-template</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">adl-support.core</h1><div class="doc"><pre class="plaintext">Application Description Language support - utility functions likely
|
||||
to be useful in user-written code.</pre></div><div class="public anchor" id="var-*warn*"><h3>*warn*</h3><h4 class="dynamic">dynamic</h4><div class="usage"></div><div class="doc"><pre class="plaintext">The idea here is to have a function with which to show warnings to the user,
|
||||
which can be dynamically bound. Any binding should be a function of one
|
||||
argument, which it should print, log, or otherwise display.</pre></div></div><div class="public anchor" id="var-compose-exception-reason"><h3>compose-exception-reason</h3><h4 class="type">macro</h4><div class="usage"><code>(compose-exception-reason exception intro)</code><code>(compose-exception-reason exception)</code></div><div class="doc"><pre class="plaintext">Compose and return a sensible reason message for this `exception`.
|
||||
</pre></div></div><div class="public anchor" id="var-compose-reason-and-log"><h3>compose-reason-and-log</h3><h4 class="type">macro</h4><div class="usage"><code>(compose-reason-and-log exception intro)</code><code>(compose-reason-and-log exception)</code></div><div class="doc"><pre class="plaintext">Compose a reason message for this `exception`, log it (with its
|
||||
stacktrace), and return the reason message.</pre></div></div><div class="public anchor" id="var-do-or-log-and-return-reason"><h3>do-or-log-and-return-reason</h3><h4 class="type">macro</h4><div class="usage"><code>(do-or-log-and-return-reason form)</code></div><div class="doc"><pre class="plaintext">Clojure stacktraces are unreadable. We have to do better; evaluate
|
||||
this `form` in a try-catch block; return a map. If the evaluation
|
||||
succeeds, the map will have a key `:result` whose value is the result;
|
||||
otherwise it will have a key `:error` which will be bound to the most
|
||||
sensible error message we can construct. Additionally, log the exception</pre></div></div><div class="public anchor" id="var-do-or-log-error"><h3>do-or-log-error</h3><h4 class="type">macro</h4><div class="usage"><code>(do-or-log-error form & {:keys [message error-return], :or {message (clojure.core/seq (clojure.core/concat (clojure.core/list (quote clojure.core/str)) (clojure.core/list "A failure occurred in ") (clojure.core/list (list (quote quote) form))))}})</code></div><div class="doc"><pre class="plaintext">Evaluate the supplied `form` in a try/catch block. If the
|
||||
keyword param `:message` is supplied, the value will be used
|
||||
as the log message; if the keyword param `:error-return` is
|
||||
supplied, the value will be returned if an exception is caught.</pre></div></div><div class="public anchor" id="var-do-or-return-reason"><h3>do-or-return-reason</h3><h4 class="type">macro</h4><div class="usage"><code>(do-or-return-reason form intro)</code><code>(do-or-return-reason form)</code></div><div class="doc"><pre class="plaintext">Clojure stacktraces are unreadable. We have to do better; evaluate
|
||||
this `form` in a try-catch block; return a map. If the evaluation
|
||||
succeeds, the map will have a key `:result` whose value is the result;
|
||||
otherwise it will have a key `:error` which will be bound to the most
|
||||
sensible error message we can construct.</pre></div></div><div class="public anchor" id="var-do-or-warn"><h3>do-or-warn</h3><h4 class="type">macro</h4><div class="usage"><code>(do-or-warn form)</code><code>(do-or-warn form intro)</code></div><div class="doc"><pre class="plaintext">Evaluate this `form`; if any exception is thrown, show it to the user
|
||||
via the `*warn*` mechanism.</pre></div></div><div class="public anchor" id="var-do-or-warn-and-log"><h3>do-or-warn-and-log</h3><h4 class="type">macro</h4><div class="usage"><code>(do-or-warn-and-log form)</code><code>(do-or-warn-and-log form intro)</code></div><div class="doc"><pre class="plaintext">Evaluate this `form`; if any exception is thrown, log the reason and
|
||||
show it to the user via the `*warn*` mechanism.</pre></div></div><div class="public anchor" id="var-massage-params"><h3>massage-params</h3><div class="usage"></div><div class="doc"><pre class="plaintext">Sending empty strings, or numbers as strings, to the database often isn't
|
||||
helpful. Massage these `params` and `form-params` to eliminate these problems.
|
||||
We must take key field values out of just params, but we should take all other
|
||||
values out of form-params - because we need the key to load the form in
|
||||
the first place, but just accepting values of other params would allow spoofing.</pre></div></div><div class="public anchor" id="var-massage-value"><h3>massage-value</h3><div class="usage"><code>(massage-value k m)</code></div><div class="doc"><pre class="plaintext">Return a map with one key, this `k` as a keyword, whose value is the binding of
|
||||
`k` in map `m`, as read by read.</pre></div></div><div class="public anchor" id="var-raw-massage-params"><h3>raw-massage-params</h3><div class="usage"><code>(raw-massage-params request entity)</code><code>(raw-massage-params request)</code></div><div class="doc"><pre class="plaintext">Sending empty strings, or numbers as strings, to the database often isn't
|
||||
helpful. Massage these `params` and `form-params` to eliminate these problems.
|
||||
Date and time fields also need massaging.</pre></div></div><div class="public anchor" id="var-raw-resolve-template"><h3>raw-resolve-template</h3><div class="usage"><code>(raw-resolve-template n)</code></div><div class="doc"><pre class="plaintext">FIXME: write docs</pre></div></div><div class="public anchor" id="var-resolve-template"><h3>resolve-template</h3><div class="usage"></div><div class="doc"><pre class="plaintext">FIXME: write docs</pre></div></div></div></body></html>
|
7
doc/adl-support.filters.html
Normal file
7
doc/adl-support.filters.html
Normal file
|
@ -0,0 +1,7 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>adl-support.filters documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Adl-support</span> <span class="project-version">0.1.4-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to adl-support</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>adl-support</span></div></div></li><li class="depth-2 branch"><a href="adl-support.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch current"><a href="adl-support.filters.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>filters</span></div></a></li><li class="depth-2 branch"><a href="adl-support.forms-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>forms-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.print-usage.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>print-usage</span></div></a></li><li class="depth-2 branch"><a href="adl-support.rest-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>rest-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.tags.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tags</span></div></a></li><li class="depth-2"><a href="adl-support.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="adl-support.filters.html#var-*default-international-dialing-prefix*"><div class="inner"><span>*default-international-dialing-prefix*</span></div></a></li><li class="depth-1"><a href="adl-support.filters.html#var-email"><div class="inner"><span>email</span></div></a></li><li class="depth-1"><a href="adl-support.filters.html#var-telephone"><div class="inner"><span>telephone</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">adl-support.filters</h1><div class="doc"><pre class="plaintext">Application Description Language support - custom Selmer filters
|
||||
used in generated templates.</pre></div><div class="public anchor" id="var-*default-international-dialing-prefix*"><h3>*default-international-dialing-prefix*</h3><h4 class="dynamic">dynamic</h4><div class="usage"></div><div class="doc"><pre class="plaintext">The international dialing prefix to use, if none is specified.
|
||||
</pre></div></div><div class="public anchor" id="var-email"><h3>email</h3><div class="usage"><code>(email arg)</code></div><div class="doc"><pre class="plaintext">If `arg` is, or appears to be, a valid email address, convert it into
|
||||
a `mailto:` link, else leave it be.</pre></div></div><div class="public anchor" id="var-telephone"><h3>telephone</h3><div class="usage"><code>(telephone arg)</code></div><div class="doc"><pre class="plaintext">If `arg` is, or appears to be, a valid telephone number, convert it into
|
||||
a `tel:` link, else leave it be.</pre></div></div></div></body></html>
|
15
doc/adl-support.forms-support.html
Normal file
15
doc/adl-support.forms-support.html
Normal file
|
@ -0,0 +1,15 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>adl-support.forms-support documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Adl-support</span> <span class="project-version">0.1.4-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to adl-support</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>adl-support</span></div></div></li><li class="depth-2 branch"><a href="adl-support.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="adl-support.filters.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>filters</span></div></a></li><li class="depth-2 branch current"><a href="adl-support.forms-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>forms-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.print-usage.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>print-usage</span></div></a></li><li class="depth-2 branch"><a href="adl-support.rest-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>rest-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.tags.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tags</span></div></a></li><li class="depth-2"><a href="adl-support.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="adl-support.forms-support.html#var-all-keys-present.3F"><div class="inner"><span>all-keys-present?</span></div></a></li><li class="depth-1"><a href="adl-support.forms-support.html#var-auxlist-data-name"><div class="inner"><span>auxlist-data-name</span></div></a></li><li class="depth-1"><a href="adl-support.forms-support.html#var-get-current-value"><div class="inner"><span>get-current-value</span></div></a></li><li class="depth-1"><a href="adl-support.forms-support.html#var-get-menu-options"><div class="inner"><span>get-menu-options</span></div></a></li><li class="depth-1"><a href="adl-support.forms-support.html#var-prepare-insertion-params"><div class="inner"><span>prepare-insertion-params</span></div></a></li><li class="depth-1"><a href="adl-support.forms-support.html#var-property-defaults"><div class="inner"><span>property-defaults</span></div></a></li><li class="depth-1"><a href="adl-support.forms-support.html#var-query-name"><div class="inner"><span>query-name</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">adl-support.forms-support</h1><div class="doc"><pre class="plaintext">Application Description Language support - functions useful in
|
||||
generating forms.</pre></div><div class="public anchor" id="var-all-keys-present.3F"><h3>all-keys-present?</h3><h4 class="type">macro</h4><div class="usage"><code>(all-keys-present? m keys)</code></div><div class="doc"><pre class="plaintext">Return true if all the keys in `keys` are present in the map `m`.
|
||||
</pre></div></div><div class="public anchor" id="var-auxlist-data-name"><h3>auxlist-data-name</h3><h4 class="type">macro</h4><div class="usage"><code>(auxlist-data-name auxlist)</code></div><div class="doc"><pre class="plaintext">The name to which data for this `auxlist` will be bound in the
|
||||
Selmer params.</pre></div></div><div class="public anchor" id="var-get-current-value"><h3>get-current-value</h3><h4 class="type">macro</h4><div class="usage"><code>(get-current-value f params entity-name)</code></div><div class="doc"><pre class="plaintext">Use the function `f` and these `params` to fetch an `entity` record from the database.
|
||||
</pre></div></div><div class="public anchor" id="var-get-menu-options"><h3>get-menu-options</h3><h4 class="type">macro</h4><div class="usage"><code>(get-menu-options entity-name get-q list-q fk value)</code></div><div class="doc"><pre class="plaintext">Fetch options for a menu of `entity-name` from the database, using this
|
||||
`get-q` query and this `list-q` query, using the key `fk`, where the current
|
||||
value is this `value`.</pre></div></div><div class="public anchor" id="var-prepare-insertion-params"><h3>prepare-insertion-params</h3><h4 class="type">macro</h4><div class="usage"><code>(prepare-insertion-params params fields)</code></div><div class="doc"><pre class="plaintext">Params for insertion into the database must have keys for all fields in the
|
||||
insert query, even if the value of some of those keys is nil. Massage these
|
||||
`params` to have a value for each field in these `fields`.</pre></div></div><div class="public anchor" id="var-property-defaults"><h3>property-defaults</h3><div class="usage"><code>(property-defaults entity)</code></div><div class="doc"><pre class="plaintext">Get a map of property names and default values for all properties of this
|
||||
`entity` which have explicit defaults.</pre></div></div><div class="public anchor" id="var-query-name"><h3>query-name</h3><div class="usage"><code>(query-name entity-or-name q-type)</code></div><div class="doc"><pre class="plaintext">Generate a query name for the query of type `q-type` (expected to be one
|
||||
of `:create`, `:delete`, `:get`, `:list`, `:search-strings`, `:update`) of
|
||||
the entity `entity-or-name` NOTE: if `entity-or-name` is passed as a string,
|
||||
it should be the full, unaltered name of the entity.</pre></div></div></div></body></html>
|
8
doc/adl-support.print-usage.html
Normal file
8
doc/adl-support.print-usage.html
Normal file
|
@ -0,0 +1,8 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>adl-support.print-usage documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Adl-support</span> <span class="project-version">0.1.4-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to adl-support</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>adl-support</span></div></div></li><li class="depth-2 branch"><a href="adl-support.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="adl-support.filters.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>filters</span></div></a></li><li class="depth-2 branch"><a href="adl-support.forms-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>forms-support</span></div></a></li><li class="depth-2 branch current"><a href="adl-support.print-usage.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>print-usage</span></div></a></li><li class="depth-2 branch"><a href="adl-support.rest-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>rest-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.tags.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tags</span></div></a></li><li class="depth-2"><a href="adl-support.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="adl-support.print-usage.html#var-print-usage"><div class="inner"><span>print-usage</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">adl-support.print-usage</h1><div class="doc"><pre class="plaintext">Application Description Language support - print a usage message.
|
||||
</pre></div><div class="public anchor" id="var-print-usage"><h3>print-usage</h3><div class="usage"><code>(print-usage project-name parsed-options)</code><code>(print-usage project-name parsed-options extra-args)</code></div><div class="doc"><pre class="plaintext">Print a UN*X style usage message. `project-name` should be the base name of
|
||||
the executable jar file you generate, `parsed-options` should be options as
|
||||
parsed by [clojure.tools.cli](<a href="https://github.com/clojure/tools.cli)">https://github.com/clojure/tools.cli)</a>. If
|
||||
`extra-args` is supplied, it should be a map of name, documentation pairs
|
||||
for each additional argument which may be supplied.</pre></div></div></div></body></html>
|
9
doc/adl-support.rest-support.html
Normal file
9
doc/adl-support.rest-support.html
Normal file
|
@ -0,0 +1,9 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>adl-support.rest-support documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Adl-support</span> <span class="project-version">0.1.4-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to adl-support</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>adl-support</span></div></div></li><li class="depth-2 branch"><a href="adl-support.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="adl-support.filters.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>filters</span></div></a></li><li class="depth-2 branch"><a href="adl-support.forms-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>forms-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.print-usage.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>print-usage</span></div></a></li><li class="depth-2 branch current"><a href="adl-support.rest-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>rest-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.tags.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tags</span></div></a></li><li class="depth-2"><a href="adl-support.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="adl-support.rest-support.html#var-do-or-server-fail"><div class="inner"><span>do-or-server-fail</span></div></a></li><li class="depth-1"><a href="adl-support.rest-support.html#var-if-valid-user"><div class="inner"><span>if-valid-user</span></div></a></li><li class="depth-1"><a href="adl-support.rest-support.html#var-valid-user-or-forbid"><div class="inner"><span>valid-user-or-forbid</span></div></a></li><li class="depth-1"><a href="adl-support.rest-support.html#var-with-params-or-error"><div class="inner"><span>with-params-or-error</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">adl-support.rest-support</h1><div class="doc"><pre class="plaintext">Application Description Language support - functions useful in
|
||||
generating JSON route handlers.</pre></div><div class="public anchor" id="var-do-or-server-fail"><h3>do-or-server-fail</h3><h4 class="type">macro</h4><div class="usage"><code>(do-or-server-fail form status)</code></div><div class="doc"><pre class="plaintext">Evaluate this `form`; if it succeeds, return an HTTP response with this
|
||||
status code and the JSON-formatted result as body; if it fails, return an
|
||||
HTTP 500 response.</pre></div></div><div class="public anchor" id="var-if-valid-user"><h3>if-valid-user</h3><h4 class="type">macro</h4><div class="usage"><code>(if-valid-user form request error-return)</code><code>(if-valid-user form request)</code></div><div class="doc"><pre class="plaintext">Evaluate this `form` only if there is a valid user in the session of
|
||||
this `request`; otherwise return the `error-return` value.</pre></div></div><div class="public anchor" id="var-valid-user-or-forbid"><h3>valid-user-or-forbid</h3><h4 class="type">macro</h4><div class="usage"><code>(valid-user-or-forbid form request)</code></div><div class="doc"><pre class="plaintext">Evaluate this `form` only if there is a valid user in the session of
|
||||
this `request`; otherwise return an HTTP forbidden response.</pre></div></div><div class="public anchor" id="var-with-params-or-error"><h3>with-params-or-error</h3><h4 class="type">macro</h4><div class="usage"><code>(with-params-or-error form params required)</code></div><div class="doc"><pre class="plaintext">Evaluate this `form` only if these `params` contain all these `required` keys;
|
||||
otherwise return an HTTP 400 response.</pre></div></div></div></body></html>
|
5
doc/adl-support.tags.html
Normal file
5
doc/adl-support.tags.html
Normal file
|
@ -0,0 +1,5 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>adl-support.tags documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Adl-support</span> <span class="project-version">0.1.4-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>Introduction to adl-support</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>adl-support</span></div></div></li><li class="depth-2 branch"><a href="adl-support.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="adl-support.filters.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>filters</span></div></a></li><li class="depth-2 branch"><a href="adl-support.forms-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>forms-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.print-usage.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>print-usage</span></div></a></li><li class="depth-2 branch"><a href="adl-support.rest-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>rest-support</span></div></a></li><li class="depth-2 branch current"><a href="adl-support.tags.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tags</span></div></a></li><li class="depth-2"><a href="adl-support.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="adl-support.tags.html#var-add-tags"><div class="inner"><span>add-tags</span></div></a></li><li class="depth-1"><a href="adl-support.tags.html#var-if-member-of-permitted"><div class="inner"><span>if-member-of-permitted</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">adl-support.tags</h1><div class="doc"><pre class="plaintext">Application Description Language support - custom Selmer tags used
|
||||
in generated templates.</pre></div><div class="public anchor" id="var-add-tags"><h3>add-tags</h3><div class="usage"><code>(add-tags)</code></div><div class="doc"><pre class="plaintext">FIXME: write docs</pre></div></div><div class="public anchor" id="var-if-member-of-permitted"><h3>if-member-of-permitted</h3><div class="usage"><code>(if-member-of-permitted args context success failure)</code></div><div class="doc"><pre class="plaintext">If at least one of these `args` matches some group name in the `:user-roles`
|
||||
of this `context`, return this `success`, else this `failure`.</pre></div></div></div></body></html>
|
65
doc/adl-support.utils.html
Normal file
65
doc/adl-support.utils.html
Normal file
File diff suppressed because one or more lines are too long
551
doc/css/default.css
Normal file
551
doc/css/default.css
Normal file
|
@ -0,0 +1,551 @@
|
|||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-family: Monaco, DejaVu Sans Mono, Consolas, monospace;
|
||||
font-size: 9pt;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 29px;
|
||||
margin: 10px 0 2px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: normal;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
h5.license {
|
||||
margin: 9px 0 22px 0;
|
||||
color: #555;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.document h1, .namespace-index h1 {
|
||||
font-size: 32px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
#header, #content, .sidebar {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#header {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 22px;
|
||||
color: #f5f5f5;
|
||||
padding: 5px 7px;
|
||||
}
|
||||
|
||||
#content {
|
||||
top: 32px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
color: #333;
|
||||
padding: 0 18px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 32px;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.sidebar.primary {
|
||||
background: #e2e2e2;
|
||||
border-right: solid 1px #cccccc;
|
||||
left: 0;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.sidebar.secondary {
|
||||
background: #f2f2f2;
|
||||
border-right: solid 1px #d7d7d7;
|
||||
left: 251px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#content.namespace-index, #content.document {
|
||||
left: 251px;
|
||||
}
|
||||
|
||||
#content.namespace-docs {
|
||||
left: 452px;
|
||||
}
|
||||
|
||||
#content.document {
|
||||
padding-bottom: 10%;
|
||||
}
|
||||
|
||||
#header {
|
||||
background: #3f3f3f;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 18px;
|
||||
font-weight: lighter;
|
||||
text-shadow: -1px -1px 0px #333;
|
||||
}
|
||||
|
||||
#header h1 .project-version {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.project-version {
|
||||
padding-left: 0.15em;
|
||||
}
|
||||
|
||||
#header a, .sidebar a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header a {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#header h2 {
|
||||
float: right;
|
||||
font-size: 9pt;
|
||||
font-weight: normal;
|
||||
margin: 4px 3px;
|
||||
padding: 0;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
#header h2 a {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.sidebar h3 {
|
||||
margin: 0;
|
||||
padding: 10px 13px 0 13px;
|
||||
font-size: 19px;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.sidebar h3 a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.sidebar h3.no-link {
|
||||
color: #636363;
|
||||
}
|
||||
|
||||
.sidebar ul {
|
||||
padding: 7px 0 6px 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar ul.index-link {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.sidebar li {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.sidebar li a, .sidebar li .no-link {
|
||||
border-left: 3px solid transparent;
|
||||
padding: 0 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sidebar li .no-link {
|
||||
display: block;
|
||||
color: #777;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sidebar li .inner {
|
||||
display: inline-block;
|
||||
padding-top: 7px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.sidebar li a, .sidebar li .tree {
|
||||
height: 31px;
|
||||
}
|
||||
|
||||
.depth-1 .inner { padding-left: 2px; }
|
||||
.depth-2 .inner { padding-left: 6px; }
|
||||
.depth-3 .inner { padding-left: 20px; }
|
||||
.depth-4 .inner { padding-left: 34px; }
|
||||
.depth-5 .inner { padding-left: 48px; }
|
||||
.depth-6 .inner { padding-left: 62px; }
|
||||
|
||||
.sidebar li .tree {
|
||||
display: block;
|
||||
float: left;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
margin: 0 4px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar li.depth-1 .tree {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar li .tree .top, .sidebar li .tree .bottom {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.sidebar li .tree .top {
|
||||
border-left: 1px solid #aaa;
|
||||
border-bottom: 1px solid #aaa;
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
.sidebar li .tree .bottom {
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.sidebar li.branch .tree .bottom {
|
||||
border-left: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.sidebar.primary li.current a {
|
||||
border-left: 3px solid #a33;
|
||||
color: #a33;
|
||||
}
|
||||
|
||||
.sidebar.secondary li.current a {
|
||||
border-left: 3px solid #33a;
|
||||
color: #33a;
|
||||
}
|
||||
|
||||
.namespace-index h2 {
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
|
||||
.namespace-index h3 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.namespace-index .topics {
|
||||
padding-left: 30px;
|
||||
margin: 11px 0 0 0;
|
||||
}
|
||||
|
||||
.namespace-index .topics li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.namespace-docs h3 {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.public h3 {
|
||||
margin: 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.usage {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.public {
|
||||
margin: 0;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.public:last-child {
|
||||
margin-bottom: 20%;
|
||||
}
|
||||
|
||||
.members .public:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.members {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.members h4 {
|
||||
color: #555;
|
||||
font-weight: normal;
|
||||
font-variant: small-caps;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.members .inner {
|
||||
padding-top: 5px;
|
||||
padding-left: 12px;
|
||||
margin-top: 2px;
|
||||
margin-left: 7px;
|
||||
border-left: 1px solid #bbb;
|
||||
}
|
||||
|
||||
#content .members .inner h3 {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.members .public {
|
||||
border-top: none;
|
||||
margin-top: 0;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.members .public:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
h4.type,
|
||||
h4.dynamic,
|
||||
h4.added,
|
||||
h4.deprecated {
|
||||
float: left;
|
||||
margin: 3px 10px 15px 0;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
|
||||
.public h4.type,
|
||||
.public h4.dynamic,
|
||||
.public h4.added,
|
||||
.public h4.deprecated {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
margin: 3px 0 0 10px;
|
||||
}
|
||||
|
||||
.members h4.type,
|
||||
.members h4.added,
|
||||
.members h4.deprecated {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
h4.type {
|
||||
color: #717171;
|
||||
}
|
||||
|
||||
h4.dynamic {
|
||||
color: #9933aa;
|
||||
}
|
||||
|
||||
h4.added {
|
||||
color: #508820;
|
||||
}
|
||||
|
||||
h4.deprecated {
|
||||
color: #880000;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.namespace:last-child {
|
||||
margin-bottom: 10%;
|
||||
}
|
||||
|
||||
.index {
|
||||
padding: 0;
|
||||
font-size: 80%;
|
||||
margin: 15px 0;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.index * {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.index p {
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.index li {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.index ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.type-sig {
|
||||
clear: both;
|
||||
color: #088;
|
||||
}
|
||||
|
||||
.type-sig pre {
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.usage code {
|
||||
display: block;
|
||||
color: #008;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.usage code:first-child {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.public p:first-child, .public pre.plaintext {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.doc {
|
||||
margin: 0 0 26px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.public .doc {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.namespace-index .doc {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.namespace-index .namespace .doc {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.markdown li {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-weight: normal;
|
||||
font-size: 25px;
|
||||
margin: 30px 0 10px 0;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 15px;
|
||||
margin: 22px 0 -4px 0;
|
||||
}
|
||||
|
||||
.doc, .public, .namespace .index {
|
||||
max-width: 680px;
|
||||
overflow-x: visible;
|
||||
}
|
||||
|
||||
.markdown pre > code {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.markdown pre > code, .src-link a {
|
||||
border: 1px solid #e4e4e4;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.markdown code:not(.hljs), .src-link a {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
pre.deps {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
border: 1px solid #e4e4e4;
|
||||
border-radius: 2px;
|
||||
padding: 10px;
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
border-style: solid;
|
||||
border-top: none;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.doc ul, .doc ol {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.doc table {
|
||||
border-collapse: collapse;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.doc table td, .doc table th {
|
||||
border: 1px solid #dddddd;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.doc table th {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
.doc dl {
|
||||
margin: 0 10px 20px 10px;
|
||||
}
|
||||
|
||||
.doc dl dt {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.doc dl dd {
|
||||
padding: 5px 0;
|
||||
margin: 0 0 5px 10px;
|
||||
}
|
||||
|
||||
.doc abbr {
|
||||
border-bottom: 1px dotted #333;
|
||||
font-variant: none;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.src-link {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.src-link a {
|
||||
font-size: 70%;
|
||||
padding: 1px 4px;
|
||||
text-decoration: none;
|
||||
color: #5555bb;
|
||||
}
|
97
doc/css/highlight.css
Normal file
97
doc/css/highlight.css
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #333;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #998;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-subst {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-literal,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag .hljs-attr {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-doctag {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-id {
|
||||
color: #900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-type,
|
||||
.hljs-class .hljs-title {
|
||||
color: #458;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-attribute {
|
||||
color: #000080;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-link {
|
||||
color: #009926;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #990073;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-meta {
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
background: #fdd;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
background: #dfd;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
8
doc/index.html
Normal file
8
doc/index.html
Normal file
File diff suppressed because one or more lines are too long
4
doc/intro.html
Normal file
4
doc/intro.html
Normal file
|
@ -0,0 +1,4 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>Introduction to adl-support</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Adl-support</span> <span class="project-version">0.1.4-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>Introduction to adl-support</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1"><div class="no-link"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>adl-support</span></div></div></li><li class="depth-2 branch"><a href="adl-support.core.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>core</span></div></a></li><li class="depth-2 branch"><a href="adl-support.filters.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>filters</span></div></a></li><li class="depth-2 branch"><a href="adl-support.forms-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>forms-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.print-usage.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>print-usage</span></div></a></li><li class="depth-2 branch"><a href="adl-support.rest-support.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>rest-support</span></div></a></li><li class="depth-2 branch"><a href="adl-support.tags.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>tags</span></div></a></li><li class="depth-2"><a href="adl-support.utils.html"><div class="inner"><span class="tree"><span class="top"></span><span class="bottom"></span></span><span>utils</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#introduction-to-adl-support" name="introduction-to-adl-support"></a>Introduction to adl-support</h1>
|
||||
<p><code>adl-support</code> is a small library of functions used by the <a href="https://github.com/simon-brooke/adl">Application Description Language</a> system to generate elements of a Clojure web-app, which may be useful to people writing web-apps either based on Application Description Language or otherwise.</p></div></div></div></body></html>
|
|
@ -1,3 +1,6 @@
|
|||
# Introduction to adl-support
|
||||
|
||||
TODO: write [great documentation](http://jacobian.org/writing/what-to-write/)
|
||||
`adl-support` is a small library of functions used by the [Application
|
||||
Description Language](https://github.com/simon-brooke/adl) system to generate
|
||||
elements of a Clojure web-app, which may be useful to people writing web-apps
|
||||
either based on Application Description Language or otherwise.
|
||||
|
|
2
doc/js/highlight.min.js
vendored
Normal file
2
doc/js/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
doc/js/jquery.min.js
vendored
Normal file
4
doc/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
112
doc/js/page_effects.js
Normal file
112
doc/js/page_effects.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
function visibleInParent(element) {
|
||||
var position = $(element).position().top
|
||||
return position > -50 && position < ($(element).offsetParent().height() - 50)
|
||||
}
|
||||
|
||||
function hasFragment(link, fragment) {
|
||||
return $(link).attr("href").indexOf("#" + fragment) != -1
|
||||
}
|
||||
|
||||
function findLinkByFragment(elements, fragment) {
|
||||
return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first()
|
||||
}
|
||||
|
||||
function scrollToCurrentVarLink(elements) {
|
||||
var elements = $(elements);
|
||||
var parent = elements.offsetParent();
|
||||
|
||||
if (elements.length == 0) return;
|
||||
|
||||
var top = elements.first().position().top;
|
||||
var bottom = elements.last().position().top + elements.last().height();
|
||||
|
||||
if (top >= 0 && bottom <= parent.height()) return;
|
||||
|
||||
if (top < 0) {
|
||||
parent.scrollTop(parent.scrollTop() + top);
|
||||
}
|
||||
else if (bottom > parent.height()) {
|
||||
parent.scrollTop(parent.scrollTop() + bottom - parent.height());
|
||||
}
|
||||
}
|
||||
|
||||
function setCurrentVarLink() {
|
||||
$('.secondary a').parent().removeClass('current')
|
||||
$('.anchor').
|
||||
filter(function(index) { return visibleInParent(this) }).
|
||||
each(function(index, element) {
|
||||
findLinkByFragment(".secondary a", element.id).
|
||||
parent().
|
||||
addClass('current')
|
||||
});
|
||||
scrollToCurrentVarLink('.secondary .current');
|
||||
}
|
||||
|
||||
var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }())
|
||||
|
||||
function scrollPositionId(element) {
|
||||
var directory = window.location.href.replace(/[^\/]+\.html$/, '')
|
||||
return 'scroll::' + $(element).attr('id') + '::' + directory
|
||||
}
|
||||
|
||||
function storeScrollPosition(element) {
|
||||
if (!hasStorage) return;
|
||||
localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft())
|
||||
localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop())
|
||||
}
|
||||
|
||||
function recallScrollPosition(element) {
|
||||
if (!hasStorage) return;
|
||||
$(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x"))
|
||||
$(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y"))
|
||||
}
|
||||
|
||||
function persistScrollPosition(element) {
|
||||
recallScrollPosition(element)
|
||||
$(element).scroll(function() { storeScrollPosition(element) })
|
||||
}
|
||||
|
||||
function sidebarContentWidth(element) {
|
||||
var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() })
|
||||
return Math.max.apply(Math, widths)
|
||||
}
|
||||
|
||||
function calculateSize(width, snap, margin, minimum) {
|
||||
if (width == 0) {
|
||||
return 0
|
||||
}
|
||||
else {
|
||||
return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2))
|
||||
}
|
||||
}
|
||||
|
||||
function resizeSidebars() {
|
||||
var primaryWidth = sidebarContentWidth('.primary')
|
||||
var secondaryWidth = 0
|
||||
|
||||
if ($('.secondary').length != 0) {
|
||||
secondaryWidth = sidebarContentWidth('.secondary')
|
||||
}
|
||||
|
||||
// snap to grid
|
||||
primaryWidth = calculateSize(primaryWidth, 32, 13, 160)
|
||||
secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160)
|
||||
|
||||
$('.primary').css('width', primaryWidth)
|
||||
$('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1)
|
||||
|
||||
if (secondaryWidth > 0) {
|
||||
$('#content').css('left', primaryWidth + secondaryWidth + 2)
|
||||
}
|
||||
else {
|
||||
$('#content').css('left', primaryWidth + 1)
|
||||
}
|
||||
}
|
||||
|
||||
$(window).ready(resizeSidebars)
|
||||
$(window).ready(setCurrentVarLink)
|
||||
$(window).ready(function() { persistScrollPosition('.primary')})
|
||||
$(window).ready(function() {
|
||||
$('#content').scroll(setCurrentVarLink)
|
||||
$(window).resize(setCurrentVarLink)
|
||||
})
|
1
pkg/README
Normal file
1
pkg/README
Normal file
|
@ -0,0 +1 @@
|
|||
this directory is required by the release process
|
19
project.clj
19
project.clj
|
@ -1,20 +1,25 @@
|
|||
(defproject adl-support "0.1.3"
|
||||
(defproject adl-support "0.1.5"
|
||||
:description "A small library of functions called by generated ADL code."
|
||||
:url "https://github.com/simon-brooke/adl-support"
|
||||
:license {:name "MIT License"
|
||||
:url "https://opensource.org/licenses/MIT"}
|
||||
|
||||
:dependencies [[org.clojure/clojure "1.8.0"]
|
||||
[org.clojure/core.memoize "0.7.1"]
|
||||
[org.clojure/math.numeric-tower "0.0.4"]
|
||||
[org.clojure/tools.logging "0.3.1"]
|
||||
[selmer "1.10.6"]]
|
||||
[org.clojure/tools.logging "0.4.1"]
|
||||
[selmer "1.11.8"]]
|
||||
|
||||
:plugins [[lein-codox "0.10.3"]
|
||||
:plugins [[lein-codox "0.10.4"]
|
||||
[lein-release "1.0.5"]]
|
||||
|
||||
;; `lein release` doesn't work with `git flow release`. To use
|
||||
;; `lein release`, first merge `develop` into `master`, and then, in branch
|
||||
;; `master`, run `lein release`
|
||||
:deploy-repositories [["releases" :clojars]
|
||||
["snapshots" :clojars]]
|
||||
:codox {:metadata {:doc "FIXME: write docs"}
|
||||
:output-path "doc"}
|
||||
|
||||
;; `lein release` doesn't play nice with `git flow release`. Run `lein release` in the
|
||||
;; `develop` branch, then merge the release tag into the `master` branch.
|
||||
|
||||
:release-tasks [["vcs" "assert-committed"]
|
||||
["clean"]
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
(ns adl-support.core
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.string :refer [split]]))
|
||||
(ns ^{:doc "Application Description Language support - utility functions likely
|
||||
to be useful in user-written code."
|
||||
:author "Simon Brooke"}
|
||||
adl-support.core
|
||||
(:require [clojure.core.memoize :as memo]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :refer [split join]]
|
||||
[clojure.tools.logging]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
|
@ -26,71 +32,64 @@
|
|||
(fn [s] (println s)))
|
||||
|
||||
|
||||
(defn query-string-to-map
|
||||
"A `query-string` - the query-part of a URL - comprises generally
|
||||
`<name>=<value>&<name>=<value>...`; reduce such a string to a map.
|
||||
If `query-string` is nil or empty return an empty map."
|
||||
[query-string]
|
||||
(if
|
||||
(empty? query-string)
|
||||
{}
|
||||
(reduce
|
||||
merge
|
||||
(map
|
||||
#(let [pair (split % #"=")]
|
||||
(if (= (count pair) 2)
|
||||
(let
|
||||
[v (try
|
||||
(read-string (nth pair 1))
|
||||
(catch Exception _
|
||||
(nth pair 1)))
|
||||
value (if (number? v) v (str v))]
|
||||
(hash-map (keyword (first pair)) value))
|
||||
{}))
|
||||
(split query-string #"\&")))))
|
||||
|
||||
|
||||
(defn massage-value
|
||||
"Return a map with one key, this `k` as a keyword, whose value is the binding of
|
||||
`k` in map `m`, as read by read."
|
||||
[k m]
|
||||
(let [v (m k)
|
||||
vr (if
|
||||
(string? v)
|
||||
(try
|
||||
(read-string v)
|
||||
(json/read-str v)
|
||||
(catch Exception _ nil)))]
|
||||
(cond
|
||||
(nil? v) {}
|
||||
(= v "") {}
|
||||
(number? vr) {(keyword k) vr}
|
||||
(and
|
||||
(number? vr)
|
||||
;; there's a problem that json/read-str will read "07777 888999" as 7777
|
||||
(re-matches #"^[0-9.]+$" v)) {(keyword k) vr}
|
||||
true
|
||||
{(keyword k) v})))
|
||||
|
||||
|
||||
(defn massage-params
|
||||
(defn raw-massage-params
|
||||
"Sending empty strings, or numbers as strings, to the database often isn't
|
||||
helpful. Massage these `params` and `form-params` to eliminate these problems.
|
||||
Date and time fields also need massaging."
|
||||
([request entity]
|
||||
(let
|
||||
[params (:params request)
|
||||
form-params (:form-params request)
|
||||
p (reduce
|
||||
merge
|
||||
{}
|
||||
(map
|
||||
#(massage-value % params)
|
||||
(keys params)))]
|
||||
(if
|
||||
(empty? (keys form-params))
|
||||
p
|
||||
(reduce
|
||||
merge
|
||||
;; do the keyfields first, from params
|
||||
p
|
||||
;; then merge in everything from form-params, potentially overriding what
|
||||
;; we got from params.
|
||||
(map
|
||||
#(massage-value % form-params)
|
||||
(keys form-params))))))
|
||||
([request]
|
||||
(raw-massage-params request nil)))
|
||||
|
||||
|
||||
(def massage-params
|
||||
"Sending empty strings, or numbers as strings, to the database often isn't
|
||||
helpful. Massage these `params` and `form-params` to eliminate these problems.
|
||||
We must take key field values out of just params, but we should take all other
|
||||
values out of form-params - because we need the key to load the form in
|
||||
the first place, but just accepting values of other params would allow spoofing."
|
||||
[params form-params key-fields]
|
||||
(let
|
||||
[ks (set (map keyword key-fields))]
|
||||
(reduce
|
||||
merge
|
||||
;; do the keyfields first, from params
|
||||
(reduce
|
||||
merge
|
||||
{}
|
||||
(map
|
||||
#(massage-value % params)
|
||||
(filter
|
||||
#(ks (keyword %))
|
||||
(keys params))))
|
||||
;; then merge in everything from form-params, potentially overriding what
|
||||
;; we got from params.
|
||||
(map
|
||||
#(massage-value % form-params)
|
||||
(keys form-params)))))
|
||||
(memo/ttl raw-massage-params {} :ttl/threshold 5000))
|
||||
|
||||
|
||||
(defn
|
||||
|
@ -105,6 +104,43 @@
|
|||
(def resolve-template (memoize raw-resolve-template))
|
||||
|
||||
|
||||
(defmacro compose-exception-reason
|
||||
"Compose and return a sensible reason message for this `exception`."
|
||||
([exception intro]
|
||||
`(str
|
||||
~intro
|
||||
(if ~intro ": ")
|
||||
(join
|
||||
"\n\tcaused by: "
|
||||
(reverse
|
||||
(loop [ex# ~exception result# ()]
|
||||
(if-not (nil? ex#)
|
||||
(recur
|
||||
(.getCause ex#)
|
||||
(cons (str
|
||||
(.getName (.getClass ex#))
|
||||
": "
|
||||
(.getMessage ex#)) result#))
|
||||
result#))))))
|
||||
([exception]
|
||||
`(compose-exception-reason ~exception nil)))
|
||||
|
||||
|
||||
(defmacro compose-reason-and-log
|
||||
"Compose a reason message for this `exception`, log it (with its
|
||||
stacktrace), and return the reason message."
|
||||
([exception intro]
|
||||
`(let [reason# (compose-exception-reason ~exception ~intro)]
|
||||
(clojure.tools.logging/error
|
||||
reason#
|
||||
"\n"
|
||||
(with-out-str
|
||||
(-> ~exception .printStackTrace)))
|
||||
reason#))
|
||||
([exception]
|
||||
`(compose-reason-and-log ~exception nil)))
|
||||
|
||||
|
||||
(defmacro do-or-log-error
|
||||
"Evaluate the supplied `form` in a try/catch block. If the
|
||||
keyword param `:message` is supplied, the value will be used
|
||||
|
@ -116,10 +152,68 @@
|
|||
`(try
|
||||
~form
|
||||
(catch Exception any#
|
||||
(clojure.tools.logging/error
|
||||
(str ~message
|
||||
(with-out-str
|
||||
(-> any# .printStackTrace))))
|
||||
(compose-reason-and-log any# ~message)
|
||||
~error-return)))
|
||||
|
||||
|
||||
(defmacro do-or-return-reason
|
||||
"Clojure stacktraces are unreadable. We have to do better; evaluate
|
||||
this `form` in a try-catch block; return a map. If the evaluation
|
||||
succeeds, the map will have a key `:result` whose value is the result;
|
||||
otherwise it will have a key `:error` which will be bound to the most
|
||||
sensible error message we can construct."
|
||||
([form intro]
|
||||
`(try
|
||||
{:result ~form}
|
||||
(catch Exception any#
|
||||
{:error (compose-exception-reason any# ~intro)})))
|
||||
([form]
|
||||
`(do-or-return-reason ~form nil)))
|
||||
|
||||
|
||||
(defmacro do-or-log-and-return-reason
|
||||
"Clojure stacktraces are unreadable. We have to do better; evaluate
|
||||
this `form` in a try-catch block; return a map. If the evaluation
|
||||
succeeds, the map will have a key `:result` whose value is the result;
|
||||
otherwise it will have a key `:error` which will be bound to the most
|
||||
sensible error message we can construct. Additionally, log the exception"
|
||||
[form]
|
||||
`(try
|
||||
{:result ~form}
|
||||
(catch Exception any#
|
||||
{:error (compose-reason-and-log any#)})))
|
||||
|
||||
|
||||
(defmacro do-or-warn
|
||||
"Evaluate this `form`; if any exception is thrown, show it to the user
|
||||
via the `*warn*` mechanism."
|
||||
([form]
|
||||
`(try
|
||||
~form
|
||||
(catch Exception any#
|
||||
(*warn* (compose-exception-reason any#))
|
||||
nil)))
|
||||
([form intro]
|
||||
`(try
|
||||
~form
|
||||
(catch Exception any#
|
||||
(*warn* (str ~intro ":\n\t" (compose-exception-reason any#)))
|
||||
nil))))
|
||||
|
||||
|
||||
(defmacro do-or-warn-and-log
|
||||
"Evaluate this `form`; if any exception is thrown, log the reason and
|
||||
show it to the user via the `*warn*` mechanism."
|
||||
([form]
|
||||
`(try
|
||||
~form
|
||||
(catch Exception any#
|
||||
(*warn* (compose-reason-and-log any#))
|
||||
nil)))
|
||||
([form intro]
|
||||
`(try
|
||||
~form
|
||||
(catch Exception any#
|
||||
(*warn* (compose-reason-and-log any# ~intro ))
|
||||
nil))))
|
||||
|
||||
|
|
66
src/adl_support/filters.clj
Normal file
66
src/adl_support/filters.clj
Normal file
|
@ -0,0 +1,66 @@
|
|||
(ns ^{:doc "Application Description Language support - custom Selmer filters
|
||||
used in generated templates."
|
||||
:author "Simon Brooke"}
|
||||
adl-support.filters
|
||||
(:require [clojure.string :as s]
|
||||
[selmer.filters :as f]
|
||||
[selmer.parser :as p]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; adl-support.filters: selmer filters required by ADL selmer views.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the MIT-style licence provided; see LICENSE.
|
||||
;;;;
|
||||
;;;; This program is distributed in the hope that it will be useful,
|
||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;;; License for more details.
|
||||
;;;;
|
||||
;;;; Copyright (C) 2018 Simon Brooke
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
(def ^:dynamic *default-international-dialing-prefix*
|
||||
"The international dialing prefix to use, if none is specified."
|
||||
"44")
|
||||
|
||||
(defn telephone
|
||||
"If `arg` is, or appears to be, a valid telephone number, convert it into
|
||||
a `tel:` link, else leave it be."
|
||||
[^String arg]
|
||||
(let [number
|
||||
(s/replace
|
||||
(s/replace
|
||||
arg
|
||||
#"^0"
|
||||
(str "+" *default-international-dialing-prefix* "-"))
|
||||
#"\s+" "-")]
|
||||
(if (re-matches #"[0-9 +-]*" arg)
|
||||
[:safe (str "<a href='tel:" number "'>" arg "</a>")]
|
||||
arg)))
|
||||
|
||||
|
||||
;; (telephone "07768 130255")
|
||||
;; (telephone "Freddy")
|
||||
|
||||
(defn email
|
||||
"If `arg` is, or appears to be, a valid email address, convert it into
|
||||
a `mailto:` link, else leave it be."
|
||||
[^String arg]
|
||||
(if (re-matches #"^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,6}$" arg)
|
||||
[:safe (str "<a href='mailto:" arg "'>" arg "</a>")]
|
||||
arg))
|
||||
|
||||
|
||||
;; (email "simon@journeyman.cc")
|
||||
;; (email "simon@journeyman")
|
||||
;; (email "simon@journeyman.cc.")
|
||||
|
||||
(f/add-filter! :telephone telephone)
|
||||
|
||||
(f/add-filter! :email email)
|
||||
|
||||
;; (p/render "{{p|telephone}}" {:p "07768 130255"})
|
124
src/adl_support/forms_support.clj
Normal file
124
src/adl_support/forms_support.clj
Normal file
|
@ -0,0 +1,124 @@
|
|||
(ns ^{:doc "Application Description Language support - functions useful in
|
||||
generating forms."
|
||||
:author "Simon Brooke"}
|
||||
adl-support.forms-support
|
||||
(:require [adl-support.core :refer :all]
|
||||
[adl-support.utils :refer [descendants-with-tag safe-name singularise]]
|
||||
[clojure.core.memoize :as memo]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :refer [lower-case]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; adl-support.forms-support: functions used by ADL-generated code:
|
||||
;;;; support functions for HTML forms.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the MIT-style licence provided; see LICENSE.
|
||||
;;;;
|
||||
;;;; This program is distributed in the hope that it will be useful,
|
||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;;; License for more details.
|
||||
;;;;
|
||||
;;;; Copyright (C) 2018 Simon Brooke
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
(defn query-name
|
||||
"Generate a query name for the query of type `q-type` (expected to be one
|
||||
of `:create`, `:delete`, `:get`, `:list`, `:search-strings`, `:update`) of
|
||||
the entity `entity-or-name` NOTE: if `entity-or-name` is passed as a string,
|
||||
it should be the full, unaltered name of the entity."
|
||||
[entity-or-name q-type]
|
||||
(symbol
|
||||
(str
|
||||
"db/"
|
||||
(lower-case (name q-type))
|
||||
"-"
|
||||
(let [n (safe-name
|
||||
(if
|
||||
(string? entity-or-name)
|
||||
entity-or-name
|
||||
(:name (:attrs entity-or-name))) :sql)]
|
||||
(case q-type
|
||||
(:list :search-strings) n
|
||||
(singularise n)))
|
||||
(case q-type
|
||||
(:create :delete :update) "!"
|
||||
nil))))
|
||||
|
||||
|
||||
(defmacro get-current-value
|
||||
"Use the function `f` and these `params` to fetch an `entity` record from the database."
|
||||
[f params entity-name]
|
||||
`(let
|
||||
[message# (str "Error while fetching " ~entity-name " record " ~params)]
|
||||
(support/do-or-log-error
|
||||
(~f db/*db* ~params)
|
||||
:message message#
|
||||
:error-return {:warnings [message#]})))
|
||||
|
||||
|
||||
(defmacro get-menu-options
|
||||
"Fetch options for a menu of `entity-name` from the database, using this
|
||||
`get-q` query and this `list-q` query, using the key `fk`, where the current
|
||||
value is this `value`."
|
||||
[entity-name get-q list-q fk value]
|
||||
`(remove
|
||||
nil?
|
||||
(flatten
|
||||
(list
|
||||
(if
|
||||
~value
|
||||
(do-or-log-error
|
||||
(apply
|
||||
~get-q
|
||||
(list db/*db* {~fk ~value}))
|
||||
:message
|
||||
(str "Error while fetching " ~entity-name " record '" ~value "'")))
|
||||
(do-or-log-error
|
||||
(apply
|
||||
~list-q
|
||||
(list db/*db*)
|
||||
{})
|
||||
:message
|
||||
(str "Error while fetching " ~entity-name " list"))))))
|
||||
|
||||
|
||||
(defmacro auxlist-data-name
|
||||
"The name to which data for this `auxlist` will be bound in the
|
||||
Selmer params."
|
||||
[auxlist]
|
||||
`(safe-name (str "auxlist-" (-> ~auxlist :attrs :property)) :clojure))
|
||||
|
||||
|
||||
(defmacro all-keys-present?
|
||||
"Return true if all the keys in `keys` are present in the map `m`."
|
||||
[m keys]
|
||||
`(clojure.set/subset? (set ~keys) (set (keys ~m))))
|
||||
|
||||
|
||||
(defmacro prepare-insertion-params
|
||||
"Params for insertion into the database must have keys for all fields in the
|
||||
insert query, even if the value of some of those keys is nil. Massage these
|
||||
`params` to have a value for each field in these `fields`."
|
||||
;; TODO: should intelligently handle dates and times, but that might imply
|
||||
;; access to ADL at runtime!
|
||||
[params fields]
|
||||
`(merge
|
||||
(reduce merge {} (map #(hash-map (keyword %) nil) ~fields))
|
||||
~params))
|
||||
|
||||
|
||||
(defn property-defaults
|
||||
"Get a map of property names and default values for all properties of this
|
||||
`entity` which have explicit defaults."
|
||||
[entity]
|
||||
(reduce
|
||||
merge {}
|
||||
(map
|
||||
#(hash-map (keyword (-> % :attrs :name)) (-> % :attrs :default))
|
||||
(descendants-with-tag entity :property #(-> % :attrs :default)))))
|
|
@ -1,4 +1,6 @@
|
|||
(ns adl-support.print-usage
|
||||
(ns ^{:doc "Application Description Language support - print a usage message."
|
||||
:author "Simon Brooke"}
|
||||
adl-support.print-usage
|
||||
(:require [clojure.string :refer [join]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
83
src/adl_support/rest_support.clj
Normal file
83
src/adl_support/rest_support.clj
Normal file
|
@ -0,0 +1,83 @@
|
|||
(ns ^{:doc "Application Description Language support - functions useful in
|
||||
generating JSON route handlers."
|
||||
:author "Simon Brooke"}
|
||||
adl-support.rest-support
|
||||
(:require [adl-support.core :refer [do-or-log-error do-or-return-reason]]
|
||||
[clojure.core.memoize :as memo]
|
||||
[clojure.data.json :as json]
|
||||
[clojure.java.io :as io]
|
||||
[clojure.string :refer [split]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
;;;; adl-support.rest-support: functions used by ADL-generated code: support
|
||||
;;;; functions for REST routes.
|
||||
;;;;
|
||||
;;;; This program is free software; you can redistribute it and/or
|
||||
;;;; modify it under the terms of the MIT-style licence provided; see LICENSE.
|
||||
;;;;
|
||||
;;;; This program is distributed in the hope that it will be useful,
|
||||
;;;; but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
;;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
;;;; License for more details.
|
||||
;;;;
|
||||
;;;; Copyright (C) 2018 Simon Brooke
|
||||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
(defmacro if-valid-user
|
||||
"Evaluate this `form` only if there is a valid user in the session of
|
||||
this `request`; otherwise return the `error-return` value."
|
||||
;; TODO: candidate for moving to adl-support.core
|
||||
([form request error-return]
|
||||
`(log/debug "if-valid-user: " (-> ~request :session :user))
|
||||
`(if
|
||||
(-> ~request :session :user)
|
||||
~form
|
||||
~error-return))
|
||||
([form request]
|
||||
(if-valid-user form request nil)))
|
||||
|
||||
|
||||
(defmacro valid-user-or-forbid
|
||||
"Evaluate this `form` only if there is a valid user in the session of
|
||||
this `request`; otherwise return an HTTP forbidden response."
|
||||
;; TODO: candidate for moving to adl-support.core
|
||||
[form request]
|
||||
`(if-valid-user
|
||||
~form
|
||||
~request
|
||||
{:status 403
|
||||
:body (json/write-str "You must be logged in to do that")}))
|
||||
|
||||
|
||||
(defmacro with-params-or-error
|
||||
"Evaluate this `form` only if these `params` contain all these `required` keys;
|
||||
otherwise return an HTTP 400 response."
|
||||
;; TODO: candidate for moving to adl-support.core
|
||||
[form params required]
|
||||
`(if-not
|
||||
(some #(not (% ~params)) ~required)
|
||||
~form
|
||||
{:status 400
|
||||
:body (json/write-str (str "The following params are required: " ~required))}))
|
||||
|
||||
|
||||
;; (with-params-or-error (/ 1 0) {:a 1 :b 2} #{:a :b :c})
|
||||
;; (with-params-or-error "hello" {:a 1 :b 2} #{:a :b })
|
||||
|
||||
(defmacro do-or-server-fail
|
||||
"Evaluate this `form`; if it succeeds, return an HTTP response with this
|
||||
status code and the JSON-formatted result as body; if it fails, return an
|
||||
HTTP 500 response."
|
||||
[form status]
|
||||
`(let [r# (do-or-return-reason ~form)]
|
||||
(if
|
||||
(some #(= :result %) (keys r#)) ;; :result might legitimately be bound to nil
|
||||
{:status ~status
|
||||
:body (:result r#)}
|
||||
{:status 500
|
||||
:body r#})))
|
||||
|
||||
|
|
@ -1,4 +1,7 @@
|
|||
(ns adl-support.tags
|
||||
(ns ^{:doc "Application Description Language support - custom Selmer tags used
|
||||
in generated templates."
|
||||
:author "Simon Brooke"}
|
||||
adl-support.tags
|
||||
(:require [selmer.parser :as p]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
(ns ^{:doc "Application Description Language support library - utility functions."
|
||||
(ns ^{:doc "Application Description Language support - utility functions."
|
||||
:author "Simon Brooke"}
|
||||
adl-support.utils
|
||||
(:require [clojure.math.numeric-tower :refer [expt]]
|
||||
(:require [adl-support.core :refer [*warn*]]
|
||||
[clojure.math.numeric-tower :refer [expt]]
|
||||
[clojure.pprint :as p]
|
||||
[clojure.string :as s]))
|
||||
|
||||
|
@ -42,6 +43,12 @@
|
|||
(and (map? o) (:tag o) (:attrs o)))
|
||||
|
||||
|
||||
(defmacro entity?
|
||||
"True if `o` is a Clojure representation of an ADL entity."
|
||||
[o]
|
||||
`(= (:tag ~o) :entity))
|
||||
|
||||
|
||||
(defn wrap-lines
|
||||
"Wrap lines in this `text` to this `width`; return a list of lines."
|
||||
;; Shamelessly adapted from https://www.rosettacode.org/wiki/Word_wrap#Clojure
|
||||
|
@ -79,37 +86,11 @@
|
|||
|
||||
|
||||
(defn sort-by-name
|
||||
"Sort these `elements` by their `:name` attribute."
|
||||
[elements]
|
||||
(sort #(compare (:name (:attrs %1)) (:name (:attrs %2))) elements))
|
||||
|
||||
|
||||
(defn link-table-name
|
||||
"Canonical name of a link table between entity `e1` and entity `e2`. However, there
|
||||
may be different links between the same two tables with different semantics; if
|
||||
`property` is specified, and if more than one property in `e1` links to `e2`, generate
|
||||
a more specific link name."
|
||||
([e1 e2]
|
||||
(s/join
|
||||
"_"
|
||||
(cons
|
||||
"ln"
|
||||
(sort
|
||||
(list
|
||||
(:name (:attrs e1)) (:name (:attrs e2)))))))
|
||||
([property e1 e2]
|
||||
(if (count
|
||||
(descendants
|
||||
e1
|
||||
#(and
|
||||
(= (-> % :attrs :type) "link")
|
||||
(=
|
||||
(-> % :attrs :entity)
|
||||
(-> property :attrs :entity)))))
|
||||
(s/join
|
||||
"_" (cons "ln" (map #(:name (:attrs %)) (list property e1 e2))))
|
||||
(link-table-name e1 e2))))
|
||||
|
||||
|
||||
(defn children
|
||||
"Return the children of this `element`; if `predicate` is passed, return only those
|
||||
children satisfying the predicate."
|
||||
|
@ -281,36 +262,128 @@
|
|||
" "
|
||||
(map
|
||||
#(apply str (cons (Character/toUpperCase (first %)) (rest %)))
|
||||
(s/split s #"[ \t\r\n]+")))
|
||||
(s/split s #"[^a-zA-Z0-9]+")))
|
||||
s))
|
||||
|
||||
|
||||
(defn pretty-name
|
||||
[entity]
|
||||
(capitalise (singularise (:name (:attrs entity)))))
|
||||
"Return a version of the name of this `element` (entity, field,
|
||||
form, list, page, property) suitable for use in text visible to the user."
|
||||
[element]
|
||||
(capitalise (singularise (:name (:attrs element)))))
|
||||
|
||||
|
||||
(defn safe-name
|
||||
"Return a safe name for the object `o`, given the specified `convention`.
|
||||
`o` is expected to be either a string or an element."
|
||||
`o` is expected to be either a string or an element. Recognised values for
|
||||
`convention` are: #{:c :c-sharp :java :sql}"
|
||||
([o]
|
||||
(if
|
||||
(cond
|
||||
(element? o)
|
||||
(safe-name (:name (:attrs o)))
|
||||
true
|
||||
(s/replace (str o) #"[^a-zA-Z0-9-]" "")))
|
||||
([o convention]
|
||||
(if
|
||||
(cond
|
||||
(and (entity? o) (= convention :sql))
|
||||
;; if it's an entity, it's permitted to have a different table name
|
||||
;; from its entity name. This isn't actually likely, but...
|
||||
(safe-name (or (-> o :attrs :table) (-> o :attrs :name)) :sql)
|
||||
(and (property? o) (= convention :sql))
|
||||
;; if it's a property, it's entitle to have a different column name
|
||||
;; from its property name.
|
||||
(safe-name (or (-> o :attrs :column) (-> o :attrs :name)) :sql)
|
||||
(element? o)
|
||||
(safe-name (:name (:attrs o)) convention)
|
||||
(let [string (str o)]
|
||||
true
|
||||
(let [string (str o)
|
||||
capitalised (capitalise string)]
|
||||
(case convention
|
||||
(:sql :c) (s/replace string #"[^a-zA-Z0-9_]" "_")
|
||||
:c-sharp (s/replace (capitalise string) #"[^a-zA-Z0-9]" "")
|
||||
:c-sharp (s/replace capitalised #"[^a-zA-Z0-9]" "")
|
||||
:java (let
|
||||
[camel (s/replace (capitalise string) #"[^a-zA-Z0-9]" "")]
|
||||
[camel (s/replace capitalised #"[^a-zA-Z0-9]" "")]
|
||||
(apply str (cons (Character/toLowerCase (first camel)) (rest camel))))
|
||||
(safe-name string))))))
|
||||
|
||||
;; (safe-name "address-id" :sql)
|
||||
;; (safe-name {:tag :property :attrs {:name "address-id"}} :sql)
|
||||
|
||||
|
||||
(defn unique-link?
|
||||
"True if there is exactly one link between entities `e1` and `e2`."
|
||||
[e1 e2]
|
||||
(let [n1 (count (children-with-tag e1 :property
|
||||
#(and (= (-> % :attrs :type) "link")
|
||||
(= (-> % :attrs :entity)(-> e2 :attrs :name)))))
|
||||
n2 (count (children-with-tag e2 :property
|
||||
#(and (= (-> % :attrs :type) "link")
|
||||
(= (-> % :attrs :entity)(-> e1 :attrs :name)))))]
|
||||
(= (max n1 n2) 1)))
|
||||
|
||||
|
||||
(defn link-related-query-name
|
||||
"link is tricky. If there's exactly than one link between the two
|
||||
entities, we need to generate the same name from both
|
||||
ends of the link"
|
||||
[property nearside farside]
|
||||
(if (unique-link? nearside farside)
|
||||
(let [ordered (sort-by #(-> % :attrs :name) (list nearside farside))]
|
||||
(str "list-"
|
||||
(safe-name (first ordered) :sql)
|
||||
"-by-"
|
||||
(safe-name (nth ordered 1) :sql)))
|
||||
(str "list-"
|
||||
(safe-name property :sql) "-by-"
|
||||
(singularise (safe-name nearside :sql)))))
|
||||
|
||||
|
||||
(defn link-table-name
|
||||
"Canonical name of a link table between entity `e1` and entity `e2`. However, there
|
||||
may be different links between the same two tables with different semantics; if
|
||||
`property` is specified, and if more than one property in `e1` links to `e2`, generate
|
||||
a more specific link name."
|
||||
([e1 e2]
|
||||
(s/join
|
||||
"_"
|
||||
(cons
|
||||
"ln"
|
||||
(sort
|
||||
(list
|
||||
(:name (:attrs e1)) (:name (:attrs e2)))))))
|
||||
([property e1 e2]
|
||||
(if (unique-link? e1 e2)
|
||||
(link-table-name e1 e2)
|
||||
(s/join
|
||||
"_" (cons "ln" (map #(:name (:attrs %)) (list property e1)))))))
|
||||
|
||||
|
||||
(defn list-related-query-name
|
||||
"Return the canonical name of the HugSQL query to return all records on
|
||||
`farside` which match a given record on `nearside`, where `nearide` and
|
||||
`farside` are both entities."
|
||||
[property nearside farside]
|
||||
(if
|
||||
(and
|
||||
(property? property)
|
||||
(entity? nearside)
|
||||
(entity? farside))
|
||||
(case (-> property :attrs :type)
|
||||
"link" (link-related-query-name property nearside farside)
|
||||
"list" (str "list-"
|
||||
(safe-name farside :sql) "-by-"
|
||||
(singularise (safe-name nearside :sql)))
|
||||
"entity" (str "list-"
|
||||
(safe-name nearside :sql) "-by-"
|
||||
(singularise (safe-name farside :sql)))
|
||||
;; default
|
||||
(str "ERROR-bad-property-type-"
|
||||
(-> ~property :attrs :type) "-of-"
|
||||
(-> ~property :attrs :name)))
|
||||
(do
|
||||
(*warn* "Argument passed to `list-related-query-name` was a non-entity")
|
||||
nil)))
|
||||
|
||||
|
||||
(defn property-for-field
|
||||
"Return the property within this `entity` which matches this `field`."
|
||||
|
@ -396,13 +469,23 @@
|
|||
elements))))
|
||||
|
||||
|
||||
(defn system-generated?
|
||||
"True if the value of the `property` is system generated, and
|
||||
should not be set by the user."
|
||||
[property]
|
||||
(child-with-tag
|
||||
property
|
||||
:generator
|
||||
#(#{"native" "guid"} (-> % :attrs :action))))
|
||||
|
||||
|
||||
(defn insertable?
|
||||
"Return `true` it the value of this `property` may be set from user-supplied data."
|
||||
[property]
|
||||
(and
|
||||
(= (:tag property) :property)
|
||||
(not (#{"link"} (:type (:attrs property))))
|
||||
(not (= (:distinct (:attrs property)) "system"))))
|
||||
(not (#{"link" "list"} (:type (:attrs property))))
|
||||
(not (system-generated? property))))
|
||||
|
||||
|
||||
(defmacro all-properties
|
||||
|
@ -426,6 +509,14 @@
|
|||
(user-distinct-properties entity))))
|
||||
|
||||
|
||||
(defn column-name
|
||||
"Return, as a string, the name for the column which represents this `property`."
|
||||
[property]
|
||||
(safe-name
|
||||
(or (-> property :attrs :column) (-> property :attrs :name))
|
||||
:sql))
|
||||
|
||||
|
||||
(defmacro insertable-properties
|
||||
"Return all the properties of this `entity` (including key properties) into
|
||||
which user-supplied data can be inserted"
|
||||
|
@ -435,6 +526,17 @@
|
|||
(all-properties ~entity)))
|
||||
|
||||
|
||||
(defn required-properties
|
||||
"Return the properties of this `entity` which are required and are not
|
||||
system generated."
|
||||
[entity]
|
||||
(filter
|
||||
#(and
|
||||
(= (:required (:attrs %)) "true")
|
||||
(not (system-generated? %)))
|
||||
(descendants-with-tag entity :property)))
|
||||
|
||||
|
||||
(defmacro key-properties
|
||||
[entity]
|
||||
`(children-with-tag (first (children-with-tag ~entity :key)) :property))
|
||||
|
@ -523,14 +625,14 @@
|
|||
first child of the `entity` of the specified type will be used."
|
||||
[form entity application]
|
||||
(cond
|
||||
(and (map? form) (#{:list :form :page} (:tag form)))
|
||||
(s/join
|
||||
(and (map? form) (#{:list :form :page} (:tag form)))
|
||||
(s/join
|
||||
"-"
|
||||
(flatten
|
||||
(list
|
||||
(name (:tag form)) (:name (:attrs entity)) (s/split (:name (:attrs form)) #"[ \n\r\t]+"))))
|
||||
(keyword? form)
|
||||
(path-part (first (children-with-tag entity form)) entity application)))
|
||||
(list
|
||||
(name (:tag form)) (:name (:attrs entity)) (s/split (:name (:attrs form)) #"[ \n\r\t]+"))))
|
||||
(keyword? form)
|
||||
(path-part (first (children-with-tag entity form)) entity application)))
|
||||
|
||||
|
||||
(defn editor-name
|
||||
|
|
|
@ -2,43 +2,60 @@
|
|||
(:require [clojure.test :refer :all]
|
||||
[adl-support.core :refer :all]))
|
||||
|
||||
(deftest query-string-to-map-tests
|
||||
(testing "conversion of query strings to maps"
|
||||
(let [expected {}
|
||||
actual (query-string-to-map nil)]
|
||||
(is (= expected actual) "Nil arg"))
|
||||
(let [expected {}
|
||||
actual (query-string-to-map "")]
|
||||
(is (= expected actual) "Empty string arg"))
|
||||
(let [expected {:id 1}
|
||||
actual (query-string-to-map "id=1")]
|
||||
(is (= expected actual) "One integer value"))
|
||||
(let [expected {:name "simon"}
|
||||
actual (query-string-to-map "name=simon")]
|
||||
(is (= expected actual) "One string value."))
|
||||
(let [expected {:name "simon" :id 1}
|
||||
actual (query-string-to-map "id=1&name=simon")]
|
||||
(is (= expected actual) "One string value, one integer. Order of pairs might be reversed, and that's OK"))
|
||||
(let [expected {:address_id_expanded "AIRDS"}
|
||||
actual (query-string-to-map "id=&address_id_expanded=AIRDS&sub-address=")]
|
||||
(is (= expected actual) "Yeys with no values should not be included in the map"))
|
||||
))
|
||||
|
||||
(deftest massage-params-tests
|
||||
(testing "Massaging of params"
|
||||
(let [expected {:id 67}
|
||||
actual (massage-params {:id 67} {} #{:id})]
|
||||
(is (= expected actual) "numeric param"))
|
||||
(let [expected {:id 67}
|
||||
actual (massage-params {:id "67"} {} #{:id})]
|
||||
(is (= expected actual) "string param"))
|
||||
(let [expected {:id 67}
|
||||
actual (massage-params {"id" "67"} {} #{:id})]
|
||||
(is (= expected actual) "string keyword"))
|
||||
(let [expected {:id 67}
|
||||
actual (massage-params {:id 60} {:id 67} #{:id})]
|
||||
(is (= expected actual) "params and form-params differ"))
|
||||
(let [expected {:id 67 :offset 0 :limit 50}
|
||||
actual (massage-params {:id 60} {:id "67" :offset "0" :limit "50"} #{:id})]
|
||||
(is (= expected actual) "Limit and offset in form-params"))
|
||||
))
|
||||
actual (massage-params {:params {:id "67" :offset "0" :limit "50"} :form-params {}})]
|
||||
(is (= expected actual) "Request with no form params"))
|
||||
(let [expected {:id 67 :offset 0 :limit 50}
|
||||
actual (massage-params {:params {:id "0" :offset "1000" :limit "150"}
|
||||
:form-params {:id "67" :offset "0" :limit "50"}})]
|
||||
(is (= expected actual) "Request with form params, params and form params differ"))
|
||||
(let [expected {:phone "07777 888999"}
|
||||
actual (massage-params {:params {:phone "07777 888999"}})]
|
||||
(is (= expected actual) "A phone number with a space in needs to be treated as a string"))))
|
||||
|
||||
|
||||
(deftest compose-exception-reason-tests
|
||||
(testing "Compose exception reason"
|
||||
(let [expected "java.lang.Exception: hello"
|
||||
actual (compose-exception-reason
|
||||
(Exception. "hello"))]
|
||||
(is (= expected actual) "Exception with no cause"))
|
||||
(let [expected "java.lang.Exception: Top-level exception\n\tcaused by: java.lang.Exception: cause"
|
||||
actual (compose-exception-reason
|
||||
(Exception.
|
||||
"Top-level exception"
|
||||
(Exception. "cause")))]
|
||||
(is (= expected actual) "Exception with cause"))
|
||||
(let [expected ""
|
||||
actual (compose-exception-reason nil)]
|
||||
(is (= expected actual) "Exception with no cause"))))
|
||||
|
||||
|
||||
(deftest do-or-return-reason-tests
|
||||
(testing "do-or-return-reason"
|
||||
(let [expected {:result 1}
|
||||
actual (do-or-return-reason (/ 1 1))]
|
||||
(is (= expected actual) "No exception thrown"))
|
||||
(let [expected {:error "java.lang.ArithmeticException: Divide by zero"}
|
||||
actual (do-or-return-reason (/ 1 0))]
|
||||
(is (= expected actual) "Exception thrown"))
|
||||
(let [expected {:error "Hello: java.lang.ArithmeticException: Divide by zero"}
|
||||
actual (do-or-return-reason (/ 1 0) "Hello")]
|
||||
(is (= expected actual) "Exception thrown, with intro"))))
|
||||
|
||||
|
||||
;; These work in REPL, but break in tests. Why?
|
||||
;; (deftest "do-or-warn-tests"
|
||||
;; (testing "do-or-warn"
|
||||
;; (let [expected 1
|
||||
;; actual (do-or-warn (/ 1 1))]
|
||||
;; (is (= expected actual) "No exception thrown"))
|
||||
;; (let [expected nil
|
||||
;; actual (do-or-warn (/ 1 0))]
|
||||
;; (is (= expected actual) "Exception thrown"))
|
||||
;; (let [expected nil
|
||||
;; actual (do-or-warn (/ 1 0) "hello")]
|
||||
;; (is (= expected actual) "Exception thrown"))
|
||||
;; ))
|
||||
|
|
34
test/adl_support/forms_support_test.clj
Normal file
34
test/adl_support/forms_support_test.clj
Normal file
|
@ -0,0 +1,34 @@
|
|||
(ns adl-support.forms-support-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[adl-support.forms-support :refer :all]))
|
||||
|
||||
|
||||
(deftest auxlist-data-name-test
|
||||
(testing "auxlist-data-name"
|
||||
(let [auxlist {:tag :auxlist,
|
||||
:attrs {:property "dwellings"},
|
||||
:content [{:tag :field,
|
||||
:attrs {:name "sub-address"},
|
||||
:content nil}]}
|
||||
expected "auxlist-dwellings"
|
||||
actual (auxlist-data-name auxlist)]
|
||||
(is (= expected actual) "Just checking..."))))
|
||||
|
||||
|
||||
(deftest prepare-insertion-params-tests
|
||||
(testing "prepare-insertion-params"
|
||||
(is (= {:test1 nil :test2 nil}
|
||||
(prepare-insertion-params {} #{:test1 :test2}))
|
||||
"Empty params; set")
|
||||
(is (= {:test1 nil :test2 nil}
|
||||
(prepare-insertion-params {} '(:test1 :test2)))
|
||||
"Empty params; list")
|
||||
(is (= {:test1 nil :test2 nil :test3 6}
|
||||
(prepare-insertion-params {:test3 6} #{:test1 :test2}))
|
||||
"Unlisted param; set")
|
||||
(is (= {:test1 "foo" :test2 nil}
|
||||
(prepare-insertion-params {:test1 "foo"} '(:test1 :test2)))
|
||||
"Listed param; list")
|
||||
(is (= {:test1 "foo" :test2 6}
|
||||
(prepare-insertion-params {:test1 "foo" :test2 6} '(:test1 :test2)))
|
||||
"Listed params; list")))
|
38
test/adl_support/rest_support_test.clj
Normal file
38
test/adl_support/rest_support_test.clj
Normal file
|
@ -0,0 +1,38 @@
|
|||
(ns adl-support.rest-support-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[adl-support.rest-support :refer :all]))
|
||||
|
||||
|
||||
(deftest if-valid-user-tests
|
||||
(testing "correct handling of if-valid-user"
|
||||
(let [expected "hello"
|
||||
actual (if-valid-user "hello" {:session {:user {:id 4}}} "goodbye")]
|
||||
(is (= expected actual) "User in session"))
|
||||
(let [expected "goodbye"
|
||||
actual (if-valid-user "hello" {:session {}} "goodbye")]
|
||||
(is (= expected actual) "No user in session"))))
|
||||
|
||||
|
||||
(deftest valid-user-or-forbid-tests
|
||||
(testing "valid-user-or-forbid"
|
||||
(let [expected "hello"
|
||||
actual (valid-user-or-forbid "hello" {:session {:user {:id 4}}})]
|
||||
(is (= expected actual) "User in session"))
|
||||
(let [expected 403
|
||||
actual (:status (valid-user-or-forbid "hello" {:session {}}))]
|
||||
(is (= expected actual) "No user in session"))))
|
||||
|
||||
|
||||
(deftest with-params-or-error-tests
|
||||
(let [expected "hello"
|
||||
actual (with-params-or-error "hello" {:a 1 :b 2} #{:a :b})]
|
||||
(is (= expected actual) "All requirements satisfied"))
|
||||
(let [expected "hello"
|
||||
actual (with-params-or-error "hello" {:a 1 :b 2 :c 3} #{:a :b})]
|
||||
(is (= expected actual) "Unrequired parameter present"))
|
||||
(let [expected 400
|
||||
actual (:status (with-params-or-error "hello" {:a 1 :b 2} #{:a :b :c}))]
|
||||
(is (= expected actual) "Some requirements unsatisfied"))
|
||||
(let [expected 400
|
||||
actual (:status (with-params-or-error (/ 1 0) {:a 1 :b 2} #{:a :b :c}))]
|
||||
(is (= expected actual) "Exception should not be throwen")))
|
|
@ -1,9 +1,17 @@
|
|||
(ns adl-support.utils-test
|
||||
(:require [clojure.test :refer :all]
|
||||
[adl-support.core :refer [*warn*]]
|
||||
[adl-support.utils :refer :all]))
|
||||
|
||||
;; Yes, there's MASSES in utils which ought to be tested. I'll add more tests over time.
|
||||
|
||||
(deftest singularise-tests
|
||||
(testing "Singularise"
|
||||
(is (= "address" (singularise "addresses")))
|
||||
(is (= "address" (singularise "address")))
|
||||
(is (= "expertise" (singularise "expertise")))))
|
||||
|
||||
|
||||
(deftest child-with-tag-tests
|
||||
(testing "child-with-tag"
|
||||
(let [expected {:tag :prompt
|
||||
|
@ -297,3 +305,296 @@
|
|||
with appropriate property with prompt in current locale"))
|
||||
))
|
||||
|
||||
|
||||
(deftest list-related-query-name-tests
|
||||
(testing "list-related-query-name"
|
||||
(let [e1 {:tag :entity,
|
||||
:attrs {:volatility "6", :magnitude "1", :name "genders", :table "genders"},
|
||||
:content [{:tag :documentation,
|
||||
:content ["All genders which may be assigned to\n electors."]}
|
||||
{:tag :key, :attrs nil,
|
||||
:content [{:tag :property,
|
||||
:attrs {:distinct "all", :size "32", :type "string", :name "id"},
|
||||
:content [{:tag :prompt,
|
||||
:attrs {:locale "en_GB.UTF-8",
|
||||
:prompt "Gender"},
|
||||
:content nil}]}]}
|
||||
{:tag :list, :attrs {:name "Genders", :properties "all"}}
|
||||
{:tag :form, :attrs {:name "Gender", :properties "all"}}]}
|
||||
e2 {:tag :entity,
|
||||
:attrs {:volatility "6", :magnitude "1", :name "electors", :table "electors"},
|
||||
:content [{:tag :documentation,
|
||||
:attrs nil,
|
||||
:content
|
||||
["All electors known to the system; electors are
|
||||
people believed to be entitled to vote in the current
|
||||
campaign."]}
|
||||
{:tag :key,
|
||||
:attrs nil,
|
||||
:content
|
||||
[{:tag :property,
|
||||
:attrs
|
||||
{:distinct "system",
|
||||
:immutable "true",
|
||||
:column "id",
|
||||
:name "id",
|
||||
:type "integer",
|
||||
:required "true"},
|
||||
:content
|
||||
[{:tag :generator, :attrs {:action "native"}, :content nil}]}]}
|
||||
{:tag :property,
|
||||
:attrs
|
||||
{:distinct "user",
|
||||
:column "name",
|
||||
:name "name",
|
||||
:type "string",
|
||||
:required "true",
|
||||
:size "64"},
|
||||
:content
|
||||
[{:tag :prompt,
|
||||
:attrs {:locale "en_GB.UTF-8", :prompt "Name"},
|
||||
:content nil}]}
|
||||
{:tag :property,
|
||||
:attrs
|
||||
{:default "Unknown",
|
||||
:farkey "id",
|
||||
:entity "genders",
|
||||
:column "gender",
|
||||
:type "entity",
|
||||
:name "gender"},
|
||||
:content
|
||||
[{:tag :prompt,
|
||||
:attrs {:locale "en_GB.UTF-8", :prompt "Gender"},
|
||||
:content nil}]}]}
|
||||
property (child e2 #(= (-> % :attrs :name) "gender"))
|
||||
expected "list-electors-by-gender"
|
||||
actual (list-related-query-name property e2 e1)]
|
||||
(is (= expected actual) "just checking..."))
|
||||
(let [e1 {:tag :entity
|
||||
:attrs {:name "dwellings"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "address" :type "entity" :entity "addresses"}}]}
|
||||
e2 {:tag :entity
|
||||
:attrs {:name "addresses"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "dwellings" :type "list" :entity "dwellings"}}]}]
|
||||
(let [property {:tag :property
|
||||
:attrs {:name "address" :type "entity" :entity "addresses"}}
|
||||
expected "list-dwellings-by-address"
|
||||
actual (list-related-query-name property e1 e2)]
|
||||
(is (= expected actual) "Entity property"))
|
||||
(let [property {:tag :property
|
||||
:attrs {:name "dwellings" :type "list" :entity "dwellings"}}
|
||||
expected "list-dwellings-by-address"
|
||||
actual (list-related-query-name property e2 e1)]
|
||||
(is (= expected actual) "List property")))
|
||||
(let [e1 {:tag :entity
|
||||
:attrs {:name "teams"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "members" :type "link" :entity "canvassers"}}
|
||||
{:tag :property
|
||||
:attrs {:name "organisers" :type "link" :entity "canvassers"}}]}
|
||||
e2 {:tag :entity
|
||||
:attrs {:name "canvassers"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "memberships" :type "link" :entity "teams"}}]}]
|
||||
(let [property {:tag :property
|
||||
:attrs {:name "members" :type "link" :entity "canvassers"}}
|
||||
expected "list-members-by-team"
|
||||
actual (list-related-query-name property e1 e2)]
|
||||
(is (= actual expected) "Link property - members"))
|
||||
(let [property {:tag :property
|
||||
:attrs {:name "organisers" :type "link" :entity "canvassers"}}
|
||||
expected "list-organisers-by-team"
|
||||
actual (list-related-query-name property e1 e2)]
|
||||
(is (= actual expected) "Link property - organisers"))
|
||||
(let [property {:tag :property
|
||||
:attrs {:name "memberships" :type "link" :entity "teams"}}
|
||||
expected "list-memberships-by-canvasser"
|
||||
actual (list-related-query-name property e2 e1)]
|
||||
(is (= actual expected) "Link property - membersips")))))
|
||||
|
||||
|
||||
(deftest link-table-name-tests
|
||||
(testing "link-table-name"
|
||||
(let [e1 {:tag :entity
|
||||
:attrs {:name "teams"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "members" :type "link" :entity "canvassers"}}
|
||||
{:tag :property
|
||||
:attrs {:name "organisers" :type "link" :entity "canvassers"}}]}
|
||||
e2 {:tag :entity
|
||||
:attrs {:name "canvassers"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "memberships" :type "link" :entity "teams"}}
|
||||
{:tag :property
|
||||
:attrs {:name "roles" :type "link" :entity "roles"}}]}
|
||||
e3 {:tag :entity
|
||||
:attrs {:name "roles"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:type "string"
|
||||
:distinct "all"
|
||||
:name "id"}]}]}]
|
||||
(let [property {:tag :property
|
||||
:attrs {:name "members" :type "link" :entity "canvassers"}}
|
||||
expected "ln_members_teams"
|
||||
actual (link-table-name property e1 e2)]
|
||||
(is (= actual expected) "Link property - members"))
|
||||
(let [property {:tag :property
|
||||
:attrs {:name "organisers" :type "link" :entity "canvassers"}}
|
||||
expected "ln_organisers_teams"
|
||||
actual (link-table-name property e1 e2)]
|
||||
(is (= actual expected) "Link property - organisers"))
|
||||
(let [property {:tag :property
|
||||
:attrs {:name "memberships" :type "link" :entity "teams"}}
|
||||
expected "ln_memberships_canvassers"
|
||||
actual (link-table-name property e2 e1)]
|
||||
(is (= actual expected) "Link property - membersips"))
|
||||
(let [property {:tag :property
|
||||
:attrs {:name "roles" :type "link" :entity "roles"}}
|
||||
expected "ln_canvassers_roles"
|
||||
actual (link-table-name property e2 e3)]
|
||||
(is (= actual expected) "Link property - roles")))))
|
||||
|
||||
|
||||
(deftest unique-link-tests
|
||||
(testing "unique-link?"
|
||||
(let [e1 {:tag :entity
|
||||
:attrs {:name "teams"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "members" :type "link" :entity "canvassers"}}
|
||||
{:tag :property
|
||||
:attrs {:name "organisers" :type "link" :entity "canvassers"}}]}
|
||||
e2 {:tag :entity
|
||||
:attrs {:name "canvassers"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "memberships" :type "link" :entity "teams"}}
|
||||
{:tag :property
|
||||
:attrs {:name "roles" :type "link" :entity "roles"}}]}
|
||||
e3 {:tag :entity
|
||||
:attrs {:name "roles"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:type "string"
|
||||
:distinct "all"
|
||||
:name "id"}]}]}]
|
||||
(is (= false (unique-link? e1 e2)) "There are two logical links, three link properties, between e1 and e2")
|
||||
(is (= true (unique-link? e2 e3)) "There is only one link between e2 and e3"))))
|
||||
|
||||
(deftest capitalise-tests
|
||||
(testing "capitalise"
|
||||
(is (= (capitalise "the quick brown fox jumped over the lazy dog") "The Quick Brown Fox Jumped Over The Lazy Dog"))))
|
||||
|
||||
(deftest safe-name-tests
|
||||
(testing "safe-name"
|
||||
(let [e1 {:tag :entity
|
||||
:attrs {:name "canvass-teams" :table "team"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "members" :type "link" :entity "canvassers"}}
|
||||
{:tag :property
|
||||
:attrs {:name "organisers" :type "link" :entity "canvassers"}}]}
|
||||
p1 {:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}
|
||||
p2 {:tag :property
|
||||
:attrs {:name "with_underscore" :column "with-hyphen" :type "integer"}}]
|
||||
(is
|
||||
(= (safe-name "the quick brown fox jumped over the lazy dog")
|
||||
"thequickbrownfoxjumpedoverthelazydog")
|
||||
"absent a convention, spaces are suppressed")
|
||||
(is
|
||||
(= (safe-name "the quick brown fox jumped over the lazy dog" :c)
|
||||
"the_quick_brown_fox_jumped_over_the_lazy_dog")
|
||||
"in :c convention, spaces are replaced with underscores")
|
||||
(is
|
||||
(= (safe-name "the quick brown fox jumped over the lazy dog" :c-sharp)
|
||||
"TheQuickBrownFoxJumpedOverTheLazyDog")
|
||||
"in :c-sharp convention spaces are suppressed and all words camel cased")
|
||||
(is
|
||||
(= (safe-name "the quick brown fox jumped over the lazy dog" :java)
|
||||
"theQuickBrownFoxJumpedOverTheLazyDog")
|
||||
"in :java convention spaces are suppressed and embedded words camel cased")
|
||||
(is
|
||||
(= (safe-name "the quick brown fox jumped over the lazy dog" :sql)
|
||||
"the_quick_brown_fox_jumped_over_the_lazy_dog")
|
||||
"in :sql convention, spaces are replaced with underscores")
|
||||
(is (= (safe-name e1) "canvass-teams"))
|
||||
(is (= (safe-name e1 :c) "canvass_teams")
|
||||
"In :c convention, hyphen is replaced by underscore")
|
||||
(is (= (safe-name e1 :c-sharp) "CanvassTeams")
|
||||
"In :c-sharp convention, hyphen is suppressed and words capitalised")
|
||||
(is (= (safe-name e1 :java) "canvassTeams")
|
||||
"In :java convention, hyphen is suppressed and embedded words capitalised")
|
||||
(is (= (safe-name e1 :sql) "team")
|
||||
"In :sql convention, the :table attribute is preferred")
|
||||
(is (= (safe-name p1) "id"))
|
||||
(is (= (safe-name p1 :c) "id"))
|
||||
(is (= (safe-name p1 :c-sharp) "Id"))
|
||||
(is (= (safe-name p1 :java) "id"))
|
||||
(is (= (safe-name p1 :sql) "id"))
|
||||
(is (= (safe-name p2) "withunderscore")
|
||||
"Absent a convention, underscore is not considered safe")
|
||||
(is (= (safe-name p2 :c) "with_underscore")
|
||||
"In :c convention, underscore is considered safe")
|
||||
(is (= (safe-name p2 :c-sharp) "WithUnderscore")
|
||||
"In :c-sharp convention, initial letters are capialised and underscore is suppressed")
|
||||
(is (= (safe-name p2 :java) "withUnderscore")
|
||||
"In :java convention, underscore is suppressed and embedded words capitalised")
|
||||
(is (= (safe-name p2 :sql) "with_hyphen")
|
||||
"In :sql convention, the column-name variant is preferred, and hyphens replaced with underscores"))))
|
||||
|
||||
|
||||
(deftest key-names-tests
|
||||
(testing "key-names"
|
||||
(let [e1 {:tag :entity
|
||||
:attrs {:name "canvass-teams" :table "team"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "members" :type "link" :entity "canvassers"}}
|
||||
{:tag :property
|
||||
:attrs {:name "organisers" :type "link" :entity "canvassers"}}]}
|
||||
e2 {:tag :entity
|
||||
:attrs {:name "canvass-teams" :table "team"}
|
||||
:content [{:tag :key
|
||||
:content [{:tag :property
|
||||
:attrs {:name "id" :type "integer" :distinct "system"}}
|
||||
{:tag :property
|
||||
:attrs {:name "shard" :type "string" :default "SW"}}]}
|
||||
{:tag :property
|
||||
:attrs {:name "members" :type "link" :entity "canvassers"}}
|
||||
{:tag :property
|
||||
:attrs {:name "organisers" :type "link" :entity "canvassers"}}]}]
|
||||
(is (= (key-names e1) #{"id"}))
|
||||
(is (= (key-names e1 true) #{:id}))
|
||||
(is (= (key-names e2) #{"id" "shard"}))
|
||||
(is (= (key-names e2 true) #{:id :shard})))))
|
||||
|
||||
|
|
Loading…
Reference in a new issue