Skip to content

Commit

Permalink
Bugfix/nested class protocol (#20)
Browse files Browse the repository at this point in the history
* Typo in comment

* Use s-c:load everywhere, fix it to work with nested objects better

Also update tests

* Reorganize some exports

* Simplify a function call

* Remove unused collect-initargs-from-list

* Leave slots with missing, optional values unbound

* Version 0.7.0

* Update docs
  • Loading branch information
fisxoj authored Nov 4, 2019
1 parent 465ea1c commit 869a693
Show file tree
Hide file tree
Showing 12 changed files with 108 additions and 125 deletions.
18 changes: 2 additions & 16 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

Sanity clause is a data validation/contract library. You might use it for configuration data, validating an api response, or documents from a datastore. In a dynamically typed language, it helps you define clearly defined areas of doubt and uncertainty. We should love our users, but we should never blindly trust their inputs.

To make use of it, you define schemas, which can be property lists with symbols for keys and instances of :class:`sanity-clause.field:field` subclasses that dictate the type of values you expect as well as the shape of the property list to be returned after deserializing and validating data. For example::
To make use of it, you define schemas, which can be property lists with keys and instances of :class:`sanity-clause.field:field` subclasses as values (eg. :class:`sanity-clause.field:integer-field`, :class:`sanity-clause.field:string-field`, &c.) or using the class-based interface via :class:`sanity-clause.schema:validated-metaclass`. For example::

(list :name (make-field :string) :age (make-field :integer))

Expand All @@ -35,19 +35,16 @@ You can load these sorts of schemas from a file by writing them as sexps with ke
and then loading them using :function:`sanity-clause.loadable-schema:load-schema` to load them.


Finally, you can also define class-based schemas using :class:`sanity-clause:validated-metaclass` like::
To use class-based schemas using :class:`sanity-clause:validated-metaclass` you can do things like::

(defclass person ()
((favorite-dog :type symbol
:field-type :member
:members (:wedge :walter)
:initarg :favorite-dog
:required t)
(age :type (integer 0)
:initarg :age
:required t)
(potato :type string
:initarg :potato
:required t))
(:metaclass sanity-clause:validated-metaclass))

Expand Down Expand Up @@ -84,55 +81,44 @@ Example

(defclass contact-object ()
((name :type string
:initarg :name
:documentation "The identifying name of the contact person/organization.")
(url :type string
:field-type :uri
:initarg :url
:documentation "The URL pointing to the contact information. MUST be in the format of a URL.")
(email :type string
:field-type :email
:initarg :email
:documentation "The email address of the contact person/organization. MUST be in the format of an email address."))
(:metaclass sanity-clause:validated-metaclass))


(defclass license-object ()
((name :type string
:initarg :name
:documentation "The license name used for the API.")
(url :type string
:field-type :uri
:initarg :url
:documentation "A URL to the license used for the API. MUST be in the format of a URL."))
(:metaclass sanity-clause:validated-metaclass))


(defclass info-object ()
((title :type string
:data-key "title"
:initarg :title
:required t
:documentation "The title of the application.")
(description :type string
:initarg :description
:documentation "A short description of the application. GFM syntax can be used for rich text representation.")
(terms-of-service :type string
:data-key "termsOfService"
:initarg :terms-of-service
:documentation "The Terms of Service for the API.")
(contact :type contact-object
:field-type :nested
:element-type contact-object
:initarg :contact
:documentation "The contact information for the exposed API.")
(license :type license-object
:field-type :nested
:element-type license-object
:initarg :license
:documentation "The license information for the exposed API.")
(version :type string
:initarg :version
:documentation "Provides the version of the application API (not to be confused with the specification version)."
:required t))
(:metaclass sanity-clause:validated-metaclass))
Expand Down
45 changes: 20 additions & 25 deletions docs/default.css
Original file line number Diff line number Diff line change
Expand Up @@ -210,11 +210,8 @@ pre.address {
font-size: 100% }

pre.literal-block, pre.doctest-block {
font-family: 'Fira Mono', monospace;
padding: 1em 2em;
background: #eee;
border-radius: 4px;
overflow-x: auto; }
margin-left: 2em ;
margin-right: 2em }

span.classifier {
font-family: sans-serif ;
Expand Down Expand Up @@ -282,12 +279,11 @@ ul.auto-toc {

/* Begin coo customization */

@import url("//fonts.googleapis.com/css?family=Fira+Mono|Fira+Sans&display=swap");
@import url("//fonts.googleapis.com/css?family=Fira+Mono|Fira+Sans");

:root {
font-family: "Fira Sans", sans-serif;
font-size: 16px;
line-height: 1.5em;
}

.document {
Expand All @@ -299,56 +295,55 @@ span.pre {
font-family: "Fira Mono", monospace;
}


h2 {
background: #eee;
border-width: 1px 0 0;
border-width: 1px 0;
border-color: black;
border-style: solid;
padding: 1em 0 0.5em 1em;
letter-spacing: 2px;
}

tt {
font-family: "Fira Mono", monospace;
}

.ref-package {
font-family: 'Fira Mono', monospace;
font-family: "Fira Mono", mono;
background-color: #eee;
color: #ab7967;
padding: 3px;
border-radius: 4px;
border-radius: 6px;
}

.ref-class {
font-family: 'Fira Mono', monospace;
font-family: "Fira Mono", mono;
background-color: #eee;
color: #99c794;
padding: 3px;
border-radius: 4px;
border-radius: 6px;
}

.ref-variable {
font-family: 'Fira Mono', monospace;
font-family: "Fira Mono", mono;
background-color: #eee;
color: #6699cc;
padding: 3px;
border-radius: 4px;
border-radius: 6px;
}

.ref-macro {
font-family: 'Fira Mono', monospace;
font-family: "Fira Mono", mono;
background-color: #eee;
color: #fac863;
padding: 3px;
border-radius: 4px;
border-radius: 6px;
}

.ref-function {
font-family: 'Fira Mono', monospace;
font-family: "Fira Mono", mono;
background-color: #eee;
color: #c594c5;
padding: 3px;
border-radius: 4px;
border-radius: 6px;
}

.param {
background-color: #eee;
color: #6699cc;
}

Expand Down
22 changes: 4 additions & 18 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<table class="docinfo" frame="void" rules="none"><col class="docinfo-name" />
<col class="docinfo-content" />
<tbody valign="top">
<tr><th class="docinfo-name">Author</th><td>Matt Novenstern</td></tr><tr><th class="docinfo-name">Version</th><td>0.6.0</td></tr><tr class="field"><th colspan="1" class="docinfo-name">license:</th><td class="field-body">LLGPLv3+</td>
<tr><th class="docinfo-name">Author</th><td>Matt Novenstern</td></tr><tr><th class="docinfo-name">Version</th><td>0.7.0</td></tr><tr class="field"><th colspan="1" class="docinfo-name">license:</th><td class="field-body">LGPLv3</td>
</tr>
<tr class="field"><th colspan="1" class="docinfo-name">homepage:</th><td class="field-body"><a class="reference" href="https://fisxoj.github.io/sanity-clause/"><span>https://fisxoj.github.io/sanity-clause/</span></a></td>
</tr>
Expand All @@ -33,7 +33,7 @@
<blockquote> <p>You can't fool me. There ain't no santy clause!</p>
<p class="attribution">&mdash; Chico Marx </p>
</blockquote> <p>Sanity clause is a data validation/contract library. You might use it for configuration data, validating an api response, or documents from a datastore. In a dynamically typed language, it helps you define clearly defined areas of doubt and uncertainty. We should love our users, but we should never blindly trust their inputs.</p>
<p>To make use of it, you define schemas, which can be property lists with symbols for keys and instances of <a class="reference ref-class" href="sanity-clause.field.html#class__field"><span class="ref-class">sanity-clause.field:field</span></a> subclasses that dictate the type of values you expect as well as the shape of the property list to be returned after deserializing and validating data. For example:</p>
<p>To make use of it, you define schemas, which can be property lists with keys and instances of <a class="reference ref-class" href="sanity-clause.field.html#class__field"><span class="ref-class">sanity-clause.field:field</span></a> subclasses as values (eg. <a class="reference ref-class" href="sanity-clause.field.html#class__integer-field"><span class="ref-class">sanity-clause.field:integer-field</span></a>, <a class="reference ref-class" href="sanity-clause.field.html#class__string-field"><span class="ref-class">sanity-clause.field:string-field</span></a>, &amp;c.) or using the class-based interface via <a class="reference ref-class" href="sanity-clause.schema.html#class__validated-metaclass"><span class="ref-class">sanity-clause.schema:validated-metaclass</span></a>. For example:</p>
<pre class="literal-block">
(list :name (make-field :string) :age (make-field :integer))
</pre><p>You can load these sorts of schemas from a file by writing them as sexps with keywords, like this:</p>
Expand All @@ -43,19 +43,16 @@
(:key (:string :validator (:not-empty) :default "potato")
:key2 (:integer :validator ((:int :min 0)) :default 2))
</pre><p>and then loading them using <a class="reference ref-function" href="sanity-clause.loadable-schema.html#function__load-schema"><span class="ref-function">sanity-clause.loadable-schema:load-schema</span></a> to load them.</p>
<p>Finally, you can also define class-based schemas using <a class="reference ref-class" href="sanity-clause.schema.html#class__validated-metaclass"><span class="ref-class">sanity-clause.schema:validated-metaclass</span></a> like:</p>
<p>To use class-based schemas using <a class="reference ref-class" href="sanity-clause.schema.html#class__validated-metaclass"><span class="ref-class">sanity-clause.schema:validated-metaclass</span></a> you can do things like:</p>
<pre class="literal-block">
(defclass person ()
((favorite-dog :type symbol
:field-type :member
:members (:wedge :walter)
:initarg :favorite-dog
:required t)
(age :type (integer 0)
:initarg :age
:required t)
(potato :type string
:initarg :potato
:required t))
(:metaclass sanity-clause:validated-metaclass))
</pre><p>which will validate their initargs when you instantiate them (<strong><span>BUT NOT WHEN YOU SET SLOTS</span></strong>). Hopefully, that will be added eventually, perhaps as an optional feature.</p>
Expand Down Expand Up @@ -87,55 +84,44 @@

(defclass contact-object ()
((name :type string
:initarg :name
:documentation "The identifying name of the contact person/organization.")
(url :type string
:field-type :uri
:initarg :url
:documentation "The URL pointing to the contact information. MUST be in the format of a URL.")
(email :type string
:field-type :email
:initarg :email
:documentation "The email address of the contact person/organization. MUST be in the format of an email address."))
(:metaclass sanity-clause:validated-metaclass))


(defclass license-object ()
((name :type string
:initarg :name
:documentation "The license name used for the API.")
(url :type string
:field-type :uri
:initarg :url
:documentation "A URL to the license used for the API. MUST be in the format of a URL."))
(:metaclass sanity-clause:validated-metaclass))


(defclass info-object ()
((title :type string
:data-key "title"
:initarg :title
:required t
:documentation "The title of the application.")
(description :type string
:initarg :description
:documentation "A short description of the application. GFM syntax can be used for rich text representation.")
(terms-of-service :type string
:data-key "termsOfService"
:initarg :terms-of-service
:documentation "The Terms of Service for the API.")
(contact :type contact-object
:field-type :nested
:element-type contact-object
:initarg :contact
:documentation "The contact information for the exposed API.")
(license :type license-object
:field-type :nested
:element-type license-object
:initarg :license
:documentation "The license information for the exposed API.")
(version :type string
:initarg :version
:documentation "Provides the version of the application API (not to be confused with the specification version)."
:required t))
(:metaclass sanity-clause:validated-metaclass))
Expand Down Expand Up @@ -169,7 +155,7 @@
</ul>
</div>
</div>
<hr class ="docutils footer" /><div class="footer">Generated on : Thu, 24 Oct 2019 17:05:33 .
<hr class ="docutils footer" /><div class="footer">Generated on : Sun, 03 Nov 2019 21:45:48 .
</div>
</div>
</body>
Expand Down
28 changes: 26 additions & 2 deletions docs/sanity-clause.field.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,31 @@
(make-field :map :key-field :string :value-field :integer)
(deserialize * '(("potato" . 4) ("chimp" . 11)))

</pre><a id="condition-required-value-error" class="target" name="condition-required-value-error"></a></div>
</pre><a id="class-nested-field" class="target" name="class-nested-field"></a></div>
<div id="nested-field" class="section"><h3><a name="nested-field"><tt class="docutils literal">
<span class="pre">nested-field</span></tt></a></h3>
<table class="field-list" frame="void" rules="none"><col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field"><th colspan="1" class="field-name">Superclasses:</th><td class="field-body"><tt class="docutils literal">
<span class="pre">(FIELD</span> <span class="pre">NESTED-ELEMENT)</span></tt></td>
</tr>
</tbody>
</table>
<p>A field that represents a complex object located at this slot.</p>
<a id="class-list-field" class="target" name="class-list-field"></a></div>
<div id="list-field" class="section"><h3><a name="list-field"><tt class="docutils literal">
<span class="pre">list-field</span></tt></a></h3>
<table class="field-list" frame="void" rules="none"><col class="field-name" />
<col class="field-body" />
<tbody valign="top">
<tr class="field"><th colspan="1" class="field-name">Superclasses:</th><td class="field-body"><tt class="docutils literal">
<span class="pre">(FIELD</span> <span class="pre">NESTED-ELEMENT)</span></tt></td>
</tr>
</tbody>
</table>
<p>A field that contains a list of values satsified by another field.</p>
<a id="condition-required-value-error" class="target" name="condition-required-value-error"></a></div>
<div id="required-value-error" class="section"><h3><a name="required-value-error"><tt class="docutils literal">
<span class="pre">required-value-error</span></tt></a></h3>
<table class="field-list" frame="void" rules="none"><col class="field-name" />
Expand Down Expand Up @@ -236,7 +260,7 @@
<p>A base class for all fields that controls how they are (de?)serialized.</p>
</div>
</div>
<hr class ="docutils footer" /><div class="footer">Generated on : Thu, 24 Oct 2019 17:05:33 .
<hr class ="docutils footer" /><div class="footer">Generated on : Sun, 03 Nov 2019 21:45:48 .
</div>
</div>
</body>
Expand Down
2 changes: 1 addition & 1 deletion docs/sanity-clause.protocol.html
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
<p>A function that encapsulates getting, corecing, and validating values for a field. Calls <a class="reference ref-function" href="sanity-clause.protocol.html#function__get-value"><span class="ref-function">get-value</span></a>, <a class="reference ref-function" href="sanity-clause.protocol.html#function__deserialize"><span class="ref-function">deserialize</span></a>, and <a class="reference ref-function" href="sanity-clause.protocol.html#function__validate"><span class="ref-function">validate</span></a>.</p>
</div>
</div>
<hr class ="docutils footer" /><div class="footer">Generated on : Thu, 24 Oct 2019 17:05:33 .
<hr class ="docutils footer" /><div class="footer">Generated on : Sun, 03 Nov 2019 21:45:48 .
</div>
</div>
</body>
Expand Down
2 changes: 1 addition & 1 deletion docs/sanity-clause.util.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<span class="pre">((KEY</span> <span class="pre">VALUE)</span> <span class="pre">DATA</span> <span class="pre">&amp;BODY</span> <span class="pre">BODY)</span></tt></p>
</div>
</div>
<hr class ="docutils footer" /><div class="footer">Generated on : Thu, 24 Oct 2019 17:05:33 .
<hr class ="docutils footer" /><div class="footer">Generated on : Sun, 03 Nov 2019 21:45:48 .
</div>
</div>
</body>
Expand Down
2 changes: 1 addition & 1 deletion docs/sanity-clause.validator.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<p>Find a validator function by keyword-spec and return the function represented by the spec.</p>
</div>
</div>
<hr class ="docutils footer" /><div class="footer">Generated on : Thu, 24 Oct 2019 17:05:33 .
<hr class ="docutils footer" /><div class="footer">Generated on : Sun, 03 Nov 2019 21:45:48 .
</div>
</div>
</body>
Expand Down
4 changes: 2 additions & 2 deletions sanity-clause.asd
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(defsystem sanity-clause
:author "Matt Novenstern"
:license "LGPLv3+"
:version "0.6.0"
:license "LGPLv3"
:version "0.7.0"
:homepage "https://fisxoj.github.io/sanity-clause/"
:depends-on ("alexandria"
"cl-arrows"
Expand Down
Loading

0 comments on commit 869a693

Please sign in to comment.