Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

We’re showing branches in this repository, but you can also compare across forks.

base fork: semmypurewal/jermaine
base: 562f2ae0de
...
head fork: semmypurewal/jermaine
compare: 96df37577a
  • 2 commits
  • 4 files changed
  • 0 commit comments
  • 1 contributor
2  build/jermaine-min.js
View
@@ -9,4 +9,4 @@ if(!Array.prototype.indexOf){Array.prototype.indexOf=function(c){if(this==null){
*
* + See issue 24 on github
*/
-window.jermaine.util.namespace("window.jermaine",function(a){var b=function(f){var h=[],l=this,m="invalid setter call for "+f,j,k,e,c,o=false,d,n={},p=window.jermaine.AttrList,g=window.jermaine.Validator;if(f===undefined||typeof(f)!=="string"){throw new Error("Attr: constructor requires a name parameter which must be a string")}d=function(i){for(k=0;k<h.length;++k){h[k](i)}return true};this.validatesWith=function(i){if(typeof(i)==="function"){h.push(new g(i));return this}else{throw new Error("Attr: validator must be a function")}};this.defaultsTo=function(i){j=i;return this};this.isReadOnly=function(){o=true;return this};this.isWritable=function(){o=false;return this};this.on=function(i,q){if(i!=="set"&&i!=="get"){throw new Error("Attr: first argument to the 'on' method should be 'set' or 'get'")}else{if(typeof(q)!=="function"){throw new Error("Attr: second argument to the 'on' method should be a function")}else{n[i]=q}}};this.name=function(){return f};this.validator=function(){return d};this.and=this;this.which=this;this.isImmutable=this.isReadOnly;this.isMutable=this.isWritable;this.clone=function(){var q,r;q=this instanceof p?new p(f):new b(f);for(r=0;r<h.length;++r){q.validatesWith(h[r])}q.defaultsTo(j);if(o){q.isImmutable()}return q};this.addTo=function(s){var q,r,i;if(!s||typeof(s)!=="object"){throw new Error("Attr: addAttr method requires an object parameter")}s[f]=function(u){var t;if(u!==undefined){if(o&&q!==undefined){throw new Error("cannot set the immutable property "+f+" after it has been set")}else{if(!d(u)){throw new Error(m)}else{t=q;q=u;if(n.set!==undefined){n.set.call(s,u,t)}}}return s}else{if(n.get!==undefined){n.get.call(s,q)}return q}};i=typeof(j)==="function"?j():j;if(i!==undefined&&d(i)){s[f](i)}else{if(i!==undefined&&!d(i)){throw new Error("Attr: Default value of "+i+" does not pass validation for "+f)}}};c=function(i){l[i]=function(q){h.push(g.getValidator(i)(q));return l}};for(k=0;k<g.validators().length;++k){c(g.validators()[k])}};a.Attr=b});window.jermaine.util.namespace("window.jermaine",function(a){function b(c){var e=this;a.Attr.call(this,c);var d=function(g,f){return function(){return g[f].apply(g,arguments)}};this.validateWith=this.validatesWith;this.defaultsTo=function(){};this.isImmutable=function(){};this.isMutable=function(){};this.eachOfWhich=this;this.addTo=function(h){var i,f=[],g={};if(!h||typeof(h)!=="object"){throw new Error("AttrList: addTo method requires an object parameter")}else{g.pop=d(f,"pop");g.add=function(j){if((e.validator())(j)){f.push(j);return this}else{throw new Error(e.errorMessage())}};g.replace=function(j,k){if((typeof(j)!=="number")||(parseInt(j,10)!==j)){throw new Error("AttrList: replace method requires index parameter to be an integer")}if(j<0||j>=this.size()){throw new Error("AttrList: replace method index parameter out of bounds")}if(!(e.validator())(k)){throw new Error(e.errorMessage())}f[j]=k;return this};g.at=function(j){if(j<0||j>=this.size()){throw new Error("AttrList: Index out of bounds")}return f[j]};g.get=g.at;g.size=function(){return f.length};g.toJSON=function(m){var k=[],n,l;if(m!==undefined){for(n=0;n<m.length;++n){if(m[n].object===this){k=m[n].JSONrep}}}for(n=0;n<f.length;++n){if(f[n].toJSON){k.push(f[n].toJSON(m))}else{k.push(f[n])}}return k};h[c]=function(){return g}}}}b.prototype=new window.jermaine.Attr(name);a.AttrList=b});window.jermaine.util.namespace("window.jermaine",function(a){var b=function(c,d){if(!c||typeof(c)!=="string"){throw new Error("Method: constructor requires a name parameter which must be a string")}else{if(!d||typeof(d)!=="function"){throw new Error("Method: second parameter must be a function")}}this.addTo=function(e){if(!e||typeof(e)!=="object"){throw new Error("Method: addTo method requires an object parameter")}e[c]=d}};a.Method=b});window.jermaine.util.namespace("window.jermaine",function(a){function b(x){var i=this,s={},j={},r,n,t,f=true,d=[],p=[],u=[],c=a.Method,w=a.Attr,m=a.AttrList,h=a.util.EventEmitter,g,y,l,k,v=function(){},q=function(){},e=function(){if(f){l()}return q.apply(this,arguments)};if(arguments.length>1){x=arguments[arguments.length-1]}if(x&&typeof(x)==="function"){e=new b();x.call(e);return e}else{if(x){throw new Error("Model: specification parameter must be a function")}}var o=function(B,A){var D,z,C;D=B==="Attr"?w:m;z=B==="Attr"?"hasA":"hasMany";f=true;if(typeof(A)==="string"){C=new D(A);j[A]=C;return C}else{throw new Error("Model: "+z+" parameter must be a string")}};g=function(B,A){var z;if(typeof(A)!=="string"){throw new Error("Model: expected string argument to "+B+" method, but recieved "+A)}z=B==="attribute"?j[A]:s[A];if(z===undefined){throw new Error("Model: "+B+" "+A+" does not exist!")}return z};y=function(B){var A,C=[],z=B==="attributes"?j:s;for(A in z){if(z.hasOwnProperty(A)){C.push(A)}}return C};l=function(A){var D=this,B,z,C;e.validate();q=function(){var I=this,G,F,E,M;if(!(this instanceof e)){throw new Error("Model: instances must be created using the new operator")}M=function(Q,P){var O=P==="attributes"?j:s,N;for(N in O){if(O.hasOwnProperty(N)){if(O===j&&k){O[N].isImmutable()}O[N].addTo(Q)}}};E=new h();this.emitter=function(){return E};this.on=function(N,O){I.emitter().on(N,function(P){O.call(I,P)})};var K,L={},J,H;J=function(N){N.on("set",function(Q,P){var O=this;if(L[N.name()]===undefined){L[N.name()]=function(T){var S=[],R=true;for(G=0;G<T.length&&R===true;++G){S.push(T[G]);if(T[G].origin===this){R=false}}if(R){S.push({key:N.name(),origin:this});this.emitter().emit("change",S)}}}if(H!==undefined){if(P!==undefined&&P!==null&&P.emitter!==undefined){P.emitter().removeListener("change",H);H=undefined}}if(Q!==null&&typeof(Q)==="object"&&Q.on!==undefined&&Q.emitter!==undefined){if(P!==undefined&&P!==null&&H!==undefined){P.emitter().removeListener("change",H)}H=function(R){L[N.name()].call(O,R)};Q.emitter().on("change",H)}O.emitter().emit("change",[{key:N.name(),value:Q,origin:O}])})};for(G=0;G<y("attributes").length;++G){K=j[y("attributes")[G]];if(K instanceof w){J.call(this,K)}}this.toJSON=function(O){var S=e.attributes(),R,P,N,Q={},T;if(O===undefined){O=[];O.push({object:this,JSONrep:Q})}else{if(typeof(O)!=="object"){throw new Error("Instance: toJSON should not take a parameter (unless called recursively)")}else{for(P=0;P<O.length;++P){if(O[P].object===this){Q=O[P].JSONrep}}}}for(P=0;P<S.length;++P){T=null;R=this[S[P]]();for(N=0;N<O.length;++N){if(O[N].object===R){T=O[N].JSONrep}}if(R!==undefined&&R!==null&&R.toJSON!==undefined&&T===null){T=(j[S[P]] instanceof m)?[]:{};O.push({object:R,JSONrep:T});O[O.length-1].JSONrep=R.toJSON(O)}if(T===null){Q[S[P]]=R}else{Q[S[P]]=T}}return Q};M(this,"attributes");M(this,"methods");if(r!==undefined){this.toString=r}if(arguments.length>0){if(arguments.length<d.length){C="Constructor requires ";for(G=0;G<d.length;++G){C+=d[G];C+=G===d.length-1?"":", "}C+=" to be specified";throw new Error(C)}if(arguments.length>d.length+p.length){throw new Error("Too many arguments to constructor. Expected "+d.length+" required arguments and "+p.length+" optional arguments")}else{for(G=0;G<arguments.length;++G){F=G<d.length?d[G]:p[G-d.length];if(e.attribute(F) instanceof m){if(Object.prototype.toString.call(arguments[G])!=="[object Array]"){throw new Error("Model: Constructor requires 'names' attribute to be set with an Array")}else{for(z=0;z<arguments[G].length;++z){this[F]().add(arguments[G][z])}}}else{this[F](arguments[G])}}}}v.call(this)};return q};e.hasA=function(z){return o("Attr",z)};e.hasAn=e.hasA;e.hasSome=e.hasA;e.hasMany=function(z){return o("AttrList",z)};e.isA=function(B){var A,z,D,C;f=true;C=function(F){var E,G=new b();for(E in G){if(G.hasOwnProperty(E)&&typeof(F[E])!==typeof(G[E])){return false}}return true};if(typeof(B)!=="function"||!C(B)){throw new Error("Model: parameter sent to isA function must be a Model")}if(u.length===0){u.push(B)}else{throw new Error("Model: Model only supports single inheritance at this time")}z=u[0].attributes();for(A=0;A<z.length;++A){if(j[z[A]]===undefined){j[z[A]]=u[0].attribute(z[A]).clone();j[z[A]].isMutable()}}D=u[0].methods();for(A=0;A<D.length;++A){if(s[D[A]]===undefined){s[D[A]]=u[0].method(D[A])}}for(A=0;A<u.length;A++){e.prototype=new u[A]()}};e.isAn=e.isA;e.parent=function(){return u[0].apply(this,arguments)};e.attribute=function(z){return g("attribute",z)};e.attributes=function(){return y("attributes")};e.method=function(z){return g("method",z)};e.methods=function(){return y("methods")};e.isBuiltWith=function(){var z=false,A;f=true;d=[];p=[];for(A=0;A<arguments.length;++A){if(typeof(arguments[A])==="string"&&arguments[A].charAt(0)!=="%"){if(z){throw new Error("Model: isBuiltWith requires parameters preceded with a % to be the final parameters before the optional function")}else{d.push(arguments[A])}}else{if(typeof(arguments[A])==="string"&&arguments[A].charAt(0)==="%"){z=true;p.push(arguments[A].slice(1))}else{if(typeof(arguments[A])==="function"&&A===arguments.length-1){v=arguments[A]}else{throw new Error("Model: isBuiltWith parameters must be strings except for a function as the optional final parameter")}}}}};e.isImmutable=function(){k=true};e.looksLike=function(z){f=true;r=z};e.respondsTo=function(A,B){var z=new c(A,B);f=true;s[A]=z};e.validate=function(){var B,A=this.attributes(),z=this.methods();for(B=0;B<d.length;++B){try{this.attribute(d[B])}catch(C){throw new Error(d[B]+", specified in the isBuiltWith method, is not an attribute")}}for(B=0;B<p.length;++B){try{this.attribute(p[B])}catch(C){throw new Error(p[B]+", specified in the isBuiltWith method, is not an attribute")}}for(B=0;B<A.length;B++){if(z.indexOf(A[B])>-1){throw new Error("Model: invalid model specification to "+A[B]+" being both an attribute and method")}}if(k){for(B=0;B<A.length;B++){if(d.indexOf(A[B])<0){throw new Error("immutable objects must have all attributes required in a call to isBuiltWith")}}}f=false};return e}a.Model=b});
+window.jermaine.util.namespace("window.jermaine",function(a){var b=function(f){var h=[],l=this,m="invalid setter call for "+f,j,k,e,c,o=false,d,n={},p=window.jermaine.AttrList,g=window.jermaine.Validator;if(f===undefined||typeof(f)!=="string"){throw new Error("Attr: constructor requires a name parameter which must be a string")}d=function(i){for(k=0;k<h.length;++k){h[k](i)}return true};this.validatesWith=function(i){if(typeof(i)==="function"){h.push(new g(i));return this}else{throw new Error("Attr: validator must be a function")}};this.defaultsTo=function(i){j=i;return this};this.isReadOnly=function(){o=true;return this};this.isWritable=function(){o=false;return this};this.on=function(i,q){if(i!=="set"&&i!=="get"){throw new Error("Attr: first argument to the 'on' method should be 'set' or 'get'")}else{if(typeof(q)!=="function"){throw new Error("Attr: second argument to the 'on' method should be a function")}else{n[i]=q}}};this.name=function(){return f};this.validator=function(){return d};this.and=this;this.which=this;this.isImmutable=this.isReadOnly;this.isMutable=this.isWritable;this.clone=function(){var q,r;q=this instanceof p?new p(f):new b(f);for(r=0;r<h.length;++r){q.validatesWith(h[r])}q.defaultsTo(j);if(o){q.isImmutable()}return q};this.addTo=function(s){var q,r,i;if(!s||typeof(s)!=="object"){throw new Error("Attr: addAttr method requires an object parameter")}s[f]=function(u){var t;if(u!==undefined){if(o&&q!==undefined){throw new Error("cannot set the immutable property "+f+" after it has been set")}else{if(!d(u)){throw new Error(m)}else{t=q;q=u;if(n.set!==undefined){n.set.call(s,u,t)}}}return s}else{if(n.get!==undefined){n.get.call(s,q)}return q}};i=typeof(j)==="function"?j():j;if(i!==undefined&&d(i)){s[f](i)}else{if(i!==undefined&&!d(i)){throw new Error("Attr: Default value of "+i+" does not pass validation for "+f)}}};c=function(i){l[i]=function(q){h.push(g.getValidator(i)(q));return l}};for(k=0;k<g.validators().length;++k){c(g.validators()[k])}};a.Attr=b});window.jermaine.util.namespace("window.jermaine",function(a){function b(c){var e=this;a.Attr.call(this,c);var d=function(g,f){return function(){return g[f].apply(g,arguments)}};this.validateWith=this.validatesWith;this.defaultsTo=function(){};this.isImmutable=function(){};this.isMutable=function(){};this.eachOfWhich=this;this.addTo=function(h){var i,f=[],g={};if(!h||typeof(h)!=="object"){throw new Error("AttrList: addTo method requires an object parameter")}else{g.pop=d(f,"pop");g.add=function(j){if((e.validator())(j)){f.push(j);return this}else{throw new Error(e.errorMessage())}};g.replace=function(j,k){if((typeof(j)!=="number")||(parseInt(j,10)!==j)){throw new Error("AttrList: replace method requires index parameter to be an integer")}if(j<0||j>=this.size()){throw new Error("AttrList: replace method index parameter out of bounds")}if(!(e.validator())(k)){throw new Error(e.errorMessage())}f[j]=k;return this};g.at=function(j){if(j<0||j>=this.size()){throw new Error("AttrList: Index out of bounds")}return f[j]};g.get=g.at;g.size=function(){return f.length};g.toJSON=function(m){var k=[],n,l;if(m!==undefined){for(n=0;n<m.length;++n){if(m[n].object===this){k=m[n].JSONrep}}}for(n=0;n<f.length;++n){if(f[n].toJSON){k.push(f[n].toJSON(m))}else{k.push(f[n])}}return k};h[c]=function(){return g}}}}b.prototype=new window.jermaine.Attr(name);a.AttrList=b});window.jermaine.util.namespace("window.jermaine",function(a){var b=function(c,d){if(!c||typeof(c)!=="string"){throw new Error("Method: constructor requires a name parameter which must be a string")}else{if(!d||typeof(d)!=="function"){throw new Error("Method: second parameter must be a function")}}this.addTo=function(e){if(!e||typeof(e)!=="object"){throw new Error("Method: addTo method requires an object parameter")}e[c]=d}};a.Method=b});window.jermaine.util.namespace("window.jermaine",function(a){var b=function(u){var q={},i={},p,f=true,d=[],n=[],r=[],c=a.Method,s=a.Attr,l=a.AttrList,h=a.util.EventEmitter,g,v,k,j,t=function(){},o=function(){},e=function(){if(f){e.validate();k()}return o.apply(this,arguments)};if(arguments.length>1){u=arguments[arguments.length-1]}if(u&&typeof(u)==="function"){e=new b();u.call(e);return e}else{if(u){throw new Error("Model: specification parameter must be a function")}}var m=function(y,x){var A,w,z;A=y==="Attr"?s:l;w=y==="Attr"?"hasA":"hasMany";f=true;if(typeof(x)==="string"){z=new A(x);i[x]=z;return z}else{throw new Error("Model: "+w+" parameter must be a string")}};g=function(y,x){var w;if(typeof(x)!=="string"){throw new Error("Model: expected string argument to "+y+" method, but recieved "+x)}w=y==="attribute"?i[x]:q[x];if(w===undefined){throw new Error("Model: "+y+" "+x+" does not exist!")}return w};v=function(y){var x,z=[],w=y==="attributes"?i:q;for(x in w){if(w.hasOwnProperty(x)){z.push(x)}}return z};k=function(){o=function(){var C,B,z,x,y=e.attributes(),A=e.methods(),w=new h(),F,G={},E,D,H;if(!(this instanceof e)){throw new Error("Model: instances must be created using the new operator")}this.emitter=function(){return w};this.on=this.emitter().on;this.emit=this.emitter().emit;this.toJSON=function(J){var M,K,I,L={},N;if(J===undefined){J=[];J.push({object:this,JSONrep:L})}else{if(typeof(J)!=="object"){throw new Error("Instance: toJSON should not take a parameter (unless called recursively)")}else{for(K=0;K<J.length;++K){if(J[K].object===this){L=J[K].JSONrep}}}}for(K=0;K<y.length;++K){N=null;M=this[y[K]]();for(I=0;I<J.length;++I){if(J[I].object===M){N=J[I].JSONrep}}if(M!==undefined&&M!==null&&M.toJSON!==undefined&&N===null){N=(i[y[K]] instanceof l)?[]:{};J.push({object:M,JSONrep:N});J[J.length-1].JSONrep=M.toJSON(J)}if(N===null){L[y[K]]=M}else{L[y[K]]=N}}return L};this.toString=(p!==undefined)?p:function(){return"Jermaine Model Instance"};E=function(I){I.on("set",function(L,K){var J=this;if(G[I.name()]===undefined){G[I.name()]=function(O){var N=[],M=true;for(C=0;C<O.length&&M===true;++C){N.push(O[C]);if(O[C].origin===this){M=false}}if(M){N.push({key:I.name(),origin:this});this.emit("change",N)}}}if(D!==undefined){if(K!==undefined&&K!==null&&K.emitter!==undefined){K.emitter().removeListener("change",D);D=undefined}}if(L!==null&&typeof(L)==="object"&&L.on!==undefined&&L.emitter!==undefined){if(K!==undefined&&K!==null&&D!==undefined){K.emitter().removeListener("change",D)}D=function(M){G[I.name()].call(J,M)};L.emitter().on("change",D)}this.emit("change",[{key:I.name(),value:L,origin:this}])})};for(C=0;C<y.length;++C){F=e.attribute(y[C]);if(F instanceof s){E.call(this,F)}}for(C=0;C<y.length+A.length;++C){if(C<y.length){if(j){e.attribute(y[C]).isImmutable()}e.attribute(y[C]).addTo(this)}else{e.method(A[C-y.length]).addTo(this)}}if(arguments.length>0){if(arguments.length<d.length){z="Constructor requires ";for(C=0;C<d.length;++C){z+=d[C];z+=C===d.length-1?"":", "}z+=" to be specified";throw new Error(z)}if(arguments.length>d.length+n.length){throw new Error("Too many arguments to constructor. Expected "+d.length+" required arguments and "+n.length+" optional arguments")}else{for(C=0;C<arguments.length;++C){x=C<d.length?d[C]:n[C-d.length];if(e.attribute(x) instanceof l){if(Object.prototype.toString.call(arguments[C])!=="[object Array]"){throw new Error("Model: Constructor requires 'names' attribute to be set with an Array")}else{for(B=0;B<arguments[C].length;++B){this[x]().add(arguments[C][B])}}}else{this[x](arguments[C])}}}}t.call(this)}};e.hasA=function(w){return m("Attr",w)};e.hasAn=e.hasA;e.hasSome=e.hasA;e.hasMany=function(w){return m("AttrList",w)};e.isA=function(y){var x,w,A,z;f=true;z=function(C){var B,D=new b();for(B in D){if(D.hasOwnProperty(B)&&typeof(C[B])!==typeof(D[B])){return false}}return true};if(typeof(y)!=="function"||!z(y)){throw new Error("Model: parameter sent to isA function must be a Model")}if(r.length===0){r.push(y)}else{throw new Error("Model: Model only supports single inheritance at this time")}w=r[0].attributes();for(x=0;x<w.length;++x){if(i[w[x]]===undefined){i[w[x]]=r[0].attribute(w[x]).clone();i[w[x]].isMutable()}}A=r[0].methods();for(x=0;x<A.length;++x){if(q[A[x]]===undefined){q[A[x]]=r[0].method(A[x])}}for(x=0;x<r.length;x++){e.prototype=new r[x]()}};e.isAn=e.isA;e.parent=function(){return r[0].apply(this,arguments)};e.attribute=function(w){return g("attribute",w)};e.attributes=function(){return v("attributes")};e.method=function(w){return g("method",w)};e.methods=function(){return v("methods")};e.isBuiltWith=function(){var w=false,x;f=true;d=[];n=[];for(x=0;x<arguments.length;++x){if(typeof(arguments[x])==="string"&&arguments[x].charAt(0)!=="%"){if(w){throw new Error("Model: isBuiltWith requires parameters preceded with a % to be the final parameters before the optional function")}else{d.push(arguments[x])}}else{if(typeof(arguments[x])==="string"&&arguments[x].charAt(0)==="%"){w=true;n.push(arguments[x].slice(1))}else{if(typeof(arguments[x])==="function"&&x===arguments.length-1){t=arguments[x]}else{throw new Error("Model: isBuiltWith parameters must be strings except for a function as the optional final parameter")}}}}};e.isImmutable=function(){j=true};e.looksLike=function(w){f=true;p=w};e.respondsTo=function(x,y){var w=new c(x,y);f=true;q[x]=w};e.validate=function(){var y,x=this.attributes(),w=this.methods();for(y=0;y<d.length;++y){try{this.attribute(d[y])}catch(z){throw new Error(d[y]+", specified in the isBuiltWith method, is not an attribute")}}for(y=0;y<n.length;++y){try{this.attribute(n[y])}catch(z){throw new Error(n[y]+", specified in the isBuiltWith method, is not an attribute")}}for(y=0;y<x.length;y++){if(w.indexOf(x[y])>-1){throw new Error("Model: invalid model specification to "+x[y]+" being both an attribute and method")}}if(j){for(y=0;y<x.length;y++){if(d.indexOf(x[y])<0){throw new Error("immutable objects must have all attributes required in a call to isBuiltWith")}}}f=false};return e};a.Model=b});
235 build/jermaine.js
View
@@ -828,13 +828,11 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
});
window.jermaine.util.namespace("window.jermaine", function (ns) {
"use strict";
- function Model(specification) {
- var that = this,
- methods = {},
+
+ var Model = function (specification) {
+ var methods = {},
attributes = {},
pattern,
- getObserver,
- setObserver,
modified = true,
requiredConstructorArgs = [],
optionalConstructorArgs = [],
@@ -845,21 +843,20 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
EventEmitter = ns.util.EventEmitter,
property,
listProperties,
- create,
+ updateConstructor,
isImmutable,
initializer = function () {},
constructor = function () {},
model = function () {
if (modified) {
- create();
+ //validate the model if it has been modified
+ model.validate();
+ updateConstructor();
}
return constructor.apply(this, arguments);
};
- //make instances of models instances of eventemitters
- //model.prototype = new EventEmitter();
-
//temporary fix so API stays the same
if (arguments.length > 1) {
specification = arguments[arguments.length-1];
@@ -930,59 +927,116 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
return list;
};
- /* private function that creates the constructor */
- create = function (name) {
- var that = this,
- i, j,
- err;
-
- //validate the model first
- model.validate();
-
+ /* private function that updates the constructor */
+ updateConstructor = function () {
constructor = function () {
- var that = this,
- i,
+ var i, j,
+ err,
attribute,
- emitter,
+ attributeList = model.attributes(),
+ methodList = model.methods(),
+ emitter = new EventEmitter(),
+ attr,
+ attrChangeListeners = {},
+ setHandler,
+ lastListener,
addProperties;
if (!(this instanceof model)) {
throw new Error("Model: instances must be created using the new operator");
}
- //utility function that adds methods and attributes
- addProperties = function (obj, type) {
- var properties = type==="attributes" ? attributes : methods,
- i;
- for (i in properties) {
- if (properties.hasOwnProperty(i)) {
- //if the object is immutable, all attributes should be immutable
- if(properties === attributes && isImmutable) {
- properties[i].isImmutable();
+
+ ////////////////////////////////////////////////////////////////
+ ////////////// PUBLIC API FOR ALL INSTANCES ////////////////////
+ ////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns the EventEmitter associated with this instance.
+ *
+ */
+ this.emitter = function () {
+ return emitter;
+ };
+
+ /**
+ * Registers a listener for this instance's changes.
+ *
+ */
+ this.on = this.emitter().on;
+
+ /**
+ * Emits an event
+ */
+ this.emit = this.emitter().emit;
+
+ /**
+ * Returns a JSON representation of this instance.
+ *
+ */
+ this.toJSON = function (JSONreps) {
+ var attributeValue,
+ i, j,
+ thisJSONrep = {},
+ attributeJSONrep;
+
+ if (JSONreps === undefined) {
+ // first call
+ JSONreps = [];
+ JSONreps.push({object:this, JSONrep:thisJSONrep});
+ } else if (typeof(JSONreps) !== "object") {
+ // error condition
+ throw new Error("Instance: toJSON should not take a parameter (unless called recursively)");
+ } else {
+ // find the current JSON representation of this object, if it exists
+ for (i = 0; i < JSONreps.length; ++i) {
+ if (JSONreps[i].object === this) {
+ thisJSONrep = JSONreps[i].JSONrep;
}
- properties[i].addTo(obj);
}
}
- };
- emitter = new EventEmitter();
+ for (i = 0; i < attributeList.length; ++i) {
+ attributeJSONrep = null;
+ // get the attribute
+ attributeValue = this[attributeList[i]]();
+
+ // find the current JSON representation for the attribute, if it exists
+ for (j = 0; j < JSONreps.length; ++j) {
+ if (JSONreps[j].object === attributeValue) {
+ attributeJSONrep = JSONreps[j].JSONrep;
+ }
+ }
- this.emitter = function () {
- return emitter;
+ if (attributeValue !== undefined && attributeValue !== null && attributeValue.toJSON !== undefined && attributeJSONrep === null) {
+ // create a new entry for the attribute
+ attributeJSONrep = (attributes[attributeList[i]] instanceof AttrList)?[]:{};
+ JSONreps.push({object:attributeValue, JSONrep:attributeJSONrep});
+ JSONreps[JSONreps.length-1].JSONrep = attributeValue.toJSON(JSONreps);
+ }
+
+ // fill out the JSON representation for this object
+ if(attributeJSONrep === null) {
+ thisJSONrep[attributeList[i]] = attributeValue;
+ } else {
+ thisJSONrep[attributeList[i]] = attributeJSONrep;
+ }
+ }
+ return thisJSONrep;
};
- //expose the the on method
- this.on = function (event, listener) {
- that.emitter().on(event, function (data) {
- listener.call(that, data);
- });
+ /**
+ * Returns a String representation of this instance
+ *
+ */
+ this.toString = (pattern !== undefined)?pattern:function () {
+ return "Jermaine Model Instance";
};
- //this.on = this.emitter().on;
- var attr,
- attrChangeListeners = {},
- setHandler,
- lastListener;
+ ////////////////////////////////////////////////////////////////
+ ////////////// END PUBLIC API FOR ALL INSTANCES ////////////////
+ ////////////////////////////////////////////////////////////////
+
setHandler = function (attr) {
//when set handler is called, this should be the current object
@@ -1002,9 +1056,8 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
}
if (emit) {
- //maybe we should manipulate the data directly? copy it and emit a new data object?
newData.push({key:attr.name(), origin:this});
- this.emitter().emit("change", newData);
+ this.emit("change", newData);
}
};
}
@@ -1016,7 +1069,6 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
}
}
- //get current attribute
if (newValue !== null && typeof(newValue) === "object" && newValue.on !== undefined && newValue.emitter !== undefined) {
if (preValue !== undefined && preValue !== null && lastListener !== undefined) {
preValue.emitter().removeListener("change", lastListener);
@@ -1026,81 +1078,34 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
};
newValue.emitter().on("change", lastListener);
}
- that.emitter().emit("change", [{key:attr.name(), value:newValue, origin:that}]);
+ this.emit("change", [{key:attr.name(), value:newValue, origin:this}]);
});
};
//set up event handling for sub objects
- for (i = 0; i < listProperties("attributes").length; ++i) {
- attr = attributes[listProperties("attributes")[i]];
+ for (i = 0; i < attributeList.length; ++i) {
+ attr = model.attribute(attributeList[i]);
if (attr instanceof Attr) {
setHandler.call(this, attr);
}
}
- this.toJSON = function (JSONreps) {
- var attributeList = model.attributes(),
- attributeValue,
- i, j,
- thisJSONrep = {},
- attributeJSONrep;
-
- if (JSONreps === undefined) {
- /* first call */
- JSONreps = [];
- JSONreps.push({object:this, JSONrep:thisJSONrep});
- } else if (typeof(JSONreps) !== "object") {
- /* error condition */
- throw new Error("Instance: toJSON should not take a parameter (unless called recursively)");
- } else {
- /* find the current JSON representation of this object, if it exists */
- for (i = 0; i < JSONreps.length; ++i) {
- if (JSONreps[i].object === this) {
- thisJSONrep = JSONreps[i].JSONrep;
- }
- }
- }
-
- for (i = 0; i < attributeList.length; ++i) {
- attributeJSONrep = null;
- /* get the attribute */
- attributeValue = this[attributeList[i]]();
-
- /* find the current JSON representation for the attribute, if it exists */
- for (j = 0; j < JSONreps.length; ++j) {
- if (JSONreps[j].object === attributeValue) {
- attributeJSONrep = JSONreps[j].JSONrep;
- }
- }
-
- if (attributeValue !== undefined && attributeValue !== null && attributeValue.toJSON !== undefined && attributeJSONrep === null) {
- /* create a new entry for the attribute */
- attributeJSONrep = (attributes[attributeList[i]] instanceof AttrList)?[]:{};
- JSONreps.push({object:attributeValue, JSONrep:attributeJSONrep});
- JSONreps[JSONreps.length-1].JSONrep = attributeValue.toJSON(JSONreps);
- }
- /* fill out the JSON representation for this object */
- if(attributeJSONrep === null) {
- thisJSONrep[attributeList[i]] = attributeValue;
- } else {
- thisJSONrep[attributeList[i]] = attributeJSONrep;
+ // add all of the attributes and the methods to the object
+ for (i = 0; i < attributeList.length + methodList.length; ++i) {
+ if (i < attributeList.length) {
+ //if the object is immutable, all attributes should be immutable
+ if (isImmutable) {
+ model.attribute(attributeList[i]).isImmutable();
}
+ model.attribute(attributeList[i]).addTo(this);
+ } else {
+ model.method(methodList[i-attributeList.length]).addTo(this);
}
- return thisJSONrep;
-
- };
-
- //add attributes
- addProperties(this, "attributes");
- addProperties(this, "methods");
-
- if (pattern !== undefined) {
- this.toString = pattern;
}
- //use constructor args to build object
+ // build the object using the constructor arguments
if(arguments.length > 0) {
if (arguments.length < requiredConstructorArgs.length) {
//construct and throw error
@@ -1121,7 +1126,6 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
requiredConstructorArgs[i]:
optionalConstructorArgs[i-requiredConstructorArgs.length];
-
if (model.attribute(attribute) instanceof AttrList) {
//make sure that arguments[i] is an array
if (Object.prototype.toString.call(arguments[i]) !== "[object Array]") {
@@ -1139,9 +1143,10 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
}
}
}
+
+ // finally, call the initializer
initializer.call(this);
};
- return constructor;
};
/*********** END PRIVATE METHODS **************/
@@ -1283,8 +1288,8 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
model.validate = function () {
var i,
- attributes = this.attributes(),
- methods = this.methods();
+ attributes = this.attributes(),
+ methods = this.methods();
//check to make sure that isBuiltWith has actual attributes
for (i = 0; i < requiredConstructorArgs.length; ++i) {
@@ -1323,14 +1328,12 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
modified = false;
};
/************** END PUBLIC API ****************/
-
-
//here we are returning our model object
//which is a function with a bunch of methods that
//manipulate how the function behaves
return model;
- }
+ };
ns.Model = Model;
});
8 spec/core/model.js
View
@@ -1104,6 +1104,14 @@ describe("Model", function () {
describe("on method", function () {
+ //this functionality is temporarily deprecated unless it is needed.
+ //if it is, the current function can be replaced with this:
+ /*var that = this;
+ this.on = function (event, listener) {
+ that.emitter().on(event, function (data) {
+ listener.call(that, data);
+ });
+ };*/
it("should reference 'this' as the current object, and not the underlying event emitter", function () {
var p = new Person();
p.on("change", function () {
237 src/core/model.js
View
@@ -1,12 +1,10 @@
window.jermaine.util.namespace("window.jermaine", function (ns) {
"use strict";
- function Model(specification) {
- var that = this,
- methods = {},
+
+ var Model = function (specification) {
+ var methods = {},
attributes = {},
pattern,
- getObserver,
- setObserver,
modified = true,
requiredConstructorArgs = [],
optionalConstructorArgs = [],
@@ -17,21 +15,20 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
EventEmitter = ns.util.EventEmitter,
property,
listProperties,
- create,
+ updateConstructor,
isImmutable,
initializer = function () {},
constructor = function () {},
model = function () {
if (modified) {
- create();
+ //validate the model if it has been modified
+ model.validate();
+ updateConstructor();
}
return constructor.apply(this, arguments);
};
- //make instances of models instances of eventemitters
- //model.prototype = new EventEmitter();
-
//temporary fix so API stays the same
if (arguments.length > 1) {
specification = arguments[arguments.length-1];
@@ -102,59 +99,116 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
return list;
};
- /* private function that creates the constructor */
- create = function (name) {
- var that = this,
- i, j,
- err;
-
- //validate the model first
- model.validate();
-
+ /* private function that updates the constructor */
+ updateConstructor = function () {
constructor = function () {
- var that = this,
- i,
+ var i, j,
+ err,
attribute,
- emitter,
+ attributeList = model.attributes(),
+ methodList = model.methods(),
+ emitter = new EventEmitter(),
+ attr,
+ attrChangeListeners = {},
+ setHandler,
+ lastListener,
addProperties;
if (!(this instanceof model)) {
throw new Error("Model: instances must be created using the new operator");
}
- //utility function that adds methods and attributes
- addProperties = function (obj, type) {
- var properties = type==="attributes" ? attributes : methods,
- i;
- for (i in properties) {
- if (properties.hasOwnProperty(i)) {
- //if the object is immutable, all attributes should be immutable
- if(properties === attributes && isImmutable) {
- properties[i].isImmutable();
+
+ ////////////////////////////////////////////////////////////////
+ ////////////// PUBLIC API FOR ALL INSTANCES ////////////////////
+ ////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns the EventEmitter associated with this instance.
+ *
+ */
+ this.emitter = function () {
+ return emitter;
+ };
+
+ /**
+ * Registers a listener for this instance's changes.
+ *
+ */
+ this.on = this.emitter().on;
+
+ /**
+ * Emits an event
+ */
+ this.emit = this.emitter().emit;
+
+ /**
+ * Returns a JSON representation of this instance.
+ *
+ */
+ this.toJSON = function (JSONreps) {
+ var attributeValue,
+ i, j,
+ thisJSONrep = {},
+ attributeJSONrep;
+
+ if (JSONreps === undefined) {
+ // first call
+ JSONreps = [];
+ JSONreps.push({object:this, JSONrep:thisJSONrep});
+ } else if (typeof(JSONreps) !== "object") {
+ // error condition
+ throw new Error("Instance: toJSON should not take a parameter (unless called recursively)");
+ } else {
+ // find the current JSON representation of this object, if it exists
+ for (i = 0; i < JSONreps.length; ++i) {
+ if (JSONreps[i].object === this) {
+ thisJSONrep = JSONreps[i].JSONrep;
}
- properties[i].addTo(obj);
}
}
- };
- emitter = new EventEmitter();
+ for (i = 0; i < attributeList.length; ++i) {
+ attributeJSONrep = null;
+ // get the attribute
+ attributeValue = this[attributeList[i]]();
+
+ // find the current JSON representation for the attribute, if it exists
+ for (j = 0; j < JSONreps.length; ++j) {
+ if (JSONreps[j].object === attributeValue) {
+ attributeJSONrep = JSONreps[j].JSONrep;
+ }
+ }
- this.emitter = function () {
- return emitter;
+ if (attributeValue !== undefined && attributeValue !== null && attributeValue.toJSON !== undefined && attributeJSONrep === null) {
+ // create a new entry for the attribute
+ attributeJSONrep = (attributes[attributeList[i]] instanceof AttrList)?[]:{};
+ JSONreps.push({object:attributeValue, JSONrep:attributeJSONrep});
+ JSONreps[JSONreps.length-1].JSONrep = attributeValue.toJSON(JSONreps);
+ }
+
+ // fill out the JSON representation for this object
+ if(attributeJSONrep === null) {
+ thisJSONrep[attributeList[i]] = attributeValue;
+ } else {
+ thisJSONrep[attributeList[i]] = attributeJSONrep;
+ }
+ }
+ return thisJSONrep;
};
- //expose the the on method
- this.on = function (event, listener) {
- that.emitter().on(event, function (data) {
- listener.call(that, data);
- });
+ /**
+ * Returns a String representation of this instance
+ *
+ */
+ this.toString = (pattern !== undefined)?pattern:function () {
+ return "Jermaine Model Instance";
};
- //this.on = this.emitter().on;
- var attr,
- attrChangeListeners = {},
- setHandler,
- lastListener;
+ ////////////////////////////////////////////////////////////////
+ ////////////// END PUBLIC API FOR ALL INSTANCES ////////////////
+ ////////////////////////////////////////////////////////////////
+
setHandler = function (attr) {
//when set handler is called, this should be the current object
@@ -174,9 +228,8 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
}
if (emit) {
- //maybe we should manipulate the data directly? copy it and emit a new data object?
newData.push({key:attr.name(), origin:this});
- this.emitter().emit("change", newData);
+ this.emit("change", newData);
}
};
}
@@ -188,7 +241,6 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
}
}
- //get current attribute
if (newValue !== null && typeof(newValue) === "object" && newValue.on !== undefined && newValue.emitter !== undefined) {
if (preValue !== undefined && preValue !== null && lastListener !== undefined) {
preValue.emitter().removeListener("change", lastListener);
@@ -198,81 +250,34 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
};
newValue.emitter().on("change", lastListener);
}
- that.emitter().emit("change", [{key:attr.name(), value:newValue, origin:that}]);
+ this.emit("change", [{key:attr.name(), value:newValue, origin:this}]);
});
};
//set up event handling for sub objects
- for (i = 0; i < listProperties("attributes").length; ++i) {
- attr = attributes[listProperties("attributes")[i]];
+ for (i = 0; i < attributeList.length; ++i) {
+ attr = model.attribute(attributeList[i]);
if (attr instanceof Attr) {
setHandler.call(this, attr);
}
}
- this.toJSON = function (JSONreps) {
- var attributeList = model.attributes(),
- attributeValue,
- i, j,
- thisJSONrep = {},
- attributeJSONrep;
-
- if (JSONreps === undefined) {
- /* first call */
- JSONreps = [];
- JSONreps.push({object:this, JSONrep:thisJSONrep});
- } else if (typeof(JSONreps) !== "object") {
- /* error condition */
- throw new Error("Instance: toJSON should not take a parameter (unless called recursively)");
- } else {
- /* find the current JSON representation of this object, if it exists */
- for (i = 0; i < JSONreps.length; ++i) {
- if (JSONreps[i].object === this) {
- thisJSONrep = JSONreps[i].JSONrep;
- }
- }
- }
-
- for (i = 0; i < attributeList.length; ++i) {
- attributeJSONrep = null;
- /* get the attribute */
- attributeValue = this[attributeList[i]]();
-
- /* find the current JSON representation for the attribute, if it exists */
- for (j = 0; j < JSONreps.length; ++j) {
- if (JSONreps[j].object === attributeValue) {
- attributeJSONrep = JSONreps[j].JSONrep;
- }
- }
-
- if (attributeValue !== undefined && attributeValue !== null && attributeValue.toJSON !== undefined && attributeJSONrep === null) {
- /* create a new entry for the attribute */
- attributeJSONrep = (attributes[attributeList[i]] instanceof AttrList)?[]:{};
- JSONreps.push({object:attributeValue, JSONrep:attributeJSONrep});
- JSONreps[JSONreps.length-1].JSONrep = attributeValue.toJSON(JSONreps);
- }
- /* fill out the JSON representation for this object */
- if(attributeJSONrep === null) {
- thisJSONrep[attributeList[i]] = attributeValue;
- } else {
- thisJSONrep[attributeList[i]] = attributeJSONrep;
+ // add all of the attributes and the methods to the object
+ for (i = 0; i < attributeList.length + methodList.length; ++i) {
+ if (i < attributeList.length) {
+ //if the object is immutable, all attributes should be immutable
+ if (isImmutable) {
+ model.attribute(attributeList[i]).isImmutable();
}
+ model.attribute(attributeList[i]).addTo(this);
+ } else {
+ model.method(methodList[i-attributeList.length]).addTo(this);
}
- return thisJSONrep;
-
- };
-
- //add attributes
- addProperties(this, "attributes");
- addProperties(this, "methods");
-
- if (pattern !== undefined) {
- this.toString = pattern;
}
- //use constructor args to build object
+ // build the object using the constructor arguments
if(arguments.length > 0) {
if (arguments.length < requiredConstructorArgs.length) {
//construct and throw error
@@ -293,7 +298,6 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
requiredConstructorArgs[i]:
optionalConstructorArgs[i-requiredConstructorArgs.length];
-
if (model.attribute(attribute) instanceof AttrList) {
//make sure that arguments[i] is an array
if (Object.prototype.toString.call(arguments[i]) !== "[object Array]") {
@@ -311,9 +315,10 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
}
}
}
+
+ // finally, call the initializer
initializer.call(this);
};
- return constructor;
};
/*********** END PRIVATE METHODS **************/
@@ -455,8 +460,8 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
model.validate = function () {
var i,
- attributes = this.attributes(),
- methods = this.methods();
+ attributes = this.attributes(),
+ methods = this.methods();
//check to make sure that isBuiltWith has actual attributes
for (i = 0; i < requiredConstructorArgs.length; ++i) {
@@ -495,14 +500,12 @@ window.jermaine.util.namespace("window.jermaine", function (ns) {
modified = false;
};
/************** END PUBLIC API ****************/
-
-
//here we are returning our model object
//which is a function with a bunch of methods that
//manipulate how the function behaves
return model;
- }
+ };
ns.Model = Model;
-});
+});

No commit comments for this range

Something went wrong with that request. Please try again.