From de7c7f734cd031733ea8b76f94d1e4fd78e79e3c Mon Sep 17 00:00:00 2001 From: Sascha Gehlich Date: Tue, 7 Jan 2014 12:47:42 +0100 Subject: [PATCH] `modelsMatch` option, error handling in _resolveAssociations() --- index.js | 350 +------------------------------- lib/sequenice.js | 413 ++++++++++++++++++++++++++++++++++++++ package.json | 1 + test/index.test.js | 57 +++++- test/models/admin/user.js | 10 + test/models/project.js | 8 +- test/models/user.js | 30 +-- 7 files changed, 499 insertions(+), 370 deletions(-) create mode 100644 lib/sequenice.js create mode 100644 test/models/admin/user.js diff --git a/index.js b/index.js index 06f71d6..766493f 100644 --- a/index.js +++ b/index.js @@ -1,348 +1,2 @@ -"use strict"; -var path = require("path"); -var fs = require("fs"); -var _ = require("underscore"); -var Sequelize = require("sequelize"); -var Utils = Sequelize.Utils; -_.str = require("underscore.string"); -var IndicesSyncer = require("./lib/indices-syncer"); - -function Sequenice(sequelize, options) { - if (!sequelize) throw new Error("sequenice needs an instance of sequelize"); - this.sequelize = sequelize; - this.models = {}; - - var defaultModelsDirectory = path.resolve( - path.dirname(module.parent.id), - "models" - ); - - this.options = { - modelsDirectory: options.modelsDirectory || defaultModelsDirectory, - modelsAttacher: options.modelsAttacher || global, - getterPrefix: options.getterPrefix || "get", - setterPrefix: options.setterPrefix || "set" - }; - - this._loadModels(); - this._resolveAssociations(); -} - -/** - * Available sequelize keywords - * @type {Array} - */ -Sequenice.RESOLVABLE_ASSOCIATION_OPTIONS = ["joinTableModel"]; -Sequenice.ASSOCIATIONS = ["belongsTo", "hasMany", "hasOne"]; -Sequenice.HOOKS = [ - "beforeValidate", - "afterValidate", - "beforeBulkCreate", - "beforeCreate", "afterCreate", - "afterBulkCreate" -]; - -/** - * Auto-loads the models from the modelsDirectory - * @private - */ -Sequenice.prototype._loadModels = function() { - var self = this; - if (!fs.existsSync(this.options.modelsDirectory)) - throw new Error("Models directory not found: " + this.options.modelsDirectory); - - // Read the models directory - var files = fs.readdirSync(this.options.modelsDirectory); - - // Iterate over the model files - files.forEach(function (file) { - var modelPath = path.resolve(self.options.modelsDirectory, file); - self._loadModel(modelPath); - }); -}; - -/** - * Defines the associations, resolves the table names - * to real models. - * @private - */ -Sequenice.prototype._resolveAssociations = function() { - var self = this; - Object.keys(this.models).forEach(function (modelName) { - var model = self.models[modelName]; - var associations = model._associations; - associations.forEach(function (association) { - var options = association.options; - - // Turn specific option values into model references - Sequenice.RESOLVABLE_ASSOCIATION_OPTIONS.forEach(function (relationOption) { - if (options.hasOwnProperty(relationOption)) { - var modelName = options[relationOption]; - options[relationOption] = self.models[modelName]._model; - } - }); - - // Call the association method on the sequelize model - model._model[association.type]( - self.models[association.modelName]._model, association.options - ); - }); - }); -}; - -/** - * Loads a model from the given modelPath - * @param {String} modelPath - * @private - */ -Sequenice.prototype._loadModel = function(modelPath) { - var Model = require(modelPath); - var fields = {}; - var getters = {}; - var setters = {}; - var validators = {}; - var hooks = {}; - var indices = []; - var instanceMethods = {}; - var classMethods = {}; - var options = {}; - var model, modelName, modelOptions; - - // Avoid that our helpers will be added as - // instance functions to the sequelize model - Model._methodBlacklist = _.union( - ["field", "get", "set", "validates", "_methodBlacklist"], - Sequenice.ASSOCIATIONS, - Sequenice.HOOKS - ); - - // Attach the helpers that we will later call from - // the constructor - this._attachFieldHelperToModel(Model, fields); - this._attachGetHelperToModel(Model, getters); - this._attachSetHelperToModel(Model, setters); - this._attachValidatesHelperToModel(Model, validators); - this._attachAssociationHelpersToModel(Model); - this._attachHookHelpersToModel(Model, hooks); - this._attachIndexHelperToModel(Model, indices); - this._attachOptionsHelperToModel(Model, options); - this._extractMethodsFromModel(Model, instanceMethods, classMethods); - - // Call the model constructor so that our - // targets get filled with data - model = new Model(Sequelize); - - // Define the sequelize model - modelName = model.constructor.name; - modelOptions = _.extend({ - instanceMethods: instanceMethods, - classMethods: classMethods, - validate: validators, - getterMethods: getters, - setterMethods: setters, - hooks: hooks - }, options); - model._model = this.sequelize.define(modelName, fields, modelOptions); - - // Override the sync method so that it automatically - // adds the given indices - this._overrideSyncWithIndices(model._model, indices); - - // Attach the real sequelize models to the modelsAttacher. - // Since modelsAttacher defaults to `global`, we will make - // the models globally available per default - this.options.modelsAttacher[modelName] = model._model; - - // Store our fake model - this.models[modelName] = model; -}; - -/** - * Adds a `field` prototype method to modelClass - * which will add a new field to the `target` object - * @param {Class} modelClass - * @param {Object} target - * @private - */ -Sequenice.prototype._attachFieldHelperToModel = function(modelClass, target) { - modelClass.prototype.field = function (name, type, options) { - var opt = options || {}; - opt.type = type; - target[name] = opt; - }; -}; - -/** - * Adds a `get` prototype method to modelClass - * which will add a new getter - * @param {Class} modelClass - * @param {Object} target - * @private - */ -Sequenice.prototype._attachGetHelperToModel = function(modelClass, target) { - var self = this; - modelClass.prototype.get = function (attributeName) { - var capitalizedAttribute = _.str.capitalize(attributeName); - var methodName = self.options.getterPrefix + capitalizedAttribute; - - target[attributeName] = modelClass.prototype[methodName]; - - modelClass._methodBlacklist.push(attributeName); - }; -}; - -/** - * Adds a `set` prototype method to modelClass - * which will add a new setter - * @param {Class} modelClass - * @param {Object} target - * @private - */ -Sequenice.prototype._attachSetHelperToModel = function(modelClass, target) { - var self = this; - modelClass.prototype.set = function (attributeName) { - var capitalizedAttribute = _.str.capitalize(attributeName); - var methodName = self.options.setterPrefix + capitalizedAttribute; - - target[attributeName] = modelClass.prototype[methodName]; - - modelClass._methodBlacklist.push(attributeName); - }; -}; - -/** - * Adds a `validates` prototype method to modelClass - * which will add a new validator - * @param {Class} modelClass - * @param {Object} target - * @private - */ -Sequenice.prototype._attachValidatesHelperToModel = function(modelClass, target) { - modelClass.prototype.validates = function(methodName) { - target[methodName] = modelClass.prototype[methodName]; - modelClass._methodBlacklist.push(methodName); - }; -}; - -/** - * Adds prototype methods for all existing association - * types which will add a new association - * @param {Class} modelClass - * @private - */ -Sequenice.prototype._attachAssociationHelpersToModel = function(modelClass) { - modelClass.prototype._associations = []; - Sequenice.ASSOCIATIONS.forEach(function (association) { - modelClass.prototype[association] = function (modelName, options) { - modelClass.prototype._associations.push({ - type: association, - modelName: modelName, - options: options || {} - }); - }; - }); -}; - -/** - * Adds prototype methods for all existing hooks - * which will add new hook methods - * @param {Class} modelClass - * @param {Object} target - * @private - */ -Sequenice.prototype._attachHookHelpersToModel = function(modelClass, target) { - Sequenice.HOOKS.forEach(function (hook) { - modelClass.prototype[hook] = function (methodName) { - if (!target[hook]) target[hook] = []; - - target[hook].push(modelClass.prototype[methodName]); - modelClass._methodBlacklist.push(methodName); - }; - }); -}; - -/** - * Adds a `index` prototype method to modelClass - * which will add a new index - * @param {Class} modelClass - * @param {Object} target - * @private - */ -Sequenice.prototype._attachIndexHelperToModel = function(modelClass, target) { - modelClass.prototype.index = function (attributes, options) { - target.push({ attributes: attributes, options: options }); - }; -}; - -/** - * Adds a `options` prototype method to modelClass - * which will add options to the target - * @param {Class} modelClass - * @param {Object} target - * @private - */ -Sequenice.prototype._attachOptionsHelperToModel = function(modelClass, target) { - modelClass.prototype.options = function (options) { - _.extend(target, options); - }; -}; - -/** - * Extracts instance methods and class methods from the given - * model class, excluding all methods in `modelClass._methodBlacklist` - * and adds them to `instanceTarget` and `classTarget` - * @param {Class} modelClass - * @param {Object} instanceTarget - * @param {Object} classTarget - * @private - */ -Sequenice.prototype._extractMethodsFromModel = function(modelClass, instanceTarget, classTarget) { - // Extract instance methods - Object.keys(modelClass.prototype).forEach(function (method) { - if(modelClass._methodBlacklist.indexOf(method) === -1) { - instanceTarget[method] = modelClass.prototype[method]; - } - }); - - // Extract class methods - Object.keys(modelClass).forEach(function (method) { - if(modelClass._methodBlacklist.indexOf(method) === -1) { - classTarget[method] = modelClass[method]; - } - }); -}; - -/** - * Overrides model.sync() to add incides after the syncing - * has been done - * @param {Model} model - * @param {Array} indices - * @private - */ -Sequenice.prototype._overrideSyncWithIndices = function(model, indices) { - var sync = model.sync; - var sequelize = this.sequelize; - - // Override syncing method so that it calls - // addIndices() afterwards - model.sync = function () { - var self = this; - return new Utils.CustomEventEmitter(function(emitter) { - sync.apply(self, arguments) - .success(function (m) { - - // Sequelize's syncing worked, run the index syncing mechanism - var indicesSyncer = new IndicesSyncer(sequelize, m, indices); - indicesSyncer - .sync() - .proxy(emitter); - - }) - .error(function (e) { - emitter.emit("error", e); - }); - }).run(); - }; -}; - -module.exports = Sequenice; +var sequenice = require("./lib/sequenice"); +module.exports = sequenice; diff --git a/lib/sequenice.js b/lib/sequenice.js new file mode 100644 index 0000000..b580239 --- /dev/null +++ b/lib/sequenice.js @@ -0,0 +1,413 @@ +"use strict"; +var Sequelize = require("sequelize"); +var DataTypes = require("sequelize/lib/data-types"); +var Utils = Sequelize.Utils; + +var path = require("path"); +var globule = require("globule"); +var fs = require("fs"); +var _ = require("underscore"); +var _str = require("underscore.string"); + +var IndicesSyncer = require("./indices-syncer"); + +function Sequenice(sequelize, options) { + if (!sequelize) throw new Error("sequenice needs an instance of sequelize"); + this.sequelize = sequelize; + this.models = {}; + + var defaultModelsDirectory = path.resolve( + path.dirname(module.parent.id), + "models" + ); + + this.options = { + modelsDirectory: options.modelsDirectory || defaultModelsDirectory, + modelsAttacher: options.modelsAttacher || global, + modelsMatch: options.modelsMatch || /\.[js|coffee]/i, + getterPrefix: options.getterPrefix || "_get", + setterPrefix: options.setterPrefix || "_set" + }; + + this._loadModels(); + this._resolveAssociations(); +} + +/** + * Available sequelize keywords + * @type {Array} + */ +Sequenice.RESOLVABLE_ASSOCIATION_OPTIONS = ["joinTableModel"]; +Sequenice.ASSOCIATIONS = ["belongsTo", "hasMany", "hasOne"]; +Sequenice.HOOKS = [ + "beforeValidate", + "afterValidate", + "beforeBulkCreate", + "beforeCreate", "afterCreate", + "afterBulkCreate" +]; + +/** + * Deletes model references and cleans up a little + * @public + */ +Sequenice.prototype.dispose = function() { + // Delete model references from modelsAttacher + var self = this; + Object.keys(this.models).forEach(function (key) { + delete self.options.modelsAttacher[key]; + delete self.models[key]; + }); +}; + +/** + * Auto-loads the models from the modelsDirectory + * @private + */ +Sequenice.prototype._loadModels = function() { + var self = this; + var match = this.options.modelsMatch; + var files; + + if (!fs.existsSync(this.options.modelsDirectory)) + throw new Error("Models directory not found: " + this.options.modelsDirectory); + + if (typeof match === "string") { + files = globule.find(match, { + cwd: this.options.modelsDirectory + }); + } else if (match instanceof RegExp) { + files = globule.find("**/*", { + cwd: this.options.modelsDirectory, + filter: function (f) { + return !!match.test(f); + } + }); + } else if (match instanceof Function) { + files = globule.find("**/*", { + cwd: this.options.modelsDirectory, + filter: match + }); + } + + // Iterate over the model files + files.forEach(function (file) { + var modelPath = path.resolve(self.options.modelsDirectory, file); + self._loadModel(modelPath); + }); +}; + +/** + * Defines the associations, resolves the table names + * to real models. + * @private + */ +Sequenice.prototype._resolveAssociations = function() { + var self = this; + Object.keys(this.models).forEach(function (modelName) { + var model = self.models[modelName]; + var associations = model._associations; + associations.forEach(function (association) { + var options = association.options; + + // Turn specific option values into model references + Sequenice.RESOLVABLE_ASSOCIATION_OPTIONS.forEach(function (relationOption) { + if (options.hasOwnProperty(relationOption)) { + var modelName = options[relationOption]; + options[relationOption] = self.models[modelName]._model; + } + }); + + // Call the association method on the sequelize model + if (!self.models[association.modelName]) { + throw new Error("Associated model missing for " + modelName + ": " + association.modelName); + } + + model._model[association.type]( + self.models[association.modelName]._model, association.options + ); + }); + }); +}; + +/** + * Loads a model from the given modelPath + * @param {String} modelPath + * @private + */ +Sequenice.prototype._loadModel = function(modelPath) { + var Model = require(modelPath); + var map = {}; + var fields = {}; + var getters = {}; + var setters = {}; + var validators = {}; + var hooks = {}; + var indices = []; + var instanceMethods = {}; + var classMethods = {}; + var options = {}; + var model, modelName, modelOptions; + + // Avoid that our helpers will be added as + // instance functions to the sequelize model + Model._methodBlacklist = _.union( + ["field", "get", "set", "validates", "_methodBlacklist"], + Sequenice.ASSOCIATIONS, + Sequenice.HOOKS + ); + + // Attach the helpers that we will later call from + // the constructor + this._attachFieldHelperToMap(Model, map, fields); + this._attachGetHelperToMap(Model, map, getters); + this._attachSetHelperToMap(Model, map, setters); + this._attachValidatesHelperToMap(Model, map, validators); + this._attachAssociationHelpersToMap(Model, map); + this._attachHookHelpersToMap(Model, map, hooks); + this._attachIndexHelperToMap(Model, map, indices); + this._attachOptionsHelperToMap(Model, map, options); + + // Extract instance and class methods from the model + this._extractMethodsFromModel(Model, instanceMethods, classMethods); + + // Attach sequelize's data types to the map + this._attachDataTypesToMap(map); + + // Call the model constructor so that our + // targets get filled with data + model = new Model(map); + + // Define the sequelize model + modelName = model.constructor.name; + modelOptions = _.extend({ + instanceMethods: instanceMethods, + classMethods: classMethods, + validate: validators, + getterMethods: getters, + setterMethods: setters, + hooks: hooks + }, options); + model._model = this.sequelize.define(modelName, fields, modelOptions); + + // Override the sync method so that it automatically + // adds the given indices + this._overrideSyncWithIndices(model._model, indices); + + // Attach the real sequelize models to the modelsAttacher. + // Since modelsAttacher defaults to `global`, we will make + // the models globally available per default + this.options.modelsAttacher[modelName] = model._model; + + // Store our fake model + this.models[modelName] = model; +}; + +/** + * Adds a `field` prototype method to map + * which will add a new field to the `target` object + * @param {Class} modelClass + * @param {Object} map + * @param {Object} target + * @private + */ +Sequenice.prototype._attachFieldHelperToMap = function(modelClass, map, target) { + map.field = function (name, type, options) { + var opt = options || {}; + opt.type = type; + target[name] = opt; + }; +}; + +/** + * Adds a `get` prototype method to map + * which will add a new getter + * @param {Class} modelClass + * @param {Object} map + * @param {Object} target + * @private + */ +Sequenice.prototype._attachGetHelperToMap = function(modelClass, map, target) { + var self = this; + map.get = function (attributeName) { + var capitalizedAttribute = _str.capitalize(attributeName); + var methodName = self.options.getterPrefix + capitalizedAttribute; + + target[attributeName] = modelClass.prototype[methodName]; + + modelClass._methodBlacklist.push(attributeName); + }; +}; + +/** + * Adds a `set` prototype method to map + * which will add a new setter + * @param {Class} modelClass + * @param {Object} map + * @param {Object} target + * @private + */ +Sequenice.prototype._attachSetHelperToMap = function(modelClass, map, target) { + var self = this; + map.set = function (attributeName) { + var capitalizedAttribute = _str.capitalize(attributeName); + var methodName = self.options.setterPrefix + capitalizedAttribute; + + target[attributeName] = modelClass.prototype[methodName]; + + modelClass._methodBlacklist.push(attributeName); + }; +}; + +/** + * Adds a `validates` prototype method to modelClass + * which will add a new validator + * @param {Class} modelClass + * @param {Object} map + * @param {Object} target + * @private + */ +Sequenice.prototype._attachValidatesHelperToMap = function(modelClass, map, target) { + map.validates = function(methodName) { + target[methodName] = modelClass.prototype[methodName]; + modelClass._methodBlacklist.push(methodName); + }; +}; + +/** + * Adds prototype methods for all existing association + * types which will add a new association + * @param {Class} modelClass + * @param {Object} map + * @private + */ +Sequenice.prototype._attachAssociationHelpersToMap = function(modelClass, map) { + modelClass.prototype._associations = []; + Sequenice.ASSOCIATIONS.forEach(function (association) { + map[association] = function (modelName, options) { + modelClass.prototype._associations.push({ + type: association, + modelName: modelName, + options: options || {} + }); + }; + }); +}; + +/** + * Adds prototype methods for all existing hooks + * which will add new hook methods + * @param {Class} modelClass + * @param {Object} map + * @param {Object} target + * @private + */ +Sequenice.prototype._attachHookHelpersToMap = function(modelClass, map, target) { + Sequenice.HOOKS.forEach(function (hook) { + map[hook] = function (methodName) { + if (!target[hook]) target[hook] = []; + + target[hook].push(modelClass.prototype[methodName]); + modelClass._methodBlacklist.push(methodName); + }; + }); +}; + +/** + * Adds a `index` prototype method to modelClass + * which will add a new index + * @param {Class} modelClass + * @param {Object} map + * @param {Object} target + * @private + */ +Sequenice.prototype._attachIndexHelperToMap = function(modelClass, map, target) { + map.index = function (attributes, options) { + target.push({ attributes: attributes, options: options }); + }; +}; + +/** + * Adds a `options` prototype method to modelClass + * which will add options to the target + * @param {Class} modelClass + * @param {Object} map + * @param {Object} target + * @private + */ +Sequenice.prototype._attachOptionsHelperToMap = function(modelClass, map, target) { + map.options = function (options) { + _.extend(target, options); + }; +}; + +/** + * Extracts instance methods and class methods from the given + * model class, excluding all methods in `modelClass._methodBlacklist` + * and adds them to `instanceTarget` and `classTarget` + * @param {Class} modelClass + * @param {Object} instanceTarget + * @param {Object} classTarget + * @private + */ +Sequenice.prototype._extractMethodsFromModel = function(modelClass, instanceTarget, classTarget) { + // Extract instance methods + Object.keys(modelClass.prototype).forEach(function (method) { + if(modelClass._methodBlacklist.indexOf(method) === -1) { + instanceTarget[method] = modelClass.prototype[method]; + } + }); + + // Extract class methods + Object.keys(modelClass).forEach(function (method) { + if(modelClass._methodBlacklist.indexOf(method) === -1) { + classTarget[method] = modelClass[method]; + } + }); +}; + +/** + * Overrides model.sync() to add incides after the syncing + * has been done + * @param {Model} model + * @param {Array} indices + * @private + */ +Sequenice.prototype._overrideSyncWithIndices = function(model, indices) { + var sync = model.sync; + var sequelize = this.sequelize; + + // Override syncing method so that it calls + // addIndices() afterwards + model.sync = function () { + var self = this; + return new Utils.CustomEventEmitter(function(emitter) { + sync.apply(self, arguments) + .success(function (m) { + + // Sequelize's syncing worked, run the index syncing mechanism + var indicesSyncer = new IndicesSyncer(sequelize, m, indices); + indicesSyncer + .sync() + .proxy(emitter); + + }) + .error(function (e) { + emitter.emit("error", e); + }); + }).run(); + }; +}; + +/** + * Copies sequelize's data types to the map + * @param {Object} map + * @private + */ +Sequenice.prototype._attachDataTypesToMap = function(map) { + for (var key in DataTypes) { + map[key] = DataTypes[key]; + } +}; + +module.exports = Sequenice; diff --git a/package.json b/package.json index 09e91df..6ab98ab 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "mysql": "~2.0.0-alpha9" }, "dependencies": { + "globule": "~0.1.0", "underscore": "~1.5.2", "underscore.string": "~2.3.3" } diff --git a/test/index.test.js b/test/index.test.js index d1f86fe..a5e65cb 100644 --- a/test/index.test.js +++ b/test/index.test.js @@ -10,9 +10,8 @@ before(function (done) { sequelize = new Sequelize("sequenice_test", "root"); sequenice = new Sequenice(sequelize, { modelsDirectory: __dirname + "/models", - modelsAttacher: models, - getterPrefix: "get", - setterPrefix: "set" + modelsMatch: "*.js", + modelsAttacher: models }); var chain = new Sequelize.Utils.QueryChainer(); @@ -157,4 +156,56 @@ describe("sequenice example", function () { throw err; }); }); + + /** + * modelsMatch option + */ + describe("`modelsMatch` option", function () { + it("should support a string value (blob)", function (done) { + sequenice.dispose(); + sequenice = new Sequenice(sequelize, { + modelsDirectory: __dirname + "/models", + modelsMatch: "**/*.js", + modelsAttacher: models + }); + + should.exist(models.AdminUser); + should.exist(models.Project); + should.exist(models.User); + + done(); + }); + + it("should support a regex value", function (done) { + sequenice.dispose(); + sequenice = new Sequenice(sequelize, { + modelsDirectory: __dirname + "/models", + modelsMatch: /admin\/user\.js$/i, + modelsAttacher: models + }); + + should.exist(models.AdminUser); + should.not.exist(models.Project); + should.not.exist(models.User); + + done() + }); + + it("should support a function value", function (done) { + sequenice.dispose(); + sequenice = new Sequenice(sequelize, { + modelsDirectory: __dirname + "/models", + modelsMatch: function (m) { + return !!/admin\/user\.js/i.test(m); + }, + modelsAttacher: models + }); + + should.exist(models.AdminUser); + should.not.exist(models.Project); + should.not.exist(models.User); + + done(); + }); + }); }); diff --git a/test/models/admin/user.js b/test/models/admin/user.js new file mode 100644 index 0000000..24bedb6 --- /dev/null +++ b/test/models/admin/user.js @@ -0,0 +1,10 @@ +"use strict"; + +function AdminUser(m) { + /** + * Field declarations + */ + m.field("name", m.STRING); +} + +module.exports = AdminUser; diff --git a/test/models/project.js b/test/models/project.js index 7c27edf..f349585 100644 --- a/test/models/project.js +++ b/test/models/project.js @@ -1,19 +1,19 @@ "use strict"; -function Project(s) { +function Project(m) { /** * Field declarations */ - this.field("name", s.STRING); + m.field("name", m.STRING); /** * Associations */ - this.belongsTo("User"); + m.belongsTo("User"); /** * Options */ - this.options({ + m.options({ timestamps: false }); } diff --git a/test/models/user.js b/test/models/user.js index 86e1840..282ecf4 100644 --- a/test/models/user.js +++ b/test/models/user.js @@ -1,57 +1,57 @@ "use strict"; -function User(s) { +function User(m) { /** * Field declarations */ - this.field("name", s.STRING); - this.field("isAdmin", s.BOOLEAN, { defaultValue: false }); - this.field("beforeCreateCalled", s.BOOLEAN, { defaultValue: false }); + m.field("name", m.STRING); + m.field("isAdmin", m.BOOLEAN, { defaultValue: false }); + m.field("beforeCreateCalled", m.BOOLEAN, { defaultValue: false }); /** * Associations */ - this.hasMany("Project"); + m.hasMany("Project"); /** * Getters */ - this.get("price"); - this.get("priceInCents"); + m.get("price"); + m.get("priceInCents"); /** * Setters */ - this.set("price"); + m.set("price"); /** * Hooks */ - this.beforeCreate("myBeforeCreateMethod"); + m.beforeCreate("myBeforeCreateMethod"); /** * Validations */ - this.validates("myValidationMethod"); + m.validates("myValidationMethod"); /** * Indices */ - this.index(["id", "name"], { indexName: "IdName" }); - this.index(["name", "isAdmin"]); + m.index(["id", "name"], { indexName: "IdName" }); + m.index(["name", "isAdmin"]); } User.classMethod = function() {}; User.prototype.instanceMethod = function() {}; -User.prototype.getPrice = function() { +User.prototype._getPrice = function() { return "$" + (this.getDataValue("priceInCents") / 100); }; -User.prototype.getPriceInCents = function() { +User.prototype._getPriceInCents = function() { return this.dataValues.priceInCents; }; -User.prototype.setPrice = function(value) { +User.prototype._setPrice = function(value) { this.dataValues.priceInCents = value * 100; };