From 44c8a8665817769b72ba1d2d61586a6d3324d467 Mon Sep 17 00:00:00 2001 From: "D. C. Rockwell" Date: Sun, 28 Jun 2015 04:59:21 -0600 Subject: [PATCH 01/15] Initial checkin of changes after generator-oss-component generation --- .editorconfig | 12 + .eslintrc | 175 +++++ .gitignore | 27 + .jshintrc | 17 + .karma.conf.js | 93 +++ .sauce.json | 66 ++ .travis.yml | 16 + LICENSE | 22 + README.md | 82 ++ es5/lib/dovima.js | 28 + es5/spec/dovima.spec.js | 19 + es6/lib/collection.js | 133 ++++ es6/lib/dovima.js | 977 +++++++++++++++++++++++ es6/lib/model.js | 977 +++++++++++++++++++++++ es6/lib/modelFinder.js | 134 ++++ es6/spec/dovima.spec.js | 13 + es6/spec/model.spec.js | 1635 +++++++++++++++++++++++++++++++++++++++ gulpfile.babel.js | 6 + index.js | 1 + lcov.info | 44 ++ package.json | 50 ++ paths.json | 15 + tasks/build-lib.js | 10 + tasks/build-spec.js | 10 + tasks/build.js | 3 + tasks/test-browsers.js | 24 + tasks/test-local.js | 20 + tasks/test.js | 3 + 28 files changed, 4612 insertions(+) create mode 100644 .editorconfig create mode 100644 .eslintrc create mode 100644 .gitignore create mode 100644 .jshintrc create mode 100644 .karma.conf.js create mode 100644 .sauce.json create mode 100644 .travis.yml create mode 100644 LICENSE create mode 100644 README.md create mode 100644 es5/lib/dovima.js create mode 100644 es5/spec/dovima.spec.js create mode 100644 es6/lib/collection.js create mode 100644 es6/lib/dovima.js create mode 100644 es6/lib/model.js create mode 100644 es6/lib/modelFinder.js create mode 100644 es6/spec/dovima.spec.js create mode 100644 es6/spec/model.spec.js create mode 100644 gulpfile.babel.js create mode 100644 index.js create mode 100644 lcov.info create mode 100644 package.json create mode 100644 paths.json create mode 100644 tasks/build-lib.js create mode 100644 tasks/build-spec.js create mode 100644 tasks/build.js create mode 100644 tasks/test-browsers.js create mode 100644 tasks/test-local.js create mode 100644 tasks/test.js diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..10ab7cd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# http://editorconfig.org +root = true + +[*] +indent_style = tab +indent_size = 4 +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc b/.eslintrc new file mode 100644 index 0000000..a79eef4 --- /dev/null +++ b/.eslintrc @@ -0,0 +1,175 @@ +{ + "ecmaFeatures": { + "modules": true + }, + "parser": "babel-eslint", + "env": { + "browser": false, + "node": true, + "amd": false, + "mocha": true, + "jasmine": false, + "es6": true + }, + + "rules": { + "no-alert": 2, + "no-array-constructor": 2, + "no-bitwise": 0, + "no-caller": 2, + "no-catch-shadow": 2, + "no-comma-dangle": 0, + "no-cond-assign": 2, + "no-console": 2, + "no-constant-condition": 2, + "no-continue": 0, + "no-control-regex": 2, + "no-debugger": 2, + "no-delete-var": 2, + "no-div-regex": 0, + "no-dupe-keys": 2, + "no-dupe-args": 2, + "no-duplicate-case": 2, + "no-else-return": 0, + "no-empty": 2, + "no-empty-class": 2, + "no-empty-label": 2, + "no-eq-null": 0, + "no-eval": 2, + "no-ex-assign": 2, + "no-extend-native": 2, + "no-extra-bind": 2, + "no-extra-boolean-cast": 2, + "no-extra-parens": 0, + "no-extra-semi": 2, + "no-extra-strict": 2, + "no-fallthrough": 2, + "no-floating-decimal": 0, + "no-func-assign": 2, + "no-implied-eval": 2, + "no-inline-comments": 0, + "no-inner-declarations": [2, "functions"], + "no-invalid-regexp": 2, + "no-irregular-whitespace": 2, + "no-iterator": 2, + "no-label-var": 2, + "no-labels": 2, + "no-lone-blocks": 2, + "no-lonely-if": 0, + "no-loop-func": 2, + "no-mixed-requires": [0, false], + "no-mixed-spaces-and-tabs": [2, false], + "no-multi-spaces": 2, + "no-multi-str": 2, + "no-multiple-empty-lines": [0, {"max": 2}], + "no-native-reassign": 2, + "no-negated-in-lhs": 2, + "no-nested-ternary": 0, + "no-new": 2, + "no-new-func": 2, + "no-new-object": 2, + "no-new-require": 0, + "no-new-wrappers": 2, + "no-obj-calls": 2, + "no-octal": 2, + "no-octal-escape": 2, + "no-param-reassign": 0, + "no-path-concat": 0, + "no-plusplus": 0, + "no-process-env": 0, + "no-process-exit": 2, + "no-proto": 2, + "no-redeclare": 2, + "no-regex-spaces": 2, + "no-reserved-keys": 0, + "no-restricted-modules": 0, + "no-return-assign": 2, + "no-script-url": 2, + "no-self-compare": 0, + "no-sequences": 2, + "no-shadow": 2, + "no-shadow-restricted-names": 2, + "no-space-before-semi": 0, + "no-spaced-func": 2, + "no-sparse-arrays": 2, + "no-sync": 0, + "no-ternary": 0, + "no-trailing-spaces": 1, + "no-throw-literal": 0, + "no-undef": 2, + "no-undef-init": 2, + "no-undefined": 0, + "no-underscore-dangle": 0, + "no-unreachable": 2, + "no-unused-expressions": 0, + "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], + "no-use-before-define": 0, + "no-void": 0, + "no-var": 0, + "no-warning-comments": [0, { "terms": ["todo", "fixme", "xxx"], "location": "start" }], + "no-with": 2, + "no-wrap-func": 2, + + "block-scoped-var": 0, + "brace-style": [0, "1tbs"], + "camelcase": 2, + "comma-dangle": [2, "never"], + "comma-spacing": 2, + "comma-style": 0, + "complexity": [0, 11], + "consistent-return": 2, + "consistent-this": [0, "that"], + "curly": [2, "all"], + "default-case": 0, + "dot-notation": [2, { "allowKeywords": true }], + "eol-last": 2, + "eqeqeq": 2, + "func-names": 0, + "func-style": [0, "declaration"], + "generator-star": 0, + "generator-star-spacing": 0, + "global-strict": [2, "never"], + "guard-for-in": 0, + "handle-callback-err": 0, + "indent": 0, + "key-spacing": [2, { "beforeColon": false, "afterColon": true }], + "max-depth": [0, 4], + "max-len": [0, 80, 4], + "max-nested-callbacks": [0, 2], + "max-params": [0, 3], + "max-statements": [0, 10], + "new-cap": 2, + "new-parens": 2, + "newline-after-var": 0, + "object-shorthand": 0, + "one-var": 0, + "operator-assignment": [0, "always"], + "operator-linebreak": 0, + "padded-blocks": 0, + "quote-props": 0, + "quotes": [1, "double"], + "radix": 0, + "semi": 2, + "semi-spacing": [2, {"before": false, "after": true}], + "sort-vars": 0, + "space-after-function-name": [0, "never"], + "space-after-keywords": [0, "always"], + "space-before-blocks": [0, "always"], + "space-before-function-paren": [0, "always"], + "space-before-function-parentheses": [0, "always"], + "space-in-brackets": [0, "never"], + "space-in-parens": [0, "never"], + "space-infix-ops": 2, + "space-return-throw-case": 2, + "space-unary-ops": [2, { "words": true, "nonwords": false }], + "spaced-line-comment": [0, "always"], + "strict": 2, + "use-isnan": 2, + "valid-jsdoc": 0, + "valid-typeof": 2, + "vars-on-top": 0, + "wrap-iife": 0, + "wrap-regex": 0, + "yoda": [2, "never"] + } +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..123ae94 --- /dev/null +++ b/.gitignore @@ -0,0 +1,27 @@ +# Logs +logs +*.log + +# Runtime data +pids +*.pid +*.seed + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (http://nodejs.org/api/addons.html) +build/Release + +# Dependency directory +# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git +node_modules diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..10953ac --- /dev/null +++ b/.jshintrc @@ -0,0 +1,17 @@ +{ + "node": true, + "esnext": true, + "bitwise": true, + "camelcase": true, + "curly": true, + "eqeqeq": true, + "immed": true, + "indent": 2, + "latedef": true, + "newcap": true, + "noarg": true, + "quotmark": "double", + "undef": true, + "unused": true, + "strict": true +} diff --git a/.karma.conf.js b/.karma.conf.js new file mode 100644 index 0000000..c4985e5 --- /dev/null +++ b/.karma.conf.js @@ -0,0 +1,93 @@ +let frameworks = ["browserify", "mocha", "chai"]; + +let browsers = []; + +let reporters = ["progress"]; + +let configOptions = { + // base path that will be used to resolve all patterns (eg. files, exclude) + basePath: "", + + // frameworks to use + // available frameworks: https://npmjs.org/browse/keyword/karma-adapter + frameworks: frameworks, + + // list of files / patterns to load in the browser + files: [ + "node_modules/babel/polyfill.js", + "es5/**/*.js" + ], + + // list of files to exclude + exclude: [ + ], + + // preprocess matching files before serving them to the browser + // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor + preprocessors: { + "node_modules/babel/polyfill.js": ["browserify"], + "es5/**/*.js": ["browserify"] + }, + + browserify: { + debug: false + }, + + // test results reporter to use + // possible values: "dots", "progress" + // available reporters: https://npmjs.org/browse/keyword/karma-reporter + reporters: reporters, + + + // web server port + port: 9876, + + + // enable / disable colors in the output (reporters and logs) + colors: true, + + + // enable / disable watching file and executing tests whenever any file changes + autoWatch: false, + + // start these browsers + // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher + browsers: browsers, // "Chrome", "Firefox", "Safari", "Opera", "IE"], + + captureTimeout: 120000, + + // Continuous Integration mode + // if true, Karma captures browsers, runs the tests and exits + singleRun: true +}; + +/** + * Special Options For SauceLabs + */ +if (process.env.TRAVIS_BUILD_NUMBER && process.env.SAUCE_USERNAME) { + /** + * If SauceLabs credentials are available, + * set up the tests to run through them. + */ + configOptions.sauceLabs = { + testName: "Dovima.js" + }; + const customLaunchers = require("./.sauce.json").platforms; + configOptions.customLaunchers = customLaunchers; + configOptions.browsers = Object.keys(customLaunchers); + reporters.push("saucelabs"); +} else { + /** + * If there are no SauceLabs credentials available, + * detect the browsers that we *can* use. + */ + frameworks.push("detectBrowsers"); +} + +module.exports = function(config) { + // level of logging + // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG + configOptions.logLevel = config.LOG_DEBUG; + + config.set(configOptions); +}; diff --git a/.sauce.json b/.sauce.json new file mode 100644 index 0000000..0e92dd3 --- /dev/null +++ b/.sauce.json @@ -0,0 +1,66 @@ +{ + "platforms": { + "sl_chrome_43": { + "base": "SauceLabs", + "browserName": "chrome", + "version": "43" + }, + "sl_chrome_42": { + "base": "SauceLabs", + "browserName": "chrome", + "version": "42" + }, + "sl_chrome_36": { + "base": "SauceLabs", + "browserName": "chrome", + "version": "36" + }, + "sl_chrome_31": { + "base": "SauceLabs", + "browserName": "chrome", + "version": "31" + }, + + "sl_firefox_37": { + "base": "SauceLabs", + "browserName": "firefox", + "version": "37" + }, + "sl_firefox_38": { + "base": "SauceLabs", + "browserName": "firefox", + "version": "38" + }, + + "sl_win_7_ie_9": { + "base": "SauceLabs", + "browserName": "internet explorer", + "version": "9", + "platform": "Windows 7" + }, + "sl_win_7_ie_10": { + "base": "SauceLabs", + "browserName": "internet explorer", + "version": "10", + "platform": "Windows 7" + }, + "sl_win_7_ie_11": { + "base": "SauceLabs", + "browserName": "internet explorer", + "version": "11", + "platform": "Windows 7" + }, + "sl_win_8_ie_10": { + "base": "SauceLabs", + "browserName": "internet explorer", + "version": "10", + "platform": "Windows 8" + }, + "sl_win_81_ie_11": { + "base": "SauceLabs", + "browserName": "internet explorer", + "version": "11", + "platform": "Windows 8.1" + } + } +} diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6a65055 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +language: node_js +node_js: + - '0.12' + - '0.11' + - '0.10' + - iojs-v1 + - iojs-v2 +script: npm test +before_script: + - npm install +after_success: + - ./node_modules/coveralls/bin/coveralls.js < ./lcov.info +env: + global: + - secure: V20j/DfVZvCrNpIs6NiZPoGM89FoB8Ek+BnVA9InAXjJpnSB2uhdzzLjpHUF6viiPMUi24HxzYtIHneSAxv6NM1q6smNW2vwV6CwX/VEy2M4mXl70CCBrVWHxUPgxwnUohujyhmnQZE6PMkg0UEsLZerxAzD2zVmmmjikvY5dJjSTMrJHXK06cPgceZ+UFft2AAqUYqujTclx/WlXLNxgJ4JNXUZojNNXwPnAVSj7LKYpFOaKg9gNc3NgIJzzUrMu7sP+0c9wHqJjzcSX4csFY5hdsvxqc3p80GA2RsWUo/riaFODVs8aqpLqRfOgjvJzeOXDCCcfwjwcz2YJgObCnodMbkRRveuP2kVTCvo30Uf8D7D/KYft0wFPgyC5uKWxmEm+8DqzMmQ6K6p5YOtVPPad/GBc+3yijHDmKgnWpCyy3TSzB2H4EJ9KR0o2a5HnmcmqG212FuhfhgKJDKP7cs++BPpA9pmdH9LUViuQqKvwKi7kAUnHlMwo/shDV45Mhs3I4FXX7GPQTkLu6PQrYAUtTGYHTgpNtdn0r1tZ8doubE0Ow0NgWiujs8gBSRAuSu3N+VygiaS1W/s9anZpNSmjK1FQbtFNd4SULcWreu5O7JbKEAvVDdMGaRHwixSM8MHRpBhDs2Umq9YC6w6rJ7ibLmtByZVJsVEXVzRggY= + - secure: tqb/M1+qSiClaQzCmss9PEmGVvrHmE/DhaX3GJ6FFPGmb+zu2gvCp+ukr6CCiGktgcolJZX3j65/L5tLMYqdhnDawv4wtonmkhG1JT9xZzf/1cgiWB+WBe8NSkxv0v1JYwzrYQ4uA6F/RizL0G8WOjJyxL6Uf6Zlh62SL4TVtHRvZE+QMOzIhXyws3Nt18n+z6EnS5ITtdhsuxUeO2v9kMfgXKhbMA5UKYUnUMAwEzZqcxjXNe8LAcMIcAg4FTPTkve05yvMDlusmmu9v9jyM/VAjNorz/N8TRkWmjgiQDlYk8/6EoyFVhMoj8FggRXETITo6T1/tSIJ4TyvlroYQVTczGGSuyTF/IJQbm84FIkqhaLf9eQjFvIim3pRpEsn42ATgJeaMjzER5wsAn+/Nb89cbC0zSkQjjwuhRNK6oDtKdkm4YO96OAOUlur2QCjlfSVZrayE3L5u6wAnlXhY9jzXWDBcirT1HdY7fsO3/0147+MlakZTJJrc5MRV5jazySb07WgRG/GZLidfQOEShJhA+1SgmuI44qdjkzrn5wP4b2u3PckNQGdugFutoaJ0xy66My54myocDlPyi3dNbrnW/5Ur9XT5ucBh7DtOxUTjjP8eqAj9vNIT4oViaSq9F55GlIkz33pdVwVB9pD6WjvB9knLhPaolzru/hwuRs= diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..17debf0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Free All Media, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..c7f8747 --- /dev/null +++ b/README.md @@ -0,0 +1,82 @@ +# Dovima.js [![npm version](https://img.shields.io/npm/v/dovima.svg)](https://www.npmjs.com/package/dovima) [![license type](https://img.shields.io/npm/l/dovima.svg)](https://github.com/FreeAllMedia/dovima.git/blob/master/LICENSE) [![npm downloads](https://img.shields.io/npm/dm/dovima.svg)](https://www.npmjs.com/package/dovima) ![ECMAScript 6 & 5](https://img.shields.io/badge/ECMAScript-6%20/%205-red.svg) + +ES6 generic model with lots of useful special features. + +```javascript +import Dovima from "dovima"; + +const dovima = new Dovima; +dovima.saySomething(); // will output "Something" +``` + +# Quality and Compatibility + +[![Build Status](https://travis-ci.org/FreeAllMedia/dovima.png?branch=master)](https://travis-ci.org/FreeAllMedia/dovima) [![Code Climate](https://codeclimate.com/github/FreeAllMedia/dovima/badges/gpa.svg)](https://codeclimate.com/github/FreeAllMedia/dovima) [![Dependency Status](https://david-dm.org/FreeAllMedia/dovima.png?theme=shields.io)](https://david-dm.org/FreeAllMedia/dovima?theme=shields.io) [![Dev Dependency Status](https://david-dm.org/FreeAllMedia/dovima/dev-status.svg)](https://david-dm.org/FreeAllMedia/dovima?theme=shields.io#info=devDependencies) + +*Every build and release is automatically tested on the following platforms:* + +![node 0.12.x](https://img.shields.io/badge/node-0.12.x-brightgreen.svg) ![node 0.11.x](https://img.shields.io/badge/node-0.11.x-brightgreen.svg) ![node 0.10.x](https://img.shields.io/badge/node-0.10.x-brightgreen.svg) +![iojs 2.x.x](https://img.shields.io/badge/iojs-2.x.x-brightgreen.svg) ![iojs 1.x.x](https://img.shields.io/badge/iojs-1.x.x-brightgreen.svg) + + +[![Sauce Test Status](https://saucelabs.com/browser-matrix/dovima.svg)](https://saucelabs.com/u/dovima) + + +*If your platform is not listed above, you can test your local environment for compatibility by copying and pasting the following commands into your terminal:* + +``` +npm install dovima +cd node_modules/dovima +gulp test-local +``` + +# Installation + +Copy and paste the following command into your terminal to install Dovima: + +``` +npm install dovima --save +``` + +## Import / Require + +``` +// ES6 +import dovima from "dovima"; +``` + +``` +// ES5 +var dovima = require("dovima"); +``` + +``` +// Require.js +define(["require"] , function (require) { + var dovima = require("dovima"); +}); +``` + +# Getting Started + +## More insights + +In order to say something, you should know that `dovima()` ... (add your test here) + +# How to Contribute + +See something that could use improvement? Have a great feature idea? We listen! + +You can submit your ideas through our [issues system](https://github.com/FreeAllMedia/dovima/issues), or make the modifications yourself and submit them to us in the form of a [GitHub pull request](https://help.github.com/articles/using-pull-requests/). + +We always aim to be friendly and helpful. + +## Running Tests + +It's easy to run the test suite locally, and *highly recommended* if you're using Dovima.js on a platform we aren't automatically testing for. + +``` +npm test +``` + + diff --git a/es5/lib/dovima.js b/es5/lib/dovima.js new file mode 100644 index 0000000..4b34cb8 --- /dev/null +++ b/es5/lib/dovima.js @@ -0,0 +1,28 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var Dovima = (function () { + function Dovima() { + _classCallCheck(this, Dovima); + } + + _createClass(Dovima, [{ + key: "saySomething", + value: function saySomething() { + //do your stuff here + return "Something"; + } + }]); + + return Dovima; +})(); + +exports["default"] = Dovima; +module.exports = exports["default"]; \ No newline at end of file diff --git a/es5/spec/dovima.spec.js b/es5/spec/dovima.spec.js new file mode 100644 index 0000000..7012061 --- /dev/null +++ b/es5/spec/dovima.spec.js @@ -0,0 +1,19 @@ +"use strict"; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +var _libDovimaJs = require("../lib/dovima.js"); + +var _libDovimaJs2 = _interopRequireDefault(_libDovimaJs); + +describe("Dovima", function () { + var component = undefined; + + before(function () { + component = new _libDovimaJs2["default"](); + }); + + it("should say something", function () { + component.saySomething().should.equal("Something"); + }); +}); \ No newline at end of file diff --git a/es6/lib/collection.js b/es6/lib/collection.js new file mode 100644 index 0000000..0d71db3 --- /dev/null +++ b/es6/lib/collection.js @@ -0,0 +1,133 @@ +import inflect from "jargon"; + +export default class Collection extends Array { + constructor (initialData) { + let association = null, + modelConstructor = null; + + if(initialData + && (typeof initialData === "function")) { + modelConstructor = initialData; + } else { + association = initialData; + } + + Object.defineProperties(this, { + "association": { + enumerable: true, + writable: true, + value: association + }, + "_modelConstructor": { + enumerable: true, + value: modelConstructor + } + }); + } + + push(...models) { + models.forEach(model => { + if (this.indexOf(model) === -1) { + if(this.association) { + //proceed with the regular push + super.push(model); + //set inverse relationship + const pluralForeignName = inflect(this.association.foreignName).plural.toString(); + if(model.hasOwnProperty(this.association.foreignName)) { + if (model[this.association.foreignName] !== this.association.parent) { + model[this.association.foreignName] = this.association.parent; + } + } else if(model.hasOwnProperty(pluralForeignName)) { + model[pluralForeignName].push(this.association.parent); + } + //if it has through, create the intermediate model + if(this.association.through) { + //get through association + let throughAssociation = this.association.parent.associations[this.association.through]; + if(!throughAssociation) { + let modelName; + if(model && model.constructor) { + modelName = model.constructor.name; + } else { + modelName = model; + } + throw new Error(`Through association called ${this.association.through} not defined on model ${modelName}`); + } + + //lookup if there is an existing through model... how? + let throughModel = new throughAssociation.constructor(); + console.log("assigning 2", {model: model, as: this.association.foreignName, on: throughModel.constructor.name}); + throughModel[this.association.foreignName] = this.association.parent; + + // let throughAssociationPropertyName = model.associations[throughAssociationNameOnModel].foreignName; + // throughModel[this.association.foreignName] = this.association.parent; + // throughModel[throughAssociationPropertyName] = model; + //HERE I WILL NEED THE RELATIONSHIP BETWEEN MODEL and THROUGH MODEL... + //throughModel[this.association.parent.foreignName] = this.association.parent; + //this.association.p + } + } else { + if(model instanceof this._modelConstructor) { + super.push(model); + } else { + let modelName; + if(model && model.constructor) { + modelName = model.constructor.name; + } else { + modelName = model; + } + + throw TypeError(`The model ${modelName} is not an instance of ${this._modelConstructor.name}, therefore, it cannot be pushed to this collection.`); + } + } + } + }, this); + } + + fetch(callback) { + if(this.association) { + const modelFinder = new ModelFinder(this.association.constructor.database); + + const query = modelFinder + .find(this.association.constructor) + .where(this.association.foreignKey, "=", this.association.parent.id); + + function processWhereCondition(value) { + if (typeof value === "string") { + const snakeCasedValue = inflect(value).snake.toString(); + return snakeCasedValue; + } else { + return value; + } + } + + if (this.association.where) { + const processedWhereConditions = this.association.where.map(processWhereCondition); + let self = this; + query.andWhere(function () { + this.where(...processedWhereConditions); + + if (Array.isArray(self.association.andWhere)) { + self.association.andWhere.forEach((whereConditions) => { + const processedAndWhereItem = whereConditions.map(processWhereCondition); + this.andWhere(...processedAndWhereItem); + }); + } + }); + } + + query.results((error, models) => { + this.splice(0, this.length); + models.forEach((model) => { + this.push(model); + }); + callback(error); + }); + } else { + throw new Error("Cannot fetch collection without an association set. Call Model.all instead."); + } + } +} + +import ModelFinder from "./modelFinder.js"; +import Model from "./model.js"; diff --git a/es6/lib/dovima.js b/es6/lib/dovima.js new file mode 100644 index 0000000..550c7e0 --- /dev/null +++ b/es6/lib/dovima.js @@ -0,0 +1,977 @@ +const async = require("flowsync"); + +/* Component Dependencies */ +// +import MultiError from "blunder"; +import Datetime from "fleming"; +import inflect from "jargon"; + +import {ModelQuery} from "./modelFinder.js"; + +/* Private Method Symbols */ +const callDeep = Symbol(), + addAssociation = Symbol(), + getFieldAttributes = Symbol(), + parseAttributesFromFields = Symbol(), + setAttributes = Symbol(), + attributes = Symbol(), + associations = Symbol(), + properties = Symbol(), + validations = Symbol(), + isNew = Symbol(), + fetchBy = Symbol(); + +/** + * @class Model + */ + +export default class Model { + /** + * @param {Object.} [initialAttributes] Provide default values for attributes by passing a Key-Value Object. + * @constructor + */ + constructor(initialAttributes) { + [ + "_validations", + "_associations" + ].forEach((privatePropertyName) => { + Object.defineProperty(this, privatePropertyName, { + writable: true, + enumerable: false, + value: {} + }); + }); + /** + * Define dynamic properties + */ + Object.defineProperties(this, { + "_includeAssociations": { + enumerable: false, + writable: true, + value: [] + }, + + "isNew": { + get: this[isNew] + }, + + "attributes": { + get: this[attributes], + set: this[setAttributes] + }, + + "associations": { + get: this[associations] + }, + + "properties": { + get: this[properties] + }, + + "validations": { + get: this[validations] + }, + + "_tableName": { + enumerable: false, + writable: true + }, + + "tableName": { + get: () => { + return this._tableName || inflect(this.constructor.name).plural.snake.toString(); + }, + set: (newTableName) => { + this._tableName = newTableName; + } + }, + + "_primaryKey": { + enumerable: false, + writable: true + }, + + "primaryKey": { + get: () => { + return this._primaryKey || "id"; + }, + set: (newPrimaryKey) => { + this._primaryKey = newPrimaryKey; + } + } + }); + + this.associate(); + this.validate(); + + this[setAttributes](initialAttributes); + + this.initialize(); + } + + hasOne(associationName, associationConstructor) { + return this[addAssociation]({ + name: associationName, + constructor: associationConstructor, + type: "hasOne" + }); + } + + belongsTo(associationName, associationConstructor) { + return this[addAssociation]({ + name: associationName, + constructor: associationConstructor, + type: "belongsTo" + }); + } + + hasMany(associationName, associationConstructor) { + return this[addAssociation]({ + name: associationName, + constructor: associationConstructor, + type: "hasMany" + }); + } + + ensure(attributeName, validatorFunction, validatorMessage) { + this._validations[attributeName] = this._validations[attributeName] || []; + + let validatorDetails = {validator: validatorFunction}; + + if (validatorMessage) { validatorDetails.message = validatorMessage; } + + this._validations[attributeName].push(validatorDetails); + } + + /** + * Return a boolean indicating whether the model is valid or not. + * + * @method isValid + * @param {Function(boolean)} callback Callback returning the boolean. + */ + isValid(callback) { + this.invalidAttributes((invalidAttributeList) => { + callback(Object.keys(invalidAttributeList).length === 0); + }); + } + + /** + * Return an object containing all invalid attributes and their errors. + * + * @example + * ``` + * model.invalidAttributes((invalidAttributeList) => { + * console.log(invalidAttributeList); // {"name":["Cannot contain special characters", "Cannot contain numbers"], "age":["Cannot be under 18"]} + * }); + * ``` + * + * @method invalidAttributes + * @param {Function(invalidAttributeList)} callback Callback returning the invalid attribute list. + */ + invalidAttributes(callback) { + const attributeNamesWithValidators = Object.keys(this._validations); + + const compileInvalidAttributeList = (errors, validatorMessages) => { + if (errors) { + throw errors; + } else { + let invalidAttributeList = {}; + + for(var index = 0; index < attributeNamesWithValidators.length; index++){ + const invalidMessages = validatorMessages[index]; + + if (invalidMessages.length > 0) { + const attributeName = attributeNamesWithValidators[index]; + invalidAttributeList[attributeName] = invalidMessages; + } + } + + callback(invalidAttributeList); + } + }; + + const performValidationsForAttribute = (attributeName, done) => { + const attributeValidations = this._validations[attributeName]; + + const performValidation = (validation, returnValue) => { + const validator = validation.validator; + + validator.call(this, attributeName, (error, validatorDetails) => { + if (validatorDetails.result) { + returnValue(null, null); + } else { + returnValue(null, validation.message || validatorDetails.message); + } + }); + }; + + const compileValidatorResponses = (error, invalidMessages) => { + const cleanedMessages = []; + // Trick to remove falsy values from an array + for(let message of invalidMessages){ + message && cleanedMessages.push(message); + } + done(null, cleanedMessages); + }; + + async.mapParallel( + attributeValidations, + performValidation, + compileValidatorResponses + ); + }; + + async.mapSeries( + attributeNamesWithValidators, + performValidationsForAttribute, + compileInvalidAttributeList + ); + } + + include(...associationNames) { + this._includeAssociations = associationNames; + return this; + } + + fetch(...options) { + switch(options.length) { + case 0: + this[fetchBy](); + break; + case 1: + if(typeof options[0] === "function") { + this[fetchBy]([this.primaryKey], options[0]); + } else if(Array.isArray(options[0])) { + this[fetchBy](options[0]); + } else { + this[fetchBy]([options[0]]); + } + break; + case 2: + if(Array.isArray(options[0])) { + this[fetchBy](options[0], options[1]); + } else { + this[fetchBy]([options[0]], options[1]); + } + break; + } + } + + [fetchBy](fields = [this.primaryKey], callback) { + if (!this.constructor.database) { throw new Error("Cannot fetch without Model.database set."); } + + let chain = this.constructor.database + .select("*") + .from(this.tableName); + fields.forEach((field, index) => { + if (!this[field]) { throw new Error(`Cannot fetch this model by the '${field}' field because it is not set.`); } + + if(index === 0) { + chain = chain.where(field, "=", this[field]); + } else { + chain = chain.andWhere(field, "=", this[field]); + } + }, this); + + chain + .limit(1) + .results((error, records) => { + if(records.length === 0) { + callback(new Error(`There is no ${this.constructor.name} for the given (${fields.join(", ")}).`)); + } else { + this[parseAttributesFromFields](records[0]); + + if (this._includeAssociations.length > 0) { + const modelFinder = new ModelFinder(this.constructor.database); + + const associations = this.associations; + + /* We'll be putting all of our async tasks into this */ + const fetchTasks = []; + + this._includeAssociations.forEach((associationName) => { + + const association = associations[associationName]; + + if (!association) { + throw new Error(`Cannot fetch '${associationName}' because it is not a valid association on ${this.constructor.name}`); + } + + switch(association.type) { + case "hasOne": + fetchTasks.push(finished => { + + // user hasMany address + + + const ModelClass = association.constructor; + + if (association.through) { + const throughAssociation = associations[association.through]; + + //throw throughAssociation.foreignId; + //select * from Addresses where user_id = this[this.primaryKey] + //select * from PostalCodes where address_id = address.id + if (!this[this.primaryKey]) { + throw new Error(`'${this.primaryKey}' is not set on ${this.constructor.name}`); + } + + modelFinder + .find(throughAssociation.constructor) + .where(association.foreignId, "=", this[this.primaryKey]) + .limit(1) + .results((errors, models) => { + const joinModel = models[0]; + const destinationAssociation = joinModel.associations[associationName]; + + //throw destinationAssociation.foreignId; + + //throw joinModel;//throw model.associations; + //addressId + + const tempModel = new association.constructor(); + modelFinder + .find(association.constructor) + .where(tempModel.primaryKey, "=", joinModel[destinationAssociation.foreignId]) + .limit(1) + .results((associationError, associationModels) => { + const associationModel = associationModels[0]; + this[associationName] = associationModel; + finished(); + }); + }); + } else { + const query = modelFinder + .find(ModelClass) + .where(association.foreignKey, "=", this[this.primaryKey]); + + const processWhereCondition = (value) => { + if (typeof value === "string") { + const snakeCasedValue = inflect(value).snake.toString(); + return snakeCasedValue; + } else { + return value; + } + }; + + const processedWhere = association.where.map(processWhereCondition); + + query.andWhere(function () { + this.where(...processedWhere); + + if(Array.isArray(association.andWhere)) { + association.andWhere.forEach((andWhereItem) => { + const processedAndWhereItem = andWhereItem.map(processWhereCondition); + this.andWhere(...processedAndWhereItem); + }); + } + }); + + query + .limit(1) + .results((errors, models) => { + const model = models[0]; + this[associationName] = model; + finished(); + }); + } + }); + break; + + case "hasMany": + + if (association.through) { + fetchTasks.push(finished => { + + const throughAssociation = associations[association.through]; + + modelFinder + .find(throughAssociation.constructor) + .where(association.foreignId, this[this.primaryKey]) + .results((errors, models) => { + if(models.length > 0) { + const foreignAssociationName = association.as || associationName; + + if (!models[0].associations[foreignAssociationName]) { + throw new Error(`'${foreignAssociationName}' is not a valid association on through model '${throughAssociation.constructor.name}'`); + } + + const destinationAssociation = models[0].associations[foreignAssociationName]; + + let modelIds = []; + + const tempModel = new association.constructor(); + + switch(destinationAssociation.type) { + case "hasOne": + //throw {through: throughAssociation, destination: destinationAssociation}; + + modelIds = models.map(model => { return model[throughAssociation.foreignId]; }); + + modelFinder + .find(association.constructor) + .where(tempModel.primaryKey, "in", modelIds) + .results((errors, models) => { + models.forEach((model) => { + this[associationName].push(model); + }); + finished(); + }); + + break; + + case "hasMany": + modelIds = models.map(model => { return model[model.primaryKey]; }); + + modelFinder + .find(association.constructor) + .where(destinationAssociation.foreignId, "in", modelIds) + .results((errors, models) => { + models.forEach((model) => { + this[associationName].push(model); + }); + finished(); + }); + break; + case "belongsTo": + //throw {through: throughAssociation, destination: destinationAssociation}; + + //throw destinationAssociation.name; + + //throw associationName; + + //const localId = inflect(destinationAssociation.name).foreignKey.camel.toString(); + + modelIds = models.map(model => { return model[destinationAssociation.foreignId]; }); + + modelFinder + .find(association.constructor) + .where(tempModel.primaryKey, "in", modelIds) + .results((errors, models) => { + models.forEach((model) => { + this[associationName].push(model); + }); + finished(); + }); + + break; + } + + //throw {association: association.foreignName, destinationAssociation: destinationAssociation.foreignName, throughAssociation: throughAssociation.foreignName}; + //throw {association: association.foreignId, destinationAssociation: destinationAssociation.foreignId, throughAssociation: throughAssociation.foreignId}; + //throw models; + + + } + }); + + // if (!this[throughAssociation.foreignId]) { + // throw new Error(`'${throughAssociation.foreignId}' is not set on ${this.constructor.name}`); + // } + + // modelFinder + // .find(throughAssociation.constructor) + // .where("id", "=", this[throughAssociation.foreignId]) + // .limit(1) + // .results((errors, models) => { + // const joinModel = models[0]; + // const destinationAssociation = joinModel.associations[associationName]; + + // //throw joinModel;//throw model.associations; + + // modelFinder + // .find(association.constructor) + // .where("id", "=", joinModel[destinationAssociation.foreignId]) + // .results((associationError, associationModels) => { + // const associationModel = associationModels[0]; + // this[associationName] = associationModel; + // }); + + // this[associationName] = joinModel; + // finished(); + // }); + }); + } else { + fetchTasks.push(finished => { + this[associationName].fetch(finished); + }); + } + break; + + case "belongsTo": + if (!this[association.foreignId]) { + throw new Error(`Cannot fetch '${associationName}' because '${association.foreignId}' is not set on ${this.constructor.name}`); + } + + fetchTasks.push(finished => { + modelFinder + .find(association.constructor) + .where("id", "=", this[association.foreignId]) + .limit(1) + .results((errors, models) => { + const model = models[0]; + this[associationName] = model; + model[association.foreignName] = this; + finished(); + }); + }); + + } + }); + + async.parallel( + fetchTasks, + () => { + if (callback) { + callback(error, this); + } + } + ); + } else { + if (callback) { + callback(error, this); + } + } + } + }); + } + + save(callback) { + if (!this.constructor.database) { throw new Error("Cannot save without Model.database set."); } + + async.series([ + (next) => { + this.beforeValidation(next); + }, + (next) => { + this.isValid((valid) => { + if(valid) { + next(); + } else { + this.invalidAttributes((invalidAttributeList) => { + const hasInvalidAttributes = Object.keys(invalidAttributeList).length > 0; + + if (hasInvalidAttributes) { + const multiError = new MultiError(); + for(let invalidAttributeName in invalidAttributeList) { + const invalidAttributeMessages = invalidAttributeList[invalidAttributeName]; + + const errorPrefix = this.constructor.name + " is invalid"; + + const attributesMultiError = new MultiError([], errorPrefix); + for(let index in invalidAttributeMessages) { + const invalidAttributeMessage = invalidAttributeMessages[index]; + const error = new Error(`${invalidAttributeName} ${invalidAttributeMessage}`); + attributesMultiError.push(error); + } + multiError.push(attributesMultiError); + } + next(multiError); + } else { + next(); + } + }); + } + }); + }, + (next) => { + this.beforeSave(next); + }, + (next) => { + if (this.isNew) { + let now = new Datetime(); + this.createdAt = now.toDate(); + let fieldAttributes = this[getFieldAttributes](); + + this.constructor.database + .insert(fieldAttributes) + .into(this.tableName) + .results((error, ids) => { + if(error) { + next(error); + } else { + this[this.primaryKey] = ids[0]; + next(); + } + }); + } else { + let now = new Datetime(); + this.updatedAt = now.toDate(); + let attributes = this[getFieldAttributes](); + let updateAttributes = {}; + + for (let attributeName in attributes) { + if (attributeName !== "id") { + updateAttributes[attributeName] = attributes[attributeName]; + } + } + + this.constructor.database + .update(updateAttributes) + .into(this.tableName) + .where("id", "=", this[this.primaryKey]) + .results(next); + } + }, + (next) => { + this[callDeep]("save", next); + }, + (next) => { + this.afterSave(next); + } + ], + (errors) => { + if(errors) { + callback(errors); + } else { + callback(undefined, this); + } + }); + } + + /* Stubbed methods for hooks */ + beforeValidation(callback) { + callback(); + } + + beforeSave(callback) { + callback(); + } + + afterSave(callback) { + callback(); + } + + associate() {} + + validate() {} + + initialize() {} + + /** + * Private Functionality + */ + + [setAttributes](newAttributes) { + this[parseAttributesFromFields](newAttributes); + } + + [associations]() { + return this._associations; + } + + [properties]() { + return Object.keys(this); + } + + [validations]() { + return this._validations; + } + + [attributes]() { + var attributeNames = {}; + this.properties.forEach((propertyName) => { + if(!this._associations[propertyName]) { + attributeNames[propertyName] = this[propertyName]; + } + }); + return attributeNames; + } + + [isNew]() { + if (this[this.primaryKey]) { + return false; + } else { + return true; + } + } + + /** + * Call a function deeply through all associations + * + * @private + * @method callDeep + * @param {String} functionName The name of the function that you want to fire deeply. + * @param {function(errors, results)} Function called at the end of the operation. + */ + [callDeep] (methodName, callback) { + const associationNames = Object.keys(this.associations); + + async.mapParallel( + associationNames, + (associationName, next) => { + + const associationDetails = this.associations[associationName]; + + switch(associationDetails.type) { + case "hasOne": + const model = this[associationName]; + if(model) { + //pass the associationDetails.whereArgs to the function + model[methodName](next); + } else { + next(); + } + break; + + case "hasMany": + const collection = this[associationName]; + //collection set, and not many to many (nothing in that case) + if (collection && associationDetails.through === undefined) { + //let array = [].slice.call(collection); + async.eachParallel( + collection, + (collectionModel, finishSubStep) => { + collectionModel[methodName](finishSubStep); + }, + next + ); + } else { + next(); //collection not set + } + break; + + case "belongsTo": + next(); + } + }, (errors, results) => { + callback(errors, results); + } + ); + } + + /** + * @example + * ``` + * const associationDetails = { + * name: "users", + * type: "hasMany", + * constructor: User + * }; + * + * [addAssociation](associationDetails); + * ``` + * @method addAssociation + * @param {Object.} associationDetails Object containing association details in key/value pairs. + */ + [addAssociation] (associationDetails) { + const association = { + parent: this, + type: associationDetails.type, + constructor: associationDetails.constructor + }; + + /* Default Values */ + + switch(associationDetails.type) { + case "hasOne": + case "hasMany": + association.foreignKey = inflect(this.constructor.name).foreignKey.toString(); + association.foreignId = inflect(association.foreignKey).camel.toString(); + break; + case "belongsTo": + association.foreignKey = inflect(associationDetails.name).foreignKey.toString(); + association.foreignId = inflect(association.foreignKey).camel.toString(); + } + + // TODO: AS + association.foreignName = inflect(this.constructor.name).camel.toString(); + + /* Override Values */ + + const associationSetter = new AssociationSetter(association); + + /* Set private space for value to be stored internally */ + + const privateAssociationName = "_" + associationDetails.name; + + /* Association Setter By Type */ + + let setterFunction; + + switch(associationDetails.type) { + case "hasOne": + this[privateAssociationName] = null; + + setterFunction = (newModel) => { + if (newModel && this[privateAssociationName] !== newModel) { + if(!(newModel instanceof Model)) { + throw new Error("Cannot set a non model entity onto this property. It should inherit from Model"); + } + + this[privateAssociationName] = newModel; + + newModel[association.foreignName] = this; + } + }; + break; + case "belongsTo": + this[privateAssociationName] = null; + + setterFunction = (newModel) => { + if (newModel && this[privateAssociationName] !== newModel) { + if(!(newModel instanceof Model)) { + throw new Error("Cannot set a non model entity onto this property. It should inherit from Model"); + } + + this[privateAssociationName] = newModel; + this[association.foreignId] = newModel.id; + + const pluralForeignName = inflect(association.foreignName).plural.toString(); + + if (!association.ambiguous) { + if (newModel.hasOwnProperty(association.foreignName)) { + newModel[association.foreignName] = this; + } else if (newModel.hasOwnProperty(pluralForeignName)) { + //lookup is it exist and dont add it in that case + newModel[pluralForeignName].push(this); + } else { + throw new Error(`Neither "${association.foreignName}" or "${pluralForeignName}" are valid associations on "${newModel.constructor.name}"`); + } + } + + // if (newModel[association.foreignName] instanceof Collection) { + // newModel[association.foreignName].push(this); + // } else { + // newModel[association.foreignName] = this; + // } + } + }; + break; + case "hasMany": + /* Set to null by default */ + this[privateAssociationName] = new Collection(association); + + setterFunction = (newModel) => { + if (newModel && this[privateAssociationName] !== newModel) { + this[privateAssociationName] = newModel; + } + }; + break; + default: + throw new Error("Unknown association type."); + } + + Object.defineProperty( + this, + privateAssociationName, + { + enumerable: false, + writable: true + } + ); + + Object.defineProperty( + this, + associationDetails.name, + { + enumerable: true, + set: setterFunction, + get: () => { + return this[privateAssociationName]; + } + } + ); + + this[associationDetails.name] = associationDetails.value; + + this._associations[associationDetails.name] = association; + + return associationSetter; + } + + [parseAttributesFromFields](record) { + for (var field in record) { + this[inflect(field).camel.toString()] = record[field]; + } + } + + [getFieldAttributes]() { + let attributeNames = Object.keys(this.attributes); + let fieldAttributes = {}; + attributeNames.forEach((attributeName) => { + fieldAttributes[inflect(attributeName).snake.toString()] = this[attributeName]; + }); + + //add belongsTo associations and remove others + Object.keys(this.associations).forEach((associationName) => { + let relatedModel = this[associationName]; + let foreignKeyField = inflect(associationName).foreignKey.toString(); + if(this._associations[associationName].type === "belongsTo") { + //try with relatedModel and relatedModel.id + if(relatedModel && relatedModel.id) { + fieldAttributes[foreignKeyField] = relatedModel.id; + } else { + //or just with the relatedModelId + //construct the snake with _id and then camelize it + let foreignIdAsAttribute = inflect(foreignKeyField).camel.toString(); + fieldAttributes[foreignKeyField] = this[foreignIdAsAttribute]; + } + } else { + //console.log("getFieldAttributes delete on ", {on: this.constructor.name, associationName: associationName, foreignKeyField: foreignKeyField, relatedModel: relatedModel}); + delete fieldAttributes[associationName]; + delete fieldAttributes["_" + associationName]; + } + }); + + return fieldAttributes; + } +} + +const ambiguous = Symbol(); + +export class AssociationSetter { + constructor(association) { + this.association = association; + + if (association.type === "belongsTo") { + Object.defineProperties(this, { + "ambiguous": { + get: this[ambiguous] + } + }); + } + } + + foreignName(name) { + this.association.foreignName = name; + return this; + } + + where(...options) { + this.association.where = options; + return this; + } + + andWhere(...options) { + this.association.andWhere = this.association.andWhere || []; + this.association.andWhere.push(options); + return this; + } + + through(associationName) { + this.association.through = associationName; + return this; + } + + as(associationName) { + this.association.as = associationName; + return this; + } + + [ambiguous]() { + this.association.ambiguous = true; + } +} + +Object.defineProperties(Model, { + "all": { + get: function getAll() { + let modelQuery = new ModelQuery(Model.database); + return modelQuery.find(this); + } + } +}); + +import Collection from "./collection.js"; +import ModelFinder from "./modelFinder.js"; diff --git a/es6/lib/model.js b/es6/lib/model.js new file mode 100644 index 0000000..550c7e0 --- /dev/null +++ b/es6/lib/model.js @@ -0,0 +1,977 @@ +const async = require("flowsync"); + +/* Component Dependencies */ +// +import MultiError from "blunder"; +import Datetime from "fleming"; +import inflect from "jargon"; + +import {ModelQuery} from "./modelFinder.js"; + +/* Private Method Symbols */ +const callDeep = Symbol(), + addAssociation = Symbol(), + getFieldAttributes = Symbol(), + parseAttributesFromFields = Symbol(), + setAttributes = Symbol(), + attributes = Symbol(), + associations = Symbol(), + properties = Symbol(), + validations = Symbol(), + isNew = Symbol(), + fetchBy = Symbol(); + +/** + * @class Model + */ + +export default class Model { + /** + * @param {Object.} [initialAttributes] Provide default values for attributes by passing a Key-Value Object. + * @constructor + */ + constructor(initialAttributes) { + [ + "_validations", + "_associations" + ].forEach((privatePropertyName) => { + Object.defineProperty(this, privatePropertyName, { + writable: true, + enumerable: false, + value: {} + }); + }); + /** + * Define dynamic properties + */ + Object.defineProperties(this, { + "_includeAssociations": { + enumerable: false, + writable: true, + value: [] + }, + + "isNew": { + get: this[isNew] + }, + + "attributes": { + get: this[attributes], + set: this[setAttributes] + }, + + "associations": { + get: this[associations] + }, + + "properties": { + get: this[properties] + }, + + "validations": { + get: this[validations] + }, + + "_tableName": { + enumerable: false, + writable: true + }, + + "tableName": { + get: () => { + return this._tableName || inflect(this.constructor.name).plural.snake.toString(); + }, + set: (newTableName) => { + this._tableName = newTableName; + } + }, + + "_primaryKey": { + enumerable: false, + writable: true + }, + + "primaryKey": { + get: () => { + return this._primaryKey || "id"; + }, + set: (newPrimaryKey) => { + this._primaryKey = newPrimaryKey; + } + } + }); + + this.associate(); + this.validate(); + + this[setAttributes](initialAttributes); + + this.initialize(); + } + + hasOne(associationName, associationConstructor) { + return this[addAssociation]({ + name: associationName, + constructor: associationConstructor, + type: "hasOne" + }); + } + + belongsTo(associationName, associationConstructor) { + return this[addAssociation]({ + name: associationName, + constructor: associationConstructor, + type: "belongsTo" + }); + } + + hasMany(associationName, associationConstructor) { + return this[addAssociation]({ + name: associationName, + constructor: associationConstructor, + type: "hasMany" + }); + } + + ensure(attributeName, validatorFunction, validatorMessage) { + this._validations[attributeName] = this._validations[attributeName] || []; + + let validatorDetails = {validator: validatorFunction}; + + if (validatorMessage) { validatorDetails.message = validatorMessage; } + + this._validations[attributeName].push(validatorDetails); + } + + /** + * Return a boolean indicating whether the model is valid or not. + * + * @method isValid + * @param {Function(boolean)} callback Callback returning the boolean. + */ + isValid(callback) { + this.invalidAttributes((invalidAttributeList) => { + callback(Object.keys(invalidAttributeList).length === 0); + }); + } + + /** + * Return an object containing all invalid attributes and their errors. + * + * @example + * ``` + * model.invalidAttributes((invalidAttributeList) => { + * console.log(invalidAttributeList); // {"name":["Cannot contain special characters", "Cannot contain numbers"], "age":["Cannot be under 18"]} + * }); + * ``` + * + * @method invalidAttributes + * @param {Function(invalidAttributeList)} callback Callback returning the invalid attribute list. + */ + invalidAttributes(callback) { + const attributeNamesWithValidators = Object.keys(this._validations); + + const compileInvalidAttributeList = (errors, validatorMessages) => { + if (errors) { + throw errors; + } else { + let invalidAttributeList = {}; + + for(var index = 0; index < attributeNamesWithValidators.length; index++){ + const invalidMessages = validatorMessages[index]; + + if (invalidMessages.length > 0) { + const attributeName = attributeNamesWithValidators[index]; + invalidAttributeList[attributeName] = invalidMessages; + } + } + + callback(invalidAttributeList); + } + }; + + const performValidationsForAttribute = (attributeName, done) => { + const attributeValidations = this._validations[attributeName]; + + const performValidation = (validation, returnValue) => { + const validator = validation.validator; + + validator.call(this, attributeName, (error, validatorDetails) => { + if (validatorDetails.result) { + returnValue(null, null); + } else { + returnValue(null, validation.message || validatorDetails.message); + } + }); + }; + + const compileValidatorResponses = (error, invalidMessages) => { + const cleanedMessages = []; + // Trick to remove falsy values from an array + for(let message of invalidMessages){ + message && cleanedMessages.push(message); + } + done(null, cleanedMessages); + }; + + async.mapParallel( + attributeValidations, + performValidation, + compileValidatorResponses + ); + }; + + async.mapSeries( + attributeNamesWithValidators, + performValidationsForAttribute, + compileInvalidAttributeList + ); + } + + include(...associationNames) { + this._includeAssociations = associationNames; + return this; + } + + fetch(...options) { + switch(options.length) { + case 0: + this[fetchBy](); + break; + case 1: + if(typeof options[0] === "function") { + this[fetchBy]([this.primaryKey], options[0]); + } else if(Array.isArray(options[0])) { + this[fetchBy](options[0]); + } else { + this[fetchBy]([options[0]]); + } + break; + case 2: + if(Array.isArray(options[0])) { + this[fetchBy](options[0], options[1]); + } else { + this[fetchBy]([options[0]], options[1]); + } + break; + } + } + + [fetchBy](fields = [this.primaryKey], callback) { + if (!this.constructor.database) { throw new Error("Cannot fetch without Model.database set."); } + + let chain = this.constructor.database + .select("*") + .from(this.tableName); + fields.forEach((field, index) => { + if (!this[field]) { throw new Error(`Cannot fetch this model by the '${field}' field because it is not set.`); } + + if(index === 0) { + chain = chain.where(field, "=", this[field]); + } else { + chain = chain.andWhere(field, "=", this[field]); + } + }, this); + + chain + .limit(1) + .results((error, records) => { + if(records.length === 0) { + callback(new Error(`There is no ${this.constructor.name} for the given (${fields.join(", ")}).`)); + } else { + this[parseAttributesFromFields](records[0]); + + if (this._includeAssociations.length > 0) { + const modelFinder = new ModelFinder(this.constructor.database); + + const associations = this.associations; + + /* We'll be putting all of our async tasks into this */ + const fetchTasks = []; + + this._includeAssociations.forEach((associationName) => { + + const association = associations[associationName]; + + if (!association) { + throw new Error(`Cannot fetch '${associationName}' because it is not a valid association on ${this.constructor.name}`); + } + + switch(association.type) { + case "hasOne": + fetchTasks.push(finished => { + + // user hasMany address + + + const ModelClass = association.constructor; + + if (association.through) { + const throughAssociation = associations[association.through]; + + //throw throughAssociation.foreignId; + //select * from Addresses where user_id = this[this.primaryKey] + //select * from PostalCodes where address_id = address.id + if (!this[this.primaryKey]) { + throw new Error(`'${this.primaryKey}' is not set on ${this.constructor.name}`); + } + + modelFinder + .find(throughAssociation.constructor) + .where(association.foreignId, "=", this[this.primaryKey]) + .limit(1) + .results((errors, models) => { + const joinModel = models[0]; + const destinationAssociation = joinModel.associations[associationName]; + + //throw destinationAssociation.foreignId; + + //throw joinModel;//throw model.associations; + //addressId + + const tempModel = new association.constructor(); + modelFinder + .find(association.constructor) + .where(tempModel.primaryKey, "=", joinModel[destinationAssociation.foreignId]) + .limit(1) + .results((associationError, associationModels) => { + const associationModel = associationModels[0]; + this[associationName] = associationModel; + finished(); + }); + }); + } else { + const query = modelFinder + .find(ModelClass) + .where(association.foreignKey, "=", this[this.primaryKey]); + + const processWhereCondition = (value) => { + if (typeof value === "string") { + const snakeCasedValue = inflect(value).snake.toString(); + return snakeCasedValue; + } else { + return value; + } + }; + + const processedWhere = association.where.map(processWhereCondition); + + query.andWhere(function () { + this.where(...processedWhere); + + if(Array.isArray(association.andWhere)) { + association.andWhere.forEach((andWhereItem) => { + const processedAndWhereItem = andWhereItem.map(processWhereCondition); + this.andWhere(...processedAndWhereItem); + }); + } + }); + + query + .limit(1) + .results((errors, models) => { + const model = models[0]; + this[associationName] = model; + finished(); + }); + } + }); + break; + + case "hasMany": + + if (association.through) { + fetchTasks.push(finished => { + + const throughAssociation = associations[association.through]; + + modelFinder + .find(throughAssociation.constructor) + .where(association.foreignId, this[this.primaryKey]) + .results((errors, models) => { + if(models.length > 0) { + const foreignAssociationName = association.as || associationName; + + if (!models[0].associations[foreignAssociationName]) { + throw new Error(`'${foreignAssociationName}' is not a valid association on through model '${throughAssociation.constructor.name}'`); + } + + const destinationAssociation = models[0].associations[foreignAssociationName]; + + let modelIds = []; + + const tempModel = new association.constructor(); + + switch(destinationAssociation.type) { + case "hasOne": + //throw {through: throughAssociation, destination: destinationAssociation}; + + modelIds = models.map(model => { return model[throughAssociation.foreignId]; }); + + modelFinder + .find(association.constructor) + .where(tempModel.primaryKey, "in", modelIds) + .results((errors, models) => { + models.forEach((model) => { + this[associationName].push(model); + }); + finished(); + }); + + break; + + case "hasMany": + modelIds = models.map(model => { return model[model.primaryKey]; }); + + modelFinder + .find(association.constructor) + .where(destinationAssociation.foreignId, "in", modelIds) + .results((errors, models) => { + models.forEach((model) => { + this[associationName].push(model); + }); + finished(); + }); + break; + case "belongsTo": + //throw {through: throughAssociation, destination: destinationAssociation}; + + //throw destinationAssociation.name; + + //throw associationName; + + //const localId = inflect(destinationAssociation.name).foreignKey.camel.toString(); + + modelIds = models.map(model => { return model[destinationAssociation.foreignId]; }); + + modelFinder + .find(association.constructor) + .where(tempModel.primaryKey, "in", modelIds) + .results((errors, models) => { + models.forEach((model) => { + this[associationName].push(model); + }); + finished(); + }); + + break; + } + + //throw {association: association.foreignName, destinationAssociation: destinationAssociation.foreignName, throughAssociation: throughAssociation.foreignName}; + //throw {association: association.foreignId, destinationAssociation: destinationAssociation.foreignId, throughAssociation: throughAssociation.foreignId}; + //throw models; + + + } + }); + + // if (!this[throughAssociation.foreignId]) { + // throw new Error(`'${throughAssociation.foreignId}' is not set on ${this.constructor.name}`); + // } + + // modelFinder + // .find(throughAssociation.constructor) + // .where("id", "=", this[throughAssociation.foreignId]) + // .limit(1) + // .results((errors, models) => { + // const joinModel = models[0]; + // const destinationAssociation = joinModel.associations[associationName]; + + // //throw joinModel;//throw model.associations; + + // modelFinder + // .find(association.constructor) + // .where("id", "=", joinModel[destinationAssociation.foreignId]) + // .results((associationError, associationModels) => { + // const associationModel = associationModels[0]; + // this[associationName] = associationModel; + // }); + + // this[associationName] = joinModel; + // finished(); + // }); + }); + } else { + fetchTasks.push(finished => { + this[associationName].fetch(finished); + }); + } + break; + + case "belongsTo": + if (!this[association.foreignId]) { + throw new Error(`Cannot fetch '${associationName}' because '${association.foreignId}' is not set on ${this.constructor.name}`); + } + + fetchTasks.push(finished => { + modelFinder + .find(association.constructor) + .where("id", "=", this[association.foreignId]) + .limit(1) + .results((errors, models) => { + const model = models[0]; + this[associationName] = model; + model[association.foreignName] = this; + finished(); + }); + }); + + } + }); + + async.parallel( + fetchTasks, + () => { + if (callback) { + callback(error, this); + } + } + ); + } else { + if (callback) { + callback(error, this); + } + } + } + }); + } + + save(callback) { + if (!this.constructor.database) { throw new Error("Cannot save without Model.database set."); } + + async.series([ + (next) => { + this.beforeValidation(next); + }, + (next) => { + this.isValid((valid) => { + if(valid) { + next(); + } else { + this.invalidAttributes((invalidAttributeList) => { + const hasInvalidAttributes = Object.keys(invalidAttributeList).length > 0; + + if (hasInvalidAttributes) { + const multiError = new MultiError(); + for(let invalidAttributeName in invalidAttributeList) { + const invalidAttributeMessages = invalidAttributeList[invalidAttributeName]; + + const errorPrefix = this.constructor.name + " is invalid"; + + const attributesMultiError = new MultiError([], errorPrefix); + for(let index in invalidAttributeMessages) { + const invalidAttributeMessage = invalidAttributeMessages[index]; + const error = new Error(`${invalidAttributeName} ${invalidAttributeMessage}`); + attributesMultiError.push(error); + } + multiError.push(attributesMultiError); + } + next(multiError); + } else { + next(); + } + }); + } + }); + }, + (next) => { + this.beforeSave(next); + }, + (next) => { + if (this.isNew) { + let now = new Datetime(); + this.createdAt = now.toDate(); + let fieldAttributes = this[getFieldAttributes](); + + this.constructor.database + .insert(fieldAttributes) + .into(this.tableName) + .results((error, ids) => { + if(error) { + next(error); + } else { + this[this.primaryKey] = ids[0]; + next(); + } + }); + } else { + let now = new Datetime(); + this.updatedAt = now.toDate(); + let attributes = this[getFieldAttributes](); + let updateAttributes = {}; + + for (let attributeName in attributes) { + if (attributeName !== "id") { + updateAttributes[attributeName] = attributes[attributeName]; + } + } + + this.constructor.database + .update(updateAttributes) + .into(this.tableName) + .where("id", "=", this[this.primaryKey]) + .results(next); + } + }, + (next) => { + this[callDeep]("save", next); + }, + (next) => { + this.afterSave(next); + } + ], + (errors) => { + if(errors) { + callback(errors); + } else { + callback(undefined, this); + } + }); + } + + /* Stubbed methods for hooks */ + beforeValidation(callback) { + callback(); + } + + beforeSave(callback) { + callback(); + } + + afterSave(callback) { + callback(); + } + + associate() {} + + validate() {} + + initialize() {} + + /** + * Private Functionality + */ + + [setAttributes](newAttributes) { + this[parseAttributesFromFields](newAttributes); + } + + [associations]() { + return this._associations; + } + + [properties]() { + return Object.keys(this); + } + + [validations]() { + return this._validations; + } + + [attributes]() { + var attributeNames = {}; + this.properties.forEach((propertyName) => { + if(!this._associations[propertyName]) { + attributeNames[propertyName] = this[propertyName]; + } + }); + return attributeNames; + } + + [isNew]() { + if (this[this.primaryKey]) { + return false; + } else { + return true; + } + } + + /** + * Call a function deeply through all associations + * + * @private + * @method callDeep + * @param {String} functionName The name of the function that you want to fire deeply. + * @param {function(errors, results)} Function called at the end of the operation. + */ + [callDeep] (methodName, callback) { + const associationNames = Object.keys(this.associations); + + async.mapParallel( + associationNames, + (associationName, next) => { + + const associationDetails = this.associations[associationName]; + + switch(associationDetails.type) { + case "hasOne": + const model = this[associationName]; + if(model) { + //pass the associationDetails.whereArgs to the function + model[methodName](next); + } else { + next(); + } + break; + + case "hasMany": + const collection = this[associationName]; + //collection set, and not many to many (nothing in that case) + if (collection && associationDetails.through === undefined) { + //let array = [].slice.call(collection); + async.eachParallel( + collection, + (collectionModel, finishSubStep) => { + collectionModel[methodName](finishSubStep); + }, + next + ); + } else { + next(); //collection not set + } + break; + + case "belongsTo": + next(); + } + }, (errors, results) => { + callback(errors, results); + } + ); + } + + /** + * @example + * ``` + * const associationDetails = { + * name: "users", + * type: "hasMany", + * constructor: User + * }; + * + * [addAssociation](associationDetails); + * ``` + * @method addAssociation + * @param {Object.} associationDetails Object containing association details in key/value pairs. + */ + [addAssociation] (associationDetails) { + const association = { + parent: this, + type: associationDetails.type, + constructor: associationDetails.constructor + }; + + /* Default Values */ + + switch(associationDetails.type) { + case "hasOne": + case "hasMany": + association.foreignKey = inflect(this.constructor.name).foreignKey.toString(); + association.foreignId = inflect(association.foreignKey).camel.toString(); + break; + case "belongsTo": + association.foreignKey = inflect(associationDetails.name).foreignKey.toString(); + association.foreignId = inflect(association.foreignKey).camel.toString(); + } + + // TODO: AS + association.foreignName = inflect(this.constructor.name).camel.toString(); + + /* Override Values */ + + const associationSetter = new AssociationSetter(association); + + /* Set private space for value to be stored internally */ + + const privateAssociationName = "_" + associationDetails.name; + + /* Association Setter By Type */ + + let setterFunction; + + switch(associationDetails.type) { + case "hasOne": + this[privateAssociationName] = null; + + setterFunction = (newModel) => { + if (newModel && this[privateAssociationName] !== newModel) { + if(!(newModel instanceof Model)) { + throw new Error("Cannot set a non model entity onto this property. It should inherit from Model"); + } + + this[privateAssociationName] = newModel; + + newModel[association.foreignName] = this; + } + }; + break; + case "belongsTo": + this[privateAssociationName] = null; + + setterFunction = (newModel) => { + if (newModel && this[privateAssociationName] !== newModel) { + if(!(newModel instanceof Model)) { + throw new Error("Cannot set a non model entity onto this property. It should inherit from Model"); + } + + this[privateAssociationName] = newModel; + this[association.foreignId] = newModel.id; + + const pluralForeignName = inflect(association.foreignName).plural.toString(); + + if (!association.ambiguous) { + if (newModel.hasOwnProperty(association.foreignName)) { + newModel[association.foreignName] = this; + } else if (newModel.hasOwnProperty(pluralForeignName)) { + //lookup is it exist and dont add it in that case + newModel[pluralForeignName].push(this); + } else { + throw new Error(`Neither "${association.foreignName}" or "${pluralForeignName}" are valid associations on "${newModel.constructor.name}"`); + } + } + + // if (newModel[association.foreignName] instanceof Collection) { + // newModel[association.foreignName].push(this); + // } else { + // newModel[association.foreignName] = this; + // } + } + }; + break; + case "hasMany": + /* Set to null by default */ + this[privateAssociationName] = new Collection(association); + + setterFunction = (newModel) => { + if (newModel && this[privateAssociationName] !== newModel) { + this[privateAssociationName] = newModel; + } + }; + break; + default: + throw new Error("Unknown association type."); + } + + Object.defineProperty( + this, + privateAssociationName, + { + enumerable: false, + writable: true + } + ); + + Object.defineProperty( + this, + associationDetails.name, + { + enumerable: true, + set: setterFunction, + get: () => { + return this[privateAssociationName]; + } + } + ); + + this[associationDetails.name] = associationDetails.value; + + this._associations[associationDetails.name] = association; + + return associationSetter; + } + + [parseAttributesFromFields](record) { + for (var field in record) { + this[inflect(field).camel.toString()] = record[field]; + } + } + + [getFieldAttributes]() { + let attributeNames = Object.keys(this.attributes); + let fieldAttributes = {}; + attributeNames.forEach((attributeName) => { + fieldAttributes[inflect(attributeName).snake.toString()] = this[attributeName]; + }); + + //add belongsTo associations and remove others + Object.keys(this.associations).forEach((associationName) => { + let relatedModel = this[associationName]; + let foreignKeyField = inflect(associationName).foreignKey.toString(); + if(this._associations[associationName].type === "belongsTo") { + //try with relatedModel and relatedModel.id + if(relatedModel && relatedModel.id) { + fieldAttributes[foreignKeyField] = relatedModel.id; + } else { + //or just with the relatedModelId + //construct the snake with _id and then camelize it + let foreignIdAsAttribute = inflect(foreignKeyField).camel.toString(); + fieldAttributes[foreignKeyField] = this[foreignIdAsAttribute]; + } + } else { + //console.log("getFieldAttributes delete on ", {on: this.constructor.name, associationName: associationName, foreignKeyField: foreignKeyField, relatedModel: relatedModel}); + delete fieldAttributes[associationName]; + delete fieldAttributes["_" + associationName]; + } + }); + + return fieldAttributes; + } +} + +const ambiguous = Symbol(); + +export class AssociationSetter { + constructor(association) { + this.association = association; + + if (association.type === "belongsTo") { + Object.defineProperties(this, { + "ambiguous": { + get: this[ambiguous] + } + }); + } + } + + foreignName(name) { + this.association.foreignName = name; + return this; + } + + where(...options) { + this.association.where = options; + return this; + } + + andWhere(...options) { + this.association.andWhere = this.association.andWhere || []; + this.association.andWhere.push(options); + return this; + } + + through(associationName) { + this.association.through = associationName; + return this; + } + + as(associationName) { + this.association.as = associationName; + return this; + } + + [ambiguous]() { + this.association.ambiguous = true; + } +} + +Object.defineProperties(Model, { + "all": { + get: function getAll() { + let modelQuery = new ModelQuery(Model.database); + return modelQuery.find(this); + } + } +}); + +import Collection from "./collection.js"; +import ModelFinder from "./modelFinder.js"; diff --git a/es6/lib/modelFinder.js b/es6/lib/modelFinder.js new file mode 100644 index 0000000..916a0cc --- /dev/null +++ b/es6/lib/modelFinder.js @@ -0,0 +1,134 @@ +const validateDependencies = Symbol(); +import inflect from "jargon"; + +export default class ModelFinder { + constructor(database) { + Object.defineProperties(this, { + "_database": { + value: database, + writable: true, + enumerable: false + } + }); + } + + find(ModelConstructor) { + this[validateDependencies](); + + const query = new ModelQuery(this._database); + + query.find(ModelConstructor); + + return query; + } + + count(ModelConstructor) { + this[validateDependencies](); + + const query = new ModelQuery(this._database); + + query.count(ModelConstructor); + + return query; + } + + [validateDependencies] () { + if (!this._database) { throw new Error("Cannot find models without a database set."); } + } +} + +export class ModelQuery { + constructor(database) { + this._database = database; + } + + find(ModelConstructor) { + this.ModelConstructor = ModelConstructor; + + const tempModel = new this.ModelConstructor(); + + this._query = this._database + .select("*") + .from(tempModel.tableName); + + return this; + } + + count(ModelConstructor) { + this.ModelConstructor = ModelConstructor; + this.countResults = true; + + const tempModel = new this.ModelConstructor(); + + this._query = this._database + .select(null) + .count("* AS rowCount") + .from(tempModel.tableName); + + return this; + } + + where(...options) { + const formattedOptions = options.map((option, index) => { + if (typeof option === "string" && index === 0) { + return inflect(option).snake.toString(); + } else { + return option; + } + }); + + this._query.where(...formattedOptions); + return this; + } + + andWhere(...options) { + this._query.andWhere(...options); + return this; + } + + orWhere(...options) { + this._query.orWhere(...options); + return this; + } + + groupBy(...options) { + this._query.groupBy(...options); + return this; + } + + orderBy(...options) { + this._query.orderBy(...options); + return this; + } + + limit(...options) { + this._query.limit(...options); + return this; + } + + results(callback) { + this._query.results((error, rows) => { + if(!rows) { + if(error) { + return callback(error); + } else { + return callback(new Error("No rows returned by database.")); + } + } + + if (this.countResults) { + callback(error, rows[0].rowCount); + } else { + const models = new Collection(this.ModelConstructor); + + rows.forEach(row => { + models.push(new this.ModelConstructor(row)); + }); + + callback(error, models); + } + }); + } +} + +import Collection from "./collection.js"; diff --git a/es6/spec/dovima.spec.js b/es6/spec/dovima.spec.js new file mode 100644 index 0000000..55abf1b --- /dev/null +++ b/es6/spec/dovima.spec.js @@ -0,0 +1,13 @@ +import Dovima from "../lib/dovima.js"; + +describe("Dovima", () => { + let component; + + before(() => { + component = new Dovima(); + }); + + it("should say something", () => { + component.saySomething().should.equal("Something"); + }); +}); diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js new file mode 100644 index 0000000..a436f12 --- /dev/null +++ b/es6/spec/model.spec.js @@ -0,0 +1,1635 @@ +/* Testing Dependencies */ + +const sinon = require("sinon"); +//import MultiError from "../../multiError/multiError.js"; + +//import MultiError from "../../multiError/multiError.js"; +import Database from "../../database/database.js"; +import isPresent from "../validation/isPresent.js"; +import Collection from "../collection.js"; +import Model, {AssociationSetter} from "../model.js"; +import {ModelQuery} from "../modelFinder.js"; + +/* Test Configuration */ +const databaseConfig = require("../../../database.json").testing; + +let userFixtures = require("./fixtures/users.json"); + +describe("Model(attributes, options)", () => { + + /** + * Setup Model Examples + */ + + /* Simple Example */ + + class User extends Model { + associate() { + this.hasOne("address", Address); + + this.hasOne("postalCode", PostalCode).through("address"); + + this.hasMany("photos", Photo); + + // this.hasMany("deletedPhotos", Photo) + // .where("deletedAt", "!=", null); + + this.hasOne("primaryPhoto", Photo) + .where("isPrimary", true); + + this.hasMany("photoLikes", PhotoLike); + this.hasMany("likedPhotos", Photo).through("photoLikes"); + + this.hasMany("comments", Comment).through("photos"); + + this.hasMany("deletedComments", Comment).through("photos").where("comments.deletedAt", "!=", null); + } + validate() { + this.ensure("photos", isPresent); + } + } + + class Address extends Model { + associate() { + this.belongsTo("user", User); + this.belongsTo("postalCode", PostalCode); + } + validate() { + this.ensure("photos", isPresent); + } + } + + class PostalCode extends Model { + associate() { + this.hasMany("addresses"); + } + } + + class PhotoLike { + associate() { + this.belongsTo("user", User); + this.belongsTo("photo", User); + } + validate() { + this.ensure("user", isPresent); + this.ensure("photo", isPresent); + } + } + + class Photo extends Model { + associate() { + this.belongsTo("user", User).ambiguous; + + this.hasMany("comments", Comment); + + this.hasMany("commentAuthors", User) + .through("comments") + .as("author"); + + this.hasMany("photoLikes", PhotoLike); + + + this.hasMany("likedByUsers", User) + .through("photoLikes"); + } + + validate() { + this.ensure("user", isPresent); + } + } + + class Comment extends Model { + associate() { + this.belongsTo("photo", Photo); + this.belongsTo("author", User); + } + } + + class Rating extends Model { + associate() { + this.belongsTo("owner", Comment) + .isPolymorphic; + } + } + + /** + * Instantiate Model Examples + */ + + let model, + user, + userAttributes, + photo, + primaryPhoto, + postalCode, + address, + comment, + clock; + + beforeEach(() => { + clock = sinon.useFakeTimers(); + + Model.database = new Database(databaseConfig); + Model.database.mock({}); // Catch-all for database + + model = new Model(); + + userAttributes = { + id: 1, + name: "Bob Builder", + age: 35, + hasChildren: false + }; + + user = new User(userAttributes); + photo = new Photo(); + primaryPhoto = new Photo(); + comment = new Comment(); + postalCode = new PostalCode(); + address = new Address(); + }); + + afterEach(() => clock.restore()); + + /** + * Begin Testing + */ + + describe("(Properties)", () => { + describe(".attributes", () => { + it("should return all attributes and their values minus associations", () => { + user.attributes.should.eql(userAttributes); + }); + it("should assign the properties for the model", () => { + user = new User(); + user.attributes = userAttributes; + user.attributes.should.eql(userAttributes); + }); + }); + + describe(".properties", () => { + it("should return the name of all attributes plus associations on the model", () => { + user.properties.should.eql([ + "address", + "postalCode", + "photos", + "primaryPhoto", + "photoLikes", + "likedPhotos", + "comments", + "deletedComments", + "id", + "name", + "age", + "hasChildren" + ]); + }); + }); + + describe(".tableName", () => { + it("should return the model's table name", () => { + user.tableName.should.eql("users"); + }); + it("should allow overriding of the model's table name", () => { + const newTableName = "somethingElse"; + user.tableName = newTableName; + user.tableName.should.eql(newTableName); + }); + }); + + + describe(".primaryKey", () => { + it("should return the model's primary key", () => { + user.primaryKey.should.eql("id"); + }); + it("should allow overriding of the model's primaryKey", () => { + const newPrimaryKey = "different_id"; + user.primaryKey = newPrimaryKey; + user.primaryKey.should.eql(newPrimaryKey); + }); + }); + + describe(".isNew", () => { + describe("(when model has the primary key set)", () => { + it("should be false", () => { + user.isNew.should.be.false; + }); + }); + describe("(when model does not have the primary key set)", () => { + beforeEach(() => { + user.id = undefined; + }); + it("should be true", () => { + user.isNew.should.be.true; + }); + }); + }); + }); + + describe("(static properties)", () => { + describe(".all", () => { + let users, + userCollection; + + before(() => { + userCollection = new Collection(User); + userFixtures.forEach((userFiture) => { + userCollection.push(new User(userFiture)); + }); + }); + + beforeEach(done => { + Model.database.mock({ + "select * from `users` where `mom_id` = 1": + userFixtures + }); + + User + .all + .where("momId", "=", 1) + .results((error, fetchedUsers) => { + users = fetchedUsers; + done(); + }); + }); + + it("should return a ModelQuery instance", () => { + User.all.should.be.instanceOf(ModelQuery); + }); + + it("should return a collection", () => { + users.should.be.instanceOf(Collection); + }); + + it("should return the right collection", () => { + users.should.eql(userCollection); + }); + + it("should allow to search all models that matchs a certain condition", () => { + users.length.should.equal(5); + }); + }); + }); + + describe("(Initialization)", () => { + /* eslint-disable no-unused-vars */ + // This is because we instantiate Post, but we don"t do anything with it. + + let post, + initializeSpy, + validateSpy, + associateSpy; + + class Post extends Model { + initialize() { + initializeSpy(); + } + validate() { + validateSpy(); + } + associate() { + associateSpy(); + } + } + + beforeEach(() => { + initializeSpy = sinon.spy(); + validateSpy = sinon.spy(); + associateSpy = sinon.spy(); + post = new Post(); + }); + + describe(".initialize()", () => { + it("should be called during instantiation", () => { + initializeSpy.called.should.be.true; + }); + it("should be called after .associate", () => { + sinon.assert.callOrder(associateSpy, initializeSpy); + }); + it("should be called after .validate", () => { + sinon.assert.callOrder(validateSpy, initializeSpy); + }); + }); + + describe(".validate()", () => { + it("should be called during instantiation", () => { + validateSpy.called.should.be.true; + }); + it("should be called after .associate", () => { + sinon.assert.callOrder(associateSpy, validateSpy); + }); + }); + + describe(".associate()", () => { + it("should be called during instantiation", () => { + associateSpy.called.should.be.true; + }); + }); + }); + + describe("(Associations)", () => { + class Street extends Model {} + class Driver extends Model {} + class Truck extends Model {} + class Wheel extends Model {} + class SteeringWheel extends Model {} + + let street, + driver, + truck, + wheel, + steeringWheel; + + beforeEach(() => { + street = new Street(); + driver = new Driver(); + truck = new Truck(); + wheel = new Wheel(); + steeringWheel = new SteeringWheel(); + }); + + describe(".belongsTo(associationName, associationConstructor)", () => { + describe("(when inverse association is hasOne)", () => { + beforeEach(() => { + truck.hasOne("steeringWheel", SteeringWheel); + steeringWheel.belongsTo("truck", Truck); + }); + + it("should add the association to .associations", () => { + steeringWheel.associations.truck.should.eql({ + parent: steeringWheel, + type: "belongsTo", + constructor: Truck, + foreignName: "steeringWheel", + foreignId: "truckId", + foreignKey: "truck_id" + }); + }); + + it("should set the accessor property to null", () => { + (steeringWheel.truck == null).should.be.true; + }); + + + it("should add the parent model onto the child model", () => { + steeringWheel.truck = truck; + truck.steeringWheel.should.eql(steeringWheel); + }); + + it("should add the association id onto the model", () => { + truck.id = 1; + steeringWheel.truck = truck; + steeringWheel.should.have.property("truckId"); + }); + }); + + describe("(when inverse association is hasMany)", () => { + beforeEach(() => { + truck.hasMany("wheels", Wheel); + wheel.belongsTo("truck", Truck); + }); + + it("should add the association to .associations", () => { + wheel.associations.truck.should.eql({ + parent: wheel, + type: "belongsTo", + constructor: Truck, + foreignName: "wheel", + foreignId: "truckId", + foreignKey: "truck_id" + }); + }); + + it("should set the accessor property to null", () => { + (wheel.truck == null).should.be.true; + }); + + it("should add the parent model onto the child model", () => { + wheel.truck = truck; + truck.wheels[0].should.eql(wheel); + }); + + it("should add the association id onto the model", () => { + truck.id = 1; + wheel.truck = truck; + wheel.truckId.should.eql(truck.id); + }); + + it("should add a model just once on the parent", () => { + truck.id = 1; + wheel.truck = truck; + wheel.truck = truck; + + truck.wheels.length.should.equal(1); + }); + }); + + describe("(when associationName is different from the associationConstructor)", () => { + beforeEach(() => { + truck.hasOne("steeringWheel", SteeringWheel).as("superTruck"); + steeringWheel.belongsTo("superTruck", Truck); + + // steeringWheel.id = 1; + + // Model.database.mock({ + // "select * from `steering_wheels` where `id` = 1 limit 1": [ + // {"truck_id": 1} + // ], + // "select * from `trucks` where `id` = 1 limit 1": [ + // {} + // ] + // }); + }); + + it("should find the renamed association", () => { + steeringWheel.associations.superTruck.should.eql({ + parent: steeringWheel, + type: "belongsTo", + constructor: Truck, + foreignName: "steeringWheel", + foreignId: "superTruckId", + foreignKey: "super_truck_id" + }); + }); + }); + + describe("(when no inverse association is found)", () => { + beforeEach(() => { + wheel.belongsTo("truck", Truck); + }); + + it("should throw an error", () => { + () => { + wheel.truck = truck; + }.should.throw("Neither \"wheel\" or \"wheels\" are valid associations on \"Truck\""); + }); + }); + + describe("(with options)", () => { + describe(".ambiguous", () => { + beforeEach(() => { + wheel.belongsTo("truck", Truck).ambiguous; + }); + + it("should add the association to .associations", () => { + wheel.associations.truck.should.eql({ + parent: wheel, + type: "belongsTo", + constructor: Truck, + foreignName: "wheel", + foreignId: "truckId", + foreignKey: "truck_id", + ambiguous: true + }); + }); + + it("should not add the parent model to the child associatio", () => { + wheel.truck = truck; + (truck.wheel === undefined).should.be.true; + }); + }); + }); + }); + + describe(".hasOne(associationName, associationConstructor)", () => { + let associationName, + associationConstructor; + + beforeEach(() => { + associationName = "user"; + associationConstructor = User; + model.hasOne(associationName, associationConstructor); + }); + + it("should set the accessor function to null", () => { + (model[associationName] == null).should.be.true; + }); + + it("should add the association to .associations", () => { + model.associations[associationName].should.eql({ + parent: model, + type: "hasOne", + constructor: associationConstructor, + foreignId: "modelId", + foreignKey: "model_id", + foreignName: "model" + }); + }); + + it("should return an association setter", () => { + truck + .hasOne("steeringWheel", SteeringWheel) + .should.be.instanceOf(AssociationSetter); + }); + + it("should accept a custom error message", () => { + truck.hasOne("steeringWheel", SteeringWheel); + truck.ensure("steeringWheel", isPresent, "must be there."); + truck.invalidAttributes((invalidAttributeList) => { + invalidAttributeList.should.eql({ + "steeringWheel": [ + "must be there." + ] + }); + }); + }); + + describe("(with options)", () => { + describe(".ambiguous", () => { + it("should throw an error", () => { + const query = truck.hasOne("steeringWheel", SteeringWheel); + query.should.not.have.property("ambiguous"); + }); + }); + + describe(".where(...conditions)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": [{id: 1}], + "select * from `photos` where `user_id` = 1 and (`is_favorite` = true) limit 1": [{ + id: 1, + name: "Favorite Photo" + }] + }); + user + .hasOne("favoritePhoto", Photo) + .where("isFavorite", true); + }); + + it("should set more than one condition joined by AND", done => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": + [{id: 1}], + "select * from `photos` where `user_id` = 1 and (`is_favorite` = true and `is_face_photo` = true) limit 1": + [{name: "Favorite Face Photo"}] + }); + + user + .hasOne("favoriteFacePhoto", Photo) + .where("isFavorite", true) + .andWhere("isFacePhoto", true); + + user + .include("favoriteFacePhoto") + .fetch(() => { + user.favoriteFacePhoto.name.should.eql("Favorite Face Photo"); + done(); + }); + }); + + it("should set the where conditions", () => { + user.associations.favoritePhoto.where.should.eql(["isFavorite", true]); + }); + + it("should set conditions for the association", done => { + user.include("favoritePhoto").fetch(() => { + user.favoritePhoto.name.should.eql("Favorite Photo"); + done(); + }); + }); + }); + }); + }); + + describe(".hasMany(associationName, associationConstructor)", () => { + let associationName, + associationConstructor; + + beforeEach(() => { + associationName = "users"; + associationConstructor = User; + model.hasMany(associationName, associationConstructor); + }); + + it("should set the accessor function to a Collection", () => { + model[associationName].should.be.instanceOf(Collection); + }); + + it("should initially set the accessor function Collection to be empty", () => { + model[associationName].length.should.equal(0); + }); + + it("should add the association to .associate()", () => { + model.associations[associationName].should.eql({ + parent: model, + type: "hasMany", + constructor: associationConstructor, + foreignId: "modelId", + foreignKey: "model_id", + foreignName: "model" + }); + }); + + it("should accept a custom error message", () => { + truck.hasMany("wheels", Wheel); + truck.ensure("wheels", isPresent, "must be there."); + truck.invalidAttributes((invalidAttributeList) => { + invalidAttributeList.should.eql({ + "wheels": [ + "must be there." + ] + }); + }); + }); + + describe("(with options)", () => { + describe(".ambiguous", () => { + it("should throw an error", () => { + const query = truck.hasOne("steeringWheel", SteeringWheel); + query.should.not.have.property("ambiguous"); + }); + }); + + describe(".where(...conditions)", () => { + describe("(with one where condition)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": [{id: 1}], + "select * from `photos` where `user_id` = 1 and (`is_favorite` = true)": [ + { id: 1, name: "Favorite Photo" }, + { id: 2, name: "Another Favorite Photo" }, + { id: 3, name: "Mostest Favoritest Photo" } + ] + }); + + user.hasMany("favoritePhotos", Photo).where("isFavorite", true); + }); + + it("should set the where conditions", () => { + user.associations.favoritePhotos.where.should.eql(["isFavorite", true]); + }); + + it("should set conditions for the association", done => { + user.include("favoritePhotos").fetch(() => { + user.favoritePhotos.length.should.eql(3); + done(); + }); + }); + }); + + describe("(with multiple where conditions)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": [{id: 1}], + "select * from `photos` where `user_id` = 1 and (`is_favorite` = true and `is_face_photo` = true)": [ + { id: 1, name: "Favorite Face Photo" }, + { id: 2, name: "Another Favorite Face Photo" }, + { id: 3, name: "Mostest Favoritest Face Photo" } + ] + }); + + user + .hasMany("favoriteFacePhotos", Photo) + .where("isFavorite", true) + .andWhere("isFacePhoto", true); + }); + + it("should set the where conditions", () => { + user.associations.favoriteFacePhotos.andWhere.should.eql( + [ + ["isFacePhoto", true] + ] + ); + }); + + it("should set conditions for the association", done => { + user.include("favoriteFacePhotos").fetch(() => { + user.favoriteFacePhotos.length.should.eql(3); + done(); + }); + }); + }); + }); + + describe(".through(associationName)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": [ + { id: 1 }, + { id: 2 } + ], + "select * from `photos` where `user_id` = 1": [ + { id: 1, name: "Favorite Face Photo" }, + { id: 2, name: "Another Favorite Face Photo" }, + { id: 3, name: "Mostest Favoritest Face Photo" } + ], + "select * from `comments` where `photo_id` in (1, 2, 3)": [ + { message: "nice photo!", author_id: 1, photo_id: 1 }, + { message: "where is it?", author_id: 2, photo_id: 1 }, + ] + }); + }); + + it("should fetch hasMany through associations", done => { + user + .include("comments") + .fetch(() => { + user.comments[0].should.be.instanceOf(Comment); + done(); + }); + }); + + describe("(through hasMany)", () => { + let account, + accountContentPackage, + contentPackage; + //better example + class Account extends Model { + associate() { + this.hasMany("accountContentPackages", AccountContentPackage); + this.hasMany("contentPackages", ContentPackage) + .through("accountContentPackages"); + } + } + + class AccountContentPackage extends Model { + associate() { + this.belongsTo("account", Account); + this.belongsTo("contentPackage", ContentPackage); + } + } + + class ContentPackage extends Model { + associate() { + this.hasMany("accountContentPackages", AccountContentPackage); + + this.hasMany("accounts", Account) + .through("accountContentPackages", AccountContentPackage); + } + } + + beforeEach(() => { + account = new Account({id: 1}); + contentPackage = new ContentPackage({id: 2}); + account.contentPackages.push(contentPackage); + }); + + it("should create a the through model when pushing", () => { + account.accountContentPackages.length.should.equal(1); + }); + + it("should association the new through model with the parent", () => { + account.accountContentPackages[0].account.should.eql(account); + }); + + it("should association the new through model with the other side of the relationship", () => { + account.accountContentPackages[0].contentPackage.should.eql(contentPackage); + }); + + //need to prepare test case for this + xit("should throw an error if the through association does not exists", () => { + () => { + account.hasMany("specialContentPackages", Comment) + .through("specialAccountContentPackages"); + }.should.throw("Through association called specialAccountContentPackages not defined on model Account"); + }); + }); + }); + }); + }); + }); + + describe("(Validations)", () => { + describe(".invalidAttributes(callback)", () => { + describe("(when all validations pass)", () => { + beforeEach(() => { + user.photos.push(photo); + }); + + it("should return an empty object", () => { + user.invalidAttributes((attributes) => { + attributes.should.eql({}); + }); + }); + }); + + describe("(when any validations fail)", () => { + beforeEach(() => { + // Force database to fail isPresent on user.photos + Model.database.mock({ + "select count(*) as `rowCount` from `photos` where `user_id` = 1": + [{rowCount: 0}] + }); + }); + + it("should return an object containing all invalid attributes", done => { + user.invalidAttributes((attributes) => { + attributes.should.eql({ + "photos": [ + "must be present on User" + ] + }); + done(); + }); + }); + }); + }); + + describe(".isValid(callback)", () => { + describe("(when all validations pass)", () => { + beforeEach(() => { + user.photos.push(photo); + }); + + it("should return true", done => { + user.isValid((isValid) => { + isValid.should.be.true; + done(); + }); + }); + }); + + describe("(when any validations fail)", () => { + beforeEach(() => { + // Force database to fail isPresent on user.photos + Model.database.mock({ + "select count(*) as `rowCount` from `photos` where `user_id` = 1": + [{rowCount: 0}] + }); + }); + + it("should return false", done => { + user.isValid((isValid) => { + isValid.should.be.false; + done(); + }); + }); + }); + }); + + describe(".validations", () => { + it("should return an object representing all validations on the model", () => { + user.validations.should.eql({ + "photos": [ + { + validator: isPresent + } + ] + }); + }); + }); + + describe(".ensure(attributeName, validatorFunction, validatorMessage)", () => { + let validatorFunction, + validatorMessage; + beforeEach(() => { + validatorFunction = (value, callback) => { + callback(null, true); + }; + validatorMessage = "must be a number."; + }); + + it("should add the validator to .validations", () => { + user.ensure("photos", validatorFunction, validatorMessage); + user.validations.should.eql({ + "photos": [ + { + validator: isPresent + }, + { + validator: validatorFunction, + message: validatorMessage + } + ] + }); + }); + }); + }); + + describe("(Persistence)", () => { + + describe(".as(associationName)", () => { + it("should set the referencing association name in a hasMany through belongsTo", done => { + Model.database.mock({ + "select * from `photos` where `id` = 1 limit 1": [ + {} + ], + "select * from `comments` where `photo_id` = 1": [ + {id: 1, "author_id": 5}, {id: 2, "author_id": 6}, {id: 3, "author_id": 7} + ], + "select * from `users` where `id` in (5, 6, 7)": [ + {}, {}, {} + ] + }); + + photo.id = 1; + + photo + .include("commentAuthors") + .fetch((errors) => { + photo.commentAuthors.length.should.equal(3); + done(); + }); + }); + }); + + describe(".include(associationNames)", () => { + let associationNames; + + beforeEach(() => { + associationNames = ["primaryPhoto", "photos"]; + }); + + it("should throw an error if an association name is not found", () => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": + [userAttributes] + }); + () => { + user.include("bogusAssociation").fetch(); + }.should.throw("Cannot fetch 'bogusAssociation' because it is not a valid association on User"); + }); + + it("should throw an error if a belongs to association id is not set", () => { + Model.database.mock({ + "select * from `comments` where `id` = 1 limit 1": + [{id: 1}] + }); + + () => { + comment.id = 1; + comment.include("photo").fetch(); + }.should.throw("Cannot fetch 'photo' because 'photoId' is not set on Comment"); + }); + + it("should return the model to support chaining", () => { + user.include(...associationNames).should.equal(user); + }); + + it("should fetch belongsTo associations", done => { + Model.database.mock({ + "select * from `photos` where `id` = 1 limit 1": + [{id: 1}], + "select * from `users` where `id` = 1 limit 1": + [{id: 1, name: "Bob Barker"}] + }); + + photo.id = 1; + photo.userId = 1; + photo + .include("user") + .fetch((errors) => { + photo.user.name.should.eql("Bob Barker"); + done(); + }); + }); + + it("should fetch hasOne associations", done => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": + [userAttributes], + "select * from `photos` where `user_id` = 1 and (`is_primary` = true) limit 1": + [{ + name: "Primary Photo" + }] + }); + + user + .include("primaryPhoto") + .fetch((errors) => { + user.primaryPhoto.name.should.eql("Primary Photo"); + done(); + }); + }); + + it("should fetch hasMany associations", done => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": + [userAttributes], + "select * from `photos` where `user_id` = 1": + [ + { name: "Some Photo" }, + { name: "Some Other Photo" }, + { name: "Still Some Photo" } + ] + }); + + user + .include("photos") + .fetch((errors) => { + user.photos.length.should.eql(3); + done(); + }); + }); + + it("should fetch hasOne through associations", () => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": + [{}], + "select * from `addresses` where `user_id` = 1 limit 1": + [{ + "user_id": 1, + "postal_code_id": 2 + }], + "select * from `postal_codes` where `id` = 2 limit 1": + [{number: 90210}] + }); + + user + .include("postalCode") + .fetch((errors) => { + user.postalCode.number.should.eql(90210); + }); + }); + + it("should fetch hasMany through hasMany associations", done => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": [ + { id: 1 } + ], + "select * from `photos` where `user_id` = 1": [ + { id: 3 }, + { id: 4 }, + { id: 5 } + ], + "select * from `comments` where `photo_id` in (3, 4, 5)": [ + {}, {}, {} + ] + }); + + user + .include("comments") + .fetch((errors) => { + user.comments.length.should.equal(3); + done(); + }); + }); + + it("should throw an error when the destination association is not found on the through model", () => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": [{}], + "select * from `photos` where `user_id` = 1": [{id: 3}] + }); + + class Post {} // Needed to mock a through association that is incomplete + + () => { + user.hasMany("posts", Post) + .through("photos"); + + user + .include("posts") + .fetch(); + }.should.throw("'posts' is not a valid association on through model 'Photo'"); + }); + + it("should fetch hasMany through hasOne associations", done => { + Model.database.mock({ + "select * from `photos` where `id` = 1 limit 1": [ + {} + ], + "select * from `comments` where `photo_id` = 1": [ + {"author_id": 4}, + {"author_id": 5}, + {"author_id": 6} + ], + "select * from `users` where `id` in (4, 5, 6)": [ + {}, {}, {} + ] + }); + + photo.id = 1; // Need primary key to fetch + + photo + .include("commentAuthors") + .fetch((errors) => { + photo.commentAuthors.length.should.equal(3); + done(); + }); + }); + + xit("should fetch hasMany through belongsTo associations", done => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": + [{}] + }); + + user + .include("comments") + .fetch((errors) => { + user.comments.length.should.equal(3); + done(); + }); + }); + + it("should fetch more than one association at once", done => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": + [userAttributes], + "select * from `photos` where `user_id` = 1 and (`is_primary` = true) limit 1": + [ + { name: "Primary Photo" } + ], + "select * from `photos` where `user_id` = 1": + [ + { name: "Some Photo" }, + { name: "Some Other Photo" }, + { name: "Still Some Photo" }, + { name: "Primary Photo" } + ] + }); + + user + .include("photos", "primaryPhoto") + .fetch((errors) => { + [user.photos.length, user.primaryPhoto.name] + .should.eql([ + 4, + "Primary Photo" + ]); + done(); + }); + }); + }); + + describe(".fetch(callback)", () => { + describe("(Model.database is set)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": [userAttributes] + }); + }); + + describe("(when model has a primary key set)", () => { + beforeEach(() => { + user = new User({ + id: 1 + }); + }); + + it("should fetch a record from the correct table", done => { + user.fetch((error) => { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + + it("should fetch a record from the correct table", done => { + user.fetch((error) => { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + }); + + describe("(when model does not have a primary key set)", () => { + beforeEach(() => { + delete user.id; + }); + + it("should throw an error", () => { + () => { + user.fetch(); + }.should.throw("Cannot fetch this model by the 'id' field because it is not set."); + }); + }); + + describe("(when there is no model with that id)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `id` = 1 limit 1": [] + }); + user = new User({ + id: 1 + }); + }); + + it("should throw an error on the callback", done => { + user.fetch((error) => { + error.should.be.instanceOf(Error); + done(); + }); + }); + }); + }); + + describe("(Model.database not set)", () => { + beforeEach(() => { + delete Model.database; + }); + + it("should throw an error", () => { + () => { + user.fetch(); + }.should.throw("Cannot fetch without Model.database set."); + }); + }); + }); + + describe(".fetch(strategy, callback)", () => { + describe("(when the type of the strategy is a string)", () => { + describe("(Model.database is set)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `name` = 'someuser' limit 1": [userAttributes] + }); + }); + + describe("(when model has the specified attribute set)", () => { + beforeEach(() => { + user = new User({ + name: "someuser" + }); + }); + + it("should fetch a record from the correct table", done => { + user.fetch("name", (error) => { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + + it("should fetch a record from the correct table", done => { + user.fetch("name", (error) => { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + }); + + describe("(when model does not have the specified attribute set)", () => { + beforeEach(() => { + delete user.name; + }); + + it("should throw an error", () => { + () => { + user.fetch("name"); + }.should.throw("Cannot fetch this model by the 'name' field because it is not set."); + }); + }); + + describe("(when there is no model with the specified attribute)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `name` = 'someuser' limit 1": [] + }); + user = new User({ + name: "someuser" + }); + }); + + it("should throw an error on the callback", done => { + user.fetch("name", (error) => { + error.should.be.instanceOf(Error); + done(); + }); + }); + }); + }); + + describe("(Model.database not set)", () => { + beforeEach(() => { + delete Model.database; + }); + + it("should throw an error", () => { + () => { + user.fetch("name"); + }.should.throw("Cannot fetch without Model.database set."); + }); + }); + }); + + describe("(when the type of the strategy is an array)", () => { + describe("(Model.database is set)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `name` = 'someuser' and `lastName` = 'someuserLastName' limit 1": [userAttributes] + }); + }); + + describe("(when model has the specified attribute set)", () => { + beforeEach(() => { + user = new User({ + name: "someuser", + lastName: "someuserLastName" + }); + userAttributes.lastName = "someuserLastName"; + }); + + it("should fetch a record from the correct table", done => { + user.fetch(["name", "lastName"], (error) => { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + + it("should fetch a record from the correct table", done => { + user.fetch(["name", "lastName"], (error) => { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + }); + + describe("(when model does not have one of the specified attributes set)", () => { + beforeEach(() => { + delete user.lastName; + }); + + it("should throw an error", () => { + () => { + user.fetch(["name", "lastName"]); + }.should.throw("Cannot fetch this model by the 'lastName' field because it is not set."); + }); + }); + + describe("(when there is no model with the specified attribute)", () => { + beforeEach(() => { + Model.database.mock({ + "select * from `users` where `name` = 'someuser' and `lastName` = 'someuserLastName' limit 1": [] + }); + user = new User({ + name: "someuser", + lastName: "someuserLastName" + }); + }); + + it("should throw an error on the callback", done => { + user.fetch(["name", "lastName"], (error) => { + error.should.be.instanceOf(Error); + done(); + }); + }); + }); + }); + + describe("(Model.database not set)", () => { + beforeEach(() => { + delete Model.database; + }); + + it("should throw an error", () => { + () => { + user.fetch("name"); + }.should.throw("Cannot fetch without Model.database set."); + }); + }); + }); + }); + + describe(".save(callback)", () => { + describe("(Model.database is set)", () => { + describe("(when the model has associations)", () => { + + beforeEach(() => { + user.primaryPhoto = primaryPhoto; + user.photos.push(photo); + + let regularExpressions = { + insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('1969-12-31 [0-9][0-9]:00:00.000', 1\)/, + updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, + insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('1969-12-31 [0-9][0-9]:00:00\.000', 1\)/, + insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('1969-12-31 [0-9][0-9]:00:00.000', 1\)/, + insertTruck: /insert into `trucks` (`created_at`) values ('1969-12-31 [0-24]:00:00.000')/, + updateTruck: /update `trucks` set `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, + updateWheels: /update `wheels` set `created_at` = '1969-12-31 [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + }; + + Model.database.mock({ + [regularExpressions.insertPhotos]: + [1], + [regularExpressions.updateUser]: + [], + [regularExpressions.insertWheels]: + [1], + [regularExpressions.insertSteeringWheel]: + [1], + [regularExpressions.insertTruck]: + [1], + [regularExpressions.updateTruck]: + [1], + [regularExpressions.updateWheels]: + [1] + }); + }); + + describe("(association operations)", () => { + let wheelSaveSpy, + steeringWheelSaveSpy, + truck, + owner, + wheel, + steeringWheel, + driverSeat, + passengerSeat; + + class TruckOwner extends Model { + associate() { + this.belongsTo("truck", Truck); + this.belongsTo("owner", Owner); + } + } + + class Truck extends Model { + associate() { + this.hasMany("truckOwners", TruckOwner); + this.hasMany("owners", Owner) + .through("truckOwners"); + this.hasMany("wheels", Wheel); + this.hasOne("steeringWheel", SteeringWheel); + } + } + + class Owner extends Model { + associate() { + this.hasMany("truckOwners", TruckOwner); + this.hasMany("trucks", Truck) + .through("truckOwners"); + } + } + + class Wheel extends Model { + associate() { + this.belongsTo("truck", Truck); + } + save(callback) { + wheelSaveSpy(callback); + super.save(callback); + } + } + + class SteeringWheel extends Model { + associate() { + this.belongsTo("truck", Truck); + } + save(callback) { + steeringWheelSaveSpy(callback); + super.save(callback); + } + } + + class Seat extends Model { + associate() { + this.belongsTo("truck", Truck); + } + } + + describe("(assignment)", () => { + + beforeEach(() => { + wheelSaveSpy = sinon.spy(); + steeringWheelSaveSpy = sinon.spy(); + + truck = new Truck(); + owner = new Owner(); + wheel = new Wheel(); + steeringWheel = new SteeringWheel(); + + driverSeat = new Seat(); + passengerSeat = new Seat(); + }); + + it("should throw when assign a non model object to a belongsTo association", () => { + () => { + steeringWheel.truck = {}; + }.should.throw("Cannot set a non model entity onto this property. It should inherit from Model"); + }); + + it("should throw when assign a non model object to a hasOne association", () => { + () => { + truck.steeringWheel = {}; + }.should.throw("Cannot set a non model entity onto this property. It should inherit from Model"); + }); + + it("should associate a hasOne from a belongsTo", () => { + steeringWheel.truck = truck; + truck.should.have.property("steeringWheel"); + }); + + // TODO: This is impossible as is + xit("should associate a hasMany from a belongsTo", () => { + wheel.truck = truck; + truck.wheels.length.should.equal(1); + }); + + it("should associate a belongsTo from a hasMany", () => { + truck.wheels.push(wheel); + wheel.truck.should.eql(truck); + }); + + it("should associate a hasMany from a hasMany", () => { + truck.owners.push(owner); + owner.trucks[0].should.eql(truck); + }); + + it("should associate a belongsTo from a hasOne", () => { + truck.steeringWheel = steeringWheel; + steeringWheel.truck.should.eql(truck); + }); + }); + + describe("(propagation)", () => { + beforeEach(() => { + wheelSaveSpy = sinon.spy(); + steeringWheelSaveSpy = sinon.spy(); + + truck = new Truck({id: 1}); + wheel = new Wheel(); + steeringWheel = new SteeringWheel(); + + truck.steeringWheel = steeringWheel; + + //TODO: wrap push on typed collection? + wheel.truck = truck; + truck.wheels.push(wheel); + }); + + it("should propagate .save() to hasOne associations", done => { + truck.save((error) => { + steeringWheelSaveSpy.calledOnce.should.be.true; + done(); + }); + }); + + it("should propagate .save() to hasMany associations", done => { + truck.save((error) => { + wheelSaveSpy.called.should.be.true; + done(); + }); + }); + }); + }); + }); + + describe("(database updating)", () => { + describe("(model not new)", () => { + beforeEach(() => { + model.id = 1; + + Model.database.update = sinon.spy(Model.database.update); + + let regularExpressions = { + updateModel: /update `models` set `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + }; + + Model.database.mock({ + [regularExpressions.updateModel]: + [{}] + }); + }); + it("should update the record the database", () => { + model.save((error) => { + Model.database.update.called.should.be.true; + }); + }); + }); + + describe("(model not new)", () => { + beforeEach(() => { + Model.database.insert = sinon.spy(Model.database.insert); + + let regularExpressions = { + insertModel: /insert into `models` \(`created_at`\) values \('1969-12-31 [0-9][0-9]:00:00.000'\)/ + }; + + Model.database.mock({ + [regularExpressions.insertModel]: + [{}] + }); + }); + it("should update the record the database", () => { + model.save((error) => { + Model.database.insert.called.should.be.true; + }); + }); + }); + }); + + describe("(when model is invalid)", () => { + beforeEach(() => { + Model.database.mock({ + "select count(*) as `rowCount` from `photos` where `user_id` = 1": + [{rowCount: 0}] + }); + }); + + it("should call back with an error", () => { + user.save((error) => { + error.should.be.instanceOf(Error); + }); + }); + + it("should inform the user that the model is invalid", () => { + user.save((error) => { + error.message.should.eql("photos must be present on User"); + }); + }); + }); + }); + + describe("(without Model.database set)", () => { + beforeEach(() => { + delete Model.database; + }); + + it("should call back with an error", () => { + () => { + user.save(); + }.should.throw("Cannot save without Model.database set."); + }); + }); + }); + }); +}); diff --git a/gulpfile.babel.js b/gulpfile.babel.js new file mode 100644 index 0000000..12555ac --- /dev/null +++ b/gulpfile.babel.js @@ -0,0 +1,6 @@ +import "./tasks/build-spec.js"; +import "./tasks/build-lib.js"; +import "./tasks/build.js"; +import "./tasks/test-local.js"; +import "./tasks/test-browsers.js"; +import "./tasks/test.js"; diff --git a/index.js b/index.js new file mode 100644 index 0000000..a3a33cd --- /dev/null +++ b/index.js @@ -0,0 +1 @@ +module.exports = require("./es5/lib/dovima.js"); diff --git a/lcov.info b/lcov.info new file mode 100644 index 0000000..35b2af6 --- /dev/null +++ b/lcov.info @@ -0,0 +1,44 @@ +TN: +SF:/Users/dslieker/Documents/source-code/dovima/es5/lib/dovima.js +FN:7,(anonymous_1) +FN:7,defineProperties +FN:7,(anonymous_3) +FN:9,_classCallCheck +FN:11,(anonymous_5) +FN:12,Dovima +FN:18,saySomething +FNF:7 +FNH:7 +FNDA:1,(anonymous_1) +FNDA:1,defineProperties +FNDA:1,(anonymous_3) +FNDA:1,_classCallCheck +FNDA:1,(anonymous_5) +FNDA:1,Dovima +FNDA:1,saySomething +DA:3,1 +DA:7,1 +DA:9,1 +DA:11,1 +DA:12,1 +DA:13,1 +DA:16,1 +DA:20,1 +DA:24,1 +DA:27,1 +DA:28,1 +LF:11 +LH:11 +BRDA:7,1,0,1 +BRDA:7,1,1,1 +BRDA:7,2,0,1 +BRDA:7,2,1,0 +BRDA:7,3,0,1 +BRDA:7,3,1,0 +BRDA:7,4,0,0 +BRDA:7,4,1,1 +BRDA:9,5,0,0 +BRDA:9,5,1,1 +BRF:10 +BRH:6 +end_of_record diff --git a/package.json b/package.json new file mode 100644 index 0000000..795203b --- /dev/null +++ b/package.json @@ -0,0 +1,50 @@ +{ + "name": "dovima", + "version": "0.1.0", + "description": "ES6 generic model with lots of useful special features.", + "main": "index.js", + "scripts": { + "test": "gulp test" + }, + "repository": { + "type": "git", + "url": "https://github.com/FreeAllMedia/dovima.git" + }, + "keywords": [ + "dovima" + ], + "author": "Free All Media, LLC", + "contributors": [], + "license": "MIT", + "bugs": { + "url": "https://github.com/FreeAllMedia/dovima/issues" + }, + "homepage": "https://github.com/FreeAllMedia/dovima", + "dependencies": { + "blunder": "^0.1.0", + "fleming": "^0.1.0", + "flowsync": "^0.1.4", + "jargon": "^0.1.14" + }, + "devDependencies": { + "babel": "^5.5.6", + "chai": "^3.0.0", + "coveralls": "^2.11.2", + "gulp": "^3.9.0", + "gulp-babel": "^5.1.0", + "gulp-istanbul": "^0.10.0", + "gulp-mocha": "^2.1.1", + "karma": "^0.12.36", + "karma-browserify": "^4.2.1", + "karma-chai": "^0.1.0", + "karma-mocha": "^0.1.10", + "karma-detect-browsers": "^2.0.0", + "karma-chrome-launcher": "^0.2.0", + "karma-firefox-launcher": "^0.1.6", + "karma-ie-launcher": "^0.2.0", + "karma-phantomjs-launcher": "^0.2.0", + "karma-safari-launcher": "^0.1.1", + "karma-sauce-launcher": "^0.2.11", + "mocha": "^2.2.5" + } +} diff --git a/paths.json b/paths.json new file mode 100644 index 0000000..1780085 --- /dev/null +++ b/paths.json @@ -0,0 +1,15 @@ +{ + "source": { + "lib": "./es6/lib/**/*.js", + "spec": "./es6/spec/**/*.spec.js" + }, + + "build": { + "directories": { + "lib": "./es5/lib", + "spec": "./es5/spec" + }, + "lib": "./es5/lib/**/*.js", + "spec": "./es5/spec/**/*.spec.js" + } +} diff --git a/tasks/build-lib.js b/tasks/build-lib.js new file mode 100644 index 0000000..e1bf7ce --- /dev/null +++ b/tasks/build-lib.js @@ -0,0 +1,10 @@ +import gulp from "gulp"; +import babel from "gulp-babel"; + +import paths from "../paths.json"; + +gulp.task("build-lib", () => { + return gulp.src(paths.source.lib) + .pipe(babel()) + .pipe(gulp.dest(paths.build.directories.lib)); +}); diff --git a/tasks/build-spec.js b/tasks/build-spec.js new file mode 100644 index 0000000..062e8b5 --- /dev/null +++ b/tasks/build-spec.js @@ -0,0 +1,10 @@ +import gulp from "gulp"; +import babel from "gulp-babel"; + +import paths from "../paths.json"; + +gulp.task("build-spec", () => { + return gulp.src(paths.source.spec) + .pipe(babel()) + .pipe(gulp.dest(paths.build.directories.spec)); +}); diff --git a/tasks/build.js b/tasks/build.js new file mode 100644 index 0000000..f6afedf --- /dev/null +++ b/tasks/build.js @@ -0,0 +1,3 @@ +import gulp from "gulp"; + +gulp.task("build", ["build-lib", "build-spec"]); diff --git a/tasks/test-browsers.js b/tasks/test-browsers.js new file mode 100644 index 0000000..11c16b9 --- /dev/null +++ b/tasks/test-browsers.js @@ -0,0 +1,24 @@ +var gulp = require("gulp"); +var karma = require("karma").server; + +gulp.task("test-browsers", ["build"], function (done) { + /** + * This ensures that the browser tests only run on the first job, + * instead of wastefully running the browser tests on every job. + */ + if (process.env.TRAVIS_BUILD_NUMBER) { + if (process.env.TRAVIS_JOB_NUMBER === `${process.env.TRAVIS_BUILD_NUMBER}.1`) { + runKarma(done); + } else { + done(); + } + } else { + runKarma(done); + } +}); + +function runKarma(done) { + karma.start({ + configFile: __dirname + "/../.karma.conf.js" + }, done); +} diff --git a/tasks/test-local.js b/tasks/test-local.js new file mode 100644 index 0000000..f6a843a --- /dev/null +++ b/tasks/test-local.js @@ -0,0 +1,20 @@ +import gulp from "gulp"; +import mocha from "gulp-mocha"; +import istanbul from "gulp-istanbul"; +import paths from "../paths.json"; + +import chai from "chai"; +chai.should(); // This enables should-style syntax + +gulp.task("test-local", ["build"], (cb) => { + gulp.src(paths.build.lib) + .pipe(istanbul()) // Covering files + .pipe(istanbul.hookRequire()) // Force `require` to return covered files + .on("finish", () => { + gulp.src(paths.build.spec) + .pipe(mocha()) + .pipe(istanbul.writeReports({dir: `${__dirname}/../`, reporters: ["text-summary", "lcovonly"]})) // Creating the reports after tests ran + //.pipe(istanbul.enforceThresholds({ thresholds: { global: 90 } })) // Enforce a coverage of at least 90% + .on("end", cb); + }); +}); diff --git a/tasks/test.js b/tasks/test.js new file mode 100644 index 0000000..74c63dc --- /dev/null +++ b/tasks/test.js @@ -0,0 +1,3 @@ +import gulp from "gulp"; + +gulp.task("test", ["test-local", "test-browsers"]); From 65462b2b08b2beafb9e91eb01119cb281404f089 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Mon, 29 Jun 2015 20:15:00 -0300 Subject: [PATCH 02/15] partial advance with dependencies and source code --- .gitignore | 3 + .travis.yml | 4 +- README.md | 6 +- es5/lib/collection.js | 183 +++ es5/lib/dovima.js | 28 - es5/lib/model.js | 1160 ++++++++++++++ es5/lib/modelFinder.js | 198 +++ es5/lib/validation/isPresent.js | 132 ++ es5/spec/dovima.spec.js | 19 - es5/spec/fixtures/users.json | 27 + es5/spec/model.spec.js | 2063 +++++++++++++++++++++++++ es5/spec/validation/isPresent.spec.js | 346 +++++ es6/lib/collection.js | 9 +- es6/lib/dovima.js | 977 ------------ es6/lib/model.js | 163 +- es6/lib/modelFinder.js | 2 +- es6/lib/validation/isPresent.js | 120 ++ es6/spec/dovima.spec.js | 13 - es6/spec/fixtures/users.json | 27 + es6/spec/model.spec.js | 420 ++++- es6/spec/validation/isPresent.spec.js | 289 ++++ gulpfile.babel.js | 2 + package.json | 23 +- paths.json | 8 +- tasks/build-lib-assets.js | 9 + tasks/build-resources.js | 8 + tasks/build-spec-assets.js | 9 + tasks/build.js | 2 +- 28 files changed, 5080 insertions(+), 1170 deletions(-) create mode 100644 es5/lib/collection.js delete mode 100644 es5/lib/dovima.js create mode 100644 es5/lib/model.js create mode 100644 es5/lib/modelFinder.js create mode 100644 es5/lib/validation/isPresent.js delete mode 100644 es5/spec/dovima.spec.js create mode 100644 es5/spec/fixtures/users.json create mode 100644 es5/spec/model.spec.js create mode 100644 es5/spec/validation/isPresent.spec.js delete mode 100644 es6/lib/dovima.js create mode 100644 es6/lib/validation/isPresent.js delete mode 100644 es6/spec/dovima.spec.js create mode 100644 es6/spec/fixtures/users.json create mode 100644 es6/spec/validation/isPresent.spec.js create mode 100644 tasks/build-lib-assets.js create mode 100644 tasks/build-resources.js create mode 100644 tasks/build-spec-assets.js diff --git a/.gitignore b/.gitignore index 123ae94..7070605 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ build/Release # Dependency directory # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git node_modules + +# lcov file +lcov.info diff --git a/.travis.yml b/.travis.yml index 6a65055..f088f4e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,5 +12,5 @@ after_success: - ./node_modules/coveralls/bin/coveralls.js < ./lcov.info env: global: - - secure: V20j/DfVZvCrNpIs6NiZPoGM89FoB8Ek+BnVA9InAXjJpnSB2uhdzzLjpHUF6viiPMUi24HxzYtIHneSAxv6NM1q6smNW2vwV6CwX/VEy2M4mXl70CCBrVWHxUPgxwnUohujyhmnQZE6PMkg0UEsLZerxAzD2zVmmmjikvY5dJjSTMrJHXK06cPgceZ+UFft2AAqUYqujTclx/WlXLNxgJ4JNXUZojNNXwPnAVSj7LKYpFOaKg9gNc3NgIJzzUrMu7sP+0c9wHqJjzcSX4csFY5hdsvxqc3p80GA2RsWUo/riaFODVs8aqpLqRfOgjvJzeOXDCCcfwjwcz2YJgObCnodMbkRRveuP2kVTCvo30Uf8D7D/KYft0wFPgyC5uKWxmEm+8DqzMmQ6K6p5YOtVPPad/GBc+3yijHDmKgnWpCyy3TSzB2H4EJ9KR0o2a5HnmcmqG212FuhfhgKJDKP7cs++BPpA9pmdH9LUViuQqKvwKi7kAUnHlMwo/shDV45Mhs3I4FXX7GPQTkLu6PQrYAUtTGYHTgpNtdn0r1tZ8doubE0Ow0NgWiujs8gBSRAuSu3N+VygiaS1W/s9anZpNSmjK1FQbtFNd4SULcWreu5O7JbKEAvVDdMGaRHwixSM8MHRpBhDs2Umq9YC6w6rJ7ibLmtByZVJsVEXVzRggY= - - secure: tqb/M1+qSiClaQzCmss9PEmGVvrHmE/DhaX3GJ6FFPGmb+zu2gvCp+ukr6CCiGktgcolJZX3j65/L5tLMYqdhnDawv4wtonmkhG1JT9xZzf/1cgiWB+WBe8NSkxv0v1JYwzrYQ4uA6F/RizL0G8WOjJyxL6Uf6Zlh62SL4TVtHRvZE+QMOzIhXyws3Nt18n+z6EnS5ITtdhsuxUeO2v9kMfgXKhbMA5UKYUnUMAwEzZqcxjXNe8LAcMIcAg4FTPTkve05yvMDlusmmu9v9jyM/VAjNorz/N8TRkWmjgiQDlYk8/6EoyFVhMoj8FggRXETITo6T1/tSIJ4TyvlroYQVTczGGSuyTF/IJQbm84FIkqhaLf9eQjFvIim3pRpEsn42ATgJeaMjzER5wsAn+/Nb89cbC0zSkQjjwuhRNK6oDtKdkm4YO96OAOUlur2QCjlfSVZrayE3L5u6wAnlXhY9jzXWDBcirT1HdY7fsO3/0147+MlakZTJJrc5MRV5jazySb07WgRG/GZLidfQOEShJhA+1SgmuI44qdjkzrn5wP4b2u3PckNQGdugFutoaJ0xy66My54myocDlPyi3dNbrnW/5Ur9XT5ucBh7DtOxUTjjP8eqAj9vNIT4oViaSq9F55GlIkz33pdVwVB9pD6WjvB9knLhPaolzru/hwuRs= + - secure: i1pENwoJlg0NEyl42AYoOcC6fqA//3FXpLjsaIxx5U1485ikz7KuZ6ejYHmvbQxu1HSy+E4dgri9nUZSexrwyDU4ZB3uc9RjI+BkDerJaa9uWqjLUdPEDgkqGayJc5rhz25u7ykND4EOYoU9Q2vqgjqj7hJxmEzW5g/VStsUcV0BP8Xef2WJ4ubt3+8/f9/EZYBugr5LV0STp/a3A52/QyV+Td8+/TsUiReidFlUCiXB+6IVMRxdLX380iWvs2AKcIntW5MTFh6EyUGqUjXfJTZUUZibOsT+QwMEntKl7jydWPIxcwB+MSqYUBgreivvpN75MZbUfT919FoOQAKqndv9nNOFIPq+7vGiraxzgaHcbY/oSI1mkA+OhG0JW3gQXhvjDlPpDHJHIFovExk+G4/N7wEwvPFAuRB2SZN2WHPVp5sFHZ2hcom6vB//9YIgCLI1sfffp3wuuTqB3Psv3QYZhvOrSJ75fmPTpGLWrLf5WqL0IngsJUse9zT3NDTZF/GzKd2SUeWyhXsIIvSHHJLE/EWsR9eRUhE11JtdIhrbX+sLSoXdT54xYbF5ZYVDfe40VetUu1mpCLp83Z0RLqLgAGBEt4fqOrXEbqCnmLdIa1PTKYvrexwtPbIemI3g5up/L3WSxRUY6XxNVvA5e/P/hQNZo946v0QB5TI8tuk= + - secure: ERQRWonb1ZOGDcgTSkZxoHzyGLPrrftyyfQez4tEdUQQikzMk+dwAT8OiLe9slLQ6JqbvKXY8M3mrFD3hlfkIcoZw2plMDhB6ocw0oiD7yKDFGgO3Ff6a9x4gDC2oslDpXxZ3anOyF6M0XyHHJbYy1/XbMdJ6cgLXQoOIYZXxTBmFD/6RKsV0TBKPWCV1eQ+5uuaPeSNpfKoUgohFvFtLflMgDNTyhkoaH0/siK+ThUi745ycnAoclaIf60hea6mN8fddnxKkyckbi2WfXkKtXrl718q6exK5Ba8JXWJiLL+VtT4FB3KTZTKP46eMHRL/LWXd58lLdRyTn1W1qST95tn8koEJgDBXPG2WUPByMCyoEiYCLn9wp+AlTJqUEVdiB4C6ajvNTF5k6wmXvP3AWjbkLphKxCqLmCQ3o0rkwjNC4KB0cVeLBZYEfQMq0wq/hnlixrgYT81/hd/OxcS79zQPa7eblOsBNO3U7goi4CKjOqPyWqAK3Wi3JwyG+0B8YBiSakMwdZrz9Rin0DdmGOyQgNz972OwaFyng5B6EdYr2YkS6iRckPsCAT8OXPtVGl7kXC50zTiUY2wb4SLIbwZAJxV9VhGX9Izt7+1X4xUWxNbwDkDOir02Hg6i6ZcAqykf2B7jJKnINIUGzPjmadC1Gf7WiOTZzxUC0Je2Ro= diff --git a/README.md b/README.md index c7f8747..86fc81f 100644 --- a/README.md +++ b/README.md @@ -59,9 +59,7 @@ define(["require"] , function (require) { # Getting Started -## More insights - -In order to say something, you should know that `dovima()` ... (add your test here) +You should create on some directory a config # How to Contribute @@ -78,5 +76,3 @@ It's easy to run the test suite locally, and *highly recommended* if you're usin ``` npm test ``` - - diff --git a/es5/lib/collection.js b/es5/lib/collection.js new file mode 100644 index 0000000..4de7956 --- /dev/null +++ b/es5/lib/collection.js @@ -0,0 +1,183 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + +var _jargon = require("jargon"); + +var _jargon2 = _interopRequireDefault(_jargon); + +var _modelFinderJs = require("./modelFinder.js"); + +var _modelFinderJs2 = _interopRequireDefault(_modelFinderJs); + +var _modelJs = require("./model.js"); + +var _modelJs2 = _interopRequireDefault(_modelJs); + +var Collection = (function (_Array) { + function Collection(initialData) { + _classCallCheck(this, Collection); + + _get(Object.getPrototypeOf(Collection.prototype), "constructor", this).call(this); + + var association = null, + modelConstructor = null; + + if (initialData && typeof initialData === "function") { + modelConstructor = initialData; + } else { + association = initialData; + } + + Object.defineProperties(this, { + "association": { + enumerable: true, + writable: true, + value: association + }, + "_modelConstructor": { + enumerable: true, + value: modelConstructor + } + }); + } + + _inherits(Collection, _Array); + + _createClass(Collection, [{ + key: "push", + value: function push() { + var _this = this; + + for (var _len = arguments.length, models = Array(_len), _key = 0; _key < _len; _key++) { + models[_key] = arguments[_key]; + } + + models.forEach(function (model) { + if (_this.indexOf(model) === -1) { + if (_this.association) { + //proceed with the regular push + _get(Object.getPrototypeOf(Collection.prototype), "push", _this).call(_this, model); + //set inverse relationship + var pluralForeignName = (0, _jargon2["default"])(_this.association.foreignName).plural.toString(); + if (model.hasOwnProperty(_this.association.foreignName)) { + if (model[_this.association.foreignName] !== _this.association.parent) { + model[_this.association.foreignName] = _this.association.parent; + } + } else if (model.hasOwnProperty(pluralForeignName)) { + model[pluralForeignName].push(_this.association.parent); + } + //if it has through, create the intermediate model + if (_this.association.through) { + //get through association + var throughAssociation = _this.association.parent.associations[_this.association.through]; + if (!throughAssociation) { + var modelName = undefined; + if (model && model.constructor) { + modelName = model.constructor.name; + } else { + modelName = model; + } + throw new Error("Through association called " + _this.association.through + " not defined on model " + modelName); + } + + //lookup if there is an existing through model... how? + var throughModel = new throughAssociation.constructor(); + throughModel[_this.association.foreignName] = _this.association.parent; + + // work in progress for future automations + // let throughAssociationPropertyName = model.associations[throughAssociationNameOnModel].foreignName; + // throughModel[this.association.foreignName] = this.association.parent; + // throughModel[throughAssociationPropertyName] = model; + // HERE I WILL NEED THE RELATIONSHIP BETWEEN MODEL and THROUGH MODEL... + // throughModel[this.association.parent.foreignName] = this.association.parent; + } + } else { + if (model instanceof _this._modelConstructor) { + _get(Object.getPrototypeOf(Collection.prototype), "push", _this).call(_this, model); + } else { + var modelName = undefined; + if (model && model.constructor) { + modelName = model.constructor.name; + } else { + modelName = model; + } + + throw TypeError("The model " + modelName + " is not an instance of " + _this._modelConstructor.name + ", therefore, it cannot be pushed to this collection."); + } + } + } + }, this); + } + }, { + key: "fetch", + value: function fetch(callback) { + var _this2 = this; + + if (this.association) { + (function () { + var processWhereCondition = function processWhereCondition(value) { + if (typeof value === "string") { + var snakeCasedValue = (0, _jargon2["default"])(value).snake.toString(); + return snakeCasedValue; + } else { + return value; + } + }; + + var modelFinder = new _modelFinderJs2["default"](_this2.association.constructor.database); + + var query = modelFinder.find(_this2.association.constructor).where(_this2.association.foreignKey, "=", _this2.association.parent.id); + + if (_this2.association.where) { + (function () { + var processedWhereConditions = _this2.association.where.map(processWhereCondition); + var self = _this2; + query.andWhere(function () { + var _this3 = this; + + this.where.apply(this, _toConsumableArray(processedWhereConditions)); + + if (Array.isArray(self.association.andWhere)) { + self.association.andWhere.forEach(function (whereConditions) { + var processedAndWhereItem = whereConditions.map(processWhereCondition); + _this3.andWhere.apply(_this3, _toConsumableArray(processedAndWhereItem)); + }); + } + }); + })(); + } + + query.results(function (error, models) { + _this2.splice(0, _this2.length); + models.forEach(function (model) { + _this2.push(model); + }); + callback(error); + }); + })(); + } else { + throw new Error("Cannot fetch collection without an association set. Call Model.all instead."); + } + } + }]); + + return Collection; +})(Array); + +exports["default"] = Collection; +module.exports = exports["default"]; \ No newline at end of file diff --git a/es5/lib/dovima.js b/es5/lib/dovima.js deleted file mode 100644 index 4b34cb8..0000000 --- a/es5/lib/dovima.js +++ /dev/null @@ -1,28 +0,0 @@ -"use strict"; - -Object.defineProperty(exports, "__esModule", { - value: true -}); - -var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); - -function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - -var Dovima = (function () { - function Dovima() { - _classCallCheck(this, Dovima); - } - - _createClass(Dovima, [{ - key: "saySomething", - value: function saySomething() { - //do your stuff here - return "Something"; - } - }]); - - return Dovima; -})(); - -exports["default"] = Dovima; -module.exports = exports["default"]; \ No newline at end of file diff --git a/es5/lib/model.js b/es5/lib/model.js new file mode 100644 index 0000000..d273bd1 --- /dev/null +++ b/es5/lib/model.js @@ -0,0 +1,1160 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +/* Component Dependencies */ +// + +var _blunder = require("blunder"); + +var _blunder2 = _interopRequireDefault(_blunder); + +var _fleming = require("fleming"); + +var _fleming2 = _interopRequireDefault(_fleming); + +var _jargon = require("jargon"); + +var _jargon2 = _interopRequireDefault(_jargon); + +var _modelFinderJs = require("./modelFinder.js"); + +var _collectionJs = require("./collection.js"); + +var _collectionJs2 = _interopRequireDefault(_collectionJs); + +var _modelFinderJs2 = _interopRequireDefault(_modelFinderJs); + +var flowsync = require("flowsync"); + +/* Private Method Symbols */ +var callDeep = Symbol(), + addAssociation = Symbol(), + getFieldAttributes = Symbol(), + parseAttributesFromFields = Symbol(), + setAttributes = Symbol(), + attributes = Symbol(), + associations = Symbol(), + properties = Symbol(), + validations = Symbol(), + isNew = Symbol(), + fetchBy = Symbol(); + +/** + * @class Model + */ + +var Model = (function () { + /** + * @param {Object.} [initialAttributes] Provide default values for attributes by passing a Key-Value Object. + * @constructor + */ + + function Model(initialAttributes) { + var _this = this; + + _classCallCheck(this, Model); + + ["_validations", "_associations"].forEach(function (privatePropertyName) { + Object.defineProperty(_this, privatePropertyName, { + writable: true, + enumerable: false, + value: {} + }); + }); + /** + * Define dynamic properties + */ + Object.defineProperties(this, { + "_includeAssociations": { + enumerable: false, + writable: true, + value: [] + }, + + "isNew": { + get: this[isNew] + }, + + "attributes": { + get: this[attributes], + set: this[setAttributes] + }, + + "associations": { + get: this[associations] + }, + + "properties": { + get: this[properties] + }, + + "validations": { + get: this[validations] + }, + + "_tableName": { + enumerable: false, + writable: true + }, + + "tableName": { + get: function get() { + return _this._tableName || (0, _jargon2["default"])(_this.constructor.name).plural.snake.toString(); + }, + set: function set(newTableName) { + _this._tableName = newTableName; + } + }, + + "_primaryKey": { + enumerable: false, + writable: true + }, + + "primaryKey": { + get: function get() { + return _this._primaryKey || "id"; + }, + set: function set(newPrimaryKey) { + _this._primaryKey = newPrimaryKey; + } + }, + + "_softDelete": { + enumerable: false, + writable: true, + value: false + }, + + "softDelete": { + get: function get() { + _this._softDelete = true; + } + } + }); + + this.associate(); + this.validate(); + + this[setAttributes](initialAttributes); + + this.initialize(); + } + + _createClass(Model, [{ + key: "hasOne", + value: function hasOne(associationName, associationConstructor) { + return this[addAssociation]({ + name: associationName, + constructor: associationConstructor, + type: "hasOne" + }); + } + }, { + key: "belongsTo", + value: function belongsTo(associationName, associationConstructor) { + return this[addAssociation]({ + name: associationName, + constructor: associationConstructor, + type: "belongsTo" + }); + } + }, { + key: "hasMany", + value: function hasMany(associationName, associationConstructor) { + return this[addAssociation]({ + name: associationName, + constructor: associationConstructor, + type: "hasMany" + }); + } + }, { + key: "ensure", + value: function ensure(attributeName, validatorFunction, validatorMessage) { + this._validations[attributeName] = this._validations[attributeName] || []; + + var validatorDetails = { validator: validatorFunction }; + + if (validatorMessage) { + validatorDetails.message = validatorMessage; + } + + this._validations[attributeName].push(validatorDetails); + } + }, { + key: "isValid", + + /** + * Return a boolean indicating whether the model is valid or not. + * + * @method isValid + * @param {Function(boolean)} callback Callback returning the boolean. + */ + value: function isValid(callback) { + this.invalidAttributes(function (invalidAttributeList) { + callback(Object.keys(invalidAttributeList).length === 0); + }); + } + }, { + key: "invalidAttributes", + + /** + * Return an object containing all invalid attributes and their errors. + * + * @example + * ``` + * model.invalidAttributes((invalidAttributeList) => { + * console.log(invalidAttributeList); // {"name":["Cannot contain special characters", "Cannot contain numbers"], "age":["Cannot be under 18"]} + * }); + * ``` + * + * @method invalidAttributes + * @param {Function(invalidAttributeList)} callback Callback returning the invalid attribute list. + */ + value: function invalidAttributes(callback) { + var _this2 = this; + + var attributeNamesWithValidators = Object.keys(this._validations); + + var compileInvalidAttributeList = function compileInvalidAttributeList(errors, validatorMessages) { + if (errors) { + throw errors; + } else { + var invalidAttributeList = {}; + + for (var index = 0; index < attributeNamesWithValidators.length; index++) { + var invalidMessages = validatorMessages[index]; + + if (invalidMessages.length > 0) { + var attributeName = attributeNamesWithValidators[index]; + invalidAttributeList[attributeName] = invalidMessages; + } + } + + callback(invalidAttributeList); + } + }; + + var performValidationsForAttribute = function performValidationsForAttribute(attributeName, done) { + var attributeValidations = _this2._validations[attributeName]; + + var performValidation = function performValidation(validation, returnValue) { + var validator = validation.validator; + + validator.call(_this2, attributeName, function (error, validatorDetails) { + if (validatorDetails.result) { + returnValue(null, null); + } else { + returnValue(null, validation.message || validatorDetails.message); + } + }); + }; + + var compileValidatorResponses = function compileValidatorResponses(error, invalidMessages) { + var cleanedMessages = []; + // Trick to remove falsy values from an array + var _iteratorNormalCompletion = true; + var _didIteratorError = false; + var _iteratorError = undefined; + + try { + for (var _iterator = invalidMessages[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { + var message = _step.value; + + message && cleanedMessages.push(message); + } + } catch (err) { + _didIteratorError = true; + _iteratorError = err; + } finally { + try { + if (!_iteratorNormalCompletion && _iterator["return"]) { + _iterator["return"](); + } + } finally { + if (_didIteratorError) { + throw _iteratorError; + } + } + } + + done(null, cleanedMessages); + }; + + flowsync.mapParallel(attributeValidations, performValidation, compileValidatorResponses); + }; + + flowsync.mapSeries(attributeNamesWithValidators, performValidationsForAttribute, compileInvalidAttributeList); + } + }, { + key: "include", + value: function include() { + for (var _len = arguments.length, associationNames = Array(_len), _key = 0; _key < _len; _key++) { + associationNames[_key] = arguments[_key]; + } + + this._includeAssociations = associationNames; + return this; + } + }, { + key: "fetch", + value: function fetch() { + for (var _len2 = arguments.length, options = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) { + options[_key2] = arguments[_key2]; + } + + switch (options.length) { + case 0: + this[fetchBy](); + break; + case 1: + if (typeof options[0] === "function") { + this[fetchBy]([this.primaryKey], options[0]); + } else if (Array.isArray(options[0])) { + this[fetchBy](options[0]); + } else { + this[fetchBy]([options[0]]); + } + break; + case 2: + if (Array.isArray(options[0])) { + this[fetchBy](options[0], options[1]); + } else { + this[fetchBy]([options[0]], options[1]); + } + break; + } + } + }, { + key: fetchBy, + value: function value(fields, callback) { + var _this3 = this; + + if (fields === undefined) fields = [this.primaryKey]; + + if (!this.constructor.database) { + throw new Error("Cannot fetch without Model.database set."); + } + + var chain = this.constructor.database.select("*").from(this.tableName); + fields.forEach(function (field, index) { + if (!_this3[field]) { + throw new Error("Cannot fetch this model by the '" + field + "' field because it is not set."); + } + + if (index === 0) { + chain = chain.where(field, "=", _this3[field]); + } else { + chain = chain.andWhere(field, "=", _this3[field]); + } + }, this); + + if (this._softDelete) { + chain = chain.whereNull((0, _jargon2["default"])("deletedAt").snake.toString()); + } + + chain.limit(1).results(function (error, records) { + if (records.length === 0) { + callback(new Error("There is no " + _this3.constructor.name + " for the given (" + fields.join(", ") + ").")); + } else { + _this3[parseAttributesFromFields](records[0]); + + if (_this3._includeAssociations.length > 0) { + (function () { + var modelFinder = new _modelFinderJs2["default"](_this3.constructor.database); + + var associations = _this3.associations; + + /* We'll be putting all of our Async tasks into this */ + var fetchTasks = []; + + _this3._includeAssociations.forEach(function (associationName) { + + var association = associations[associationName]; + + if (!association) { + throw new Error("Cannot fetch '" + associationName + "' because it is not a valid association on " + _this3.constructor.name); + } + + switch (association.type) { + case "hasOne": + fetchTasks.push(function (finished) { + + // user hasMany address + + var ModelClass = association.constructor; + + if (association.through) { + var throughAssociation = associations[association.through]; + + //throw throughAssociation.foreignId; + //select * from Addresses where user_id = this[this.primaryKey] + //select * from PostalCodes where address_id = address.id + if (!_this3[_this3.primaryKey]) { + throw new Error("'" + _this3.primaryKey + "' is not set on " + _this3.constructor.name); + } + + modelFinder.find(throughAssociation.constructor).where(association.foreignId, "=", _this3[_this3.primaryKey]).limit(1).results(function (errors, models) { + var joinModel = models[0]; + var destinationAssociation = joinModel.associations[associationName]; + + //throw destinationAssociation.foreignId; + + //throw joinModel;//throw model.associations; + //addressId + + var tempModel = new association.constructor(); + modelFinder.find(association.constructor).where(tempModel.primaryKey, "=", joinModel[destinationAssociation.foreignId]).limit(1).results(function (associationError, associationModels) { + var associationModel = associationModels[0]; + _this3[associationName] = associationModel; + finished(); + }); + }); + } else { + (function () { + var query = modelFinder.find(ModelClass).where(association.foreignKey, "=", _this3[_this3.primaryKey]); + + var processWhereCondition = function processWhereCondition(value) { + if (typeof value === "string") { + var snakeCasedValue = (0, _jargon2["default"])(value).snake.toString(); + return snakeCasedValue; + } else { + return value; + } + }; + + var processedWhere = association.where.map(processWhereCondition); + + query.andWhere(function () { + var _this4 = this; + + this.where.apply(this, _toConsumableArray(processedWhere)); + + if (Array.isArray(association.andWhere)) { + association.andWhere.forEach(function (andWhereItem) { + var processedAndWhereItem = andWhereItem.map(processWhereCondition); + _this4.andWhere.apply(_this4, _toConsumableArray(processedAndWhereItem)); + }); + } + }); + + query.limit(1).results(function (errors, models) { + var model = models[0]; + _this3[associationName] = model; + finished(); + }); + })(); + } + }); + break; + + case "hasMany": + + if (association.through) { + fetchTasks.push(function (finished) { + + var throughAssociation = associations[association.through]; + + modelFinder.find(throughAssociation.constructor).where(association.foreignId, _this3[_this3.primaryKey]).results(function (errors, models) { + if (models.length > 0) { + (function () { + var foreignAssociationName = association.as || associationName; + + if (!models[0].associations[foreignAssociationName]) { + throw new Error("'" + foreignAssociationName + "' is not a valid association on through model '" + throughAssociation.constructor.name + "'"); + } + + var destinationAssociation = models[0].associations[foreignAssociationName]; + + var modelIds = []; + + var tempModel = new association.constructor(); + + switch (destinationAssociation.type) { + case "hasOne": + //throw {through: throughAssociation, destination: destinationAssociation}; + + modelIds = models.map(function (model) { + return model[throughAssociation.foreignId]; + }); + + modelFinder.find(association.constructor).where(tempModel.primaryKey, "in", modelIds).results(function (errors, models) { + models.forEach(function (model) { + _this3[associationName].push(model); + }); + finished(); + }); + + break; + + case "hasMany": + modelIds = models.map(function (model) { + return model[model.primaryKey]; + }); + + modelFinder.find(association.constructor).where(destinationAssociation.foreignId, "in", modelIds).results(function (errors, models) { + models.forEach(function (model) { + _this3[associationName].push(model); + }); + finished(); + }); + break; + case "belongsTo": + //throw {through: throughAssociation, destination: destinationAssociation}; + + //throw destinationAssociation.name; + + //throw associationName; + + //const localId = inflect(destinationAssociation.name).foreignKey.camel.toString(); + + modelIds = models.map(function (model) { + return model[destinationAssociation.foreignId]; + }); + + modelFinder.find(association.constructor).where(tempModel.primaryKey, "in", modelIds).results(function (errors, models) { + models.forEach(function (model) { + _this3[associationName].push(model); + }); + finished(); + }); + + break; + } + + //throw {association: association.foreignName, destinationAssociation: destinationAssociation.foreignName, throughAssociation: throughAssociation.foreignName}; + //throw {association: association.foreignId, destinationAssociation: destinationAssociation.foreignId, throughAssociation: throughAssociation.foreignId}; + //throw models; + })(); + } + }); + + // if (!this[throughAssociation.foreignId]) { + // throw new Error(`'${throughAssociation.foreignId}' is not set on ${this.constructor.name}`); + // } + + // modelFinder + // .find(throughAssociation.constructor) + // .where(this.primaryKey, "=", this[throughAssociation.foreignId]) + // .limit(1) + // .results((errors, models) => { + // const joinModel = models[0]; + // const destinationAssociation = joinModel.associations[associationName]; + + // //throw joinModel;//throw model.associations; + + // modelFinder + // .find(association.constructor) + // .where(this.primaryKey, "=", joinModel[destinationAssociation.foreignId]) + // .results((associationError, associationModels) => { + // const associationModel = associationModels[0]; + // this[associationName] = associationModel; + // }); + + // this[associationName] = joinModel; + // finished(); + // }); + }); + } else { + fetchTasks.push(function (finished) { + _this3[associationName].fetch(finished); + }); + } + break; + + case "belongsTo": + if (!_this3[association.foreignId]) { + throw new Error("Cannot fetch '" + associationName + "' because '" + association.foreignId + "' is not set on " + _this3.constructor.name); + } + + fetchTasks.push(function (finished) { + modelFinder.find(association.constructor).where(_this3.primaryKey, "=", _this3[association.foreignId]).limit(1).results(function (errors, models) { + var model = models[0]; + _this3[associationName] = model; + model[association.foreignName] = _this3; + finished(); + }); + }); + + } + }); + + flowsync.parallel(fetchTasks, function () { + if (callback) { + callback(error, _this3); + } + }); + })(); + } else { + if (callback) { + callback(error, _this3); + } + } + } + }); + } + }, { + key: "delete", + value: function _delete(callback) { + var _this5 = this; + + if (this._softDelete) { + if (!this.constructor.database) { + throw new Error("Cannot delete without Model.database set."); + } + + if (this[this.primaryKey]) { + flowsync.series([function (next) { + _this5[callDeep]("delete", function (associationDetails) { + return associationDetails.type !== "belongsTo" && associationDetails.dependent === true; + }, next); + }, function (next) { + var now = new _fleming2["default"](); + var attributesToUpdate = {}; + attributesToUpdate[(0, _jargon2["default"])("deletedAt").snake.toString()] = now.toDate(); + _this5.constructor.database.update(attributesToUpdate).into(_this5.tableName).where(_this5.primaryKey, "=", _this5[_this5.primaryKey]).results(function (error, results) { + if (error) { + next(error); + } else if (results === 0) { + next(new Error(_this5.constructor.name + " with " + _this5.primaryKey + " " + _this5[_this5.primaryKey] + " cannot be soft deleted because it doesn't exists.")); + } else { + next(); + } + }); + }], function (errors, results) { + callback(errors, results); + }); + } else { + throw new Error("Cannot delete the " + this.constructor.name + " because the primary key is not set."); + } + } else { + throw new Error("Not implemented."); + } + } + }, { + key: "save", + value: function save(callback) { + var _this6 = this; + + if (!this.constructor.database) { + throw new Error("Cannot save without Model.database set."); + } + + flowsync.series([function (next) { + _this6.beforeValidation(next); + }, function (next) { + _this6.isValid(function (valid) { + if (valid) { + next(); + } else { + _this6.invalidAttributes(function (invalidAttributeList) { + var hasInvalidAttributes = Object.keys(invalidAttributeList).length > 0; + + if (hasInvalidAttributes) { + var multiError = new _blunder2["default"](); + for (var invalidAttributeName in invalidAttributeList) { + var invalidAttributeMessages = invalidAttributeList[invalidAttributeName]; + + var errorPrefix = _this6.constructor.name + " is invalid"; + + var attributesMultiError = new _blunder2["default"]([], errorPrefix); + for (var index in invalidAttributeMessages) { + var invalidAttributeMessage = invalidAttributeMessages[index]; + var error = new Error(invalidAttributeName + " " + invalidAttributeMessage); + attributesMultiError.push(error); + } + multiError.push(attributesMultiError); + } + next(multiError); + } else { + next(); + } + }); + } + }); + }, function (next) { + _this6.beforeSave(next); + }, function (next) { + if (_this6.isNew) { + var now = new _fleming2["default"](); + _this6.createdAt = now.toDate(); + var fieldAttributes = _this6[getFieldAttributes](); + + _this6.constructor.database.insert(fieldAttributes).into(_this6.tableName).results(function (error, ids) { + if (error) { + next(error); + } else { + _this6[_this6.primaryKey] = ids[0]; + next(); + } + }); + } else { + var now = new _fleming2["default"](); + _this6.updatedAt = now.toDate(); + var _attributes = _this6[getFieldAttributes](); + var updateAttributes = {}; + + for (var attributeName in _attributes) { + if (attributeName !== _this6.primaryKey) { + updateAttributes[attributeName] = _attributes[attributeName]; + } + } + + _this6.constructor.database.update(updateAttributes).into(_this6.tableName).where(_this6.primaryKey, "=", _this6[_this6.primaryKey]).results(next); + } + }, function (next) { + //disabling this rule because break is not necessary when return is present + /* eslint-disable no-fallthrough */ + _this6[callDeep]("save", function (associationDetails) { + switch (associationDetails.type) { + case "hasOne": + return true; + case "hasMany": + if (associationDetails.through === undefined) { + return true; + } else { + return false; + } + case "belongsTo": + return false; + } + }, next); + }, function (next) { + _this6.afterSave(next); + }], function (errors) { + if (errors) { + callback(errors); + } else { + callback(undefined, _this6); + } + }); + } + }, { + key: "beforeValidation", + + /* Stubbed methods for hooks */ + value: function beforeValidation(callback) { + callback(); + } + }, { + key: "beforeSave", + value: function beforeSave(callback) { + callback(); + } + }, { + key: "afterSave", + value: function afterSave(callback) { + callback(); + } + }, { + key: "associate", + value: function associate() {} + }, { + key: "validate", + value: function validate() {} + }, { + key: "initialize", + value: function initialize() {} + }, { + key: "toJSON", + value: function toJSON() { + if (Model.jsonFormatter && typeof Model.jsonFormatter === "function") { + return Model.jsonFormatter(this); + } else { + return this.attributes; + } + } + }, { + key: setAttributes, + + /** + * Private Functionality + */ + + value: function value(newAttributes) { + this[parseAttributesFromFields](newAttributes); + } + }, { + key: associations, + value: function value() { + return this._associations; + } + }, { + key: properties, + value: function value() { + return Object.keys(this); + } + }, { + key: validations, + value: function value() { + return this._validations; + } + }, { + key: attributes, + value: function value() { + var _this7 = this; + + var attributeNames = {}; + this.properties.forEach(function (propertyName) { + if (!_this7._associations[propertyName]) { + attributeNames[propertyName] = _this7[propertyName]; + } + }); + return attributeNames; + } + }, { + key: isNew, + value: function value() { + if (this[this.primaryKey]) { + return false; + } else { + return true; + } + } + }, { + key: callDeep, + + /** + * Call a function deeply through all associations + * + * @private + * @method callDeep + * @param {String} functionName The name of the function that you want to fire deeply. + * @param {function(errors, results)} Function called at the end of the operation. + */ + value: function value(methodName, predicate, callback) { + var _this8 = this; + + var associationNames = Object.keys(this.associations); + + flowsync.mapParallel(associationNames, function (associationName, next) { + + var associationDetails = _this8.associations[associationName]; + + switch (associationDetails.type) { + case "belongsTo": + case "hasOne": + var model = _this8[associationName]; + if (model) { + //pass the associationDetails.whereArgs to the function + var result = predicate(associationDetails); + if (result) { + model[methodName](next); + } else { + next(); + } + } else { + next(); + } + break; + + case "hasMany": + var collection = _this8[associationName]; + //collection set, and not many to many (nothing in that case) + if (collection) { + //let array = [].slice.call(collection); + flowsync.eachParallel(collection, function (collectionModel, finishSubStep) { + var result = predicate(associationDetails); + if (result) { + collectionModel[methodName](finishSubStep); + } else { + next(); + } + }, next); + } else { + next(); //collection not set + } + break; + } + }, function (errors, results) { + callback(errors, results); + }); + } + }, { + key: addAssociation, + + /** + * @example + * ``` + * const associationDetails = { + * name: "users", + * type: "hasMany", + * constructor: User + * }; + * + * [addAssociation](associationDetails); + * ``` + * @method addAssociation + * @param {Object.} associationDetails Object containing association details in key/value pairs. + */ + value: function value(associationDetails) { + var _this9 = this; + + var association = { + parent: this, + type: associationDetails.type, + constructor: associationDetails.constructor + }; + + /* Default Values */ + + switch (associationDetails.type) { + case "hasOne": + case "hasMany": + association.foreignKey = (0, _jargon2["default"])(this.constructor.name).foreignKey.toString(); + association.foreignId = (0, _jargon2["default"])(association.foreignKey).camel.toString(); + break; + case "belongsTo": + association.foreignKey = (0, _jargon2["default"])(associationDetails.name).foreignKey.toString(); + association.foreignId = (0, _jargon2["default"])(association.foreignKey).camel.toString(); + } + + // TODO: AS + association.foreignName = (0, _jargon2["default"])(this.constructor.name).camel.toString(); + + /* Override Values */ + + var associationSetter = new AssociationSetter(association); + + /* Set private space for value to be stored internally */ + + var privateAssociationName = "_" + associationDetails.name; + + /* Association Setter By Type */ + + var setterFunction = undefined; + + switch (associationDetails.type) { + case "hasOne": + this[privateAssociationName] = null; + + setterFunction = function (newModel) { + if (newModel && _this9[privateAssociationName] !== newModel) { + if (!(newModel instanceof Model)) { + throw new Error("Cannot set a non model entity onto this property. It should inherit from Model"); + } + + _this9[privateAssociationName] = newModel; + + newModel[association.foreignName] = _this9; + } + }; + break; + case "belongsTo": + this[privateAssociationName] = null; + + setterFunction = function (newModel) { + if (newModel && _this9[privateAssociationName] !== newModel) { + if (!(newModel instanceof Model)) { + throw new Error("Cannot set a non model entity onto this property. It should inherit from Model"); + } + + _this9[privateAssociationName] = newModel; + _this9[association.foreignId] = newModel.id; + + var pluralForeignName = (0, _jargon2["default"])(association.foreignName).plural.toString(); + + if (!association.ambiguous) { + if (newModel.hasOwnProperty(association.foreignName)) { + newModel[association.foreignName] = _this9; + } else if (newModel.hasOwnProperty(pluralForeignName)) { + //lookup is it exist and dont add it in that case + newModel[pluralForeignName].push(_this9); + } else { + throw new Error("Neither \"" + association.foreignName + "\" or \"" + pluralForeignName + "\" are valid associations on \"" + newModel.constructor.name + "\""); + } + } + + // if (newModel[association.foreignName] instanceof Collection) { + // newModel[association.foreignName].push(this); + // } else { + // newModel[association.foreignName] = this; + // } + } + }; + break; + case "hasMany": + /* Set to null by default */ + this[privateAssociationName] = new _collectionJs2["default"](association); + + setterFunction = function (newModel) { + if (newModel && _this9[privateAssociationName] !== newModel) { + _this9[privateAssociationName] = newModel; + } + }; + break; + default: + throw new Error("Unknown association type."); + } + + Object.defineProperty(this, privateAssociationName, { + enumerable: false, + writable: true + }); + + Object.defineProperty(this, associationDetails.name, { + enumerable: true, + set: setterFunction, + get: function get() { + return _this9[privateAssociationName]; + } + }); + + this[associationDetails.name] = associationDetails.value; + + this._associations[associationDetails.name] = association; + + return associationSetter; + } + }, { + key: parseAttributesFromFields, + value: function value(record) { + for (var field in record) { + this[(0, _jargon2["default"])(field).camel.toString()] = record[field]; + } + } + }, { + key: getFieldAttributes, + value: function value() { + var _this10 = this; + + var attributeNames = Object.keys(this.attributes); + var fieldAttributes = {}; + attributeNames.forEach(function (attributeName) { + fieldAttributes[(0, _jargon2["default"])(attributeName).snake.toString()] = _this10[attributeName]; + }); + + //add belongsTo associations and remove others + Object.keys(this.associations).forEach(function (associationName) { + var relatedModel = _this10[associationName]; + var foreignKeyField = (0, _jargon2["default"])(associationName).foreignKey.toString(); + if (_this10._associations[associationName].type === "belongsTo") { + //try with relatedModel and relatedModel.id + if (relatedModel && relatedModel.id) { + fieldAttributes[foreignKeyField] = relatedModel.id; + } else { + //or just with the relatedModelId + //construct the snake with _id and then camelize it + var foreignIdAsAttribute = (0, _jargon2["default"])(foreignKeyField).camel.toString(); + fieldAttributes[foreignKeyField] = _this10[foreignIdAsAttribute]; + } + } else { + //console.log("getFieldAttributes delete on ", {on: this.constructor.name, associationName: associationName, foreignKeyField: foreignKeyField, relatedModel: relatedModel}); + delete fieldAttributes[associationName]; + delete fieldAttributes["_" + associationName]; + } + }); + + return fieldAttributes; + } + }]); + + return Model; +})(); + +exports["default"] = Model; + +var ambiguous = Symbol(), + dependent = Symbol(); + +var AssociationSetter = (function () { + function AssociationSetter(association) { + _classCallCheck(this, AssociationSetter); + + this.association = association; + + switch (association.type) { + case "belongsTo": + Object.defineProperties(this, { + "ambiguous": { + get: this[ambiguous] + } + }); + break; + case "hasOne": + case "hasMany": + Object.defineProperties(this, { + "dependent": { + get: this[dependent] + } + }); + break; + } + } + + _createClass(AssociationSetter, [{ + key: "foreignName", + value: function foreignName(name) { + this.association.foreignName = name; + return this; + } + }, { + key: "where", + value: function where() { + for (var _len3 = arguments.length, options = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) { + options[_key3] = arguments[_key3]; + } + + this.association.where = options; + return this; + } + }, { + key: "andWhere", + value: function andWhere() { + this.association.andWhere = this.association.andWhere || []; + + for (var _len4 = arguments.length, options = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) { + options[_key4] = arguments[_key4]; + } + + this.association.andWhere.push(options); + return this; + } + }, { + key: "through", + value: function through(associationName) { + this.association.through = associationName; + return this; + } + }, { + key: "as", + value: function as(associationName) { + this.association.as = associationName; + return this; + } + }, { + key: ambiguous, + value: function value() { + this.association.ambiguous = true; + } + }, { + key: dependent, + value: function value() { + this.association.dependent = true; + } + }]); + + return AssociationSetter; +})(); + +exports.AssociationSetter = AssociationSetter; + +Object.defineProperties(Model, { + "find": { + get: function modelFind() { + var modelQuery = new _modelFinderJs.ModelQuery(Model.database); + return modelQuery.find(this); + } + } +}); \ No newline at end of file diff --git a/es5/lib/modelFinder.js b/es5/lib/modelFinder.js new file mode 100644 index 0000000..d06d854 --- /dev/null +++ b/es5/lib/modelFinder.js @@ -0,0 +1,198 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +var _jargon = require("jargon"); + +var _jargon2 = _interopRequireDefault(_jargon); + +var _collectionJs = require("./collection.js"); + +var _collectionJs2 = _interopRequireDefault(_collectionJs); + +var validateDependencies = Symbol(); + +var ModelFinder = (function () { + function ModelFinder(database) { + _classCallCheck(this, ModelFinder); + + Object.defineProperties(this, { + "_database": { + value: database, + writable: true, + enumerable: false + } + }); + } + + _createClass(ModelFinder, [{ + key: "find", + value: function find(ModelConstructor) { + this[validateDependencies](); + + var query = new ModelQuery(this._database); + + query.find(ModelConstructor); + + return query; + } + }, { + key: "count", + value: function count(ModelConstructor) { + this[validateDependencies](); + + var query = new ModelQuery(this._database); + + query.count(ModelConstructor); + + return query; + } + }, { + key: validateDependencies, + value: function value() { + if (!this._database) { + throw new Error("Cannot find models without a database set."); + } + } + }]); + + return ModelFinder; +})(); + +exports["default"] = ModelFinder; + +var ModelQuery = (function () { + function ModelQuery(database) { + _classCallCheck(this, ModelQuery); + + this._database = database; + } + + _createClass(ModelQuery, [{ + key: "find", + value: function find(ModelConstructor) { + this.ModelConstructor = ModelConstructor; + + var tempModel = new this.ModelConstructor(); + + this._query = this._database.select("*").from(tempModel.tableName); + + return this; + } + }, { + key: "count", + value: function count(ModelConstructor) { + this.ModelConstructor = ModelConstructor; + this.countResults = true; + + var tempModel = new this.ModelConstructor(); + + this._query = this._database.select(null).count("* AS rowCount").from(tempModel.tableName); + + return this; + } + }, { + key: "where", + value: function where() { + var _query; + + for (var _len = arguments.length, options = Array(_len), _key = 0; _key < _len; _key++) { + options[_key] = arguments[_key]; + } + + var formattedOptions = options.map(function (option, index) { + if (typeof option === "string" && index === 0) { + return (0, _jargon2["default"])(option).snake.toString(); + } else { + return option; + } + }); + + (_query = this._query).where.apply(_query, _toConsumableArray(formattedOptions)); + return this; + } + }, { + key: "andWhere", + value: function andWhere() { + var _query2; + + (_query2 = this._query).andWhere.apply(_query2, arguments); + return this; + } + }, { + key: "orWhere", + value: function orWhere() { + var _query3; + + (_query3 = this._query).orWhere.apply(_query3, arguments); + return this; + } + }, { + key: "groupBy", + value: function groupBy() { + var _query4; + + (_query4 = this._query).groupBy.apply(_query4, arguments); + return this; + } + }, { + key: "orderBy", + value: function orderBy() { + var _query5; + + (_query5 = this._query).orderBy.apply(_query5, arguments); + return this; + } + }, { + key: "limit", + value: function limit() { + var _query6; + + (_query6 = this._query).limit.apply(_query6, arguments); + return this; + } + }, { + key: "results", + value: function results(callback) { + var _this = this; + + this._query.results(function (error, rows) { + if (!rows) { + if (error) { + return callback(error); + } else { + return callback(new Error("No rows returned by database.")); + } + } + + if (_this.countResults) { + callback(error, rows[0].rowCount); + } else { + (function () { + var models = new _collectionJs2["default"](_this.ModelConstructor); + + rows.forEach(function (row) { + models.push(new _this.ModelConstructor(row)); + }); + + callback(error, models); + })(); + } + }); + } + }]); + + return ModelQuery; +})(); + +exports.ModelQuery = ModelQuery; \ No newline at end of file diff --git a/es5/lib/validation/isPresent.js b/es5/lib/validation/isPresent.js new file mode 100644 index 0000000..bbed36a --- /dev/null +++ b/es5/lib/validation/isPresent.js @@ -0,0 +1,132 @@ +"use strict"; + +Object.defineProperty(exports, "__esModule", { + value: true +}); +exports["default"] = isPresent; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +var _modelFinderJs = require("../modelFinder.js"); + +var _modelFinderJs2 = _interopRequireDefault(_modelFinderJs); + +function isPresent(associationName, callback) { + var model = this; + var defaultErrorMessage = "must be present on " + model.constructor.name; + + if (!model.constructor.database) { + throw new Error("Cannot check isPresent without a database set."); + } + + //apiKey + var association = model.associations[associationName]; + var modelFinder = new _modelFinderJs2["default"](model.constructor.database); + + var associationModel = model[associationName]; //this["apiKey"], this["apiKeyId"] + + var resultDetails = { + result: undefined, + message: defaultErrorMessage + }; + + var foreignId = association.foreignId; + + if (associationModel) { + switch (association.type) { + case "hasOne": + case "belongsTo": + resultDetails.result = true; + callback(undefined, resultDetails); + break; + case "hasMany": + var collection = associationModel; + if (collection.length > 0) { + resultDetails.result = true; + callback(undefined, resultDetails); + } else { + if (model.isNew) { + //it does not have an id to look for + resultDetails.result = false; + callback(null, resultDetails); + } else { + modelFinder.count(association.constructor).where(association.foreignKey, "=", model.id).results(function (error, count) { + if (error) { + resultDetails.result = false; + callback(error, resultDetails); + } else { + var modelsFound = count > 0; + + if (modelsFound) { + resultDetails.result = true; + callback(null, resultDetails); + } else { + resultDetails.result = false; + callback(null, resultDetails); + } + } + }); + } + } + break; + default: + throw new Error("Unknown association type"); + } + } else { + if (model.isNew) { + resultDetails.result = false; + switch (association.type) { + case "hasOne": + case "hasMany": + break; + case "belongsTo": + //console.log("LOOK", {model: model, foreignId: foreignId}) + if (model[foreignId]) { + resultDetails.result = true; + } else { + resultDetails.result = false; + } + break; + default: + throw new Error("Unknown association type"); + } + callback(null, resultDetails); + } else { + switch (association.type) { + case "hasOne": + case "hasMany": + modelFinder.count(association.constructor).where(association.foreignKey, "=", model.id).results(function (error, count) { + if (error) { + resultDetails.result = false; + callback(error, resultDetails); + } else { + var modelsFound = count > 0; + + if (modelsFound) { + resultDetails.result = true; + callback(null, resultDetails); + } else { + resultDetails.result = false; + callback(null, resultDetails); + } + } + }); + break; + case "belongsTo": + + if (model[foreignId]) { + resultDetails.result = true; + callback(null, resultDetails); + } else { + resultDetails.result = false; + callback(null, resultDetails); + } + break; + default: + throw new Error("Unknown association type"); + } + } + } +} + +module.exports = exports["default"]; \ No newline at end of file diff --git a/es5/spec/dovima.spec.js b/es5/spec/dovima.spec.js deleted file mode 100644 index 7012061..0000000 --- a/es5/spec/dovima.spec.js +++ /dev/null @@ -1,19 +0,0 @@ -"use strict"; - -function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } - -var _libDovimaJs = require("../lib/dovima.js"); - -var _libDovimaJs2 = _interopRequireDefault(_libDovimaJs); - -describe("Dovima", function () { - var component = undefined; - - before(function () { - component = new _libDovimaJs2["default"](); - }); - - it("should say something", function () { - component.saySomething().should.equal("Something"); - }); -}); \ No newline at end of file diff --git a/es5/spec/fixtures/users.json b/es5/spec/fixtures/users.json new file mode 100644 index 0000000..ec08e0c --- /dev/null +++ b/es5/spec/fixtures/users.json @@ -0,0 +1,27 @@ +[ + { + "id": 5, + "name": "Bob Belcher", + "age": 46 + }, + { + "id": 1, + "name": "Linda Belcher", + "age": 42 + }, + { + "id": 2, + "name": "Gene Belcher", + "age": 11 + }, + { + "id": 3, + "name": "Tina Belcher", + "age": 13 + }, + { + "id": 4, + "name": "Louise Belcher", + "age": 9 + } +] diff --git a/es5/spec/model.spec.js b/es5/spec/model.spec.js new file mode 100644 index 0000000..8d27abc --- /dev/null +++ b/es5/spec/model.spec.js @@ -0,0 +1,2063 @@ +/* Testing Dependencies */ + +"use strict"; + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + +//import MultiError from "../../multiError/multiError.js"; + +//import MultiError from "../../multiError/multiError.js"; + +var _almaden = require("almaden"); + +var _almaden2 = _interopRequireDefault(_almaden); + +var _libValidationIsPresentJs = require("../lib/validation/isPresent.js"); + +var _libValidationIsPresentJs2 = _interopRequireDefault(_libValidationIsPresentJs); + +var _libCollectionJs = require("../lib/collection.js"); + +var _libCollectionJs2 = _interopRequireDefault(_libCollectionJs); + +var _libModelJs = require("../lib/model.js"); + +var _libModelJs2 = _interopRequireDefault(_libModelJs); + +var _libModelFinderJs = require("../lib/modelFinder.js"); + +var sinon = require("sinon"); + +/* Test Configuration */ +//nothing from a real connection needed since we are mocking here +var databaseConfig = { + "debug": true, + "client": "mysql", + "connection": {}, + "pool": { + "max": 2, + "min": 2 + } +}; + +var userFixtures = require("./fixtures/users.json"); + +describe("Model(attributes, options)", function () { + + /** + * Setup Model Examples + */ + + /* Simple Example */ + + var User = (function (_Model) { + function User() { + _classCallCheck(this, User); + + _get(Object.getPrototypeOf(User.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(User, _Model); + + _createClass(User, [{ + key: "associate", + value: function associate() { + this.hasOne("address", Address); + + this.hasOne("postalCode", PostalCode).through("address"); + + this.hasMany("photos", Photo); + + // this.hasMany("deletedPhotos", Photo) + // .where("deletedAt", "!=", null); + + this.hasOne("primaryPhoto", Photo).where("isPrimary", true); + + this.hasMany("photoLikes", PhotoLike); + this.hasMany("likedPhotos", Photo).through("photoLikes"); + + this.hasMany("comments", Comment).through("photos"); + + this.hasMany("deletedComments", Comment).through("photos").where("comments.deletedAt", "!=", null); + } + }, { + key: "validate", + value: function validate() { + this.ensure("photos", _libValidationIsPresentJs2["default"]); + } + }]); + + return User; + })(_libModelJs2["default"]); + + var Address = (function (_Model2) { + function Address() { + _classCallCheck(this, Address); + + _get(Object.getPrototypeOf(Address.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Address, _Model2); + + _createClass(Address, [{ + key: "associate", + value: function associate() { + this.belongsTo("user", User); + this.belongsTo("postalCode", PostalCode); + } + }, { + key: "validate", + value: function validate() { + this.ensure("photos", _libValidationIsPresentJs2["default"]); + } + }]); + + return Address; + })(_libModelJs2["default"]); + + var PostalCode = (function (_Model3) { + function PostalCode() { + _classCallCheck(this, PostalCode); + + _get(Object.getPrototypeOf(PostalCode.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(PostalCode, _Model3); + + _createClass(PostalCode, [{ + key: "associate", + value: function associate() { + this.hasMany("addresses"); + } + }]); + + return PostalCode; + })(_libModelJs2["default"]); + + var PhotoLike = (function () { + function PhotoLike() { + _classCallCheck(this, PhotoLike); + } + + _createClass(PhotoLike, [{ + key: "associate", + value: function associate() { + this.belongsTo("user", User); + this.belongsTo("photo", User); + } + }, { + key: "validate", + value: function validate() { + this.ensure("user", _libValidationIsPresentJs2["default"]); + this.ensure("photo", _libValidationIsPresentJs2["default"]); + } + }]); + + return PhotoLike; + })(); + + var Photo = (function (_Model4) { + function Photo() { + _classCallCheck(this, Photo); + + _get(Object.getPrototypeOf(Photo.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Photo, _Model4); + + _createClass(Photo, [{ + key: "associate", + value: function associate() { + this.belongsTo("user", User).ambiguous; + + this.hasMany("comments", Comment); + + this.hasMany("commentAuthors", User).through("comments").as("author"); + + this.hasMany("photoLikes", PhotoLike); + + this.hasMany("likedByUsers", User).through("photoLikes"); + } + }, { + key: "validate", + value: function validate() { + this.ensure("user", _libValidationIsPresentJs2["default"]); + } + }]); + + return Photo; + })(_libModelJs2["default"]); + + var Comment = (function (_Model5) { + function Comment() { + _classCallCheck(this, Comment); + + _get(Object.getPrototypeOf(Comment.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Comment, _Model5); + + _createClass(Comment, [{ + key: "associate", + value: function associate() { + this.belongsTo("photo", Photo); + this.belongsTo("author", User); + } + }]); + + return Comment; + })(_libModelJs2["default"]); + + var Rating = (function (_Model6) { + function Rating() { + _classCallCheck(this, Rating); + + _get(Object.getPrototypeOf(Rating.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Rating, _Model6); + + _createClass(Rating, [{ + key: "associate", + value: function associate() { + this.belongsTo("owner", Comment).isPolymorphic; + } + }]); + + return Rating; + })(_libModelJs2["default"]); + + /** + * Instantiate Model Examples + */ + + var model = undefined, + user = undefined, + userAttributes = undefined, + photo = undefined, + primaryPhoto = undefined, + postalCode = undefined, + address = undefined, + comment = undefined, + clock = undefined; + + beforeEach(function () { + clock = sinon.useFakeTimers(); + + _libModelJs2["default"].database = new _almaden2["default"](databaseConfig); + _libModelJs2["default"].database.mock({}); // Catch-all for database + + model = new _libModelJs2["default"](); + + userAttributes = { + id: 1, + name: "Bob Builder", + age: 35, + hasChildren: false + }; + + user = new User(userAttributes); + photo = new Photo(); + primaryPhoto = new Photo(); + comment = new Comment(); + postalCode = new PostalCode(); + address = new Address(); + }); + + afterEach(function () { + return clock.restore(); + }); + + /** + * Begin Testing + */ + + describe("(Properties)", function () { + describe(".attributes", function () { + it("should return all attributes and their values minus associations", function () { + user.attributes.should.eql(userAttributes); + }); + it("should assign the properties for the model", function () { + user = new User(); + user.attributes = userAttributes; + user.attributes.should.eql(userAttributes); + }); + }); + + describe(".properties", function () { + it("should return the name of all attributes plus associations on the model", function () { + user.properties.should.eql(["address", "postalCode", "photos", "primaryPhoto", "photoLikes", "likedPhotos", "comments", "deletedComments", "id", "name", "age", "hasChildren"]); + }); + }); + + describe(".tableName", function () { + it("should return the model's table name", function () { + user.tableName.should.eql("users"); + }); + it("should allow overriding of the model's table name", function () { + var newTableName = "somethingElse"; + user.tableName = newTableName; + user.tableName.should.eql(newTableName); + }); + }); + + describe(".primaryKey", function () { + it("should return the model's primary key", function () { + user.primaryKey.should.eql("id"); + }); + it("should allow overriding of the model's primaryKey", function () { + var newPrimaryKey = "different_id"; + user.primaryKey = newPrimaryKey; + user.primaryKey.should.eql(newPrimaryKey); + }); + }); + + describe(".isNew", function () { + describe("(when model has the primary key set)", function () { + it("should be false", function () { + user.isNew.should.be["false"]; + }); + }); + describe("(when model does not have the primary key set)", function () { + beforeEach(function () { + user.id = undefined; + }); + it("should be true", function () { + user.isNew.should.be["true"]; + }); + }); + }); + }); + + describe("(static properties)", function () { + describe(".find", function () { + var users = undefined, + userCollection = undefined; + + before(function () { + userCollection = new _libCollectionJs2["default"](User); + userFixtures.forEach(function (userFiture) { + userCollection.push(new User(userFiture)); + }); + }); + + beforeEach(function (done) { + _libModelJs2["default"].database.mock({ + "select * from `users` where `mom_id` = 1": userFixtures + }); + + User.find.where("momId", "=", 1).results(function (error, fetchedUsers) { + users = fetchedUsers; + done(); + }); + }); + + it("should return a ModelQuery instance", function () { + User.find.should.be.instanceOf(_libModelFinderJs.ModelQuery); + }); + + it("should return a collection", function () { + users.should.be.instanceOf(_libCollectionJs2["default"]); + }); + + it("should return the right collection", function () { + users.should.eql(userCollection); + }); + + it("should allow to search all models that matchs a certain condition", function () { + users.length.should.equal(5); + }); + + describe(".one", function () { + beforeEach(function (done) { + _libModelJs2["default"].database.mock({ + "select * from `users` where `mom_id` = 1 limit 1": [userFixtures[0]] + }); + + User.find.one.where("momId", 1).results(function (error, fetchedUsers) { + users = fetchedUsers; + done(); + }); + }); + + it("should return just one user", function () { + users.length.should.equal(1); + }); + }); + + describe(".all", function () { + beforeEach(function (done) { + User.find.all.where("momId", 1).results(function (error, fetchedUsers) { + users = fetchedUsers; + done(); + }); + }); + + it("should return just all users matching the condition", function () { + users.length.should.equal(5); + }); + }); + + describe(".deleted", function () { + var SoftUser = (function (_Model7) { + function SoftUser() { + _classCallCheck(this, SoftUser); + + _get(Object.getPrototypeOf(SoftUser.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(SoftUser, _Model7); + + _createClass(SoftUser, [{ + key: "initialize", + value: function initialize() { + this.softDelete; + } + }]); + + return SoftUser; + })(_libModelJs2["default"]); + + beforeEach(function (done) { + _libModelJs2["default"].database.mock({ + "select * from `soft_users` where `mom_id` = 1 and `deleted_at` is not null": userFixtures + }); + + SoftUser.find.all.where("momId", 1).deleted.results(function (error, fetchedUsers) { + users = fetchedUsers; + done(); + }); + }); + + it("should return just all users matching the condition", function () { + users.length.should.equal(5); + }); + }); + }); + }); + + describe("(Initialization)", function () { + /* eslint-disable no-unused-vars */ + // This is because we instantiate Post, but we don"t do anything with it. + + var post = undefined, + initializeSpy = undefined, + validateSpy = undefined, + associateSpy = undefined; + + var Post = (function (_Model8) { + function Post() { + _classCallCheck(this, Post); + + _get(Object.getPrototypeOf(Post.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Post, _Model8); + + _createClass(Post, [{ + key: "initialize", + value: function initialize() { + initializeSpy(); + } + }, { + key: "validate", + value: function validate() { + validateSpy(); + } + }, { + key: "associate", + value: function associate() { + associateSpy(); + } + }]); + + return Post; + })(_libModelJs2["default"]); + + beforeEach(function () { + initializeSpy = sinon.spy(); + validateSpy = sinon.spy(); + associateSpy = sinon.spy(); + post = new Post(); + }); + + describe(".initialize()", function () { + it("should be called during instantiation", function () { + initializeSpy.called.should.be["true"]; + }); + it("should be called after .associate", function () { + sinon.assert.callOrder(associateSpy, initializeSpy); + }); + it("should be called after .validate", function () { + sinon.assert.callOrder(validateSpy, initializeSpy); + }); + }); + + describe(".validate()", function () { + it("should be called during instantiation", function () { + validateSpy.called.should.be["true"]; + }); + it("should be called after .associate", function () { + sinon.assert.callOrder(associateSpy, validateSpy); + }); + }); + + describe(".associate()", function () { + it("should be called during instantiation", function () { + associateSpy.called.should.be["true"]; + }); + }); + }); + + describe("(Associations)", function () { + var Street = (function (_Model9) { + function Street() { + _classCallCheck(this, Street); + + _get(Object.getPrototypeOf(Street.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Street, _Model9); + + return Street; + })(_libModelJs2["default"]); + + var Driver = (function (_Model10) { + function Driver() { + _classCallCheck(this, Driver); + + _get(Object.getPrototypeOf(Driver.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Driver, _Model10); + + return Driver; + })(_libModelJs2["default"]); + + var Truck = (function (_Model11) { + function Truck() { + _classCallCheck(this, Truck); + + _get(Object.getPrototypeOf(Truck.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Truck, _Model11); + + return Truck; + })(_libModelJs2["default"]); + + var Wheel = (function (_Model12) { + function Wheel() { + _classCallCheck(this, Wheel); + + _get(Object.getPrototypeOf(Wheel.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Wheel, _Model12); + + return Wheel; + })(_libModelJs2["default"]); + + var SteeringWheel = (function (_Model13) { + function SteeringWheel() { + _classCallCheck(this, SteeringWheel); + + _get(Object.getPrototypeOf(SteeringWheel.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(SteeringWheel, _Model13); + + return SteeringWheel; + })(_libModelJs2["default"]); + + var street = undefined, + driver = undefined, + truck = undefined, + wheel = undefined, + steeringWheel = undefined; + + beforeEach(function () { + street = new Street(); + driver = new Driver(); + truck = new Truck(); + wheel = new Wheel(); + steeringWheel = new SteeringWheel(); + }); + + describe(".belongsTo(associationName, associationConstructor)", function () { + describe("(when inverse association is hasOne)", function () { + beforeEach(function () { + truck.hasOne("steeringWheel", SteeringWheel); + steeringWheel.belongsTo("truck", Truck); + }); + + it("should add the association to .associations", function () { + steeringWheel.associations.truck.should.eql({ + parent: steeringWheel, + type: "belongsTo", + constructor: Truck, + foreignName: "steeringWheel", + foreignId: "truckId", + foreignKey: "truck_id" + }); + }); + + it("should set the accessor property to null", function () { + (steeringWheel.truck == null).should.be["true"]; + }); + + it("should add the parent model onto the child model", function () { + steeringWheel.truck = truck; + truck.steeringWheel.should.eql(steeringWheel); + }); + + it("should add the association id onto the model", function () { + truck.id = 1; + steeringWheel.truck = truck; + steeringWheel.should.have.property("truckId"); + }); + }); + + describe("(when inverse association is hasMany)", function () { + beforeEach(function () { + truck.hasMany("wheels", Wheel); + wheel.belongsTo("truck", Truck); + }); + + it("should add the association to .associations", function () { + wheel.associations.truck.should.eql({ + parent: wheel, + type: "belongsTo", + constructor: Truck, + foreignName: "wheel", + foreignId: "truckId", + foreignKey: "truck_id" + }); + }); + + it("should set the accessor property to null", function () { + (wheel.truck == null).should.be["true"]; + }); + + it("should add the parent model onto the child model", function () { + wheel.truck = truck; + truck.wheels[0].should.eql(wheel); + }); + + it("should add the association id onto the model", function () { + truck.id = 1; + wheel.truck = truck; + wheel.truckId.should.eql(truck.id); + }); + + it("should add a model just once on the parent", function () { + truck.id = 1; + wheel.truck = truck; + wheel.truck = truck; + + truck.wheels.length.should.equal(1); + }); + }); + + describe("(when associationName is different from the associationConstructor)", function () { + beforeEach(function () { + truck.hasOne("steeringWheel", SteeringWheel).as("superTruck"); + steeringWheel.belongsTo("superTruck", Truck); + + // steeringWheel.id = 1; + + // Model.database.mock({ + // "select * from `steering_wheels` where `id` = 1 limit 1": [ + // {"truck_id": 1} + // ], + // "select * from `trucks` where `id` = 1 limit 1": [ + // {} + // ] + // }); + }); + + it("should find the renamed association", function () { + steeringWheel.associations.superTruck.should.eql({ + parent: steeringWheel, + type: "belongsTo", + constructor: Truck, + foreignName: "steeringWheel", + foreignId: "superTruckId", + foreignKey: "super_truck_id" + }); + }); + }); + + describe("(when no inverse association is found)", function () { + beforeEach(function () { + wheel.belongsTo("truck", Truck); + }); + + it("should throw an error", function () { + (function () { + wheel.truck = truck; + }).should["throw"]("Neither \"wheel\" or \"wheels\" are valid associations on \"Truck\""); + }); + }); + + describe("(with options)", function () { + describe(".ambiguous", function () { + beforeEach(function () { + wheel.belongsTo("truck", Truck).ambiguous; + }); + + it("should add the association to .associations", function () { + wheel.associations.truck.should.eql({ + parent: wheel, + type: "belongsTo", + constructor: Truck, + foreignName: "wheel", + foreignId: "truckId", + foreignKey: "truck_id", + ambiguous: true + }); + }); + + it("should not add the parent model to the child associatio", function () { + wheel.truck = truck; + (truck.wheel === undefined).should.be["true"]; + }); + }); + }); + }); + + describe(".hasOne(associationName, associationConstructor)", function () { + var associationName = undefined, + associationConstructor = undefined; + + beforeEach(function () { + associationName = "user"; + associationConstructor = User; + model.hasOne(associationName, associationConstructor); + }); + + it("should set the accessor function to null", function () { + (model[associationName] == null).should.be["true"]; + }); + + it("should add the association to .associations", function () { + model.associations[associationName].should.eql({ + parent: model, + type: "hasOne", + constructor: associationConstructor, + foreignId: "modelId", + foreignKey: "model_id", + foreignName: "model" + }); + }); + + it("should return an association setter", function () { + truck.hasOne("steeringWheel", SteeringWheel).should.be.instanceOf(_libModelJs.AssociationSetter); + }); + + it("should accept a custom error message", function () { + truck.hasOne("steeringWheel", SteeringWheel); + truck.ensure("steeringWheel", _libValidationIsPresentJs2["default"], "must be there."); + truck.invalidAttributes(function (invalidAttributeList) { + invalidAttributeList.should.eql({ + "steeringWheel": ["must be there."] + }); + }); + }); + + describe("(with options)", function () { + describe(".ambiguous", function () { + it("should throw an error", function () { + var query = truck.hasOne("steeringWheel", SteeringWheel); + query.should.not.have.property("ambiguous"); + }); + }); + + describe(".where(...conditions)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [{ id: 1 }], + "select * from `photos` where `user_id` = 1 and (`is_favorite` = true) limit 1": [{ + id: 1, + name: "Favorite Photo" + }] + }); + user.hasOne("favoritePhoto", Photo).where("isFavorite", true); + }); + + it("should set more than one condition joined by AND", function (done) { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [{ id: 1 }], + "select * from `photos` where `user_id` = 1 and (`is_favorite` = true and `is_face_photo` = true) limit 1": [{ name: "Favorite Face Photo" }] + }); + + user.hasOne("favoriteFacePhoto", Photo).where("isFavorite", true).andWhere("isFacePhoto", true); + + user.include("favoriteFacePhoto").fetch(function () { + user.favoriteFacePhoto.name.should.eql("Favorite Face Photo"); + done(); + }); + }); + + it("should set the where conditions", function () { + user.associations.favoritePhoto.where.should.eql(["isFavorite", true]); + }); + + it("should set conditions for the association", function (done) { + user.include("favoritePhoto").fetch(function () { + user.favoritePhoto.name.should.eql("Favorite Photo"); + done(); + }); + }); + }); + }); + }); + + describe(".hasMany(associationName, associationConstructor)", function () { + var associationName = undefined, + associationConstructor = undefined; + + beforeEach(function () { + associationName = "users"; + associationConstructor = User; + model.hasMany(associationName, associationConstructor); + }); + + it("should set the accessor function to a Collection", function () { + model[associationName].should.be.instanceOf(_libCollectionJs2["default"]); + }); + + it("should initially set the accessor function Collection to be empty", function () { + model[associationName].length.should.equal(0); + }); + + it("should add the association to .associate()", function () { + model.associations[associationName].should.eql({ + parent: model, + type: "hasMany", + constructor: associationConstructor, + foreignId: "modelId", + foreignKey: "model_id", + foreignName: "model" + }); + }); + + it("should accept a custom error message", function () { + truck.hasMany("wheels", Wheel); + truck.ensure("wheels", _libValidationIsPresentJs2["default"], "must be there."); + truck.invalidAttributes(function (invalidAttributeList) { + invalidAttributeList.should.eql({ + "wheels": ["must be there."] + }); + }); + }); + + describe("(with options)", function () { + describe(".ambiguous", function () { + it("should throw an error", function () { + var query = truck.hasOne("steeringWheel", SteeringWheel); + query.should.not.have.property("ambiguous"); + }); + }); + + describe(".where(...conditions)", function () { + describe("(with one where condition)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [{ id: 1 }], + "select * from `photos` where `user_id` = 1 and (`is_favorite` = true)": [{ id: 1, name: "Favorite Photo" }, { id: 2, name: "Another Favorite Photo" }, { id: 3, name: "Mostest Favoritest Photo" }] + }); + + user.hasMany("favoritePhotos", Photo).where("isFavorite", true); + }); + + it("should set the where conditions", function () { + user.associations.favoritePhotos.where.should.eql(["isFavorite", true]); + }); + + it("should set conditions for the association", function (done) { + user.include("favoritePhotos").fetch(function () { + user.favoritePhotos.length.should.eql(3); + done(); + }); + }); + }); + + describe("(with multiple where conditions)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [{ id: 1 }], + "select * from `photos` where `user_id` = 1 and (`is_favorite` = true and `is_face_photo` = true)": [{ id: 1, name: "Favorite Face Photo" }, { id: 2, name: "Another Favorite Face Photo" }, { id: 3, name: "Mostest Favoritest Face Photo" }] + }); + + user.hasMany("favoriteFacePhotos", Photo).where("isFavorite", true).andWhere("isFacePhoto", true); + }); + + it("should set the where conditions", function () { + user.associations.favoriteFacePhotos.andWhere.should.eql([["isFacePhoto", true]]); + }); + + it("should set conditions for the association", function (done) { + user.include("favoriteFacePhotos").fetch(function () { + user.favoriteFacePhotos.length.should.eql(3); + done(); + }); + }); + }); + }); + + xdescribe(".through(associationName)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({}); + }); + + it("should return the association set to allow further chaining", function () { + user.hasMany("comments", Comment).through("apiKey").should.be.instanceOf(_libModelJs.AssociationSetter); + }); + + it("should fetch hasMany through associations", function (done) { + user.include("comments").fetch(function (error) { + user.comments.user.comments[0].instanceOf(Comment); + done(); + }); + }); + }); + }); + }); + }); + + describe("(Validations)", function () { + describe(".invalidAttributes(callback)", function () { + describe("(when all validations pass)", function () { + beforeEach(function () { + user.photos.push(photo); + }); + + it("should return an empty object", function () { + user.invalidAttributes(function (attributes) { + attributes.should.eql({}); + }); + }); + }); + + describe("(when any validations fail)", function () { + beforeEach(function () { + // Force database to fail isPresent on user.photos + _libModelJs2["default"].database.mock({ + "select count(*) as `rowCount` from `photos` where `user_id` = 1": [{ rowCount: 0 }] + }); + }); + + it("should return an object containing all invalid attributes", function (done) { + user.invalidAttributes(function (attributes) { + attributes.should.eql({ + "photos": ["must be present on User"] + }); + done(); + }); + }); + }); + }); + + describe(".isValid(callback)", function () { + describe("(when all validations pass)", function () { + beforeEach(function () { + user.photos.push(photo); + }); + + it("should return true", function (done) { + user.isValid(function (isValid) { + isValid.should.be["true"]; + done(); + }); + }); + }); + + describe("(when any validations fail)", function () { + beforeEach(function () { + // Force database to fail isPresent on user.photos + _libModelJs2["default"].database.mock({ + "select count(*) as `rowCount` from `photos` where `user_id` = 1": [{ rowCount: 0 }] + }); + }); + + it("should return false", function (done) { + user.isValid(function (isValid) { + isValid.should.be["false"]; + done(); + }); + }); + }); + }); + + describe(".validations", function () { + it("should return an object representing all validations on the model", function () { + user.validations.should.eql({ + "photos": [{ + validator: _libValidationIsPresentJs2["default"] + }] + }); + }); + }); + + describe(".ensure(attributeName, validatorFunction, validatorMessage)", function () { + var validatorFunction = undefined, + validatorMessage = undefined; + beforeEach(function () { + validatorFunction = function (value, callback) { + callback(null, true); + }; + validatorMessage = "must be a number."; + }); + + it("should add the validator to .validations", function () { + user.ensure("photos", validatorFunction, validatorMessage); + user.validations.should.eql({ + "photos": [{ + validator: _libValidationIsPresentJs2["default"] + }, { + validator: validatorFunction, + message: validatorMessage + }] + }); + }); + }); + }); + + describe("(Persistence)", function () { + + describe(".as(associationName)", function () { + it("should set the referencing association name in a hasMany through belongsTo", function (done) { + _libModelJs2["default"].database.mock({ + "select * from `photos` where `id` = 1 limit 1": [{}], + "select * from `comments` where `photo_id` = 1": [{ id: 1, "author_id": 5 }, { id: 2, "author_id": 6 }, { id: 3, "author_id": 7 }], + "select * from `users` where `id` in (5, 6, 7)": [{}, {}, {}] + }); + + photo.id = 1; + + photo.include("commentAuthors").fetch(function (errors) { + photo.commentAuthors.length.should.equal(3); + done(); + }); + }); + }); + + describe(".include(associationNames)", function () { + var associationNames = undefined; + + beforeEach(function () { + associationNames = ["primaryPhoto", "photos"]; + }); + + it("should throw an error if an association name is not found", function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [userAttributes] + }); + (function () { + user.include("bogusAssociation").fetch(); + }).should["throw"]("Cannot fetch 'bogusAssociation' because it is not a valid association on User"); + }); + + it("should throw an error if a belongs to association id is not set", function () { + _libModelJs2["default"].database.mock({ + "select * from `comments` where `id` = 1 limit 1": [{ id: 1 }] + }); + + (function () { + comment.id = 1; + comment.include("photo").fetch(); + }).should["throw"]("Cannot fetch 'photo' because 'photoId' is not set on Comment"); + }); + + it("should return the model to support chaining", function () { + user.include.apply(user, _toConsumableArray(associationNames)).should.equal(user); + }); + + it("should fetch belongsTo associations", function (done) { + _libModelJs2["default"].database.mock({ + "select * from `photos` where `id` = 1 limit 1": [{ id: 1 }], + "select * from `users` where `id` = 1 limit 1": [{ id: 1, name: "Bob Barker" }] + }); + + photo.id = 1; + photo.userId = 1; + photo.include("user").fetch(function (errors) { + photo.user.name.should.eql("Bob Barker"); + done(); + }); + }); + + it("should fetch hasOne associations", function (done) { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [userAttributes], + "select * from `photos` where `user_id` = 1 and (`is_primary` = true) limit 1": [{ + name: "Primary Photo" + }] + }); + + user.include("primaryPhoto").fetch(function (errors) { + user.primaryPhoto.name.should.eql("Primary Photo"); + done(); + }); + }); + + it("should fetch hasMany associations", function (done) { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [userAttributes], + "select * from `photos` where `user_id` = 1": [{ name: "Some Photo" }, { name: "Some Other Photo" }, { name: "Still Some Photo" }] + }); + + user.include("photos").fetch(function (errors) { + user.photos.length.should.eql(3); + done(); + }); + }); + + it("should fetch hasOne through associations", function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [{}], + "select * from `addresses` where `user_id` = 1 limit 1": [{ + "user_id": 1, + "postal_code_id": 2 + }], + "select * from `postal_codes` where `id` = 2 limit 1": [{ number: 90210 }] + }); + + user.include("postalCode").fetch(function (errors) { + user.postalCode.number.should.eql(90210); + }); + }); + + it("should fetch hasMany through hasMany associations", function (done) { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [{ id: 1 }], + "select * from `photos` where `user_id` = 1": [{ id: 3 }, { id: 4 }, { id: 5 }], + "select * from `comments` where `photo_id` in (3, 4, 5)": [{}, {}, {}] + }); + + user.include("comments").fetch(function (errors) { + user.comments.length.should.equal(3); + done(); + }); + }); + + it("should throw an error when the destination association is not found on the through model", function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [{}], + "select * from `photos` where `user_id` = 1": [{ id: 3 }] + }); + + var Post = function Post() { + _classCallCheck(this, Post); + }; + + // Needed to mock a through association that is incomplete + + (function () { + user.hasMany("posts", Post).through("photos"); + + user.include("posts").fetch(); + }).should["throw"]("'posts' is not a valid association on through model 'Photo'"); + }); + + it("should fetch hasMany through hasOne associations", function (done) { + _libModelJs2["default"].database.mock({ + "select * from `photos` where `id` = 1 limit 1": [{}], + "select * from `comments` where `photo_id` = 1": [{ "author_id": 4 }, { "author_id": 5 }, { "author_id": 6 }], + "select * from `users` where `id` in (4, 5, 6)": [{}, {}, {}] + }); + + photo.id = 1; // Need primary key to fetch + + photo.include("commentAuthors").fetch(function (errors) { + photo.commentAuthors.length.should.equal(3); + done(); + }); + }); + + xit("should fetch hasMany through belongsTo associations", function (done) { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [{}] + }); + + user.include("comments").fetch(function (errors) { + user.comments.length.should.equal(3); + done(); + }); + }); + + it("should fetch more than one association at once", function (done) { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [userAttributes], + "select * from `photos` where `user_id` = 1 and (`is_primary` = true) limit 1": [{ name: "Primary Photo" }], + "select * from `photos` where `user_id` = 1": [{ name: "Some Photo" }, { name: "Some Other Photo" }, { name: "Still Some Photo" }, { name: "Primary Photo" }] + }); + + user.include("photos", "primaryPhoto").fetch(function (errors) { + [user.photos.length, user.primaryPhoto.name].should.eql([4, "Primary Photo"]); + done(); + }); + }); + }); + + describe(".delete(callback)", function () { + describe("(when dependent is declared on the association)", function () { + var Account = (function (_Model14) { + function Account() { + _classCallCheck(this, Account); + + _get(Object.getPrototypeOf(Account.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Account, _Model14); + + _createClass(Account, [{ + key: "initialize", + value: function initialize() { + this.softDelete; + } + }, { + key: "associate", + value: function associate() { + this.hasOne("forumUser", ForumUser).dependent; + } + }]); + + return Account; + })(_libModelJs2["default"]); + + var ForumUser = (function (_Model15) { + function ForumUser() { + _classCallCheck(this, ForumUser); + + _get(Object.getPrototypeOf(ForumUser.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(ForumUser, _Model15); + + _createClass(ForumUser, [{ + key: "initialize", + value: function initialize() { + this.softDelete; + } + }, { + key: "associate", + value: function associate() { + this.hasMany("posts", Post).dependent; + this.belongsTo("account", Account).dependent; + } + }]); + + return ForumUser; + })(_libModelJs2["default"]); + + var Post = (function (_Model16) { + function Post() { + _classCallCheck(this, Post); + + _get(Object.getPrototypeOf(Post.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Post, _Model16); + + _createClass(Post, [{ + key: "initialize", + value: function initialize() { + this.softDelete; + } + }, { + key: "associate", + value: function associate() { + this.belongsTo("forumUser", ForumUser); + } + }]); + + return Post; + })(_libModelJs2["default"]); + + var forumUser = undefined, + account = undefined, + post = undefined; + + beforeEach(function () { + account = new Account({ id: 1 }); + forumUser = new ForumUser({ id: 2 }); + post = new Post({ id: 3 }); + }); + + it("should add the association to .associations", function () { + account.associations.forumUser.should.eql({ + parent: account, + type: "hasOne", + constructor: ForumUser, + foreignName: "account", + foreignId: "accountId", + foreignKey: "account_id", + dependent: true + }); + }); + + describe("(on a hasOne)", function () { + var userDeleteQuerySpy = undefined; + + beforeEach(function () { + _libModelJs2["default"].database.mock(_defineProperty({}, /update `accounts` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, 1)); + + userDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `forum_users` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, 1); + + account.forumUser = forumUser; + }); + + it("should propagate delete on those models", function (done) { + account["delete"](function () { + userDeleteQuerySpy.callCount.should.equal(1); + done(); + }); + }); + }); + + describe("(on a hasMany)", function () { + var postDeleteQuerySpy = undefined; + + beforeEach(function () { + _libModelJs2["default"].database.mock(_defineProperty({}, /update `forum_users` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, 1)); + + postDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `posts` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 3/, 1); + + forumUser.posts.push(post); + }); + + it("should propagate delete on those models", function (done) { + forumUser["delete"](function () { + postDeleteQuerySpy.callCount.should.equal(1); + done(); + }); + }); + }); + }); + + describe("(when .softDelete is not called)", function () { + var Post = (function (_Model17) { + function Post() { + _classCallCheck(this, Post); + + _get(Object.getPrototypeOf(Post.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Post, _Model17); + + return Post; + })(_libModelJs2["default"]); + + var post = undefined; + + beforeEach(function () { + post = new Post(); + }); + + it("should throw when calling delete", function () { + (function () { + post["delete"](); + }).should["throw"]("Not implemented."); + }); + }); + + describe("when softDelete called)", function () { + var post = undefined; + + var Post = (function (_Model18) { + function Post() { + _classCallCheck(this, Post); + + _get(Object.getPrototypeOf(Post.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Post, _Model18); + + _createClass(Post, [{ + key: "initialize", + value: function initialize() { + this.softDelete; + } + }]); + + return Post; + })(_libModelJs2["default"]); + + beforeEach(function () { + post = new Post(); + }); + + describe("(Model.database is set)", function () { + describe("(when primaryKey is set)", function () { + beforeEach(function () { + post.id = 1; + _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'1969-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/, 1)); + }); + + it("should not throw when calling delete", function () { + (function () { + post["delete"](function () {}); + }).should.not["throw"](); + }); + + it("should return no error", function () { + post["delete"](function (error) { + (error == null).should.be["true"]; + }); + }); + + describe("(when primary key is set but not exists)", function () { + beforeEach(function () { + post.id = 1; + _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'1969-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/, 0)); + }); + + it("should return an error", function () { + post["delete"](function (error) { + error.should.eql(new Error("Post with id 1 cannot be soft deleted because it doesn't exists.")); + }); + }); + }); + }); + + describe("(when primaryKey is not set)", function () { + it("should throw an error", function () { + (function () { + post["delete"](function () {}); + }).should["throw"]("Cannot delete the Post because the primary key is not set."); + }); + }); + }); + + describe("(Model.database not set)", function () { + beforeEach(function () { + delete _libModelJs2["default"].database; + }); + + it("should throw an error", function () { + (function () { + post["delete"](); + }).should["throw"]("Cannot delete without Model.database set."); + }); + }); + }); + }); + + describe(".fetch(callback)", function () { + describe("(Model.database is set)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [userAttributes] + }); + }); + + describe("(when model has a primary key set)", function () { + beforeEach(function () { + user = new User({ + id: 1 + }); + }); + + it("should fetch a record from the correct table", function (done) { + user.fetch(function (error) { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + + it("should fetch a record from the correct table", function (done) { + user.fetch(function (error) { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + + describe("(when soft delete is enabled)", function () { + var post = undefined, + deleteQuerySpy = undefined; + + var Post = (function (_Model19) { + function Post() { + _classCallCheck(this, Post); + + _get(Object.getPrototypeOf(Post.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Post, _Model19); + + _createClass(Post, [{ + key: "initialize", + value: function initialize() { + this.softDelete; + } + }]); + + return Post; + })(_libModelJs2["default"]); + + beforeEach(function () { + post = new Post({ id: 1 }); + //querySpy + deleteQuerySpy = _libModelJs2["default"].database.spy("select * from `posts` where `id` = 1 and `deleted_at` is null limit 1", [{}]); + }); + + it("should add a where deleted is not null condition", function (done) { + post.fetch(function () { + deleteQuerySpy.callCount.should.equal(1); + done(); + }); + }); + }); + }); + + describe("(when model does not have a primary key set)", function () { + beforeEach(function () { + delete user.id; + }); + + it("should throw an error", function () { + (function () { + user.fetch(); + }).should["throw"]("Cannot fetch this model by the 'id' field because it is not set."); + }); + }); + + describe("(when there is no model with that id)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `id` = 1 limit 1": [] + }); + user = new User({ + id: 1 + }); + }); + + it("should throw an error on the callback", function (done) { + user.fetch(function (error) { + error.should.be.instanceOf(Error); + done(); + }); + }); + }); + }); + + describe("(Model.database not set)", function () { + beforeEach(function () { + delete _libModelJs2["default"].database; + }); + + it("should throw an error", function () { + (function () { + user.fetch(); + }).should["throw"]("Cannot fetch without Model.database set."); + }); + }); + }); + + describe(".fetch(strategy, callback)", function () { + describe("(when the type of the strategy is a string)", function () { + describe("(Model.database is set)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `name` = 'someuser' limit 1": [userAttributes] + }); + }); + + describe("(when model has the specified attribute set)", function () { + beforeEach(function () { + user = new User({ + name: "someuser" + }); + }); + + it("should fetch a record from the correct table", function (done) { + user.fetch("name", function (error) { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + + it("should fetch a record from the correct table", function (done) { + user.fetch("name", function (error) { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + }); + + describe("(when model does not have the specified attribute set)", function () { + beforeEach(function () { + delete user.name; + }); + + it("should throw an error", function () { + (function () { + user.fetch("name"); + }).should["throw"]("Cannot fetch this model by the 'name' field because it is not set."); + }); + }); + + describe("(when there is no model with the specified attribute)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `name` = 'someuser' limit 1": [] + }); + user = new User({ + name: "someuser" + }); + }); + + it("should throw an error on the callback", function (done) { + user.fetch("name", function (error) { + error.should.be.instanceOf(Error); + done(); + }); + }); + }); + }); + + describe("(Model.database not set)", function () { + beforeEach(function () { + delete _libModelJs2["default"].database; + }); + + it("should throw an error", function () { + (function () { + user.fetch("name"); + }).should["throw"]("Cannot fetch without Model.database set."); + }); + }); + }); + + describe("(when the type of the strategy is an array)", function () { + describe("(Model.database is set)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `name` = 'someuser' and `lastName` = 'someuserLastName' limit 1": [userAttributes] + }); + }); + + describe("(when model has the specified attribute set)", function () { + beforeEach(function () { + user = new User({ + name: "someuser", + lastName: "someuserLastName" + }); + userAttributes.lastName = "someuserLastName"; + }); + + it("should fetch a record from the correct table", function (done) { + user.fetch(["name", "lastName"], function (error) { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + + it("should fetch a record from the correct table", function (done) { + user.fetch(["name", "lastName"], function (error) { + user.attributes.should.eql(userAttributes); + done(); + }); + }); + }); + + describe("(when model does not have one of the specified attributes set)", function () { + beforeEach(function () { + delete user.lastName; + }); + + it("should throw an error", function () { + (function () { + user.fetch(["name", "lastName"]); + }).should["throw"]("Cannot fetch this model by the 'lastName' field because it is not set."); + }); + }); + + describe("(when there is no model with the specified attribute)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select * from `users` where `name` = 'someuser' and `lastName` = 'someuserLastName' limit 1": [] + }); + user = new User({ + name: "someuser", + lastName: "someuserLastName" + }); + }); + + it("should throw an error on the callback", function (done) { + user.fetch(["name", "lastName"], function (error) { + error.should.be.instanceOf(Error); + done(); + }); + }); + }); + }); + + describe("(Model.database not set)", function () { + beforeEach(function () { + delete _libModelJs2["default"].database; + }); + + it("should throw an error", function () { + (function () { + user.fetch("name"); + }).should["throw"]("Cannot fetch without Model.database set."); + }); + }); + }); + }); + + describe(".save(callback)", function () { + describe("(Model.database is set)", function () { + describe("(when the model has associations)", function () { + + beforeEach(function () { + var _Model$database$mock5; + + user.primaryPhoto = primaryPhoto; + user.photos.push(photo); + + var regularExpressions = { + insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('1969-12-31 [0-9][0-9]:00:00.000', 1\)/, + updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, + insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('1969-12-31 [0-9][0-9]:00:00\.000', 1\)/, + insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('1969-12-31 [0-9][0-9]:00:00.000', 1\)/, + insertTruck: /insert into `trucks` (`created_at`) values ('1969-12-31 [0-24]:00:00.000')/, + updateTruck: /update `trucks` set `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, + updateWheels: /update `wheels` set `created_at` = '1969-12-31 [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + }; + + _libModelJs2["default"].database.mock((_Model$database$mock5 = {}, _defineProperty(_Model$database$mock5, regularExpressions.insertPhotos, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateUser, []), _defineProperty(_Model$database$mock5, regularExpressions.insertWheels, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertSteeringWheel, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateWheels, [1]), _Model$database$mock5)); + }); + + describe("(association operations)", function () { + var wheelSaveSpy = undefined, + steeringWheelSaveSpy = undefined, + truck = undefined, + owner = undefined, + wheel = undefined, + steeringWheel = undefined, + driverSeat = undefined, + passengerSeat = undefined; + + var TruckOwner = (function (_Model20) { + function TruckOwner() { + _classCallCheck(this, TruckOwner); + + _get(Object.getPrototypeOf(TruckOwner.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(TruckOwner, _Model20); + + _createClass(TruckOwner, [{ + key: "associate", + value: function associate() { + this.belongsTo("truck"); + this.belongsTo("owner"); + } + }]); + + return TruckOwner; + })(_libModelJs2["default"]); + + var Truck = (function (_Model21) { + function Truck() { + _classCallCheck(this, Truck); + + _get(Object.getPrototypeOf(Truck.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Truck, _Model21); + + _createClass(Truck, [{ + key: "associate", + value: function associate() { + this.hasMany("truckOwners"); + this.hasMany("owners", Owner).through("truckOwners"); + this.hasMany("wheels", Wheel); + this.hasOne("steeringWheel", SteeringWheel); + } + }]); + + return Truck; + })(_libModelJs2["default"]); + + var Owner = (function (_Model22) { + function Owner() { + _classCallCheck(this, Owner); + + _get(Object.getPrototypeOf(Owner.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Owner, _Model22); + + _createClass(Owner, [{ + key: "associate", + value: function associate() { + this.hasMany("truckOwners", TruckOwner); + this.hasMany("trucks", Truck).through("truckOwners"); + } + }]); + + return Owner; + })(_libModelJs2["default"]); + + var Wheel = (function (_Model23) { + function Wheel() { + _classCallCheck(this, Wheel); + + _get(Object.getPrototypeOf(Wheel.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Wheel, _Model23); + + _createClass(Wheel, [{ + key: "associate", + value: function associate() { + this.belongsTo("truck", Truck); + } + }, { + key: "save", + value: function save(callback) { + wheelSaveSpy(callback); + _get(Object.getPrototypeOf(Wheel.prototype), "save", this).call(this, callback); + } + }]); + + return Wheel; + })(_libModelJs2["default"]); + + var SteeringWheel = (function (_Model24) { + function SteeringWheel() { + _classCallCheck(this, SteeringWheel); + + _get(Object.getPrototypeOf(SteeringWheel.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(SteeringWheel, _Model24); + + _createClass(SteeringWheel, [{ + key: "associate", + value: function associate() { + this.belongsTo("truck", Truck); + } + }, { + key: "save", + value: function save(callback) { + steeringWheelSaveSpy(callback); + _get(Object.getPrototypeOf(SteeringWheel.prototype), "save", this).call(this, callback); + } + }]); + + return SteeringWheel; + })(_libModelJs2["default"]); + + var Seat = (function (_Model25) { + function Seat() { + _classCallCheck(this, Seat); + + _get(Object.getPrototypeOf(Seat.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Seat, _Model25); + + _createClass(Seat, [{ + key: "associate", + value: function associate() { + this.belongsTo("truck", Truck); + } + }]); + + return Seat; + })(_libModelJs2["default"]); + + describe("(assignment)", function () { + + beforeEach(function () { + wheelSaveSpy = sinon.spy(); + steeringWheelSaveSpy = sinon.spy(); + + truck = new Truck(); + owner = new Owner(); + wheel = new Wheel(); + steeringWheel = new SteeringWheel(); + + driverSeat = new Seat(); + passengerSeat = new Seat(); + }); + + it("should throw when assign a non model object to a belongsTo association", function () { + (function () { + steeringWheel.truck = {}; + }).should["throw"]("Cannot set a non model entity onto this property. It should inherit from Model"); + }); + + it("should throw when assign a non model object to a hasOne association", function () { + (function () { + truck.steeringWheel = {}; + }).should["throw"]("Cannot set a non model entity onto this property. It should inherit from Model"); + }); + + it("should associate a hasOne from a belongsTo", function () { + steeringWheel.truck = truck; + truck.should.have.property("steeringWheel"); + }); + + // TODO: This is impossible as is + xit("should associate a hasMany from a belongsTo", function () { + wheel.truck = truck; + truck.wheels.length.should.equal(1); + }); + + it("should associate a belongsTo from a hasMany", function () { + truck.wheels.push(wheel); + wheel.truck.should.eql(truck); + }); + + it("should associate a hasMany from a hasMany", function () { + truck.owners.push(owner); + owner.trucks[0].should.eql(truck); + }); + + it("should associate a belongsTo from a hasOne", function () { + truck.steeringWheel = steeringWheel; + steeringWheel.truck.should.eql(truck); + }); + }); + + describe("(propagation)", function () { + beforeEach(function () { + wheelSaveSpy = sinon.spy(); + steeringWheelSaveSpy = sinon.spy(); + + truck = new Truck({ id: 1 }); + wheel = new Wheel(); + steeringWheel = new SteeringWheel(); + + truck.steeringWheel = steeringWheel; + + //TODO: wrap push on typed collection? + wheel.truck = truck; + truck.wheels.push(wheel); + }); + + it("should propagate .save() to hasOne associations", function (done) { + truck.save(function (error) { + steeringWheelSaveSpy.calledOnce.should.be["true"]; + done(); + }); + }); + + it("should propagate .save() to hasMany associations", function (done) { + truck.save(function (error) { + wheelSaveSpy.called.should.be["true"]; + done(); + }); + }); + }); + }); + }); + + describe("(database updating)", function () { + describe("(model not new)", function () { + beforeEach(function () { + model.id = 1; + + _libModelJs2["default"].database.update = sinon.spy(_libModelJs2["default"].database.update); + + var regularExpressions = { + updateModel: /update `models` set `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + }; + + _libModelJs2["default"].database.mock(_defineProperty({}, regularExpressions.updateModel, [{}])); + }); + it("should update the record the database", function () { + model.save(function (error) { + _libModelJs2["default"].database.update.called.should.be["true"]; + }); + }); + }); + + describe("(model not new)", function () { + beforeEach(function () { + _libModelJs2["default"].database.insert = sinon.spy(_libModelJs2["default"].database.insert); + + var regularExpressions = { + insertModel: /insert into `models` \(`created_at`\) values \('1969-12-31 [0-9][0-9]:00:00.000'\)/ + }; + + _libModelJs2["default"].database.mock(_defineProperty({}, regularExpressions.insertModel, [{}])); + }); + it("should update the record the database", function () { + model.save(function (error) { + _libModelJs2["default"].database.insert.called.should.be["true"]; + }); + }); + }); + }); + + describe("(when model is invalid)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select count(*) as `rowCount` from `photos` where `user_id` = 1": [{ rowCount: 0 }] + }); + }); + + it("should call back with an error", function () { + user.save(function (error) { + error.should.be.instanceOf(Error); + }); + }); + + it("should inform the user that the model is invalid", function () { + user.save(function (error) { + error.message.should.eql("photos must be present on User"); + }); + }); + }); + }); + + describe("(without Model.database set)", function () { + beforeEach(function () { + delete _libModelJs2["default"].database; + }); + + it("should call back with an error", function () { + (function () { + user.save(); + }).should["throw"]("Cannot save without Model.database set."); + }); + }); + }); + }); + + describe("(exporting)", function () { + describe(".toJSON()", function () { + it("should return a plain unformatted model", function () { + user.toJSON().should.eql(userAttributes); + }); + + it("should return a formatted model if a formatter is set on Model.jsonFormatter", function () { + var newUserAttributes = { someCustomAttribute: "someCustomAttributeValue" }; + _libModelJs2["default"].jsonFormatter = function () { + return newUserAttributes; + }; + user.toJSON().should.eql(newUserAttributes); + }); + }); + }); +}); \ No newline at end of file diff --git a/es5/spec/validation/isPresent.spec.js b/es5/spec/validation/isPresent.spec.js new file mode 100644 index 0000000..d913c68 --- /dev/null +++ b/es5/spec/validation/isPresent.spec.js @@ -0,0 +1,346 @@ +"use strict"; + +var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); + +var _get = function get(_x, _x2, _x3) { var _again = true; _function: while (_again) { var object = _x, property = _x2, receiver = _x3; desc = parent = getter = undefined; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x = parent; _x2 = property; _x3 = receiver; _again = true; continue _function; } } else if ("value" in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; + +function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { "default": obj }; } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) subClass.__proto__ = superClass; } + +var _libValidationIsPresentJs = require("../../lib/validation/isPresent.js"); + +var _libValidationIsPresentJs2 = _interopRequireDefault(_libValidationIsPresentJs); + +var _libModelJs = require("../../lib/model.js"); + +var _libModelJs2 = _interopRequireDefault(_libModelJs); + +var _almaden = require("almaden"); + +var _almaden2 = _interopRequireDefault(_almaden); + +/* Test Configuration */ +//nothing from a real connection needed since we are mocking here +var databaseConfig = { + "debug": true, + "client": "mysql", + "connection": {}, + "pool": { + "max": 2, + "min": 2 + } +}; + +describe("isPresent(item, callback)", function () { + var Wheel = (function (_Model) { + function Wheel() { + _classCallCheck(this, Wheel); + + _get(Object.getPrototypeOf(Wheel.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Wheel, _Model); + + return Wheel; + })(_libModelJs2["default"]); + + var SteeringWheel = (function (_Model2) { + function SteeringWheel() { + _classCallCheck(this, SteeringWheel); + + _get(Object.getPrototypeOf(SteeringWheel.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(SteeringWheel, _Model2); + + return SteeringWheel; + })(_libModelJs2["default"]); + + var Street = (function (_Model3) { + function Street() { + _classCallCheck(this, Street); + + _get(Object.getPrototypeOf(Street.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Street, _Model3); + + _createClass(Street, [{ + key: "associate", + value: function associate() { + this.hasMany("trucks", Truck); + } + }]); + + return Street; + })(_libModelJs2["default"]); + + var Truck = (function (_Model4) { + function Truck() { + _classCallCheck(this, Truck); + + _get(Object.getPrototypeOf(Truck.prototype), "constructor", this).apply(this, arguments); + } + + _inherits(Truck, _Model4); + + _createClass(Truck, [{ + key: "associate", + value: function associate() { + this.belongsTo("street", Street); + this.hasOne("steeringWheel", SteeringWheel); + this.hasMany("wheels", Wheel); + } + }, { + key: "validate", + value: function validate() { + this.ensure("steeringWheel", _libValidationIsPresentJs2["default"]); + this.ensure("street", _libValidationIsPresentJs2["default"]); + this.ensure("wheels", _libValidationIsPresentJs2["default"]); + } + }]); + + return Truck; + })(_libModelJs2["default"]); + + var truck = undefined, + steeringWheel = undefined, + street = undefined, + trueValue = undefined, + falseValue = undefined; + + beforeEach(function () { + + _libModelJs2["default"].database = new _almaden2["default"](databaseConfig); + + truck = new Truck(); + street = new Street(); + steeringWheel = new SteeringWheel(); + + truck.id = 1; + + trueValue = { + result: true, + message: "must be present on Truck" + }; + + falseValue = { + result: false, + message: "must be present on Truck" + }; + }); + + it("should be a function", function () { + (typeof _libValidationIsPresentJs2["default"]).should.equal("function"); + }); + + describe("(default error message)", function () { + it("should return a default error message with failure", function () {}); + }); + + describe("(association is present)", function () { + describe("(as a property)", function () { + beforeEach(function () { + truck.wheels.push(new Wheel()); + truck.wheels.push(new Wheel()); + truck.wheels.push(new Wheel()); + truck.wheels.push(new Wheel()); + truck.steeringWheel = steeringWheel; + truck.street = street; + }); + + describe("(hasOne)", function () { + it("should return true", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "steeringWheel", function (error, result) { + result.should.eql(trueValue); + done(); + }); + }); + }); + + describe("(hasMany)", function () { + it("should return true if collections has elements", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "wheels", function (error, result) { + result.should.eql(trueValue); + done(); + }); + }); + }); + + describe("(belongsTo)", function () { + it("should return true", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "street", function (error, result) { + result.should.eql(trueValue); + done(); + }); + }); + }); + }); + + describe("(on the database due to lazy loading)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select count(*) as `rowCount` from `wheels` where `truck_id` = 1": [{ rowCount: 1 }] + }); + truck = new Truck(); + truck.id = 1; + truck.steeringWheel = steeringWheel; + }); + + describe("(hasMany)", function () { + it("should return true if collection is zero length but there are records on the database", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "wheels", function (error, result) { + result.should.eql(trueValue); + done(); + }); + }); + }); + }); + }); + + describe("(association not present)", function () { + describe("(when model is new)", function () { + beforeEach(function () { + delete truck.id; + }); + + describe("(when property is set)", function () { + describe("(belongsTo)", function () { + beforeEach(function () { + truck.streetId = 1; + truck.street = null; + }); + it("should return true", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "street", function (error, result) { + result.should.eql(trueValue); + done(); + }); + }); + }); + + describe("(on the database due to lazy loading)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select count(*) as `rowCount` from `wheels` where `truck_id` = 1": [{ rowCount: 0 }] + }); + truck = new Truck(); + truck.steeringWheel = steeringWheel; + }); + + describe("(hasMany)", function () { + it("should return false if collection is zero length AND there are no records on the database", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "wheels", function (error, result) { + result.should.eql(falseValue); + done(); + }); + }); + }); + }); + }); + + describe("(when property not set)", function () { + describe("(hasOne)", function () { + it("should return false", function (done) { + truck.steeringWheel = null; + _libValidationIsPresentJs2["default"].call(truck, "steeringWheel", function (error, result) { + result.should.eql(falseValue); + done(); + }); + }); + }); + + describe("(belongsTo)", function () { + it("should return false when a belongsTo association is not present", function (done) { + truck.street = null; + _libValidationIsPresentJs2["default"].call(truck, "street", function (error, result) { + result.should.eql(falseValue); + done(); + }); + }); + }); + }); + + describe("(hasMany)", function () { + it("should return false when a hasMany association is not present", function (done) { + delete truck.id; + _libValidationIsPresentJs2["default"].call(truck, "wheels", function (error, result) { + result.should.eql(falseValue); + done(); + }); + }); + }); + }); + + describe("(when model not new)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({}); + }); + + describe("(hasOne)", function () { + describe("(association not present in database)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select count(*) as `rowCount` from `steering_wheels` where `truck_id` = 1": [{ rowCount: 0 }] + }); + }); + + it("should return false", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "steeringWheel", function (error, result) { + result.should.eql(falseValue); + done(); + }); + }); + }); + + describe("(association is present in database)", function () { + beforeEach(function () { + _libModelJs2["default"].database.mock({ + "select count(*) as `rowCount` from `steering_wheels` where `truck_id` = 1": [{ rowCount: 1 }] + }); + }); + + it("should return true", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "steeringWheel", function (error, result) { + result.should.eql(trueValue); + done(); + }); + }); + }); + }); + + describe("(belongsTo)", function () { + describe("(association id is present)", function () { + beforeEach(function () { + truck.streetId = 1; + }); + it("should return true", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "street", function (error, result) { + result.should.eql(trueValue); + done(); + }); + }); + }); + describe("(association id not present)", function () { + it("should return false", function (done) { + _libValidationIsPresentJs2["default"].call(truck, "street", function (error, result) { + result.should.eql(falseValue); + done(); + }); + }); + }); + }); + }); + }); + + it("should return true when a hasMany association is present", function (done) { + truck.id = undefined; + truck.wheels.push(new Wheel()); + _libValidationIsPresentJs2["default"].call(truck, "wheels", function (error, result) { + result.should.eql(trueValue); + done(); + }); + }); +}); \ No newline at end of file diff --git a/es6/lib/collection.js b/es6/lib/collection.js index 0d71db3..7193bb8 100644 --- a/es6/lib/collection.js +++ b/es6/lib/collection.js @@ -2,6 +2,8 @@ import inflect from "jargon"; export default class Collection extends Array { constructor (initialData) { + super(); + let association = null, modelConstructor = null; @@ -56,15 +58,14 @@ export default class Collection extends Array { //lookup if there is an existing through model... how? let throughModel = new throughAssociation.constructor(); - console.log("assigning 2", {model: model, as: this.association.foreignName, on: throughModel.constructor.name}); throughModel[this.association.foreignName] = this.association.parent; + // work in progress for future automations // let throughAssociationPropertyName = model.associations[throughAssociationNameOnModel].foreignName; // throughModel[this.association.foreignName] = this.association.parent; // throughModel[throughAssociationPropertyName] = model; - //HERE I WILL NEED THE RELATIONSHIP BETWEEN MODEL and THROUGH MODEL... - //throughModel[this.association.parent.foreignName] = this.association.parent; - //this.association.p + // HERE I WILL NEED THE RELATIONSHIP BETWEEN MODEL and THROUGH MODEL... + // throughModel[this.association.parent.foreignName] = this.association.parent; } } else { if(model instanceof this._modelConstructor) { diff --git a/es6/lib/dovima.js b/es6/lib/dovima.js deleted file mode 100644 index 550c7e0..0000000 --- a/es6/lib/dovima.js +++ /dev/null @@ -1,977 +0,0 @@ -const async = require("flowsync"); - -/* Component Dependencies */ -// -import MultiError from "blunder"; -import Datetime from "fleming"; -import inflect from "jargon"; - -import {ModelQuery} from "./modelFinder.js"; - -/* Private Method Symbols */ -const callDeep = Symbol(), - addAssociation = Symbol(), - getFieldAttributes = Symbol(), - parseAttributesFromFields = Symbol(), - setAttributes = Symbol(), - attributes = Symbol(), - associations = Symbol(), - properties = Symbol(), - validations = Symbol(), - isNew = Symbol(), - fetchBy = Symbol(); - -/** - * @class Model - */ - -export default class Model { - /** - * @param {Object.} [initialAttributes] Provide default values for attributes by passing a Key-Value Object. - * @constructor - */ - constructor(initialAttributes) { - [ - "_validations", - "_associations" - ].forEach((privatePropertyName) => { - Object.defineProperty(this, privatePropertyName, { - writable: true, - enumerable: false, - value: {} - }); - }); - /** - * Define dynamic properties - */ - Object.defineProperties(this, { - "_includeAssociations": { - enumerable: false, - writable: true, - value: [] - }, - - "isNew": { - get: this[isNew] - }, - - "attributes": { - get: this[attributes], - set: this[setAttributes] - }, - - "associations": { - get: this[associations] - }, - - "properties": { - get: this[properties] - }, - - "validations": { - get: this[validations] - }, - - "_tableName": { - enumerable: false, - writable: true - }, - - "tableName": { - get: () => { - return this._tableName || inflect(this.constructor.name).plural.snake.toString(); - }, - set: (newTableName) => { - this._tableName = newTableName; - } - }, - - "_primaryKey": { - enumerable: false, - writable: true - }, - - "primaryKey": { - get: () => { - return this._primaryKey || "id"; - }, - set: (newPrimaryKey) => { - this._primaryKey = newPrimaryKey; - } - } - }); - - this.associate(); - this.validate(); - - this[setAttributes](initialAttributes); - - this.initialize(); - } - - hasOne(associationName, associationConstructor) { - return this[addAssociation]({ - name: associationName, - constructor: associationConstructor, - type: "hasOne" - }); - } - - belongsTo(associationName, associationConstructor) { - return this[addAssociation]({ - name: associationName, - constructor: associationConstructor, - type: "belongsTo" - }); - } - - hasMany(associationName, associationConstructor) { - return this[addAssociation]({ - name: associationName, - constructor: associationConstructor, - type: "hasMany" - }); - } - - ensure(attributeName, validatorFunction, validatorMessage) { - this._validations[attributeName] = this._validations[attributeName] || []; - - let validatorDetails = {validator: validatorFunction}; - - if (validatorMessage) { validatorDetails.message = validatorMessage; } - - this._validations[attributeName].push(validatorDetails); - } - - /** - * Return a boolean indicating whether the model is valid or not. - * - * @method isValid - * @param {Function(boolean)} callback Callback returning the boolean. - */ - isValid(callback) { - this.invalidAttributes((invalidAttributeList) => { - callback(Object.keys(invalidAttributeList).length === 0); - }); - } - - /** - * Return an object containing all invalid attributes and their errors. - * - * @example - * ``` - * model.invalidAttributes((invalidAttributeList) => { - * console.log(invalidAttributeList); // {"name":["Cannot contain special characters", "Cannot contain numbers"], "age":["Cannot be under 18"]} - * }); - * ``` - * - * @method invalidAttributes - * @param {Function(invalidAttributeList)} callback Callback returning the invalid attribute list. - */ - invalidAttributes(callback) { - const attributeNamesWithValidators = Object.keys(this._validations); - - const compileInvalidAttributeList = (errors, validatorMessages) => { - if (errors) { - throw errors; - } else { - let invalidAttributeList = {}; - - for(var index = 0; index < attributeNamesWithValidators.length; index++){ - const invalidMessages = validatorMessages[index]; - - if (invalidMessages.length > 0) { - const attributeName = attributeNamesWithValidators[index]; - invalidAttributeList[attributeName] = invalidMessages; - } - } - - callback(invalidAttributeList); - } - }; - - const performValidationsForAttribute = (attributeName, done) => { - const attributeValidations = this._validations[attributeName]; - - const performValidation = (validation, returnValue) => { - const validator = validation.validator; - - validator.call(this, attributeName, (error, validatorDetails) => { - if (validatorDetails.result) { - returnValue(null, null); - } else { - returnValue(null, validation.message || validatorDetails.message); - } - }); - }; - - const compileValidatorResponses = (error, invalidMessages) => { - const cleanedMessages = []; - // Trick to remove falsy values from an array - for(let message of invalidMessages){ - message && cleanedMessages.push(message); - } - done(null, cleanedMessages); - }; - - async.mapParallel( - attributeValidations, - performValidation, - compileValidatorResponses - ); - }; - - async.mapSeries( - attributeNamesWithValidators, - performValidationsForAttribute, - compileInvalidAttributeList - ); - } - - include(...associationNames) { - this._includeAssociations = associationNames; - return this; - } - - fetch(...options) { - switch(options.length) { - case 0: - this[fetchBy](); - break; - case 1: - if(typeof options[0] === "function") { - this[fetchBy]([this.primaryKey], options[0]); - } else if(Array.isArray(options[0])) { - this[fetchBy](options[0]); - } else { - this[fetchBy]([options[0]]); - } - break; - case 2: - if(Array.isArray(options[0])) { - this[fetchBy](options[0], options[1]); - } else { - this[fetchBy]([options[0]], options[1]); - } - break; - } - } - - [fetchBy](fields = [this.primaryKey], callback) { - if (!this.constructor.database) { throw new Error("Cannot fetch without Model.database set."); } - - let chain = this.constructor.database - .select("*") - .from(this.tableName); - fields.forEach((field, index) => { - if (!this[field]) { throw new Error(`Cannot fetch this model by the '${field}' field because it is not set.`); } - - if(index === 0) { - chain = chain.where(field, "=", this[field]); - } else { - chain = chain.andWhere(field, "=", this[field]); - } - }, this); - - chain - .limit(1) - .results((error, records) => { - if(records.length === 0) { - callback(new Error(`There is no ${this.constructor.name} for the given (${fields.join(", ")}).`)); - } else { - this[parseAttributesFromFields](records[0]); - - if (this._includeAssociations.length > 0) { - const modelFinder = new ModelFinder(this.constructor.database); - - const associations = this.associations; - - /* We'll be putting all of our async tasks into this */ - const fetchTasks = []; - - this._includeAssociations.forEach((associationName) => { - - const association = associations[associationName]; - - if (!association) { - throw new Error(`Cannot fetch '${associationName}' because it is not a valid association on ${this.constructor.name}`); - } - - switch(association.type) { - case "hasOne": - fetchTasks.push(finished => { - - // user hasMany address - - - const ModelClass = association.constructor; - - if (association.through) { - const throughAssociation = associations[association.through]; - - //throw throughAssociation.foreignId; - //select * from Addresses where user_id = this[this.primaryKey] - //select * from PostalCodes where address_id = address.id - if (!this[this.primaryKey]) { - throw new Error(`'${this.primaryKey}' is not set on ${this.constructor.name}`); - } - - modelFinder - .find(throughAssociation.constructor) - .where(association.foreignId, "=", this[this.primaryKey]) - .limit(1) - .results((errors, models) => { - const joinModel = models[0]; - const destinationAssociation = joinModel.associations[associationName]; - - //throw destinationAssociation.foreignId; - - //throw joinModel;//throw model.associations; - //addressId - - const tempModel = new association.constructor(); - modelFinder - .find(association.constructor) - .where(tempModel.primaryKey, "=", joinModel[destinationAssociation.foreignId]) - .limit(1) - .results((associationError, associationModels) => { - const associationModel = associationModels[0]; - this[associationName] = associationModel; - finished(); - }); - }); - } else { - const query = modelFinder - .find(ModelClass) - .where(association.foreignKey, "=", this[this.primaryKey]); - - const processWhereCondition = (value) => { - if (typeof value === "string") { - const snakeCasedValue = inflect(value).snake.toString(); - return snakeCasedValue; - } else { - return value; - } - }; - - const processedWhere = association.where.map(processWhereCondition); - - query.andWhere(function () { - this.where(...processedWhere); - - if(Array.isArray(association.andWhere)) { - association.andWhere.forEach((andWhereItem) => { - const processedAndWhereItem = andWhereItem.map(processWhereCondition); - this.andWhere(...processedAndWhereItem); - }); - } - }); - - query - .limit(1) - .results((errors, models) => { - const model = models[0]; - this[associationName] = model; - finished(); - }); - } - }); - break; - - case "hasMany": - - if (association.through) { - fetchTasks.push(finished => { - - const throughAssociation = associations[association.through]; - - modelFinder - .find(throughAssociation.constructor) - .where(association.foreignId, this[this.primaryKey]) - .results((errors, models) => { - if(models.length > 0) { - const foreignAssociationName = association.as || associationName; - - if (!models[0].associations[foreignAssociationName]) { - throw new Error(`'${foreignAssociationName}' is not a valid association on through model '${throughAssociation.constructor.name}'`); - } - - const destinationAssociation = models[0].associations[foreignAssociationName]; - - let modelIds = []; - - const tempModel = new association.constructor(); - - switch(destinationAssociation.type) { - case "hasOne": - //throw {through: throughAssociation, destination: destinationAssociation}; - - modelIds = models.map(model => { return model[throughAssociation.foreignId]; }); - - modelFinder - .find(association.constructor) - .where(tempModel.primaryKey, "in", modelIds) - .results((errors, models) => { - models.forEach((model) => { - this[associationName].push(model); - }); - finished(); - }); - - break; - - case "hasMany": - modelIds = models.map(model => { return model[model.primaryKey]; }); - - modelFinder - .find(association.constructor) - .where(destinationAssociation.foreignId, "in", modelIds) - .results((errors, models) => { - models.forEach((model) => { - this[associationName].push(model); - }); - finished(); - }); - break; - case "belongsTo": - //throw {through: throughAssociation, destination: destinationAssociation}; - - //throw destinationAssociation.name; - - //throw associationName; - - //const localId = inflect(destinationAssociation.name).foreignKey.camel.toString(); - - modelIds = models.map(model => { return model[destinationAssociation.foreignId]; }); - - modelFinder - .find(association.constructor) - .where(tempModel.primaryKey, "in", modelIds) - .results((errors, models) => { - models.forEach((model) => { - this[associationName].push(model); - }); - finished(); - }); - - break; - } - - //throw {association: association.foreignName, destinationAssociation: destinationAssociation.foreignName, throughAssociation: throughAssociation.foreignName}; - //throw {association: association.foreignId, destinationAssociation: destinationAssociation.foreignId, throughAssociation: throughAssociation.foreignId}; - //throw models; - - - } - }); - - // if (!this[throughAssociation.foreignId]) { - // throw new Error(`'${throughAssociation.foreignId}' is not set on ${this.constructor.name}`); - // } - - // modelFinder - // .find(throughAssociation.constructor) - // .where("id", "=", this[throughAssociation.foreignId]) - // .limit(1) - // .results((errors, models) => { - // const joinModel = models[0]; - // const destinationAssociation = joinModel.associations[associationName]; - - // //throw joinModel;//throw model.associations; - - // modelFinder - // .find(association.constructor) - // .where("id", "=", joinModel[destinationAssociation.foreignId]) - // .results((associationError, associationModels) => { - // const associationModel = associationModels[0]; - // this[associationName] = associationModel; - // }); - - // this[associationName] = joinModel; - // finished(); - // }); - }); - } else { - fetchTasks.push(finished => { - this[associationName].fetch(finished); - }); - } - break; - - case "belongsTo": - if (!this[association.foreignId]) { - throw new Error(`Cannot fetch '${associationName}' because '${association.foreignId}' is not set on ${this.constructor.name}`); - } - - fetchTasks.push(finished => { - modelFinder - .find(association.constructor) - .where("id", "=", this[association.foreignId]) - .limit(1) - .results((errors, models) => { - const model = models[0]; - this[associationName] = model; - model[association.foreignName] = this; - finished(); - }); - }); - - } - }); - - async.parallel( - fetchTasks, - () => { - if (callback) { - callback(error, this); - } - } - ); - } else { - if (callback) { - callback(error, this); - } - } - } - }); - } - - save(callback) { - if (!this.constructor.database) { throw new Error("Cannot save without Model.database set."); } - - async.series([ - (next) => { - this.beforeValidation(next); - }, - (next) => { - this.isValid((valid) => { - if(valid) { - next(); - } else { - this.invalidAttributes((invalidAttributeList) => { - const hasInvalidAttributes = Object.keys(invalidAttributeList).length > 0; - - if (hasInvalidAttributes) { - const multiError = new MultiError(); - for(let invalidAttributeName in invalidAttributeList) { - const invalidAttributeMessages = invalidAttributeList[invalidAttributeName]; - - const errorPrefix = this.constructor.name + " is invalid"; - - const attributesMultiError = new MultiError([], errorPrefix); - for(let index in invalidAttributeMessages) { - const invalidAttributeMessage = invalidAttributeMessages[index]; - const error = new Error(`${invalidAttributeName} ${invalidAttributeMessage}`); - attributesMultiError.push(error); - } - multiError.push(attributesMultiError); - } - next(multiError); - } else { - next(); - } - }); - } - }); - }, - (next) => { - this.beforeSave(next); - }, - (next) => { - if (this.isNew) { - let now = new Datetime(); - this.createdAt = now.toDate(); - let fieldAttributes = this[getFieldAttributes](); - - this.constructor.database - .insert(fieldAttributes) - .into(this.tableName) - .results((error, ids) => { - if(error) { - next(error); - } else { - this[this.primaryKey] = ids[0]; - next(); - } - }); - } else { - let now = new Datetime(); - this.updatedAt = now.toDate(); - let attributes = this[getFieldAttributes](); - let updateAttributes = {}; - - for (let attributeName in attributes) { - if (attributeName !== "id") { - updateAttributes[attributeName] = attributes[attributeName]; - } - } - - this.constructor.database - .update(updateAttributes) - .into(this.tableName) - .where("id", "=", this[this.primaryKey]) - .results(next); - } - }, - (next) => { - this[callDeep]("save", next); - }, - (next) => { - this.afterSave(next); - } - ], - (errors) => { - if(errors) { - callback(errors); - } else { - callback(undefined, this); - } - }); - } - - /* Stubbed methods for hooks */ - beforeValidation(callback) { - callback(); - } - - beforeSave(callback) { - callback(); - } - - afterSave(callback) { - callback(); - } - - associate() {} - - validate() {} - - initialize() {} - - /** - * Private Functionality - */ - - [setAttributes](newAttributes) { - this[parseAttributesFromFields](newAttributes); - } - - [associations]() { - return this._associations; - } - - [properties]() { - return Object.keys(this); - } - - [validations]() { - return this._validations; - } - - [attributes]() { - var attributeNames = {}; - this.properties.forEach((propertyName) => { - if(!this._associations[propertyName]) { - attributeNames[propertyName] = this[propertyName]; - } - }); - return attributeNames; - } - - [isNew]() { - if (this[this.primaryKey]) { - return false; - } else { - return true; - } - } - - /** - * Call a function deeply through all associations - * - * @private - * @method callDeep - * @param {String} functionName The name of the function that you want to fire deeply. - * @param {function(errors, results)} Function called at the end of the operation. - */ - [callDeep] (methodName, callback) { - const associationNames = Object.keys(this.associations); - - async.mapParallel( - associationNames, - (associationName, next) => { - - const associationDetails = this.associations[associationName]; - - switch(associationDetails.type) { - case "hasOne": - const model = this[associationName]; - if(model) { - //pass the associationDetails.whereArgs to the function - model[methodName](next); - } else { - next(); - } - break; - - case "hasMany": - const collection = this[associationName]; - //collection set, and not many to many (nothing in that case) - if (collection && associationDetails.through === undefined) { - //let array = [].slice.call(collection); - async.eachParallel( - collection, - (collectionModel, finishSubStep) => { - collectionModel[methodName](finishSubStep); - }, - next - ); - } else { - next(); //collection not set - } - break; - - case "belongsTo": - next(); - } - }, (errors, results) => { - callback(errors, results); - } - ); - } - - /** - * @example - * ``` - * const associationDetails = { - * name: "users", - * type: "hasMany", - * constructor: User - * }; - * - * [addAssociation](associationDetails); - * ``` - * @method addAssociation - * @param {Object.} associationDetails Object containing association details in key/value pairs. - */ - [addAssociation] (associationDetails) { - const association = { - parent: this, - type: associationDetails.type, - constructor: associationDetails.constructor - }; - - /* Default Values */ - - switch(associationDetails.type) { - case "hasOne": - case "hasMany": - association.foreignKey = inflect(this.constructor.name).foreignKey.toString(); - association.foreignId = inflect(association.foreignKey).camel.toString(); - break; - case "belongsTo": - association.foreignKey = inflect(associationDetails.name).foreignKey.toString(); - association.foreignId = inflect(association.foreignKey).camel.toString(); - } - - // TODO: AS - association.foreignName = inflect(this.constructor.name).camel.toString(); - - /* Override Values */ - - const associationSetter = new AssociationSetter(association); - - /* Set private space for value to be stored internally */ - - const privateAssociationName = "_" + associationDetails.name; - - /* Association Setter By Type */ - - let setterFunction; - - switch(associationDetails.type) { - case "hasOne": - this[privateAssociationName] = null; - - setterFunction = (newModel) => { - if (newModel && this[privateAssociationName] !== newModel) { - if(!(newModel instanceof Model)) { - throw new Error("Cannot set a non model entity onto this property. It should inherit from Model"); - } - - this[privateAssociationName] = newModel; - - newModel[association.foreignName] = this; - } - }; - break; - case "belongsTo": - this[privateAssociationName] = null; - - setterFunction = (newModel) => { - if (newModel && this[privateAssociationName] !== newModel) { - if(!(newModel instanceof Model)) { - throw new Error("Cannot set a non model entity onto this property. It should inherit from Model"); - } - - this[privateAssociationName] = newModel; - this[association.foreignId] = newModel.id; - - const pluralForeignName = inflect(association.foreignName).plural.toString(); - - if (!association.ambiguous) { - if (newModel.hasOwnProperty(association.foreignName)) { - newModel[association.foreignName] = this; - } else if (newModel.hasOwnProperty(pluralForeignName)) { - //lookup is it exist and dont add it in that case - newModel[pluralForeignName].push(this); - } else { - throw new Error(`Neither "${association.foreignName}" or "${pluralForeignName}" are valid associations on "${newModel.constructor.name}"`); - } - } - - // if (newModel[association.foreignName] instanceof Collection) { - // newModel[association.foreignName].push(this); - // } else { - // newModel[association.foreignName] = this; - // } - } - }; - break; - case "hasMany": - /* Set to null by default */ - this[privateAssociationName] = new Collection(association); - - setterFunction = (newModel) => { - if (newModel && this[privateAssociationName] !== newModel) { - this[privateAssociationName] = newModel; - } - }; - break; - default: - throw new Error("Unknown association type."); - } - - Object.defineProperty( - this, - privateAssociationName, - { - enumerable: false, - writable: true - } - ); - - Object.defineProperty( - this, - associationDetails.name, - { - enumerable: true, - set: setterFunction, - get: () => { - return this[privateAssociationName]; - } - } - ); - - this[associationDetails.name] = associationDetails.value; - - this._associations[associationDetails.name] = association; - - return associationSetter; - } - - [parseAttributesFromFields](record) { - for (var field in record) { - this[inflect(field).camel.toString()] = record[field]; - } - } - - [getFieldAttributes]() { - let attributeNames = Object.keys(this.attributes); - let fieldAttributes = {}; - attributeNames.forEach((attributeName) => { - fieldAttributes[inflect(attributeName).snake.toString()] = this[attributeName]; - }); - - //add belongsTo associations and remove others - Object.keys(this.associations).forEach((associationName) => { - let relatedModel = this[associationName]; - let foreignKeyField = inflect(associationName).foreignKey.toString(); - if(this._associations[associationName].type === "belongsTo") { - //try with relatedModel and relatedModel.id - if(relatedModel && relatedModel.id) { - fieldAttributes[foreignKeyField] = relatedModel.id; - } else { - //or just with the relatedModelId - //construct the snake with _id and then camelize it - let foreignIdAsAttribute = inflect(foreignKeyField).camel.toString(); - fieldAttributes[foreignKeyField] = this[foreignIdAsAttribute]; - } - } else { - //console.log("getFieldAttributes delete on ", {on: this.constructor.name, associationName: associationName, foreignKeyField: foreignKeyField, relatedModel: relatedModel}); - delete fieldAttributes[associationName]; - delete fieldAttributes["_" + associationName]; - } - }); - - return fieldAttributes; - } -} - -const ambiguous = Symbol(); - -export class AssociationSetter { - constructor(association) { - this.association = association; - - if (association.type === "belongsTo") { - Object.defineProperties(this, { - "ambiguous": { - get: this[ambiguous] - } - }); - } - } - - foreignName(name) { - this.association.foreignName = name; - return this; - } - - where(...options) { - this.association.where = options; - return this; - } - - andWhere(...options) { - this.association.andWhere = this.association.andWhere || []; - this.association.andWhere.push(options); - return this; - } - - through(associationName) { - this.association.through = associationName; - return this; - } - - as(associationName) { - this.association.as = associationName; - return this; - } - - [ambiguous]() { - this.association.ambiguous = true; - } -} - -Object.defineProperties(Model, { - "all": { - get: function getAll() { - let modelQuery = new ModelQuery(Model.database); - return modelQuery.find(this); - } - } -}); - -import Collection from "./collection.js"; -import ModelFinder from "./modelFinder.js"; diff --git a/es6/lib/model.js b/es6/lib/model.js index 550c7e0..8c96764 100644 --- a/es6/lib/model.js +++ b/es6/lib/model.js @@ -1,4 +1,4 @@ -const async = require("flowsync"); +const flowsync = require("flowsync"); /* Component Dependencies */ // @@ -98,6 +98,18 @@ export default class Model { set: (newPrimaryKey) => { this._primaryKey = newPrimaryKey; } + }, + + "_softDelete": { + enumerable: false, + writable: true, + value: false + }, + + "softDelete": { + get: () => { + this._softDelete = true; + } } }); @@ -214,14 +226,14 @@ export default class Model { done(null, cleanedMessages); }; - async.mapParallel( + flowsync.mapParallel( attributeValidations, performValidation, compileValidatorResponses ); }; - async.mapSeries( + flowsync.mapSeries( attributeNamesWithValidators, performValidationsForAttribute, compileInvalidAttributeList @@ -273,6 +285,10 @@ export default class Model { } }, this); + if(this._softDelete) { + chain = chain.whereNull(inflect("deletedAt").snake.toString()); + } + chain .limit(1) .results((error, records) => { @@ -286,7 +302,7 @@ export default class Model { const associations = this.associations; - /* We'll be putting all of our async tasks into this */ + /* We'll be putting all of our Async tasks into this */ const fetchTasks = []; this._includeAssociations.forEach((associationName) => { @@ -471,7 +487,7 @@ export default class Model { // modelFinder // .find(throughAssociation.constructor) - // .where("id", "=", this[throughAssociation.foreignId]) + // .where(this.primaryKey, "=", this[throughAssociation.foreignId]) // .limit(1) // .results((errors, models) => { // const joinModel = models[0]; @@ -481,7 +497,7 @@ export default class Model { // modelFinder // .find(association.constructor) - // .where("id", "=", joinModel[destinationAssociation.foreignId]) + // .where(this.primaryKey, "=", joinModel[destinationAssociation.foreignId]) // .results((associationError, associationModels) => { // const associationModel = associationModels[0]; // this[associationName] = associationModel; @@ -506,7 +522,7 @@ export default class Model { fetchTasks.push(finished => { modelFinder .find(association.constructor) - .where("id", "=", this[association.foreignId]) + .where(this.primaryKey, "=", this[association.foreignId]) .limit(1) .results((errors, models) => { const model = models[0]; @@ -519,7 +535,7 @@ export default class Model { } }); - async.parallel( + flowsync.parallel( fetchTasks, () => { if (callback) { @@ -536,10 +552,51 @@ export default class Model { }); } + delete(callback) { + if(this._softDelete) { + if (!this.constructor.database) { throw new Error("Cannot delete without Model.database set."); } + + if(this[this.primaryKey]) { + flowsync.series([ + (next) => { + this[callDeep]("delete", (associationDetails) => { + return (associationDetails.type !== "belongsTo" + && associationDetails.dependent === true); + }, next); + }, + (next) => { + let now = new Datetime(); + let attributesToUpdate = {}; + attributesToUpdate[inflect("deletedAt").snake.toString()] = now.toDate(); + this.constructor.database + .update(attributesToUpdate) + .into(this.tableName) + .where(this.primaryKey, "=", this[this.primaryKey]) + .results((error, results) => { + if(error) { + next(error); + } else if (results === 0) { + next(new Error(`${this.constructor.name} with ${this.primaryKey} ${this[this.primaryKey]} cannot be soft deleted because it doesn't exists.`)); + } else { + next(); + } + }); + } + ], (errors, results) => { + callback(errors, results); + }); + } else { + throw new Error(`Cannot delete the ${this.constructor.name} because the primary key is not set.`); + } + } else { + throw new Error("Not implemented."); + } + } + save(callback) { if (!this.constructor.database) { throw new Error("Cannot save without Model.database set."); } - async.series([ + flowsync.series([ (next) => { this.beforeValidation(next); }, @@ -601,7 +658,7 @@ export default class Model { let updateAttributes = {}; for (let attributeName in attributes) { - if (attributeName !== "id") { + if (attributeName !== this.primaryKey) { updateAttributes[attributeName] = attributes[attributeName]; } } @@ -609,12 +666,27 @@ export default class Model { this.constructor.database .update(updateAttributes) .into(this.tableName) - .where("id", "=", this[this.primaryKey]) + .where(this.primaryKey, "=", this[this.primaryKey]) .results(next); } }, (next) => { - this[callDeep]("save", next); + //disabling this rule because break is not necessary when return is present + /* eslint-disable no-fallthrough */ + this[callDeep]("save", (associationDetails) => { + switch(associationDetails.type) { + case "hasOne": + return true; + case "hasMany": + if(associationDetails.through === undefined) { + return true; + } else { + return false; + } + case "belongsTo": + return false; + } + }, next); }, (next) => { this.afterSave(next); @@ -648,6 +720,14 @@ export default class Model { initialize() {} + toJSON() { + if(Model.jsonFormatter && typeof Model.jsonFormatter === "function") { + return Model.jsonFormatter(this); + } else { + return this.attributes; + } + } + /** * Private Functionality */ @@ -694,21 +774,27 @@ export default class Model { * @param {String} functionName The name of the function that you want to fire deeply. * @param {function(errors, results)} Function called at the end of the operation. */ - [callDeep] (methodName, callback) { + [callDeep] (methodName, predicate, callback) { const associationNames = Object.keys(this.associations); - async.mapParallel( + flowsync.mapParallel( associationNames, (associationName, next) => { const associationDetails = this.associations[associationName]; switch(associationDetails.type) { + case "belongsTo": case "hasOne": const model = this[associationName]; if(model) { //pass the associationDetails.whereArgs to the function - model[methodName](next); + const result = predicate(associationDetails); + if(result) { + model[methodName](next); + } else { + next(); + } } else { next(); } @@ -717,12 +803,17 @@ export default class Model { case "hasMany": const collection = this[associationName]; //collection set, and not many to many (nothing in that case) - if (collection && associationDetails.through === undefined) { + if (collection) { //let array = [].slice.call(collection); - async.eachParallel( + flowsync.eachParallel( collection, (collectionModel, finishSubStep) => { - collectionModel[methodName](finishSubStep); + const result = predicate(associationDetails); + if(result) { + collectionModel[methodName](finishSubStep); + } else { + next(); + } }, next ); @@ -730,9 +821,6 @@ export default class Model { next(); //collection not set } break; - - case "belongsTo": - next(); } }, (errors, results) => { callback(errors, results); @@ -918,18 +1006,29 @@ export default class Model { } } -const ambiguous = Symbol(); +const ambiguous = Symbol(), + dependent = Symbol(); export class AssociationSetter { constructor(association) { this.association = association; - if (association.type === "belongsTo") { - Object.defineProperties(this, { - "ambiguous": { - get: this[ambiguous] - } - }); + switch(association.type) { + case "belongsTo": + Object.defineProperties(this, { + "ambiguous": { + get: this[ambiguous] + } + }); + break; + case "hasOne": + case "hasMany": + Object.defineProperties(this, { + "dependent": { + get: this[dependent] + } + }); + break; } } @@ -962,11 +1061,15 @@ export class AssociationSetter { [ambiguous]() { this.association.ambiguous = true; } + + [dependent]() { + this.association.dependent = true; + } } Object.defineProperties(Model, { - "all": { - get: function getAll() { + "find": { + get: function modelFind() { let modelQuery = new ModelQuery(Model.database); return modelQuery.find(this); } diff --git a/es6/lib/modelFinder.js b/es6/lib/modelFinder.js index 916a0cc..969d708 100644 --- a/es6/lib/modelFinder.js +++ b/es6/lib/modelFinder.js @@ -2,7 +2,7 @@ const validateDependencies = Symbol(); import inflect from "jargon"; export default class ModelFinder { - constructor(database) { + constructor(database) { Object.defineProperties(this, { "_database": { value: database, diff --git a/es6/lib/validation/isPresent.js b/es6/lib/validation/isPresent.js new file mode 100644 index 0000000..7f8afe8 --- /dev/null +++ b/es6/lib/validation/isPresent.js @@ -0,0 +1,120 @@ +import ModelFinder from "../modelFinder.js"; + +export default function isPresent(associationName, callback) { + const model = this; + const defaultErrorMessage = "must be present on " + model.constructor.name; + + if (!model.constructor.database) { throw new Error("Cannot check isPresent without a database set."); } + + //apiKey + let association = model.associations[associationName]; + let modelFinder = new ModelFinder(model.constructor.database); + + let associationModel = model[associationName]; //this["apiKey"], this["apiKeyId"] + + const resultDetails = { + result: undefined, + message: defaultErrorMessage + }; + + const foreignId = association.foreignId; + + if (associationModel) { + switch(association.type) { + case "hasOne": + case "belongsTo": + resultDetails.result = true; + callback(undefined, resultDetails); + break; + case "hasMany": + const collection = associationModel; + if (collection.length > 0) { + resultDetails.result = true; + callback(undefined, resultDetails); + } else { + if (model.isNew) { //it does not have an id to look for + resultDetails.result = false; + callback(null, resultDetails); + } else { + modelFinder.count(association.constructor) + .where(association.foreignKey, "=", model.id) + .results((error, count) => { + if (error) { + resultDetails.result = false; + callback(error, resultDetails); + } else { + const modelsFound = (count > 0); + + if(modelsFound){ + resultDetails.result = true; + callback(null, resultDetails); + } else { + resultDetails.result = false; + callback(null, resultDetails); + } + } + }); + } + } + break; + default: + throw new Error("Unknown association type"); + } + } else { + if (model.isNew) { + resultDetails.result = false; + switch(association.type) { + case "hasOne": + case "hasMany": + break; + case "belongsTo": + //console.log("LOOK", {model: model, foreignId: foreignId}) + if (model[foreignId]) { + resultDetails.result = true; + } else { + resultDetails.result = false; + } + break; + default: + throw new Error("Unknown association type"); + } + callback(null, resultDetails); + } else { + switch(association.type) { + case "hasOne": + case "hasMany": + modelFinder.count(association.constructor) + .where(association.foreignKey, "=", model.id) + .results((error, count) => { + if (error) { + resultDetails.result = false; + callback(error, resultDetails); + } else { + const modelsFound = (count > 0); + + if(modelsFound){ + resultDetails.result = true; + callback(null, resultDetails); + } else { + resultDetails.result = false; + callback(null, resultDetails); + } + } + }); + break; + case "belongsTo": + + if (model[foreignId]) { + resultDetails.result = true; + callback(null, resultDetails); + } else { + resultDetails.result = false; + callback(null, resultDetails); + } + break; + default: + throw new Error("Unknown association type"); + } + } + } +} diff --git a/es6/spec/dovima.spec.js b/es6/spec/dovima.spec.js deleted file mode 100644 index 55abf1b..0000000 --- a/es6/spec/dovima.spec.js +++ /dev/null @@ -1,13 +0,0 @@ -import Dovima from "../lib/dovima.js"; - -describe("Dovima", () => { - let component; - - before(() => { - component = new Dovima(); - }); - - it("should say something", () => { - component.saySomething().should.equal("Something"); - }); -}); diff --git a/es6/spec/fixtures/users.json b/es6/spec/fixtures/users.json new file mode 100644 index 0000000..ec08e0c --- /dev/null +++ b/es6/spec/fixtures/users.json @@ -0,0 +1,27 @@ +[ + { + "id": 5, + "name": "Bob Belcher", + "age": 46 + }, + { + "id": 1, + "name": "Linda Belcher", + "age": 42 + }, + { + "id": 2, + "name": "Gene Belcher", + "age": 11 + }, + { + "id": 3, + "name": "Tina Belcher", + "age": 13 + }, + { + "id": 4, + "name": "Louise Belcher", + "age": 9 + } +] diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index a436f12..68c9a49 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -4,14 +4,23 @@ const sinon = require("sinon"); //import MultiError from "../../multiError/multiError.js"; //import MultiError from "../../multiError/multiError.js"; -import Database from "../../database/database.js"; -import isPresent from "../validation/isPresent.js"; -import Collection from "../collection.js"; -import Model, {AssociationSetter} from "../model.js"; -import {ModelQuery} from "../modelFinder.js"; +import Database from "almaden"; +import isPresent from "../lib/validation/isPresent.js"; +import Collection from "../lib/collection.js"; +import Model, {AssociationSetter} from "../lib/model.js"; +import {ModelQuery} from "../lib/modelFinder.js"; /* Test Configuration */ -const databaseConfig = require("../../../database.json").testing; +//nothing from a real connection needed since we are mocking here +const databaseConfig = { + "debug": true, + "client": "mysql", + "connection": {}, + "pool": { + "max": 2, + "min": 2 + } +}; let userFixtures = require("./fixtures/users.json"); @@ -227,7 +236,7 @@ describe("Model(attributes, options)", () => { }); describe("(static properties)", () => { - describe(".all", () => { + describe(".find", () => { let users, userCollection; @@ -245,7 +254,7 @@ describe("Model(attributes, options)", () => { }); User - .all + .find .where("momId", "=", 1) .results((error, fetchedUsers) => { users = fetchedUsers; @@ -254,7 +263,7 @@ describe("Model(attributes, options)", () => { }); it("should return a ModelQuery instance", () => { - User.all.should.be.instanceOf(ModelQuery); + User.find.should.be.instanceOf(ModelQuery); }); it("should return a collection", () => { @@ -268,6 +277,72 @@ describe("Model(attributes, options)", () => { it("should allow to search all models that matchs a certain condition", () => { users.length.should.equal(5); }); + + describe(".one", () => { + beforeEach(done => { + Model.database.mock({ + "select * from `users` where `mom_id` = 1 limit 1": [ + userFixtures[0] + ] + }); + + User.find + .one + .where("momId", 1) + .results((error, fetchedUsers) => { + users = fetchedUsers; + done(); + }); + }); + + it("should return just one user", () => { + users.length.should.equal(1); + }); + }); + + describe(".all", () => { + beforeEach(done => { + User.find + .all + .where("momId", 1) + .results((error, fetchedUsers) => { + users = fetchedUsers; + done(); + }); + }); + + it("should return just all users matching the condition", () => { + users.length.should.equal(5); + }); + }); + + describe(".deleted", () => { + class SoftUser extends Model { + initialize() { + this.softDelete; + } + } + + beforeEach(done => { + Model.database.mock({ + "select * from `soft_users` where `mom_id` = 1 and `deleted_at` is not null": + userFixtures + }); + + SoftUser.find + .all + .where("momId", 1) + .deleted + .results((error, fetchedUsers) => { + users = fetchedUsers; + done(); + }); + }); + + it("should return just all users matching the condition", () => { + users.length.should.equal(5); + }); + }); }); }); @@ -701,87 +776,25 @@ describe("Model(attributes, options)", () => { }); }); - describe(".through(associationName)", () => { + xdescribe(".through(associationName)", () => { beforeEach(() => { Model.database.mock({ - "select * from `users` where `id` = 1 limit 1": [ - { id: 1 }, - { id: 2 } - ], - "select * from `photos` where `user_id` = 1": [ - { id: 1, name: "Favorite Face Photo" }, - { id: 2, name: "Another Favorite Face Photo" }, - { id: 3, name: "Mostest Favoritest Face Photo" } - ], - "select * from `comments` where `photo_id` in (1, 2, 3)": [ - { message: "nice photo!", author_id: 1, photo_id: 1 }, - { message: "where is it?", author_id: 2, photo_id: 1 }, - ] + }); }); - it("should fetch hasMany through associations", done => { + it("should return the association set to allow further chaining", () => { user - .include("comments") - .fetch(() => { - user.comments[0].should.be.instanceOf(Comment); - done(); - }); + .hasMany("comments", Comment) + .through("apiKey") + .should.be.instanceOf(AssociationSetter); }); - describe("(through hasMany)", () => { - let account, - accountContentPackage, - contentPackage; - //better example - class Account extends Model { - associate() { - this.hasMany("accountContentPackages", AccountContentPackage); - this.hasMany("contentPackages", ContentPackage) - .through("accountContentPackages"); - } - } - - class AccountContentPackage extends Model { - associate() { - this.belongsTo("account", Account); - this.belongsTo("contentPackage", ContentPackage); - } - } - - class ContentPackage extends Model { - associate() { - this.hasMany("accountContentPackages", AccountContentPackage); - - this.hasMany("accounts", Account) - .through("accountContentPackages", AccountContentPackage); - } - } - - beforeEach(() => { - account = new Account({id: 1}); - contentPackage = new ContentPackage({id: 2}); - account.contentPackages.push(contentPackage); - }); - - it("should create a the through model when pushing", () => { - account.accountContentPackages.length.should.equal(1); - }); - - it("should association the new through model with the parent", () => { - account.accountContentPackages[0].account.should.eql(account); - }); - - it("should association the new through model with the other side of the relationship", () => { - account.accountContentPackages[0].contentPackage.should.eql(contentPackage); - }); - - //need to prepare test case for this - xit("should throw an error if the through association does not exists", () => { - () => { - account.hasMany("specialContentPackages", Comment) - .through("specialAccountContentPackages"); - }.should.throw("Through association called specialAccountContentPackages not defined on model Account"); + it("should fetch hasMany through associations", done => { + user.include("comments").fetch((error) => { + user.comments. + user.comments[0].instanceOf(Comment); + done(); }); }); }); @@ -1142,6 +1155,203 @@ describe("Model(attributes, options)", () => { }); }); + describe(".delete(callback)", () => { + describe("(when dependent is declared on the association)", () => { + class Account extends Model { + initialize() { + this.softDelete; + } + + associate() { + this.hasOne("forumUser", ForumUser) + .dependent; + } + } + + class ForumUser extends Model { + initialize() { + this.softDelete; + } + + associate() { + this.hasMany("posts", Post) + .dependent; + this.belongsTo("account", Account) + .dependent; + } + } + + class Post extends Model { + initialize() { + this.softDelete; + } + + associate() { + this.belongsTo("forumUser", ForumUser); + } + } + + let forumUser, + account, + post; + + beforeEach(() => { + account = new Account({id: 1}); + forumUser = new ForumUser({id: 2}); + post = new Post({id: 3}); + }); + + it("should add the association to .associations", () => { + account.associations.forumUser.should.eql({ + parent: account, + type: "hasOne", + constructor: ForumUser, + foreignName: "account", + foreignId: "accountId", + foreignKey: "account_id", + dependent: true + }); + }); + + describe("(on a hasOne)", () => { + let userDeleteQuerySpy; + + beforeEach(() => { + Model.database.mock({ + [/update `accounts` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/]: + 1 + }); + + userDeleteQuerySpy = Model.database.spy( + /update `forum_users` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, + 1); + + account.forumUser = forumUser; + }); + + it("should propagate delete on those models", done => { + account.delete(() => { + userDeleteQuerySpy.callCount.should.equal(1); + done(); + }); + }); + }); + + describe("(on a hasMany)", () => { + let postDeleteQuerySpy; + + beforeEach(() => { + Model.database.mock({ + [/update `forum_users` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 2/]: + 1 + }); + + postDeleteQuerySpy = Model.database.spy( + /update `posts` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 3/, + 1); + + forumUser.posts.push(post); + }); + + it("should propagate delete on those models", done => { + forumUser.delete(() => { + postDeleteQuerySpy.callCount.should.equal(1); + done(); + }); + }); + }); + }); + + describe("(when .softDelete is not called)", () => { + class Post extends Model {} + let post; + + beforeEach(() => { + post = new Post(); + }); + + it("should throw when calling delete", () => { + () => { + post.delete(); + }.should.throw("Not implemented."); + }); + }); + + describe("when softDelete called)", () => { + let post; + + class Post extends Model { + initialize() { + this.softDelete; + } + } + + beforeEach(() => { + post = new Post(); + }); + + describe("(Model.database is set)", () => { + describe("(when primaryKey is set)", () => { + beforeEach(() => { + post.id = 1; + Model.database.mock({ + [/update `posts` set `deleted_at` = \'1969-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/]: + 1 + }); + }); + + it("should not throw when calling delete", () => { + () => { + post.delete(() => {}); + }.should.not.throw(); + }); + + it("should return no error", () => { + post.delete((error) => { + (error == null).should.be.true; + }); + }); + + describe("(when primary key is set but not exists)", () => { + beforeEach(() => { + post.id = 1; + Model.database.mock({ + [/update `posts` set `deleted_at` = \'1969-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/]: + 0 + }); + }); + + it("should return an error", () => { + post.delete((error) => { + error.should.eql(new Error("Post with id 1 cannot be soft deleted because it doesn't exists.")); + }); + }); + }); + }); + + describe("(when primaryKey is not set)", () => { + it("should throw an error", () => { + () => { + post.delete(() => {}); + }.should.throw("Cannot delete the Post because the primary key is not set."); + }); + }); + }); + + describe("(Model.database not set)", () => { + beforeEach(() => { + delete Model.database; + }); + + it("should throw an error", () => { + () => { + post.delete(); + }.should.throw("Cannot delete without Model.database set."); + }); + }); + }); + }); + describe(".fetch(callback)", () => { describe("(Model.database is set)", () => { beforeEach(() => { @@ -1170,6 +1380,30 @@ describe("Model(attributes, options)", () => { done(); }); }); + + describe("(when soft delete is enabled)", () => { + let post, + deleteQuerySpy; + + class Post extends Model { + initialize() { + this.softDelete; + } + } + + beforeEach(() => { + post = new Post({id: 1}); + //querySpy + deleteQuerySpy = Model.database.spy("select * from `posts` where `id` = 1 and `deleted_at` is null limit 1", [{}]); + }); + + it("should add a where deleted is not null condition", done => { + post.fetch(() => { + deleteQuerySpy.callCount.should.equal(1); + done(); + }); + }); + }); }); describe("(when model does not have a primary key set)", () => { @@ -1417,14 +1651,14 @@ describe("Model(attributes, options)", () => { class TruckOwner extends Model { associate() { - this.belongsTo("truck", Truck); - this.belongsTo("owner", Owner); + this.belongsTo("truck"); + this.belongsTo("owner"); } } class Truck extends Model { associate() { - this.hasMany("truckOwners", TruckOwner); + this.hasMany("truckOwners"); this.hasMany("owners", Owner) .through("truckOwners"); this.hasMany("wheels", Wheel); @@ -1632,4 +1866,20 @@ describe("Model(attributes, options)", () => { }); }); }); + + describe("(exporting)", () => { + describe(".toJSON()", () => { + it("should return a plain unformatted model", () => { + user.toJSON().should.eql(userAttributes); + }); + + it("should return a formatted model if a formatter is set on Model.jsonFormatter", () => { + let newUserAttributes = {someCustomAttribute: "someCustomAttributeValue"}; + Model.jsonFormatter = () => { + return newUserAttributes; + }; + user.toJSON().should.eql(newUserAttributes); + }); + }); + }); }); diff --git a/es6/spec/validation/isPresent.spec.js b/es6/spec/validation/isPresent.spec.js new file mode 100644 index 0000000..f3dcfc3 --- /dev/null +++ b/es6/spec/validation/isPresent.spec.js @@ -0,0 +1,289 @@ +import isPresent from "../../lib/validation/isPresent.js"; +import Model from "../../lib/model.js"; +import Database from "almaden"; + +/* Test Configuration */ +//nothing from a real connection needed since we are mocking here +const databaseConfig = { + "debug": true, + "client": "mysql", + "connection": {}, + "pool": { + "max": 2, + "min": 2 + } +}; + +describe("isPresent(item, callback)", () => { + class Wheel extends Model {} + + class SteeringWheel extends Model {} + + class Street extends Model { + associate() { + this.hasMany("trucks", Truck); + } + } + + class Truck extends Model { + associate() { + this.belongsTo("street", Street); + this.hasOne("steeringWheel", SteeringWheel); + this.hasMany("wheels", Wheel); + } + + validate() { + this.ensure("steeringWheel", isPresent); + this.ensure("street", isPresent); + this.ensure("wheels", isPresent); + } + } + + let truck, + steeringWheel, + street, + trueValue, + falseValue; + + beforeEach(() => { + + Model.database = new Database(databaseConfig); + + truck = new Truck(); + street = new Street(); + steeringWheel = new SteeringWheel(); + + truck.id = 1; + + trueValue = { + result: true, + message: "must be present on Truck" + }; + + falseValue = { + result: false, + message: "must be present on Truck" + }; + }); + + it("should be a function", () => { + (typeof isPresent).should.equal("function"); + }); + + describe("(default error message)", () => { + it("should return a default error message with failure", () => { + + }); + }); + + describe("(association is present)", () => { + describe("(as a property)", () => { + beforeEach(() => { + truck.wheels.push(new Wheel()); + truck.wheels.push(new Wheel()); + truck.wheels.push(new Wheel()); + truck.wheels.push(new Wheel()); + truck.steeringWheel = steeringWheel; + truck.street = street; + }); + + describe("(hasOne)", () => { + it("should return true", done => { + isPresent.call(truck, "steeringWheel", (error, result) => { + result.should.eql(trueValue); + done(); + }); + }); + }); + + describe("(hasMany)", () => { + it("should return true if collections has elements", done => { + isPresent.call(truck, "wheels", (error, result) => { + result.should.eql(trueValue); + done(); + }); + }); + }); + + describe("(belongsTo)", () => { + it("should return true", done => { + isPresent.call(truck, "street", (error, result) => { + result.should.eql(trueValue); + done(); + }); + }); + }); + }); + + describe("(on the database due to lazy loading)", () => { + beforeEach(() => { + Model.database.mock({ + "select count(*) as `rowCount` from `wheels` where `truck_id` = 1": [ + {rowCount: 1} + ] + }); + truck = new Truck(); + truck.id = 1; + truck.steeringWheel = steeringWheel; + }); + + describe("(hasMany)", () => { + it("should return true if collection is zero length but there are records on the database", done => { + isPresent.call(truck, "wheels", (error, result) => { + result.should.eql(trueValue); + done(); + }); + }); + }); + }); + }); + + describe("(association not present)", () => { + describe("(when model is new)", () => { + beforeEach(() => { + delete truck.id; + }); + + describe("(when property is set)", () => { + describe("(belongsTo)", () => { + beforeEach(() => { + truck.streetId = 1; + truck.street = null; + }); + it("should return true", done => { + isPresent.call(truck, "street", (error, result) => { + result.should.eql(trueValue); + done(); + }); + }); + }); + + describe("(on the database due to lazy loading)", () => { + beforeEach(() => { + Model.database.mock({ + "select count(*) as `rowCount` from `wheels` where `truck_id` = 1": [ + {rowCount: 0} + ] + }); + truck = new Truck(); + truck.steeringWheel = steeringWheel; + }); + + describe("(hasMany)", () => { + it("should return false if collection is zero length AND there are no records on the database", done => { + isPresent.call(truck, "wheels", (error, result) => { + result.should.eql(falseValue); + done(); + }); + }); + }); + }); + }); + + describe("(when property not set)", () => { + describe("(hasOne)", () => { + it("should return false", done => { + truck.steeringWheel = null; + isPresent.call(truck, "steeringWheel", (error, result) => { + result.should.eql(falseValue); + done(); + }); + }); + }); + + describe("(belongsTo)", () => { + it("should return false when a belongsTo association is not present", done => { + truck.street = null; + isPresent.call(truck, "street", (error, result) => { + result.should.eql(falseValue); + done(); + }); + }); + }); + }); + + describe("(hasMany)", () => { + it("should return false when a hasMany association is not present", done => { + delete truck.id + isPresent.call(truck, "wheels", (error, result) => { + result.should.eql(falseValue); + done(); + }); + }); + }); + }); + + describe("(when model not new)", () => { + beforeEach(() => { + Model.database.mock({}); + }); + + describe("(hasOne)", () => { + describe("(association not present in database)", () => { + beforeEach(() => { + Model.database.mock({ + "select count(*) as `rowCount` from `steering_wheels` where `truck_id` = 1": [ + {rowCount: 0} + ] + }); + }); + + it("should return false", done => { + isPresent.call(truck, "steeringWheel", (error, result) => { + result.should.eql(falseValue); + done(); + }); + }); + }); + + describe("(association is present in database)", () => { + beforeEach(() => { + Model.database.mock({ + "select count(*) as `rowCount` from `steering_wheels` where `truck_id` = 1": [ + {rowCount: 1} + ] + }); + }); + + it("should return true", done => { + isPresent.call(truck, "steeringWheel", (error, result) => { + result.should.eql(trueValue); + done(); + }); + }); + }); + }); + + describe("(belongsTo)", () => { + describe("(association id is present)", () => { + beforeEach(() => { + truck.streetId = 1; + }); + it("should return true", done => { + isPresent.call(truck, "street", (error, result) => { + result.should.eql(trueValue); + done(); + }); + }); + }); + describe("(association id not present)", () => { + it("should return false", done => { + isPresent.call(truck, "street", (error, result) => { + result.should.eql(falseValue); + done(); + }); + }); + }); + }); + }); + }); + + it("should return true when a hasMany association is present", done => { + truck.id = undefined; + truck.wheels.push(new Wheel()); + isPresent.call(truck, "wheels", (error, result) => { + result.should.eql(trueValue); + done(); + }); + }); +}); diff --git a/gulpfile.babel.js b/gulpfile.babel.js index 12555ac..b2cb701 100644 --- a/gulpfile.babel.js +++ b/gulpfile.babel.js @@ -1,5 +1,7 @@ import "./tasks/build-spec.js"; +import "./tasks/build-spec-assets.js"; import "./tasks/build-lib.js"; +import "./tasks/build-lib-assets.js"; import "./tasks/build.js"; import "./tasks/test-local.js"; import "./tasks/test-browsers.js"; diff --git a/package.json b/package.json index 795203b..9011fd4 100644 --- a/package.json +++ b/package.json @@ -14,13 +14,27 @@ "dovima" ], "author": "Free All Media, LLC", - "contributors": [], + "contributors": [ + { + "name": "nicosommi", + "email": "nicosommi@gmail.com" + }, + { + "name": "dcrockwell", + "email": "dcautomates@gmail.com" + }, + { + "name": "paulgoggin", + "email": "pgoggin@gmail.com" + } + ], "license": "MIT", "bugs": { "url": "https://github.com/FreeAllMedia/dovima/issues" }, "homepage": "https://github.com/FreeAllMedia/dovima", "dependencies": { + "almaden": "^0.1.0", "blunder": "^0.1.0", "fleming": "^0.1.0", "flowsync": "^0.1.4", @@ -37,14 +51,15 @@ "karma": "^0.12.36", "karma-browserify": "^4.2.1", "karma-chai": "^0.1.0", - "karma-mocha": "^0.1.10", - "karma-detect-browsers": "^2.0.0", "karma-chrome-launcher": "^0.2.0", + "karma-detect-browsers": "^2.0.0", "karma-firefox-launcher": "^0.1.6", "karma-ie-launcher": "^0.2.0", + "karma-mocha": "^0.1.10", "karma-phantomjs-launcher": "^0.2.0", "karma-safari-launcher": "^0.1.1", "karma-sauce-launcher": "^0.2.11", - "mocha": "^2.2.5" + "mocha": "^2.2.5", + "sinon": "^1.15.4" } } diff --git a/paths.json b/paths.json index 1780085..ff6bfa3 100644 --- a/paths.json +++ b/paths.json @@ -1,7 +1,13 @@ { "source": { "lib": "./es6/lib/**/*.js", - "spec": "./es6/spec/**/*.spec.js" + "spec": "./es6/spec/**/*.spec.js", + "specAssets": [ + "./es6/spec/**/*.json" + ], + "libAssets": [ + "./es6/lib/**/*.json" + ] }, "build": { diff --git a/tasks/build-lib-assets.js b/tasks/build-lib-assets.js new file mode 100644 index 0000000..9df19aa --- /dev/null +++ b/tasks/build-lib-assets.js @@ -0,0 +1,9 @@ +import gulp from "gulp"; +//import babel from "gulp-babel"; + +import paths from "../paths.json"; + +gulp.task("build-lib-assets", () => { + return gulp.src(paths.source.specAssets) + .pipe(gulp.dest(paths.build.directories.spec)); +}); diff --git a/tasks/build-resources.js b/tasks/build-resources.js new file mode 100644 index 0000000..5eb99c1 --- /dev/null +++ b/tasks/build-resources.js @@ -0,0 +1,8 @@ +import gulp from "gulp"; + +import paths from "../paths.json"; + +gulp.task("build-resources", () => { + return gulp.src(paths.source.resources) + .pipe(gulp.dest(paths.build.directories.resources)); +}); diff --git a/tasks/build-spec-assets.js b/tasks/build-spec-assets.js new file mode 100644 index 0000000..2ececa8 --- /dev/null +++ b/tasks/build-spec-assets.js @@ -0,0 +1,9 @@ +import gulp from "gulp"; +//import babel from "gulp-babel"; + +import paths from "../paths.json"; + +gulp.task("build-spec-assets", () => { + return gulp.src(paths.source.specAssets) + .pipe(gulp.dest(paths.build.directories.spec)); +}); diff --git a/tasks/build.js b/tasks/build.js index f6afedf..e3b50be 100644 --- a/tasks/build.js +++ b/tasks/build.js @@ -1,3 +1,3 @@ import gulp from "gulp"; -gulp.task("build", ["build-lib", "build-spec"]); +gulp.task("build", ["build-lib", "build-spec", "build-lib-assets", "build-spec-assets"]); From 3b2e746d1e6c032099f257db18633a011f6f6ed3 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Tue, 30 Jun 2015 14:09:35 -0300 Subject: [PATCH 03/15] code fixes, source code sync and test passing --- es5/lib/collection.js | 27 - es5/lib/modelFinder.js | 39 +- es6/lib/collection.js | 29 +- es6/lib/modelFinder.js | 31 +- lcov.info | 1352 +++++++++++++++++++++++++++++++++++++++- tasks/test.js | 2 +- 6 files changed, 1391 insertions(+), 89 deletions(-) diff --git a/es5/lib/collection.js b/es5/lib/collection.js index 4de7956..4ff177e 100644 --- a/es5/lib/collection.js +++ b/es5/lib/collection.js @@ -70,9 +70,7 @@ var Collection = (function (_Array) { models.forEach(function (model) { if (_this.indexOf(model) === -1) { if (_this.association) { - //proceed with the regular push _get(Object.getPrototypeOf(Collection.prototype), "push", _this).call(_this, model); - //set inverse relationship var pluralForeignName = (0, _jargon2["default"])(_this.association.foreignName).plural.toString(); if (model.hasOwnProperty(_this.association.foreignName)) { if (model[_this.association.foreignName] !== _this.association.parent) { @@ -81,31 +79,6 @@ var Collection = (function (_Array) { } else if (model.hasOwnProperty(pluralForeignName)) { model[pluralForeignName].push(_this.association.parent); } - //if it has through, create the intermediate model - if (_this.association.through) { - //get through association - var throughAssociation = _this.association.parent.associations[_this.association.through]; - if (!throughAssociation) { - var modelName = undefined; - if (model && model.constructor) { - modelName = model.constructor.name; - } else { - modelName = model; - } - throw new Error("Through association called " + _this.association.through + " not defined on model " + modelName); - } - - //lookup if there is an existing through model... how? - var throughModel = new throughAssociation.constructor(); - throughModel[_this.association.foreignName] = _this.association.parent; - - // work in progress for future automations - // let throughAssociationPropertyName = model.associations[throughAssociationNameOnModel].foreignName; - // throughModel[this.association.foreignName] = this.association.parent; - // throughModel[throughAssociationPropertyName] = model; - // HERE I WILL NEED THE RELATIONSHIP BETWEEN MODEL and THROUGH MODEL... - // throughModel[this.association.parent.foreignName] = this.association.parent; - } } else { if (model instanceof _this._modelConstructor) { _get(Object.getPrototypeOf(Collection.prototype), "push", _this).call(_this, model); diff --git a/es5/lib/modelFinder.js b/es5/lib/modelFinder.js index d06d854..8e165f2 100644 --- a/es5/lib/modelFinder.js +++ b/es5/lib/modelFinder.js @@ -73,9 +73,35 @@ exports["default"] = ModelFinder; var ModelQuery = (function () { function ModelQuery(database) { + var _this = this; + _classCallCheck(this, ModelQuery); this._database = database; + Object.defineProperties(this, { + "_one": { + enumerable: false, + writable: true, + value: false + }, + "one": { + get: function get() { + _this._one = true; + return _this; + } + }, + "deleted": { + get: function get() { + _this._query.whereNotNull((0, _jargon2["default"])("deletedAt").snake.toString()); + return _this; + } + }, + "all": { + get: function get() { + return _this; + } + } + }); } _createClass(ModelQuery, [{ @@ -158,13 +184,18 @@ var ModelQuery = (function () { value: function limit() { var _query6; + this._one = false; (_query6 = this._query).limit.apply(_query6, arguments); return this; } }, { key: "results", value: function results(callback) { - var _this = this; + var _this2 = this; + + if (this._one) { + this.limit(1); + } this._query.results(function (error, rows) { if (!rows) { @@ -175,14 +206,14 @@ var ModelQuery = (function () { } } - if (_this.countResults) { + if (_this2.countResults) { callback(error, rows[0].rowCount); } else { (function () { - var models = new _collectionJs2["default"](_this.ModelConstructor); + var models = new _collectionJs2["default"](_this2.ModelConstructor); rows.forEach(function (row) { - models.push(new _this.ModelConstructor(row)); + models.push(new _this2.ModelConstructor(row)); }); callback(error, models); diff --git a/es6/lib/collection.js b/es6/lib/collection.js index 7193bb8..cb3bff2 100644 --- a/es6/lib/collection.js +++ b/es6/lib/collection.js @@ -31,42 +31,15 @@ export default class Collection extends Array { models.forEach(model => { if (this.indexOf(model) === -1) { if(this.association) { - //proceed with the regular push super.push(model); - //set inverse relationship const pluralForeignName = inflect(this.association.foreignName).plural.toString(); - if(model.hasOwnProperty(this.association.foreignName)) { + if (model.hasOwnProperty(this.association.foreignName)) { if (model[this.association.foreignName] !== this.association.parent) { model[this.association.foreignName] = this.association.parent; } } else if(model.hasOwnProperty(pluralForeignName)) { model[pluralForeignName].push(this.association.parent); } - //if it has through, create the intermediate model - if(this.association.through) { - //get through association - let throughAssociation = this.association.parent.associations[this.association.through]; - if(!throughAssociation) { - let modelName; - if(model && model.constructor) { - modelName = model.constructor.name; - } else { - modelName = model; - } - throw new Error(`Through association called ${this.association.through} not defined on model ${modelName}`); - } - - //lookup if there is an existing through model... how? - let throughModel = new throughAssociation.constructor(); - throughModel[this.association.foreignName] = this.association.parent; - - // work in progress for future automations - // let throughAssociationPropertyName = model.associations[throughAssociationNameOnModel].foreignName; - // throughModel[this.association.foreignName] = this.association.parent; - // throughModel[throughAssociationPropertyName] = model; - // HERE I WILL NEED THE RELATIONSHIP BETWEEN MODEL and THROUGH MODEL... - // throughModel[this.association.parent.foreignName] = this.association.parent; - } } else { if(model instanceof this._modelConstructor) { super.push(model); diff --git a/es6/lib/modelFinder.js b/es6/lib/modelFinder.js index 969d708..9bc5394 100644 --- a/es6/lib/modelFinder.js +++ b/es6/lib/modelFinder.js @@ -2,7 +2,7 @@ const validateDependencies = Symbol(); import inflect from "jargon"; export default class ModelFinder { - constructor(database) { + constructor(database) { Object.defineProperties(this, { "_database": { value: database, @@ -40,6 +40,30 @@ export default class ModelFinder { export class ModelQuery { constructor(database) { this._database = database; + Object.defineProperties(this, { + "_one": { + enumerable: false, + writable: true, + value: false + }, + "one": { + get: () => { + this._one = true; + return this; + } + }, + "deleted": { + get: () => { + this._query.whereNotNull(inflect("deletedAt").snake.toString()); + return this; + } + }, + "all": { + get: () => { + return this; + } + } + }); } find(ModelConstructor) { @@ -102,11 +126,16 @@ export class ModelQuery { } limit(...options) { + this._one = false; this._query.limit(...options); return this; } results(callback) { + if(this._one) { + this.limit(1); + } + this._query.results((error, rows) => { if(!rows) { if(error) { diff --git a/lcov.info b/lcov.info index 35b2af6..74e9863 100644 --- a/lcov.info +++ b/lcov.info @@ -1,44 +1,1340 @@ TN: -SF:/Users/dslieker/Documents/source-code/dovima/es5/lib/dovima.js +SF:/home/intag/workspace/fam/dovima/es5/lib/validation/isPresent.js +FN:8,_interopRequireDefault +FN:14,isPresent +FN:53,(anonymous_3) +FN:98,(anonymous_4) +FNF:4 +FNH:4 +FNDA:1,_interopRequireDefault +FNDA:24,isPresent +FNDA:7,(anonymous_3) +FNDA:2,(anonymous_4) +DA:3,1 +DA:6,1 +DA:8,1 +DA:10,1 +DA:12,1 +DA:14,1 +DA:15,24 +DA:16,24 +DA:18,24 +DA:19,0 +DA:23,24 +DA:24,24 +DA:26,24 +DA:28,24 +DA:33,24 +DA:35,24 +DA:36,16 +DA:39,2 +DA:40,2 +DA:41,2 +DA:43,14 +DA:44,14 +DA:45,4 +DA:46,4 +DA:48,10 +DA:50,3 +DA:51,3 +DA:53,7 +DA:54,7 +DA:55,0 +DA:56,0 +DA:58,7 +DA:60,7 +DA:61,1 +DA:62,1 +DA:64,6 +DA:65,6 +DA:71,14 +DA:73,0 +DA:76,8 +DA:77,4 +DA:78,4 +DA:81,2 +DA:84,2 +DA:85,1 +DA:87,1 +DA:89,2 +DA:91,0 +DA:93,4 +DA:95,4 +DA:98,2 +DA:99,2 +DA:100,0 +DA:101,0 +DA:103,2 +DA:105,2 +DA:106,1 +DA:107,1 +DA:109,1 +DA:110,1 +DA:114,2 +DA:117,2 +DA:118,1 +DA:119,1 +DA:121,1 +DA:122,1 +DA:124,2 +DA:126,0 +DA:132,1 +LF:69 +LH:61 +BRDA:8,1,0,1 +BRDA:8,1,1,0 +BRDA:8,2,0,1 +BRDA:8,2,1,1 +BRDA:18,3,0,0 +BRDA:18,3,1,24 +BRDA:35,4,0,16 +BRDA:35,4,1,8 +BRDA:36,5,0,1 +BRDA:36,5,1,2 +BRDA:36,5,2,14 +BRDA:36,5,3,0 +BRDA:44,6,0,4 +BRDA:44,6,1,10 +BRDA:48,7,0,3 +BRDA:48,7,1,7 +BRDA:54,8,0,0 +BRDA:54,8,1,7 +BRDA:60,9,0,1 +BRDA:60,9,1,6 +BRDA:76,10,0,4 +BRDA:76,10,1,4 +BRDA:78,11,0,2 +BRDA:78,11,1,2 +BRDA:78,11,2,2 +BRDA:78,11,3,0 +BRDA:84,12,0,1 +BRDA:84,12,1,1 +BRDA:95,13,0,2 +BRDA:95,13,1,2 +BRDA:95,13,2,2 +BRDA:95,13,3,0 +BRDA:99,14,0,0 +BRDA:99,14,1,2 +BRDA:105,15,0,1 +BRDA:105,15,1,1 +BRDA:117,16,0,1 +BRDA:117,16,1,1 +BRF:38 +BRH:31 +end_of_record +TN: +SF:/home/intag/workspace/fam/dovima/es5/lib/modelFinder.js FN:7,(anonymous_1) FN:7,defineProperties FN:7,(anonymous_3) -FN:9,_classCallCheck -FN:11,(anonymous_5) -FN:12,Dovima -FN:18,saySomething -FNF:7 -FNH:7 +FN:9,_interopRequireDefault +FN:11,_toConsumableArray +FN:13,_classCallCheck +FN:25,(anonymous_7) +FN:26,ModelFinder +FN:40,find +FN:51,count +FN:62,value +FN:74,(anonymous_12) +FN:75,ModelQuery +FN:88,get +FN:94,get +FN:100,get +FN:109,find +FN:120,count +FN:132,where +FN:139,(anonymous_20) +FN:152,andWhere +FN:160,orWhere +FN:168,groupBy +FN:176,orderBy +FN:184,limit +FN:193,results +FN:200,(anonymous_27) +FN:212,(anonymous_28) +FN:215,(anonymous_29) +FNF:29 +FNH:26 +FNDA:1,(anonymous_1) +FNDA:2,defineProperties +FNDA:2,(anonymous_3) +FNDA:2,_interopRequireDefault +FNDA:37,_toConsumableArray +FNDA:81,_classCallCheck +FNDA:1,(anonymous_7) +FNDA:43,ModelFinder +FNDA:18,find +FNDA:9,count +FNDA:27,value +FNDA:1,(anonymous_12) +FNDA:38,ModelQuery +FNDA:1,get +FNDA:1,get +FNDA:2,get +FNDA:29,find +FNDA:9,count +FNDA:37,where +FNDA:104,(anonymous_20) +FNDA:6,andWhere +FNDA:0,orWhere +FNDA:0,groupBy +FNDA:0,orderBy +FNDA:8,limit +FNDA:37,results +FNDA:37,(anonymous_27) +FNDA:28,(anonymous_28) +FNDA:85,(anonymous_29) +DA:3,1 +DA:7,12 +DA:9,2 +DA:11,104 +DA:13,81 +DA:15,1 +DA:17,1 +DA:19,1 +DA:21,1 +DA:23,1 +DA:25,1 +DA:26,1 +DA:27,43 +DA:29,43 +DA:38,1 +DA:41,18 +DA:43,18 +DA:45,18 +DA:47,18 +DA:52,9 +DA:54,9 +DA:56,9 +DA:58,9 +DA:63,27 +DA:64,0 +DA:69,1 +DA:72,1 +DA:74,1 +DA:75,1 +DA:76,38 +DA:78,38 +DA:80,38 +DA:81,38 +DA:89,1 +DA:90,1 +DA:95,1 +DA:96,1 +DA:101,2 +DA:107,1 +DA:110,29 +DA:112,29 +DA:114,29 +DA:116,29 +DA:121,9 +DA:122,9 +DA:124,9 +DA:126,9 +DA:128,9 +DA:133,37 +DA:135,37 +DA:136,104 +DA:139,37 +DA:140,104 +DA:141,37 +DA:143,67 +DA:147,37 +DA:148,37 +DA:153,6 +DA:155,6 +DA:156,6 +DA:161,0 +DA:163,0 +DA:164,0 +DA:169,0 +DA:171,0 +DA:172,0 +DA:177,0 +DA:179,0 +DA:180,0 +DA:185,8 +DA:187,8 +DA:188,8 +DA:189,8 +DA:194,37 +DA:196,37 +DA:197,1 +DA:200,37 +DA:201,37 +DA:202,0 +DA:203,0 +DA:205,0 +DA:209,37 +DA:210,9 +DA:212,28 +DA:213,28 +DA:215,28 +DA:216,85 +DA:219,28 +DA:226,1 +DA:229,1 +LF:90 +LH:77 +BRDA:7,1,0,12 +BRDA:7,1,1,12 +BRDA:7,2,0,12 +BRDA:7,2,1,0 +BRDA:7,3,0,2 +BRDA:7,3,1,0 +BRDA:7,4,0,0 +BRDA:7,4,1,2 +BRDA:9,5,0,1 +BRDA:9,5,1,1 +BRDA:9,6,0,2 +BRDA:9,6,1,2 +BRDA:11,7,0,37 +BRDA:11,7,1,0 +BRDA:13,8,0,0 +BRDA:13,8,1,81 +BRDA:63,9,0,0 +BRDA:63,9,1,27 +BRDA:140,10,0,37 +BRDA:140,10,1,67 +BRDA:140,11,0,104 +BRDA:140,11,1,67 +BRDA:196,12,0,1 +BRDA:196,12,1,36 +BRDA:201,13,0,0 +BRDA:201,13,1,37 +BRDA:202,14,0,0 +BRDA:202,14,1,0 +BRDA:209,15,0,9 +BRDA:209,15,1,28 +BRF:30 +BRH:21 +end_of_record +TN: +SF:/home/intag/workspace/fam/dovima/es5/lib/collection.js +FN:7,(anonymous_1) +FN:7,defineProperties +FN:7,(anonymous_3) +FN:9,get +FN:11,_interopRequireDefault +FN:13,_toConsumableArray +FN:15,_classCallCheck +FN:17,_inherits +FN:31,(anonymous_9) +FN:32,Collection +FN:63,push +FN:70,(anonymous_12) +FN:101,fetch +FN:105,(anonymous_14) +FN:106,processWhereCondition +FN:120,(anonymous_16) +FN:123,(anonymous_17) +FN:129,(anonymous_18) +FN:138,(anonymous_19) +FN:140,(anonymous_20) +FNF:20 +FNH:20 FNDA:1,(anonymous_1) FNDA:1,defineProperties FNDA:1,(anonymous_3) -FNDA:1,_classCallCheck -FNDA:1,(anonymous_5) -FNDA:1,Dovima -FNDA:1,saySomething +FNDA:2361,get +FNDA:3,_interopRequireDefault +FNDA:3,_toConsumableArray +FNDA:2208,_classCallCheck +FNDA:1,_inherits +FNDA:1,(anonymous_9) +FNDA:2208,Collection +FNDA:158,push +FNDA:158,(anonymous_12) +FNDA:4,fetch +FNDA:4,(anonymous_14) +FNDA:6,processWhereCondition +FNDA:2,(anonymous_16) +FNDA:2,(anonymous_17) +FNDA:1,(anonymous_18) +FNDA:4,(anonymous_19) +FNDA:13,(anonymous_20) DA:3,1 -DA:7,1 -DA:9,1 -DA:11,1 -DA:12,1 -DA:13,1 -DA:16,1 -DA:20,1 -DA:24,1 +DA:7,2 +DA:9,2361 +DA:11,3 +DA:13,6 +DA:15,2208 +DA:17,1 +DA:19,1 +DA:21,1 +DA:23,1 +DA:25,1 DA:27,1 -DA:28,1 -LF:11 -LH:11 -BRDA:7,1,0,1 -BRDA:7,1,1,1 -BRDA:7,2,0,1 +DA:29,1 +DA:31,1 +DA:32,1 +DA:33,2208 +DA:35,2208 +DA:37,2208 +DA:40,2208 +DA:41,29 +DA:43,2179 +DA:46,2208 +DA:59,1 +DA:61,1 +DA:64,158 +DA:66,158 +DA:67,158 +DA:70,158 +DA:71,158 +DA:72,153 +DA:73,63 +DA:74,63 +DA:75,63 +DA:76,39 +DA:77,31 +DA:79,24 +DA:80,8 +DA:83,90 +DA:84,90 +DA:86,0 +DA:87,0 +DA:88,0 +DA:90,0 +DA:93,0 +DA:102,4 +DA:104,4 +DA:105,4 +DA:106,4 +DA:107,6 +DA:108,3 +DA:109,3 +DA:111,3 +DA:115,4 +DA:117,4 +DA:119,4 +DA:120,2 +DA:121,2 +DA:122,2 +DA:123,2 +DA:124,2 +DA:126,2 +DA:128,2 +DA:129,1 +DA:130,1 +DA:131,1 +DA:138,4 +DA:139,4 +DA:140,4 +DA:141,13 +DA:143,4 +DA:147,0 +DA:152,1 +DA:155,1 +DA:156,1 +LF:74 +LH:68 +BRDA:7,1,0,2 +BRDA:7,1,1,2 +BRDA:7,2,0,2 BRDA:7,2,1,0 BRDA:7,3,0,1 BRDA:7,3,1,0 BRDA:7,4,0,0 BRDA:7,4,1,1 BRDA:9,5,0,0 -BRDA:9,5,1,1 -BRF:10 -BRH:6 +BRDA:9,5,1,2361 +BRDA:9,6,0,0 +BRDA:9,6,1,2361 +BRDA:9,7,0,0 +BRDA:9,7,1,0 +BRDA:9,8,0,2361 +BRDA:9,8,1,0 +BRDA:9,9,0,0 +BRDA:9,9,1,0 +BRDA:11,10,0,3 +BRDA:11,10,1,0 +BRDA:11,11,0,3 +BRDA:11,11,1,3 +BRDA:13,12,0,3 +BRDA:13,12,1,0 +BRDA:15,13,0,0 +BRDA:15,13,1,2208 +BRDA:17,14,0,0 +BRDA:17,14,1,1 +BRDA:17,15,0,1 +BRDA:17,15,1,0 +BRDA:17,16,0,1 +BRDA:17,16,1,1 +BRDA:17,17,0,1 +BRDA:17,17,1,0 +BRDA:40,18,0,29 +BRDA:40,18,1,2179 +BRDA:40,19,0,2208 +BRDA:40,19,1,2208 +BRDA:71,20,0,153 +BRDA:71,20,1,5 +BRDA:72,21,0,63 +BRDA:72,21,1,90 +BRDA:75,22,0,39 +BRDA:75,22,1,24 +BRDA:76,23,0,31 +BRDA:76,23,1,8 +BRDA:79,24,0,8 +BRDA:79,24,1,16 +BRDA:83,25,0,90 +BRDA:83,25,1,0 +BRDA:87,26,0,0 +BRDA:87,26,1,0 +BRDA:87,27,0,0 +BRDA:87,27,1,0 +BRDA:104,28,0,4 +BRDA:104,28,1,0 +BRDA:107,29,0,3 +BRDA:107,29,1,3 +BRDA:119,30,0,2 +BRDA:119,30,1,2 +BRDA:128,31,0,1 +BRDA:128,31,1,1 +BRF:62 +BRH:40 +end_of_record +TN: +SF:/home/intag/workspace/fam/dovima/es5/lib/model.js +FN:7,(anonymous_1) +FN:7,defineProperties +FN:7,(anonymous_3) +FN:9,_interopRequireDefault +FN:11,_toConsumableArray +FN:13,_classCallCheck +FN:57,(anonymous_7) +FN:63,Model +FN:68,(anonymous_9) +FN:112,get +FN:115,set +FN:126,get +FN:129,set +FN:141,get +FN:157,hasOne +FN:166,belongsTo +FN:175,hasMany +FN:184,ensure +FN:204,isValid +FN:205,(anonymous_20) +FN:225,invalidAttributes +FN:230,compileInvalidAttributeList +FN:249,performValidationsForAttribute +FN:252,performValidation +FN:255,(anonymous_25) +FN:264,compileValidatorResponses +FN:302,include +FN:312,fetch +FN:341,value +FN:351,(anonymous_30) +FN:367,(anonymous_31) +FN:374,(anonymous_32) +FN:382,(anonymous_33) +FN:392,(anonymous_34) +FN:408,(anonymous_35) +FN:418,(anonymous_36) +FN:425,(anonymous_37) +FN:428,processWhereCondition +FN:439,(anonymous_39) +FN:445,(anonymous_40) +FN:452,(anonymous_41) +FN:465,(anonymous_42) +FN:469,(anonymous_43) +FN:471,(anonymous_44) +FN:488,(anonymous_45) +FN:492,(anonymous_46) +FN:493,(anonymous_47) +FN:502,(anonymous_48) +FN:506,(anonymous_49) +FN:507,(anonymous_50) +FN:522,(anonymous_51) +FN:526,(anonymous_52) +FN:527,(anonymous_53) +FN:570,(anonymous_54) +FN:581,(anonymous_55) +FN:582,(anonymous_56) +FN:593,(anonymous_57) +FN:609,_delete +FN:618,(anonymous_59) +FN:619,(anonymous_60) +FN:622,(anonymous_61) +FN:626,(anonymous_62) +FN:635,(anonymous_63) +FN:647,save +FN:654,(anonymous_65) +FN:656,(anonymous_66) +FN:657,(anonymous_67) +FN:661,(anonymous_68) +FN:686,(anonymous_69) +FN:688,(anonymous_70) +FN:694,(anonymous_71) +FN:716,(anonymous_72) +FN:719,(anonymous_73) +FN:733,(anonymous_74) +FN:735,(anonymous_75) +FN:747,beforeValidation +FN:752,beforeSave +FN:757,afterSave +FN:762,associate +FN:765,validate +FN:768,initialize +FN:771,toJSON +FN:785,value +FN:790,value +FN:795,value +FN:800,value +FN:805,value +FN:809,(anonymous_88) +FN:818,value +FN:836,value +FN:841,(anonymous_91) +FN:867,(anonymous_92) +FN:880,(anonymous_93) +FN:901,value +FN:942,(anonymous_95) +FN:957,(anonymous_96) +FN:991,(anonymous_97) +FN:1009,get +FN:1022,value +FN:1029,value +FN:1034,(anonymous_101) +FN:1039,(anonymous_102) +FN:1071,(anonymous_103) +FN:1072,AssociationSetter +FN:1098,foreignName +FN:1104,where +FN:1114,andWhere +FN:1126,through +FN:1132,as +FN:1138,value +FN:1143,value +FN:1155,modelFind +FNF:112 +FNH:108 +FNDA:1,(anonymous_1) +FNDA:2,defineProperties +FNDA:2,(anonymous_3) +FNDA:5,_interopRequireDefault +FNDA:5,_toConsumableArray +FNDA:4778,_classCallCheck +FNDA:1,(anonymous_7) +FNDA:1190,Model +FNDA:2380,(anonymous_9) +FNDA:83,get +FNDA:1,set +FNDA:109,get +FNDA:1,set +FNDA:21,get +FNDA:617,hasOne +FNDA:792,belongsTo +FNDA:2179,hasMany +FNDA:615,ensure +FNDA:12,isValid +FNDA:12,(anonymous_20) +FNDA:18,invalidAttributes +FNDA:18,compileInvalidAttributeList +FNDA:10,performValidationsForAttribute +FNDA:10,performValidation +FNDA:10,(anonymous_25) +FNDA:10,compileValidatorResponses +FNDA:16,include +FNDA:31,fetch +FNDA:31,value +FNDA:32,(anonymous_30) +FNDA:25,(anonymous_31) +FNDA:15,(anonymous_32) +FNDA:16,(anonymous_33) +FNDA:5,(anonymous_34) +FNDA:1,(anonymous_35) +FNDA:1,(anonymous_36) +FNDA:4,(anonymous_37) +FNDA:10,processWhereCondition +FNDA:4,(anonymous_39) +FNDA:1,(anonymous_40) +FNDA:4,(anonymous_41) +FNDA:4,(anonymous_42) +FNDA:4,(anonymous_43) +FNDA:4,(anonymous_44) +FNDA:0,(anonymous_45) +FNDA:0,(anonymous_46) +FNDA:0,(anonymous_47) +FNDA:3,(anonymous_48) +FNDA:1,(anonymous_49) +FNDA:3,(anonymous_50) +FNDA:6,(anonymous_51) +FNDA:2,(anonymous_52) +FNDA:6,(anonymous_53) +FNDA:4,(anonymous_54) +FNDA:1,(anonymous_55) +FNDA:1,(anonymous_56) +FNDA:12,(anonymous_57) +FNDA:10,_delete +FNDA:7,(anonymous_59) +FNDA:4,(anonymous_60) +FNDA:7,(anonymous_61) +FNDA:7,(anonymous_62) +FNDA:7,(anonymous_63) +FNDA:11,save +FNDA:10,(anonymous_65) +FNDA:10,(anonymous_66) +FNDA:10,(anonymous_67) +FNDA:2,(anonymous_68) +FNDA:8,(anonymous_69) +FNDA:8,(anonymous_70) +FNDA:5,(anonymous_71) +FNDA:8,(anonymous_72) +FNDA:8,(anonymous_73) +FNDA:8,(anonymous_74) +FNDA:10,(anonymous_75) +FNDA:10,beforeValidation +FNDA:8,beforeSave +FNDA:8,afterSave +FNDA:305,associate +FNDA:608,validate +FNDA:1163,initialize +FNDA:2,toJSON +FNDA:1191,value +FNDA:98,value +FNDA:18,value +FNDA:2,value +FNDA:17,value +FNDA:137,(anonymous_88) +FNDA:28,value +FNDA:15,value +FNDA:18,(anonymous_91) +FNDA:3,(anonymous_92) +FNDA:15,(anonymous_93) +FNDA:3588,value +FNDA:648,(anonymous_95) +FNDA:860,(anonymous_96) +FNDA:2179,(anonymous_97) +FNDA:257,get +FNDA:1213,value +FNDA:8,value +FNDA:15,(anonymous_101) +FNDA:12,(anonymous_102) +FNDA:1,(anonymous_103) +FNDA:3588,AssociationSetter +FNDA:0,foreignName +FNDA:386,where +FNDA:3,andWhere +FNDA:1285,through +FNDA:258,as +FNDA:259,value +FNDA:6,value +FNDA:11,modelFind +DA:3,1 +DA:7,35 +DA:9,5 +DA:11,10 +DA:13,4778 +DA:18,1 +DA:20,1 +DA:22,1 +DA:24,1 +DA:26,1 +DA:28,1 +DA:30,1 +DA:32,1 +DA:34,1 +DA:36,1 +DA:38,1 +DA:41,1 +DA:57,1 +DA:63,1 +DA:64,1190 +DA:66,1190 +DA:68,1190 +DA:69,2380 +DA:78,1190 +DA:113,83 +DA:116,1 +DA:127,109 +DA:130,1 +DA:142,21 +DA:147,1190 +DA:148,1190 +DA:150,1190 +DA:152,1190 +DA:155,1 +DA:158,617 +DA:167,792 +DA:176,2179 +DA:185,615 +DA:187,615 +DA:189,615 +DA:190,3 +DA:193,615 +DA:205,12 +DA:206,12 +DA:226,18 +DA:228,18 +DA:230,18 +DA:231,18 +DA:232,0 +DA:234,18 +DA:236,18 +DA:237,10 +DA:239,10 +DA:240,8 +DA:241,8 +DA:245,18 +DA:249,18 +DA:250,10 +DA:252,10 +DA:253,10 +DA:255,10 +DA:256,10 +DA:257,2 +DA:259,8 +DA:264,10 +DA:265,10 +DA:267,10 +DA:268,10 +DA:269,10 +DA:271,10 +DA:272,10 +DA:273,10 +DA:275,10 +DA:278,0 +DA:279,0 +DA:281,10 +DA:282,10 +DA:283,0 +DA:286,10 +DA:287,0 +DA:292,10 +DA:295,10 +DA:298,18 +DA:303,16 +DA:304,18 +DA:307,16 +DA:308,16 +DA:313,31 +DA:314,32 +DA:317,31 +DA:319,5 +DA:320,0 +DA:322,20 +DA:323,16 +DA:324,4 +DA:325,1 +DA:327,3 +DA:329,16 +DA:331,6 +DA:332,3 +DA:334,3 +DA:336,6 +DA:342,31 +DA:344,31 +DA:346,31 +DA:347,3 +DA:350,28 +DA:351,28 +DA:352,32 +DA:353,3 +DA:356,29 +DA:357,26 +DA:359,3 +DA:363,25 +DA:364,1 +DA:367,25 +DA:368,25 +DA:369,3 +DA:371,22 +DA:373,22 +DA:374,15 +DA:375,15 +DA:377,15 +DA:380,15 +DA:382,15 +DA:384,16 +DA:386,16 +DA:387,1 +DA:390,15 +DA:392,5 +DA:396,5 +DA:398,5 +DA:399,1 +DA:404,1 +DA:405,0 +DA:408,1 +DA:409,1 +DA:410,1 +DA:417,1 +DA:418,1 +DA:419,1 +DA:420,1 +DA:421,1 +DA:425,4 +DA:426,4 +DA:428,4 +DA:429,10 +DA:430,5 +DA:431,5 +DA:433,5 +DA:437,4 +DA:439,4 +DA:440,4 +DA:442,4 +DA:444,4 +DA:445,1 +DA:446,1 +DA:447,1 +DA:452,4 +DA:453,4 +DA:454,4 +DA:455,4 +DA:460,5 +DA:464,8 +DA:465,4 +DA:467,4 +DA:469,4 +DA:470,4 +DA:471,4 +DA:472,4 +DA:474,4 +DA:475,1 +DA:478,3 +DA:480,3 +DA:482,3 +DA:484,3 +DA:488,0 +DA:489,0 +DA:492,0 +DA:493,0 +DA:494,0 +DA:496,0 +DA:499,0 +DA:502,1 +DA:503,3 +DA:506,1 +DA:507,1 +DA:508,3 +DA:510,1 +DA:512,1 +DA:522,2 +DA:523,6 +DA:526,2 +DA:527,2 +DA:528,6 +DA:530,2 +DA:533,2 +DA:570,4 +DA:571,4 +DA:574,8 +DA:577,2 +DA:578,1 +DA:581,1 +DA:582,1 +DA:583,1 +DA:584,1 +DA:585,1 +DA:586,1 +DA:593,13 +DA:594,12 +DA:595,12 +DA:600,7 +DA:601,7 +DA:610,10 +DA:612,10 +DA:613,9 +DA:614,1 +DA:617,8 +DA:618,7 +DA:619,7 +DA:620,4 +DA:623,7 +DA:624,7 +DA:625,7 +DA:626,7 +DA:627,7 +DA:628,0 +DA:629,7 +DA:630,1 +DA:632,6 +DA:636,7 +DA:639,1 +DA:642,1 +DA:648,11 +DA:650,11 +DA:651,1 +DA:654,10 +DA:655,10 +DA:657,10 +DA:658,10 +DA:659,8 +DA:661,2 +DA:662,2 +DA:664,2 +DA:665,2 +DA:666,2 +DA:667,2 +DA:669,2 +DA:671,2 +DA:672,2 +DA:673,2 +DA:674,2 +DA:675,2 +DA:677,2 +DA:679,2 +DA:681,0 +DA:687,8 +DA:689,8 +DA:690,5 +DA:691,5 +DA:692,5 +DA:694,5 +DA:695,5 +DA:696,0 +DA:698,5 +DA:699,5 +DA:703,3 +DA:704,3 +DA:705,3 +DA:706,3 +DA:708,3 +DA:709,6 +DA:710,3 +DA:714,3 +DA:719,8 +DA:720,8 +DA:722,2 +DA:724,2 +DA:725,2 +DA:727,0 +DA:730,4 +DA:734,8 +DA:736,10 +DA:737,2 +DA:739,8 +DA:748,10 +DA:753,8 +DA:758,8 +DA:772,2 +DA:773,1 +DA:775,1 +DA:786,1191 +DA:791,98 +DA:796,18 +DA:801,2 +DA:806,17 +DA:808,17 +DA:809,17 +DA:810,137 +DA:811,53 +DA:814,17 +DA:819,28 +DA:820,15 +DA:822,13 +DA:837,15 +DA:839,15 +DA:841,15 +DA:843,18 +DA:845,18 +DA:848,10 +DA:849,10 +DA:851,9 +DA:852,9 +DA:853,3 +DA:855,6 +DA:858,1 +DA:860,10 +DA:863,8 +DA:865,8 +DA:867,8 +DA:868,3 +DA:869,3 +DA:870,3 +DA:872,0 +DA:876,0 +DA:878,8 +DA:881,15 +DA:902,3588 +DA:904,3588 +DA:912,3588 +DA:915,2796 +DA:916,2796 +DA:917,2796 +DA:919,792 +DA:920,792 +DA:924,3588 +DA:928,3588 +DA:932,3588 +DA:936,3588 +DA:938,3588 +DA:940,617 +DA:942,617 +DA:943,648 +DA:944,26 +DA:945,1 +DA:948,25 +DA:950,25 +DA:953,617 +DA:955,792 +DA:957,792 +DA:958,860 +DA:959,62 +DA:960,1 +DA:963,61 +DA:964,61 +DA:966,61 +DA:968,61 +DA:969,18 +DA:970,7 +DA:971,11 +DA:973,10 +DA:975,1 +DA:986,792 +DA:989,2179 +DA:991,2179 +DA:992,2179 +DA:993,0 +DA:996,2179 +DA:998,0 +DA:1001,3588 +DA:1006,3588 +DA:1010,257 +DA:1014,3588 +DA:1016,3588 +DA:1018,3588 +DA:1023,1213 +DA:1024,713 +DA:1030,8 +DA:1032,8 +DA:1033,8 +DA:1034,8 +DA:1035,15 +DA:1039,8 +DA:1040,12 +DA:1041,12 +DA:1042,12 +DA:1044,4 +DA:1045,4 +DA:1049,0 +DA:1050,0 +DA:1054,8 +DA:1055,8 +DA:1059,8 +DA:1063,1 +DA:1066,1 +DA:1068,1 +DA:1071,1 +DA:1072,1 +DA:1073,3588 +DA:1075,3588 +DA:1077,3588 +DA:1079,792 +DA:1084,792 +DA:1087,2796 +DA:1092,2796 +DA:1096,1 +DA:1099,0 +DA:1100,0 +DA:1105,386 +DA:1106,961 +DA:1109,386 +DA:1110,386 +DA:1115,3 +DA:1117,3 +DA:1118,6 +DA:1121,3 +DA:1122,3 +DA:1127,1285 +DA:1128,1285 +DA:1133,258 +DA:1134,258 +DA:1139,259 +DA:1144,6 +DA:1148,1 +DA:1151,1 +DA:1153,1 +DA:1156,11 +DA:1157,11 +LF:428 +LH:402 +BRDA:7,1,0,35 +BRDA:7,1,1,35 +BRDA:7,2,0,35 +BRDA:7,2,1,0 +BRDA:7,3,0,2 +BRDA:7,3,1,0 +BRDA:7,4,0,0 +BRDA:7,4,1,2 +BRDA:9,5,0,3 +BRDA:9,5,1,2 +BRDA:9,6,0,5 +BRDA:9,6,1,5 +BRDA:11,7,0,5 +BRDA:11,7,1,0 +BRDA:13,8,0,0 +BRDA:13,8,1,4778 +BRDA:113,9,0,83 +BRDA:113,9,1,82 +BRDA:127,10,0,109 +BRDA:127,10,1,108 +BRDA:185,11,0,615 +BRDA:185,11,1,614 +BRDA:189,12,0,3 +BRDA:189,12,1,612 +BRDA:231,13,0,0 +BRDA:231,13,1,18 +BRDA:239,14,0,8 +BRDA:239,14,1,2 +BRDA:256,15,0,2 +BRDA:256,15,1,8 +BRDA:259,16,0,8 +BRDA:259,16,1,6 +BRDA:275,17,0,10 +BRDA:275,17,1,8 +BRDA:282,18,0,0 +BRDA:282,18,1,10 +BRDA:282,19,0,10 +BRDA:282,19,1,0 +BRDA:286,20,0,0 +BRDA:286,20,1,10 +BRDA:317,21,0,5 +BRDA:317,21,1,20 +BRDA:317,21,2,6 +BRDA:322,22,0,16 +BRDA:322,22,1,4 +BRDA:324,23,0,1 +BRDA:324,23,1,3 +BRDA:331,24,0,3 +BRDA:331,24,1,3 +BRDA:344,25,0,5 +BRDA:344,25,1,26 +BRDA:346,26,0,3 +BRDA:346,26,1,28 +BRDA:352,27,0,3 +BRDA:352,27,1,29 +BRDA:356,28,0,26 +BRDA:356,28,1,3 +BRDA:363,29,0,1 +BRDA:363,29,1,24 +BRDA:368,30,0,3 +BRDA:368,30,1,22 +BRDA:373,31,0,15 +BRDA:373,31,1,7 +BRDA:386,32,0,1 +BRDA:386,32,1,15 +BRDA:390,33,0,5 +BRDA:390,33,1,8 +BRDA:390,33,2,2 +BRDA:398,34,0,1 +BRDA:398,34,1,4 +BRDA:404,35,0,0 +BRDA:404,35,1,1 +BRDA:429,36,0,5 +BRDA:429,36,1,5 +BRDA:444,37,0,1 +BRDA:444,37,1,3 +BRDA:464,38,0,4 +BRDA:464,38,1,4 +BRDA:470,39,0,4 +BRDA:470,39,1,0 +BRDA:472,40,0,4 +BRDA:472,40,1,2 +BRDA:474,41,0,1 +BRDA:474,41,1,3 +BRDA:484,42,0,0 +BRDA:484,42,1,1 +BRDA:484,42,2,2 +BRDA:577,43,0,1 +BRDA:577,43,1,1 +BRDA:594,44,0,12 +BRDA:594,44,1,0 +BRDA:600,45,0,7 +BRDA:600,45,1,0 +BRDA:612,46,0,9 +BRDA:612,46,1,1 +BRDA:613,47,0,1 +BRDA:613,47,1,8 +BRDA:617,48,0,7 +BRDA:617,48,1,1 +BRDA:620,49,0,4 +BRDA:620,49,1,2 +BRDA:627,50,0,0 +BRDA:627,50,1,7 +BRDA:629,51,0,1 +BRDA:629,51,1,6 +BRDA:650,52,0,1 +BRDA:650,52,1,10 +BRDA:658,53,0,8 +BRDA:658,53,1,2 +BRDA:664,54,0,2 +BRDA:664,54,1,0 +BRDA:689,55,0,5 +BRDA:689,55,1,3 +BRDA:695,56,0,0 +BRDA:695,56,1,5 +BRDA:709,57,0,3 +BRDA:709,57,1,3 +BRDA:720,58,0,2 +BRDA:720,58,1,2 +BRDA:720,58,2,4 +BRDA:724,59,0,2 +BRDA:724,59,1,0 +BRDA:736,60,0,2 +BRDA:736,60,1,8 +BRDA:772,61,0,1 +BRDA:772,61,1,1 +BRDA:772,62,0,2 +BRDA:772,62,1,1 +BRDA:810,63,0,53 +BRDA:810,63,1,84 +BRDA:819,64,0,15 +BRDA:819,64,1,13 +BRDA:845,65,0,7 +BRDA:845,65,1,10 +BRDA:845,65,2,8 +BRDA:849,66,0,9 +BRDA:849,66,1,1 +BRDA:852,67,0,3 +BRDA:852,67,1,6 +BRDA:865,68,0,8 +BRDA:865,68,1,0 +BRDA:869,69,0,3 +BRDA:869,69,1,0 +BRDA:912,70,0,617 +BRDA:912,70,1,2796 +BRDA:912,70,2,792 +BRDA:938,71,0,617 +BRDA:938,71,1,792 +BRDA:938,71,2,2179 +BRDA:938,71,3,0 +BRDA:943,72,0,26 +BRDA:943,72,1,622 +BRDA:943,73,0,648 +BRDA:943,73,1,30 +BRDA:944,74,0,1 +BRDA:944,74,1,25 +BRDA:958,75,0,62 +BRDA:958,75,1,798 +BRDA:958,76,0,860 +BRDA:958,76,1,66 +BRDA:959,77,0,1 +BRDA:959,77,1,61 +BRDA:968,78,0,18 +BRDA:968,78,1,43 +BRDA:969,79,0,7 +BRDA:969,79,1,11 +BRDA:971,80,0,10 +BRDA:971,80,1,1 +BRDA:992,81,0,0 +BRDA:992,81,1,2179 +BRDA:992,82,0,2179 +BRDA:992,82,1,0 +BRDA:1042,83,0,4 +BRDA:1042,83,1,8 +BRDA:1044,84,0,4 +BRDA:1044,84,1,0 +BRDA:1044,85,0,4 +BRDA:1044,85,1,4 +BRDA:1077,86,0,792 +BRDA:1077,86,1,617 +BRDA:1077,86,2,2796 +BRDA:1115,87,0,3 +BRDA:1115,87,1,3 +BRF:183 +BRH:159 end_of_record diff --git a/tasks/test.js b/tasks/test.js index 74c63dc..1bdc78c 100644 --- a/tasks/test.js +++ b/tasks/test.js @@ -1,3 +1,3 @@ import gulp from "gulp"; -gulp.task("test", ["test-local", "test-browsers"]); +gulp.task("test", ["test-local"]); From 11fece54820c57f24465509d2df2cb3609add55f Mon Sep 17 00:00:00 2001 From: nicosommi Date: Tue, 30 Jun 2015 14:24:55 -0300 Subject: [PATCH 04/15] add flexible year for travis --- es5/spec/model.spec.js | 30 +++++++++++++++--------------- es6/spec/model.spec.js | 30 +++++++++++++++--------------- 2 files changed, 30 insertions(+), 30 deletions(-) diff --git a/es5/spec/model.spec.js b/es5/spec/model.spec.js index 8d27abc..136c1b8 100644 --- a/es5/spec/model.spec.js +++ b/es5/spec/model.spec.js @@ -1314,9 +1314,9 @@ describe("Model(attributes, options)", function () { var userDeleteQuerySpy = undefined; beforeEach(function () { - _libModelJs2["default"].database.mock(_defineProperty({}, /update `accounts` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, 1)); + _libModelJs2["default"].database.mock(_defineProperty({}, /update `accounts` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, 1)); - userDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `forum_users` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, 1); + userDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `forum_users` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, 1); account.forumUser = forumUser; }); @@ -1333,9 +1333,9 @@ describe("Model(attributes, options)", function () { var postDeleteQuerySpy = undefined; beforeEach(function () { - _libModelJs2["default"].database.mock(_defineProperty({}, /update `forum_users` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, 1)); + _libModelJs2["default"].database.mock(_defineProperty({}, /update `forum_users` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, 1)); - postDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `posts` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 3/, 1); + postDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `posts` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 3/, 1); forumUser.posts.push(post); }); @@ -1405,7 +1405,7 @@ describe("Model(attributes, options)", function () { describe("(when primaryKey is set)", function () { beforeEach(function () { post.id = 1; - _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'1969-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/, 1)); + _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/, 1)); }); it("should not throw when calling delete", function () { @@ -1423,7 +1423,7 @@ describe("Model(attributes, options)", function () { describe("(when primary key is set but not exists)", function () { beforeEach(function () { post.id = 1; - _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'1969-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/, 0)); + _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/, 0)); }); it("should return an error", function () { @@ -1732,13 +1732,13 @@ describe("Model(attributes, options)", function () { user.photos.push(photo); var regularExpressions = { - insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('1969-12-31 [0-9][0-9]:00:00.000', 1\)/, - updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, - insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('1969-12-31 [0-9][0-9]:00:00\.000', 1\)/, - insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('1969-12-31 [0-9][0-9]:00:00.000', 1\)/, - insertTruck: /insert into `trucks` (`created_at`) values ('1969-12-31 [0-24]:00:00.000')/, - updateTruck: /update `trucks` set `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, - updateWheels: /update `wheels` set `created_at` = '1969-12-31 [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', 1\)/, + updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, + insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00\.000', 1\)/, + insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', 1\)/, + insertTruck: /insert into `trucks` (`created_at`) values ('19[0-9][0-9]-12-31 [0-24]:00:00.000')/, + updateTruck: /update `trucks` set `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, + updateWheels: /update `wheels` set `created_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ }; _libModelJs2["default"].database.mock((_Model$database$mock5 = {}, _defineProperty(_Model$database$mock5, regularExpressions.insertPhotos, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateUser, []), _defineProperty(_Model$database$mock5, regularExpressions.insertWheels, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertSteeringWheel, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateWheels, [1]), _Model$database$mock5)); @@ -1980,7 +1980,7 @@ describe("Model(attributes, options)", function () { _libModelJs2["default"].database.update = sinon.spy(_libModelJs2["default"].database.update); var regularExpressions = { - updateModel: /update `models` set `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + updateModel: /update `models` set `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ }; _libModelJs2["default"].database.mock(_defineProperty({}, regularExpressions.updateModel, [{}])); @@ -1997,7 +1997,7 @@ describe("Model(attributes, options)", function () { _libModelJs2["default"].database.insert = sinon.spy(_libModelJs2["default"].database.insert); var regularExpressions = { - insertModel: /insert into `models` \(`created_at`\) values \('1969-12-31 [0-9][0-9]:00:00.000'\)/ + insertModel: /insert into `models` \(`created_at`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000'\)/ }; _libModelJs2["default"].database.mock(_defineProperty({}, regularExpressions.insertModel, [{}])); diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index 68c9a49..6b9fa45 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -1218,12 +1218,12 @@ describe("Model(attributes, options)", () => { beforeEach(() => { Model.database.mock({ - [/update `accounts` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/]: + [/update `accounts` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/]: 1 }); userDeleteQuerySpy = Model.database.spy( - /update `forum_users` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, + /update `forum_users` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, 1); account.forumUser = forumUser; @@ -1242,12 +1242,12 @@ describe("Model(attributes, options)", () => { beforeEach(() => { Model.database.mock({ - [/update `forum_users` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 2/]: + [/update `forum_users` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 2/]: 1 }); postDeleteQuerySpy = Model.database.spy( - /update `posts` set `deleted_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 3/, + /update `posts` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 3/, 1); forumUser.posts.push(post); @@ -1295,7 +1295,7 @@ describe("Model(attributes, options)", () => { beforeEach(() => { post.id = 1; Model.database.mock({ - [/update `posts` set `deleted_at` = \'1969-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/]: + [/update `posts` set `deleted_at` = \'19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/]: 1 }); }); @@ -1316,7 +1316,7 @@ describe("Model(attributes, options)", () => { beforeEach(() => { post.id = 1; Model.database.mock({ - [/update `posts` set `deleted_at` = \'1969-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/]: + [/update `posts` set `deleted_at` = \'19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/]: 0 }); }); @@ -1612,13 +1612,13 @@ describe("Model(attributes, options)", () => { user.photos.push(photo); let regularExpressions = { - insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('1969-12-31 [0-9][0-9]:00:00.000', 1\)/, - updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, - insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('1969-12-31 [0-9][0-9]:00:00\.000', 1\)/, - insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('1969-12-31 [0-9][0-9]:00:00.000', 1\)/, - insertTruck: /insert into `trucks` (`created_at`) values ('1969-12-31 [0-24]:00:00.000')/, - updateTruck: /update `trucks` set `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, - updateWheels: /update `wheels` set `created_at` = '1969-12-31 [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', 1\)/, + updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, + insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00\.000', 1\)/, + insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', 1\)/, + insertTruck: /insert into `trucks` (`created_at`) values ('19[0-9][0-9]-12-31 [0-24]:00:00.000')/, + updateTruck: /update `trucks` set `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, + updateWheels: /update `wheels` set `created_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ }; Model.database.mock({ @@ -1795,7 +1795,7 @@ describe("Model(attributes, options)", () => { Model.database.update = sinon.spy(Model.database.update); let regularExpressions = { - updateModel: /update `models` set `updated_at` = '1969-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + updateModel: /update `models` set `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ }; Model.database.mock({ @@ -1815,7 +1815,7 @@ describe("Model(attributes, options)", () => { Model.database.insert = sinon.spy(Model.database.insert); let regularExpressions = { - insertModel: /insert into `models` \(`created_at`\) values \('1969-12-31 [0-9][0-9]:00:00.000'\)/ + insertModel: /insert into `models` \(`created_at`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000'\)/ }; Model.database.mock({ From 340ca688c95ff4c981234aafc20acaf3a603e832 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Tue, 30 Jun 2015 14:35:40 -0300 Subject: [PATCH 05/15] new fixes left for travis time --- es6/spec/model.spec.js | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index 6b9fa45..11d79a0 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -1218,12 +1218,12 @@ describe("Model(attributes, options)", () => { beforeEach(() => { Model.database.mock({ - [/update `accounts` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/]: + [/update `accounts` set `deleted_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/]: 1 }); userDeleteQuerySpy = Model.database.spy( - /update `forum_users` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, + /update `forum_users` set `deleted_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 2/, 1); account.forumUser = forumUser; @@ -1242,12 +1242,12 @@ describe("Model(attributes, options)", () => { beforeEach(() => { Model.database.mock({ - [/update `forum_users` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 2/]: + [/update `forum_users` set `deleted_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 2/]: 1 }); postDeleteQuerySpy = Model.database.spy( - /update `posts` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 3/, + /update `posts` set `deleted_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 3/, 1); forumUser.posts.push(post); @@ -1295,7 +1295,7 @@ describe("Model(attributes, options)", () => { beforeEach(() => { post.id = 1; Model.database.mock({ - [/update `posts` set `deleted_at` = \'19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/]: + [/update `posts` set `deleted_at` = \'19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000\' where `id` = 1/]: 1 }); }); @@ -1316,7 +1316,7 @@ describe("Model(attributes, options)", () => { beforeEach(() => { post.id = 1; Model.database.mock({ - [/update `posts` set `deleted_at` = \'19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/]: + [/update `posts` set `deleted_at` = \'19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000\' where `id` = 1/]: 0 }); }); @@ -1612,13 +1612,13 @@ describe("Model(attributes, options)", () => { user.photos.push(photo); let regularExpressions = { - insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', 1\)/, - updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, - insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00\.000', 1\)/, - insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', 1\)/, - insertTruck: /insert into `trucks` (`created_at`) values ('19[0-9][0-9]-12-31 [0-24]:00:00.000')/, - updateTruck: /update `trucks` set `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, - updateWheels: /update `wheels` set `created_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', 1\)/, + updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, + insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00\.000', 1\)/, + insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', 1\)/, + insertTruck: /insert into `trucks` (`created_at`) values ('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-24]:00:00.000')/, + updateTruck: /update `trucks` set `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, + updateWheels: /update `wheels` set `created_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/ }; Model.database.mock({ @@ -1795,7 +1795,7 @@ describe("Model(attributes, options)", () => { Model.database.update = sinon.spy(Model.database.update); let regularExpressions = { - updateModel: /update `models` set `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + updateModel: /update `models` set `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/ }; Model.database.mock({ @@ -1815,7 +1815,7 @@ describe("Model(attributes, options)", () => { Model.database.insert = sinon.spy(Model.database.insert); let regularExpressions = { - insertModel: /insert into `models` \(`created_at`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000'\)/ + insertModel: /insert into `models` \(`created_at`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000'\)/ }; Model.database.mock({ From ac3bd4fde12d9d84e099d2c3ce16c85a8aba2807 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Tue, 30 Jun 2015 16:43:53 -0300 Subject: [PATCH 06/15] changes to readme and added isPresent to exports --- README.md | 150 ++++++- es5/spec/model.spec.js | 60 +-- es6/spec/model.spec.js | 10 +- index.js | 3 +- lcov.info | 972 ++++++++++++++++++++--------------------- 5 files changed, 668 insertions(+), 527 deletions(-) diff --git a/README.md b/README.md index 86fc81f..fab491e 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,75 @@ # Dovima.js [![npm version](https://img.shields.io/npm/v/dovima.svg)](https://www.npmjs.com/package/dovima) [![license type](https://img.shields.io/npm/l/dovima.svg)](https://github.com/FreeAllMedia/dovima.git/blob/master/LICENSE) [![npm downloads](https://img.shields.io/npm/dm/dovima.svg)](https://www.npmjs.com/package/dovima) ![ECMAScript 6 & 5](https://img.shields.io/badge/ECMAScript-6%20/%205-red.svg) -ES6 generic model with lots of useful special features. +ES6 generic model with lots of useful special features like relations, validations, logical deletion, finding, typed collections, chained readable usage. It uses [almaden](https://github.com/FreeAllMedia/almaden) as the database adapter. ```javascript -import Dovima from "dovima"; - -const dovima = new Dovima; -dovima.saySomething(); // will output "Something" +//Declaring models with relationships +//On runtime you need to set the static Model.database to a valid [almaden](https://github.com/FreeAllMedia/almaden) object +import Model from "dovima"; +import {isPresent} from "dovima"; //dovima-provided validation +import {isNotEmpty} from "proven"; //validation utility framework + + +class TruckOwner extends Model { + associate() { + this.belongsTo("truck", Truck); + this.belongsTo("owner", Owner); + } +} + +class Truck extends Model { + initialize() { + this.softDelete; + } + + associate() { + this.hasMany("truckOwners"); + this.hasMany("owners", Owner) + .through("truckOwners"); + this.hasMany("wheels", Wheel); + this.hasOne("steeringWheel", SteeringWheel); + } + + validate() { + this.ensure("brand", isNotEmpty); + this.ensure("wheels", isPresent); + this.ensure("steeringWheel", isPresent); + } +} + +class Owner extends Model { + associate() { + this.hasMany("truckOwners", TruckOwner); + this.hasMany("trucks", Truck) + .through("truckOwners"); + } +} + +class Wheel extends Model { + associate() { + this.belongsTo("truck", Truck); + } + save(callback) { + wheelSaveSpy(callback); + super.save(callback); + } +} + +class SteeringWheel extends Model { + associate() { + this.belongsTo("truck", Truck); + } + save(callback) { + steeringWheelSaveSpy(callback); + super.save(callback); + } +} + +class Seat extends Model { + associate() { + this.belongsTo("truck", Truck); + } +} ``` # Quality and Compatibility @@ -19,7 +82,7 @@ dovima.saySomething(); // will output "Something" ![iojs 2.x.x](https://img.shields.io/badge/iojs-2.x.x-brightgreen.svg) ![iojs 1.x.x](https://img.shields.io/badge/iojs-1.x.x-brightgreen.svg) -[![Sauce Test Status](https://saucelabs.com/browser-matrix/dovima.svg)](https://saucelabs.com/u/dovima) + *If your platform is not listed above, you can test your local environment for compatibility by copying and pasting the following commands into your terminal:* @@ -42,24 +105,91 @@ npm install dovima --save ``` // ES6 -import dovima from "dovima"; +import Model from "dovima"; ``` ``` // ES5 -var dovima = require("dovima"); +var Model = require("dovima"); ``` ``` // Require.js define(["require"] , function (require) { - var dovima = require("dovima"); + var Model = require("dovima"); }); ``` # Getting Started +Dovima provides a Model class which you should extend with your own models like the Truck example. It is similar to the Active Record pattern. +Besides of extending the base Model you will need to set the Model.database static property to a valid [almaden](https://github.com/FreeAllMedia/almaden) object. Almaden is a DB-agnostic adapter with query chaining support. + +Important note: Dovima follows a strict casing rule. Object properties are always camelCased and database fields are snake_cased. + +## Features +Dovima lets you (use the truck example at the top of this README as a reference to understand feature explanations): + +### Relate models +You can relate models with the `hasOne`, `hasMany` and `belongsTo` methods provided by the Model base class by writing the `associate` method. + +### Add validations +Also Dovima let's you add validation to the models property by writing a simple `validate` method. You can call the `ensure(propertyName, validator)` method and that will receive the property name on the model to execute the validator. For validations there are some provided within Dovima (isPresent) and some provided by the [proven](https://github.com/FreeAllMedia/proven) package. Validations can be sync or async and new ones can be created anytime by using the same interface. + +### Soft delete +When you write your Model class you can mark it as a softDelete able Model, case in which it will add the logical deletion behavior, so then when you delete it, it will be an update and it will be excluded from regular model queries except if you find explicitly the deleted ones. See below on find for that example. -You should create on some directory a config +### Find models +Finding will return the error and the result collection using the node callback convention (error, data). + +Find a truck with id = 3. +```javascript +Truck + .find + .one + .where("id", "3") + .results((error, trucks) => { + //do something with the first truck on the collection (in this case will be just one for sure) + }); +``` + +Find all trucks. +```javascript +Truck + .find + .all + .where("brand", "like", "Mer%") + .results((error, trucks) => { + //do something with all the Mer% trucks + }); +``` + +Find deleted trucks. +```javascript +Truck + .find + .deleted + .where("brand", "like", "Mer%") + .results((error, trucks) => { + //do something with all the Mer% trucks that where logically deleted (see softDelete model feature) + }); +``` + +### Save/update models +You can save models with the primary key (id by default) and the appropiate timestamps (createdAt and updatedAt) managed automatically by dovima. +```javascript +truck.save((error) => { + //the truck variable now it has an id if it's new and a createdAt property + //or just an updatedAt property refreshed if it was an existing one + }); +``` + +### Delete models +```javascript +truck.delete((error) => { + //as the truck model was marked with soft delete, the truck will have a new deletedAt property + //otherwise, it does nothing (yet) + }); +``` # How to Contribute diff --git a/es5/spec/model.spec.js b/es5/spec/model.spec.js index 136c1b8..738cc2d 100644 --- a/es5/spec/model.spec.js +++ b/es5/spec/model.spec.js @@ -24,10 +24,6 @@ var _almaden = require("almaden"); var _almaden2 = _interopRequireDefault(_almaden); -var _libValidationIsPresentJs = require("../lib/validation/isPresent.js"); - -var _libValidationIsPresentJs2 = _interopRequireDefault(_libValidationIsPresentJs); - var _libCollectionJs = require("../lib/collection.js"); var _libCollectionJs2 = _interopRequireDefault(_libCollectionJs); @@ -38,6 +34,8 @@ var _libModelJs2 = _interopRequireDefault(_libModelJs); var _libModelFinderJs = require("../lib/modelFinder.js"); +var _ = require("../../"); + var sinon = require("sinon"); /* Test Configuration */ @@ -95,7 +93,7 @@ describe("Model(attributes, options)", function () { }, { key: "validate", value: function validate() { - this.ensure("photos", _libValidationIsPresentJs2["default"]); + this.ensure("photos", _.isPresent); } }]); @@ -120,7 +118,7 @@ describe("Model(attributes, options)", function () { }, { key: "validate", value: function validate() { - this.ensure("photos", _libValidationIsPresentJs2["default"]); + this.ensure("photos", _.isPresent); } }]); @@ -160,8 +158,8 @@ describe("Model(attributes, options)", function () { }, { key: "validate", value: function validate() { - this.ensure("user", _libValidationIsPresentJs2["default"]); - this.ensure("photo", _libValidationIsPresentJs2["default"]); + this.ensure("user", _.isPresent); + this.ensure("photo", _.isPresent); } }]); @@ -193,7 +191,7 @@ describe("Model(attributes, options)", function () { }, { key: "validate", value: function validate() { - this.ensure("user", _libValidationIsPresentJs2["default"]); + this.ensure("user", _.isPresent); } }]); @@ -280,6 +278,12 @@ describe("Model(attributes, options)", function () { return clock.restore(); }); + describe("(module properties)", function () { + it("should provide the isPresent validation", function () { + (typeof _.isPresent).should.equal("function"); + }); + }); + /** * Begin Testing */ @@ -769,7 +773,7 @@ describe("Model(attributes, options)", function () { it("should accept a custom error message", function () { truck.hasOne("steeringWheel", SteeringWheel); - truck.ensure("steeringWheel", _libValidationIsPresentJs2["default"], "must be there."); + truck.ensure("steeringWheel", _.isPresent, "must be there."); truck.invalidAttributes(function (invalidAttributeList) { invalidAttributeList.should.eql({ "steeringWheel": ["must be there."] @@ -856,7 +860,7 @@ describe("Model(attributes, options)", function () { it("should accept a custom error message", function () { truck.hasMany("wheels", Wheel); - truck.ensure("wheels", _libValidationIsPresentJs2["default"], "must be there."); + truck.ensure("wheels", _.isPresent, "must be there."); truck.invalidAttributes(function (invalidAttributeList) { invalidAttributeList.should.eql({ "wheels": ["must be there."] @@ -1006,7 +1010,7 @@ describe("Model(attributes, options)", function () { it("should return an object representing all validations on the model", function () { user.validations.should.eql({ "photos": [{ - validator: _libValidationIsPresentJs2["default"] + validator: _.isPresent }] }); }); @@ -1026,7 +1030,7 @@ describe("Model(attributes, options)", function () { user.ensure("photos", validatorFunction, validatorMessage); user.validations.should.eql({ "photos": [{ - validator: _libValidationIsPresentJs2["default"] + validator: _.isPresent }, { validator: validatorFunction, message: validatorMessage @@ -1314,9 +1318,9 @@ describe("Model(attributes, options)", function () { var userDeleteQuerySpy = undefined; beforeEach(function () { - _libModelJs2["default"].database.mock(_defineProperty({}, /update `accounts` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, 1)); + _libModelJs2["default"].database.mock(_defineProperty({}, /update `accounts` set `deleted_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, 1)); - userDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `forum_users` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, 1); + userDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `forum_users` set `deleted_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 2/, 1); account.forumUser = forumUser; }); @@ -1333,9 +1337,9 @@ describe("Model(attributes, options)", function () { var postDeleteQuerySpy = undefined; beforeEach(function () { - _libModelJs2["default"].database.mock(_defineProperty({}, /update `forum_users` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 2/, 1)); + _libModelJs2["default"].database.mock(_defineProperty({}, /update `forum_users` set `deleted_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 2/, 1)); - postDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `posts` set `deleted_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 3/, 1); + postDeleteQuerySpy = _libModelJs2["default"].database.spy(/update `posts` set `deleted_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 3/, 1); forumUser.posts.push(post); }); @@ -1405,7 +1409,7 @@ describe("Model(attributes, options)", function () { describe("(when primaryKey is set)", function () { beforeEach(function () { post.id = 1; - _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/, 1)); + _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000\' where `id` = 1/, 1)); }); it("should not throw when calling delete", function () { @@ -1423,7 +1427,7 @@ describe("Model(attributes, options)", function () { describe("(when primary key is set but not exists)", function () { beforeEach(function () { post.id = 1; - _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000\' where `id` = 1/, 0)); + _libModelJs2["default"].database.mock(_defineProperty({}, /update `posts` set `deleted_at` = \'19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000\' where `id` = 1/, 0)); }); it("should return an error", function () { @@ -1732,13 +1736,13 @@ describe("Model(attributes, options)", function () { user.photos.push(photo); var regularExpressions = { - insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', 1\)/, - updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, - insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00\.000', 1\)/, - insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', 1\)/, - insertTruck: /insert into `trucks` (`created_at`) values ('19[0-9][0-9]-12-31 [0-24]:00:00.000')/, - updateTruck: /update `trucks` set `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/, - updateWheels: /update `wheels` set `created_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + insertPhotos: /insert into `photos` \(`created_at`, `user_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', 1\)/, + updateUser: /update `users` set `age` = 35, `has_children` = false, `name` = 'Bob Builder', `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, + insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00\.000', 1\)/, + insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', 1\)/, + insertTruck: /insert into `trucks` (`created_at`) values ('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-24]:00:00.000')/, + updateTruck: /update `trucks` set `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, + updateWheels: /update `wheels` set `created_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/ }; _libModelJs2["default"].database.mock((_Model$database$mock5 = {}, _defineProperty(_Model$database$mock5, regularExpressions.insertPhotos, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateUser, []), _defineProperty(_Model$database$mock5, regularExpressions.insertWheels, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertSteeringWheel, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateWheels, [1]), _Model$database$mock5)); @@ -1980,7 +1984,7 @@ describe("Model(attributes, options)", function () { _libModelJs2["default"].database.update = sinon.spy(_libModelJs2["default"].database.update); var regularExpressions = { - updateModel: /update `models` set `updated_at` = '19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000' where `id` = 1/ + updateModel: /update `models` set `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/ }; _libModelJs2["default"].database.mock(_defineProperty({}, regularExpressions.updateModel, [{}])); @@ -1997,7 +2001,7 @@ describe("Model(attributes, options)", function () { _libModelJs2["default"].database.insert = sinon.spy(_libModelJs2["default"].database.insert); var regularExpressions = { - insertModel: /insert into `models` \(`created_at`\) values \('19[0-9][0-9]-12-31 [0-9][0-9]:00:00.000'\)/ + insertModel: /insert into `models` \(`created_at`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000'\)/ }; _libModelJs2["default"].database.mock(_defineProperty({}, regularExpressions.insertModel, [{}])); diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index 11d79a0..087d3ae 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -5,10 +5,10 @@ const sinon = require("sinon"); //import MultiError from "../../multiError/multiError.js"; import Database from "almaden"; -import isPresent from "../lib/validation/isPresent.js"; -import Collection from "../lib/collection.js"; +import {Collection} from "../../"; import Model, {AssociationSetter} from "../lib/model.js"; import {ModelQuery} from "../lib/modelFinder.js"; +import {isPresent} from "../../"; /* Test Configuration */ //nothing from a real connection needed since we are mocking here @@ -160,6 +160,12 @@ describe("Model(attributes, options)", () => { afterEach(() => clock.restore()); + describe("(module properties)", () => { + it("should provide the isPresent validation", () => { + (typeof isPresent).should.equal("function"); + }); + }); + /** * Begin Testing */ diff --git a/index.js b/index.js index a3a33cd..e33207e 100644 --- a/index.js +++ b/index.js @@ -1 +1,2 @@ -module.exports = require("./es5/lib/dovima.js"); +module.exports = require("./es5/lib/model.js"); +module.exports.isPresent = require("./es5/lib/validation/isPresent.js"); diff --git a/lcov.info b/lcov.info index 74e9863..59bc8ba 100644 --- a/lcov.info +++ b/lcov.info @@ -1,315 +1,4 @@ TN: -SF:/home/intag/workspace/fam/dovima/es5/lib/validation/isPresent.js -FN:8,_interopRequireDefault -FN:14,isPresent -FN:53,(anonymous_3) -FN:98,(anonymous_4) -FNF:4 -FNH:4 -FNDA:1,_interopRequireDefault -FNDA:24,isPresent -FNDA:7,(anonymous_3) -FNDA:2,(anonymous_4) -DA:3,1 -DA:6,1 -DA:8,1 -DA:10,1 -DA:12,1 -DA:14,1 -DA:15,24 -DA:16,24 -DA:18,24 -DA:19,0 -DA:23,24 -DA:24,24 -DA:26,24 -DA:28,24 -DA:33,24 -DA:35,24 -DA:36,16 -DA:39,2 -DA:40,2 -DA:41,2 -DA:43,14 -DA:44,14 -DA:45,4 -DA:46,4 -DA:48,10 -DA:50,3 -DA:51,3 -DA:53,7 -DA:54,7 -DA:55,0 -DA:56,0 -DA:58,7 -DA:60,7 -DA:61,1 -DA:62,1 -DA:64,6 -DA:65,6 -DA:71,14 -DA:73,0 -DA:76,8 -DA:77,4 -DA:78,4 -DA:81,2 -DA:84,2 -DA:85,1 -DA:87,1 -DA:89,2 -DA:91,0 -DA:93,4 -DA:95,4 -DA:98,2 -DA:99,2 -DA:100,0 -DA:101,0 -DA:103,2 -DA:105,2 -DA:106,1 -DA:107,1 -DA:109,1 -DA:110,1 -DA:114,2 -DA:117,2 -DA:118,1 -DA:119,1 -DA:121,1 -DA:122,1 -DA:124,2 -DA:126,0 -DA:132,1 -LF:69 -LH:61 -BRDA:8,1,0,1 -BRDA:8,1,1,0 -BRDA:8,2,0,1 -BRDA:8,2,1,1 -BRDA:18,3,0,0 -BRDA:18,3,1,24 -BRDA:35,4,0,16 -BRDA:35,4,1,8 -BRDA:36,5,0,1 -BRDA:36,5,1,2 -BRDA:36,5,2,14 -BRDA:36,5,3,0 -BRDA:44,6,0,4 -BRDA:44,6,1,10 -BRDA:48,7,0,3 -BRDA:48,7,1,7 -BRDA:54,8,0,0 -BRDA:54,8,1,7 -BRDA:60,9,0,1 -BRDA:60,9,1,6 -BRDA:76,10,0,4 -BRDA:76,10,1,4 -BRDA:78,11,0,2 -BRDA:78,11,1,2 -BRDA:78,11,2,2 -BRDA:78,11,3,0 -BRDA:84,12,0,1 -BRDA:84,12,1,1 -BRDA:95,13,0,2 -BRDA:95,13,1,2 -BRDA:95,13,2,2 -BRDA:95,13,3,0 -BRDA:99,14,0,0 -BRDA:99,14,1,2 -BRDA:105,15,0,1 -BRDA:105,15,1,1 -BRDA:117,16,0,1 -BRDA:117,16,1,1 -BRF:38 -BRH:31 -end_of_record -TN: -SF:/home/intag/workspace/fam/dovima/es5/lib/modelFinder.js -FN:7,(anonymous_1) -FN:7,defineProperties -FN:7,(anonymous_3) -FN:9,_interopRequireDefault -FN:11,_toConsumableArray -FN:13,_classCallCheck -FN:25,(anonymous_7) -FN:26,ModelFinder -FN:40,find -FN:51,count -FN:62,value -FN:74,(anonymous_12) -FN:75,ModelQuery -FN:88,get -FN:94,get -FN:100,get -FN:109,find -FN:120,count -FN:132,where -FN:139,(anonymous_20) -FN:152,andWhere -FN:160,orWhere -FN:168,groupBy -FN:176,orderBy -FN:184,limit -FN:193,results -FN:200,(anonymous_27) -FN:212,(anonymous_28) -FN:215,(anonymous_29) -FNF:29 -FNH:26 -FNDA:1,(anonymous_1) -FNDA:2,defineProperties -FNDA:2,(anonymous_3) -FNDA:2,_interopRequireDefault -FNDA:37,_toConsumableArray -FNDA:81,_classCallCheck -FNDA:1,(anonymous_7) -FNDA:43,ModelFinder -FNDA:18,find -FNDA:9,count -FNDA:27,value -FNDA:1,(anonymous_12) -FNDA:38,ModelQuery -FNDA:1,get -FNDA:1,get -FNDA:2,get -FNDA:29,find -FNDA:9,count -FNDA:37,where -FNDA:104,(anonymous_20) -FNDA:6,andWhere -FNDA:0,orWhere -FNDA:0,groupBy -FNDA:0,orderBy -FNDA:8,limit -FNDA:37,results -FNDA:37,(anonymous_27) -FNDA:28,(anonymous_28) -FNDA:85,(anonymous_29) -DA:3,1 -DA:7,12 -DA:9,2 -DA:11,104 -DA:13,81 -DA:15,1 -DA:17,1 -DA:19,1 -DA:21,1 -DA:23,1 -DA:25,1 -DA:26,1 -DA:27,43 -DA:29,43 -DA:38,1 -DA:41,18 -DA:43,18 -DA:45,18 -DA:47,18 -DA:52,9 -DA:54,9 -DA:56,9 -DA:58,9 -DA:63,27 -DA:64,0 -DA:69,1 -DA:72,1 -DA:74,1 -DA:75,1 -DA:76,38 -DA:78,38 -DA:80,38 -DA:81,38 -DA:89,1 -DA:90,1 -DA:95,1 -DA:96,1 -DA:101,2 -DA:107,1 -DA:110,29 -DA:112,29 -DA:114,29 -DA:116,29 -DA:121,9 -DA:122,9 -DA:124,9 -DA:126,9 -DA:128,9 -DA:133,37 -DA:135,37 -DA:136,104 -DA:139,37 -DA:140,104 -DA:141,37 -DA:143,67 -DA:147,37 -DA:148,37 -DA:153,6 -DA:155,6 -DA:156,6 -DA:161,0 -DA:163,0 -DA:164,0 -DA:169,0 -DA:171,0 -DA:172,0 -DA:177,0 -DA:179,0 -DA:180,0 -DA:185,8 -DA:187,8 -DA:188,8 -DA:189,8 -DA:194,37 -DA:196,37 -DA:197,1 -DA:200,37 -DA:201,37 -DA:202,0 -DA:203,0 -DA:205,0 -DA:209,37 -DA:210,9 -DA:212,28 -DA:213,28 -DA:215,28 -DA:216,85 -DA:219,28 -DA:226,1 -DA:229,1 -LF:90 -LH:77 -BRDA:7,1,0,12 -BRDA:7,1,1,12 -BRDA:7,2,0,12 -BRDA:7,2,1,0 -BRDA:7,3,0,2 -BRDA:7,3,1,0 -BRDA:7,4,0,0 -BRDA:7,4,1,2 -BRDA:9,5,0,1 -BRDA:9,5,1,1 -BRDA:9,6,0,2 -BRDA:9,6,1,2 -BRDA:11,7,0,37 -BRDA:11,7,1,0 -BRDA:13,8,0,0 -BRDA:13,8,1,81 -BRDA:63,9,0,0 -BRDA:63,9,1,27 -BRDA:140,10,0,37 -BRDA:140,10,1,67 -BRDA:140,11,0,104 -BRDA:140,11,1,67 -BRDA:196,12,0,1 -BRDA:196,12,1,36 -BRDA:201,13,0,0 -BRDA:201,13,1,37 -BRDA:202,14,0,0 -BRDA:202,14,1,0 -BRDA:209,15,0,9 -BRDA:209,15,1,28 -BRF:30 -BRH:21 -end_of_record -TN: SF:/home/intag/workspace/fam/dovima/es5/lib/collection.js FN:7,(anonymous_1) FN:7,defineProperties @@ -336,13 +25,13 @@ FNH:20 FNDA:1,(anonymous_1) FNDA:1,defineProperties FNDA:1,(anonymous_3) -FNDA:2361,get +FNDA:2375,get FNDA:3,_interopRequireDefault FNDA:3,_toConsumableArray -FNDA:2208,_classCallCheck +FNDA:2222,_classCallCheck FNDA:1,_inherits FNDA:1,(anonymous_9) -FNDA:2208,Collection +FNDA:2222,Collection FNDA:158,push FNDA:158,(anonymous_12) FNDA:4,fetch @@ -355,10 +44,10 @@ FNDA:4,(anonymous_19) FNDA:13,(anonymous_20) DA:3,1 DA:7,2 -DA:9,2361 +DA:9,2375 DA:11,3 DA:13,6 -DA:15,2208 +DA:15,2222 DA:17,1 DA:19,1 DA:21,1 @@ -368,13 +57,13 @@ DA:27,1 DA:29,1 DA:31,1 DA:32,1 -DA:33,2208 -DA:35,2208 -DA:37,2208 -DA:40,2208 +DA:33,2222 +DA:35,2222 +DA:37,2222 +DA:40,2222 DA:41,29 -DA:43,2179 -DA:46,2208 +DA:43,2193 +DA:46,2222 DA:59,1 DA:61,1 DA:64,158 @@ -433,66 +122,253 @@ BRDA:7,1,0,2 BRDA:7,1,1,2 BRDA:7,2,0,2 BRDA:7,2,1,0 -BRDA:7,3,0,1 +BRDA:7,3,0,1 +BRDA:7,3,1,0 +BRDA:7,4,0,0 +BRDA:7,4,1,1 +BRDA:9,5,0,0 +BRDA:9,5,1,2375 +BRDA:9,6,0,0 +BRDA:9,6,1,2375 +BRDA:9,7,0,0 +BRDA:9,7,1,0 +BRDA:9,8,0,2375 +BRDA:9,8,1,0 +BRDA:9,9,0,0 +BRDA:9,9,1,0 +BRDA:11,10,0,3 +BRDA:11,10,1,0 +BRDA:11,11,0,3 +BRDA:11,11,1,3 +BRDA:13,12,0,3 +BRDA:13,12,1,0 +BRDA:15,13,0,0 +BRDA:15,13,1,2222 +BRDA:17,14,0,0 +BRDA:17,14,1,1 +BRDA:17,15,0,1 +BRDA:17,15,1,0 +BRDA:17,16,0,1 +BRDA:17,16,1,1 +BRDA:17,17,0,1 +BRDA:17,17,1,0 +BRDA:40,18,0,29 +BRDA:40,18,1,2193 +BRDA:40,19,0,2222 +BRDA:40,19,1,2222 +BRDA:71,20,0,153 +BRDA:71,20,1,5 +BRDA:72,21,0,63 +BRDA:72,21,1,90 +BRDA:75,22,0,39 +BRDA:75,22,1,24 +BRDA:76,23,0,31 +BRDA:76,23,1,8 +BRDA:79,24,0,8 +BRDA:79,24,1,16 +BRDA:83,25,0,90 +BRDA:83,25,1,0 +BRDA:87,26,0,0 +BRDA:87,26,1,0 +BRDA:87,27,0,0 +BRDA:87,27,1,0 +BRDA:104,28,0,4 +BRDA:104,28,1,0 +BRDA:107,29,0,3 +BRDA:107,29,1,3 +BRDA:119,30,0,2 +BRDA:119,30,1,2 +BRDA:128,31,0,1 +BRDA:128,31,1,1 +BRF:62 +BRH:40 +end_of_record +TN: +SF:/home/intag/workspace/fam/dovima/es5/lib/modelFinder.js +FN:7,(anonymous_1) +FN:7,defineProperties +FN:7,(anonymous_3) +FN:9,_interopRequireDefault +FN:11,_toConsumableArray +FN:13,_classCallCheck +FN:25,(anonymous_7) +FN:26,ModelFinder +FN:40,find +FN:51,count +FN:62,value +FN:74,(anonymous_12) +FN:75,ModelQuery +FN:88,get +FN:94,get +FN:100,get +FN:109,find +FN:120,count +FN:132,where +FN:139,(anonymous_20) +FN:152,andWhere +FN:160,orWhere +FN:168,groupBy +FN:176,orderBy +FN:184,limit +FN:193,results +FN:200,(anonymous_27) +FN:212,(anonymous_28) +FN:215,(anonymous_29) +FNF:29 +FNH:26 +FNDA:1,(anonymous_1) +FNDA:2,defineProperties +FNDA:2,(anonymous_3) +FNDA:2,_interopRequireDefault +FNDA:37,_toConsumableArray +FNDA:81,_classCallCheck +FNDA:1,(anonymous_7) +FNDA:43,ModelFinder +FNDA:18,find +FNDA:9,count +FNDA:27,value +FNDA:1,(anonymous_12) +FNDA:38,ModelQuery +FNDA:1,get +FNDA:1,get +FNDA:2,get +FNDA:29,find +FNDA:9,count +FNDA:37,where +FNDA:104,(anonymous_20) +FNDA:6,andWhere +FNDA:0,orWhere +FNDA:0,groupBy +FNDA:0,orderBy +FNDA:8,limit +FNDA:37,results +FNDA:37,(anonymous_27) +FNDA:28,(anonymous_28) +FNDA:85,(anonymous_29) +DA:3,1 +DA:7,12 +DA:9,2 +DA:11,104 +DA:13,81 +DA:15,1 +DA:17,1 +DA:19,1 +DA:21,1 +DA:23,1 +DA:25,1 +DA:26,1 +DA:27,43 +DA:29,43 +DA:38,1 +DA:41,18 +DA:43,18 +DA:45,18 +DA:47,18 +DA:52,9 +DA:54,9 +DA:56,9 +DA:58,9 +DA:63,27 +DA:64,0 +DA:69,1 +DA:72,1 +DA:74,1 +DA:75,1 +DA:76,38 +DA:78,38 +DA:80,38 +DA:81,38 +DA:89,1 +DA:90,1 +DA:95,1 +DA:96,1 +DA:101,2 +DA:107,1 +DA:110,29 +DA:112,29 +DA:114,29 +DA:116,29 +DA:121,9 +DA:122,9 +DA:124,9 +DA:126,9 +DA:128,9 +DA:133,37 +DA:135,37 +DA:136,104 +DA:139,37 +DA:140,104 +DA:141,37 +DA:143,67 +DA:147,37 +DA:148,37 +DA:153,6 +DA:155,6 +DA:156,6 +DA:161,0 +DA:163,0 +DA:164,0 +DA:169,0 +DA:171,0 +DA:172,0 +DA:177,0 +DA:179,0 +DA:180,0 +DA:185,8 +DA:187,8 +DA:188,8 +DA:189,8 +DA:194,37 +DA:196,37 +DA:197,1 +DA:200,37 +DA:201,37 +DA:202,0 +DA:203,0 +DA:205,0 +DA:209,37 +DA:210,9 +DA:212,28 +DA:213,28 +DA:215,28 +DA:216,85 +DA:219,28 +DA:226,1 +DA:229,1 +LF:90 +LH:77 +BRDA:7,1,0,12 +BRDA:7,1,1,12 +BRDA:7,2,0,12 +BRDA:7,2,1,0 +BRDA:7,3,0,2 BRDA:7,3,1,0 BRDA:7,4,0,0 -BRDA:7,4,1,1 -BRDA:9,5,0,0 -BRDA:9,5,1,2361 -BRDA:9,6,0,0 -BRDA:9,6,1,2361 -BRDA:9,7,0,0 -BRDA:9,7,1,0 -BRDA:9,8,0,2361 -BRDA:9,8,1,0 -BRDA:9,9,0,0 -BRDA:9,9,1,0 -BRDA:11,10,0,3 -BRDA:11,10,1,0 -BRDA:11,11,0,3 -BRDA:11,11,1,3 -BRDA:13,12,0,3 -BRDA:13,12,1,0 -BRDA:15,13,0,0 -BRDA:15,13,1,2208 -BRDA:17,14,0,0 -BRDA:17,14,1,1 -BRDA:17,15,0,1 -BRDA:17,15,1,0 -BRDA:17,16,0,1 -BRDA:17,16,1,1 -BRDA:17,17,0,1 -BRDA:17,17,1,0 -BRDA:40,18,0,29 -BRDA:40,18,1,2179 -BRDA:40,19,0,2208 -BRDA:40,19,1,2208 -BRDA:71,20,0,153 -BRDA:71,20,1,5 -BRDA:72,21,0,63 -BRDA:72,21,1,90 -BRDA:75,22,0,39 -BRDA:75,22,1,24 -BRDA:76,23,0,31 -BRDA:76,23,1,8 -BRDA:79,24,0,8 -BRDA:79,24,1,16 -BRDA:83,25,0,90 -BRDA:83,25,1,0 -BRDA:87,26,0,0 -BRDA:87,26,1,0 -BRDA:87,27,0,0 -BRDA:87,27,1,0 -BRDA:104,28,0,4 -BRDA:104,28,1,0 -BRDA:107,29,0,3 -BRDA:107,29,1,3 -BRDA:119,30,0,2 -BRDA:119,30,1,2 -BRDA:128,31,0,1 -BRDA:128,31,1,1 -BRF:62 -BRH:40 +BRDA:7,4,1,2 +BRDA:9,5,0,2 +BRDA:9,5,1,0 +BRDA:9,6,0,2 +BRDA:9,6,1,2 +BRDA:11,7,0,37 +BRDA:11,7,1,0 +BRDA:13,8,0,0 +BRDA:13,8,1,81 +BRDA:63,9,0,0 +BRDA:63,9,1,27 +BRDA:140,10,0,37 +BRDA:140,10,1,67 +BRDA:140,11,0,104 +BRDA:140,11,1,67 +BRDA:196,12,0,1 +BRDA:196,12,1,36 +BRDA:201,13,0,0 +BRDA:201,13,1,37 +BRDA:202,14,0,0 +BRDA:202,14,1,0 +BRDA:209,15,0,9 +BRDA:209,15,1,28 +BRF:30 +BRH:20 end_of_record TN: SF:/home/intag/workspace/fam/dovima/es5/lib/model.js @@ -615,19 +491,19 @@ FNDA:2,defineProperties FNDA:2,(anonymous_3) FNDA:5,_interopRequireDefault FNDA:5,_toConsumableArray -FNDA:4778,_classCallCheck +FNDA:4808,_classCallCheck FNDA:1,(anonymous_7) -FNDA:1190,Model -FNDA:2380,(anonymous_9) +FNDA:1197,Model +FNDA:2394,(anonymous_9) FNDA:83,get FNDA:1,set FNDA:109,get FNDA:1,set FNDA:21,get -FNDA:617,hasOne -FNDA:792,belongsTo -FNDA:2179,hasMany -FNDA:615,ensure +FNDA:620,hasOne +FNDA:798,belongsTo +FNDA:2193,hasMany +FNDA:619,ensure FNDA:12,isValid FNDA:12,(anonymous_20) FNDA:18,invalidAttributes @@ -688,11 +564,11 @@ FNDA:10,(anonymous_75) FNDA:10,beforeValidation FNDA:8,beforeSave FNDA:8,afterSave -FNDA:305,associate -FNDA:608,validate -FNDA:1163,initialize +FNDA:306,associate +FNDA:611,validate +FNDA:1170,initialize FNDA:2,toJSON -FNDA:1191,value +FNDA:1198,value FNDA:98,value FNDA:18,value FNDA:2,value @@ -703,30 +579,30 @@ FNDA:15,value FNDA:18,(anonymous_91) FNDA:3,(anonymous_92) FNDA:15,(anonymous_93) -FNDA:3588,value -FNDA:648,(anonymous_95) -FNDA:860,(anonymous_96) -FNDA:2179,(anonymous_97) +FNDA:3611,value +FNDA:651,(anonymous_95) +FNDA:866,(anonymous_96) +FNDA:2193,(anonymous_97) FNDA:257,get -FNDA:1213,value +FNDA:1220,value FNDA:8,value FNDA:15,(anonymous_101) FNDA:12,(anonymous_102) FNDA:1,(anonymous_103) -FNDA:3588,AssociationSetter +FNDA:3611,AssociationSetter FNDA:0,foreignName -FNDA:386,where +FNDA:388,where FNDA:3,andWhere -FNDA:1285,through -FNDA:258,as -FNDA:259,value +FNDA:1293,through +FNDA:260,as +FNDA:261,value FNDA:6,value FNDA:11,modelFind DA:3,1 DA:7,35 DA:9,5 DA:11,10 -DA:13,4778 +DA:13,4808 DA:18,1 DA:20,1 DA:22,1 @@ -741,29 +617,29 @@ DA:38,1 DA:41,1 DA:57,1 DA:63,1 -DA:64,1190 -DA:66,1190 -DA:68,1190 -DA:69,2380 -DA:78,1190 +DA:64,1197 +DA:66,1197 +DA:68,1197 +DA:69,2394 +DA:78,1197 DA:113,83 DA:116,1 DA:127,109 DA:130,1 DA:142,21 -DA:147,1190 -DA:148,1190 -DA:150,1190 -DA:152,1190 +DA:147,1197 +DA:148,1197 +DA:150,1197 +DA:152,1197 DA:155,1 -DA:158,617 -DA:167,792 -DA:176,2179 -DA:185,615 -DA:187,615 -DA:189,615 +DA:158,620 +DA:167,798 +DA:176,2193 +DA:185,619 +DA:187,619 +DA:189,619 DA:190,3 -DA:193,615 +DA:193,619 DA:205,12 DA:206,12 DA:226,18 @@ -1013,7 +889,7 @@ DA:758,8 DA:772,2 DA:773,1 DA:775,1 -DA:786,1191 +DA:786,1198 DA:791,98 DA:796,18 DA:801,2 @@ -1049,30 +925,30 @@ DA:872,0 DA:876,0 DA:878,8 DA:881,15 -DA:902,3588 -DA:904,3588 -DA:912,3588 -DA:915,2796 -DA:916,2796 -DA:917,2796 -DA:919,792 -DA:920,792 -DA:924,3588 -DA:928,3588 -DA:932,3588 -DA:936,3588 -DA:938,3588 -DA:940,617 -DA:942,617 -DA:943,648 +DA:902,3611 +DA:904,3611 +DA:912,3611 +DA:915,2813 +DA:916,2813 +DA:917,2813 +DA:919,798 +DA:920,798 +DA:924,3611 +DA:928,3611 +DA:932,3611 +DA:936,3611 +DA:938,3611 +DA:940,620 +DA:942,620 +DA:943,651 DA:944,26 DA:945,1 DA:948,25 DA:950,25 -DA:953,617 -DA:955,792 -DA:957,792 -DA:958,860 +DA:953,620 +DA:955,798 +DA:957,798 +DA:958,866 DA:959,62 DA:960,1 DA:963,61 @@ -1084,21 +960,21 @@ DA:970,7 DA:971,11 DA:973,10 DA:975,1 -DA:986,792 -DA:989,2179 -DA:991,2179 -DA:992,2179 +DA:986,798 +DA:989,2193 +DA:991,2193 +DA:992,2193 DA:993,0 -DA:996,2179 +DA:996,2193 DA:998,0 -DA:1001,3588 -DA:1006,3588 +DA:1001,3611 +DA:1006,3611 DA:1010,257 -DA:1014,3588 -DA:1016,3588 -DA:1018,3588 -DA:1023,1213 -DA:1024,713 +DA:1014,3611 +DA:1016,3611 +DA:1018,3611 +DA:1023,1220 +DA:1024,717 DA:1030,8 DA:1032,8 DA:1033,8 @@ -1120,30 +996,30 @@ DA:1066,1 DA:1068,1 DA:1071,1 DA:1072,1 -DA:1073,3588 -DA:1075,3588 -DA:1077,3588 -DA:1079,792 -DA:1084,792 -DA:1087,2796 -DA:1092,2796 +DA:1073,3611 +DA:1075,3611 +DA:1077,3611 +DA:1079,798 +DA:1084,798 +DA:1087,2813 +DA:1092,2813 DA:1096,1 DA:1099,0 DA:1100,0 -DA:1105,386 -DA:1106,961 -DA:1109,386 -DA:1110,386 +DA:1105,388 +DA:1106,966 +DA:1109,388 +DA:1110,388 DA:1115,3 DA:1117,3 DA:1118,6 DA:1121,3 DA:1122,3 -DA:1127,1285 -DA:1128,1285 -DA:1133,258 -DA:1134,258 -DA:1139,259 +DA:1127,1293 +DA:1128,1293 +DA:1133,260 +DA:1134,260 +DA:1139,261 DA:1144,6 DA:1148,1 DA:1151,1 @@ -1167,15 +1043,15 @@ BRDA:9,6,1,5 BRDA:11,7,0,5 BRDA:11,7,1,0 BRDA:13,8,0,0 -BRDA:13,8,1,4778 +BRDA:13,8,1,4808 BRDA:113,9,0,83 BRDA:113,9,1,82 BRDA:127,10,0,109 BRDA:127,10,1,108 -BRDA:185,11,0,615 -BRDA:185,11,1,614 +BRDA:185,11,0,619 +BRDA:185,11,1,618 BRDA:189,12,0,3 -BRDA:189,12,1,612 +BRDA:189,12,1,616 BRDA:231,13,0,0 BRDA:231,13,1,18 BRDA:239,14,0,8 @@ -1295,22 +1171,22 @@ BRDA:865,68,0,8 BRDA:865,68,1,0 BRDA:869,69,0,3 BRDA:869,69,1,0 -BRDA:912,70,0,617 -BRDA:912,70,1,2796 -BRDA:912,70,2,792 -BRDA:938,71,0,617 -BRDA:938,71,1,792 -BRDA:938,71,2,2179 +BRDA:912,70,0,620 +BRDA:912,70,1,2813 +BRDA:912,70,2,798 +BRDA:938,71,0,620 +BRDA:938,71,1,798 +BRDA:938,71,2,2193 BRDA:938,71,3,0 BRDA:943,72,0,26 -BRDA:943,72,1,622 -BRDA:943,73,0,648 +BRDA:943,72,1,625 +BRDA:943,73,0,651 BRDA:943,73,1,30 BRDA:944,74,0,1 BRDA:944,74,1,25 BRDA:958,75,0,62 -BRDA:958,75,1,798 -BRDA:958,76,0,860 +BRDA:958,75,1,804 +BRDA:958,76,0,866 BRDA:958,76,1,66 BRDA:959,77,0,1 BRDA:959,77,1,61 @@ -1321,8 +1197,8 @@ BRDA:969,79,1,11 BRDA:971,80,0,10 BRDA:971,80,1,1 BRDA:992,81,0,0 -BRDA:992,81,1,2179 -BRDA:992,82,0,2179 +BRDA:992,81,1,2193 +BRDA:992,82,0,2193 BRDA:992,82,1,0 BRDA:1042,83,0,4 BRDA:1042,83,1,8 @@ -1330,11 +1206,135 @@ BRDA:1044,84,0,4 BRDA:1044,84,1,0 BRDA:1044,85,0,4 BRDA:1044,85,1,4 -BRDA:1077,86,0,792 -BRDA:1077,86,1,617 -BRDA:1077,86,2,2796 +BRDA:1077,86,0,798 +BRDA:1077,86,1,620 +BRDA:1077,86,2,2813 BRDA:1115,87,0,3 BRDA:1115,87,1,3 BRF:183 BRH:159 end_of_record +TN: +SF:/home/intag/workspace/fam/dovima/es5/lib/validation/isPresent.js +FN:8,_interopRequireDefault +FN:14,isPresent +FN:53,(anonymous_3) +FN:98,(anonymous_4) +FNF:4 +FNH:4 +FNDA:1,_interopRequireDefault +FNDA:24,isPresent +FNDA:7,(anonymous_3) +FNDA:2,(anonymous_4) +DA:3,1 +DA:6,1 +DA:8,1 +DA:10,1 +DA:12,1 +DA:14,1 +DA:15,24 +DA:16,24 +DA:18,24 +DA:19,0 +DA:23,24 +DA:24,24 +DA:26,24 +DA:28,24 +DA:33,24 +DA:35,24 +DA:36,16 +DA:39,2 +DA:40,2 +DA:41,2 +DA:43,14 +DA:44,14 +DA:45,4 +DA:46,4 +DA:48,10 +DA:50,3 +DA:51,3 +DA:53,7 +DA:54,7 +DA:55,0 +DA:56,0 +DA:58,7 +DA:60,7 +DA:61,1 +DA:62,1 +DA:64,6 +DA:65,6 +DA:71,14 +DA:73,0 +DA:76,8 +DA:77,4 +DA:78,4 +DA:81,2 +DA:84,2 +DA:85,1 +DA:87,1 +DA:89,2 +DA:91,0 +DA:93,4 +DA:95,4 +DA:98,2 +DA:99,2 +DA:100,0 +DA:101,0 +DA:103,2 +DA:105,2 +DA:106,1 +DA:107,1 +DA:109,1 +DA:110,1 +DA:114,2 +DA:117,2 +DA:118,1 +DA:119,1 +DA:121,1 +DA:122,1 +DA:124,2 +DA:126,0 +DA:132,1 +LF:69 +LH:61 +BRDA:8,1,0,1 +BRDA:8,1,1,0 +BRDA:8,2,0,1 +BRDA:8,2,1,1 +BRDA:18,3,0,0 +BRDA:18,3,1,24 +BRDA:35,4,0,16 +BRDA:35,4,1,8 +BRDA:36,5,0,1 +BRDA:36,5,1,2 +BRDA:36,5,2,14 +BRDA:36,5,3,0 +BRDA:44,6,0,4 +BRDA:44,6,1,10 +BRDA:48,7,0,3 +BRDA:48,7,1,7 +BRDA:54,8,0,0 +BRDA:54,8,1,7 +BRDA:60,9,0,1 +BRDA:60,9,1,6 +BRDA:76,10,0,4 +BRDA:76,10,1,4 +BRDA:78,11,0,2 +BRDA:78,11,1,2 +BRDA:78,11,2,2 +BRDA:78,11,3,0 +BRDA:84,12,0,1 +BRDA:84,12,1,1 +BRDA:95,13,0,2 +BRDA:95,13,1,2 +BRDA:95,13,2,2 +BRDA:95,13,3,0 +BRDA:99,14,0,0 +BRDA:99,14,1,2 +BRDA:105,15,0,1 +BRDA:105,15,1,1 +BRDA:117,16,0,1 +BRDA:117,16,1,1 +BRF:38 +BRH:31 +end_of_record From 45a977413755930f17eae305da34c692f0adb6ee Mon Sep 17 00:00:00 2001 From: nicosommi Date: Tue, 30 Jun 2015 16:49:53 -0300 Subject: [PATCH 07/15] fix collection import --- README.md | 2 +- es6/spec/model.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index fab491e..34b8504 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ You can relate models with the `hasOne`, `hasMany` and `belongsTo` methods provi Also Dovima let's you add validation to the models property by writing a simple `validate` method. You can call the `ensure(propertyName, validator)` method and that will receive the property name on the model to execute the validator. For validations there are some provided within Dovima (isPresent) and some provided by the [proven](https://github.com/FreeAllMedia/proven) package. Validations can be sync or async and new ones can be created anytime by using the same interface. ### Soft delete -When you write your Model class you can mark it as a softDelete able Model, case in which it will add the logical deletion behavior, so then when you delete it, it will be an update and it will be excluded from regular model queries except if you find explicitly the deleted ones. See below on find for that example. +When you write your Model class you can mark it as a soft delete able Model by calling the `this.softDelete` property on the `initialize` method, case in which it will add the logical deletion behavior, so then when you delete it, it will be an update and it will be excluded from regular model queries except if you find explicitly the deleted ones. See below on find for that example. ### Find models Finding will return the error and the result collection using the node callback convention (error, data). diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index 087d3ae..c434a09 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -5,7 +5,7 @@ const sinon = require("sinon"); //import MultiError from "../../multiError/multiError.js"; import Database from "almaden"; -import {Collection} from "../../"; +import Collection from "../lib/collection.js"; import Model, {AssociationSetter} from "../lib/model.js"; import {ModelQuery} from "../lib/modelFinder.js"; import {isPresent} from "../../"; From 959a08ea0310c6a4543360a9a3ef6102467ea5b2 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Tue, 30 Jun 2015 17:22:22 -0300 Subject: [PATCH 08/15] added test coverage badge --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 34b8504..1b148db 100644 --- a/README.md +++ b/README.md @@ -74,7 +74,7 @@ class Seat extends Model { # Quality and Compatibility -[![Build Status](https://travis-ci.org/FreeAllMedia/dovima.png?branch=master)](https://travis-ci.org/FreeAllMedia/dovima) [![Code Climate](https://codeclimate.com/github/FreeAllMedia/dovima/badges/gpa.svg)](https://codeclimate.com/github/FreeAllMedia/dovima) [![Dependency Status](https://david-dm.org/FreeAllMedia/dovima.png?theme=shields.io)](https://david-dm.org/FreeAllMedia/dovima?theme=shields.io) [![Dev Dependency Status](https://david-dm.org/FreeAllMedia/dovima/dev-status.svg)](https://david-dm.org/FreeAllMedia/dovima?theme=shields.io#info=devDependencies) +[![Build Status](https://travis-ci.org/FreeAllMedia/dovima.png?branch=master)](https://travis-ci.org/FreeAllMedia/dovima) [![Coverage Status](https://coveralls.io/repos/FreeAllMedia/dovima/badge.svg)](https://coveralls.io/r/FreeAllMedia/dovima) [![Code Climate](https://codeclimate.com/github/FreeAllMedia/dovima/badges/gpa.svg)](https://codeclimate.com/github/FreeAllMedia/dovima) [![Dependency Status](https://david-dm.org/FreeAllMedia/dovima.png?theme=shields.io)](https://david-dm.org/FreeAllMedia/dovima?theme=shields.io) [![Dev Dependency Status](https://david-dm.org/FreeAllMedia/dovima/dev-status.svg)](https://david-dm.org/FreeAllMedia/dovima?theme=shields.io#info=devDependencies) *Every build and release is automatically tested on the following platforms:* From 8671326aefb916e4285a29c07e5d6b116a546eba Mon Sep 17 00:00:00 2001 From: nicosommi Date: Wed, 1 Jul 2015 19:04:13 -0300 Subject: [PATCH 09/15] resolved nested unnecesary multierror object --- es5/lib/model.js | 9 +- es5/spec/model.spec.js | 14 + es6/lib/model.js | 9 +- es6/spec/model.spec.js | 14 + lcov.info | 1030 ++++++++++++++++++++-------------------- package.json | 2 +- 6 files changed, 549 insertions(+), 529 deletions(-) diff --git a/es5/lib/model.js b/es5/lib/model.js index d273bd1..b41bf48 100644 --- a/es5/lib/model.js +++ b/es5/lib/model.js @@ -662,19 +662,16 @@ var Model = (function () { var hasInvalidAttributes = Object.keys(invalidAttributeList).length > 0; if (hasInvalidAttributes) { - var multiError = new _blunder2["default"](); + var errorPrefix = _this6.constructor.name + " is invalid"; + var multiError = new _blunder2["default"]([], errorPrefix); for (var invalidAttributeName in invalidAttributeList) { var invalidAttributeMessages = invalidAttributeList[invalidAttributeName]; - var errorPrefix = _this6.constructor.name + " is invalid"; - - var attributesMultiError = new _blunder2["default"]([], errorPrefix); for (var index in invalidAttributeMessages) { var invalidAttributeMessage = invalidAttributeMessages[index]; var error = new Error(invalidAttributeName + " " + invalidAttributeMessage); - attributesMultiError.push(error); + multiError.push(error); } - multiError.push(attributesMultiError); } next(multiError); } else { diff --git a/es5/spec/model.spec.js b/es5/spec/model.spec.js index 738cc2d..d08ccf7 100644 --- a/es5/spec/model.spec.js +++ b/es5/spec/model.spec.js @@ -964,6 +964,20 @@ describe("Model(attributes, options)", function () { }); }); + it("should retun just one multi error object the appropiate number of errors", function (done) { + user.save(function (error) { + error.errors.length.should.equal(1); + done(); + }); + }); + + it("should retun just one multi error object the appropiate name", function (done) { + user.save(function (error) { + error.name.should.equal("User is invalid"); + done(); + }); + }); + it("should return an object containing all invalid attributes", function (done) { user.invalidAttributes(function (attributes) { attributes.should.eql({ diff --git a/es6/lib/model.js b/es6/lib/model.js index 8c96764..8a92e7a 100644 --- a/es6/lib/model.js +++ b/es6/lib/model.js @@ -609,19 +609,16 @@ export default class Model { const hasInvalidAttributes = Object.keys(invalidAttributeList).length > 0; if (hasInvalidAttributes) { - const multiError = new MultiError(); + const errorPrefix = this.constructor.name + " is invalid"; + const multiError = new MultiError([], errorPrefix); for(let invalidAttributeName in invalidAttributeList) { const invalidAttributeMessages = invalidAttributeList[invalidAttributeName]; - const errorPrefix = this.constructor.name + " is invalid"; - - const attributesMultiError = new MultiError([], errorPrefix); for(let index in invalidAttributeMessages) { const invalidAttributeMessage = invalidAttributeMessages[index]; const error = new Error(`${invalidAttributeName} ${invalidAttributeMessage}`); - attributesMultiError.push(error); + multiError.push(error); } - multiError.push(attributesMultiError); } next(multiError); } else { diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index c434a09..e616e19 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -831,6 +831,20 @@ describe("Model(attributes, options)", () => { }); }); + it("should retun just one multi error object the appropiate number of errors", done => { + user.save((error) => { + error.errors.length.should.equal(1); + done(); + }); + }); + + it("should retun just one multi error object the appropiate name", done => { + user.save((error) => { + error.name.should.equal("User is invalid"); + done(); + }); + }); + it("should return an object containing all invalid attributes", done => { user.invalidAttributes((attributes) => { attributes.should.eql({ diff --git a/lcov.info b/lcov.info index 59bc8ba..492c2f0 100644 --- a/lcov.info +++ b/lcov.info @@ -25,13 +25,13 @@ FNH:20 FNDA:1,(anonymous_1) FNDA:1,defineProperties FNDA:1,(anonymous_3) -FNDA:2375,get +FNDA:2419,get FNDA:3,_interopRequireDefault FNDA:3,_toConsumableArray -FNDA:2222,_classCallCheck +FNDA:2266,_classCallCheck FNDA:1,_inherits FNDA:1,(anonymous_9) -FNDA:2222,Collection +FNDA:2266,Collection FNDA:158,push FNDA:158,(anonymous_12) FNDA:4,fetch @@ -44,10 +44,10 @@ FNDA:4,(anonymous_19) FNDA:13,(anonymous_20) DA:3,1 DA:7,2 -DA:9,2375 +DA:9,2419 DA:11,3 DA:13,6 -DA:15,2222 +DA:15,2266 DA:17,1 DA:19,1 DA:21,1 @@ -57,13 +57,13 @@ DA:27,1 DA:29,1 DA:31,1 DA:32,1 -DA:33,2222 -DA:35,2222 -DA:37,2222 -DA:40,2222 +DA:33,2266 +DA:35,2266 +DA:37,2266 +DA:40,2266 DA:41,29 -DA:43,2193 -DA:46,2222 +DA:43,2237 +DA:46,2266 DA:59,1 DA:61,1 DA:64,158 @@ -127,12 +127,12 @@ BRDA:7,3,1,0 BRDA:7,4,0,0 BRDA:7,4,1,1 BRDA:9,5,0,0 -BRDA:9,5,1,2375 +BRDA:9,5,1,2419 BRDA:9,6,0,0 -BRDA:9,6,1,2375 +BRDA:9,6,1,2419 BRDA:9,7,0,0 BRDA:9,7,1,0 -BRDA:9,8,0,2375 +BRDA:9,8,0,2419 BRDA:9,8,1,0 BRDA:9,9,0,0 BRDA:9,9,1,0 @@ -143,7 +143,7 @@ BRDA:11,11,1,3 BRDA:13,12,0,3 BRDA:13,12,1,0 BRDA:15,13,0,0 -BRDA:15,13,1,2222 +BRDA:15,13,1,2266 BRDA:17,14,0,0 BRDA:17,14,1,1 BRDA:17,15,0,1 @@ -153,9 +153,9 @@ BRDA:17,16,1,1 BRDA:17,17,0,1 BRDA:17,17,1,0 BRDA:40,18,0,29 -BRDA:40,18,1,2193 -BRDA:40,19,0,2222 -BRDA:40,19,1,2222 +BRDA:40,18,1,2237 +BRDA:40,19,0,2266 +BRDA:40,19,1,2266 BRDA:71,20,0,153 BRDA:71,20,1,5 BRDA:72,21,0,63 @@ -220,36 +220,36 @@ FNDA:1,(anonymous_1) FNDA:2,defineProperties FNDA:2,(anonymous_3) FNDA:2,_interopRequireDefault -FNDA:37,_toConsumableArray -FNDA:81,_classCallCheck +FNDA:41,_toConsumableArray +FNDA:89,_classCallCheck FNDA:1,(anonymous_7) -FNDA:43,ModelFinder +FNDA:47,ModelFinder FNDA:18,find -FNDA:9,count -FNDA:27,value +FNDA:13,count +FNDA:31,value FNDA:1,(anonymous_12) -FNDA:38,ModelQuery +FNDA:42,ModelQuery FNDA:1,get FNDA:1,get FNDA:2,get FNDA:29,find -FNDA:9,count -FNDA:37,where -FNDA:104,(anonymous_20) +FNDA:13,count +FNDA:41,where +FNDA:116,(anonymous_20) FNDA:6,andWhere FNDA:0,orWhere FNDA:0,groupBy FNDA:0,orderBy FNDA:8,limit -FNDA:37,results -FNDA:37,(anonymous_27) +FNDA:41,results +FNDA:41,(anonymous_27) FNDA:28,(anonymous_28) FNDA:85,(anonymous_29) DA:3,1 DA:7,12 DA:9,2 -DA:11,104 -DA:13,81 +DA:11,116 +DA:13,89 DA:15,1 DA:17,1 DA:19,1 @@ -257,27 +257,27 @@ DA:21,1 DA:23,1 DA:25,1 DA:26,1 -DA:27,43 -DA:29,43 +DA:27,47 +DA:29,47 DA:38,1 DA:41,18 DA:43,18 DA:45,18 DA:47,18 -DA:52,9 -DA:54,9 -DA:56,9 -DA:58,9 -DA:63,27 +DA:52,13 +DA:54,13 +DA:56,13 +DA:58,13 +DA:63,31 DA:64,0 DA:69,1 DA:72,1 DA:74,1 DA:75,1 -DA:76,38 -DA:78,38 -DA:80,38 -DA:81,38 +DA:76,42 +DA:78,42 +DA:80,42 +DA:81,42 DA:89,1 DA:90,1 DA:95,1 @@ -288,20 +288,20 @@ DA:110,29 DA:112,29 DA:114,29 DA:116,29 -DA:121,9 -DA:122,9 -DA:124,9 -DA:126,9 -DA:128,9 -DA:133,37 -DA:135,37 -DA:136,104 -DA:139,37 -DA:140,104 -DA:141,37 -DA:143,67 -DA:147,37 -DA:148,37 +DA:121,13 +DA:122,13 +DA:124,13 +DA:126,13 +DA:128,13 +DA:133,41 +DA:135,41 +DA:136,116 +DA:139,41 +DA:140,116 +DA:141,41 +DA:143,75 +DA:147,41 +DA:148,41 DA:153,6 DA:155,6 DA:156,6 @@ -318,16 +318,16 @@ DA:185,8 DA:187,8 DA:188,8 DA:189,8 -DA:194,37 -DA:196,37 +DA:194,41 +DA:196,41 DA:197,1 -DA:200,37 -DA:201,37 +DA:200,41 +DA:201,41 DA:202,0 DA:203,0 DA:205,0 -DA:209,37 -DA:210,9 +DA:209,41 +DA:210,13 DA:212,28 DA:213,28 DA:215,28 @@ -349,23 +349,23 @@ BRDA:9,5,0,2 BRDA:9,5,1,0 BRDA:9,6,0,2 BRDA:9,6,1,2 -BRDA:11,7,0,37 +BRDA:11,7,0,41 BRDA:11,7,1,0 BRDA:13,8,0,0 -BRDA:13,8,1,81 +BRDA:13,8,1,89 BRDA:63,9,0,0 -BRDA:63,9,1,27 -BRDA:140,10,0,37 -BRDA:140,10,1,67 -BRDA:140,11,0,104 -BRDA:140,11,1,67 +BRDA:63,9,1,31 +BRDA:140,10,0,41 +BRDA:140,10,1,75 +BRDA:140,11,0,116 +BRDA:140,11,1,75 BRDA:196,12,0,1 -BRDA:196,12,1,36 +BRDA:196,12,1,40 BRDA:201,13,0,0 -BRDA:201,13,1,37 +BRDA:201,13,1,41 BRDA:202,14,0,0 BRDA:202,14,1,0 -BRDA:209,15,0,9 +BRDA:209,15,0,13 BRDA:209,15,1,28 BRF:30 BRH:20 @@ -440,50 +440,50 @@ FN:654,(anonymous_65) FN:656,(anonymous_66) FN:657,(anonymous_67) FN:661,(anonymous_68) -FN:686,(anonymous_69) -FN:688,(anonymous_70) -FN:694,(anonymous_71) -FN:716,(anonymous_72) -FN:719,(anonymous_73) -FN:733,(anonymous_74) -FN:735,(anonymous_75) -FN:747,beforeValidation -FN:752,beforeSave -FN:757,afterSave -FN:762,associate -FN:765,validate -FN:768,initialize -FN:771,toJSON -FN:785,value -FN:790,value -FN:795,value -FN:800,value -FN:805,value -FN:809,(anonymous_88) -FN:818,value -FN:836,value -FN:841,(anonymous_91) -FN:867,(anonymous_92) -FN:880,(anonymous_93) -FN:901,value -FN:942,(anonymous_95) -FN:957,(anonymous_96) -FN:991,(anonymous_97) -FN:1009,get -FN:1022,value -FN:1029,value -FN:1034,(anonymous_101) -FN:1039,(anonymous_102) -FN:1071,(anonymous_103) -FN:1072,AssociationSetter -FN:1098,foreignName -FN:1104,where -FN:1114,andWhere -FN:1126,through -FN:1132,as -FN:1138,value -FN:1143,value -FN:1155,modelFind +FN:683,(anonymous_69) +FN:685,(anonymous_70) +FN:691,(anonymous_71) +FN:713,(anonymous_72) +FN:716,(anonymous_73) +FN:730,(anonymous_74) +FN:732,(anonymous_75) +FN:744,beforeValidation +FN:749,beforeSave +FN:754,afterSave +FN:759,associate +FN:762,validate +FN:765,initialize +FN:768,toJSON +FN:782,value +FN:787,value +FN:792,value +FN:797,value +FN:802,value +FN:806,(anonymous_88) +FN:815,value +FN:833,value +FN:838,(anonymous_91) +FN:864,(anonymous_92) +FN:877,(anonymous_93) +FN:898,value +FN:939,(anonymous_95) +FN:954,(anonymous_96) +FN:988,(anonymous_97) +FN:1006,get +FN:1019,value +FN:1026,value +FN:1031,(anonymous_101) +FN:1036,(anonymous_102) +FN:1068,(anonymous_103) +FN:1069,AssociationSetter +FN:1095,foreignName +FN:1101,where +FN:1111,andWhere +FN:1123,through +FN:1129,as +FN:1135,value +FN:1140,value +FN:1152,modelFind FNF:112 FNH:108 FNDA:1,(anonymous_1) @@ -491,27 +491,27 @@ FNDA:2,defineProperties FNDA:2,(anonymous_3) FNDA:5,_interopRequireDefault FNDA:5,_toConsumableArray -FNDA:4808,_classCallCheck +FNDA:4892,_classCallCheck FNDA:1,(anonymous_7) -FNDA:1197,Model -FNDA:2394,(anonymous_9) -FNDA:83,get +FNDA:1215,Model +FNDA:2430,(anonymous_9) +FNDA:87,get FNDA:1,set -FNDA:109,get +FNDA:113,get FNDA:1,set FNDA:21,get -FNDA:620,hasOne -FNDA:798,belongsTo -FNDA:2193,hasMany -FNDA:619,ensure -FNDA:12,isValid -FNDA:12,(anonymous_20) -FNDA:18,invalidAttributes -FNDA:18,compileInvalidAttributeList -FNDA:10,performValidationsForAttribute -FNDA:10,performValidation -FNDA:10,(anonymous_25) -FNDA:10,compileValidatorResponses +FNDA:626,hasOne +FNDA:814,belongsTo +FNDA:2237,hasMany +FNDA:631,ensure +FNDA:14,isValid +FNDA:14,(anonymous_20) +FNDA:22,invalidAttributes +FNDA:22,compileInvalidAttributeList +FNDA:14,performValidationsForAttribute +FNDA:14,performValidation +FNDA:14,(anonymous_25) +FNDA:14,compileValidatorResponses FNDA:16,include FNDA:31,fetch FNDA:31,value @@ -549,60 +549,60 @@ FNDA:4,(anonymous_60) FNDA:7,(anonymous_61) FNDA:7,(anonymous_62) FNDA:7,(anonymous_63) -FNDA:11,save -FNDA:10,(anonymous_65) -FNDA:10,(anonymous_66) -FNDA:10,(anonymous_67) -FNDA:2,(anonymous_68) +FNDA:13,save +FNDA:12,(anonymous_65) +FNDA:12,(anonymous_66) +FNDA:12,(anonymous_67) +FNDA:4,(anonymous_68) FNDA:8,(anonymous_69) FNDA:8,(anonymous_70) FNDA:5,(anonymous_71) FNDA:8,(anonymous_72) FNDA:8,(anonymous_73) FNDA:8,(anonymous_74) -FNDA:10,(anonymous_75) -FNDA:10,beforeValidation +FNDA:12,(anonymous_75) +FNDA:12,beforeValidation FNDA:8,beforeSave FNDA:8,afterSave -FNDA:306,associate -FNDA:611,validate -FNDA:1170,initialize +FNDA:308,associate +FNDA:617,validate +FNDA:1188,initialize FNDA:2,toJSON -FNDA:1198,value -FNDA:98,value +FNDA:1216,value +FNDA:102,value FNDA:18,value FNDA:2,value FNDA:17,value FNDA:137,(anonymous_88) -FNDA:28,value +FNDA:32,value FNDA:15,value FNDA:18,(anonymous_91) FNDA:3,(anonymous_92) FNDA:15,(anonymous_93) -FNDA:3611,value -FNDA:651,(anonymous_95) -FNDA:866,(anonymous_96) -FNDA:2193,(anonymous_97) -FNDA:257,get -FNDA:1220,value +FNDA:3677,value +FNDA:657,(anonymous_95) +FNDA:882,(anonymous_96) +FNDA:2237,(anonymous_97) +FNDA:261,get +FNDA:1238,value FNDA:8,value FNDA:15,(anonymous_101) FNDA:12,(anonymous_102) FNDA:1,(anonymous_103) -FNDA:3611,AssociationSetter +FNDA:3677,AssociationSetter FNDA:0,foreignName -FNDA:388,where +FNDA:392,where FNDA:3,andWhere -FNDA:1293,through -FNDA:260,as -FNDA:261,value +FNDA:1317,through +FNDA:268,as +FNDA:269,value FNDA:6,value FNDA:11,modelFind DA:3,1 DA:7,35 DA:9,5 DA:11,10 -DA:13,4808 +DA:13,4892 DA:18,1 DA:20,1 DA:22,1 @@ -617,70 +617,70 @@ DA:38,1 DA:41,1 DA:57,1 DA:63,1 -DA:64,1197 -DA:66,1197 -DA:68,1197 -DA:69,2394 -DA:78,1197 -DA:113,83 +DA:64,1215 +DA:66,1215 +DA:68,1215 +DA:69,2430 +DA:78,1215 +DA:113,87 DA:116,1 -DA:127,109 +DA:127,113 DA:130,1 DA:142,21 -DA:147,1197 -DA:148,1197 -DA:150,1197 -DA:152,1197 +DA:147,1215 +DA:148,1215 +DA:150,1215 +DA:152,1215 DA:155,1 -DA:158,620 -DA:167,798 -DA:176,2193 -DA:185,619 -DA:187,619 -DA:189,619 +DA:158,626 +DA:167,814 +DA:176,2237 +DA:185,631 +DA:187,631 +DA:189,631 DA:190,3 -DA:193,619 -DA:205,12 -DA:206,12 -DA:226,18 -DA:228,18 -DA:230,18 -DA:231,18 +DA:193,631 +DA:205,14 +DA:206,14 +DA:226,22 +DA:228,22 +DA:230,22 +DA:231,22 DA:232,0 -DA:234,18 -DA:236,18 -DA:237,10 -DA:239,10 -DA:240,8 -DA:241,8 -DA:245,18 -DA:249,18 -DA:250,10 -DA:252,10 -DA:253,10 -DA:255,10 -DA:256,10 +DA:234,22 +DA:236,22 +DA:237,14 +DA:239,14 +DA:240,12 +DA:241,12 +DA:245,22 +DA:249,22 +DA:250,14 +DA:252,14 +DA:253,14 +DA:255,14 +DA:256,14 DA:257,2 -DA:259,8 -DA:264,10 -DA:265,10 -DA:267,10 -DA:268,10 -DA:269,10 -DA:271,10 -DA:272,10 -DA:273,10 -DA:275,10 +DA:259,12 +DA:264,14 +DA:265,14 +DA:267,14 +DA:268,14 +DA:269,14 +DA:271,14 +DA:272,14 +DA:273,14 +DA:275,14 DA:278,0 DA:279,0 -DA:281,10 -DA:282,10 +DA:281,14 +DA:282,14 DA:283,0 -DA:286,10 +DA:286,14 DA:287,0 -DA:292,10 -DA:295,10 -DA:298,18 +DA:292,14 +DA:295,14 +DA:298,22 DA:303,16 DA:304,18 DA:307,16 @@ -831,203 +831,201 @@ DA:632,6 DA:636,7 DA:639,1 DA:642,1 -DA:648,11 -DA:650,11 +DA:648,13 +DA:650,13 DA:651,1 -DA:654,10 -DA:655,10 -DA:657,10 -DA:658,10 +DA:654,12 +DA:655,12 +DA:657,12 +DA:658,12 DA:659,8 -DA:661,2 -DA:662,2 -DA:664,2 -DA:665,2 -DA:666,2 -DA:667,2 -DA:669,2 -DA:671,2 -DA:672,2 -DA:673,2 -DA:674,2 -DA:675,2 -DA:677,2 -DA:679,2 -DA:681,0 -DA:687,8 -DA:689,8 -DA:690,5 +DA:661,4 +DA:662,4 +DA:664,4 +DA:665,4 +DA:666,4 +DA:667,4 +DA:668,4 +DA:670,4 +DA:671,4 +DA:672,4 +DA:673,4 +DA:676,4 +DA:678,0 +DA:684,8 +DA:686,8 +DA:687,5 +DA:688,5 +DA:689,5 DA:691,5 DA:692,5 -DA:694,5 +DA:693,0 DA:695,5 -DA:696,0 -DA:698,5 -DA:699,5 +DA:696,5 +DA:700,3 +DA:701,3 +DA:702,3 DA:703,3 -DA:704,3 DA:705,3 -DA:706,3 -DA:708,3 -DA:709,6 -DA:710,3 -DA:714,3 -DA:719,8 -DA:720,8 +DA:706,6 +DA:707,3 +DA:711,3 +DA:716,8 +DA:717,8 +DA:719,2 +DA:721,2 DA:722,2 -DA:724,2 -DA:725,2 -DA:727,0 -DA:730,4 -DA:734,8 -DA:736,10 -DA:737,2 -DA:739,8 -DA:748,10 -DA:753,8 -DA:758,8 -DA:772,2 -DA:773,1 -DA:775,1 -DA:786,1198 -DA:791,98 -DA:796,18 -DA:801,2 +DA:724,0 +DA:727,4 +DA:731,8 +DA:733,12 +DA:734,4 +DA:736,8 +DA:745,12 +DA:750,8 +DA:755,8 +DA:769,2 +DA:770,1 +DA:772,1 +DA:783,1216 +DA:788,102 +DA:793,18 +DA:798,2 +DA:803,17 +DA:805,17 DA:806,17 -DA:808,17 -DA:809,17 -DA:810,137 -DA:811,53 -DA:814,17 -DA:819,28 -DA:820,15 -DA:822,13 -DA:837,15 -DA:839,15 -DA:841,15 -DA:843,18 -DA:845,18 -DA:848,10 -DA:849,10 -DA:851,9 -DA:852,9 -DA:853,3 -DA:855,6 -DA:858,1 -DA:860,10 -DA:863,8 -DA:865,8 -DA:867,8 -DA:868,3 -DA:869,3 -DA:870,3 -DA:872,0 -DA:876,0 -DA:878,8 -DA:881,15 -DA:902,3611 -DA:904,3611 -DA:912,3611 -DA:915,2813 -DA:916,2813 -DA:917,2813 -DA:919,798 -DA:920,798 -DA:924,3611 -DA:928,3611 -DA:932,3611 -DA:936,3611 -DA:938,3611 -DA:940,620 -DA:942,620 -DA:943,651 -DA:944,26 -DA:945,1 -DA:948,25 -DA:950,25 -DA:953,620 -DA:955,798 -DA:957,798 -DA:958,866 -DA:959,62 -DA:960,1 +DA:807,137 +DA:808,53 +DA:811,17 +DA:816,32 +DA:817,19 +DA:819,13 +DA:834,15 +DA:836,15 +DA:838,15 +DA:840,18 +DA:842,18 +DA:845,10 +DA:846,10 +DA:848,9 +DA:849,9 +DA:850,3 +DA:852,6 +DA:855,1 +DA:857,10 +DA:860,8 +DA:862,8 +DA:864,8 +DA:865,3 +DA:866,3 +DA:867,3 +DA:869,0 +DA:873,0 +DA:875,8 +DA:878,15 +DA:899,3677 +DA:901,3677 +DA:909,3677 +DA:912,2863 +DA:913,2863 +DA:914,2863 +DA:916,814 +DA:917,814 +DA:921,3677 +DA:925,3677 +DA:929,3677 +DA:933,3677 +DA:935,3677 +DA:937,626 +DA:939,626 +DA:940,657 +DA:941,26 +DA:942,1 +DA:945,25 +DA:947,25 +DA:950,626 +DA:952,814 +DA:954,814 +DA:955,882 +DA:956,62 +DA:957,1 +DA:960,61 +DA:961,61 DA:963,61 -DA:964,61 -DA:966,61 -DA:968,61 -DA:969,18 -DA:970,7 -DA:971,11 -DA:973,10 -DA:975,1 -DA:986,798 -DA:989,2193 -DA:991,2193 -DA:992,2193 -DA:993,0 -DA:996,2193 -DA:998,0 -DA:1001,3611 -DA:1006,3611 -DA:1010,257 -DA:1014,3611 -DA:1016,3611 -DA:1018,3611 -DA:1023,1220 -DA:1024,717 +DA:965,61 +DA:966,18 +DA:967,7 +DA:968,11 +DA:970,10 +DA:972,1 +DA:983,814 +DA:986,2237 +DA:988,2237 +DA:989,2237 +DA:990,0 +DA:993,2237 +DA:995,0 +DA:998,3677 +DA:1003,3677 +DA:1007,261 +DA:1011,3677 +DA:1013,3677 +DA:1015,3677 +DA:1020,1238 +DA:1021,725 +DA:1027,8 +DA:1029,8 DA:1030,8 -DA:1032,8 -DA:1033,8 -DA:1034,8 -DA:1035,15 -DA:1039,8 -DA:1040,12 -DA:1041,12 -DA:1042,12 -DA:1044,4 -DA:1045,4 -DA:1049,0 -DA:1050,0 -DA:1054,8 -DA:1055,8 -DA:1059,8 +DA:1031,8 +DA:1032,15 +DA:1036,8 +DA:1037,12 +DA:1038,12 +DA:1039,12 +DA:1041,4 +DA:1042,4 +DA:1046,0 +DA:1047,0 +DA:1051,8 +DA:1052,8 +DA:1056,8 +DA:1060,1 DA:1063,1 -DA:1066,1 +DA:1065,1 DA:1068,1 -DA:1071,1 -DA:1072,1 -DA:1073,3611 -DA:1075,3611 -DA:1077,3611 -DA:1079,798 -DA:1084,798 -DA:1087,2813 -DA:1092,2813 -DA:1096,1 -DA:1099,0 -DA:1100,0 -DA:1105,388 -DA:1106,966 -DA:1109,388 -DA:1110,388 -DA:1115,3 -DA:1117,3 -DA:1118,6 -DA:1121,3 -DA:1122,3 -DA:1127,1293 -DA:1128,1293 -DA:1133,260 -DA:1134,260 -DA:1139,261 -DA:1144,6 +DA:1069,1 +DA:1070,3677 +DA:1072,3677 +DA:1074,3677 +DA:1076,814 +DA:1081,814 +DA:1084,2863 +DA:1089,2863 +DA:1093,1 +DA:1096,0 +DA:1097,0 +DA:1102,392 +DA:1103,976 +DA:1106,392 +DA:1107,392 +DA:1112,3 +DA:1114,3 +DA:1115,6 +DA:1118,3 +DA:1119,3 +DA:1124,1317 +DA:1125,1317 +DA:1130,268 +DA:1131,268 +DA:1136,269 +DA:1141,6 +DA:1145,1 DA:1148,1 -DA:1151,1 -DA:1153,1 -DA:1156,11 -DA:1157,11 -LF:428 -LH:402 +DA:1150,1 +DA:1153,11 +DA:1154,11 +LF:426 +LH:400 BRDA:7,1,0,35 BRDA:7,1,1,35 BRDA:7,2,0,35 @@ -1043,31 +1041,31 @@ BRDA:9,6,1,5 BRDA:11,7,0,5 BRDA:11,7,1,0 BRDA:13,8,0,0 -BRDA:13,8,1,4808 -BRDA:113,9,0,83 -BRDA:113,9,1,82 -BRDA:127,10,0,109 -BRDA:127,10,1,108 -BRDA:185,11,0,619 -BRDA:185,11,1,618 +BRDA:13,8,1,4892 +BRDA:113,9,0,87 +BRDA:113,9,1,86 +BRDA:127,10,0,113 +BRDA:127,10,1,112 +BRDA:185,11,0,631 +BRDA:185,11,1,630 BRDA:189,12,0,3 -BRDA:189,12,1,616 +BRDA:189,12,1,628 BRDA:231,13,0,0 -BRDA:231,13,1,18 -BRDA:239,14,0,8 +BRDA:231,13,1,22 +BRDA:239,14,0,12 BRDA:239,14,1,2 BRDA:256,15,0,2 -BRDA:256,15,1,8 -BRDA:259,16,0,8 -BRDA:259,16,1,6 -BRDA:275,17,0,10 -BRDA:275,17,1,8 +BRDA:256,15,1,12 +BRDA:259,16,0,12 +BRDA:259,16,1,10 +BRDA:275,17,0,14 +BRDA:275,17,1,12 BRDA:282,18,0,0 -BRDA:282,18,1,10 -BRDA:282,19,0,10 +BRDA:282,18,1,14 +BRDA:282,19,0,14 BRDA:282,19,1,0 BRDA:286,20,0,0 -BRDA:286,20,1,10 +BRDA:286,20,1,14 BRDA:317,21,0,5 BRDA:317,21,1,20 BRDA:317,21,2,6 @@ -1134,83 +1132,83 @@ BRDA:627,50,1,7 BRDA:629,51,0,1 BRDA:629,51,1,6 BRDA:650,52,0,1 -BRDA:650,52,1,10 +BRDA:650,52,1,12 BRDA:658,53,0,8 -BRDA:658,53,1,2 -BRDA:664,54,0,2 +BRDA:658,53,1,4 +BRDA:664,54,0,4 BRDA:664,54,1,0 -BRDA:689,55,0,5 -BRDA:689,55,1,3 -BRDA:695,56,0,0 -BRDA:695,56,1,5 -BRDA:709,57,0,3 -BRDA:709,57,1,3 -BRDA:720,58,0,2 -BRDA:720,58,1,2 -BRDA:720,58,2,4 -BRDA:724,59,0,2 -BRDA:724,59,1,0 -BRDA:736,60,0,2 -BRDA:736,60,1,8 -BRDA:772,61,0,1 -BRDA:772,61,1,1 -BRDA:772,62,0,2 -BRDA:772,62,1,1 -BRDA:810,63,0,53 -BRDA:810,63,1,84 -BRDA:819,64,0,15 -BRDA:819,64,1,13 -BRDA:845,65,0,7 -BRDA:845,65,1,10 -BRDA:845,65,2,8 -BRDA:849,66,0,9 -BRDA:849,66,1,1 -BRDA:852,67,0,3 -BRDA:852,67,1,6 -BRDA:865,68,0,8 -BRDA:865,68,1,0 -BRDA:869,69,0,3 -BRDA:869,69,1,0 -BRDA:912,70,0,620 -BRDA:912,70,1,2813 -BRDA:912,70,2,798 -BRDA:938,71,0,620 -BRDA:938,71,1,798 -BRDA:938,71,2,2193 -BRDA:938,71,3,0 -BRDA:943,72,0,26 -BRDA:943,72,1,625 -BRDA:943,73,0,651 -BRDA:943,73,1,30 -BRDA:944,74,0,1 -BRDA:944,74,1,25 -BRDA:958,75,0,62 -BRDA:958,75,1,804 -BRDA:958,76,0,866 -BRDA:958,76,1,66 -BRDA:959,77,0,1 -BRDA:959,77,1,61 -BRDA:968,78,0,18 -BRDA:968,78,1,43 -BRDA:969,79,0,7 -BRDA:969,79,1,11 -BRDA:971,80,0,10 -BRDA:971,80,1,1 -BRDA:992,81,0,0 -BRDA:992,81,1,2193 -BRDA:992,82,0,2193 -BRDA:992,82,1,0 -BRDA:1042,83,0,4 -BRDA:1042,83,1,8 -BRDA:1044,84,0,4 -BRDA:1044,84,1,0 -BRDA:1044,85,0,4 -BRDA:1044,85,1,4 -BRDA:1077,86,0,798 -BRDA:1077,86,1,620 -BRDA:1077,86,2,2813 -BRDA:1115,87,0,3 -BRDA:1115,87,1,3 +BRDA:686,55,0,5 +BRDA:686,55,1,3 +BRDA:692,56,0,0 +BRDA:692,56,1,5 +BRDA:706,57,0,3 +BRDA:706,57,1,3 +BRDA:717,58,0,2 +BRDA:717,58,1,2 +BRDA:717,58,2,4 +BRDA:721,59,0,2 +BRDA:721,59,1,0 +BRDA:733,60,0,4 +BRDA:733,60,1,8 +BRDA:769,61,0,1 +BRDA:769,61,1,1 +BRDA:769,62,0,2 +BRDA:769,62,1,1 +BRDA:807,63,0,53 +BRDA:807,63,1,84 +BRDA:816,64,0,19 +BRDA:816,64,1,13 +BRDA:842,65,0,7 +BRDA:842,65,1,10 +BRDA:842,65,2,8 +BRDA:846,66,0,9 +BRDA:846,66,1,1 +BRDA:849,67,0,3 +BRDA:849,67,1,6 +BRDA:862,68,0,8 +BRDA:862,68,1,0 +BRDA:866,69,0,3 +BRDA:866,69,1,0 +BRDA:909,70,0,626 +BRDA:909,70,1,2863 +BRDA:909,70,2,814 +BRDA:935,71,0,626 +BRDA:935,71,1,814 +BRDA:935,71,2,2237 +BRDA:935,71,3,0 +BRDA:940,72,0,26 +BRDA:940,72,1,631 +BRDA:940,73,0,657 +BRDA:940,73,1,30 +BRDA:941,74,0,1 +BRDA:941,74,1,25 +BRDA:955,75,0,62 +BRDA:955,75,1,820 +BRDA:955,76,0,882 +BRDA:955,76,1,66 +BRDA:956,77,0,1 +BRDA:956,77,1,61 +BRDA:965,78,0,18 +BRDA:965,78,1,43 +BRDA:966,79,0,7 +BRDA:966,79,1,11 +BRDA:968,80,0,10 +BRDA:968,80,1,1 +BRDA:989,81,0,0 +BRDA:989,81,1,2237 +BRDA:989,82,0,2237 +BRDA:989,82,1,0 +BRDA:1039,83,0,4 +BRDA:1039,83,1,8 +BRDA:1041,84,0,4 +BRDA:1041,84,1,0 +BRDA:1041,85,0,4 +BRDA:1041,85,1,4 +BRDA:1074,86,0,814 +BRDA:1074,86,1,626 +BRDA:1074,86,2,2863 +BRDA:1112,87,0,3 +BRDA:1112,87,1,3 BRF:183 BRH:159 end_of_record @@ -1223,8 +1221,8 @@ FN:98,(anonymous_4) FNF:4 FNH:4 FNDA:1,_interopRequireDefault -FNDA:24,isPresent -FNDA:7,(anonymous_3) +FNDA:28,isPresent +FNDA:11,(anonymous_3) FNDA:2,(anonymous_4) DA:3,1 DA:6,1 @@ -1232,38 +1230,38 @@ DA:8,1 DA:10,1 DA:12,1 DA:14,1 -DA:15,24 -DA:16,24 -DA:18,24 +DA:15,28 +DA:16,28 +DA:18,28 DA:19,0 -DA:23,24 -DA:24,24 -DA:26,24 -DA:28,24 -DA:33,24 -DA:35,24 -DA:36,16 +DA:23,28 +DA:24,28 +DA:26,28 +DA:28,28 +DA:33,28 +DA:35,28 +DA:36,20 DA:39,2 DA:40,2 DA:41,2 -DA:43,14 -DA:44,14 +DA:43,18 +DA:44,18 DA:45,4 DA:46,4 -DA:48,10 +DA:48,14 DA:50,3 DA:51,3 -DA:53,7 -DA:54,7 +DA:53,11 +DA:54,11 DA:55,0 DA:56,0 -DA:58,7 -DA:60,7 +DA:58,11 +DA:60,11 DA:61,1 DA:62,1 -DA:64,6 -DA:65,6 -DA:71,14 +DA:64,10 +DA:65,10 +DA:71,16 DA:73,0 DA:76,8 DA:77,4 @@ -1302,21 +1300,21 @@ BRDA:8,1,1,0 BRDA:8,2,0,1 BRDA:8,2,1,1 BRDA:18,3,0,0 -BRDA:18,3,1,24 -BRDA:35,4,0,16 +BRDA:18,3,1,28 +BRDA:35,4,0,20 BRDA:35,4,1,8 BRDA:36,5,0,1 BRDA:36,5,1,2 -BRDA:36,5,2,14 +BRDA:36,5,2,18 BRDA:36,5,3,0 BRDA:44,6,0,4 -BRDA:44,6,1,10 +BRDA:44,6,1,14 BRDA:48,7,0,3 -BRDA:48,7,1,7 +BRDA:48,7,1,11 BRDA:54,8,0,0 -BRDA:54,8,1,7 +BRDA:54,8,1,11 BRDA:60,9,0,1 -BRDA:60,9,1,6 +BRDA:60,9,1,10 BRDA:76,10,0,4 BRDA:76,10,1,4 BRDA:78,11,0,2 diff --git a/package.json b/package.json index 9011fd4..7b9de69 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dovima", - "version": "0.1.0", + "version": "0.1.1", "description": "ES6 generic model with lots of useful special features.", "main": "index.js", "scripts": { From edf0127e6cc78eb32ab33b387680bf2e84a31a56 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Fri, 10 Jul 2015 15:31:05 -0300 Subject: [PATCH 10/15] map parallel change --- es5/lib/model.js | 2 +- es6/lib/model.js | 2 +- package.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/es5/lib/model.js b/es5/lib/model.js index b41bf48..2facb26 100644 --- a/es5/lib/model.js +++ b/es5/lib/model.js @@ -295,7 +295,7 @@ var Model = (function () { flowsync.mapParallel(attributeValidations, performValidation, compileValidatorResponses); }; - flowsync.mapSeries(attributeNamesWithValidators, performValidationsForAttribute, compileInvalidAttributeList); + flowsync.mapParallel(attributeNamesWithValidators, performValidationsForAttribute, compileInvalidAttributeList); } }, { key: "include", diff --git a/es6/lib/model.js b/es6/lib/model.js index 8a92e7a..30ed537 100644 --- a/es6/lib/model.js +++ b/es6/lib/model.js @@ -233,7 +233,7 @@ export default class Model { ); }; - flowsync.mapSeries( + flowsync.mapParallel( attributeNamesWithValidators, performValidationsForAttribute, compileInvalidAttributeList diff --git a/package.json b/package.json index 7b9de69..1b5765b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dovima", - "version": "0.1.1", + "version": "0.1.2", "description": "ES6 generic model with lots of useful special features.", "main": "index.js", "scripts": { From 0455f91bbbccbb1e66323c6f5fc1516ef54b9f63 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Fri, 10 Jul 2015 15:48:17 -0300 Subject: [PATCH 11/15] npm autopublish and generator refresh --- .karma.conf.js | 20 +------------------- .travis.yml | 9 +++++---- package.json | 2 +- 3 files changed, 7 insertions(+), 24 deletions(-) diff --git a/.karma.conf.js b/.karma.conf.js index c4985e5..2012f6f 100644 --- a/.karma.conf.js +++ b/.karma.conf.js @@ -64,25 +64,7 @@ let configOptions = { /** * Special Options For SauceLabs */ -if (process.env.TRAVIS_BUILD_NUMBER && process.env.SAUCE_USERNAME) { - /** - * If SauceLabs credentials are available, - * set up the tests to run through them. - */ - configOptions.sauceLabs = { - testName: "Dovima.js" - }; - const customLaunchers = require("./.sauce.json").platforms; - configOptions.customLaunchers = customLaunchers; - configOptions.browsers = Object.keys(customLaunchers); - reporters.push("saucelabs"); -} else { - /** - * If there are no SauceLabs credentials available, - * detect the browsers that we *can* use. - */ - frameworks.push("detectBrowsers"); -} +frameworks.push("detectBrowsers"); module.exports = function(config) { // level of logging diff --git a/.travis.yml b/.travis.yml index f088f4e..4321d8a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,7 +10,8 @@ before_script: - npm install after_success: - ./node_modules/coveralls/bin/coveralls.js < ./lcov.info -env: - global: - - secure: i1pENwoJlg0NEyl42AYoOcC6fqA//3FXpLjsaIxx5U1485ikz7KuZ6ejYHmvbQxu1HSy+E4dgri9nUZSexrwyDU4ZB3uc9RjI+BkDerJaa9uWqjLUdPEDgkqGayJc5rhz25u7ykND4EOYoU9Q2vqgjqj7hJxmEzW5g/VStsUcV0BP8Xef2WJ4ubt3+8/f9/EZYBugr5LV0STp/a3A52/QyV+Td8+/TsUiReidFlUCiXB+6IVMRxdLX380iWvs2AKcIntW5MTFh6EyUGqUjXfJTZUUZibOsT+QwMEntKl7jydWPIxcwB+MSqYUBgreivvpN75MZbUfT919FoOQAKqndv9nNOFIPq+7vGiraxzgaHcbY/oSI1mkA+OhG0JW3gQXhvjDlPpDHJHIFovExk+G4/N7wEwvPFAuRB2SZN2WHPVp5sFHZ2hcom6vB//9YIgCLI1sfffp3wuuTqB3Psv3QYZhvOrSJ75fmPTpGLWrLf5WqL0IngsJUse9zT3NDTZF/GzKd2SUeWyhXsIIvSHHJLE/EWsR9eRUhE11JtdIhrbX+sLSoXdT54xYbF5ZYVDfe40VetUu1mpCLp83Z0RLqLgAGBEt4fqOrXEbqCnmLdIa1PTKYvrexwtPbIemI3g5up/L3WSxRUY6XxNVvA5e/P/hQNZo946v0QB5TI8tuk= - - secure: ERQRWonb1ZOGDcgTSkZxoHzyGLPrrftyyfQez4tEdUQQikzMk+dwAT8OiLe9slLQ6JqbvKXY8M3mrFD3hlfkIcoZw2plMDhB6ocw0oiD7yKDFGgO3Ff6a9x4gDC2oslDpXxZ3anOyF6M0XyHHJbYy1/XbMdJ6cgLXQoOIYZXxTBmFD/6RKsV0TBKPWCV1eQ+5uuaPeSNpfKoUgohFvFtLflMgDNTyhkoaH0/siK+ThUi745ycnAoclaIf60hea6mN8fddnxKkyckbi2WfXkKtXrl718q6exK5Ba8JXWJiLL+VtT4FB3KTZTKP46eMHRL/LWXd58lLdRyTn1W1qST95tn8koEJgDBXPG2WUPByMCyoEiYCLn9wp+AlTJqUEVdiB4C6ajvNTF5k6wmXvP3AWjbkLphKxCqLmCQ3o0rkwjNC4KB0cVeLBZYEfQMq0wq/hnlixrgYT81/hd/OxcS79zQPa7eblOsBNO3U7goi4CKjOqPyWqAK3Wi3JwyG+0B8YBiSakMwdZrz9Rin0DdmGOyQgNz972OwaFyng5B6EdYr2YkS6iRckPsCAT8OXPtVGl7kXC50zTiUY2wb4SLIbwZAJxV9VhGX9Izt7+1X4xUWxNbwDkDOir02Hg6i6ZcAqykf2B7jJKnINIUGzPjmadC1Gf7WiOTZzxUC0Je2Ro= +deploy: + provider: npm + email: fam-operations@freeallmedia.com + api_key: + - secure: t5bz+k2T767nMbK5bGoSrejELRTPx+ZODsfe4gU7TbHqW3zVGfkAWflnZhpzoLgsLKmk8i19MdvbKLiMueA4LGcY35mVWk/Q9HCyVic91GBjGCVxOoNE2H6siq3pBbR6kZgUUoZeEA63G/2Vtrx1JfksxvE91WgbfoTQifGgQ5Lo0yeGtInSNvP7xvMlnqRvEwHHWM+EzJgwCURGEP8/J+jgSImDyQZVE0hbkhyfXtOiRjspvh95W2ziz/xVU6Ba60unvIqhKbrLo2Iqnw23XzgjbvOEtzNs6xcySb32eiOB8iYAxUWTQApux1onZSOe0AktjioQQRBf9OTKfYVZPJToaPBkpYBA4QhriPIINl3ytpIqNql0NGG0lSrCuDmMMxuur0AqpiF2I9GaQCZGIyt5fcYpNaK83Olkn6QlqCg79kbjkF2KJl7qsPRqQ0Qc1ZXFG2SCzGjbhswcw3Oc5/lF5+V8T/0DnX6cLqWf9H+QDx39wSqC1aacBdOejb/K/QcLzIvyQAwyEfLpxfxZXBhUR2Ajyk1fBMT6jnuJPefYf7doMQgBHvNtI0JDms4Pew6wyGLodP3JwoxpAM4qETB00SLptpGaa9e0I6LnZU2vZI7OMOM+SSTaboHvivHkRmd0WaS8PEoW85pvUjknMa35pooYFGs0GXQPJtpXWKw= diff --git a/package.json b/package.json index 1b5765b..56953a1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dovima", - "version": "0.1.2", + "version": "0.1.3", "description": "ES6 generic model with lots of useful special features.", "main": "index.js", "scripts": { From 22b8230da5875132cfb8172d6dd35043204013d9 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Fri, 10 Jul 2015 20:03:25 -0300 Subject: [PATCH 12/15] inc version and reset associationId on setting the model --- es5/lib/model.js | 66 ++++- es5/spec/model.spec.js | 45 +++- es6/lib/model.js | 78 ++++-- es6/spec/model.spec.js | 48 +++- lcov.info | 579 +++++++++++++++++++++-------------------- package.json | 2 +- 6 files changed, 509 insertions(+), 309 deletions(-) diff --git a/es5/lib/model.js b/es5/lib/model.js index 2facb26..1b8e1fa 100644 --- a/es5/lib/model.js +++ b/es5/lib/model.js @@ -928,14 +928,24 @@ var Model = (function () { var privateAssociationName = "_" + associationDetails.name; + var implicitAssociationName = associationDetails.name + "Id"; + var privateImplicitAssociationName = "_" + implicitAssociationName; + /* Association Setter By Type */ - var setterFunction = undefined; + var setterFunction = undefined, + implicitSetterFunction = undefined; switch (associationDetails.type) { case "hasOne": this[privateAssociationName] = null; + implicitSetterFunction = function (newId) { + //reset the association when assign associationId + _this9[privateImplicitAssociationName] = newId; + _this9[privateAssociationName] = null; + }; + setterFunction = function (newModel) { if (newModel && _this9[privateAssociationName] !== newModel) { if (!(newModel instanceof Model)) { @@ -943,6 +953,7 @@ var Model = (function () { } _this9[privateAssociationName] = newModel; + _this9[privateImplicitAssociationName] = newModel.id; newModel[association.foreignName] = _this9; } @@ -951,6 +962,12 @@ var Model = (function () { case "belongsTo": this[privateAssociationName] = null; + implicitSetterFunction = function (newId) { + //reset the association when assign associationId + _this9[privateImplicitAssociationName] = newId; + _this9[privateAssociationName] = null; + }; + setterFunction = function (newModel) { if (newModel && _this9[privateAssociationName] !== newModel) { if (!(newModel instanceof Model)) { @@ -958,7 +975,7 @@ var Model = (function () { } _this9[privateAssociationName] = newModel; - _this9[association.foreignId] = newModel.id; + _this9[privateImplicitAssociationName] = newModel.id; var pluralForeignName = (0, _jargon2["default"])(association.foreignName).plural.toString(); @@ -995,18 +1012,53 @@ var Model = (function () { throw new Error("Unknown association type."); } - Object.defineProperty(this, privateAssociationName, { + var newProperties = {}; + newProperties[privateAssociationName] = { enumerable: false, writable: true - }); - - Object.defineProperty(this, associationDetails.name, { + }; + newProperties[associationDetails.name] = { enumerable: true, set: setterFunction, get: function get() { return _this9[privateAssociationName]; } - }); + }; + if (implicitSetterFunction) { + newProperties[privateImplicitAssociationName] = { + enumerable: false, + writable: true + }; + newProperties[implicitAssociationName] = { + enumerable: true, + set: implicitSetterFunction, + get: function get() { + return _this9[privateImplicitAssociationName]; + } + }; + } + Object.defineProperties(this, newProperties); + + // Object.defineProperty( + // this, + // privateAssociationName, + // { + // enumerable: false, + // writable: true + // } + // ); + + // Object.defineProperty( + // this, + // associationDetails.name, + // { + // enumerable: true, + // set: setterFunction, + // get: () => { + // return this[privateAssociationName]; + // } + // } + // ); this[associationDetails.name] = associationDetails.value; diff --git a/es5/spec/model.spec.js b/es5/spec/model.spec.js index d08ccf7..da2d58c 100644 --- a/es5/spec/model.spec.js +++ b/es5/spec/model.spec.js @@ -263,7 +263,10 @@ describe("Model(attributes, options)", function () { id: 1, name: "Bob Builder", age: 35, - hasChildren: false + hasChildren: false, + addressId: undefined, + primaryPhotoId: undefined, + postalCodeId: undefined }; user = new User(userAttributes); @@ -302,7 +305,7 @@ describe("Model(attributes, options)", function () { describe(".properties", function () { it("should return the name of all attributes plus associations on the model", function () { - user.properties.should.eql(["address", "postalCode", "photos", "primaryPhoto", "photoLikes", "likedPhotos", "comments", "deletedComments", "id", "name", "age", "hasChildren"]); + user.properties.should.eql(["address", "addressId", "postalCode", "postalCodeId", "photos", "primaryPhoto", "primaryPhotoId", "photoLikes", "likedPhotos", "comments", "deletedComments", "id", "name", "age", "hasChildren"]); }); }); @@ -666,6 +669,20 @@ describe("Model(attributes, options)", function () { wheel.truckId.should.eql(truck.id); }); + it("should reset the association when a new id is set onto the model", function () { + truck.id = 1; + wheel.truck = truck; + wheel.truckId = 2; + (wheel.truck == null).should.be["true"]; + }); + + it("should reset the associationId when a new model is set onto the model", function () { + truck.id = 1; + wheel.truckId = 2; + wheel.truck = truck; + wheel.truckId.should.not.equal(2); + }); + it("should add a model just once on the parent", function () { truck.id = 1; wheel.truck = truck; @@ -771,6 +788,24 @@ describe("Model(attributes, options)", function () { truck.hasOne("steeringWheel", SteeringWheel).should.be.instanceOf(_libModelJs.AssociationSetter); }); + it("should reset the association when a new id is set onto the model", function () { + truck.hasOne("steeringWheel", SteeringWheel); + var steeringWheel = new SteeringWheel(); + steeringWheel.id = 1; + truck.steeringWheel = steeringWheel; + truck.steeringWheelId = 2; + (truck.steeringWheel == null).should.be["true"]; + }); + + it("should reset the associationId when a new model is set onto the model", function () { + truck.hasOne("steeringWheel", SteeringWheel); + var steeringWheel = new SteeringWheel(); + steeringWheel.id = 1; + truck.steeringWheelId = 2; + truck.steeringWheel = steeringWheel; + truck.steeringWheelId.should.not.equal(2); + }); + it("should accept a custom error message", function () { truck.hasOne("steeringWheel", SteeringWheel); truck.ensure("steeringWheel", _.isPresent, "must be there."); @@ -1755,11 +1790,12 @@ describe("Model(attributes, options)", function () { insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00\.000', 1\)/, insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', 1\)/, insertTruck: /insert into `trucks` (`created_at`) values ('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-24]:00:00.000')/, - updateTruck: /update `trucks` set `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, + updateTruck: /update `trucks` set `steering_wheel_id` = 1, `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, + updateSteeringWheel: /update `steering_wheels` set `truck_id` = 1, `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, updateWheels: /update `wheels` set `created_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/ }; - _libModelJs2["default"].database.mock((_Model$database$mock5 = {}, _defineProperty(_Model$database$mock5, regularExpressions.insertPhotos, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateUser, []), _defineProperty(_Model$database$mock5, regularExpressions.insertWheels, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertSteeringWheel, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateWheels, [1]), _Model$database$mock5)); + _libModelJs2["default"].database.mock((_Model$database$mock5 = {}, _defineProperty(_Model$database$mock5, regularExpressions.insertPhotos, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateUser, []), _defineProperty(_Model$database$mock5, regularExpressions.insertWheels, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertSteeringWheel, [1]), _defineProperty(_Model$database$mock5, regularExpressions.insertTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateTruck, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateSteeringWheel, [1]), _defineProperty(_Model$database$mock5, regularExpressions.updateWheels, [1]), _Model$database$mock5)); }); describe("(association operations)", function () { @@ -1965,6 +2001,7 @@ describe("Model(attributes, options)", function () { truck = new Truck({ id: 1 }); wheel = new Wheel(); steeringWheel = new SteeringWheel(); + steeringWheel.id = 1; truck.steeringWheel = steeringWheel; diff --git a/es6/lib/model.js b/es6/lib/model.js index 30ed537..8dee09d 100644 --- a/es6/lib/model.js +++ b/es6/lib/model.js @@ -870,14 +870,24 @@ export default class Model { const privateAssociationName = "_" + associationDetails.name; + const implicitAssociationName = `${associationDetails.name}Id`; + const privateImplicitAssociationName = `_${implicitAssociationName}`; + /* Association Setter By Type */ - let setterFunction; + let setterFunction, + implicitSetterFunction; switch(associationDetails.type) { case "hasOne": this[privateAssociationName] = null; + implicitSetterFunction = (newId) => { + //reset the association when assign associationId + this[privateImplicitAssociationName] = newId; + this[privateAssociationName] = null; + } + setterFunction = (newModel) => { if (newModel && this[privateAssociationName] !== newModel) { if(!(newModel instanceof Model)) { @@ -885,6 +895,7 @@ export default class Model { } this[privateAssociationName] = newModel; + this[privateImplicitAssociationName] = newModel.id; newModel[association.foreignName] = this; } @@ -893,6 +904,12 @@ export default class Model { case "belongsTo": this[privateAssociationName] = null; + implicitSetterFunction = (newId) => { + //reset the association when assign associationId + this[privateImplicitAssociationName] = newId; + this[privateAssociationName] = null; + } + setterFunction = (newModel) => { if (newModel && this[privateAssociationName] !== newModel) { if(!(newModel instanceof Model)) { @@ -900,7 +917,7 @@ export default class Model { } this[privateAssociationName] = newModel; - this[association.foreignId] = newModel.id; + this[privateImplicitAssociationName] = newModel.id; const pluralForeignName = inflect(association.foreignName).plural.toString(); @@ -937,26 +954,53 @@ export default class Model { throw new Error("Unknown association type."); } - Object.defineProperty( - this, - privateAssociationName, - { + let newProperties = {}; + newProperties[privateAssociationName] = { + enumerable: false, + writable: true + }; + newProperties[associationDetails.name] = { + enumerable: true, + set: setterFunction, + get: () => { + return this[privateAssociationName]; + } + } + if(implicitSetterFunction) { + newProperties[privateImplicitAssociationName] = { enumerable: false, writable: true - } - ); - - Object.defineProperty( - this, - associationDetails.name, - { + }; + newProperties[implicitAssociationName] = { enumerable: true, - set: setterFunction, + set: implicitSetterFunction, get: () => { - return this[privateAssociationName]; + return this[privateImplicitAssociationName]; } - } - ); + }; + } + Object.defineProperties(this, newProperties); + + // Object.defineProperty( + // this, + // privateAssociationName, + // { + // enumerable: false, + // writable: true + // } + // ); + + // Object.defineProperty( + // this, + // associationDetails.name, + // { + // enumerable: true, + // set: setterFunction, + // get: () => { + // return this[privateAssociationName]; + // } + // } + // ); this[associationDetails.name] = associationDetails.value; diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index e616e19..fe75399 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -147,7 +147,10 @@ describe("Model(attributes, options)", () => { id: 1, name: "Bob Builder", age: 35, - hasChildren: false + hasChildren: false, + addressId: undefined, + primaryPhotoId: undefined, + postalCodeId: undefined }; user = new User(userAttributes); @@ -186,9 +189,12 @@ describe("Model(attributes, options)", () => { it("should return the name of all attributes plus associations on the model", () => { user.properties.should.eql([ "address", + "addressId", "postalCode", + "postalCodeId", "photos", "primaryPhoto", + "primaryPhotoId", "photoLikes", "likedPhotos", "comments", @@ -496,6 +502,20 @@ describe("Model(attributes, options)", () => { wheel.truckId.should.eql(truck.id); }); + it("should reset the association when a new id is set onto the model", () => { + truck.id = 1; + wheel.truck = truck; + wheel.truckId = 2; + (wheel.truck == null).should.be.true; + }); + + it("should reset the associationId when a new model is set onto the model", () => { + truck.id = 1; + wheel.truckId = 2; + wheel.truck = truck; + wheel.truckId.should.not.equal(2); + }); + it("should add a model just once on the parent", () => { truck.id = 1; wheel.truck = truck; @@ -603,6 +623,26 @@ describe("Model(attributes, options)", () => { .should.be.instanceOf(AssociationSetter); }); + it("should reset the association when a new id is set onto the model", () => { + truck + .hasOne("steeringWheel", SteeringWheel); + let steeringWheel = new SteeringWheel(); + steeringWheel.id = 1; + truck.steeringWheel = steeringWheel; + truck.steeringWheelId = 2; + (truck.steeringWheel == null).should.be.true; + }); + + it("should reset the associationId when a new model is set onto the model", () => { + truck + .hasOne("steeringWheel", SteeringWheel); + let steeringWheel = new SteeringWheel(); + steeringWheel.id = 1; + truck.steeringWheelId = 2; + truck.steeringWheel = steeringWheel; + truck.steeringWheelId.should.not.equal(2); + }); + it("should accept a custom error message", () => { truck.hasOne("steeringWheel", SteeringWheel); truck.ensure("steeringWheel", isPresent, "must be there."); @@ -1637,7 +1677,8 @@ describe("Model(attributes, options)", () => { insertWheels: /insert into `wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00\.000', 1\)/, insertSteeringWheel: /insert into `steering_wheels` \(`created_at`, `truck_id`\) values \('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', 1\)/, insertTruck: /insert into `trucks` (`created_at`) values ('19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-24]:00:00.000')/, - updateTruck: /update `trucks` set `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, + updateTruck: /update `trucks` set `steering_wheel_id` = 1, `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, + updateSteeringWheel: /update `steering_wheels` set `truck_id` = 1, `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/, updateWheels: /update `wheels` set `created_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000', `truck_id` = 1, `updated_at` = '19[0-9][0-9]-[0-9][0-9]-[0-9][0-9] [0-9][0-9]:00:00.000' where `id` = 1/ }; @@ -1654,6 +1695,8 @@ describe("Model(attributes, options)", () => { [1], [regularExpressions.updateTruck]: [1], + [regularExpressions.updateSteeringWheel]: + [1], [regularExpressions.updateWheels]: [1] }); @@ -1782,6 +1825,7 @@ describe("Model(attributes, options)", () => { truck = new Truck({id: 1}); wheel = new Wheel(); steeringWheel = new SteeringWheel(); + steeringWheel.id = 1; truck.steeringWheel = steeringWheel; diff --git a/lcov.info b/lcov.info index 492c2f0..2f4455a 100644 --- a/lcov.info +++ b/lcov.info @@ -25,15 +25,15 @@ FNH:20 FNDA:1,(anonymous_1) FNDA:1,defineProperties FNDA:1,(anonymous_3) -FNDA:2419,get +FNDA:2479,get FNDA:3,_interopRequireDefault FNDA:3,_toConsumableArray -FNDA:2266,_classCallCheck +FNDA:2324,_classCallCheck FNDA:1,_inherits FNDA:1,(anonymous_9) -FNDA:2266,Collection -FNDA:158,push -FNDA:158,(anonymous_12) +FNDA:2324,Collection +FNDA:160,push +FNDA:160,(anonymous_12) FNDA:4,fetch FNDA:4,(anonymous_14) FNDA:6,processWhereCondition @@ -44,10 +44,10 @@ FNDA:4,(anonymous_19) FNDA:13,(anonymous_20) DA:3,1 DA:7,2 -DA:9,2419 +DA:9,2479 DA:11,3 DA:13,6 -DA:15,2266 +DA:15,2324 DA:17,1 DA:19,1 DA:21,1 @@ -57,25 +57,25 @@ DA:27,1 DA:29,1 DA:31,1 DA:32,1 -DA:33,2266 -DA:35,2266 -DA:37,2266 -DA:40,2266 +DA:33,2324 +DA:35,2324 +DA:37,2324 +DA:40,2324 DA:41,29 -DA:43,2237 -DA:46,2266 +DA:43,2295 +DA:46,2324 DA:59,1 DA:61,1 -DA:64,158 -DA:66,158 -DA:67,158 -DA:70,158 -DA:71,158 -DA:72,153 -DA:73,63 -DA:74,63 -DA:75,63 -DA:76,39 +DA:64,160 +DA:66,160 +DA:67,160 +DA:70,160 +DA:71,160 +DA:72,155 +DA:73,65 +DA:74,65 +DA:75,65 +DA:76,41 DA:77,31 DA:79,24 DA:80,8 @@ -127,12 +127,12 @@ BRDA:7,3,1,0 BRDA:7,4,0,0 BRDA:7,4,1,1 BRDA:9,5,0,0 -BRDA:9,5,1,2419 +BRDA:9,5,1,2479 BRDA:9,6,0,0 -BRDA:9,6,1,2419 +BRDA:9,6,1,2479 BRDA:9,7,0,0 BRDA:9,7,1,0 -BRDA:9,8,0,2419 +BRDA:9,8,0,2479 BRDA:9,8,1,0 BRDA:9,9,0,0 BRDA:9,9,1,0 @@ -143,7 +143,7 @@ BRDA:11,11,1,3 BRDA:13,12,0,3 BRDA:13,12,1,0 BRDA:15,13,0,0 -BRDA:15,13,1,2266 +BRDA:15,13,1,2324 BRDA:17,14,0,0 BRDA:17,14,1,1 BRDA:17,15,0,1 @@ -153,17 +153,17 @@ BRDA:17,16,1,1 BRDA:17,17,0,1 BRDA:17,17,1,0 BRDA:40,18,0,29 -BRDA:40,18,1,2237 -BRDA:40,19,0,2266 -BRDA:40,19,1,2266 -BRDA:71,20,0,153 +BRDA:40,18,1,2295 +BRDA:40,19,0,2324 +BRDA:40,19,1,2324 +BRDA:71,20,0,155 BRDA:71,20,1,5 -BRDA:72,21,0,63 +BRDA:72,21,0,65 BRDA:72,21,1,90 -BRDA:75,22,0,39 +BRDA:75,22,0,41 BRDA:75,22,1,24 BRDA:76,23,0,31 -BRDA:76,23,1,8 +BRDA:76,23,1,10 BRDA:79,24,0,8 BRDA:79,24,1,16 BRDA:83,25,0,90 @@ -466,44 +466,47 @@ FN:838,(anonymous_91) FN:864,(anonymous_92) FN:877,(anonymous_93) FN:898,value -FN:939,(anonymous_95) -FN:954,(anonymous_96) -FN:988,(anonymous_97) -FN:1006,get -FN:1019,value -FN:1026,value -FN:1031,(anonymous_101) -FN:1036,(anonymous_102) -FN:1068,(anonymous_103) -FN:1069,AssociationSetter -FN:1095,foreignName -FN:1101,where -FN:1111,andWhere -FN:1123,through -FN:1129,as -FN:1135,value -FN:1140,value -FN:1152,modelFind -FNF:112 -FNH:108 +FN:943,(anonymous_95) +FN:949,(anonymous_96) +FN:965,(anonymous_97) +FN:971,(anonymous_98) +FN:1005,(anonymous_99) +FN:1023,get +FN:1035,get +FN:1071,value +FN:1078,value +FN:1083,(anonymous_104) +FN:1088,(anonymous_105) +FN:1120,(anonymous_106) +FN:1121,AssociationSetter +FN:1147,foreignName +FN:1153,where +FN:1163,andWhere +FN:1175,through +FN:1181,as +FN:1187,value +FN:1192,value +FN:1204,modelFind +FNF:115 +FNH:111 FNDA:1,(anonymous_1) FNDA:2,defineProperties FNDA:2,(anonymous_3) FNDA:5,_interopRequireDefault FNDA:5,_toConsumableArray -FNDA:4892,_classCallCheck +FNDA:5042,_classCallCheck FNDA:1,(anonymous_7) -FNDA:1215,Model -FNDA:2430,(anonymous_9) +FNDA:1265,Model +FNDA:2530,(anonymous_9) FNDA:87,get FNDA:1,set -FNDA:113,get +FNDA:123,get FNDA:1,set FNDA:21,get -FNDA:626,hasOne -FNDA:814,belongsTo -FNDA:2237,hasMany -FNDA:631,ensure +FNDA:642,hasOne +FNDA:840,belongsTo +FNDA:2295,hasMany +FNDA:647,ensure FNDA:14,isValid FNDA:14,(anonymous_20) FNDA:22,invalidAttributes @@ -556,7 +559,7 @@ FNDA:12,(anonymous_67) FNDA:4,(anonymous_68) FNDA:8,(anonymous_69) FNDA:8,(anonymous_70) -FNDA:5,(anonymous_71) +FNDA:3,(anonymous_71) FNDA:8,(anonymous_72) FNDA:8,(anonymous_73) FNDA:8,(anonymous_74) @@ -564,45 +567,48 @@ FNDA:12,(anonymous_75) FNDA:12,beforeValidation FNDA:8,beforeSave FNDA:8,afterSave -FNDA:308,associate -FNDA:617,validate -FNDA:1188,initialize +FNDA:334,associate +FNDA:651,validate +FNDA:1238,initialize FNDA:2,toJSON -FNDA:1216,value +FNDA:1266,value FNDA:102,value FNDA:18,value FNDA:2,value FNDA:17,value -FNDA:137,(anonymous_88) +FNDA:168,(anonymous_88) FNDA:32,value FNDA:15,value FNDA:18,(anonymous_91) FNDA:3,(anonymous_92) FNDA:15,(anonymous_93) -FNDA:3677,value -FNDA:657,(anonymous_95) -FNDA:882,(anonymous_96) -FNDA:2237,(anonymous_97) -FNDA:261,get -FNDA:1238,value +FNDA:3777,value +FNDA:386,(anonymous_95) +FNDA:675,(anonymous_96) +FNDA:13,(anonymous_97) +FNDA:910,(anonymous_98) +FNDA:2295,(anonymous_99) +FNDA:267,get +FNDA:87,get +FNDA:1288,value FNDA:8,value -FNDA:15,(anonymous_101) -FNDA:12,(anonymous_102) -FNDA:1,(anonymous_103) -FNDA:3677,AssociationSetter +FNDA:19,(anonymous_104) +FNDA:12,(anonymous_105) +FNDA:1,(anonymous_106) +FNDA:3777,AssociationSetter FNDA:0,foreignName -FNDA:392,where +FNDA:400,where FNDA:3,andWhere -FNDA:1317,through -FNDA:268,as -FNDA:269,value +FNDA:1349,through +FNDA:276,as +FNDA:277,value FNDA:6,value FNDA:11,modelFind DA:3,1 DA:7,35 DA:9,5 DA:11,10 -DA:13,4892 +DA:13,5042 DA:18,1 DA:20,1 DA:22,1 @@ -617,29 +623,29 @@ DA:38,1 DA:41,1 DA:57,1 DA:63,1 -DA:64,1215 -DA:66,1215 -DA:68,1215 -DA:69,2430 -DA:78,1215 +DA:64,1265 +DA:66,1265 +DA:68,1265 +DA:69,2530 +DA:78,1265 DA:113,87 DA:116,1 -DA:127,113 +DA:127,123 DA:130,1 DA:142,21 -DA:147,1215 -DA:148,1215 -DA:150,1215 -DA:152,1215 +DA:147,1265 +DA:148,1265 +DA:150,1265 +DA:152,1265 DA:155,1 -DA:158,626 -DA:167,814 -DA:176,2237 -DA:185,631 -DA:187,631 -DA:189,631 +DA:158,642 +DA:167,840 +DA:176,2295 +DA:185,647 +DA:187,647 +DA:189,647 DA:190,3 -DA:193,631 +DA:193,647 DA:205,14 DA:206,14 DA:226,22 @@ -854,22 +860,22 @@ DA:676,4 DA:678,0 DA:684,8 DA:686,8 -DA:687,5 -DA:688,5 -DA:689,5 -DA:691,5 -DA:692,5 +DA:687,3 +DA:688,3 +DA:689,3 +DA:691,3 +DA:692,3 DA:693,0 -DA:695,5 -DA:696,5 -DA:700,3 -DA:701,3 -DA:702,3 -DA:703,3 -DA:705,3 -DA:706,6 -DA:707,3 -DA:711,3 +DA:695,3 +DA:696,3 +DA:700,5 +DA:701,5 +DA:702,5 +DA:703,5 +DA:705,5 +DA:706,14 +DA:707,9 +DA:711,5 DA:716,8 DA:717,8 DA:719,2 @@ -887,19 +893,19 @@ DA:755,8 DA:769,2 DA:770,1 DA:772,1 -DA:783,1216 +DA:783,1266 DA:788,102 DA:793,18 DA:798,2 DA:803,17 DA:805,17 DA:806,17 -DA:807,137 -DA:808,53 +DA:807,168 +DA:808,84 DA:811,17 DA:816,32 -DA:817,19 -DA:819,13 +DA:817,21 +DA:819,11 DA:834,15 DA:836,15 DA:838,15 @@ -923,109 +929,124 @@ DA:869,0 DA:873,0 DA:875,8 DA:878,15 -DA:899,3677 -DA:901,3677 -DA:909,3677 -DA:912,2863 -DA:913,2863 -DA:914,2863 -DA:916,814 -DA:917,814 -DA:921,3677 -DA:925,3677 -DA:929,3677 -DA:933,3677 -DA:935,3677 -DA:937,626 -DA:939,626 -DA:940,657 -DA:941,26 -DA:942,1 -DA:945,25 -DA:947,25 -DA:950,626 -DA:952,814 -DA:954,814 -DA:955,882 -DA:956,62 -DA:957,1 -DA:960,61 -DA:961,61 -DA:963,61 -DA:965,61 -DA:966,18 -DA:967,7 -DA:968,11 -DA:970,10 -DA:972,1 -DA:983,814 -DA:986,2237 -DA:988,2237 -DA:989,2237 -DA:990,0 -DA:993,2237 -DA:995,0 -DA:998,3677 -DA:1003,3677 -DA:1007,261 -DA:1011,3677 -DA:1013,3677 -DA:1015,3677 -DA:1020,1238 -DA:1021,725 -DA:1027,8 -DA:1029,8 -DA:1030,8 -DA:1031,8 -DA:1032,15 -DA:1036,8 -DA:1037,12 -DA:1038,12 -DA:1039,12 -DA:1041,4 -DA:1042,4 -DA:1046,0 -DA:1047,0 -DA:1051,8 -DA:1052,8 -DA:1056,8 -DA:1060,1 -DA:1063,1 -DA:1065,1 -DA:1068,1 -DA:1069,1 -DA:1070,3677 -DA:1072,3677 -DA:1074,3677 -DA:1076,814 -DA:1081,814 -DA:1084,2863 -DA:1089,2863 -DA:1093,1 -DA:1096,0 -DA:1097,0 -DA:1102,392 -DA:1103,976 -DA:1106,392 -DA:1107,392 -DA:1112,3 -DA:1114,3 -DA:1115,6 -DA:1118,3 -DA:1119,3 -DA:1124,1317 -DA:1125,1317 -DA:1130,268 -DA:1131,268 -DA:1136,269 -DA:1141,6 +DA:899,3777 +DA:901,3777 +DA:909,3777 +DA:912,2937 +DA:913,2937 +DA:914,2937 +DA:916,840 +DA:917,840 +DA:921,3777 +DA:925,3777 +DA:929,3777 +DA:931,3777 +DA:932,3777 +DA:936,3777 +DA:939,3777 +DA:941,642 +DA:943,642 +DA:945,386 +DA:946,386 +DA:949,642 +DA:950,675 +DA:951,28 +DA:952,1 +DA:955,27 +DA:956,27 +DA:958,27 +DA:961,642 +DA:963,840 +DA:965,840 +DA:967,13 +DA:968,13 +DA:971,840 +DA:972,910 +DA:973,64 +DA:974,1 +DA:977,63 +DA:978,63 +DA:980,63 +DA:982,63 +DA:983,20 +DA:984,7 +DA:985,13 +DA:987,12 +DA:989,1 +DA:1000,840 +DA:1003,2295 +DA:1005,2295 +DA:1006,2295 +DA:1007,0 +DA:1010,2295 +DA:1012,0 +DA:1015,3777 +DA:1016,3777 +DA:1020,3777 +DA:1024,267 +DA:1027,3777 +DA:1028,1482 +DA:1032,1482 +DA:1036,87 +DA:1040,3777 +DA:1063,3777 +DA:1065,3777 +DA:1067,3777 +DA:1072,1288 +DA:1073,1125 +DA:1079,8 +DA:1081,8 +DA:1082,8 +DA:1083,8 +DA:1084,19 +DA:1088,8 +DA:1089,12 +DA:1090,12 +DA:1091,12 +DA:1093,4 +DA:1094,4 +DA:1098,0 +DA:1099,0 +DA:1103,8 +DA:1104,8 +DA:1108,8 +DA:1112,1 +DA:1115,1 +DA:1117,1 +DA:1120,1 +DA:1121,1 +DA:1122,3777 +DA:1124,3777 +DA:1126,3777 +DA:1128,840 +DA:1133,840 +DA:1136,2937 +DA:1141,2937 DA:1145,1 -DA:1148,1 -DA:1150,1 -DA:1153,11 -DA:1154,11 -LF:426 -LH:400 +DA:1148,0 +DA:1149,0 +DA:1154,400 +DA:1155,996 +DA:1158,400 +DA:1159,400 +DA:1164,3 +DA:1166,3 +DA:1167,6 +DA:1170,3 +DA:1171,3 +DA:1176,1349 +DA:1177,1349 +DA:1182,276 +DA:1183,276 +DA:1188,277 +DA:1193,6 +DA:1197,1 +DA:1200,1 +DA:1202,1 +DA:1205,11 +DA:1206,11 +LF:441 +LH:415 BRDA:7,1,0,35 BRDA:7,1,1,35 BRDA:7,2,0,35 @@ -1041,15 +1062,15 @@ BRDA:9,6,1,5 BRDA:11,7,0,5 BRDA:11,7,1,0 BRDA:13,8,0,0 -BRDA:13,8,1,4892 +BRDA:13,8,1,5042 BRDA:113,9,0,87 BRDA:113,9,1,86 -BRDA:127,10,0,113 -BRDA:127,10,1,112 -BRDA:185,11,0,631 -BRDA:185,11,1,630 +BRDA:127,10,0,123 +BRDA:127,10,1,122 +BRDA:185,11,0,647 +BRDA:185,11,1,646 BRDA:189,12,0,3 -BRDA:189,12,1,628 +BRDA:189,12,1,644 BRDA:231,13,0,0 BRDA:231,13,1,22 BRDA:239,14,0,12 @@ -1137,12 +1158,12 @@ BRDA:658,53,0,8 BRDA:658,53,1,4 BRDA:664,54,0,4 BRDA:664,54,1,0 -BRDA:686,55,0,5 -BRDA:686,55,1,3 +BRDA:686,55,0,3 +BRDA:686,55,1,5 BRDA:692,56,0,0 -BRDA:692,56,1,5 -BRDA:706,57,0,3 -BRDA:706,57,1,3 +BRDA:692,56,1,3 +BRDA:706,57,0,9 +BRDA:706,57,1,5 BRDA:717,58,0,2 BRDA:717,58,1,2 BRDA:717,58,2,4 @@ -1154,10 +1175,10 @@ BRDA:769,61,0,1 BRDA:769,61,1,1 BRDA:769,62,0,2 BRDA:769,62,1,1 -BRDA:807,63,0,53 +BRDA:807,63,0,84 BRDA:807,63,1,84 -BRDA:816,64,0,19 -BRDA:816,64,1,13 +BRDA:816,64,0,21 +BRDA:816,64,1,11 BRDA:842,65,0,7 BRDA:842,65,1,10 BRDA:842,65,2,8 @@ -1169,48 +1190,50 @@ BRDA:862,68,0,8 BRDA:862,68,1,0 BRDA:866,69,0,3 BRDA:866,69,1,0 -BRDA:909,70,0,626 -BRDA:909,70,1,2863 -BRDA:909,70,2,814 -BRDA:935,71,0,626 -BRDA:935,71,1,814 -BRDA:935,71,2,2237 -BRDA:935,71,3,0 -BRDA:940,72,0,26 -BRDA:940,72,1,631 -BRDA:940,73,0,657 -BRDA:940,73,1,30 -BRDA:941,74,0,1 -BRDA:941,74,1,25 -BRDA:955,75,0,62 -BRDA:955,75,1,820 -BRDA:955,76,0,882 -BRDA:955,76,1,66 -BRDA:956,77,0,1 -BRDA:956,77,1,61 -BRDA:965,78,0,18 -BRDA:965,78,1,43 -BRDA:966,79,0,7 -BRDA:966,79,1,11 -BRDA:968,80,0,10 -BRDA:968,80,1,1 -BRDA:989,81,0,0 -BRDA:989,81,1,2237 -BRDA:989,82,0,2237 -BRDA:989,82,1,0 -BRDA:1039,83,0,4 -BRDA:1039,83,1,8 -BRDA:1041,84,0,4 -BRDA:1041,84,1,0 -BRDA:1041,85,0,4 -BRDA:1041,85,1,4 -BRDA:1074,86,0,814 -BRDA:1074,86,1,626 -BRDA:1074,86,2,2863 -BRDA:1112,87,0,3 -BRDA:1112,87,1,3 -BRF:183 -BRH:159 +BRDA:909,70,0,642 +BRDA:909,70,1,2937 +BRDA:909,70,2,840 +BRDA:939,71,0,642 +BRDA:939,71,1,840 +BRDA:939,71,2,2295 +BRDA:939,71,3,0 +BRDA:950,72,0,28 +BRDA:950,72,1,647 +BRDA:950,73,0,675 +BRDA:950,73,1,32 +BRDA:951,74,0,1 +BRDA:951,74,1,27 +BRDA:972,75,0,64 +BRDA:972,75,1,846 +BRDA:972,76,0,910 +BRDA:972,76,1,68 +BRDA:973,77,0,1 +BRDA:973,77,1,63 +BRDA:982,78,0,20 +BRDA:982,78,1,43 +BRDA:983,79,0,7 +BRDA:983,79,1,13 +BRDA:985,80,0,12 +BRDA:985,80,1,1 +BRDA:1006,81,0,0 +BRDA:1006,81,1,2295 +BRDA:1006,82,0,2295 +BRDA:1006,82,1,0 +BRDA:1027,83,0,1482 +BRDA:1027,83,1,2295 +BRDA:1091,84,0,4 +BRDA:1091,84,1,8 +BRDA:1093,85,0,4 +BRDA:1093,85,1,0 +BRDA:1093,86,0,4 +BRDA:1093,86,1,4 +BRDA:1126,87,0,840 +BRDA:1126,87,1,642 +BRDA:1126,87,2,2937 +BRDA:1164,88,0,3 +BRDA:1164,88,1,3 +BRF:185 +BRH:161 end_of_record TN: SF:/home/intag/workspace/fam/dovima/es5/lib/validation/isPresent.js diff --git a/package.json b/package.json index 56953a1..4670af7 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dovima", - "version": "0.1.3", + "version": "0.1.4", "description": "ES6 generic model with lots of useful special features.", "main": "index.js", "scripts": { From 1edf8635d908a57f9a562e15f4ce73fa7ffd5031 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Mon, 13 Jul 2015 15:10:25 -0300 Subject: [PATCH 13/15] add snake case on other params --- es5/lib/modelFinder.js | 29 +- es5/spec/model.spec.js | 77 ++++++ es6/lib/modelFinder.js | 22 +- es6/spec/model.spec.js | 92 +++++++ lcov.info | 603 +++++++++++++++++++++-------------------- package.json | 2 +- 6 files changed, 509 insertions(+), 316 deletions(-) diff --git a/es5/lib/modelFinder.js b/es5/lib/modelFinder.js index 8e165f2..e26058a 100644 --- a/es5/lib/modelFinder.js +++ b/es5/lib/modelFinder.js @@ -20,7 +20,8 @@ var _collectionJs = require("./collection.js"); var _collectionJs2 = _interopRequireDefault(_collectionJs); -var validateDependencies = Symbol(); +var validateDependencies = Symbol(), + transformNames = Symbol(); var ModelFinder = (function () { function ModelFinder(database) { @@ -128,22 +129,26 @@ var ModelQuery = (function () { return this; } }, { - key: "where", - value: function where() { - var _query; - + key: transformNames, + value: function value() { for (var _len = arguments.length, options = Array(_len), _key = 0; _key < _len; _key++) { options[_key] = arguments[_key]; } - var formattedOptions = options.map(function (option, index) { + return options.map(function (option, index) { if (typeof option === "string" && index === 0) { return (0, _jargon2["default"])(option).snake.toString(); } else { return option; } }); + } + }, { + key: "where", + value: function where() { + var _query; + var formattedOptions = this[transformNames].apply(this, arguments); (_query = this._query).where.apply(_query, _toConsumableArray(formattedOptions)); return this; } @@ -152,7 +157,8 @@ var ModelQuery = (function () { value: function andWhere() { var _query2; - (_query2 = this._query).andWhere.apply(_query2, arguments); + var formattedOptions = this[transformNames].apply(this, arguments); + (_query2 = this._query).andWhere.apply(_query2, _toConsumableArray(formattedOptions)); return this; } }, { @@ -160,7 +166,8 @@ var ModelQuery = (function () { value: function orWhere() { var _query3; - (_query3 = this._query).orWhere.apply(_query3, arguments); + var formattedOptions = this[transformNames].apply(this, arguments); + (_query3 = this._query).orWhere.apply(_query3, _toConsumableArray(formattedOptions)); return this; } }, { @@ -168,7 +175,8 @@ var ModelQuery = (function () { value: function groupBy() { var _query4; - (_query4 = this._query).groupBy.apply(_query4, arguments); + var formattedOptions = this[transformNames].apply(this, arguments); + (_query4 = this._query).groupBy.apply(_query4, _toConsumableArray(formattedOptions)); return this; } }, { @@ -176,7 +184,8 @@ var ModelQuery = (function () { value: function orderBy() { var _query5; - (_query5 = this._query).orderBy.apply(_query5, arguments); + var formattedOptions = this[transformNames].apply(this, arguments); + (_query5 = this._query).orderBy.apply(_query5, _toConsumableArray(formattedOptions)); return this; } }, { diff --git a/es5/spec/model.spec.js b/es5/spec/model.spec.js index da2d58c..dfd1558 100644 --- a/es5/spec/model.spec.js +++ b/es5/spec/model.spec.js @@ -375,6 +375,83 @@ describe("Model(attributes, options)", function () { User.find.should.be.instanceOf(_libModelFinderJs.ModelQuery); }); + describe("(ModelQuery operations)", function () { + describe(".where", function () { + var querySpy = undefined; + + beforeEach(function (done) { + querySpy = _libModelJs2["default"].database.spy("select * from `users` where `some_id` = 1", [1]); + User.find.where("someId", 1).results(function () { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", function () { + querySpy.callCount.should.equal(1); + }); + }); + + describe(".andWhere", function () { + var querySpy = undefined; + + beforeEach(function (done) { + querySpy = _libModelJs2["default"].database.spy("select * from `users` where `some_id` = 1", [1]); + User.find.andWhere("someId", 1).results(function () { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", function () { + querySpy.callCount.should.equal(1); + }); + }); + + describe(".orWhere", function () { + var querySpy = undefined; + + beforeEach(function (done) { + querySpy = _libModelJs2["default"].database.spy("select * from `users` where `some_id` = 1", [1]); + User.find.orWhere("someId", 1).results(function () { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", function () { + querySpy.callCount.should.equal(1); + }); + }); + + describe(".groupBy", function () { + var querySpy = undefined; + + beforeEach(function (done) { + querySpy = _libModelJs2["default"].database.spy("select * from `users` group by `some_id`", [1]); + User.find.groupBy("someId").results(function () { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", function () { + querySpy.callCount.should.equal(1); + }); + }); + + describe(".orderBy", function () { + var querySpy = undefined; + + beforeEach(function (done) { + querySpy = _libModelJs2["default"].database.spy("select * from `users` order by `some_id` asc", [1]); + User.find.orderBy("someId").results(function () { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", function () { + querySpy.callCount.should.equal(1); + }); + }); + }); + it("should return a collection", function () { users.should.be.instanceOf(_libCollectionJs2["default"]); }); diff --git a/es6/lib/modelFinder.js b/es6/lib/modelFinder.js index 9bc5394..954c593 100644 --- a/es6/lib/modelFinder.js +++ b/es6/lib/modelFinder.js @@ -1,4 +1,5 @@ -const validateDependencies = Symbol(); +const validateDependencies = Symbol(), + transformNames = Symbol(); import inflect from "jargon"; export default class ModelFinder { @@ -92,36 +93,43 @@ export class ModelQuery { return this; } - where(...options) { - const formattedOptions = options.map((option, index) => { + [transformNames](...options) { + return options.map((option, index) => { if (typeof option === "string" && index === 0) { return inflect(option).snake.toString(); } else { return option; } }); + } + where(...options) { + const formattedOptions = this[transformNames](...options); this._query.where(...formattedOptions); return this; } andWhere(...options) { - this._query.andWhere(...options); + const formattedOptions = this[transformNames](...options); + this._query.andWhere(...formattedOptions); return this; } orWhere(...options) { - this._query.orWhere(...options); + const formattedOptions = this[transformNames](...options); + this._query.orWhere(...formattedOptions); return this; } groupBy(...options) { - this._query.groupBy(...options); + const formattedOptions = this[transformNames](...options); + this._query.groupBy(...formattedOptions); return this; } orderBy(...options) { - this._query.orderBy(...options); + const formattedOptions = this[transformNames](...options); + this._query.orderBy(...formattedOptions); return this; } diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index fe75399..68a55e5 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -278,6 +278,98 @@ describe("Model(attributes, options)", () => { User.find.should.be.instanceOf(ModelQuery); }); + describe("(ModelQuery operations)", () => { + describe(".where", () => { + let querySpy; + + beforeEach(done => { + querySpy = Model.database.spy("select * from `users` where `some_id` = 1", [1]); + User + .find + .where("someId", 1) + .results(() => { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", () => { + querySpy.callCount.should.equal(1); + }); + }); + + describe(".andWhere", () => { + let querySpy; + + beforeEach(done => { + querySpy = Model.database.spy("select * from `users` where `some_id` = 1", [1]); + User + .find + .andWhere("someId", 1) + .results(() => { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", () => { + querySpy.callCount.should.equal(1); + }); + }); + + describe(".orWhere", () => { + let querySpy; + + beforeEach(done => { + querySpy = Model.database.spy("select * from `users` where `some_id` = 1", [1]); + User + .find + .orWhere("someId", 1) + .results(() => { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", () => { + querySpy.callCount.should.equal(1); + }); + }); + + describe(".groupBy", () => { + let querySpy; + + beforeEach(done => { + querySpy = Model.database.spy("select * from `users` group by `some_id`", [1]); + User + .find + .groupBy("someId") + .results(() => { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", () => { + querySpy.callCount.should.equal(1); + }); + }); + + describe(".orderBy", () => { + let querySpy; + + beforeEach(done => { + querySpy = Model.database.spy("select * from `users` order by `some_id` asc", [1]); + User + .find + .orderBy("someId") + .results(() => { + done(); + }); + }); + + it("should convert camel case names to snake case names on a where", () => { + querySpy.callCount.should.equal(1); + }); + }); + }); + it("should return a collection", () => { users.should.be.instanceOf(Collection); }); diff --git a/lcov.info b/lcov.info index 2f4455a..93d57d2 100644 --- a/lcov.info +++ b/lcov.info @@ -25,15 +25,15 @@ FNH:20 FNDA:1,(anonymous_1) FNDA:1,defineProperties FNDA:1,(anonymous_3) -FNDA:2479,get +FNDA:2789,get FNDA:3,_interopRequireDefault FNDA:3,_toConsumableArray -FNDA:2324,_classCallCheck +FNDA:2604,_classCallCheck FNDA:1,_inherits FNDA:1,(anonymous_9) -FNDA:2324,Collection -FNDA:160,push -FNDA:160,(anonymous_12) +FNDA:2604,Collection +FNDA:190,push +FNDA:190,(anonymous_12) FNDA:4,fetch FNDA:4,(anonymous_14) FNDA:6,processWhereCondition @@ -44,10 +44,10 @@ FNDA:4,(anonymous_19) FNDA:13,(anonymous_20) DA:3,1 DA:7,2 -DA:9,2479 +DA:9,2789 DA:11,3 DA:13,6 -DA:15,2324 +DA:15,2604 DA:17,1 DA:19,1 DA:21,1 @@ -57,21 +57,21 @@ DA:27,1 DA:29,1 DA:31,1 DA:32,1 -DA:33,2324 -DA:35,2324 -DA:37,2324 -DA:40,2324 -DA:41,29 -DA:43,2295 -DA:46,2324 +DA:33,2604 +DA:35,2604 +DA:37,2604 +DA:40,2604 +DA:41,39 +DA:43,2565 +DA:46,2604 DA:59,1 DA:61,1 -DA:64,160 -DA:66,160 -DA:67,160 -DA:70,160 -DA:71,160 -DA:72,155 +DA:64,190 +DA:66,190 +DA:67,190 +DA:70,190 +DA:71,190 +DA:72,185 DA:73,65 DA:74,65 DA:75,65 @@ -79,8 +79,8 @@ DA:76,41 DA:77,31 DA:79,24 DA:80,8 -DA:83,90 -DA:84,90 +DA:83,120 +DA:84,120 DA:86,0 DA:87,0 DA:88,0 @@ -127,12 +127,12 @@ BRDA:7,3,1,0 BRDA:7,4,0,0 BRDA:7,4,1,1 BRDA:9,5,0,0 -BRDA:9,5,1,2479 +BRDA:9,5,1,2789 BRDA:9,6,0,0 -BRDA:9,6,1,2479 +BRDA:9,6,1,2789 BRDA:9,7,0,0 BRDA:9,7,1,0 -BRDA:9,8,0,2479 +BRDA:9,8,0,2789 BRDA:9,8,1,0 BRDA:9,9,0,0 BRDA:9,9,1,0 @@ -143,7 +143,7 @@ BRDA:11,11,1,3 BRDA:13,12,0,3 BRDA:13,12,1,0 BRDA:15,13,0,0 -BRDA:15,13,1,2324 +BRDA:15,13,1,2604 BRDA:17,14,0,0 BRDA:17,14,1,1 BRDA:17,15,0,1 @@ -152,21 +152,21 @@ BRDA:17,16,0,1 BRDA:17,16,1,1 BRDA:17,17,0,1 BRDA:17,17,1,0 -BRDA:40,18,0,29 -BRDA:40,18,1,2295 -BRDA:40,19,0,2324 -BRDA:40,19,1,2324 -BRDA:71,20,0,155 +BRDA:40,18,0,39 +BRDA:40,18,1,2565 +BRDA:40,19,0,2604 +BRDA:40,19,1,2604 +BRDA:71,20,0,185 BRDA:71,20,1,5 BRDA:72,21,0,65 -BRDA:72,21,1,90 +BRDA:72,21,1,120 BRDA:75,22,0,41 BRDA:75,22,1,24 BRDA:76,23,0,31 BRDA:76,23,1,10 BRDA:79,24,0,8 BRDA:79,24,1,16 -BRDA:83,25,0,90 +BRDA:83,25,0,120 BRDA:83,25,1,0 BRDA:87,26,0,0 BRDA:87,26,1,0 @@ -191,155 +191,162 @@ FN:7,(anonymous_3) FN:9,_interopRequireDefault FN:11,_toConsumableArray FN:13,_classCallCheck -FN:25,(anonymous_7) -FN:26,ModelFinder -FN:40,find -FN:51,count -FN:62,value -FN:74,(anonymous_12) -FN:75,ModelQuery -FN:88,get -FN:94,get -FN:100,get -FN:109,find -FN:120,count -FN:132,where -FN:139,(anonymous_20) -FN:152,andWhere -FN:160,orWhere -FN:168,groupBy -FN:176,orderBy -FN:184,limit -FN:193,results -FN:200,(anonymous_27) -FN:212,(anonymous_28) -FN:215,(anonymous_29) -FNF:29 -FNH:26 +FN:26,(anonymous_7) +FN:27,ModelFinder +FN:41,find +FN:52,count +FN:63,value +FN:75,(anonymous_12) +FN:76,ModelQuery +FN:89,get +FN:95,get +FN:101,get +FN:110,find +FN:121,count +FN:133,value +FN:138,(anonymous_20) +FN:148,where +FN:157,andWhere +FN:166,orWhere +FN:175,groupBy +FN:184,orderBy +FN:193,limit +FN:202,results +FN:209,(anonymous_28) +FN:221,(anonymous_29) +FN:224,(anonymous_30) +FNF:30 +FNH:30 FNDA:1,(anonymous_1) FNDA:2,defineProperties FNDA:2,(anonymous_3) FNDA:2,_interopRequireDefault -FNDA:41,_toConsumableArray -FNDA:89,_classCallCheck +FNDA:57,_toConsumableArray +FNDA:99,_classCallCheck FNDA:1,(anonymous_7) FNDA:47,ModelFinder FNDA:18,find FNDA:13,count FNDA:31,value FNDA:1,(anonymous_12) -FNDA:42,ModelQuery +FNDA:52,ModelQuery FNDA:1,get FNDA:1,get FNDA:2,get -FNDA:29,find +FNDA:39,find FNDA:13,count -FNDA:41,where -FNDA:116,(anonymous_20) -FNDA:6,andWhere -FNDA:0,orWhere -FNDA:0,groupBy -FNDA:0,orderBy +FNDA:57,value +FNDA:145,(anonymous_20) +FNDA:47,where +FNDA:7,andWhere +FNDA:1,orWhere +FNDA:1,groupBy +FNDA:1,orderBy FNDA:8,limit -FNDA:41,results -FNDA:41,(anonymous_27) -FNDA:28,(anonymous_28) -FNDA:85,(anonymous_29) +FNDA:51,results +FNDA:51,(anonymous_28) +FNDA:38,(anonymous_29) +FNDA:115,(anonymous_30) DA:3,1 -DA:7,12 +DA:7,13 DA:9,2 -DA:11,116 -DA:13,89 +DA:11,145 +DA:13,99 DA:15,1 DA:17,1 DA:19,1 DA:21,1 DA:23,1 -DA:25,1 DA:26,1 -DA:27,47 -DA:29,47 -DA:38,1 -DA:41,18 -DA:43,18 -DA:45,18 -DA:47,18 -DA:52,13 -DA:54,13 -DA:56,13 -DA:58,13 -DA:63,31 -DA:64,0 -DA:69,1 -DA:72,1 -DA:74,1 +DA:27,1 +DA:28,47 +DA:30,47 +DA:39,1 +DA:42,18 +DA:44,18 +DA:46,18 +DA:48,18 +DA:53,13 +DA:55,13 +DA:57,13 +DA:59,13 +DA:64,31 +DA:65,0 +DA:70,1 +DA:73,1 DA:75,1 -DA:76,42 -DA:78,42 -DA:80,42 -DA:81,42 -DA:89,1 +DA:76,1 +DA:77,52 +DA:79,52 +DA:81,52 +DA:82,52 DA:90,1 -DA:95,1 +DA:91,1 DA:96,1 -DA:101,2 -DA:107,1 -DA:110,29 -DA:112,29 -DA:114,29 -DA:116,29 -DA:121,13 +DA:97,1 +DA:102,2 +DA:108,1 +DA:111,39 +DA:113,39 +DA:115,39 +DA:117,39 DA:122,13 -DA:124,13 -DA:126,13 -DA:128,13 -DA:133,41 -DA:135,41 -DA:136,116 -DA:139,41 -DA:140,116 -DA:141,41 -DA:143,75 -DA:147,41 -DA:148,41 -DA:153,6 -DA:155,6 -DA:156,6 -DA:161,0 -DA:163,0 -DA:164,0 -DA:169,0 -DA:171,0 -DA:172,0 -DA:177,0 -DA:179,0 -DA:180,0 -DA:185,8 -DA:187,8 -DA:188,8 -DA:189,8 -DA:194,41 -DA:196,41 -DA:197,1 -DA:200,41 -DA:201,41 -DA:202,0 -DA:203,0 -DA:205,0 -DA:209,41 -DA:210,13 -DA:212,28 -DA:213,28 -DA:215,28 -DA:216,85 -DA:219,28 -DA:226,1 -DA:229,1 -LF:90 -LH:77 -BRDA:7,1,0,12 -BRDA:7,1,1,12 -BRDA:7,2,0,12 +DA:123,13 +DA:125,13 +DA:127,13 +DA:129,13 +DA:134,57 +DA:135,145 +DA:138,57 +DA:139,145 +DA:140,51 +DA:142,94 +DA:149,47 +DA:151,47 +DA:152,47 +DA:153,47 +DA:158,7 +DA:160,7 +DA:161,7 +DA:162,7 +DA:167,1 +DA:169,1 +DA:170,1 +DA:171,1 +DA:176,1 +DA:178,1 +DA:179,1 +DA:180,1 +DA:185,1 +DA:187,1 +DA:188,1 +DA:189,1 +DA:194,8 +DA:196,8 +DA:197,8 +DA:198,8 +DA:203,51 +DA:205,51 +DA:206,1 +DA:209,51 +DA:210,51 +DA:211,0 +DA:212,0 +DA:214,0 +DA:218,51 +DA:219,13 +DA:221,38 +DA:222,38 +DA:224,38 +DA:225,115 +DA:228,38 +DA:235,1 +DA:238,1 +LF:95 +LH:91 +BRDA:7,1,0,13 +BRDA:7,1,1,13 +BRDA:7,2,0,13 BRDA:7,2,1,0 BRDA:7,3,0,2 BRDA:7,3,1,0 @@ -349,24 +356,24 @@ BRDA:9,5,0,2 BRDA:9,5,1,0 BRDA:9,6,0,2 BRDA:9,6,1,2 -BRDA:11,7,0,41 +BRDA:11,7,0,57 BRDA:11,7,1,0 BRDA:13,8,0,0 -BRDA:13,8,1,89 -BRDA:63,9,0,0 -BRDA:63,9,1,31 -BRDA:140,10,0,41 -BRDA:140,10,1,75 -BRDA:140,11,0,116 -BRDA:140,11,1,75 -BRDA:196,12,0,1 -BRDA:196,12,1,40 -BRDA:201,13,0,0 -BRDA:201,13,1,41 -BRDA:202,14,0,0 -BRDA:202,14,1,0 -BRDA:209,15,0,13 -BRDA:209,15,1,28 +BRDA:13,8,1,99 +BRDA:64,9,0,0 +BRDA:64,9,1,31 +BRDA:139,10,0,51 +BRDA:139,10,1,94 +BRDA:139,11,0,145 +BRDA:139,11,1,90 +BRDA:205,12,0,1 +BRDA:205,12,1,50 +BRDA:210,13,0,0 +BRDA:210,13,1,51 +BRDA:211,14,0,0 +BRDA:211,14,1,0 +BRDA:218,15,0,13 +BRDA:218,15,1,38 BRF:30 BRH:20 end_of_record @@ -494,19 +501,19 @@ FNDA:2,defineProperties FNDA:2,(anonymous_3) FNDA:5,_interopRequireDefault FNDA:5,_toConsumableArray -FNDA:5042,_classCallCheck +FNDA:5552,_classCallCheck FNDA:1,(anonymous_7) -FNDA:1265,Model -FNDA:2530,(anonymous_9) -FNDA:87,get +FNDA:1340,Model +FNDA:2680,(anonymous_9) +FNDA:97,get FNDA:1,set FNDA:123,get FNDA:1,set FNDA:21,get -FNDA:642,hasOne -FNDA:840,belongsTo -FNDA:2295,hasMany -FNDA:647,ensure +FNDA:777,hasOne +FNDA:870,belongsTo +FNDA:2565,hasMany +FNDA:707,ensure FNDA:14,isValid FNDA:14,(anonymous_20) FNDA:22,invalidAttributes @@ -567,11 +574,11 @@ FNDA:12,(anonymous_75) FNDA:12,beforeValidation FNDA:8,beforeSave FNDA:8,afterSave -FNDA:334,associate -FNDA:651,validate -FNDA:1238,initialize +FNDA:339,associate +FNDA:666,validate +FNDA:1313,initialize FNDA:2,toJSON -FNDA:1266,value +FNDA:1341,value FNDA:102,value FNDA:18,value FNDA:2,value @@ -582,33 +589,33 @@ FNDA:15,value FNDA:18,(anonymous_91) FNDA:3,(anonymous_92) FNDA:15,(anonymous_93) -FNDA:3777,value -FNDA:386,(anonymous_95) -FNDA:675,(anonymous_96) +FNDA:4212,value +FNDA:401,(anonymous_95) +FNDA:810,(anonymous_96) FNDA:13,(anonymous_97) -FNDA:910,(anonymous_98) -FNDA:2295,(anonymous_99) +FNDA:940,(anonymous_98) +FNDA:2565,(anonymous_99) FNDA:267,get FNDA:87,get -FNDA:1288,value +FNDA:1363,value FNDA:8,value FNDA:19,(anonymous_104) FNDA:12,(anonymous_105) FNDA:1,(anonymous_106) -FNDA:3777,AssociationSetter +FNDA:4212,AssociationSetter FNDA:0,foreignName -FNDA:400,where +FNDA:490,where FNDA:3,andWhere -FNDA:1349,through -FNDA:276,as -FNDA:277,value +FNDA:1549,through +FNDA:286,as +FNDA:287,value FNDA:6,value -FNDA:11,modelFind +FNDA:21,modelFind DA:3,1 DA:7,35 DA:9,5 DA:11,10 -DA:13,5042 +DA:13,5552 DA:18,1 DA:20,1 DA:22,1 @@ -623,29 +630,29 @@ DA:38,1 DA:41,1 DA:57,1 DA:63,1 -DA:64,1265 -DA:66,1265 -DA:68,1265 -DA:69,2530 -DA:78,1265 -DA:113,87 +DA:64,1340 +DA:66,1340 +DA:68,1340 +DA:69,2680 +DA:78,1340 +DA:113,97 DA:116,1 DA:127,123 DA:130,1 DA:142,21 -DA:147,1265 -DA:148,1265 -DA:150,1265 -DA:152,1265 +DA:147,1340 +DA:148,1340 +DA:150,1340 +DA:152,1340 DA:155,1 -DA:158,642 -DA:167,840 -DA:176,2295 -DA:185,647 -DA:187,647 -DA:189,647 +DA:158,777 +DA:167,870 +DA:176,2565 +DA:185,707 +DA:187,707 +DA:189,707 DA:190,3 -DA:193,647 +DA:193,707 DA:205,14 DA:206,14 DA:226,22 @@ -893,7 +900,7 @@ DA:755,8 DA:769,2 DA:770,1 DA:772,1 -DA:783,1266 +DA:783,1341 DA:788,102 DA:793,18 DA:798,2 @@ -929,39 +936,39 @@ DA:869,0 DA:873,0 DA:875,8 DA:878,15 -DA:899,3777 -DA:901,3777 -DA:909,3777 -DA:912,2937 -DA:913,2937 -DA:914,2937 -DA:916,840 -DA:917,840 -DA:921,3777 -DA:925,3777 -DA:929,3777 -DA:931,3777 -DA:932,3777 -DA:936,3777 -DA:939,3777 -DA:941,642 -DA:943,642 -DA:945,386 -DA:946,386 -DA:949,642 -DA:950,675 +DA:899,4212 +DA:901,4212 +DA:909,4212 +DA:912,3342 +DA:913,3342 +DA:914,3342 +DA:916,870 +DA:917,870 +DA:921,4212 +DA:925,4212 +DA:929,4212 +DA:931,4212 +DA:932,4212 +DA:936,4212 +DA:939,4212 +DA:941,777 +DA:943,777 +DA:945,401 +DA:946,401 +DA:949,777 +DA:950,810 DA:951,28 DA:952,1 DA:955,27 DA:956,27 DA:958,27 -DA:961,642 -DA:963,840 -DA:965,840 +DA:961,777 +DA:963,870 +DA:965,870 DA:967,13 DA:968,13 -DA:971,840 -DA:972,910 +DA:971,870 +DA:972,940 DA:973,64 DA:974,1 DA:977,63 @@ -973,27 +980,27 @@ DA:984,7 DA:985,13 DA:987,12 DA:989,1 -DA:1000,840 -DA:1003,2295 -DA:1005,2295 -DA:1006,2295 +DA:1000,870 +DA:1003,2565 +DA:1005,2565 +DA:1006,2565 DA:1007,0 -DA:1010,2295 +DA:1010,2565 DA:1012,0 -DA:1015,3777 -DA:1016,3777 -DA:1020,3777 +DA:1015,4212 +DA:1016,4212 +DA:1020,4212 DA:1024,267 -DA:1027,3777 -DA:1028,1482 -DA:1032,1482 +DA:1027,4212 +DA:1028,1647 +DA:1032,1647 DA:1036,87 -DA:1040,3777 -DA:1063,3777 -DA:1065,3777 -DA:1067,3777 -DA:1072,1288 -DA:1073,1125 +DA:1040,4212 +DA:1063,4212 +DA:1065,4212 +DA:1067,4212 +DA:1072,1363 +DA:1073,1235 DA:1079,8 DA:1081,8 DA:1082,8 @@ -1015,36 +1022,36 @@ DA:1115,1 DA:1117,1 DA:1120,1 DA:1121,1 -DA:1122,3777 -DA:1124,3777 -DA:1126,3777 -DA:1128,840 -DA:1133,840 -DA:1136,2937 -DA:1141,2937 +DA:1122,4212 +DA:1124,4212 +DA:1126,4212 +DA:1128,870 +DA:1133,870 +DA:1136,3342 +DA:1141,3342 DA:1145,1 DA:1148,0 DA:1149,0 -DA:1154,400 -DA:1155,996 -DA:1158,400 -DA:1159,400 +DA:1154,490 +DA:1155,1221 +DA:1158,490 +DA:1159,490 DA:1164,3 DA:1166,3 DA:1167,6 DA:1170,3 DA:1171,3 -DA:1176,1349 -DA:1177,1349 -DA:1182,276 -DA:1183,276 -DA:1188,277 +DA:1176,1549 +DA:1177,1549 +DA:1182,286 +DA:1183,286 +DA:1188,287 DA:1193,6 DA:1197,1 DA:1200,1 DA:1202,1 -DA:1205,11 -DA:1206,11 +DA:1205,21 +DA:1206,21 LF:441 LH:415 BRDA:7,1,0,35 @@ -1062,15 +1069,15 @@ BRDA:9,6,1,5 BRDA:11,7,0,5 BRDA:11,7,1,0 BRDA:13,8,0,0 -BRDA:13,8,1,5042 -BRDA:113,9,0,87 -BRDA:113,9,1,86 +BRDA:13,8,1,5552 +BRDA:113,9,0,97 +BRDA:113,9,1,96 BRDA:127,10,0,123 BRDA:127,10,1,122 -BRDA:185,11,0,647 -BRDA:185,11,1,646 +BRDA:185,11,0,707 +BRDA:185,11,1,706 BRDA:189,12,0,3 -BRDA:189,12,1,644 +BRDA:189,12,1,704 BRDA:231,13,0,0 BRDA:231,13,1,22 BRDA:239,14,0,12 @@ -1190,22 +1197,22 @@ BRDA:862,68,0,8 BRDA:862,68,1,0 BRDA:866,69,0,3 BRDA:866,69,1,0 -BRDA:909,70,0,642 -BRDA:909,70,1,2937 -BRDA:909,70,2,840 -BRDA:939,71,0,642 -BRDA:939,71,1,840 -BRDA:939,71,2,2295 +BRDA:909,70,0,777 +BRDA:909,70,1,3342 +BRDA:909,70,2,870 +BRDA:939,71,0,777 +BRDA:939,71,1,870 +BRDA:939,71,2,2565 BRDA:939,71,3,0 BRDA:950,72,0,28 -BRDA:950,72,1,647 -BRDA:950,73,0,675 +BRDA:950,72,1,782 +BRDA:950,73,0,810 BRDA:950,73,1,32 BRDA:951,74,0,1 BRDA:951,74,1,27 BRDA:972,75,0,64 -BRDA:972,75,1,846 -BRDA:972,76,0,910 +BRDA:972,75,1,876 +BRDA:972,76,0,940 BRDA:972,76,1,68 BRDA:973,77,0,1 BRDA:973,77,1,63 @@ -1216,20 +1223,20 @@ BRDA:983,79,1,13 BRDA:985,80,0,12 BRDA:985,80,1,1 BRDA:1006,81,0,0 -BRDA:1006,81,1,2295 -BRDA:1006,82,0,2295 +BRDA:1006,81,1,2565 +BRDA:1006,82,0,2565 BRDA:1006,82,1,0 -BRDA:1027,83,0,1482 -BRDA:1027,83,1,2295 +BRDA:1027,83,0,1647 +BRDA:1027,83,1,2565 BRDA:1091,84,0,4 BRDA:1091,84,1,8 BRDA:1093,85,0,4 BRDA:1093,85,1,0 BRDA:1093,86,0,4 BRDA:1093,86,1,4 -BRDA:1126,87,0,840 -BRDA:1126,87,1,642 -BRDA:1126,87,2,2937 +BRDA:1126,87,0,870 +BRDA:1126,87,1,777 +BRDA:1126,87,2,3342 BRDA:1164,88,0,3 BRDA:1164,88,1,3 BRF:185 diff --git a/package.json b/package.json index 4670af7..7fe667b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dovima", - "version": "0.1.4", + "version": "0.1.5", "description": "ES6 generic model with lots of useful special features.", "main": "index.js", "scripts": { From 08658482a064b8068f18699fc861c81b1ad8f66c Mon Sep 17 00:00:00 2001 From: nicosommi Date: Tue, 14 Jul 2015 19:56:28 -0300 Subject: [PATCH 14/15] partial advance on quirk implementation --- es5/lib/model.js | 22 +- es5/spec/model.spec.js | 27 + es6/lib/model.js | 21 +- es6/spec/model.spec.js | 24 + lcov.info | 1535 ++++++++++++++++++++-------------------- package.json | 7 +- 6 files changed, 863 insertions(+), 773 deletions(-) diff --git a/es5/lib/model.js b/es5/lib/model.js index 1b8e1fa..82998ae 100644 --- a/es5/lib/model.js +++ b/es5/lib/model.js @@ -27,6 +27,10 @@ var _jargon = require("jargon"); var _jargon2 = _interopRequireDefault(_jargon); +var _quirk = require("quirk"); + +var _quirk2 = _interopRequireDefault(_quirk); + var _modelFinderJs = require("./modelFinder.js"); var _collectionJs = require("./collection.js"); @@ -82,6 +86,13 @@ var Model = (function () { value: [] }, + "additionalAttributes": { + enumerable: false, //this fix db related issues + get: function get() { + return _this.constructor.attributes; + } + }, + "isNew": { get: this[isNew] }, @@ -802,13 +813,13 @@ var Model = (function () { value: function value() { var _this7 = this; - var attributeNames = {}; + var attributes = {}; this.properties.forEach(function (propertyName) { if (!_this7._associations[propertyName]) { - attributeNames[propertyName] = _this7[propertyName]; + attributes[propertyName] = _this7[propertyName]; } }); - return attributeNames; + return attributes; } }, { key: isNew, @@ -1205,5 +1216,10 @@ Object.defineProperties(Model, { var modelQuery = new _modelFinderJs.ModelQuery(Model.database); return modelQuery.find(this); } + }, + //problem here: if it's static on Model, it will be shared for every Model + //if it's static on Item, it will be inaccesible from Model methods to use that on save for eg + "attributes": { + value: new _quirk2["default"]() } }); \ No newline at end of file diff --git a/es5/spec/model.spec.js b/es5/spec/model.spec.js index da2d58c..45080b6 100644 --- a/es5/spec/model.spec.js +++ b/es5/spec/model.spec.js @@ -32,6 +32,10 @@ var _libModelJs = require("../lib/model.js"); var _libModelJs2 = _interopRequireDefault(_libModelJs); +var _quirk = require("quirk"); + +var _quirk2 = _interopRequireDefault(_quirk); + var _libModelFinderJs = require("../lib/modelFinder.js"); var _ = require("../../"); @@ -349,6 +353,29 @@ describe("Model(attributes, options)", function () { }); describe("(static properties)", function () { + describe(".attributes", function () { + it("should be an instance of Quirk", function () { + User.attributes.should.be.instanceOf(_quirk2["default"]); + }); + + it("should be able to get the attributes from the regular methods", function () { + user.additionalAttributes.should.be.instanceOf(_quirk2["default"]); + }); + + it("should be able to define a new static property to the model", function () { + User.attributes.specialAttribute = 2; + user.specialAttribute.should.equal(2); + }); + + it("should be able to define a new static property to the model", function () { + User.attributes.specialAttribute = function specialAttributeGetter() { + return this.id; + }; + user = new User({ id: 2 }); + user.specialAttribute.should.equal(2); + }); + }); + describe(".find", function () { var users = undefined, userCollection = undefined; diff --git a/es6/lib/model.js b/es6/lib/model.js index 8dee09d..c2c0dd3 100644 --- a/es6/lib/model.js +++ b/es6/lib/model.js @@ -5,6 +5,7 @@ const flowsync = require("flowsync"); import MultiError from "blunder"; import Datetime from "fleming"; import inflect from "jargon"; +import Quirk from "quirk"; import {ModelQuery} from "./modelFinder.js"; @@ -51,6 +52,13 @@ export default class Model { value: [] }, + "additionalAttributes": { + enumerable: false, //this fix db related issues + get: () => { + return this.constructor.attributes; + } + }, + "isNew": { get: this[isNew] }, @@ -746,13 +754,13 @@ export default class Model { } [attributes]() { - var attributeNames = {}; + var attributes = {}; this.properties.forEach((propertyName) => { if(!this._associations[propertyName]) { - attributeNames[propertyName] = this[propertyName]; + attributes[propertyName] = this[propertyName]; } }); - return attributeNames; + return attributes; } [isNew]() { @@ -1016,6 +1024,8 @@ export default class Model { } [getFieldAttributes]() { + //this, using quirk, will return target with the new attributes on it, useful to EXCLUDE them from here + //TODO: this.additionalAttributes.addAttributes(target); //magic line let attributeNames = Object.keys(this.attributes); let fieldAttributes = {}; attributeNames.forEach((attributeName) => { @@ -1114,6 +1124,11 @@ Object.defineProperties(Model, { let modelQuery = new ModelQuery(Model.database); return modelQuery.find(this); } + }, + //problem here: can't assign property automatically to the concrete model to use it + //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe#Browser_compatibility + "attributes": { + value: new Quirk() } }); diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index fe75399..8c7183f 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -7,6 +7,7 @@ const sinon = require("sinon"); import Database from "almaden"; import Collection from "../lib/collection.js"; import Model, {AssociationSetter} from "../lib/model.js"; +import Quirk from "quirk"; import {ModelQuery} from "../lib/modelFinder.js"; import {isPresent} from "../../"; @@ -248,6 +249,29 @@ describe("Model(attributes, options)", () => { }); describe("(static properties)", () => { + describe(".attributes", () => { + it("should be an instance of Quirk", () => { + User.attributes.should.be.instanceOf(Quirk); + }); + + it("should be able to get the attributes from the regular methods", () => { + user.additionalAttributes.should.be.instanceOf(Quirk); + }); + + it("should be able to define a new static property to the model", () => { + User.attributes.specialAttribute = 2; + user.specialAttribute.should.equal(2); + }); + + it("should be able to define a new static property to the model", () => { + User.attributes.specialAttribute = function specialAttributeGetter() { + return this.id; + }; + user = new User({id: 2}); + user.specialAttribute.should.equal(2); + }); + }); + describe(".find", () => { let users, userCollection; diff --git a/lcov.info b/lcov.info index 2f4455a..1b20985 100644 --- a/lcov.info +++ b/lcov.info @@ -25,13 +25,13 @@ FNH:20 FNDA:1,(anonymous_1) FNDA:1,defineProperties FNDA:1,(anonymous_3) -FNDA:2479,get +FNDA:2507,get FNDA:3,_interopRequireDefault FNDA:3,_toConsumableArray -FNDA:2324,_classCallCheck +FNDA:2352,_classCallCheck FNDA:1,_inherits FNDA:1,(anonymous_9) -FNDA:2324,Collection +FNDA:2352,Collection FNDA:160,push FNDA:160,(anonymous_12) FNDA:4,fetch @@ -44,10 +44,10 @@ FNDA:4,(anonymous_19) FNDA:13,(anonymous_20) DA:3,1 DA:7,2 -DA:9,2479 +DA:9,2507 DA:11,3 DA:13,6 -DA:15,2324 +DA:15,2352 DA:17,1 DA:19,1 DA:21,1 @@ -57,13 +57,13 @@ DA:27,1 DA:29,1 DA:31,1 DA:32,1 -DA:33,2324 -DA:35,2324 -DA:37,2324 -DA:40,2324 +DA:33,2352 +DA:35,2352 +DA:37,2352 +DA:40,2352 DA:41,29 -DA:43,2295 -DA:46,2324 +DA:43,2323 +DA:46,2352 DA:59,1 DA:61,1 DA:64,160 @@ -127,12 +127,12 @@ BRDA:7,3,1,0 BRDA:7,4,0,0 BRDA:7,4,1,1 BRDA:9,5,0,0 -BRDA:9,5,1,2479 +BRDA:9,5,1,2507 BRDA:9,6,0,0 -BRDA:9,6,1,2479 +BRDA:9,6,1,2507 BRDA:9,7,0,0 BRDA:9,7,1,0 -BRDA:9,8,0,2479 +BRDA:9,8,0,2507 BRDA:9,8,1,0 BRDA:9,9,0,0 BRDA:9,9,1,0 @@ -143,7 +143,7 @@ BRDA:11,11,1,3 BRDA:13,12,0,3 BRDA:13,12,1,0 BRDA:15,13,0,0 -BRDA:15,13,1,2324 +BRDA:15,13,1,2352 BRDA:17,14,0,0 BRDA:17,14,1,1 BRDA:17,15,0,1 @@ -153,9 +153,9 @@ BRDA:17,16,1,1 BRDA:17,17,0,1 BRDA:17,17,1,0 BRDA:40,18,0,29 -BRDA:40,18,1,2295 -BRDA:40,19,0,2324 -BRDA:40,19,1,2324 +BRDA:40,18,1,2323 +BRDA:40,19,0,2352 +BRDA:40,19,1,2352 BRDA:71,20,0,155 BRDA:71,20,1,5 BRDA:72,21,0,65 @@ -378,237 +378,239 @@ FN:7,(anonymous_3) FN:9,_interopRequireDefault FN:11,_toConsumableArray FN:13,_classCallCheck -FN:57,(anonymous_7) -FN:63,Model -FN:68,(anonymous_9) -FN:112,get -FN:115,set -FN:126,get -FN:129,set -FN:141,get -FN:157,hasOne -FN:166,belongsTo -FN:175,hasMany -FN:184,ensure -FN:204,isValid -FN:205,(anonymous_20) -FN:225,invalidAttributes -FN:230,compileInvalidAttributeList -FN:249,performValidationsForAttribute -FN:252,performValidation -FN:255,(anonymous_25) -FN:264,compileValidatorResponses -FN:302,include -FN:312,fetch -FN:341,value -FN:351,(anonymous_30) -FN:367,(anonymous_31) -FN:374,(anonymous_32) -FN:382,(anonymous_33) +FN:61,(anonymous_7) +FN:67,Model +FN:72,(anonymous_9) +FN:90,get +FN:122,get +FN:125,set +FN:136,get +FN:139,set +FN:151,get +FN:167,hasOne +FN:176,belongsTo +FN:185,hasMany +FN:194,ensure +FN:214,isValid +FN:215,(anonymous_21) +FN:235,invalidAttributes +FN:240,compileInvalidAttributeList +FN:259,performValidationsForAttribute +FN:262,performValidation +FN:265,(anonymous_26) +FN:274,compileValidatorResponses +FN:312,include +FN:322,fetch +FN:351,value +FN:361,(anonymous_31) +FN:377,(anonymous_32) +FN:384,(anonymous_33) FN:392,(anonymous_34) -FN:408,(anonymous_35) +FN:402,(anonymous_35) FN:418,(anonymous_36) -FN:425,(anonymous_37) -FN:428,processWhereCondition -FN:439,(anonymous_39) -FN:445,(anonymous_40) -FN:452,(anonymous_41) -FN:465,(anonymous_42) -FN:469,(anonymous_43) -FN:471,(anonymous_44) -FN:488,(anonymous_45) -FN:492,(anonymous_46) -FN:493,(anonymous_47) -FN:502,(anonymous_48) -FN:506,(anonymous_49) -FN:507,(anonymous_50) -FN:522,(anonymous_51) -FN:526,(anonymous_52) -FN:527,(anonymous_53) -FN:570,(anonymous_54) -FN:581,(anonymous_55) -FN:582,(anonymous_56) -FN:593,(anonymous_57) -FN:609,_delete -FN:618,(anonymous_59) -FN:619,(anonymous_60) -FN:622,(anonymous_61) -FN:626,(anonymous_62) -FN:635,(anonymous_63) -FN:647,save -FN:654,(anonymous_65) -FN:656,(anonymous_66) -FN:657,(anonymous_67) -FN:661,(anonymous_68) -FN:683,(anonymous_69) -FN:685,(anonymous_70) -FN:691,(anonymous_71) -FN:713,(anonymous_72) -FN:716,(anonymous_73) -FN:730,(anonymous_74) -FN:732,(anonymous_75) -FN:744,beforeValidation -FN:749,beforeSave -FN:754,afterSave -FN:759,associate -FN:762,validate -FN:765,initialize -FN:768,toJSON -FN:782,value -FN:787,value +FN:428,(anonymous_37) +FN:435,(anonymous_38) +FN:438,processWhereCondition +FN:449,(anonymous_40) +FN:455,(anonymous_41) +FN:462,(anonymous_42) +FN:475,(anonymous_43) +FN:479,(anonymous_44) +FN:481,(anonymous_45) +FN:498,(anonymous_46) +FN:502,(anonymous_47) +FN:503,(anonymous_48) +FN:512,(anonymous_49) +FN:516,(anonymous_50) +FN:517,(anonymous_51) +FN:532,(anonymous_52) +FN:536,(anonymous_53) +FN:537,(anonymous_54) +FN:580,(anonymous_55) +FN:591,(anonymous_56) +FN:592,(anonymous_57) +FN:603,(anonymous_58) +FN:619,_delete +FN:628,(anonymous_60) +FN:629,(anonymous_61) +FN:632,(anonymous_62) +FN:636,(anonymous_63) +FN:645,(anonymous_64) +FN:657,save +FN:664,(anonymous_66) +FN:666,(anonymous_67) +FN:667,(anonymous_68) +FN:671,(anonymous_69) +FN:693,(anonymous_70) +FN:695,(anonymous_71) +FN:701,(anonymous_72) +FN:723,(anonymous_73) +FN:726,(anonymous_74) +FN:740,(anonymous_75) +FN:742,(anonymous_76) +FN:754,beforeValidation +FN:759,beforeSave +FN:764,afterSave +FN:769,associate +FN:772,validate +FN:775,initialize +FN:778,toJSON FN:792,value FN:797,value FN:802,value -FN:806,(anonymous_88) -FN:815,value -FN:833,value -FN:838,(anonymous_91) -FN:864,(anonymous_92) -FN:877,(anonymous_93) -FN:898,value -FN:943,(anonymous_95) -FN:949,(anonymous_96) -FN:965,(anonymous_97) -FN:971,(anonymous_98) -FN:1005,(anonymous_99) -FN:1023,get -FN:1035,get -FN:1071,value -FN:1078,value -FN:1083,(anonymous_104) -FN:1088,(anonymous_105) -FN:1120,(anonymous_106) -FN:1121,AssociationSetter -FN:1147,foreignName -FN:1153,where -FN:1163,andWhere -FN:1175,through -FN:1181,as -FN:1187,value -FN:1192,value -FN:1204,modelFind -FNF:115 -FNH:111 +FN:807,value +FN:812,value +FN:816,(anonymous_89) +FN:825,value +FN:843,value +FN:848,(anonymous_92) +FN:874,(anonymous_93) +FN:887,(anonymous_94) +FN:908,value +FN:953,(anonymous_96) +FN:959,(anonymous_97) +FN:975,(anonymous_98) +FN:981,(anonymous_99) +FN:1015,(anonymous_100) +FN:1033,get +FN:1045,get +FN:1081,value +FN:1088,value +FN:1093,(anonymous_105) +FN:1098,(anonymous_106) +FN:1130,(anonymous_107) +FN:1131,AssociationSetter +FN:1157,foreignName +FN:1163,where +FN:1173,andWhere +FN:1185,through +FN:1191,as +FN:1197,value +FN:1202,value +FN:1214,modelFind +FNF:116 +FNH:112 FNDA:1,(anonymous_1) FNDA:2,defineProperties FNDA:2,(anonymous_3) -FNDA:5,_interopRequireDefault +FNDA:6,_interopRequireDefault FNDA:5,_toConsumableArray -FNDA:5042,_classCallCheck +FNDA:5102,_classCallCheck FNDA:1,(anonymous_7) -FNDA:1265,Model -FNDA:2530,(anonymous_9) +FNDA:1279,Model +FNDA:2558,(anonymous_9) +FNDA:1,get FNDA:87,get FNDA:1,set FNDA:123,get FNDA:1,set FNDA:21,get -FNDA:642,hasOne -FNDA:840,belongsTo -FNDA:2295,hasMany -FNDA:647,ensure +FNDA:648,hasOne +FNDA:852,belongsTo +FNDA:2323,hasMany +FNDA:655,ensure FNDA:14,isValid -FNDA:14,(anonymous_20) +FNDA:14,(anonymous_21) FNDA:22,invalidAttributes FNDA:22,compileInvalidAttributeList FNDA:14,performValidationsForAttribute FNDA:14,performValidation -FNDA:14,(anonymous_25) +FNDA:14,(anonymous_26) FNDA:14,compileValidatorResponses FNDA:16,include FNDA:31,fetch FNDA:31,value -FNDA:32,(anonymous_30) -FNDA:25,(anonymous_31) -FNDA:15,(anonymous_32) -FNDA:16,(anonymous_33) -FNDA:5,(anonymous_34) -FNDA:1,(anonymous_35) +FNDA:32,(anonymous_31) +FNDA:25,(anonymous_32) +FNDA:15,(anonymous_33) +FNDA:16,(anonymous_34) +FNDA:5,(anonymous_35) FNDA:1,(anonymous_36) -FNDA:4,(anonymous_37) +FNDA:1,(anonymous_37) +FNDA:4,(anonymous_38) FNDA:10,processWhereCondition -FNDA:4,(anonymous_39) -FNDA:1,(anonymous_40) -FNDA:4,(anonymous_41) +FNDA:4,(anonymous_40) +FNDA:1,(anonymous_41) FNDA:4,(anonymous_42) FNDA:4,(anonymous_43) FNDA:4,(anonymous_44) -FNDA:0,(anonymous_45) +FNDA:4,(anonymous_45) FNDA:0,(anonymous_46) FNDA:0,(anonymous_47) -FNDA:3,(anonymous_48) -FNDA:1,(anonymous_49) -FNDA:3,(anonymous_50) -FNDA:6,(anonymous_51) -FNDA:2,(anonymous_52) -FNDA:6,(anonymous_53) -FNDA:4,(anonymous_54) -FNDA:1,(anonymous_55) +FNDA:0,(anonymous_48) +FNDA:3,(anonymous_49) +FNDA:1,(anonymous_50) +FNDA:3,(anonymous_51) +FNDA:6,(anonymous_52) +FNDA:2,(anonymous_53) +FNDA:6,(anonymous_54) +FNDA:4,(anonymous_55) FNDA:1,(anonymous_56) -FNDA:12,(anonymous_57) +FNDA:1,(anonymous_57) +FNDA:12,(anonymous_58) FNDA:10,_delete -FNDA:7,(anonymous_59) -FNDA:4,(anonymous_60) -FNDA:7,(anonymous_61) +FNDA:7,(anonymous_60) +FNDA:4,(anonymous_61) FNDA:7,(anonymous_62) FNDA:7,(anonymous_63) +FNDA:7,(anonymous_64) FNDA:13,save -FNDA:12,(anonymous_65) FNDA:12,(anonymous_66) FNDA:12,(anonymous_67) -FNDA:4,(anonymous_68) -FNDA:8,(anonymous_69) +FNDA:12,(anonymous_68) +FNDA:4,(anonymous_69) FNDA:8,(anonymous_70) -FNDA:3,(anonymous_71) -FNDA:8,(anonymous_72) +FNDA:8,(anonymous_71) +FNDA:3,(anonymous_72) FNDA:8,(anonymous_73) FNDA:8,(anonymous_74) -FNDA:12,(anonymous_75) +FNDA:8,(anonymous_75) +FNDA:12,(anonymous_76) FNDA:12,beforeValidation FNDA:8,beforeSave FNDA:8,afterSave -FNDA:334,associate -FNDA:651,validate -FNDA:1238,initialize +FNDA:336,associate +FNDA:657,validate +FNDA:1252,initialize FNDA:2,toJSON -FNDA:1266,value +FNDA:1280,value FNDA:102,value FNDA:18,value FNDA:2,value FNDA:17,value -FNDA:168,(anonymous_88) +FNDA:168,(anonymous_89) FNDA:32,value FNDA:15,value -FNDA:18,(anonymous_91) -FNDA:3,(anonymous_92) -FNDA:15,(anonymous_93) -FNDA:3777,value -FNDA:386,(anonymous_95) -FNDA:675,(anonymous_96) -FNDA:13,(anonymous_97) -FNDA:910,(anonymous_98) -FNDA:2295,(anonymous_99) +FNDA:18,(anonymous_92) +FNDA:3,(anonymous_93) +FNDA:15,(anonymous_94) +FNDA:3823,value +FNDA:392,(anonymous_96) +FNDA:681,(anonymous_97) +FNDA:13,(anonymous_98) +FNDA:922,(anonymous_99) +FNDA:2323,(anonymous_100) FNDA:267,get FNDA:87,get -FNDA:1288,value +FNDA:1302,value FNDA:8,value -FNDA:19,(anonymous_104) -FNDA:12,(anonymous_105) -FNDA:1,(anonymous_106) -FNDA:3777,AssociationSetter +FNDA:19,(anonymous_105) +FNDA:12,(anonymous_106) +FNDA:1,(anonymous_107) +FNDA:3823,AssociationSetter FNDA:0,foreignName -FNDA:400,where +FNDA:404,where FNDA:3,andWhere -FNDA:1349,through -FNDA:276,as -FNDA:277,value +FNDA:1365,through +FNDA:280,as +FNDA:281,value FNDA:6,value FNDA:11,modelFind DA:3,1 DA:7,35 -DA:9,5 +DA:9,6 DA:11,10 -DA:13,5042 +DA:13,5102 DA:18,1 DA:20,1 DA:22,1 @@ -620,433 +622,436 @@ DA:32,1 DA:34,1 DA:36,1 DA:38,1 -DA:41,1 -DA:57,1 -DA:63,1 -DA:64,1265 -DA:66,1265 -DA:68,1265 -DA:69,2530 -DA:78,1265 -DA:113,87 -DA:116,1 -DA:127,123 -DA:130,1 -DA:142,21 -DA:147,1265 -DA:148,1265 -DA:150,1265 -DA:152,1265 -DA:155,1 -DA:158,642 -DA:167,840 -DA:176,2295 -DA:185,647 -DA:187,647 -DA:189,647 -DA:190,3 -DA:193,647 -DA:205,14 -DA:206,14 -DA:226,22 -DA:228,22 -DA:230,22 -DA:231,22 -DA:232,0 -DA:234,22 +DA:40,1 +DA:42,1 +DA:45,1 +DA:61,1 +DA:67,1 +DA:68,1279 +DA:70,1279 +DA:72,1279 +DA:73,2558 +DA:82,1279 +DA:91,1 +DA:123,87 +DA:126,1 +DA:137,123 +DA:140,1 +DA:152,21 +DA:157,1279 +DA:158,1279 +DA:160,1279 +DA:162,1279 +DA:165,1 +DA:168,648 +DA:177,852 +DA:186,2323 +DA:195,655 +DA:197,655 +DA:199,655 +DA:200,3 +DA:203,655 +DA:215,14 +DA:216,14 DA:236,22 -DA:237,14 -DA:239,14 -DA:240,12 -DA:241,12 -DA:245,22 -DA:249,22 -DA:250,14 -DA:252,14 -DA:253,14 -DA:255,14 -DA:256,14 -DA:257,2 -DA:259,12 -DA:264,14 +DA:238,22 +DA:240,22 +DA:241,22 +DA:242,0 +DA:244,22 +DA:246,22 +DA:247,14 +DA:249,14 +DA:250,12 +DA:251,12 +DA:255,22 +DA:259,22 +DA:260,14 +DA:262,14 +DA:263,14 DA:265,14 -DA:267,14 -DA:268,14 -DA:269,14 -DA:271,14 -DA:272,14 -DA:273,14 +DA:266,14 +DA:267,2 +DA:269,12 +DA:274,14 DA:275,14 -DA:278,0 -DA:279,0 +DA:277,14 +DA:278,14 +DA:279,14 DA:281,14 DA:282,14 -DA:283,0 -DA:286,14 -DA:287,0 +DA:283,14 +DA:285,14 +DA:288,0 +DA:289,0 +DA:291,14 DA:292,14 -DA:295,14 -DA:298,22 -DA:303,16 -DA:304,18 -DA:307,16 -DA:308,16 -DA:313,31 -DA:314,32 -DA:317,31 -DA:319,5 -DA:320,0 -DA:322,20 -DA:323,16 -DA:324,4 -DA:325,1 -DA:327,3 -DA:329,16 -DA:331,6 -DA:332,3 -DA:334,3 -DA:336,6 -DA:342,31 -DA:344,31 -DA:346,31 -DA:347,3 -DA:350,28 -DA:351,28 -DA:352,32 -DA:353,3 -DA:356,29 -DA:357,26 -DA:359,3 -DA:363,25 -DA:364,1 -DA:367,25 -DA:368,25 +DA:293,0 +DA:296,14 +DA:297,0 +DA:302,14 +DA:305,14 +DA:308,22 +DA:313,16 +DA:314,18 +DA:317,16 +DA:318,16 +DA:323,31 +DA:324,32 +DA:327,31 +DA:329,5 +DA:330,0 +DA:332,20 +DA:333,16 +DA:334,4 +DA:335,1 +DA:337,3 +DA:339,16 +DA:341,6 +DA:342,3 +DA:344,3 +DA:346,6 +DA:352,31 +DA:354,31 +DA:356,31 +DA:357,3 +DA:360,28 +DA:361,28 +DA:362,32 +DA:363,3 +DA:366,29 +DA:367,26 DA:369,3 -DA:371,22 -DA:373,22 -DA:374,15 -DA:375,15 -DA:377,15 -DA:380,15 -DA:382,15 -DA:384,16 -DA:386,16 -DA:387,1 +DA:373,25 +DA:374,1 +DA:377,25 +DA:378,25 +DA:379,3 +DA:381,22 +DA:383,22 +DA:384,15 +DA:385,15 +DA:387,15 DA:390,15 -DA:392,5 -DA:396,5 -DA:398,5 -DA:399,1 -DA:404,1 -DA:405,0 -DA:408,1 +DA:392,15 +DA:394,16 +DA:396,16 +DA:397,1 +DA:400,15 +DA:402,5 +DA:406,5 +DA:408,5 DA:409,1 -DA:410,1 -DA:417,1 +DA:414,1 +DA:415,0 DA:418,1 DA:419,1 DA:420,1 -DA:421,1 -DA:425,4 -DA:426,4 -DA:428,4 -DA:429,10 -DA:430,5 -DA:431,5 -DA:433,5 -DA:437,4 -DA:439,4 -DA:440,4 -DA:442,4 -DA:444,4 -DA:445,1 -DA:446,1 -DA:447,1 +DA:427,1 +DA:428,1 +DA:429,1 +DA:430,1 +DA:431,1 +DA:435,4 +DA:436,4 +DA:438,4 +DA:439,10 +DA:440,5 +DA:441,5 +DA:443,5 +DA:447,4 +DA:449,4 +DA:450,4 DA:452,4 -DA:453,4 DA:454,4 -DA:455,4 -DA:460,5 -DA:464,8 +DA:455,1 +DA:456,1 +DA:457,1 +DA:462,4 +DA:463,4 +DA:464,4 DA:465,4 -DA:467,4 -DA:469,4 -DA:470,4 -DA:471,4 -DA:472,4 -DA:474,4 -DA:475,1 -DA:478,3 -DA:480,3 -DA:482,3 -DA:484,3 -DA:488,0 -DA:489,0 -DA:492,0 -DA:493,0 -DA:494,0 -DA:496,0 +DA:470,5 +DA:474,8 +DA:475,4 +DA:477,4 +DA:479,4 +DA:480,4 +DA:481,4 +DA:482,4 +DA:484,4 +DA:485,1 +DA:488,3 +DA:490,3 +DA:492,3 +DA:494,3 +DA:498,0 DA:499,0 -DA:502,1 -DA:503,3 -DA:506,1 -DA:507,1 -DA:508,3 -DA:510,1 +DA:502,0 +DA:503,0 +DA:504,0 +DA:506,0 +DA:509,0 DA:512,1 -DA:522,2 -DA:523,6 -DA:526,2 -DA:527,2 -DA:528,6 -DA:530,2 -DA:533,2 -DA:570,4 -DA:571,4 -DA:574,8 -DA:577,2 -DA:578,1 -DA:581,1 -DA:582,1 -DA:583,1 -DA:584,1 -DA:585,1 -DA:586,1 -DA:593,13 -DA:594,12 -DA:595,12 -DA:600,7 -DA:601,7 -DA:610,10 -DA:612,10 -DA:613,9 -DA:614,1 -DA:617,8 -DA:618,7 -DA:619,7 -DA:620,4 -DA:623,7 -DA:624,7 -DA:625,7 -DA:626,7 -DA:627,7 -DA:628,0 +DA:513,3 +DA:516,1 +DA:517,1 +DA:518,3 +DA:520,1 +DA:522,1 +DA:532,2 +DA:533,6 +DA:536,2 +DA:537,2 +DA:538,6 +DA:540,2 +DA:543,2 +DA:580,4 +DA:581,4 +DA:584,8 +DA:587,2 +DA:588,1 +DA:591,1 +DA:592,1 +DA:593,1 +DA:594,1 +DA:595,1 +DA:596,1 +DA:603,13 +DA:604,12 +DA:605,12 +DA:610,7 +DA:611,7 +DA:620,10 +DA:622,10 +DA:623,9 +DA:624,1 +DA:627,8 +DA:628,7 DA:629,7 -DA:630,1 -DA:632,6 +DA:630,4 +DA:633,7 +DA:634,7 +DA:635,7 DA:636,7 -DA:639,1 -DA:642,1 -DA:648,13 -DA:650,13 -DA:651,1 -DA:654,12 -DA:655,12 -DA:657,12 -DA:658,12 -DA:659,8 -DA:661,4 -DA:662,4 -DA:664,4 -DA:665,4 -DA:666,4 -DA:667,4 -DA:668,4 -DA:670,4 +DA:637,7 +DA:638,0 +DA:639,7 +DA:640,1 +DA:642,6 +DA:646,7 +DA:649,1 +DA:652,1 +DA:658,13 +DA:660,13 +DA:661,1 +DA:664,12 +DA:665,12 +DA:667,12 +DA:668,12 +DA:669,8 DA:671,4 DA:672,4 -DA:673,4 +DA:674,4 +DA:675,4 DA:676,4 -DA:678,0 -DA:684,8 -DA:686,8 -DA:687,3 -DA:688,3 -DA:689,3 -DA:691,3 -DA:692,3 -DA:693,0 -DA:695,3 -DA:696,3 -DA:700,5 -DA:701,5 -DA:702,5 -DA:703,5 -DA:705,5 -DA:706,14 -DA:707,9 +DA:677,4 +DA:678,4 +DA:680,4 +DA:681,4 +DA:682,4 +DA:683,4 +DA:686,4 +DA:688,0 +DA:694,8 +DA:696,8 +DA:697,3 +DA:698,3 +DA:699,3 +DA:701,3 +DA:702,3 +DA:703,0 +DA:705,3 +DA:706,3 +DA:710,5 DA:711,5 -DA:716,8 -DA:717,8 -DA:719,2 -DA:721,2 -DA:722,2 -DA:724,0 -DA:727,4 -DA:731,8 -DA:733,12 -DA:734,4 -DA:736,8 -DA:745,12 -DA:750,8 -DA:755,8 -DA:769,2 -DA:770,1 -DA:772,1 -DA:783,1266 -DA:788,102 -DA:793,18 -DA:798,2 -DA:803,17 -DA:805,17 -DA:806,17 -DA:807,168 -DA:808,84 -DA:811,17 -DA:816,32 -DA:817,21 -DA:819,11 -DA:834,15 -DA:836,15 -DA:838,15 -DA:840,18 -DA:842,18 -DA:845,10 -DA:846,10 -DA:848,9 -DA:849,9 -DA:850,3 -DA:852,6 -DA:855,1 -DA:857,10 -DA:860,8 -DA:862,8 -DA:864,8 -DA:865,3 -DA:866,3 -DA:867,3 -DA:869,0 -DA:873,0 -DA:875,8 -DA:878,15 -DA:899,3777 -DA:901,3777 -DA:909,3777 -DA:912,2937 -DA:913,2937 -DA:914,2937 -DA:916,840 -DA:917,840 -DA:921,3777 -DA:925,3777 -DA:929,3777 -DA:931,3777 -DA:932,3777 -DA:936,3777 -DA:939,3777 -DA:941,642 -DA:943,642 -DA:945,386 -DA:946,386 -DA:949,642 -DA:950,675 -DA:951,28 -DA:952,1 -DA:955,27 -DA:956,27 -DA:958,27 -DA:961,642 -DA:963,840 -DA:965,840 -DA:967,13 -DA:968,13 -DA:971,840 -DA:972,910 -DA:973,64 -DA:974,1 -DA:977,63 -DA:978,63 -DA:980,63 -DA:982,63 -DA:983,20 -DA:984,7 -DA:985,13 -DA:987,12 -DA:989,1 -DA:1000,840 -DA:1003,2295 -DA:1005,2295 -DA:1006,2295 -DA:1007,0 -DA:1010,2295 -DA:1012,0 -DA:1015,3777 -DA:1016,3777 -DA:1020,3777 -DA:1024,267 -DA:1027,3777 -DA:1028,1482 -DA:1032,1482 -DA:1036,87 -DA:1040,3777 -DA:1063,3777 -DA:1065,3777 -DA:1067,3777 -DA:1072,1288 -DA:1073,1125 -DA:1079,8 -DA:1081,8 -DA:1082,8 -DA:1083,8 -DA:1084,19 -DA:1088,8 -DA:1089,12 -DA:1090,12 -DA:1091,12 -DA:1093,4 -DA:1094,4 -DA:1098,0 -DA:1099,0 -DA:1103,8 -DA:1104,8 -DA:1108,8 -DA:1112,1 -DA:1115,1 -DA:1117,1 -DA:1120,1 -DA:1121,1 -DA:1122,3777 -DA:1124,3777 -DA:1126,3777 -DA:1128,840 -DA:1133,840 -DA:1136,2937 -DA:1141,2937 -DA:1145,1 -DA:1148,0 -DA:1149,0 -DA:1154,400 -DA:1155,996 -DA:1158,400 -DA:1159,400 -DA:1164,3 -DA:1166,3 -DA:1167,6 -DA:1170,3 -DA:1171,3 -DA:1176,1349 -DA:1177,1349 -DA:1182,276 -DA:1183,276 -DA:1188,277 -DA:1193,6 -DA:1197,1 -DA:1200,1 -DA:1202,1 -DA:1205,11 -DA:1206,11 -LF:441 -LH:415 +DA:712,5 +DA:713,5 +DA:715,5 +DA:716,14 +DA:717,9 +DA:721,5 +DA:726,8 +DA:727,8 +DA:729,2 +DA:731,2 +DA:732,2 +DA:734,0 +DA:737,4 +DA:741,8 +DA:743,12 +DA:744,4 +DA:746,8 +DA:755,12 +DA:760,8 +DA:765,8 +DA:779,2 +DA:780,1 +DA:782,1 +DA:793,1280 +DA:798,102 +DA:803,18 +DA:808,2 +DA:813,17 +DA:815,17 +DA:816,17 +DA:817,168 +DA:818,84 +DA:821,17 +DA:826,32 +DA:827,21 +DA:829,11 +DA:844,15 +DA:846,15 +DA:848,15 +DA:850,18 +DA:852,18 +DA:855,10 +DA:856,10 +DA:858,9 +DA:859,9 +DA:860,3 +DA:862,6 +DA:865,1 +DA:867,10 +DA:870,8 +DA:872,8 +DA:874,8 +DA:875,3 +DA:876,3 +DA:877,3 +DA:879,0 +DA:883,0 +DA:885,8 +DA:888,15 +DA:909,3823 +DA:911,3823 +DA:919,3823 +DA:922,2971 +DA:923,2971 +DA:924,2971 +DA:926,852 +DA:927,852 +DA:931,3823 +DA:935,3823 +DA:939,3823 +DA:941,3823 +DA:942,3823 +DA:946,3823 +DA:949,3823 +DA:951,648 +DA:953,648 +DA:955,392 +DA:956,392 +DA:959,648 +DA:960,681 +DA:961,28 +DA:962,1 +DA:965,27 +DA:966,27 +DA:968,27 +DA:971,648 +DA:973,852 +DA:975,852 +DA:977,13 +DA:978,13 +DA:981,852 +DA:982,922 +DA:983,64 +DA:984,1 +DA:987,63 +DA:988,63 +DA:990,63 +DA:992,63 +DA:993,20 +DA:994,7 +DA:995,13 +DA:997,12 +DA:999,1 +DA:1010,852 +DA:1013,2323 +DA:1015,2323 +DA:1016,2323 +DA:1017,0 +DA:1020,2323 +DA:1022,0 +DA:1025,3823 +DA:1026,3823 +DA:1030,3823 +DA:1034,267 +DA:1037,3823 +DA:1038,1500 +DA:1042,1500 +DA:1046,87 +DA:1050,3823 +DA:1073,3823 +DA:1075,3823 +DA:1077,3823 +DA:1082,1302 +DA:1083,1139 +DA:1089,8 +DA:1091,8 +DA:1092,8 +DA:1093,8 +DA:1094,19 +DA:1098,8 +DA:1099,12 +DA:1100,12 +DA:1101,12 +DA:1103,4 +DA:1104,4 +DA:1108,0 +DA:1109,0 +DA:1113,8 +DA:1114,8 +DA:1118,8 +DA:1122,1 +DA:1125,1 +DA:1127,1 +DA:1130,1 +DA:1131,1 +DA:1132,3823 +DA:1134,3823 +DA:1136,3823 +DA:1138,852 +DA:1143,852 +DA:1146,2971 +DA:1151,2971 +DA:1155,1 +DA:1158,0 +DA:1159,0 +DA:1164,404 +DA:1165,1006 +DA:1168,404 +DA:1169,404 +DA:1174,3 +DA:1176,3 +DA:1177,6 +DA:1180,3 +DA:1181,3 +DA:1186,1365 +DA:1187,1365 +DA:1192,280 +DA:1193,280 +DA:1198,281 +DA:1203,6 +DA:1207,1 +DA:1210,1 +DA:1212,1 +DA:1215,11 +DA:1216,11 +LF:444 +LH:418 BRDA:7,1,0,35 BRDA:7,1,1,35 BRDA:7,2,0,35 @@ -1056,182 +1061,182 @@ BRDA:7,3,1,0 BRDA:7,4,0,0 BRDA:7,4,1,2 BRDA:9,5,0,3 -BRDA:9,5,1,2 -BRDA:9,6,0,5 -BRDA:9,6,1,5 +BRDA:9,5,1,3 +BRDA:9,6,0,6 +BRDA:9,6,1,6 BRDA:11,7,0,5 BRDA:11,7,1,0 BRDA:13,8,0,0 -BRDA:13,8,1,5042 -BRDA:113,9,0,87 -BRDA:113,9,1,86 -BRDA:127,10,0,123 -BRDA:127,10,1,122 -BRDA:185,11,0,647 -BRDA:185,11,1,646 -BRDA:189,12,0,3 -BRDA:189,12,1,644 -BRDA:231,13,0,0 -BRDA:231,13,1,22 -BRDA:239,14,0,12 -BRDA:239,14,1,2 -BRDA:256,15,0,2 -BRDA:256,15,1,12 -BRDA:259,16,0,12 -BRDA:259,16,1,10 -BRDA:275,17,0,14 -BRDA:275,17,1,12 -BRDA:282,18,0,0 -BRDA:282,18,1,14 -BRDA:282,19,0,14 -BRDA:282,19,1,0 -BRDA:286,20,0,0 -BRDA:286,20,1,14 -BRDA:317,21,0,5 -BRDA:317,21,1,20 -BRDA:317,21,2,6 -BRDA:322,22,0,16 -BRDA:322,22,1,4 -BRDA:324,23,0,1 -BRDA:324,23,1,3 -BRDA:331,24,0,3 -BRDA:331,24,1,3 -BRDA:344,25,0,5 -BRDA:344,25,1,26 -BRDA:346,26,0,3 -BRDA:346,26,1,28 -BRDA:352,27,0,3 -BRDA:352,27,1,29 -BRDA:356,28,0,26 -BRDA:356,28,1,3 -BRDA:363,29,0,1 -BRDA:363,29,1,24 -BRDA:368,30,0,3 -BRDA:368,30,1,22 -BRDA:373,31,0,15 -BRDA:373,31,1,7 -BRDA:386,32,0,1 -BRDA:386,32,1,15 -BRDA:390,33,0,5 -BRDA:390,33,1,8 -BRDA:390,33,2,2 -BRDA:398,34,0,1 -BRDA:398,34,1,4 -BRDA:404,35,0,0 -BRDA:404,35,1,1 -BRDA:429,36,0,5 -BRDA:429,36,1,5 -BRDA:444,37,0,1 -BRDA:444,37,1,3 -BRDA:464,38,0,4 -BRDA:464,38,1,4 -BRDA:470,39,0,4 -BRDA:470,39,1,0 -BRDA:472,40,0,4 -BRDA:472,40,1,2 -BRDA:474,41,0,1 -BRDA:474,41,1,3 -BRDA:484,42,0,0 -BRDA:484,42,1,1 -BRDA:484,42,2,2 -BRDA:577,43,0,1 -BRDA:577,43,1,1 -BRDA:594,44,0,12 -BRDA:594,44,1,0 -BRDA:600,45,0,7 -BRDA:600,45,1,0 -BRDA:612,46,0,9 -BRDA:612,46,1,1 -BRDA:613,47,0,1 -BRDA:613,47,1,8 -BRDA:617,48,0,7 -BRDA:617,48,1,1 -BRDA:620,49,0,4 -BRDA:620,49,1,2 -BRDA:627,50,0,0 -BRDA:627,50,1,7 -BRDA:629,51,0,1 -BRDA:629,51,1,6 -BRDA:650,52,0,1 -BRDA:650,52,1,12 -BRDA:658,53,0,8 -BRDA:658,53,1,4 -BRDA:664,54,0,4 -BRDA:664,54,1,0 -BRDA:686,55,0,3 -BRDA:686,55,1,5 -BRDA:692,56,0,0 -BRDA:692,56,1,3 -BRDA:706,57,0,9 -BRDA:706,57,1,5 -BRDA:717,58,0,2 -BRDA:717,58,1,2 -BRDA:717,58,2,4 -BRDA:721,59,0,2 -BRDA:721,59,1,0 -BRDA:733,60,0,4 -BRDA:733,60,1,8 -BRDA:769,61,0,1 -BRDA:769,61,1,1 -BRDA:769,62,0,2 -BRDA:769,62,1,1 -BRDA:807,63,0,84 -BRDA:807,63,1,84 -BRDA:816,64,0,21 -BRDA:816,64,1,11 -BRDA:842,65,0,7 -BRDA:842,65,1,10 -BRDA:842,65,2,8 -BRDA:846,66,0,9 -BRDA:846,66,1,1 -BRDA:849,67,0,3 -BRDA:849,67,1,6 -BRDA:862,68,0,8 -BRDA:862,68,1,0 -BRDA:866,69,0,3 -BRDA:866,69,1,0 -BRDA:909,70,0,642 -BRDA:909,70,1,2937 -BRDA:909,70,2,840 -BRDA:939,71,0,642 -BRDA:939,71,1,840 -BRDA:939,71,2,2295 -BRDA:939,71,3,0 -BRDA:950,72,0,28 -BRDA:950,72,1,647 -BRDA:950,73,0,675 -BRDA:950,73,1,32 -BRDA:951,74,0,1 -BRDA:951,74,1,27 -BRDA:972,75,0,64 -BRDA:972,75,1,846 -BRDA:972,76,0,910 -BRDA:972,76,1,68 -BRDA:973,77,0,1 -BRDA:973,77,1,63 -BRDA:982,78,0,20 -BRDA:982,78,1,43 -BRDA:983,79,0,7 -BRDA:983,79,1,13 -BRDA:985,80,0,12 -BRDA:985,80,1,1 -BRDA:1006,81,0,0 -BRDA:1006,81,1,2295 -BRDA:1006,82,0,2295 -BRDA:1006,82,1,0 -BRDA:1027,83,0,1482 -BRDA:1027,83,1,2295 -BRDA:1091,84,0,4 -BRDA:1091,84,1,8 -BRDA:1093,85,0,4 -BRDA:1093,85,1,0 -BRDA:1093,86,0,4 -BRDA:1093,86,1,4 -BRDA:1126,87,0,840 -BRDA:1126,87,1,642 -BRDA:1126,87,2,2937 -BRDA:1164,88,0,3 -BRDA:1164,88,1,3 +BRDA:13,8,1,5102 +BRDA:123,9,0,87 +BRDA:123,9,1,86 +BRDA:137,10,0,123 +BRDA:137,10,1,122 +BRDA:195,11,0,655 +BRDA:195,11,1,654 +BRDA:199,12,0,3 +BRDA:199,12,1,652 +BRDA:241,13,0,0 +BRDA:241,13,1,22 +BRDA:249,14,0,12 +BRDA:249,14,1,2 +BRDA:266,15,0,2 +BRDA:266,15,1,12 +BRDA:269,16,0,12 +BRDA:269,16,1,10 +BRDA:285,17,0,14 +BRDA:285,17,1,12 +BRDA:292,18,0,0 +BRDA:292,18,1,14 +BRDA:292,19,0,14 +BRDA:292,19,1,0 +BRDA:296,20,0,0 +BRDA:296,20,1,14 +BRDA:327,21,0,5 +BRDA:327,21,1,20 +BRDA:327,21,2,6 +BRDA:332,22,0,16 +BRDA:332,22,1,4 +BRDA:334,23,0,1 +BRDA:334,23,1,3 +BRDA:341,24,0,3 +BRDA:341,24,1,3 +BRDA:354,25,0,5 +BRDA:354,25,1,26 +BRDA:356,26,0,3 +BRDA:356,26,1,28 +BRDA:362,27,0,3 +BRDA:362,27,1,29 +BRDA:366,28,0,26 +BRDA:366,28,1,3 +BRDA:373,29,0,1 +BRDA:373,29,1,24 +BRDA:378,30,0,3 +BRDA:378,30,1,22 +BRDA:383,31,0,15 +BRDA:383,31,1,7 +BRDA:396,32,0,1 +BRDA:396,32,1,15 +BRDA:400,33,0,5 +BRDA:400,33,1,8 +BRDA:400,33,2,2 +BRDA:408,34,0,1 +BRDA:408,34,1,4 +BRDA:414,35,0,0 +BRDA:414,35,1,1 +BRDA:439,36,0,5 +BRDA:439,36,1,5 +BRDA:454,37,0,1 +BRDA:454,37,1,3 +BRDA:474,38,0,4 +BRDA:474,38,1,4 +BRDA:480,39,0,4 +BRDA:480,39,1,0 +BRDA:482,40,0,4 +BRDA:482,40,1,2 +BRDA:484,41,0,1 +BRDA:484,41,1,3 +BRDA:494,42,0,0 +BRDA:494,42,1,1 +BRDA:494,42,2,2 +BRDA:587,43,0,1 +BRDA:587,43,1,1 +BRDA:604,44,0,12 +BRDA:604,44,1,0 +BRDA:610,45,0,7 +BRDA:610,45,1,0 +BRDA:622,46,0,9 +BRDA:622,46,1,1 +BRDA:623,47,0,1 +BRDA:623,47,1,8 +BRDA:627,48,0,7 +BRDA:627,48,1,1 +BRDA:630,49,0,4 +BRDA:630,49,1,2 +BRDA:637,50,0,0 +BRDA:637,50,1,7 +BRDA:639,51,0,1 +BRDA:639,51,1,6 +BRDA:660,52,0,1 +BRDA:660,52,1,12 +BRDA:668,53,0,8 +BRDA:668,53,1,4 +BRDA:674,54,0,4 +BRDA:674,54,1,0 +BRDA:696,55,0,3 +BRDA:696,55,1,5 +BRDA:702,56,0,0 +BRDA:702,56,1,3 +BRDA:716,57,0,9 +BRDA:716,57,1,5 +BRDA:727,58,0,2 +BRDA:727,58,1,2 +BRDA:727,58,2,4 +BRDA:731,59,0,2 +BRDA:731,59,1,0 +BRDA:743,60,0,4 +BRDA:743,60,1,8 +BRDA:779,61,0,1 +BRDA:779,61,1,1 +BRDA:779,62,0,2 +BRDA:779,62,1,1 +BRDA:817,63,0,84 +BRDA:817,63,1,84 +BRDA:826,64,0,21 +BRDA:826,64,1,11 +BRDA:852,65,0,7 +BRDA:852,65,1,10 +BRDA:852,65,2,8 +BRDA:856,66,0,9 +BRDA:856,66,1,1 +BRDA:859,67,0,3 +BRDA:859,67,1,6 +BRDA:872,68,0,8 +BRDA:872,68,1,0 +BRDA:876,69,0,3 +BRDA:876,69,1,0 +BRDA:919,70,0,648 +BRDA:919,70,1,2971 +BRDA:919,70,2,852 +BRDA:949,71,0,648 +BRDA:949,71,1,852 +BRDA:949,71,2,2323 +BRDA:949,71,3,0 +BRDA:960,72,0,28 +BRDA:960,72,1,653 +BRDA:960,73,0,681 +BRDA:960,73,1,32 +BRDA:961,74,0,1 +BRDA:961,74,1,27 +BRDA:982,75,0,64 +BRDA:982,75,1,858 +BRDA:982,76,0,922 +BRDA:982,76,1,68 +BRDA:983,77,0,1 +BRDA:983,77,1,63 +BRDA:992,78,0,20 +BRDA:992,78,1,43 +BRDA:993,79,0,7 +BRDA:993,79,1,13 +BRDA:995,80,0,12 +BRDA:995,80,1,1 +BRDA:1016,81,0,0 +BRDA:1016,81,1,2323 +BRDA:1016,82,0,2323 +BRDA:1016,82,1,0 +BRDA:1037,83,0,1500 +BRDA:1037,83,1,2323 +BRDA:1101,84,0,4 +BRDA:1101,84,1,8 +BRDA:1103,85,0,4 +BRDA:1103,85,1,0 +BRDA:1103,86,0,4 +BRDA:1103,86,1,4 +BRDA:1136,87,0,852 +BRDA:1136,87,1,648 +BRDA:1136,87,2,2971 +BRDA:1174,88,0,3 +BRDA:1174,88,1,3 BRF:185 BRH:161 end_of_record diff --git a/package.json b/package.json index 4670af7..cbd1764 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dovima", - "version": "0.1.4", + "version": "0.1.5", "description": "ES6 generic model with lots of useful special features.", "main": "index.js", "scripts": { @@ -38,12 +38,15 @@ "blunder": "^0.1.0", "fleming": "^0.1.0", "flowsync": "^0.1.4", - "jargon": "^0.1.14" + "jargon": "^0.1.14", + "quirk": "0.0.2" }, "devDependencies": { "babel": "^5.5.6", + "babel-eslint": "^3.1.23", "chai": "^3.0.0", "coveralls": "^2.11.2", + "eslint": "^0.24.1", "gulp": "^3.9.0", "gulp-babel": "^5.1.0", "gulp-istanbul": "^0.10.0", From 6529b7f5b8d0caeec53caf5c96f9a9e7157f94c9 Mon Sep 17 00:00:00 2001 From: nicosommi Date: Wed, 15 Jul 2015 14:08:44 -0300 Subject: [PATCH 15/15] quirk support v1 --- es5/lib/model.js | 15 +- es5/spec/model.spec.js | 52 +- es6/lib/model.js | 13 +- es6/spec/model.spec.js | 52 +- lcov.info | 1390 ++++++++++++++++++++-------------------- 5 files changed, 813 insertions(+), 709 deletions(-) diff --git a/es5/lib/model.js b/es5/lib/model.js index 82998ae..c2d4333 100644 --- a/es5/lib/model.js +++ b/es5/lib/model.js @@ -155,6 +155,9 @@ var Model = (function () { } }); + //add the quirk to this instance + this.additionalAttributes.addAttributes(this); + this.associate(); this.validate(); @@ -1092,7 +1095,13 @@ var Model = (function () { var attributeNames = Object.keys(this.attributes); var fieldAttributes = {}; attributeNames.forEach(function (attributeName) { - fieldAttributes[(0, _jargon2["default"])(attributeName).snake.toString()] = _this10[attributeName]; + var found = Object.keys(_this10.additionalAttributes).find(function (additionalAttributeName) { + return additionalAttributeName === attributeName; + }); + //is just on db if is not an additional attribute + if (!found) { + fieldAttributes[(0, _jargon2["default"])(attributeName).snake.toString()] = _this10[attributeName]; + } }); //add belongsTo associations and remove others @@ -1217,8 +1226,8 @@ Object.defineProperties(Model, { return modelQuery.find(this); } }, - //problem here: if it's static on Model, it will be shared for every Model - //if it's static on Item, it will be inaccesible from Model methods to use that on save for eg + //problem here: can't assign property automatically to the concrete model to use it + //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe#Browser_compatibility "attributes": { value: new _quirk2["default"]() } diff --git a/es5/spec/model.spec.js b/es5/spec/model.spec.js index 45080b6..de14425 100644 --- a/es5/spec/model.spec.js +++ b/es5/spec/model.spec.js @@ -354,6 +354,11 @@ describe("Model(attributes, options)", function () { describe("(static properties)", function () { describe(".attributes", function () { + afterEach(function () { + delete User.attributes.specialAttribute; //so does not affect other tests + user = new User(); + }); + it("should be an instance of Quirk", function () { User.attributes.should.be.instanceOf(_quirk2["default"]); }); @@ -364,15 +369,50 @@ describe("Model(attributes, options)", function () { it("should be able to define a new static property to the model", function () { User.attributes.specialAttribute = 2; + user = new User(); user.specialAttribute.should.equal(2); }); - it("should be able to define a new static property to the model", function () { - User.attributes.specialAttribute = function specialAttributeGetter() { - return this.id; - }; - user = new User({ id: 2 }); - user.specialAttribute.should.equal(2); + describe("(read only)", function () { + beforeEach(function () { + User.attributes.specialAttribute = function specialAttributeGetter() { + return this.id; + }; + user = new User({ id: 2 }); + }); + + it("should be able to define a new read only property to the model", function () { + user.specialAttribute.should.equal(2); + }); + + it("should throw when assign to a new read only property", function () { + (function () { + user.specialAttribute = 3; + }).should["throw"]("Cannot set property"); + }); + }); + + describe("(getter and setter)", function () { + beforeEach(function () { + User.attributes.specialAttribute = { + get: function getSpecialAttribute() { + return this.id; + }, + set: function setSpecialAttribute(newValue) { + this.id = newValue; + } + }; + user = new User({ id: 2 }); + }); + + it("should be able to define a new property with get and set to the model", function () { + user.specialAttribute.should.equal(2); + }); + + it("should be able to change a property with get and set to the model", function () { + user.specialAttribute = 3; + user.specialAttribute.should.equal(3); + }); }); }); diff --git a/es6/lib/model.js b/es6/lib/model.js index c2c0dd3..02620ce 100644 --- a/es6/lib/model.js +++ b/es6/lib/model.js @@ -121,6 +121,9 @@ export default class Model { } }); + //add the quirk to this instance + this.additionalAttributes.addAttributes(this); + this.associate(); this.validate(); @@ -1024,12 +1027,16 @@ export default class Model { } [getFieldAttributes]() { - //this, using quirk, will return target with the new attributes on it, useful to EXCLUDE them from here - //TODO: this.additionalAttributes.addAttributes(target); //magic line let attributeNames = Object.keys(this.attributes); let fieldAttributes = {}; attributeNames.forEach((attributeName) => { - fieldAttributes[inflect(attributeName).snake.toString()] = this[attributeName]; + let found = Object.keys(this.additionalAttributes).find((additionalAttributeName) => { + return additionalAttributeName === attributeName; + }); + //is just on db if is not an additional attribute + if(!found) { + fieldAttributes[inflect(attributeName).snake.toString()] = this[attributeName]; + } }); //add belongsTo associations and remove others diff --git a/es6/spec/model.spec.js b/es6/spec/model.spec.js index 8c7183f..c92f9a0 100644 --- a/es6/spec/model.spec.js +++ b/es6/spec/model.spec.js @@ -250,6 +250,11 @@ describe("Model(attributes, options)", () => { describe("(static properties)", () => { describe(".attributes", () => { + afterEach(() => { + delete User.attributes.specialAttribute; //so does not affect other tests + user = new User(); + }); + it("should be an instance of Quirk", () => { User.attributes.should.be.instanceOf(Quirk); }); @@ -260,15 +265,50 @@ describe("Model(attributes, options)", () => { it("should be able to define a new static property to the model", () => { User.attributes.specialAttribute = 2; + user = new User(); user.specialAttribute.should.equal(2); }); - it("should be able to define a new static property to the model", () => { - User.attributes.specialAttribute = function specialAttributeGetter() { - return this.id; - }; - user = new User({id: 2}); - user.specialAttribute.should.equal(2); + describe("(read only)", () => { + beforeEach(() => { + User.attributes.specialAttribute = function specialAttributeGetter() { + return this.id; + }; + user = new User({id: 2}); + }); + + it("should be able to define a new read only property to the model", () => { + user.specialAttribute.should.equal(2); + }); + + it("should throw when assign to a new read only property", () => { + () => { + user.specialAttribute = 3; + }.should.throw("Cannot set property"); + }); + }); + + describe("(getter and setter)", () => { + beforeEach(() => { + User.attributes.specialAttribute = { + get: function getSpecialAttribute() { + return this.id; + }, + set: function setSpecialAttribute(newValue) { + this.id = newValue; + } + }; + user = new User({id: 2}); + }); + + it("should be able to define a new property with get and set to the model", () => { + user.specialAttribute.should.equal(2); + }); + + it("should be able to change a property with get and set to the model", () => { + user.specialAttribute = 3; + user.specialAttribute.should.equal(3); + }); }); }); diff --git a/lcov.info b/lcov.info index 1b20985..7d20161 100644 --- a/lcov.info +++ b/lcov.info @@ -25,13 +25,13 @@ FNH:20 FNDA:1,(anonymous_1) FNDA:1,defineProperties FNDA:1,(anonymous_3) -FNDA:2507,get +FNDA:2637,get FNDA:3,_interopRequireDefault FNDA:3,_toConsumableArray -FNDA:2352,_classCallCheck +FNDA:2482,_classCallCheck FNDA:1,_inherits FNDA:1,(anonymous_9) -FNDA:2352,Collection +FNDA:2482,Collection FNDA:160,push FNDA:160,(anonymous_12) FNDA:4,fetch @@ -44,10 +44,10 @@ FNDA:4,(anonymous_19) FNDA:13,(anonymous_20) DA:3,1 DA:7,2 -DA:9,2507 +DA:9,2637 DA:11,3 DA:13,6 -DA:15,2352 +DA:15,2482 DA:17,1 DA:19,1 DA:21,1 @@ -57,13 +57,13 @@ DA:27,1 DA:29,1 DA:31,1 DA:32,1 -DA:33,2352 -DA:35,2352 -DA:37,2352 -DA:40,2352 +DA:33,2482 +DA:35,2482 +DA:37,2482 +DA:40,2482 DA:41,29 -DA:43,2323 -DA:46,2352 +DA:43,2453 +DA:46,2482 DA:59,1 DA:61,1 DA:64,160 @@ -127,12 +127,12 @@ BRDA:7,3,1,0 BRDA:7,4,0,0 BRDA:7,4,1,1 BRDA:9,5,0,0 -BRDA:9,5,1,2507 +BRDA:9,5,1,2637 BRDA:9,6,0,0 -BRDA:9,6,1,2507 +BRDA:9,6,1,2637 BRDA:9,7,0,0 BRDA:9,7,1,0 -BRDA:9,8,0,2507 +BRDA:9,8,0,2637 BRDA:9,8,1,0 BRDA:9,9,0,0 BRDA:9,9,1,0 @@ -143,7 +143,7 @@ BRDA:11,11,1,3 BRDA:13,12,0,3 BRDA:13,12,1,0 BRDA:15,13,0,0 -BRDA:15,13,1,2352 +BRDA:15,13,1,2482 BRDA:17,14,0,0 BRDA:17,14,1,1 BRDA:17,15,0,1 @@ -153,9 +153,9 @@ BRDA:17,16,1,1 BRDA:17,17,0,1 BRDA:17,17,1,0 BRDA:40,18,0,29 -BRDA:40,18,1,2323 -BRDA:40,19,0,2352 -BRDA:40,19,1,2352 +BRDA:40,18,1,2453 +BRDA:40,19,0,2482 +BRDA:40,19,1,2482 BRDA:71,20,0,155 BRDA:71,20,1,5 BRDA:72,21,0,65 @@ -381,134 +381,135 @@ FN:13,_classCallCheck FN:61,(anonymous_7) FN:67,Model FN:72,(anonymous_9) -FN:90,get -FN:122,get -FN:125,set -FN:136,get -FN:139,set -FN:151,get -FN:167,hasOne -FN:176,belongsTo -FN:185,hasMany -FN:194,ensure -FN:214,isValid -FN:215,(anonymous_21) -FN:235,invalidAttributes -FN:240,compileInvalidAttributeList -FN:259,performValidationsForAttribute -FN:262,performValidation -FN:265,(anonymous_26) -FN:274,compileValidatorResponses -FN:312,include -FN:322,fetch -FN:351,value -FN:361,(anonymous_31) -FN:377,(anonymous_32) -FN:384,(anonymous_33) -FN:392,(anonymous_34) -FN:402,(anonymous_35) -FN:418,(anonymous_36) -FN:428,(anonymous_37) -FN:435,(anonymous_38) -FN:438,processWhereCondition -FN:449,(anonymous_40) -FN:455,(anonymous_41) -FN:462,(anonymous_42) -FN:475,(anonymous_43) -FN:479,(anonymous_44) -FN:481,(anonymous_45) -FN:498,(anonymous_46) -FN:502,(anonymous_47) -FN:503,(anonymous_48) -FN:512,(anonymous_49) -FN:516,(anonymous_50) -FN:517,(anonymous_51) -FN:532,(anonymous_52) -FN:536,(anonymous_53) -FN:537,(anonymous_54) -FN:580,(anonymous_55) -FN:591,(anonymous_56) -FN:592,(anonymous_57) -FN:603,(anonymous_58) -FN:619,_delete -FN:628,(anonymous_60) -FN:629,(anonymous_61) -FN:632,(anonymous_62) -FN:636,(anonymous_63) -FN:645,(anonymous_64) -FN:657,save -FN:664,(anonymous_66) -FN:666,(anonymous_67) -FN:667,(anonymous_68) -FN:671,(anonymous_69) -FN:693,(anonymous_70) -FN:695,(anonymous_71) -FN:701,(anonymous_72) -FN:723,(anonymous_73) -FN:726,(anonymous_74) -FN:740,(anonymous_75) -FN:742,(anonymous_76) -FN:754,beforeValidation -FN:759,beforeSave -FN:764,afterSave -FN:769,associate -FN:772,validate -FN:775,initialize -FN:778,toJSON -FN:792,value -FN:797,value -FN:802,value -FN:807,value -FN:812,value -FN:816,(anonymous_89) -FN:825,value -FN:843,value -FN:848,(anonymous_92) -FN:874,(anonymous_93) -FN:887,(anonymous_94) -FN:908,value -FN:953,(anonymous_96) -FN:959,(anonymous_97) -FN:975,(anonymous_98) -FN:981,(anonymous_99) -FN:1015,(anonymous_100) -FN:1033,get -FN:1045,get -FN:1081,value -FN:1088,value -FN:1093,(anonymous_105) +FN:91,get +FN:123,get +FN:126,set +FN:137,get +FN:140,set +FN:152,get +FN:171,hasOne +FN:180,belongsTo +FN:189,hasMany +FN:198,ensure +FN:218,isValid +FN:219,(anonymous_21) +FN:239,invalidAttributes +FN:244,compileInvalidAttributeList +FN:263,performValidationsForAttribute +FN:266,performValidation +FN:269,(anonymous_26) +FN:278,compileValidatorResponses +FN:316,include +FN:326,fetch +FN:355,value +FN:365,(anonymous_31) +FN:381,(anonymous_32) +FN:388,(anonymous_33) +FN:396,(anonymous_34) +FN:406,(anonymous_35) +FN:422,(anonymous_36) +FN:432,(anonymous_37) +FN:439,(anonymous_38) +FN:442,processWhereCondition +FN:453,(anonymous_40) +FN:459,(anonymous_41) +FN:466,(anonymous_42) +FN:479,(anonymous_43) +FN:483,(anonymous_44) +FN:485,(anonymous_45) +FN:502,(anonymous_46) +FN:506,(anonymous_47) +FN:507,(anonymous_48) +FN:516,(anonymous_49) +FN:520,(anonymous_50) +FN:521,(anonymous_51) +FN:536,(anonymous_52) +FN:540,(anonymous_53) +FN:541,(anonymous_54) +FN:584,(anonymous_55) +FN:595,(anonymous_56) +FN:596,(anonymous_57) +FN:607,(anonymous_58) +FN:623,_delete +FN:632,(anonymous_60) +FN:633,(anonymous_61) +FN:636,(anonymous_62) +FN:640,(anonymous_63) +FN:649,(anonymous_64) +FN:661,save +FN:668,(anonymous_66) +FN:670,(anonymous_67) +FN:671,(anonymous_68) +FN:675,(anonymous_69) +FN:697,(anonymous_70) +FN:699,(anonymous_71) +FN:705,(anonymous_72) +FN:727,(anonymous_73) +FN:730,(anonymous_74) +FN:744,(anonymous_75) +FN:746,(anonymous_76) +FN:758,beforeValidation +FN:763,beforeSave +FN:768,afterSave +FN:773,associate +FN:776,validate +FN:779,initialize +FN:782,toJSON +FN:796,value +FN:801,value +FN:806,value +FN:811,value +FN:816,value +FN:820,(anonymous_89) +FN:829,value +FN:847,value +FN:852,(anonymous_92) +FN:878,(anonymous_93) +FN:891,(anonymous_94) +FN:912,value +FN:957,(anonymous_96) +FN:963,(anonymous_97) +FN:979,(anonymous_98) +FN:985,(anonymous_99) +FN:1019,(anonymous_100) +FN:1037,get +FN:1049,get +FN:1085,value +FN:1092,value +FN:1097,(anonymous_105) FN:1098,(anonymous_106) -FN:1130,(anonymous_107) -FN:1131,AssociationSetter -FN:1157,foreignName -FN:1163,where -FN:1173,andWhere -FN:1185,through -FN:1191,as -FN:1197,value -FN:1202,value -FN:1214,modelFind -FNF:116 +FN:1108,(anonymous_107) +FN:1140,(anonymous_108) +FN:1141,AssociationSetter +FN:1167,foreignName +FN:1173,where +FN:1183,andWhere +FN:1195,through +FN:1201,as +FN:1207,value +FN:1212,value +FN:1224,modelFind +FNF:117 FNH:112 FNDA:1,(anonymous_1) FNDA:2,defineProperties FNDA:2,(anonymous_3) FNDA:6,_interopRequireDefault FNDA:5,_toConsumableArray -FNDA:5102,_classCallCheck +FNDA:5360,_classCallCheck FNDA:1,(anonymous_7) -FNDA:1279,Model -FNDA:2558,(anonymous_9) -FNDA:1,get +FNDA:1326,Model +FNDA:2652,(anonymous_9) +FNDA:1346,get FNDA:87,get FNDA:1,set FNDA:123,get FNDA:1,set FNDA:21,get -FNDA:648,hasOne -FNDA:852,belongsTo -FNDA:2323,hasMany -FNDA:655,ensure +FNDA:699,hasOne +FNDA:882,belongsTo +FNDA:2453,hasMany +FNDA:687,ensure FNDA:14,isValid FNDA:14,(anonymous_21) FNDA:22,invalidAttributes @@ -569,11 +570,11 @@ FNDA:12,(anonymous_76) FNDA:12,beforeValidation FNDA:8,beforeSave FNDA:8,afterSave -FNDA:336,associate -FNDA:657,validate -FNDA:1252,initialize +FNDA:341,associate +FNDA:672,validate +FNDA:1299,initialize FNDA:2,toJSON -FNDA:1280,value +FNDA:1327,value FNDA:102,value FNDA:18,value FNDA:2,value @@ -584,33 +585,34 @@ FNDA:15,value FNDA:18,(anonymous_92) FNDA:3,(anonymous_93) FNDA:15,(anonymous_94) -FNDA:3823,value -FNDA:392,(anonymous_96) -FNDA:681,(anonymous_97) +FNDA:4034,value +FNDA:407,(anonymous_96) +FNDA:732,(anonymous_97) FNDA:13,(anonymous_98) -FNDA:922,(anonymous_99) -FNDA:2323,(anonymous_100) +FNDA:952,(anonymous_99) +FNDA:2453,(anonymous_100) FNDA:267,get FNDA:87,get -FNDA:1302,value +FNDA:1349,value FNDA:8,value FNDA:19,(anonymous_105) -FNDA:12,(anonymous_106) -FNDA:1,(anonymous_107) -FNDA:3823,AssociationSetter +FNDA:0,(anonymous_106) +FNDA:12,(anonymous_107) +FNDA:1,(anonymous_108) +FNDA:4034,AssociationSetter FNDA:0,foreignName -FNDA:404,where +FNDA:438,where FNDA:3,andWhere -FNDA:1365,through -FNDA:280,as -FNDA:281,value +FNDA:1453,through +FNDA:290,as +FNDA:291,value FNDA:6,value FNDA:11,modelFind DA:3,1 DA:7,35 DA:9,6 DA:11,10 -DA:13,5102 +DA:13,5360 DA:18,1 DA:20,1 DA:22,1 @@ -627,431 +629,435 @@ DA:42,1 DA:45,1 DA:61,1 DA:67,1 -DA:68,1279 -DA:70,1279 -DA:72,1279 -DA:73,2558 -DA:82,1279 -DA:91,1 -DA:123,87 -DA:126,1 -DA:137,123 -DA:140,1 -DA:152,21 -DA:157,1279 -DA:158,1279 -DA:160,1279 -DA:162,1279 -DA:165,1 -DA:168,648 -DA:177,852 -DA:186,2323 -DA:195,655 -DA:197,655 -DA:199,655 -DA:200,3 -DA:203,655 -DA:215,14 -DA:216,14 -DA:236,22 -DA:238,22 +DA:68,1326 +DA:70,1326 +DA:72,1326 +DA:73,2652 +DA:82,1326 +DA:92,1346 +DA:124,87 +DA:127,1 +DA:138,123 +DA:141,1 +DA:153,21 +DA:159,1326 +DA:161,1326 +DA:162,1326 +DA:164,1326 +DA:166,1326 +DA:169,1 +DA:172,699 +DA:181,882 +DA:190,2453 +DA:199,687 +DA:201,687 +DA:203,687 +DA:204,3 +DA:207,687 +DA:219,14 +DA:220,14 DA:240,22 -DA:241,22 -DA:242,0 +DA:242,22 DA:244,22 -DA:246,22 -DA:247,14 -DA:249,14 -DA:250,12 -DA:251,12 -DA:255,22 +DA:245,22 +DA:246,0 +DA:248,22 +DA:250,22 +DA:251,14 +DA:253,14 +DA:254,12 +DA:255,12 DA:259,22 -DA:260,14 -DA:262,14 -DA:263,14 -DA:265,14 +DA:263,22 +DA:264,14 DA:266,14 -DA:267,2 -DA:269,12 -DA:274,14 -DA:275,14 -DA:277,14 +DA:267,14 +DA:269,14 +DA:270,14 +DA:271,2 +DA:273,12 DA:278,14 DA:279,14 DA:281,14 DA:282,14 DA:283,14 DA:285,14 -DA:288,0 -DA:289,0 -DA:291,14 -DA:292,14 +DA:286,14 +DA:287,14 +DA:289,14 +DA:292,0 DA:293,0 +DA:295,14 DA:296,14 DA:297,0 -DA:302,14 -DA:305,14 -DA:308,22 -DA:313,16 -DA:314,18 +DA:300,14 +DA:301,0 +DA:306,14 +DA:309,14 +DA:312,22 DA:317,16 -DA:318,16 -DA:323,31 -DA:324,32 +DA:318,18 +DA:321,16 +DA:322,16 DA:327,31 -DA:329,5 -DA:330,0 -DA:332,20 -DA:333,16 -DA:334,4 -DA:335,1 -DA:337,3 -DA:339,16 -DA:341,6 -DA:342,3 -DA:344,3 -DA:346,6 -DA:352,31 -DA:354,31 +DA:328,32 +DA:331,31 +DA:333,5 +DA:334,0 +DA:336,20 +DA:337,16 +DA:338,4 +DA:339,1 +DA:341,3 +DA:343,16 +DA:345,6 +DA:346,3 +DA:348,3 +DA:350,6 DA:356,31 -DA:357,3 -DA:360,28 -DA:361,28 -DA:362,32 -DA:363,3 -DA:366,29 -DA:367,26 -DA:369,3 -DA:373,25 -DA:374,1 +DA:358,31 +DA:360,31 +DA:361,3 +DA:364,28 +DA:365,28 +DA:366,32 +DA:367,3 +DA:370,29 +DA:371,26 +DA:373,3 DA:377,25 -DA:378,25 -DA:379,3 -DA:381,22 -DA:383,22 -DA:384,15 -DA:385,15 -DA:387,15 -DA:390,15 -DA:392,15 -DA:394,16 -DA:396,16 -DA:397,1 -DA:400,15 -DA:402,5 +DA:378,1 +DA:381,25 +DA:382,25 +DA:383,3 +DA:385,22 +DA:387,22 +DA:388,15 +DA:389,15 +DA:391,15 +DA:394,15 +DA:396,15 +DA:398,16 +DA:400,16 +DA:401,1 +DA:404,15 DA:406,5 -DA:408,5 -DA:409,1 -DA:414,1 -DA:415,0 +DA:410,5 +DA:412,5 +DA:413,1 DA:418,1 -DA:419,1 -DA:420,1 -DA:427,1 -DA:428,1 -DA:429,1 -DA:430,1 +DA:419,0 +DA:422,1 +DA:423,1 +DA:424,1 DA:431,1 -DA:435,4 -DA:436,4 -DA:438,4 -DA:439,10 -DA:440,5 -DA:441,5 -DA:443,5 -DA:447,4 -DA:449,4 -DA:450,4 -DA:452,4 +DA:432,1 +DA:433,1 +DA:434,1 +DA:435,1 +DA:439,4 +DA:440,4 +DA:442,4 +DA:443,10 +DA:444,5 +DA:445,5 +DA:447,5 +DA:451,4 +DA:453,4 DA:454,4 -DA:455,1 -DA:456,1 -DA:457,1 -DA:462,4 -DA:463,4 -DA:464,4 -DA:465,4 -DA:470,5 -DA:474,8 -DA:475,4 -DA:477,4 +DA:456,4 +DA:458,4 +DA:459,1 +DA:460,1 +DA:461,1 +DA:466,4 +DA:467,4 +DA:468,4 +DA:469,4 +DA:474,5 +DA:478,8 DA:479,4 -DA:480,4 DA:481,4 -DA:482,4 +DA:483,4 DA:484,4 -DA:485,1 -DA:488,3 -DA:490,3 +DA:485,4 +DA:486,4 +DA:488,4 +DA:489,1 DA:492,3 DA:494,3 -DA:498,0 -DA:499,0 +DA:496,3 +DA:498,3 DA:502,0 DA:503,0 -DA:504,0 DA:506,0 -DA:509,0 -DA:512,1 -DA:513,3 +DA:507,0 +DA:508,0 +DA:510,0 +DA:513,0 DA:516,1 -DA:517,1 -DA:518,3 +DA:517,3 DA:520,1 -DA:522,1 -DA:532,2 -DA:533,6 +DA:521,1 +DA:522,3 +DA:524,1 +DA:526,1 DA:536,2 -DA:537,2 -DA:538,6 +DA:537,6 DA:540,2 -DA:543,2 -DA:580,4 -DA:581,4 -DA:584,8 -DA:587,2 -DA:588,1 -DA:591,1 +DA:541,2 +DA:542,6 +DA:544,2 +DA:547,2 +DA:584,4 +DA:585,4 +DA:588,8 +DA:591,2 DA:592,1 -DA:593,1 -DA:594,1 DA:595,1 DA:596,1 -DA:603,13 -DA:604,12 -DA:605,12 -DA:610,7 -DA:611,7 -DA:620,10 -DA:622,10 -DA:623,9 -DA:624,1 -DA:627,8 -DA:628,7 -DA:629,7 -DA:630,4 +DA:597,1 +DA:598,1 +DA:599,1 +DA:600,1 +DA:607,13 +DA:608,12 +DA:609,12 +DA:614,7 +DA:615,7 +DA:624,10 +DA:626,10 +DA:627,9 +DA:628,1 +DA:631,8 +DA:632,7 DA:633,7 -DA:634,7 -DA:635,7 -DA:636,7 +DA:634,4 DA:637,7 -DA:638,0 +DA:638,7 DA:639,7 -DA:640,1 -DA:642,6 -DA:646,7 -DA:649,1 -DA:652,1 -DA:658,13 -DA:660,13 -DA:661,1 -DA:664,12 -DA:665,12 -DA:667,12 +DA:640,7 +DA:641,7 +DA:642,0 +DA:643,7 +DA:644,1 +DA:646,6 +DA:650,7 +DA:653,1 +DA:656,1 +DA:662,13 +DA:664,13 +DA:665,1 DA:668,12 -DA:669,8 -DA:671,4 -DA:672,4 -DA:674,4 +DA:669,12 +DA:671,12 +DA:672,12 +DA:673,8 DA:675,4 DA:676,4 -DA:677,4 DA:678,4 +DA:679,4 DA:680,4 DA:681,4 DA:682,4 -DA:683,4 +DA:684,4 +DA:685,4 DA:686,4 -DA:688,0 -DA:694,8 -DA:696,8 -DA:697,3 -DA:698,3 -DA:699,3 +DA:687,4 +DA:690,4 +DA:692,0 +DA:698,8 +DA:700,8 DA:701,3 DA:702,3 -DA:703,0 +DA:703,3 DA:705,3 DA:706,3 -DA:710,5 -DA:711,5 -DA:712,5 -DA:713,5 +DA:707,0 +DA:709,3 +DA:710,3 +DA:714,5 DA:715,5 -DA:716,14 -DA:717,9 -DA:721,5 -DA:726,8 -DA:727,8 -DA:729,2 -DA:731,2 -DA:732,2 -DA:734,0 -DA:737,4 -DA:741,8 -DA:743,12 -DA:744,4 -DA:746,8 -DA:755,12 -DA:760,8 -DA:765,8 -DA:779,2 -DA:780,1 -DA:782,1 -DA:793,1280 -DA:798,102 -DA:803,18 -DA:808,2 -DA:813,17 -DA:815,17 -DA:816,17 -DA:817,168 -DA:818,84 -DA:821,17 -DA:826,32 -DA:827,21 -DA:829,11 -DA:844,15 -DA:846,15 +DA:716,5 +DA:717,5 +DA:719,5 +DA:720,14 +DA:721,9 +DA:725,5 +DA:730,8 +DA:731,8 +DA:733,2 +DA:735,2 +DA:736,2 +DA:738,0 +DA:741,4 +DA:745,8 +DA:747,12 +DA:748,4 +DA:750,8 +DA:759,12 +DA:764,8 +DA:769,8 +DA:783,2 +DA:784,1 +DA:786,1 +DA:797,1327 +DA:802,102 +DA:807,18 +DA:812,2 +DA:817,17 +DA:819,17 +DA:820,17 +DA:821,168 +DA:822,84 +DA:825,17 +DA:830,32 +DA:831,21 +DA:833,11 DA:848,15 -DA:850,18 -DA:852,18 -DA:855,10 -DA:856,10 -DA:858,9 -DA:859,9 -DA:860,3 -DA:862,6 -DA:865,1 -DA:867,10 -DA:870,8 -DA:872,8 +DA:850,15 +DA:852,15 +DA:854,18 +DA:856,18 +DA:859,10 +DA:860,10 +DA:862,9 +DA:863,9 +DA:864,3 +DA:866,6 +DA:869,1 +DA:871,10 DA:874,8 -DA:875,3 -DA:876,3 -DA:877,3 -DA:879,0 +DA:876,8 +DA:878,8 +DA:879,3 +DA:880,3 +DA:881,3 DA:883,0 -DA:885,8 -DA:888,15 -DA:909,3823 -DA:911,3823 -DA:919,3823 -DA:922,2971 -DA:923,2971 -DA:924,2971 -DA:926,852 -DA:927,852 -DA:931,3823 -DA:935,3823 -DA:939,3823 -DA:941,3823 -DA:942,3823 -DA:946,3823 -DA:949,3823 -DA:951,648 -DA:953,648 -DA:955,392 -DA:956,392 -DA:959,648 -DA:960,681 -DA:961,28 -DA:962,1 -DA:965,27 -DA:966,27 -DA:968,27 -DA:971,648 -DA:973,852 -DA:975,852 -DA:977,13 -DA:978,13 -DA:981,852 -DA:982,922 -DA:983,64 -DA:984,1 -DA:987,63 -DA:988,63 -DA:990,63 +DA:887,0 +DA:889,8 +DA:892,15 +DA:913,4034 +DA:915,4034 +DA:923,4034 +DA:926,3152 +DA:927,3152 +DA:928,3152 +DA:930,882 +DA:931,882 +DA:935,4034 +DA:939,4034 +DA:943,4034 +DA:945,4034 +DA:946,4034 +DA:950,4034 +DA:953,4034 +DA:955,699 +DA:957,699 +DA:959,407 +DA:960,407 +DA:963,699 +DA:964,732 +DA:965,28 +DA:966,1 +DA:969,27 +DA:970,27 +DA:972,27 +DA:975,699 +DA:977,882 +DA:979,882 +DA:981,13 +DA:982,13 +DA:985,882 +DA:986,952 +DA:987,64 +DA:988,1 +DA:991,63 DA:992,63 -DA:993,20 -DA:994,7 -DA:995,13 -DA:997,12 -DA:999,1 -DA:1010,852 -DA:1013,2323 -DA:1015,2323 -DA:1016,2323 -DA:1017,0 -DA:1020,2323 -DA:1022,0 -DA:1025,3823 -DA:1026,3823 -DA:1030,3823 -DA:1034,267 -DA:1037,3823 -DA:1038,1500 -DA:1042,1500 -DA:1046,87 -DA:1050,3823 -DA:1073,3823 -DA:1075,3823 -DA:1077,3823 -DA:1082,1302 -DA:1083,1139 -DA:1089,8 -DA:1091,8 -DA:1092,8 +DA:994,63 +DA:996,63 +DA:997,20 +DA:998,7 +DA:999,13 +DA:1001,12 +DA:1003,1 +DA:1014,882 +DA:1017,2453 +DA:1019,2453 +DA:1020,2453 +DA:1021,0 +DA:1024,2453 +DA:1026,0 +DA:1029,4034 +DA:1030,4034 +DA:1034,4034 +DA:1038,267 +DA:1041,4034 +DA:1042,1581 +DA:1046,1581 +DA:1050,87 +DA:1054,4034 +DA:1077,4034 +DA:1079,4034 +DA:1081,4034 +DA:1086,1349 +DA:1087,1178 DA:1093,8 -DA:1094,19 -DA:1098,8 -DA:1099,12 -DA:1100,12 -DA:1101,12 -DA:1103,4 -DA:1104,4 -DA:1108,0 -DA:1109,0 -DA:1113,8 -DA:1114,8 -DA:1118,8 -DA:1122,1 -DA:1125,1 -DA:1127,1 -DA:1130,1 -DA:1131,1 -DA:1132,3823 -DA:1134,3823 -DA:1136,3823 -DA:1138,852 -DA:1143,852 -DA:1146,2971 -DA:1151,2971 -DA:1155,1 -DA:1158,0 -DA:1159,0 -DA:1164,404 -DA:1165,1006 -DA:1168,404 -DA:1169,404 -DA:1174,3 -DA:1176,3 -DA:1177,6 -DA:1180,3 -DA:1181,3 -DA:1186,1365 -DA:1187,1365 -DA:1192,280 -DA:1193,280 -DA:1198,281 -DA:1203,6 -DA:1207,1 -DA:1210,1 -DA:1212,1 -DA:1215,11 -DA:1216,11 -LF:444 -LH:418 +DA:1095,8 +DA:1096,8 +DA:1097,8 +DA:1098,19 +DA:1099,0 +DA:1102,19 +DA:1103,19 +DA:1108,8 +DA:1109,12 +DA:1110,12 +DA:1111,12 +DA:1113,4 +DA:1114,4 +DA:1118,0 +DA:1119,0 +DA:1123,8 +DA:1124,8 +DA:1128,8 +DA:1132,1 +DA:1135,1 +DA:1137,1 +DA:1140,1 +DA:1141,1 +DA:1142,4034 +DA:1144,4034 +DA:1146,4034 +DA:1148,882 +DA:1153,882 +DA:1156,3152 +DA:1161,3152 +DA:1165,1 +DA:1168,0 +DA:1169,0 +DA:1174,438 +DA:1175,1091 +DA:1178,438 +DA:1179,438 +DA:1184,3 +DA:1186,3 +DA:1187,6 +DA:1190,3 +DA:1191,3 +DA:1196,1453 +DA:1197,1453 +DA:1202,290 +DA:1203,290 +DA:1208,291 +DA:1213,6 +DA:1217,1 +DA:1220,1 +DA:1222,1 +DA:1225,11 +DA:1226,11 +LF:448 +LH:421 BRDA:7,1,0,35 BRDA:7,1,1,35 BRDA:7,2,0,35 @@ -1067,178 +1073,180 @@ BRDA:9,6,1,6 BRDA:11,7,0,5 BRDA:11,7,1,0 BRDA:13,8,0,0 -BRDA:13,8,1,5102 -BRDA:123,9,0,87 -BRDA:123,9,1,86 -BRDA:137,10,0,123 -BRDA:137,10,1,122 -BRDA:195,11,0,655 -BRDA:195,11,1,654 -BRDA:199,12,0,3 -BRDA:199,12,1,652 -BRDA:241,13,0,0 -BRDA:241,13,1,22 -BRDA:249,14,0,12 -BRDA:249,14,1,2 -BRDA:266,15,0,2 -BRDA:266,15,1,12 -BRDA:269,16,0,12 -BRDA:269,16,1,10 -BRDA:285,17,0,14 -BRDA:285,17,1,12 -BRDA:292,18,0,0 -BRDA:292,18,1,14 -BRDA:292,19,0,14 -BRDA:292,19,1,0 -BRDA:296,20,0,0 -BRDA:296,20,1,14 -BRDA:327,21,0,5 -BRDA:327,21,1,20 -BRDA:327,21,2,6 -BRDA:332,22,0,16 -BRDA:332,22,1,4 -BRDA:334,23,0,1 -BRDA:334,23,1,3 -BRDA:341,24,0,3 -BRDA:341,24,1,3 -BRDA:354,25,0,5 -BRDA:354,25,1,26 -BRDA:356,26,0,3 -BRDA:356,26,1,28 -BRDA:362,27,0,3 -BRDA:362,27,1,29 -BRDA:366,28,0,26 -BRDA:366,28,1,3 -BRDA:373,29,0,1 -BRDA:373,29,1,24 -BRDA:378,30,0,3 -BRDA:378,30,1,22 -BRDA:383,31,0,15 -BRDA:383,31,1,7 -BRDA:396,32,0,1 -BRDA:396,32,1,15 -BRDA:400,33,0,5 -BRDA:400,33,1,8 -BRDA:400,33,2,2 -BRDA:408,34,0,1 -BRDA:408,34,1,4 -BRDA:414,35,0,0 -BRDA:414,35,1,1 -BRDA:439,36,0,5 -BRDA:439,36,1,5 -BRDA:454,37,0,1 -BRDA:454,37,1,3 -BRDA:474,38,0,4 -BRDA:474,38,1,4 -BRDA:480,39,0,4 -BRDA:480,39,1,0 -BRDA:482,40,0,4 -BRDA:482,40,1,2 -BRDA:484,41,0,1 -BRDA:484,41,1,3 -BRDA:494,42,0,0 -BRDA:494,42,1,1 -BRDA:494,42,2,2 -BRDA:587,43,0,1 -BRDA:587,43,1,1 -BRDA:604,44,0,12 -BRDA:604,44,1,0 -BRDA:610,45,0,7 -BRDA:610,45,1,0 -BRDA:622,46,0,9 -BRDA:622,46,1,1 -BRDA:623,47,0,1 -BRDA:623,47,1,8 -BRDA:627,48,0,7 -BRDA:627,48,1,1 -BRDA:630,49,0,4 -BRDA:630,49,1,2 -BRDA:637,50,0,0 -BRDA:637,50,1,7 -BRDA:639,51,0,1 -BRDA:639,51,1,6 -BRDA:660,52,0,1 -BRDA:660,52,1,12 -BRDA:668,53,0,8 -BRDA:668,53,1,4 -BRDA:674,54,0,4 -BRDA:674,54,1,0 -BRDA:696,55,0,3 -BRDA:696,55,1,5 -BRDA:702,56,0,0 -BRDA:702,56,1,3 -BRDA:716,57,0,9 -BRDA:716,57,1,5 -BRDA:727,58,0,2 -BRDA:727,58,1,2 -BRDA:727,58,2,4 -BRDA:731,59,0,2 -BRDA:731,59,1,0 -BRDA:743,60,0,4 -BRDA:743,60,1,8 -BRDA:779,61,0,1 -BRDA:779,61,1,1 -BRDA:779,62,0,2 -BRDA:779,62,1,1 -BRDA:817,63,0,84 -BRDA:817,63,1,84 -BRDA:826,64,0,21 -BRDA:826,64,1,11 -BRDA:852,65,0,7 -BRDA:852,65,1,10 -BRDA:852,65,2,8 -BRDA:856,66,0,9 -BRDA:856,66,1,1 -BRDA:859,67,0,3 -BRDA:859,67,1,6 -BRDA:872,68,0,8 -BRDA:872,68,1,0 -BRDA:876,69,0,3 -BRDA:876,69,1,0 -BRDA:919,70,0,648 -BRDA:919,70,1,2971 -BRDA:919,70,2,852 -BRDA:949,71,0,648 -BRDA:949,71,1,852 -BRDA:949,71,2,2323 -BRDA:949,71,3,0 -BRDA:960,72,0,28 -BRDA:960,72,1,653 -BRDA:960,73,0,681 -BRDA:960,73,1,32 -BRDA:961,74,0,1 -BRDA:961,74,1,27 -BRDA:982,75,0,64 -BRDA:982,75,1,858 -BRDA:982,76,0,922 -BRDA:982,76,1,68 -BRDA:983,77,0,1 -BRDA:983,77,1,63 -BRDA:992,78,0,20 -BRDA:992,78,1,43 -BRDA:993,79,0,7 -BRDA:993,79,1,13 -BRDA:995,80,0,12 -BRDA:995,80,1,1 -BRDA:1016,81,0,0 -BRDA:1016,81,1,2323 -BRDA:1016,82,0,2323 -BRDA:1016,82,1,0 -BRDA:1037,83,0,1500 -BRDA:1037,83,1,2323 -BRDA:1101,84,0,4 -BRDA:1101,84,1,8 -BRDA:1103,85,0,4 -BRDA:1103,85,1,0 -BRDA:1103,86,0,4 -BRDA:1103,86,1,4 -BRDA:1136,87,0,852 -BRDA:1136,87,1,648 -BRDA:1136,87,2,2971 -BRDA:1174,88,0,3 -BRDA:1174,88,1,3 -BRF:185 -BRH:161 +BRDA:13,8,1,5360 +BRDA:124,9,0,87 +BRDA:124,9,1,86 +BRDA:138,10,0,123 +BRDA:138,10,1,122 +BRDA:199,11,0,687 +BRDA:199,11,1,686 +BRDA:203,12,0,3 +BRDA:203,12,1,684 +BRDA:245,13,0,0 +BRDA:245,13,1,22 +BRDA:253,14,0,12 +BRDA:253,14,1,2 +BRDA:270,15,0,2 +BRDA:270,15,1,12 +BRDA:273,16,0,12 +BRDA:273,16,1,10 +BRDA:289,17,0,14 +BRDA:289,17,1,12 +BRDA:296,18,0,0 +BRDA:296,18,1,14 +BRDA:296,19,0,14 +BRDA:296,19,1,0 +BRDA:300,20,0,0 +BRDA:300,20,1,14 +BRDA:331,21,0,5 +BRDA:331,21,1,20 +BRDA:331,21,2,6 +BRDA:336,22,0,16 +BRDA:336,22,1,4 +BRDA:338,23,0,1 +BRDA:338,23,1,3 +BRDA:345,24,0,3 +BRDA:345,24,1,3 +BRDA:358,25,0,5 +BRDA:358,25,1,26 +BRDA:360,26,0,3 +BRDA:360,26,1,28 +BRDA:366,27,0,3 +BRDA:366,27,1,29 +BRDA:370,28,0,26 +BRDA:370,28,1,3 +BRDA:377,29,0,1 +BRDA:377,29,1,24 +BRDA:382,30,0,3 +BRDA:382,30,1,22 +BRDA:387,31,0,15 +BRDA:387,31,1,7 +BRDA:400,32,0,1 +BRDA:400,32,1,15 +BRDA:404,33,0,5 +BRDA:404,33,1,8 +BRDA:404,33,2,2 +BRDA:412,34,0,1 +BRDA:412,34,1,4 +BRDA:418,35,0,0 +BRDA:418,35,1,1 +BRDA:443,36,0,5 +BRDA:443,36,1,5 +BRDA:458,37,0,1 +BRDA:458,37,1,3 +BRDA:478,38,0,4 +BRDA:478,38,1,4 +BRDA:484,39,0,4 +BRDA:484,39,1,0 +BRDA:486,40,0,4 +BRDA:486,40,1,2 +BRDA:488,41,0,1 +BRDA:488,41,1,3 +BRDA:498,42,0,0 +BRDA:498,42,1,1 +BRDA:498,42,2,2 +BRDA:591,43,0,1 +BRDA:591,43,1,1 +BRDA:608,44,0,12 +BRDA:608,44,1,0 +BRDA:614,45,0,7 +BRDA:614,45,1,0 +BRDA:626,46,0,9 +BRDA:626,46,1,1 +BRDA:627,47,0,1 +BRDA:627,47,1,8 +BRDA:631,48,0,7 +BRDA:631,48,1,1 +BRDA:634,49,0,4 +BRDA:634,49,1,2 +BRDA:641,50,0,0 +BRDA:641,50,1,7 +BRDA:643,51,0,1 +BRDA:643,51,1,6 +BRDA:664,52,0,1 +BRDA:664,52,1,12 +BRDA:672,53,0,8 +BRDA:672,53,1,4 +BRDA:678,54,0,4 +BRDA:678,54,1,0 +BRDA:700,55,0,3 +BRDA:700,55,1,5 +BRDA:706,56,0,0 +BRDA:706,56,1,3 +BRDA:720,57,0,9 +BRDA:720,57,1,5 +BRDA:731,58,0,2 +BRDA:731,58,1,2 +BRDA:731,58,2,4 +BRDA:735,59,0,2 +BRDA:735,59,1,0 +BRDA:747,60,0,4 +BRDA:747,60,1,8 +BRDA:783,61,0,1 +BRDA:783,61,1,1 +BRDA:783,62,0,2 +BRDA:783,62,1,1 +BRDA:821,63,0,84 +BRDA:821,63,1,84 +BRDA:830,64,0,21 +BRDA:830,64,1,11 +BRDA:856,65,0,7 +BRDA:856,65,1,10 +BRDA:856,65,2,8 +BRDA:860,66,0,9 +BRDA:860,66,1,1 +BRDA:863,67,0,3 +BRDA:863,67,1,6 +BRDA:876,68,0,8 +BRDA:876,68,1,0 +BRDA:880,69,0,3 +BRDA:880,69,1,0 +BRDA:923,70,0,699 +BRDA:923,70,1,3152 +BRDA:923,70,2,882 +BRDA:953,71,0,699 +BRDA:953,71,1,882 +BRDA:953,71,2,2453 +BRDA:953,71,3,0 +BRDA:964,72,0,28 +BRDA:964,72,1,704 +BRDA:964,73,0,732 +BRDA:964,73,1,32 +BRDA:965,74,0,1 +BRDA:965,74,1,27 +BRDA:986,75,0,64 +BRDA:986,75,1,888 +BRDA:986,76,0,952 +BRDA:986,76,1,68 +BRDA:987,77,0,1 +BRDA:987,77,1,63 +BRDA:996,78,0,20 +BRDA:996,78,1,43 +BRDA:997,79,0,7 +BRDA:997,79,1,13 +BRDA:999,80,0,12 +BRDA:999,80,1,1 +BRDA:1020,81,0,0 +BRDA:1020,81,1,2453 +BRDA:1020,82,0,2453 +BRDA:1020,82,1,0 +BRDA:1041,83,0,1581 +BRDA:1041,83,1,2453 +BRDA:1102,84,0,19 +BRDA:1102,84,1,0 +BRDA:1111,85,0,4 +BRDA:1111,85,1,8 +BRDA:1113,86,0,4 +BRDA:1113,86,1,0 +BRDA:1113,87,0,4 +BRDA:1113,87,1,4 +BRDA:1146,88,0,882 +BRDA:1146,88,1,699 +BRDA:1146,88,2,3152 +BRDA:1184,89,0,3 +BRDA:1184,89,1,3 +BRF:187 +BRH:162 end_of_record TN: SF:/home/intag/workspace/fam/dovima/es5/lib/validation/isPresent.js