loco-js-model
Advanced tools
Comparing version 0.3.3 to 1.0.0
@@ -1,1 +0,1 @@ | ||
!function(t,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.LocoModel=r():t.LocoModel=r()}("undefined"!=typeof self?self:this,function(){return function(t){function r(n){if(e[n])return e[n].exports;var s=e[n]={i:n,l:!1,exports:{}};return t[n].call(s.exports,s,s.exports,r),s.l=!0,s.exports}var e={};return r.m=t,r.c=e,r.d=function(t,e,n){r.o(t,e)||Object.defineProperty(t,e,{configurable:!1,enumerable:!0,get:n})},r.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return r.d(e,"a",e),e},r.o=function(t,r){return Object.prototype.hasOwnProperty.call(t,r)},r.p="",r(r.s=7)}([function(t,r,e){"use strict";var n,s=e(3);n=function(){function t(){this.obj=null,this.attr=null,this.val=null,this.opts=null}return t.sharedInstances={},t.instance=function(t,r,e){var n,o;return o=this.identity,null==this.sharedInstances[o]&&(this.sharedInstances[o]=new s.a[o]),n=this.sharedInstances[o],n.assignAttribs(t,r,e),n},t.prototype.assignAttribs=function(t,r,e){return this.obj=t,this.attr=r,this.val=this.obj[this.attr],this.opts=e},t}(),r.a=n},function(t,r,e){"use strict";function n(t,r){if(!(t instanceof r))throw new TypeError("Cannot call a class as a function")}var s=function(){function t(t,r){for(var e=0;e<r.length;e++){var n=r[e];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(t,n.key,n)}}return function(r,e,n){return e&&t(r.prototype,e),n&&t(r,n),r}}(),o=function(){function t(){n(this,t),this.localeVar="en",this.protocolWithHostVar=null,this.scopeVar=null}return s(t,[{key:"locale",get:function(){return this.localeVar},set:function(t){return this.localeVar=t,this.localeVar}},{key:"protocolWithHost",get:function(){return this.protocolWithHostVar},set:function(t){return t?"/"===t[t.length-1]?(this.protocolWithHostVar=t.slice(0,t.length-1),this.protocolWithHostVar):(this.protocolWithHostVar=t,this.protocolWithHostVar):(this.protocolWithHostVar=null,this.protocolWithHostVar)}},{key:"scope",get:function(){return this.scopeVar},set:function(t){return this.scopeVar=t,this.scopeVar}}]),t}(),i=new o;r.a=i},function(t,r,e){"use strict";var n=e(10),s={en:n.a};r.a=s},function(t,r,e){"use strict";var n=e(9),s=e(11),o=e(12),i=e(13),a=e(14),u=e(4),l=e(15),c=e(16),h=e(17),p={Absence:n.a,Confirmation:s.a,Exclusion:o.a,Format:i.a,Inclusion:a.a,Length:u.a,Numericality:l.a,Presence:c.a,Size:h.a};r.a=p},function(t,r,e){"use strict";var n,s=e(0),o=e(2),i=e(1),a=function(t,r){function e(){this.constructor=t}for(var n in r)u.call(r,n)&&(t[n]=r[n]);return e.prototype=r.prototype,t.prototype=new e,t.__super__=r.prototype,t},u={}.hasOwnProperty;n=function(t){function r(){r.__super__.constructor.call(this)}return a(r,t),r.identity="Length",r.prototype.validate=function(){var t;if(null!=this.val&&null!==(t=null!=this._range()[0]&&null!=this._range()[1]&&this._range()[0]===this._range()[1]&&this.val.length!==this._range()[0]?this._selectErrorMessage("wrong_length",this._range()[0]):null!=this._range()[0]&&this.val.length<this._range()[0]?this._selectErrorMessage("too_short",this._range()[0]):null!=this._range()[1]&&this.val.length>this._range()[1]?this._selectErrorMessage("too_long",this._range()[1]):null))return this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._range=function(){var t,r;return t=this.opts.minimum||this.opts.is||null!=this.opts.within&&this.opts.within[0]||null,r=this.opts.maximum||this.opts.is||null!=this.opts.within&&this.opts.within[1]||null,[t,r]},r.prototype._selectErrorMessage=function(t,r){var e,n,s,a,u;if(1===r)return o.a[i.a.locale].errors.messages[t].one;for(s=null,a=["few","many"],e=0,n=a.length;e<n;e++)if(u=a[e],this._checkVariant(u,r)){s=o.a[i.a.locale].errors.messages[t][u];break}return null==s&&(s=o.a[i.a.locale].errors.messages[t].other),null!=this.opts.message&&(s=this.opts.message),/%{count}/.exec(s)&&(s=s.replace("%{count}",r)),s},r.prototype._checkVariant=function(t,r){if(null!=o.a[i.a.locale].variants[t])return o.a[i.a.locale].variants[t](r)},r}(s.a),r.a=n},function(t,r,e){"use strict";var n;n=function(){function t(){}return t.imap={},t.clear=function(){return this.imap={}},t.add=function(t){var r;return r=t.getIdentity(),null==this.imap[r]&&(this.imap[r]={}),null==this.imap[r][t.id]&&(this.imap[r][t.id]=[]),this.imap[r][t.id][0]=t},t.connect=function(t,r){var e;return null==r&&(r={}),e=r.with,this.add(e),this.imap[e.getIdentity()][e.id].push(t)},t.addCollection=function(t,r){if(null==r&&(r={}),null==this.imap[t]&&(this.imap[t]={}),null==this.imap[t].collection&&(this.imap[t].collection=[]),-1===this.imap[t].collection.indexOf(r.to))return this.imap[t].collection.push(r.to)},t.all=function(t){var r,e,n,s;if(null==this.imap[t])return null;r=[],s=this.imap[t];for(e in s)n=s[e],"collection"!==e&&r.push(n[0]);return r},t.find=function(t,r){return this.imap[t]&&this.imap[t][r]?this.imap[t][r][0]:null},t.findConnected=function(t,r){var e;return this.imap[t]&&this.imap[t][r]&&this.imap[t][r].length>1?(e=this.imap[t][r],e.slice(1,+(e.length-1)+1||9e9)):[]},t}(),r.a=n},function(t,r,e){"use strict";var n={};r.a=n},function(t,r,e){"use strict";Object.defineProperty(r,"__esModule",{value:!0});var n=e(8),s=e(5),o=e(0),i=e(3),a=e(1),u=e(2),l=e(6);e.d(r,"Base",function(){return n.a}),e.d(r,"Config",function(){return a.a}),e.d(r,"I18n",function(){return u.a}),e.d(r,"IdentityMap",function(){return s.a}),e.d(r,"Models",function(){return l.a}),e.d(r,"Validators",function(){return i.a}),i.a.Base=o.a,l.a.Base=n.a},function(t,r,e){"use strict";var n,s=e(3),o=e(1),i=e(5),a=e(6),u=e(18);n=function(){function t(t){null==t&&(t={}),this.id=null,this.errors=null,this.resource=t.resource,null!=this.constructor.attributes&&this.__initAttributes(),null!=t&&this.__assignAttributes(t)}return t.getIdentity=function(){if(null!=this.identity)return this.identity;throw"Specify Model's @identity!"},t.getRemoteName=function(){return null!=this.remoteName?this.remoteName:this.getIdentity()},t.all=function(t){return null==t&&(t={}),this.get("all",t)},t.get=function(t,r){return null==r&&(r={}),this.__send("GET",t,r)},t.post=function(t,r){return null==r&&(r={}),this.__send("POST",t,r)},t.put=function(t,r){return null==r&&(r={}),this.__send("PUT",t,r)},t.patch=function(t,r){return null==r&&(r={}),this.__send("PATCH",t,r)},t.delete=function(t,r){return null==r&&(r={}),this.__send("DELETE",t,r)},t.find=function(t){var r,e,n;return n={},"object"==typeof t?(n=t,r=t.id,delete n.id):r=t,e=Object(u.a)("GET",this.__getResourcesUrl(n)+"/"+r,n),new Promise(function(r){return function(n,s){return e.onerror=function(t){return s(t)},e.onload=function(e){var s;return s=JSON.parse(e.target.response),n(r.__initFromJSON(s,t.resource))}}}(this))},t.getAttribRemoteName=function(t){return null==this.attributes?null:null==this.attributes[t]?null:null==this.attributes[t].remoteName?t:this.attributes[t].remoteName},t.getResourcesUrlParams=function(t){var r,e,n,s;for(s=this.__getResourcesUrl({resource:t.resource}),n=/:(\w+)\/?/,e=[];r=n.exec(s);)e.push(r[1]),s=s.replace(r[0],r[1]);return e},t.__getResourcesUrl=function(t){var r,e;return e=null==this.resources?"/"+this.getRemoteName().toLowerCase()+"s":t.resource?this.resources[t.resource].url:null!=o.a.scope&&null!=this.resources[o.a.scope]?this.resources[o.a.scope].url:this.resources.url,null!=o.a.protocolWithHost&&(e=""+o.a.protocolWithHost+e),null==(r=/:(\w+)\/?/.exec(e))?e:(null!=t[r[1]]?(e=e.replace(":"+r[1],t[r[1]]),delete t[r[1]]):null!=t.obj&&null!=t.obj[r[1]]&&(e=e.replace(":"+r[1],t.obj[r[1]])),e)},t.__initSubclass=function(t){var r,e;return null==t&&(t={}),e=this.getIdentity().split("."),1===e.length?(r=a.a[e[0]],null==r?new this(t):new r(t)):new(r=a.a[e[0]][e[1]])(t)},t.__page=function(t,r,e){var n,s;return s=r.url,r.params[r.pageParam]=t,n=Object(u.a)(r.method,s,r.params),new Promise(function(t){return function(s,o){return n.onerror=function(t){return o(t)},n.onload=function(n){var o,i,a,u,l,c,h,p,f,_;if(o=JSON.parse(n.target.response),o.constructor===Array)for(i=0,l=o.length;i<l;i++)p=o[i],h=t.__initFromJSON(p,r.resource),e.push(h);else if(null!=o.resources){for(e.constructor===Array&&(e={resources:[],count:0}),f=o.resources,a=0,c=f.length;a<c;a++)p=f[a],h=t.__initFromJSON(p,r.resource),e.resources.push(h);e.count=o.count}else for(u in o)_=o[u],e[u]=_;return s(e)}}}(this))},t.__paginate=function(t){var r;return r={method:t.method,url:t.url,params:t.params,pageParam:t.pageParam,resource:t.resource},this.__page(t.pageNum||1,r,[]).then(function(e){return function(n){var s,o,i,a,u,l;if(l=n.count||t.total,a=Promise.resolve(n),null!=t.pageNum)return a;if(l<=t.perPage)return a;if(i=parseInt(l/t.perPage),i!==l/t.perPage&&(i+=1),1===i)return a;for(s=o=2,u=i;2<=u?o<=u:o>=u;s=2<=u?++o:--o)(function(t){return a=a.then(function(s){return e.__page(t,r,n)})})(s);return a}}(this))},t.__getPaginationParam=function(t){var r,e,n,s,i,a;return r="page",null!=t&&null!=this.resources&&this.resources[t]?(null!=(e=this.resources[t].paginate)?e.param:void 0)||r:null!=o.a.scope&&null!=this.resources&&null!=this.resources[o.a.scope]?(null!=(n=this.resources[o.a.scope])&&null!=(s=n.paginate)?s.param:void 0)||r:null!=(null!=(i=this.resources)&&null!=(a=i.paginate)?a.param:void 0)?this.resources.paginate.param:r},t.__getPaginationPer=function(t){var r,e,n,s,i;return null!=t&&null!=this.resources&&this.resources[t]?null!=(r=this.resources[t].paginate)?r.per:void 0:null!=o.a.scope&&null!=this.resources&&null!=this.resources[o.a.scope]?null!=(e=this.resources[o.a.scope])&&null!=(n=e.paginate)?n.per:void 0:null!=(null!=(s=this.resources)&&null!=(i=s.paginate)?i.per:void 0)?this.resources.paginate.per:null},t.__send=function(t,r,e){var n,s;return s=this.__getResourcesUrl(e),"all"!==r&&(s=s+"/"+r),n={method:t,url:s,params:e,resource:e.resource,perPage:this.__getPaginationPer(e.resource),pageNum:e.page,pageParam:this.__getPaginationParam(e.resource),total:e.total||e.count},this.__paginate(n)},t.__initFromJSON=function(t,r){var e;return e=this.__initSubclass(t),e.resource=r,i.a.add(e),e},t.prototype.setResource=function(t){return this.resource=t},t.prototype.getIdentity=function(){return this.constructor.getIdentity()},t.prototype.getAttrRemoteName=function(t){return null==this.constructor.attributes?null:null==this.constructor.attributes[t]?null:this.constructor.attributes[t].remoteName||t},t.prototype.getAttrName=function(t){var r,e,n;if(null==this.constructor.attributes)return t;if(null!=this.constructor.attributes[t])return t;n=this.constructor.attributes;for(e in n)if(r=n[e],r.remoteName===t)return e;return t},t.prototype.getAttrType=function(t){return null==this.constructor.attributes?null:null==this.constructor.attributes[t]?null:this.constructor.attributes[t].type},t.prototype.assignAttr=function(t,r){var e;if(e=this.getAttrType(t),null==r)return void(this[t]=null);switch(e){case"Date":r=new Date(Date.parse(r));break;case"Integer":case"Int":r=parseInt(r);break;case"Float":r=parseFloat(r);break;case"Boolean":case"Bool":r="boolean"==typeof r?r:Boolean(parseInt(r));break;case"Number":r=Number(r);break;case"String":r=String(r)}return this[t]=r},t.prototype.attributes=function(){var t,r,e;if(t={id:this.id},null==this.constructor.attributes)return t;e=this.constructor.attributes;for(r in e)e[r],t[r]=this[r];return t},t.prototype.isValid=function(){var t,r,e,n,o,i,a,u,l,c,h,p;if(null==this.constructor.attributes)return!0;this.errors=null,a=this.constructor.attributes;for(o in a)if(t=a[o],null!=t.validations){u=t.validations;for(c in u)h=u[c],null!=this.id&&"create"===h.on||null==this.id&&"update"===h.on||(null==h.if||h.if(this))&&(p=c.charAt(0).toUpperCase()+c.slice(1),null!=s.a[p]?(i=this.__processedValidationSettings(h),s.a[p].instance(this,o,i).validate()):console.warn('"'+p+'" validator is not implemented!'))}if(null!=this.constructor.validate)for(l=this.constructor.validate,r=0,e=l.length;r<e;r++)n=l[r],this[n]();return null==this.errors},t.prototype.isInvalid=function(){return!this.isValid()},t.prototype.isEmpty=function(){var t,r;r=this.attributes();for(t in r)if(r[t],null!==this[t])return!1;return!0},t.prototype.addErrorMessage=function(t,r){return null==r&&(r={}),null==this.errors&&(this.errors={}),null==this.errors[r.for]&&(this.errors[r.for]=[]),this.errors[r.for].push(t)},t.prototype.save=function(){var t,r;return t=null!=this.id?"PUT":"POST",r=Object(u.a)(t,this.__getResourceUrl(),this.serialize()),new Promise(function(t){return function(e,n){return r.onerror=function(t){return n(t)},r.onload=function(r){var n;return n=JSON.parse(r.target.response),n.success?void e(n):(null!=n.errors&&t.__assignRemoteErrorMessages(n.errors),e(n))}}}(this))},t.prototype.updateAttribute=function(t){var r;return r=Object(u.a)("PUT",this.__getResourceUrl(),this.serialize(t)),new Promise(function(t){return function(e,n){return r.onerror=function(t){return n(t)},r.onload=function(r){var s;return r.target.status>=200&&r.target.status<400?(s=JSON.parse(r.target.response),s.success?void e(s):(null!=s.errors&&t.__assignRemoteErrorMessages(s.errors),e(s))):r.target.status>=500?n(r):void 0}}}(this))},t.prototype.serialize=function(t){var r,e,n,s;if(null==t&&(t=null),null==this.constructor.attributes)return{};e={},n=this.constructor.getRemoteName().toLowerCase(),e[n]={},r={},null!=t?r[t]=null:r=this.constructor.attributes;for(t in r)r[t],s=this.getAttrRemoteName(t),e[n][s]=this[t];return e},t.prototype.reload=function(){var t,r,e,n,s;for(t={id:this.id,resource:this.resource},s=this.constructor.getResourcesUrlParams({resource:this.resource}),r=0,e=s.length;r<e;r++)n=s[r],t[n]=this[n];return this.constructor.find(t)},t.prototype.changes=function(){var t,r,e,n,s;n={},t=i.a.find(this.getIdentity(),this.id),e=this.attributes();for(r in e)if((s=e[r])!==t[r]){if(null!=s&&s.constructor===Date&&t[r]-s==0)continue;s!==t[r]&&(n[r]={is:t[r],was:s})}return n},t.prototype.applyChanges=function(){var t,r,e,n;r=this.changes(),e=[];for(t in r)n=r[t],e.push(this[t]=n.is);return e},t.prototype.toKey=function(){return this.getIdentity().toLowerCase()+"_"+this.id},t.prototype.get=function(t,r){return null==r&&(r={}),this.__send("GET",t,r)},t.prototype.post=function(t,r){return null==r&&(r={}),this.__send("POST",t,r)},t.prototype.put=function(t,r){return null==r&&(r={}),this.__send("PUT",t,r)},t.prototype.patch=function(t,r){return null==r&&(r={}),this.__send("PATCH",t,r)},t.prototype.delete=function(t,r){return null==r&&(r={}),this.__send("DELETE",t,r)},t.prototype.__send=function(t,r,e){var n,s;return s=this.__getResourceUrl(),null!=r&&(s=s+"/"+r),n=Object(u.a)(t,s,e),new Promise(function(t,r){return n.onerror=function(t){return r(t)},n.onload=function(n){return n.target.status>=200&&n.target.status<400?(e=JSON.parse(n.target.response),t(e)):n.target.status>=500?r(n):void 0}})},t.prototype.__assignAttributes=function(t){var r,e,n,s;n=[];for(e in t)s=t[e],r=this.getAttrName(e),n.push(this.assignAttr(r,s));return n},t.prototype.__initAttributes=function(){var t,r,e;r=this.constructor.attributes,e=[];for(t in r)r[t],e.push(this[t]=null);return e},t.prototype.__assignRemoteErrorMessages=function(t){var r,e,n,s,o;o=[];for(s in t)n=t[s],r=this.getAttrName(s),o.push(function(){var t,s,o;for(o=[],t=0,s=n.length;t<s;t++)e=n[t],o.push(this.addErrorMessage(e,{for:r}));return o}.call(this));return o},t.prototype.__getResourceUrl=function(){var t;return t=this.constructor.__getResourcesUrl({resource:this.resource,obj:this}),null==this.id?t:t+"/"+this.id},t.prototype.__processedValidationSettings=function(t){var r,e,n;n={};for(r in t)e=t[r],n[r]="function"==typeof e?e(this):e;return n},t}(),r.a=n},function(t,r,e){"use strict";var n,s=e(0),o=e(2),i=e(1),a=function(t,r){function e(){this.constructor=t}for(var n in r)u.call(r,n)&&(t[n]=r[n]);return e.prototype=r.prototype,t.prototype=new e,t.__super__=r.prototype,t},u={}.hasOwnProperty;n=function(t){function r(){r.__super__.constructor.call(this)}return a(r,t),r.identity="Absence",r.prototype.validate=function(){switch(typeof this.val){case"string":if(null!=this.val&&0===this.val.length)return;break;default:if(null==this.val)return}return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:o.a[i.a.locale].errors.messages.present,this.obj.addErrorMessage(t,{for:this.attr})},r}(s.a),r.a=n},function(t,r,e){"use strict";var n;n={variants:{},models:{},attributes:{},errors:{messages:{accepted:"must be accepted",blank:"can't be blank",confirmation:"doesn't match %{attribute}",empty:"can't be empty",equal_to:"must be equal to %{count}",even:"must be even",exclusion:"is reserved",greater_than:"must be greater than %{count}",greater_than_or_equal_to:"must be greater than or equal to %{count}",inclusion:"is not included in the list",invalid:"is invalid",less_than:"must be less than %{count}",less_than_or_equal_to:"must be less than or equal to %{count}",not_a_number:"is not a number",not_an_integer:"must be an integer",odd:"must be odd",present:"must be blank",too_long:{one:"is too long (maximum is 1 character)",other:"is too long (maximum is %{count} characters)"},too_short:{one:"is too short (minimum is 1 character)",other:"is too short (minimum is %{count} characters)"},wrong_length:{one:"is the wrong length (should be 1 character)",other:"is the wrong length (should be %{count} characters)"},other_than:"must be other than %{count}"}}},r.a=n},function(t,r,e){"use strict";var n,s=e(0),o=e(2),i=e(1),a=function(t,r){function e(){this.constructor=t}for(var n in r)u.call(r,n)&&(t[n]=r[n]);return e.prototype=r.prototype,t.prototype=new e,t.__super__=r.prototype,t},u={}.hasOwnProperty;n=function(t){function r(){r.__super__.constructor.call(this)}return a(r,t),r.identity="Confirmation",r.prototype.validate=function(){var t;if(t=this.obj[this._properAttr()],null==this.val||null==t||this.val!==t)return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t,r,e,n;return e=this.attr.charAt(0).toUpperCase()+this.attr.slice(1),r=o.a[i.a.locale].attributes[this.obj.getIdentity()],t=r&&r[this.attr]||e,n=null!=this.opts.message?this.opts.message:o.a[i.a.locale].errors.messages.confirmation,n=n.replace("%{attribute}",t),this.obj.addErrorMessage(n,{for:this._properAttr()})},r.prototype._properAttr=function(){return this.attr+"Confirmation"},r}(s.a),r.a=n},function(t,r,e){"use strict";var n,s=e(0),o=e(2),i=e(1),a=function(t,r){function e(){this.constructor=t}for(var n in r)u.call(r,n)&&(t[n]=r[n]);return e.prototype=r.prototype,t.prototype=new e,t.__super__=r.prototype,t},u={}.hasOwnProperty;n=function(t){function r(){r.__super__.constructor.call(this)}return a(r,t),r.identity="Exclusion",r.prototype.validate=function(){var t;if(t=this.opts.in||this.opts.within||[],-1!==t.indexOf(this.val))return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:o.a[i.a.locale].errors.messages.exclusion,this.obj.addErrorMessage(t,{for:this.attr})},r}(s.a),r.a=n},function(t,r,e){"use strict";var n,s=e(0),o=e(2),i=e(1),a=function(t,r){function e(){this.constructor=t}for(var n in r)u.call(r,n)&&(t[n]=r[n]);return e.prototype=r.prototype,t.prototype=new e,t.__super__=r.prototype,t},u={}.hasOwnProperty;n=function(t){function r(){r.__super__.constructor.call(this)}return a(r,t),r.identity="Format",r.prototype.validate=function(){if(null==this.opts.with.exec(this.val))return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:o.a[i.a.locale].errors.messages.invalid,this.obj.addErrorMessage(t,{for:this.attr})},r}(s.a),r.a=n},function(t,r,e){"use strict";var n,s=e(0),o=e(2),i=e(1),a=function(t,r){function e(){this.constructor=t}for(var n in r)u.call(r,n)&&(t[n]=r[n]);return e.prototype=r.prototype,t.prototype=new e,t.__super__=r.prototype,t},u={}.hasOwnProperty;n=function(t){function r(){r.__super__.constructor.call(this)}return a(r,t),r.identity="Inclusion",r.prototype.validate=function(){var t;if(t=this.opts.in||this.opts.within||[],-1===t.indexOf(this.val))return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:o.a[i.a.locale].errors.messages.inclusion,this.obj.addErrorMessage(t,{for:this.attr})},r}(s.a),r.a=n},function(t,r,e){"use strict";var n,s=e(0),o=e(2),i=e(1),a=function(t,r){function e(){this.constructor=t}for(var n in r)u.call(r,n)&&(t[n]=r[n]);return e.prototype=r.prototype,t.prototype=new e,t.__super__=r.prototype,t},u={}.hasOwnProperty;n=function(t){function r(){r.__super__.constructor.call(this)}return a(r,t),r.identity="Numericality",r.prototype.validate=function(){return isNaN(this.val)?this._addNaNErrorMessage():null!=this.opts.only_integer&&Number(this.val)!==parseInt(this.val)?this._addIntErrorMessage():null!=this.opts.greater_than&&Number(this.val)<=this.opts.greater_than?this._addGreatherThanErrorMessage():null!=this.opts.greater_than_or_equal_to&&Number(this.val)<this.opts.greater_than_or_equal_to?this._addGreatherThanOrEqualToErrorMessage():null!=this.opts.equal_to&&Number(this.val)!==this.opts.equal_to?this._addEqualToErrorMessage():null!=this.opts.less_than&&Number(this.val)>=this.opts.less_than?this._addLessThanErrorMessage():null!=this.opts.less_than_or_equal_to&&Number(this.val)>this.opts.less_than_or_equal_to?this._addLessThanOrEqualToErrorMessage():null!=this.opts.other_than&&Number(this.val)===this.opts.other_than?this._addOtherThanErrorMessage():null!=this.opts.odd&&Number(this.val)%2!=1?this._addOddErrorMessage():null!=this.opts.even&&Number(this.val)%2!=0?this._addEvenErrorMessage():void 0},r.prototype._addNaNErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:o.a[i.a.locale].errors.messages.not_a_number,this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addIntErrorMessage=function(){var t;return t=o.a[i.a.locale].errors.messages.not_an_integer,this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addGreatherThanErrorMessage=function(){var t;return t=o.a[i.a.locale].errors.messages.greater_than,t=t.replace("%{count}",this.opts.greater_than),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addGreatherThanOrEqualToErrorMessage=function(){var t;return t=o.a[i.a.locale].errors.messages.greater_than_or_equal_to,t=t.replace("%{count}",this.opts.greater_than_or_equal_to),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addEqualToErrorMessage=function(){var t;return t=o.a[i.a.locale].errors.messages.equal_to,t=t.replace("%{count}",this.opts.equal_to),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addLessThanErrorMessage=function(){var t;return t=o.a[i.a.locale].errors.messages.less_than,t=t.replace("%{count}",this.opts.less_than),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addLessThanOrEqualToErrorMessage=function(){var t;return t=o.a[i.a.locale].errors.messages.less_than_or_equal_to,t=t.replace("%{count}",this.opts.less_than_or_equal_to),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addOtherThanErrorMessage=function(){var t;return t=o.a[i.a.locale].errors.messages.other_than,t=t.replace("%{count}",this.opts.other_than),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addOddErrorMessage=function(){var t;return t=o.a[i.a.locale].errors.messages.odd,this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addEvenErrorMessage=function(){var t;return t=o.a[i.a.locale].errors.messages.even,this.obj.addErrorMessage(t,{for:this.attr})},r}(s.a),r.a=n},function(t,r,e){"use strict";var n,s=e(0),o=e(2),i=e(1),a=function(t,r){function e(){this.constructor=t}for(var n in r)u.call(r,n)&&(t[n]=r[n]);return e.prototype=r.prototype,t.prototype=new e,t.__super__=r.prototype,t},u={}.hasOwnProperty;n=function(t){function r(){r.__super__.constructor.call(this)}return a(r,t),r.identity="Presence",r.prototype.validate=function(){switch(typeof this.val){case"string":if(null!=this.val&&this.val.length>0)return;break;default:if(null!=this.val)return}return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:o.a[i.a.locale].errors.messages.blank,this.obj.addErrorMessage(t,{for:this.attr})},r}(s.a),r.a=n},function(t,r,e){"use strict";var n,s=e(0),o=e(4),i=function(t,r){function e(){this.constructor=t}for(var n in r)a.call(r,n)&&(t[n]=r[n]);return e.prototype=r.prototype,t.prototype=new e,t.__super__=r.prototype,t},a={}.hasOwnProperty;n=function(t){function r(){r.__super__.constructor.call(this)}return i(r,t),r.identity="Size",r.prototype.validate=function(){return o.a.instance(this.obj,this.attr,this.opts).validate()},r}(s.a),r.a=n},function(t,r,e){"use strict";e.d(r,"a",function(){return o});var n=e(19),s=function(t){var r={};if(!t)return r;var e=["resource","total","count"];return Object.keys(t).forEach(function(n){-1===e.indexOf(n)&&(r[n]=t[n])}),r},o=function(t,r,e){var o=s(e),i="GET"===t?r+"?"+Object(n.a)(o):r,a=document.querySelector("meta[name='csrf-token']"),u=new XMLHttpRequest;return u.open(t,i),u.setRequestHeader("Accept","application/json"),u.setRequestHeader("Content-Type","application/json"),a&&u.setRequestHeader("X-CSRF-Token",a.content),u.send(JSON.stringify(o)),u}},function(t,r,e){"use strict";e.d(r,"a",function(){return n});var n=function(t){var r="";return Object.keys(t).forEach(function(e){""!==r&&(r+="&"),r=""+r+e+"="+encodeURIComponent(t[e])}),r}}])}); | ||
!function(t,r){"object"==typeof exports&&"object"==typeof module?module.exports=r():"function"==typeof define&&define.amd?define([],r):"object"==typeof exports?exports.LocoModel=r():t.LocoModel=r()}(window,(function(){return function(t){var r={};function e(o){if(r[o])return r[o].exports;var n=r[o]={i:o,l:!1,exports:{}};return t[o].call(n.exports,n,n.exports,e),n.l=!0,n.exports}return e.m=t,e.c=r,e.d=function(t,r,o){e.o(t,r)||Object.defineProperty(t,r,{enumerable:!0,get:o})},e.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,r){if(1&r&&(t=e(t)),8&r)return t;if(4&r&&"object"==typeof t&&t&&t.__esModule)return t;var o=Object.create(null);if(e.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:t}),2&r&&"string"!=typeof t)for(var n in t)e.d(o,n,function(r){return t[r]}.bind(null,n));return o},e.n=function(t){var r=t&&t.__esModule?function(){return t.default}:function(){return t};return e.d(r,"a",r),r},e.o=function(t,r){return Object.prototype.hasOwnProperty.call(t,r)},e.p="",e(e.s=0)}([function(t,r,e){"use strict";function o(t,r){for(var e=0;e<r.length;e++){var o=r[e];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}e.r(r),e.d(r,"Config",(function(){return n})),e.d(r,"I18n",(function(){return s})),e.d(r,"IdentityMap",(function(){return p})),e.d(r,"Models",(function(){return U})),e.d(r,"Validators",(function(){return q}));var n=new(function(){function t(){!function(t,r){if(!(t instanceof r))throw new TypeError("Cannot call a class as a function")}(this,t),this.localeVar="en",this.protocolWithHostVar=null,this.scopeVar=null}var r,e,n;return r=t,(e=[{key:"locale",get:function(){return this.localeVar},set:function(t){return this.localeVar=t,this.localeVar}},{key:"protocolWithHost",get:function(){return this.protocolWithHostVar},set:function(t){return t?"/"===t[t.length-1]?(this.protocolWithHostVar=t.slice(0,t.length-1),this.protocolWithHostVar):(this.protocolWithHostVar=t,this.protocolWithHostVar):(this.protocolWithHostVar=null,this.protocolWithHostVar)}},{key:"scope",get:function(){return this.scopeVar},set:function(t){return this.scopeVar=t,this.scopeVar}}])&&o(r.prototype,e),n&&o(r,n),t}()),s={en:{variants:{},models:{},attributes:{},errors:{messages:{accepted:"must be accepted",blank:"can't be blank",confirmation:"doesn't match %{attribute}",empty:"can't be empty",equal_to:"must be equal to %{count}",even:"must be even",exclusion:"is reserved",greater_than:"must be greater than %{count}",greater_than_or_equal_to:"must be greater than or equal to %{count}",inclusion:"is not included in the list",invalid:"is invalid",less_than:"must be less than %{count}",less_than_or_equal_to:"must be less than or equal to %{count}",not_a_number:"is not a number",not_an_integer:"must be an integer",odd:"must be odd",present:"must be blank",too_long:{one:"is too long (maximum is 1 character)",other:"is too long (maximum is %{count} characters)"},too_short:{one:"is too short (minimum is 1 character)",other:"is too short (minimum is %{count} characters)"},wrong_length:{one:"is the wrong length (should be 1 character)",other:"is the wrong length (should be %{count} characters)"},other_than:"must be other than %{count}"}}}};function i(t){return(i="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var a,u={},l=function(t,r){if(-1!==t.indexOf(r))return null;var e=function(t){var r=t.length;return t.find((function(t,e){if(null===t)return r=e,!0})),r}(t);return t[e]=r,e},c=function(t,r,e){return u[t][r][e]=null},h=function(t){var r=t.getIdentity();void 0===u[r]&&(u[r]={}),void 0===u[r][t.id]&&(u[r][t.id]=[]),u[r][t.id][0]=t},p={get imap(){return u},clear:function(){return u={}},subscribe:function(t){var r=function(){};if("object"===i(t.to)){var e=function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},e=r.with;h(e);var o=u[e.getIdentity()][e.id];return l(o,t)}(t.with,{with:t.to});return null===e?r:function(){c(t.to.getIdentity(),t.to.id,e)}}if("function"==typeof t.to){var o=function(t){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};void 0===u[t]&&(u[t]={}),void 0===u[t].collection&&(u[t].collection=[]);var e=u[t].collection;return l(e,r.to)}(t.to.getIdentity(),{to:t.with});return null===o?r:function(){c(t.to.getIdentity(),"collection",o)}}},unsubscribe:c,add:h,find:function(t,r){return void 0!==u[t]&&null!=u[t][r]?u[t][r][0]:null},findConnected:function(t,r){return void 0!==u[t]&&void 0!==u[t][r]&&u[t][r].length>1?u[t][r].slice(1):[]}},f=function(){function t(){this.obj=null,this.attr=null,this.val=null,this.opts=null}return t.sharedInstances={},t.instance=function(t,r,e){var o,n;return n=this.identity,null==this.sharedInstances[n]&&(this.sharedInstances[n]=new q[n]),(o=this.sharedInstances[n]).assignAttribs(t,r,e),o},t.prototype.assignAttribs=function(t,r,e){return this.obj=t,this.attr=r,this.val=this.obj[this.attr],this.opts=e},t}(),d={}.hasOwnProperty,_=function(t){function r(){r.__super__.constructor.call(this)}return function(t,r){for(var e in r)d.call(r,e)&&(t[e]=r[e]);function o(){this.constructor=t}o.prototype=r.prototype,t.prototype=new o,t.__super__=r.prototype}(r,t),r.identity="Absence",r.prototype.validate=function(){switch(typeof this.val){case"string":if(null!=this.val&&0===this.val.length)return;break;default:if(null==this.val)return}return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:s[n.locale].errors.messages.present,this.obj.addErrorMessage(t,{for:this.attr})},r}(f),g={}.hasOwnProperty,y=function(t){function r(){r.__super__.constructor.call(this)}return function(t,r){for(var e in r)g.call(r,e)&&(t[e]=r[e]);function o(){this.constructor=t}o.prototype=r.prototype,t.prototype=new o,t.__super__=r.prototype}(r,t),r.identity="Confirmation",r.prototype.validate=function(){var t;if(t=this.obj[this._properAttr()],null==this.val||null==t||this.val!==t)return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t,r,e,o;return e=this.attr.charAt(0).toUpperCase()+this.attr.slice(1),t=(r=s[n.locale].attributes[this.obj.getIdentity()])&&r[this.attr]||e,o=(o=null!=this.opts.message?this.opts.message:s[n.locale].errors.messages.confirmation).replace("%{attribute}",t),this.obj.addErrorMessage(o,{for:this._properAttr()})},r.prototype._properAttr=function(){return this.attr+"Confirmation"},r}(f),m={}.hasOwnProperty,v=function(t){function r(){r.__super__.constructor.call(this)}return function(t,r){for(var e in r)m.call(r,e)&&(t[e]=r[e]);function o(){this.constructor=t}o.prototype=r.prototype,t.prototype=new o,t.__super__=r.prototype}(r,t),r.identity="Exclusion",r.prototype.validate=function(){if(-1!==(this.opts.in||this.opts.within||[]).indexOf(this.val))return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:s[n.locale].errors.messages.exclusion,this.obj.addErrorMessage(t,{for:this.attr})},r}(f),b={}.hasOwnProperty,E=function(t){function r(){r.__super__.constructor.call(this)}return function(t,r){for(var e in r)b.call(r,e)&&(t[e]=r[e]);function o(){this.constructor=t}o.prototype=r.prototype,t.prototype=new o,t.__super__=r.prototype}(r,t),r.identity="Format",r.prototype.validate=function(){if(null==this.opts.with.exec(this.val))return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:s[n.locale].errors.messages.invalid,this.obj.addErrorMessage(t,{for:this.attr})},r}(f),w={}.hasOwnProperty,M=function(t){function r(){r.__super__.constructor.call(this)}return function(t,r){for(var e in r)w.call(r,e)&&(t[e]=r[e]);function o(){this.constructor=t}o.prototype=r.prototype,t.prototype=new o,t.__super__=r.prototype}(r,t),r.identity="Inclusion",r.prototype.validate=function(){if(-1===(this.opts.in||this.opts.within||[]).indexOf(this.val))return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:s[n.locale].errors.messages.inclusion,this.obj.addErrorMessage(t,{for:this.attr})},r}(f),O={}.hasOwnProperty,P=function(t){function r(){r.__super__.constructor.call(this)}return function(t,r){for(var e in r)O.call(r,e)&&(t[e]=r[e]);function o(){this.constructor=t}o.prototype=r.prototype,t.prototype=new o,t.__super__=r.prototype}(r,t),r.identity="Length",r.prototype.validate=function(){var t;if(null!=this.val&&null!==(t=null!=this._range()[0]&&null!=this._range()[1]&&this._range()[0]===this._range()[1]&&this.val.length!==this._range()[0]?this._selectErrorMessage("wrong_length",this._range()[0]):null!=this._range()[0]&&this.val.length<this._range()[0]?this._selectErrorMessage("too_short",this._range()[0]):null!=this._range()[1]&&this.val.length>this._range()[1]?this._selectErrorMessage("too_long",this._range()[1]):null))return this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._range=function(){return[this.opts.minimum||this.opts.is||null!=this.opts.within&&this.opts.within[0]||null,this.opts.maximum||this.opts.is||null!=this.opts.within&&this.opts.within[1]||null]},r.prototype._selectErrorMessage=function(t,r){var e,o,i,a,u;if(1===r)return s[n.locale].errors.messages[t].one;for(i=null,e=0,o=(a=["few","many"]).length;e<o;e++)if(u=a[e],this._checkVariant(u,r)){i=s[n.locale].errors.messages[t][u];break}return null==i&&(i=s[n.locale].errors.messages[t].other),null!=this.opts.message&&(i=this.opts.message),/%{count}/.exec(i)&&(i=i.replace("%{count}",r)),i},r.prototype._checkVariant=function(t,r){if(null!=s[n.locale].variants[t])return s[n.locale].variants[t](r)},r}(f),j={}.hasOwnProperty,N=function(t){function r(){r.__super__.constructor.call(this)}return function(t,r){for(var e in r)j.call(r,e)&&(t[e]=r[e]);function o(){this.constructor=t}o.prototype=r.prototype,t.prototype=new o,t.__super__=r.prototype}(r,t),r.identity="Numericality",r.prototype.validate=function(){return isNaN(this.val)?this._addNaNErrorMessage():null!=this.opts.only_integer&&Number(this.val)!==parseInt(this.val)?this._addIntErrorMessage():null!=this.opts.greater_than&&Number(this.val)<=this.opts.greater_than?this._addGreatherThanErrorMessage():null!=this.opts.greater_than_or_equal_to&&Number(this.val)<this.opts.greater_than_or_equal_to?this._addGreatherThanOrEqualToErrorMessage():null!=this.opts.equal_to&&Number(this.val)!==this.opts.equal_to?this._addEqualToErrorMessage():null!=this.opts.less_than&&Number(this.val)>=this.opts.less_than?this._addLessThanErrorMessage():null!=this.opts.less_than_or_equal_to&&Number(this.val)>this.opts.less_than_or_equal_to?this._addLessThanOrEqualToErrorMessage():null!=this.opts.other_than&&Number(this.val)===this.opts.other_than?this._addOtherThanErrorMessage():null!=this.opts.odd&&Number(this.val)%2!=1?this._addOddErrorMessage():null!=this.opts.even&&Number(this.val)%2!=0?this._addEvenErrorMessage():void 0},r.prototype._addNaNErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:s[n.locale].errors.messages.not_a_number,this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addIntErrorMessage=function(){var t;return t=s[n.locale].errors.messages.not_an_integer,this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addGreatherThanErrorMessage=function(){var t;return t=(t=s[n.locale].errors.messages.greater_than).replace("%{count}",this.opts.greater_than),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addGreatherThanOrEqualToErrorMessage=function(){var t;return t=(t=s[n.locale].errors.messages.greater_than_or_equal_to).replace("%{count}",this.opts.greater_than_or_equal_to),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addEqualToErrorMessage=function(){var t;return t=(t=s[n.locale].errors.messages.equal_to).replace("%{count}",this.opts.equal_to),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addLessThanErrorMessage=function(){var t;return t=(t=s[n.locale].errors.messages.less_than).replace("%{count}",this.opts.less_than),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addLessThanOrEqualToErrorMessage=function(){var t;return t=(t=s[n.locale].errors.messages.less_than_or_equal_to).replace("%{count}",this.opts.less_than_or_equal_to),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addOtherThanErrorMessage=function(){var t;return t=(t=s[n.locale].errors.messages.other_than).replace("%{count}",this.opts.other_than),this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addOddErrorMessage=function(){var t;return t=s[n.locale].errors.messages.odd,this.obj.addErrorMessage(t,{for:this.attr})},r.prototype._addEvenErrorMessage=function(){var t;return t=s[n.locale].errors.messages.even,this.obj.addErrorMessage(t,{for:this.attr})},r}(f),T={}.hasOwnProperty,S=function(t){function r(){r.__super__.constructor.call(this)}return function(t,r){for(var e in r)T.call(r,e)&&(t[e]=r[e]);function o(){this.constructor=t}o.prototype=r.prototype,t.prototype=new o,t.__super__=r.prototype}(r,t),r.identity="Presence",r.prototype.validate=function(){switch(typeof this.val){case"string":if(null!=this.val&&this.val.length>0)return;break;default:if(null!=this.val)return}return this._addErrorMessage()},r.prototype._addErrorMessage=function(){var t;return t=null!=this.opts.message?this.opts.message:s[n.locale].errors.messages.blank,this.obj.addErrorMessage(t,{for:this.attr})},r}(f),I={}.hasOwnProperty;a=function(t){function r(){r.__super__.constructor.call(this)}return function(t,r){for(var e in r)I.call(r,e)&&(t[e]=r[e]);function o(){this.constructor=t}o.prototype=r.prototype,t.prototype=new o,t.__super__=r.prototype}(r,t),r.identity="Size",r.prototype.validate=function(){return P.instance(this.obj,this.attr,this.opts).validate()},r}(f);var q={Absence:_,Base:f,Confirmation:y,Exclusion:v,Format:E,Inclusion:M,Length:P,Numericality:N,Presence:S,Size:a},A=function(t){var r="";return Object.keys(t).forEach((function(e){""!==r&&(r="".concat(r,"&")),r="".concat(r).concat(e,"=").concat(encodeURIComponent(t[e]))})),r},R=function(t,r,e){var o=function(t){var r={};if(!t)return r;var e=["resource","total","count"];return Object.keys(t).forEach((function(o){-1===e.indexOf(o)&&(r[o]=t[o])})),r}(e),n="GET"===t?"".concat(r,"?").concat(A(o)):r,s=document.querySelector("meta[name='csrf-token']"),i=new XMLHttpRequest;return i.open(t,n),i.setRequestHeader("Accept","application/json"),i.setRequestHeader("Content-Type","application/json"),s&&i.setRequestHeader("X-CSRF-Token",s.content),i.send(JSON.stringify(o)),i},x=function(){function t(t){null==t&&(t={}),this.id=null,this.errors=null,this.resource=t.resource,null!=this.constructor.attributes&&this.__initAttributes(),null!=t&&this.__assignAttributes(t)}return t.getIdentity=function(){if(null!=this.identity)return this.identity;throw"Specify Model's identity!"},t.getRemoteName=function(){return null!=this.remoteName?this.remoteName:this.getIdentity()},t.all=function(t){return null==t&&(t={}),this.get("all",t)},t.get=function(t,r){return null==r&&(r={}),this.__send("GET",t,r)},t.post=function(t,r){return null==r&&(r={}),this.__send("POST",t,r)},t.put=function(t,r){return null==r&&(r={}),this.__send("PUT",t,r)},t.patch=function(t,r){return null==r&&(r={}),this.__send("PATCH",t,r)},t.delete=function(t,r){return null==r&&(r={}),this.__send("DELETE",t,r)},t.find=function(t){var r,e,o,n;return o={},"object"==typeof t?(o=t,r=t.id,delete o.id):r=t,e=R("GET",this.__getResourcesUrl(o)+"/"+r,o),new Promise((n=this,function(r,o){return e.onerror=function(t){return o(t)},e.onload=function(e){var o;return o=JSON.parse(e.target.response),r(n.__initFromJSON(o,t.resource))}}))},t.getAttribRemoteName=function(t){return null==this.attributes||null==this.attributes[t]?null:null==this.attributes[t].remoteName?t:this.attributes[t].remoteName},t.getResourcesUrlParams=function(t){var r,e,o,n;for(n=this.__getResourcesUrl({resource:t.resource}),o=/:(\w+)\/?/,e=[];r=o.exec(n);)e.push(r[1]),n=n.replace(r[0],r[1]);return e},t.__getResourcesUrl=function(t){var r,e;return e=null==this.resources?"/"+this.getRemoteName().toLowerCase()+"s":t.resource?this.resources[t.resource].url:null!=n.scope&&null!=this.resources[n.scope]?this.resources[n.scope].url:this.resources.url,null!=n.protocolWithHost&&(e=""+n.protocolWithHost+e),null==(r=/:(\w+)\/?/.exec(e))||(null!=t[r[1]]?(e=e.replace(":"+r[1],t[r[1]]),delete t[r[1]]):null!=t.obj&&null!=t.obj[r[1]]&&(e=e.replace(":"+r[1],t.obj[r[1]]))),e},t.__page=function(t,r,e){var o,n,s;return n=r.url,r.params[r.pageParam]=t,o=R(r.method,n,r.params),new Promise((s=this,function(t,n){return o.onerror=function(t){return n(t)},o.onload=function(o){var n,i,a,u,l,c,h,p,f,d;if((n=JSON.parse(o.target.response)).constructor===Array)for(i=0,l=n.length;i<l;i++)p=n[i],h=s.__initFromJSON(p,r.resource),e.push(h);else if(null!=n.resources){for(e.constructor===Array&&(e={resources:[],count:0}),a=0,c=(f=n.resources).length;a<c;a++)p=f[a],h=s.__initFromJSON(p,r.resource),e.resources.push(h);e.count=n.count}else for(u in n)d=n[u],e[u]=d;return t(e)}}))},t.__paginate=function(t){var r,e;return r={method:t.method,url:t.url,params:t.params,pageParam:t.pageParam,resource:t.resource},this.__page(t.pageNum||1,r,[]).then((e=this,function(o){var n,s,i,a,u,l;if(l=o.count||t.total,a=Promise.resolve(o),null!=t.pageNum)return a;if(l<=t.perPage)return a;if((i=parseInt(l/t.perPage))!==l/t.perPage&&(i+=1),1===i)return a;for(n=s=2,u=i;2<=u?s<=u:s>=u;n=2<=u?++s:--s)!function(t){a=a.then((function(n){return e.__page(t,r,o)}))}(n);return a}))},t.__getPaginationParam=function(t){var r,e,o,s,i;return"page",null!=t&&null!=this.resources&&this.resources[t]?(null!=(r=this.resources[t].paginate)?r.param:void 0)||"page":null!=n.scope&&null!=this.resources&&null!=this.resources[n.scope]?(null!=(e=this.resources[n.scope])&&null!=(o=e.paginate)?o.param:void 0)||"page":null!=(null!=(s=this.resources)&&null!=(i=s.paginate)?i.param:void 0)?this.resources.paginate.param:"page"},t.__getPaginationPer=function(t){var r,e,o,s,i;return null!=t&&null!=this.resources&&this.resources[t]?null!=(r=this.resources[t].paginate)?r.per:void 0:null!=n.scope&&null!=this.resources&&null!=this.resources[n.scope]?null!=(e=this.resources[n.scope])&&null!=(o=e.paginate)?o.per:void 0:null!=(null!=(s=this.resources)&&null!=(i=s.paginate)?i.per:void 0)?this.resources.paginate.per:null},t.__send=function(t,r,e){var o,n;return n=this.__getResourcesUrl(e),"all"!==r&&(n=n+"/"+r),o={method:t,url:n,params:e,resource:e.resource,perPage:this.__getPaginationPer(e.resource),pageNum:e.page,pageParam:this.__getPaginationParam(e.resource),total:e.total||e.count},this.__paginate(o)},t.__initFromJSON=function(t,r){var e;return(e=new this(t)).resource=r,p.add(e),e},t.prototype.setResource=function(t){return this.resource=t},t.prototype.getIdentity=function(){return this.constructor.getIdentity()},t.prototype.getAttrRemoteName=function(t){return null==this.constructor.attributes||null==this.constructor.attributes[t]?null:this.constructor.attributes[t].remoteName||t},t.prototype.getAttrName=function(t){var r,e;if(null==this.constructor.attributes)return t;if(null!=this.constructor.attributes[t])return t;for(r in e=this.constructor.attributes)if(e[r].remoteName===t)return r;return t},t.prototype.getAttrType=function(t){return null==this.constructor.attributes||null==this.constructor.attributes[t]?null:this.constructor.attributes[t].type},t.prototype.assignAttr=function(t,r){var e;if(e=this.getAttrType(t),null!=r){switch(e){case"Date":r=new Date(Date.parse(r));break;case"Integer":case"Int":r=parseInt(r);break;case"Float":r=parseFloat(r);break;case"Boolean":case"Bool":r="boolean"==typeof r?r:Boolean(parseInt(r));break;case"Number":r=Number(r);break;case"String":r=String(r)}return this[t]=r}this[t]=null},t.prototype.attributes=function(){var t,r,e;if(t={id:this.id},null==this.constructor.attributes)return t;for(r in e=this.constructor.attributes)e[r],t[r]=this[r];return t},t.prototype.isValid=function(){var t,r,e,o,n,s,i,a,u,l,c;if(null==this.constructor.attributes)return!0;for(o in this.errors=null,s=this.constructor.attributes)if(null!=(t=s[o]).validations)for(u in i=t.validations)l=i[u],null!=this.id&&"create"===l.on||null==this.id&&"update"===l.on||(null==l.if||l.if(this))&&(c=u.charAt(0).toUpperCase()+u.slice(1),null!=q[c]?(n=this.__processedValidationSettings(l),q[c].instance(this,o,n).validate()):console.warn('"'+c+'" validator is not implemented!'));if(null!=this.constructor.validate)for(r=0,e=(a=this.constructor.validate).length;r<e;r++)this[a[r]]();return null==this.errors},t.prototype.isInvalid=function(){return!this.isValid()},t.prototype.isEmpty=function(){var t,r;for(t in r=this.attributes())if(r[t],null!==this[t])return!1;return!0},t.prototype.addErrorMessage=function(t,r){return null==r&&(r={}),null==this.errors&&(this.errors={}),null==this.errors[r.for]&&(this.errors[r.for]=[]),this.errors[r.for].push(t)},t.prototype.save=function(){var t,r,e;return t=null!=this.id?"PUT":"POST",r=R(t,this.__getResourceUrl(),this.serialize()),new Promise((e=this,function(t,o){return r.onerror=function(t){return o(t)},r.onload=function(r){var o;if(!(o=JSON.parse(r.target.response)).success)return null!=o.errors&&e.__assignRemoteErrorMessages(o.errors),t(o);t(o)}}))},t.prototype.updateAttribute=function(t){var r,e;return r=R("PUT",this.__getResourceUrl(),this.serialize(t)),new Promise((e=this,function(t,o){return r.onerror=function(t){return o(t)},r.onload=function(r){var n;return r.target.status>=200&&r.target.status<400?(n=JSON.parse(r.target.response)).success?void t(n):(null!=n.errors&&e.__assignRemoteErrorMessages(n.errors),t(n)):r.target.status>=500?o(r):void 0}}))},t.prototype.serialize=function(t){var r,e,o,n;if(null==t&&(t=null),null==this.constructor.attributes)return{};for(t in(e={})[o=this.constructor.getRemoteName().toLowerCase()]={},r={},null!=t?r[t]=null:r=this.constructor.attributes,r)r[t],n=this.getAttrRemoteName(t),e[o][n]=this[t];return e},t.prototype.reload=function(){var t,r,e,o,n;for(t={id:this.id,resource:this.resource},r=0,e=(n=this.constructor.getResourcesUrlParams({resource:this.resource})).length;r<e;r++)t[o=n[r]]=this[o];return this.constructor.find(t)},t.prototype.changes=function(){var t,r,e,o,n;for(r in o={},t=p.find(this.getIdentity(),this.id),e=this.attributes())if((n=e[r])!==t[r]){if(null!=n&&n.constructor===Date&&t[r]-n==0)continue;n!==t[r]&&(o[r]={is:t[r],was:n})}return o},t.prototype.applyChanges=function(){var t,r,e,o;for(t in e=[],r=this.changes())o=r[t],e.push(this[t]=o.is);return e},t.prototype.toKey=function(){return this.getIdentity().toLowerCase()+"_"+this.id},t.prototype.get=function(t,r){return null==r&&(r={}),this.__send("GET",t,r)},t.prototype.post=function(t,r){return null==r&&(r={}),this.__send("POST",t,r)},t.prototype.put=function(t,r){return null==r&&(r={}),this.__send("PUT",t,r)},t.prototype.patch=function(t,r){return null==r&&(r={}),this.__send("PATCH",t,r)},t.prototype.delete=function(t,r){return null==r&&(r={}),this.__send("DELETE",t,r)},t.prototype.__send=function(t,r,e){var o,n;return n=this.__getResourceUrl(),null!=r&&(n=n+"/"+r),o=R(t,n,e),new Promise((function(t,r){return o.onerror=function(t){return r(t)},o.onload=function(o){return o.target.status>=200&&o.target.status<400?(e=JSON.parse(o.target.response),t(e)):o.target.status>=500?r(o):void 0}}))},t.prototype.__assignAttributes=function(t){var r,e,o,n;for(e in o=[],t)n=t[e],r=this.getAttrName(e),o.push(this.assignAttr(r,n));return o},t.prototype.__initAttributes=function(){var t,r,e;for(t in e=[],r=this.constructor.attributes)r[t],e.push(this[t]=null);return e},t.prototype.__assignRemoteErrorMessages=function(t){var r,e,o,n,s;for(n in s=[],t)o=t[n],r=this.getAttrName(n),s.push(function(){var t,n,s;for(s=[],t=0,n=o.length;t<n;t++)e=o[t],s.push(this.addErrorMessage(e,{for:r}));return s}.call(this));return s},t.prototype.__getResourceUrl=function(){var t;return t=this.constructor.__getResourcesUrl({resource:this.resource,obj:this}),null==this.id?t:t+"/"+this.id},t.prototype.__processedValidationSettings=function(t){var r,e,o;for(r in o={},t)e=t[r],o[r]="function"==typeof e?e(this):e;return o},t}();function k(t,r){var e=Object.keys(t);if(Object.getOwnPropertySymbols){var o=Object.getOwnPropertySymbols(t);r&&(o=o.filter((function(r){return Object.getOwnPropertyDescriptor(t,r).enumerable}))),e.push.apply(e,o)}return e}function V(t,r,e){return r in t?Object.defineProperty(t,r,{value:e,enumerable:!0,configurable:!0,writable:!0}):t[r]=e,t}x.prototype.clone=function(){return new this.constructor(function(t){for(var r=1;r<arguments.length;r++){var e=null!=arguments[r]?arguments[r]:{};r%2?k(Object(e),!0).forEach((function(r){V(t,r,e[r])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(e)):k(Object(e)).forEach((function(r){Object.defineProperty(t,r,Object.getOwnPropertyDescriptor(e,r))}))}return t}({},this.attributes()))};var U={Base:x}}])})); |
@@ -0,3 +1,10 @@ | ||
/* eslint-env node */ | ||
module.exports = { | ||
modulePaths: ["src"] | ||
modulePaths: ["src"], | ||
moduleFileExtensions: ["js", "coffee"], | ||
transform: { | ||
"^.+\\.js$": "babel-jest", | ||
"^.+\\.coffee$": "<rootDir>/jest.coffeescript.preprocessor.js" | ||
} | ||
}; |
{ | ||
"name": "loco-js-model", | ||
"version": "0.3.3", | ||
"version": "1.0.0", | ||
"description": "Model part of loco-js", | ||
"main": "dist/loco-model.js", | ||
"scripts": { | ||
"start": "node dev/server.js", | ||
"server": "node dev/server.js", | ||
"start": "webpack --watch --config webpack.config.js", | ||
"test": "jest", | ||
"build": "webpack -p --config webpack.config.js", | ||
@@ -23,29 +25,31 @@ "lint-staged": "lint-staged" | ||
"homepage": "http://locoframework.org", | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
} | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"babel-core": "^6.26.0", | ||
"babel-eslint": "^8.2.3", | ||
"babel-jest": "^22.4.3", | ||
"babel-loader": "^7.1.4", | ||
"babel-plugin-transform-object-rest-spread": "^6.26.0", | ||
"babel-preset-env": "^1.6.1", | ||
"@babel/core": "^7.9.6", | ||
"@babel/plugin-proposal-class-properties": "^7.8.3", | ||
"@babel/preset-env": "^7.9.6", | ||
"babel-eslint": "^10.1.0", | ||
"babel-jest": "^25.5.1", | ||
"babel-loader": "^8.1.0", | ||
"coffee-loader": "^0.9.0", | ||
"coffeescript": "^1.12.7", | ||
"eslint": "^4.19.1", | ||
"eslint-config-airbnb-base": "^12.1.0", | ||
"eslint-config-prettier": "^2.9.0", | ||
"eslint-plugin-import": "^2.11.0", | ||
"eslint-plugin-jest": "^21.15.0", | ||
"eslint-plugin-prettier": "^2.6.0", | ||
"express": "^4.16.3", | ||
"jest": "^22.4.3", | ||
"lint-staged": "^7.0.4", | ||
"pre-commit": "^1.2.2", | ||
"prettier": "^1.12.0", | ||
"webpack": "^3.11.0", | ||
"webpack-dev-middleware": "^2.0.6" | ||
}, | ||
"pre-commit": [ | ||
"lint-staged" | ||
] | ||
"eslint": "^6.8.0", | ||
"eslint-config-prettier": "^6.11.0", | ||
"eslint-plugin-import": "^2.20.2", | ||
"eslint-plugin-jest": "^23.13.1", | ||
"eslint-plugin-prettier": "^3.1.3", | ||
"express": "^4.17.1", | ||
"husky": "^3.1.0", | ||
"jest": "^25.5.4", | ||
"lint-staged": "^9.5.0", | ||
"prettier": "^1.19.1", | ||
"webpack": "^4.43.0", | ||
"webpack-cli": "^3.3.11", | ||
"webpack-dev-middleware": "^3.7.2" | ||
} | ||
} |
309
README.md
@@ -7,11 +7,8 @@ ![logo](https://raw.githubusercontent.com/artofcodelabs/artofcodelabs.github.io/master/assets/ext/loco_logo_trans_sqr-300px.png) | ||
It can be said that **Loco-JS-Model** is a model part of [**Loco-JS**](http://github.com/locoframework/loco-js), which __can be used separately__. | ||
**Loco-JS** is in turn a front-end part of [**Loco-Rails**](http://github.com/locoframework/loco-rails). It can be used separately as well (with limited functionality). | ||
And **Loco-Rails** is a back-end part of the whole [**Loco**](http://github.com/locoframework) framework and it requires **Loco-JS** to work. | ||
**Loco-JS-Model** is one of the [Loco framework](http://locoframework.org) components. It is a model layer for JavaScript that can be used separately. | ||
Loco framework is a concept that simplifies communication between front-end and back-end. The back-end part can be implemented in other languages and frameworks as well. | ||
I am a Rails programmer. That's why I created **Loco** for [**Rails**](https://github.com/locoframework/loco-rails). | ||
**Loco-Rails** is just a concept that simplifies communication between front-end and back-end code. It can be implemented in other languages or frameworks as well. | ||
I am a Rails programmer that's why I created **Loco** for **Rails**. | ||
*Visualization of the Loco framework:* | ||
This is how it can be visualized: | ||
``` | ||
@@ -21,59 +18,36 @@ Loco Framework | ||
|--- Loco-Rails (back-end part) | ||
| | | ||
| |--- Loco-Rails-Core (logical structure for JS / can be used separately with Loco-JS-Core) | ||
| | ||
|--- Loco-JS (front-end part / can be used separately) | ||
|--- Loco-JS (front-end part) | ||
| | ||
|--- Loco-JS-Core (logical structure for JS / can be used separately) | ||
| | ||
|--- Loco-JS-Model (model part / can be used separately) | ||
| | ||
|--- other parts of Loco-JS | ||
|--- other built-in parts of Loco-JS | ||
Loco-JS-UI - connects models with UI elements (a separate library) | ||
``` | ||
Following sections contain more detailed description of its internals and API. | ||
Loco-JS-Model works well as a part of the modern JavaScript ecosystem alongside libraries such as React and Redux. | ||
# ⛑ But how is Loco supposed to help? | ||
* by providing logical structure for a JavaScript code (along with base classes for models, controllers and views). You exactly know where to start, when looking for a JavaScript code that runs current page ([**Loco-JS**](https://github.com/locoframework/loco-js)) | ||
* you have models that protect from sending invalid data to the API endpoints. They also facilitate fetching objects of a given type from the server (**Loco-JS-Model**) | ||
* you can easily assign a model to a form what will enrich this form with fields' validation ([**Loco-JS**](https://github.com/locoframework/loco-js)) | ||
* you can connect models with controllers and views on the front-end. And they will be notified about every change made to a corresponding model on the server side. This change will be emitted as a signal to the front-end code. And signal is just a fancy name for a JS object (**Loco**) | ||
* it allows you to send messages over WebSockets in both directions with just a single line of code on each side (**Loco**) | ||
* respects permissions (you can send messages only to specified, signed in on the server models _e.g. given admin or user_) (**Loco**) | ||
* solves other common problems | ||
# 🦕 Origins | ||
**Loco** framework was created back in 2016. The main reason for it was a need to make my life easier as a full-stack developer. | ||
I was using [Coffeescript](http://coffeescript.org) on the front-end back then and [Ruby on Rails](http://rubyonrails.org) on the back-end. | ||
I still use **Rails** but my front-end toolbox has changed a lot. Now, I work with modern goodies such as **ES6**, [Webpack](https://webpack.js.org), [Babel](https://babeljs.io), [React](https://reactjs.org), [Redux](https://redux.js.org)... and **Loco-JS** obviously :) | ||
**Loco-Rails** enriches Ruby on Rails. It's a functionality layer that works on top of Rails to simplify communication between front-end na back-end code. It is a concept that utilizes good parts of Rails to make this communication straightforward. | ||
But **Loco-JS** can be used as a standalone library to structure a JavaScript code, for example. | ||
[**Loco-JS-Model**](https://github.com/locoframework/loco-js-model/) can be used without Rails as well and in cooperation with other modern tools such as React and Redux. You have to follow only a few rules of formatting JSON responses from the server. | ||
# 🔬 Tech stack of Loco-JS-Model | ||
The Origins explain why the major part of **Loco-JS-Model** is still written in CoffeeScript. It is just an extraction from Loco-JS for everyone who don't need all the features that Loco-JS provides. It shouldn't worry you though unless you want to contribute. | ||
What's more important is that all Loco-JS-Model's modules are transpiled and bundled using modern tools such as **Babel** and **Webpack** accordingly. Loco-JS-Model works well as a part of modern JavaScript ecosystem alongside libraries such as React and Redux. | ||
In the future, while adding features, all modules will be rewritten to Javascript. | ||
This 🎁[**example**](https://github.com/artofcodelabs/front-end-boilerplate)🎁 presents how to combine Loco-JS-Model with React and Redux _(+ other neat tools)_. | ||
This repo is also a good starting point when you want to start hack on multi-static-page app powered by React, Redux, React, React-Router, Webpack, Babel etc. and you look for something pre-configured and more straightforward than [Create React App](https://github.com/facebook/create-react-app) at the same time. | ||
This repo is also a good starting point if you want to start hack on a multi-static-page app powered by React, Redux, React, React-Router, Webpack, Babel, etc. | ||
Especially if you are looking for something pre-configured and more straightforward than [Create React App](https://github.com/facebook/create-react-app). | ||
# 📡 Model Layer | ||
I really liked [ActiveRecord](http://guides.rubyonrails.org/active_record_basics.html) throughout the years of using Rails. This layer stands between the business logic of your app and database itself and does a lot of useful things. One of many is providing validations of the objects to ensure that only valid data are saved into your database. It also provides several finder methods to perform certain queries on your database without writing raw SQL. | ||
I liked [ActiveRecord](http://guides.rubyonrails.org/active_record_basics.html) throughout the years of using Rails. This layer stands between the business logic of your app and a database and does a lot of useful things. One of them is providing validations of objects to ensure that only valid ones are saved in a database. It also provides several finder methods to perform certain queries on a database without writing raw SQL. | ||
## But what does model mean when it comes to the app that works inside the browser? 🤔 | ||
## But what does model mean when it comes to an app working inside the browser? 🤔 | ||
Well, you have at least 2 ways to persist your data: | ||
1. You can save them in the local storage | ||
2. You can send them to the server using the API endpoint, where they will be stored in the database | ||
1. You can save them in local storage. | ||
2. You can send them to a server using the API endpoint. Data are then stored in a database. | ||
So we can assume that validating the data before they reach destination will be useful in both cases. But when it comes to persistence, Loco-JS-Model gravitates towards communication with the server. It provides methods that facilitate both persisting data and fetching them from server. | ||
So we can assume that validating data before they reach the destination can be useful in both cases. | ||
But when it comes to persistence - Loco-JS-Model gravitates towards communication with a server. It provides methods that facilitate both: persisting data and fetching them from a server. | ||
It will be more obvious, when we look at the examples. But first we have to set things up. | ||
# 📥 Installation | ||
@@ -89,5 +63,5 @@ | ||
Although, [class properties transform](https://babeljs.io/docs/plugins/transform-class-properties/) Babel plugin may be helpful to support static class properties, which are useful in defining models. | ||
Although [babel-plugin-transform-class-properties](https://babeljs.io/docs/en/babel-plugin-transform-class-properties) may be helpful to support static class properties, which are useful in defining models. | ||
Loco-JS-Model uses Promises so remember to **polyfill** them❗️ | ||
Loco-JS-Model uses Promises, so remember to **polyfill** them❗️ | ||
@@ -99,4 +73,3 @@ # ⚙️ Configuration | ||
// If provided - Loco will be using absolute path | ||
// instead of site-root-relative path in all XHR requests | ||
// If provided - Loco-JS uses an absolute path instead of a site-root-relative path in all XHR requests | ||
Config.protocolWithHost = "http://localhost:3000"; | ||
@@ -106,9 +79,5 @@ | ||
// Models have static class property - "resources". | ||
// You use it to specify named scopes (base URLs), | ||
// where you can fetch objects of given type. | ||
// You can setup default scope for all models, | ||
// using this property. | ||
// Models have a static class property - "resources". It is used to specify base URLs (scopes) | ||
// from which data are retrieved. This property sets a default scope for all models. | ||
Config.scope = "admin"; // null by default | ||
``` | ||
@@ -120,3 +89,3 @@ | ||
This is how an exemplary model can look like: | ||
An exemplary model can look like this: | ||
@@ -128,16 +97,12 @@ ```javascript | ||
import { decimalize, nullIfNaN } from "helpers/number"; | ||
class Coupon extends Models.Base { | ||
// This property should have the name of the class. | ||
// Setting this property is required when you use full Loco framework | ||
// The value of this property should be a "stringified" class name. | ||
// Setting this property is required if you use a full Loco framework | ||
// and send notifications from the server. | ||
// Because of minification, finding this class by name, | ||
// is improssible in production env. | ||
// Loco relies on naming, so it has to be persisted. | ||
// It is because finding this class by its name is impossible in a production | ||
// environment due to minification. | ||
static identity = "Coupon"; | ||
// It stores information about scopes in your app. | ||
// You can fetch the same type of resource from different API endpoints. | ||
// So you can define them using this property. | ||
// These endpoints can be defined using resources property. | ||
static resources = { | ||
@@ -151,6 +116,6 @@ url: "/user/coupons", | ||
// This property stores information about model's attributes | ||
// This property stores information about the model's attributes | ||
static attributes = { | ||
stripeId: { | ||
// Specify if different from what API returns | ||
// Specify if different than the value returned via API | ||
remoteName: "stripe_id", | ||
@@ -161,3 +126,3 @@ // When assigning values from API endpoint, | ||
type: "String", | ||
// Available validators: Absence, Confirmation, Exclusion, | ||
// Available validators: Absence, Confirmation, Exclusion, | ||
// Format, Inclusion, Length, Numericality, Presence, Size | ||
@@ -175,3 +140,3 @@ validations: { | ||
validations: { | ||
// you can run given validators conditionally | ||
// a validator can run conditionally | ||
presence: { if: o => o.amountOff == null }, | ||
@@ -184,15 +149,5 @@ numericality: { | ||
}, | ||
// This attribute should be of type decimal but it has no type. | ||
// It is because of I use this model with React | ||
// and I change the value of this attribute, | ||
// every time user fills number in the input field. | ||
// So it can have incorrect decimal value (like "12." for example), | ||
// when user is in the middle of writting final value. | ||
// If I'd specify the type, Loco-JS-Model would make a convertion | ||
// to this type on every key press. | ||
// This would make it improssible to fill in the desired number. | ||
// If you don't use React, this "constant binding" or just you use | ||
// a different strategy, it's a good practice to always specify type. | ||
amountOff: { | ||
remoteName: "amount_off", | ||
type: "Decimal", | ||
validations: { | ||
@@ -214,22 +169,2 @@ presence: { if: o => o.percentOff == null }, | ||
}, | ||
durationInMonths: { | ||
remoteName: "duration_in_months", | ||
type: "Integer", | ||
validations: { | ||
numericality: { | ||
greater_than: 0, | ||
if: o => o.duration === "repeating" | ||
} | ||
} | ||
}, | ||
maxRedemptions: { | ||
remoteName: "max_redemptions", | ||
type: "Integer", | ||
validations: { | ||
numericality: { | ||
greater_than: 0, | ||
if: o => o.maxRedemptions != null | ||
} | ||
} | ||
}, | ||
redeemBy: { | ||
@@ -241,8 +176,4 @@ remoteName: "redeem_by", | ||
// Contains names of custom validation methods | ||
static validate = ["amountOrPercent", "futureRedeemBy"]; | ||
// This method is called when you use full Loco framework | ||
// and emit signals from the server to the whole class of objects | ||
static receivedSignal(signal, data) {} | ||
// It contains names of custom validation methods | ||
static validate = ["futureRedeemBy"]; | ||
@@ -253,54 +184,2 @@ constructor(data = {}) { | ||
get amountOffNum() { | ||
return Number(this.amountOff); | ||
} | ||
// This method is called when you use full Loco framework | ||
// and emit signals from the server to this specific instance of model | ||
receivedSignal(signal, data) {} | ||
// Custom method that is called when user changes value of field | ||
// in React component | ||
setAttribute(name, val) { | ||
// This Loco-JS-Model's method makes a convertion to given type, | ||
// when assigning value to attribute | ||
this.assignAttr(name, val); | ||
this.normalizeAttributes(); | ||
} | ||
// private | ||
normalizeAttributes() { | ||
this.percentOff = nullIfNaN(this.percentOff); | ||
this.maxRedemptions = nullIfNaN(this.maxRedemptions); | ||
this.durationInMonths = nullIfNaN(this.durationInMonths); | ||
this.normalizeAmountOff(); | ||
} | ||
normalizeAmountOff() { | ||
if (Number.isNaN(this.amountOffNum)) this.amountOff = null; | ||
if (!this.amountOff) return; | ||
this.amountOff = decimalize(this.amountOff); | ||
} | ||
amountOrPercent() { | ||
if (this.percentOff === 0 && this.amountOffNum === 0) { | ||
// This Loco-JS-Model's method allows you | ||
// to assign error message to given attribute | ||
this.addErrorMessage('can\'t be 0 if "Percent off" is 0', { | ||
for: "amountOff" | ||
}); | ||
this.addErrorMessage('can\'t be 0 if "Amount off" is 0', { | ||
for: "percentOff" | ||
}); | ||
} else if (this.percentOff !== 0 && this.amountOffNum !== 0) { | ||
this.addErrorMessage('should be 0 if "Percent off" is not 0', { | ||
for: "amountOff" | ||
}); | ||
this.addErrorMessage('should be 0 if "Amount off" is not 0', { | ||
for: "percentOff" | ||
}); | ||
} | ||
} | ||
futureRedeemBy() { | ||
@@ -317,27 +196,11 @@ if (this.redeemBy === null) return; | ||
```javascript | ||
// helpers/number.js | ||
export const decimalize = val => { | ||
const integer = parseInt(String(val).split(".")[0], 10); | ||
const precision = String(val).split(".")[1]; | ||
if (!precision) return val; | ||
if (precision.length > 2) { | ||
return `${integer}.${precision.substring(0, 2)}`; | ||
} | ||
return val; | ||
}; | ||
export const nullIfNaN = val => (Number.isNaN(val) ? null : val); | ||
``` | ||
## Fetching a collection of resources 👨👩👧👦 | ||
### Specifying scope 🔎 | ||
### Specifying a scope 🔎 | ||
You can fetch resources from given scope in 3 ways: | ||
You can fetch resources from a given scope in 3 ways: | ||
* by specifying scope in method calls e.g. `Coupon.get("all", {resource: "admin"})` | ||
* setting up default scope on configuration stage _(see Configuration)_ | ||
* if you use **Loco-JS** you can set scope by calling `setScope "<scope name>"` controller's instance method. It's done in a namespace controller most often. | ||
* by specifying a scope as `resource` in method calls e.g. `Coupon.get("all", {resource: "admin"})` | ||
* setting up default scope at the configuration stage _(see Configuration)_ | ||
* if you use **Loco-JS** you can set scope by calling `setScope("<scope name>")` controller's instance method. It's done in a namespace controller most often. | ||
@@ -358,4 +221,2 @@ ### Response formats 𝌮 | ||
"duration":"once", | ||
"duration_in_months":null, | ||
"max_redemptions":1, | ||
"percent_off":null, | ||
@@ -370,3 +231,3 @@ "redeem_by":null, | ||
To fetch all resources you have to specify total number of records by using _total_ or _count_ keys. | ||
To fetch all resources, you have to specify a total number of records by using _total_ or _count_ keys. | ||
@@ -392,4 +253,2 @@ ```javascript | ||
"duration":"once", | ||
"duration_in_months":null, | ||
"max_redemptions":1, | ||
"percent_off":null, | ||
@@ -406,3 +265,3 @@ "redeem_by":null, | ||
To fetch all resources you don't have to specify total number of records in this case, because API does it already. | ||
To fetch all resources, you don't have to specify a total number of records in this case, because API does it already. | ||
@@ -421,3 +280,3 @@ ```javascript | ||
Just pass the name of the endpoint instead of _"all"_. | ||
Just pass the name of the endpoint instead of _"all"_. | ||
This example also contains how to pass **additional parameters** to the request. | ||
@@ -438,6 +297,6 @@ | ||
Coupon.get("recent", { | ||
resource: "admin", | ||
planId: 6, | ||
total: 414, | ||
page: 4, | ||
resource: "admin", | ||
planId: 6, | ||
total: 414, | ||
page: 4, | ||
foo: 10 | ||
@@ -450,3 +309,3 @@ }).then(coupons => {}); | ||
Loco-JS-Model provides `find` static method for fetching a single resource. The response from the server should be in a plain JSON format with remote names of attributes as keys. | ||
Loco-JS-Model provides `find` static method for fetching a single resource. The server's response should be in a plain JSON format with remote names of attributes as keys. | ||
@@ -462,3 +321,3 @@ ```javascript | ||
// You can also specify a resource and pass additional params | ||
// You can also specify a resource and pass additional params | ||
@@ -492,6 +351,6 @@ Coupon.find({id: 25, resource: "admin", planId: 8, foo: 12, bar: "baz"}).then(coupon => {}); | ||
const coupon = new Coupon; | ||
coupon.isValid(); // false | ||
coupon.isValid(); // false | ||
coupon.isInvalid(); // true | ||
coupon.errors; // { | ||
// stripeId: ["can't be blank", "is not included in the list"], | ||
// stripeId: ["can't be blank", "is not included in the list"], | ||
// duration: ["can't be blank", "is invalid"] | ||
@@ -501,8 +360,8 @@ // } | ||
Loco-JS-Model implements almost all built-in [Rails](http://guides.rubyonrails.org/active_record_validations.html) validators, except of _uniqueness_. And you can use them nearly identically. | ||
You can also look at [source code](https://github.com/locoframework/loco-js-model/tree/master/src/validators) if you are looking for all available options. They are pretty straightforward to decipher. | ||
Loco-JS-Model implements almost all built-in [Rails](http://guides.rubyonrails.org/active_record_validations.html) validators, except for _uniqueness_. And you can use them nearly identically. | ||
You can also look at [source code](https://github.com/locoframework/loco-js-model/tree/master/src/validators) if you are looking for all available configuration options. They are pretty straightforward to decipher. | ||
## Saving ✍️ | ||
Loco-JS-Model provides `save` method that facilitates persisting resources on the server. This method requires responses in specific JSON format. I recommend to use the format below, but if you don't plan to use `UI.Form` from **Loco-JS** for handling forms, the only requirement is specified format of **errors** key to have errors assigned to the object. | ||
Loco-JS-Model provides the `save` method that facilitates persisting resources on the server. This method requires responses in a specific JSON format. I recommend using the format below. But if you don't plan to use `UI.Form` from **Loco-JS-UI** for handling forms, the only requirement is a specified format of the **errors** key to having errors assigned to the object. | ||
@@ -518,15 +377,14 @@ ```javascript | ||
// Parameters: { "coupon" => { "stripe_id"=>nil, "percent_off"=>50, "amount_off"=>nil, | ||
// "duration"=>nil, "duration_in_months"=>nil, | ||
// "max_redemptions"=>nil, "redeem_by"=>nil | ||
// "duration"=>nil, "redeem_by"=>nil | ||
// }, | ||
// "plan_id" => "19" | ||
// } | ||
resp; // { success: false, | ||
// status: 400, | ||
resp; // { success: false, | ||
// status: 400, | ||
// errors: { | ||
// stripe_id: ["can't be blank", "is invalid"], | ||
// stripe_id: ["can't be blank", "is invalid"], | ||
// duration: ["can't be blank", "is not included in the list"] | ||
// } | ||
// } | ||
coupon.errors; // { stripeId: ["can't be blank", "is not included in the list"], | ||
coupon.errors; // { stripeId: ["can't be blank", "is not included in the list"], | ||
// duration: ["can't be blank", "is invalid"] | ||
@@ -547,3 +405,4 @@ // } | ||
// change percent_off and duration on the server and after some time ... | ||
// change percent_off and duration on the server | ||
// after some time ... | ||
@@ -561,9 +420,9 @@ setTimeout(() => { | ||
This feature looks like a pure magic when you look at how this works for the first time. | ||
This feature looks like pure magic when you look at how this works for the first time. | ||
_Dirty object_ is an ability of model instances to express how attribute values have been changed between 2 moments in time - when an object was initialized and their current value on the server. | ||
The _Dirty object_ is an ability of model instances to express how attribute values have been changed between 2 moments in time - when an object was initialized and their current value on the server. | ||
It is especially useful when you use `Connectivity` features from Loco-JS. | ||
Just look at the example below and bare in mind the order of things 💥 | ||
Just look at the example below and bear in mind the order of things 💥 | ||
@@ -574,6 +433,6 @@ ```javascript | ||
coupon; // Coupon { ... id: 25, duration: "once", percentOff: 30 } | ||
// IN THE 3RD ORDER | ||
setTimeout(() => { | ||
coupon.changes(); // { percentOff: { is: "forever", was: "once" }, | ||
coupon.changes(); // { percentOff: { is: "forever", was: "once" }, | ||
// duration: { is: 50, was: 30 } | ||
@@ -597,5 +456,13 @@ // } | ||
## Useful methods 🔧 | ||
### `Models.Base` instance methods | ||
* `assignAttr(name, val)` - it converts `val` to a given type defined in `attributes` property before assigning | ||
* `clone` - it clones and returns a model instance. | ||
# 🇵🇱 i18n | ||
Loco-JS-Model supports internationalization. Following example shows how to display errors in a different language. | ||
Loco-JS-Model supports internationalization. The following example shows how to display errors in a different language. | ||
@@ -623,3 +490,3 @@ First, create a translation of the [base English file](https://github.com/locoframework/loco-js-model/blob/master/src/locales/en.coffee). | ||
Loco-JS-Model must have all translations assigned to `I18n` object. | ||
Loco-JS-Model must have all translations assigned to the `I18n` object. | ||
@@ -642,3 +509,3 @@ ```javascript | ||
coupon.isValid(); // false | ||
coupon.errors; // { duration: ["nie może być puste", "nie jest na liście dopuszczalnych wartości"] | ||
coupon.errors; // { duration: ["nie może być puste", "nie jest na liście dopuszczalnych wartości"] | ||
// stripeId: ["nie może być puste", "jest nieprawidłowe"] | ||
@@ -650,8 +517,10 @@ // } | ||
Like it's been said at the beginning, Loco-JS-Model has been extracted from Loco-JS. And Loco-JS is a front-end part of the whole Loco framework along with Loco-Rails. | ||
Both Loco-JS and Loco-Rails are pretty well tested. And because they work in cooperation with each other, they must be tested as one library (Loco-Rails has a suite of integration / _"end to end"_ tests). | ||
```bash | ||
$ npm run test | ||
``` | ||
So every change made to Loco-JS-Model must be tested with Loco-JS' unit tests and then together as Loco framework it must be tested against Loco-Rails' integration test suite. | ||
Loco-JS-Model has been extracted from Loco-JS. Loco-JS is a front-end part of the whole Loco framework, along with Loco-Rails (the back-end part). | ||
Both Loco-JS and Loco-Rails are pretty well tested. And because they work in cooperation with each other, they must be tested as one library (Loco-Rails has a suite of integration / _"end to end"_ tests). | ||
Future changes will also be tested with local unit tests, using [Jest](https://facebook.github.io/jest/) probably. | ||
So every change made to Loco-JS-Model must be tested with Loco-JS' unit tests and then together as Loco framework, it must be tested against Loco-Rails' integration test suite. | ||
@@ -662,2 +531,6 @@ # 📈 Changelog | ||
### 1.0 _(2020-05-19)_ | ||
* Breaking changes: `Base` is no longer exported. You must use `Models.Base` | ||
### 0.3.1 | ||
@@ -664,0 +537,0 @@ |
@@ -1,13 +0,7 @@ | ||
import Base from "./base.coffee"; | ||
import IdentityMap from "./identity_map.coffee"; | ||
import BaseValidator from "./validators/base.coffee"; | ||
import Validators from "./validators"; | ||
import Config from "./config"; | ||
import I18n from "./i18n"; | ||
import IdentityMap from "./IdentityMap"; | ||
import Models from "./models"; | ||
import Validators from "./validators"; | ||
Validators.Base = BaseValidator; | ||
Models.Base = Base; | ||
export { Base, Config, I18n, IdentityMap, Models, Validators }; | ||
export { Config, I18n, IdentityMap, Models, Validators }; |
@@ -1,3 +0,9 @@ | ||
const Models = {}; | ||
import Base from "./base.coffee"; | ||
Base.prototype.clone = function() { | ||
return new this.constructor({ ...this.attributes() }); | ||
}; | ||
const Models = { Base }; | ||
export default Models; |
import Absence from "./validators/absence.coffee"; | ||
import Base from "./validators/base.coffee"; | ||
import Confirmation from "./validators/confirmation.coffee"; | ||
@@ -13,2 +14,3 @@ import Exclusion from "./validators/exclusion.coffee"; | ||
Absence, | ||
Base, | ||
Confirmation, | ||
@@ -15,0 +17,0 @@ Exclusion, |
@@ -1,4 +0,7 @@ | ||
var path = require("path"); | ||
/* eslint-env node */ | ||
const path = require("path"); | ||
module.exports = { | ||
mode: "production", | ||
entry: "./src/index.js", | ||
@@ -15,3 +18,3 @@ module: { | ||
transpile: { | ||
presets: ["env"] | ||
presets: ["@babel/preset-env"] | ||
} | ||
@@ -18,0 +21,0 @@ } |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
107685
46
1432
0
514