nodulator
Version:
Complete NodeJS Framework for Restfull APIs
651 lines (458 loc) • 28.4 kB
HTML
<html>
<head>
<title>Schema.ls</title>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;">
<link rel="stylesheet" media="all" href="docco.css" />
</head>
<body>
<div id="container">
<div id="background"></div>
<ul id="jump_to">
<li>
<a class="large" href="javascript:void(0);">Jump To …</a>
<a class="small" href="javascript:void(0);">+</a>
<div id="jump_wrapper">
<div id="jump_page_wrapper">
<div id="jump_page">
<a class="source" href="Nodulator.html">
Nodulator.ls
</a>
<a class="source" href="ClientDB.html">
ClientDB.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="Resource.html">
Resource.ls
</a>
<a class="source" href="Bus.html">
Bus.ls
</a>
<a class="source" href="Cache.html">
Cache.ls
</a>
<a class="source" href="ChangeWatcher.html">
ChangeWatcher.ls
</a>
<a class="source" href="Debug.html">
Debug.ls
</a>
<a class="source" href="Wrappers.html">
Wrappers.ls
</a>
<a class="source" href="Nodulator.html">
Nodulator.ls
</a>
<a class="source" href="Resource.html">
Resource.ls
</a>
<a class="source" href="Schema.html">
Schema.ls
</a>
<a class="source" href="NModule.html">
NModule.ls
</a>
<a class="source" href="AccountResource.html">
AccountResource.ls
</a>
<a class="source" href="User.html">
User.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="Nodulator-Angular.html">
Nodulator-Angular.ls
</a>
<a class="source" href="TaskDirective.html">
TaskDirective.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="TaskService.html">
TaskService.ls
</a>
<a class="source" href="main.html">
main.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="Task.html">
Task.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="DOM.html">
DOM.ls
</a>
<a class="source" href="View.html">
View.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="Nodulator.html">
Nodulator.ls
</a>
<a class="source" href="Mongo.html">
Mongo.ls
</a>
<a class="source" href="Mysql.html">
Mysql.ls
</a>
<a class="source" href="SqlMem.html">
SqlMem.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
<a class="source" href="Resource.html">
Resource.ls
</a>
<a class="source" href="Request.html">
Request.ls
</a>
<a class="source" href="Route.html">
Route.ls
</a>
<a class="source" href="index.html">
index.ls
</a>
</div>
</div>
</li>
</ul>
<ul class="sections">
<li id="title">
<div class="annotation">
<h1>Schema.ls</h1>
</div>
</li>
<li id="section-1">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-1">¶</a>
</div>
</div>
<div class="content"><div class='highlight'><pre><span class="hljs-built_in">require</span>! {
async
underscore: _
validator: Validator
\./Helpers/Debug</pre></div></div>
</li>
<li id="section-2">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-2">¶</a>
</div>
<p>../../ : N</p>
</div>
<div class="content"><div class='highlight'><pre>}
<span class="hljs-function">
<span class="hljs-title">validationError</span> = <span class="hljs-params">(field, value, message)</span> -></span>
field: field
value: value
message: <span class="hljs-string">"The '#field' field <span class="hljs-subst">#{<span class="hljs-keyword">if</span> value? => <span class="hljs-string">"with value '#value'"</span>}</span> #message"</span>
typeCheck =
bool: <span class="hljs-function"><span class="hljs-params">(value)</span> -></span> <span class="hljs-keyword">typeof</span>(value) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'string'</span> <span class="hljs-keyword">and</span> <span class="hljs-string">''</span> + value <span class="hljs-keyword">is</span> <span class="hljs-string">'true'</span> <span class="hljs-keyword">or</span> <span class="hljs-string">''</span> + value <span class="hljs-keyword">is</span> <span class="hljs-string">'false'</span>
int: <span class="hljs-keyword">is</span>-type \Number
float: <span class="hljs-keyword">is</span>-type \Number
string: <span class="hljs-keyword">is</span>-type \String
date: Validator.isDate
email: Validator.isEmail
array: <span class="hljs-function"><span class="hljs-params">(value)</span> -></span> Array.isArray value
arrayOf: <span class="hljs-function"><span class="hljs-params">(type)</span> -></span> (value) ~> <span class="hljs-keyword">not</span> _(map (@[type]), value).contains (item) -> item <span class="hljs-keyword">is</span> <span class="hljs-literal">false</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SchemaProperty</span></span>
name: <span class="hljs-literal">null</span>
default: <span class="hljs-literal">null</span>
unique: <span class="hljs-literal">false</span>
optional: <span class="hljs-literal">true</span>
internal: <span class="hljs-literal">false</span>
unique: <span class="hljs-literal">false</span>
type: <span class="hljs-literal">null</span>
validation: <span class="hljs-literal">null</span>
(@name, @type, @optional) ->
<span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> Error <span class="hljs-string">'SchemaProperty must have a name'</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> @name?
<span class="hljs-keyword">if</span> <span class="hljs-keyword">is</span>-type \Array @type
@validation = typeCheck.arrayOf @type<span class="hljs-number">.0</span>
<span class="hljs-keyword">else</span>
@validation = typeCheck[@type]
Default: <span class="hljs-function"><span class="hljs-params">(@default)</span> -></span> @
Unique: <span class="hljs-function"><span class="hljs-params">(@unique = <span class="hljs-literal">true</span>)</span> -></span> @
Optional: <span class="hljs-function"><span class="hljs-params">(@optional = <span class="hljs-literal">true</span>)</span> -></span> @
Required: <span class="hljs-function"><span class="hljs-params">(required = <span class="hljs-literal">true</span>)</span> -></span> @optional = !required; @
Virtual: <span class="hljs-function"><span class="hljs-params">(@virtual = <span class="hljs-literal">null</span>)</span> -></span> @
Internal: <span class="hljs-function"><span class="hljs-params">(@internal = <span class="hljs-literal">true</span>)</span> -></span> @
Unique: <span class="hljs-function"><span class="hljs-params">(@unique = <span class="hljs-literal">true</span>)</span> -></span> @
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Schema</span></span>
mode: <span class="hljs-literal">null</span>
(@name, @mode = <span class="hljs-string">'free'</span>) ->
@properties = []
@assocs = []
@habtm = []
@debug = <span class="hljs-keyword">new</span> Debug <span class="hljs-string">"N::Resource::<span class="hljs-subst">#{@name}</span>::Schema"</span>, Debug.colors.cyan
Populate: <span class="hljs-function"><span class="hljs-params">(instance, blob)</span> -></span>
res = obj-to-pairs blob |> filter (<span class="hljs-number">.0</span><span class="hljs-number">.0</span> <span class="hljs-keyword">isnt</span> \_) |> pairs-to-obj
instance <<< res
@properties
|> each (<span class="hljs-function">-></span> instance[it.name] =
| blob[it.name]? => that
| it.default? => <span class="hljs-keyword">switch</span>
| <span class="hljs-keyword">is</span>-type \Function it.default => it.default!
| _ => it.default
| _ => void)
@assocs |> each -> instance[it.name] = that <span class="hljs-keyword">if</span> blob[it.name]?
@properties
|> filter (.virtual?)
|> each ~>
<span class="hljs-keyword">try</span>
result = it.virtual.call instance, instance, (val) ~>
instance[it.name] = val
<span class="hljs-keyword">if</span> result?
instance[it.name] = result
<span class="hljs-keyword">catch</span> e
instance[it.name] = <span class="hljs-literal">undefined</span>
instance
Filter: <span class="hljs-function"><span class="hljs-params">(instance)</span> -></span>
res = {}
<span class="hljs-keyword">if</span> @mode <span class="hljs-keyword">is</span> \strict
res.id = instance.id
@properties
|> filter (<span class="hljs-function">-></span> <span class="hljs-keyword">not</span> it.virtual?)
|> each -> res[it.name] = instance[it.name]
<span class="hljs-keyword">else</span>
each (~>
<span class="hljs-keyword">if</span> it[<span class="hljs-number">0</span>] <span class="hljs-keyword">isnt</span> \_ <span class="hljs-keyword">and</span> <span class="hljs-keyword">typeof</span>! instance[it] <span class="hljs-keyword">isnt</span> <span class="hljs-string">'Function'</span> <span class="hljs-keyword">and</span>
it <span class="hljs-keyword">not</span> <span class="hljs-keyword">in</span> map (.name), @assocs <span class="hljs-keyword">and</span>
<span class="hljs-keyword">not</span> _(@properties).findWhere(name: it)?.virtual? <span class="hljs-keyword">and</span>
(<span class="hljs-keyword">typeof</span>! instance[it]) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'Object'</span> <span class="hljs-keyword">and</span>
(<span class="hljs-keyword">typeof</span>! instance[it]) <span class="hljs-keyword">isnt</span> <span class="hljs-string">'Array'</span>
res[it] = instance[it]
), keys instance
res
RemoveInternals: <span class="hljs-function"><span class="hljs-params">(blob)</span> -></span>
@properties
|> filter (.internal)
|> each -> <span class="hljs-keyword">delete</span> blob[it.name]
blob
Field: <span class="hljs-function"><span class="hljs-params">(name, type)</span> -></span>
<span class="hljs-keyword">return</span> that <span class="hljs-keyword">if</span> _(@properties).findWhere name: name
@properties.push <span class="hljs-keyword">new</span> SchemaProperty name, type, @mode <span class="hljs-keyword">is</span> <span class="hljs-string">'free'</span>
@properties[*<span class="hljs-number">-1</span>]</pre></div></div>
</li>
<li id="section-3">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-3">¶</a>
</div>
<p>Check for schema validity</p>
</div>
<div class="content"><div class='highlight'><pre> Validate: <span class="hljs-function"><span class="hljs-params">(blob, done)</span> -></span>
errors = []
@properties
|> each ~>
errors := errors.concat @_CheckPresence blob, it
errors := errors.concat @_CheckValid blob, it
errors = errors.concat @_CheckNotInSchema blob
@_CheckUnique blob, @Resource, <span class="hljs-function"><span class="hljs-params">(err, results)</span> -></span>
<span class="hljs-keyword">if</span> err?
errors := errors.concat results
done(<span class="hljs-keyword">if</span> errors.length => {errors} <span class="hljs-keyword">else</span> <span class="hljs-literal">null</span>)
GetVirtuals: <span class="hljs-function"><span class="hljs-params">(instance, blob)</span> -></span>
res = {}
(@properties <span class="hljs-keyword">or</span> [])
|> filter (.virtual?)
|> each (<span class="hljs-function">-></span> res[it.name] = it.virtual.call instance, instance, <span class="hljs-function">-></span>)
res
_CheckPresence: <span class="hljs-function"><span class="hljs-params">(blob, property)</span> -></span>
<span class="hljs-keyword">if</span> !property.optional <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> property.default? <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> blob[property.name]? <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> property.virtual?
[validationError property.name, blob[property.name], <span class="hljs-string">' was not present.'</span>]
<span class="hljs-keyword">else</span>
[]
_CheckValid: <span class="hljs-function"><span class="hljs-params">(blob, property)</span> -></span>
<span class="hljs-keyword">if</span> blob[property.name]? <span class="hljs-keyword">and</span> <span class="hljs-keyword">not</span> (property.validation)(blob[property.name])
[validationError property.name,
blob[property.name],
<span class="hljs-string">' was not a valid '</span> + property.type]
<span class="hljs-keyword">else</span>
[]
_CheckNotInSchema: <span class="hljs-function"><span class="hljs-params">(blob)</span> -></span>
<span class="hljs-keyword">return</span> [] <span class="hljs-keyword">if</span> @mode <span class="hljs-keyword">is</span> \free
<span class="hljs-keyword">for</span> field, value <span class="hljs-keyword">of</span> blob <span class="hljs-keyword">when</span> <span class="hljs-keyword">not</span> _(@properties).findWhere name: field <span class="hljs-keyword">and</span> field <span class="hljs-keyword">isnt</span> \id
validationError field, blob[field], <span class="hljs-string">' is not in schema'</span>
_CheckUnique: <span class="hljs-function"><span class="hljs-params">(blob, Resource, done)</span> -></span>
res = []
async.eachSeries filter(<span class="hljs-function"><span class="hljs-params">(.unique)</span>, @properties), <span class="hljs-params">(property, done)</span> -></span>
Resource.Fetch (property.name): blob[property.name]
.Then ->
res.push validationError property.name, blob[property.name], <span class="hljs-string">' must be unique'</span>
done {err: <span class="hljs-string">'not unique'</span>}
.Catch -> done!
, <span class="hljs-function"><span class="hljs-params">(err, results)</span> -></span> done err, res
PrepareRelationship: <span class="hljs-function"><span class="hljs-params">(isArray, field, description)</span> -></span>
type = <span class="hljs-literal">null</span>
foreign = <span class="hljs-literal">null</span>
<span class="hljs-function"> <span class="hljs-title">get</span> = <span class="hljs-params">(blob, done)</span> -></span>
done <span class="hljs-keyword">new</span> Error <span class="hljs-string">'No local or distant key given'</span></pre></div></div>
</li>
<li id="section-4">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-4">¶</a>
</div>
<p>debug-res.Log “Preparing Relationships with #{description.type.name}”</p>
</div>
<div class="content"><div class='highlight'><pre>
<span class="hljs-keyword">if</span> description.localKey?
keyType = \local
foreign = description.localKey
<span class="hljs-function"> <span class="hljs-title">get</span> = <span class="hljs-params">(blob, done, _depth)</span> -></span>
<span class="hljs-keyword">if</span> _depth < <span class="hljs-number">0</span>
<span class="hljs-keyword">return</span> done()
<span class="hljs-keyword">if</span> !isArray
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> typeCheck.int blob[description.localKey]
<span class="hljs-keyword">return</span> done <span class="hljs-keyword">new</span> Error <span class="hljs-string">'Model association needs integer as id and key'</span>
<span class="hljs-keyword">else</span>
<span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> typeCheck.array blob[description.localKey]
<span class="hljs-keyword">return</span> done <span class="hljs-keyword">new</span> Error <span class="hljs-string">'Model association needs array of integer as ids and localKeys'</span>
description.type._FetchUnwrapped blob[description.localKey], done, _depth
<span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> description.distantKey?
foreign = description.distantKey
keyType = \distant
<span class="hljs-function"> <span class="hljs-title">get</span> = <span class="hljs-params">(blob, done, _depth)</span> -></span>
<span class="hljs-keyword">if</span> _depth < <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> blob.id?
<span class="hljs-keyword">return</span> done()
<span class="hljs-keyword">if</span> !isArray
description.type._FetchUnwrapped {<span class="hljs-string">"<span class="hljs-subst">#{description.distantKey}</span>"</span>: blob.id} , done, _depth
<span class="hljs-keyword">else</span>
description.type._ListUnwrapped {<span class="hljs-string">"<span class="hljs-subst">#{description.distantKey}</span>"</span>: blob.id}, done, _depth
toPush =
keyType: keyType
type: description.type
name: field
Get: get
foreign: foreign
toPush.default = description.default <span class="hljs-keyword">if</span> description.default?
@assocs.push toPush</pre></div></div>
</li>
<li id="section-5">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-5">¶</a>
</div>
<p>Get each associated Resource</p>
</div>
<div class="content"><div class='highlight'><pre> FetchAssoc: <span class="hljs-function"><span class="hljs-params">(blob, done, _depth)</span> -></span>
assocs = {}</pre></div></div>
</li>
<li id="section-6">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-6">¶</a>
</div>
<p>@debug.Log “Fetching #{@assocs.length} assocs with Depth #{_depth}”</p>
</div>
<div class="content"><div class='highlight'><pre> async.eachSeries @assocs, (resource, _done) ~>
<span class="hljs-function"> <span class="hljs-title">done</span> = <span class="hljs-params">(err, data)</span>-></span>
_done err, data</pre></div></div>
</li>
<li id="section-7">
<div class="annotation">
<div class="pilwrap ">
<a class="pilcrow" href="#section-7">¶</a>
</div>
<p>@debug.Log “Assoc: Fetching #{resource.name}”</p>
</div>
<div class="content"><div class='highlight'><pre> resource.Get blob, <span class="hljs-function"><span class="hljs-params">(err, instance)</span> -></span>
assocs[resource.name] = resource.default <span class="hljs-keyword">if</span> resource.default?
<span class="hljs-keyword">if</span> err? <span class="hljs-keyword">and</span> resource.type <span class="hljs-keyword">is</span> \distant => done!
<span class="hljs-keyword">else</span>
assocs[resource.name] = instance <span class="hljs-keyword">if</span> instance?
done!
, _depth
, <span class="hljs-function"><span class="hljs-params">(err)</span> -></span>
<span class="hljs-keyword">return</span> done err <span class="hljs-keyword">if</span> err?
done <span class="hljs-literal">null</span>, _.extend blob, assocs
HasOneThrough: <span class="hljs-function"><span class="hljs-params">(res, through)</span> -></span>
get = (blob, done, _depth) ~>
<span class="hljs-keyword">return</span> done! <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> _depth <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> blob.id?
assoc = _(@assocs).findWhere name: capitalize through.name
assoc.Get blob, <span class="hljs-function"><span class="hljs-params">(err, instance)</span> -></span>
<span class="hljs-keyword">return</span> done err <span class="hljs-keyword">if</span> err?
done <span class="hljs-literal">null</span>, instance[capitalize res._type]
, _depth + <span class="hljs-number">1</span>
toPush =
keyType: <span class="hljs-string">'distant'</span>
type: res
name: capitalize res._type
Get: get
@assocs.push toPush
HasManyThrough: <span class="hljs-function"><span class="hljs-params">(res, through)</span> -></span>
get = (blob, done, _depth) ~>
<span class="hljs-keyword">return</span> done! <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> _depth <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> blob.id?
assoc = _(@assocs).findWhere name: capitalize through.name
assoc.Get blob, <span class="hljs-function"><span class="hljs-params">(err, instance)</span> -></span>
<span class="hljs-keyword">return</span> done err <span class="hljs-keyword">if</span> err?
res._ListUnwrapped instance[capitalize res._type], <span class="hljs-function"><span class="hljs-params">(err, instances)</span> -></span>
<span class="hljs-keyword">return</span> done err <span class="hljs-keyword">if</span> err?
done <span class="hljs-literal">null</span>, instances
, depth
, _depth
toPush =
keyType: <span class="hljs-string">'distant'</span>
type: res
name: capitalize res._type
Get: get
@assocs.push toPush
HasAndBelongsToMany: <span class="hljs-function"><span class="hljs-params">(res, through)</span> -></span>
get = (blob, done, _depth) ~>
<span class="hljs-keyword">return</span> done! <span class="hljs-keyword">if</span> _depth < <span class="hljs-number">0</span> <span class="hljs-keyword">or</span> <span class="hljs-keyword">not</span> blob.id?
through._ListUnwrapped {(@name + \Id): blob.id}, (err, instances) ~>
<span class="hljs-keyword">return</span> done err <span class="hljs-keyword">if</span> err?
async.mapSeries instances, (instance, done) ~>
res._FetchUnwrapped instance[res._type + \Id ], done, _depth - <span class="hljs-number">1</span>
, (err, results) ~>
<span class="hljs-keyword">return</span> done err <span class="hljs-keyword">if</span> err?
done <span class="hljs-literal">null</span>, results
, _depth
toPush =
keyType: <span class="hljs-string">'distant'</span>
type: res
name: capitalize res._type + \s
Get: get
@assocs.push toPush
@habtm.push through
Inherit: <span class="hljs-function">-></span>
properties: @properties
|> map ->
sp = <span class="hljs-keyword">new</span> SchemaProperty it.name, it.type, it.optional
sp <<< it
assocs: map (<span class="hljs-function">-></span> _ {} .extend it), @assocs
habtm: map (<span class="hljs-function">-></span> _ {} .extend it), @habtm
<span class="hljs-built_in">module</span>.exports = Schema</pre></div></div>
</li>
</ul>
</div>
</body>
</html>