/* Copyright (c) 2012, Yahoo! Inc. All rights reserved. Copyrights licensed under the New BSD License. See the accompanying LICENSE file for terms. */ var util = require('util'), path = require('path'), fs = require('fs'), abbrev = require('abbrev'); function Factory(kind, dir, allowAbbreviations) { this.kind = kind; this.dir = dir; this.allowAbbreviations = allowAbbreviations; this.classMap = {}; this.abbreviations = null; } Factory.prototype = { knownTypes: function () { var keys = Object.keys(this.classMap); keys.sort(); return keys; }, resolve: function (abbreviatedType) { if (!this.abbreviations) { this.abbreviations = abbrev(this.knownTypes()); } return this.abbreviations[abbreviatedType]; }, register: function (constructor) { var type = constructor.TYPE; if (!type) { throw new Error('Could not register ' + this.kind + ' constructor [no TYPE property]: ' + util.inspect(constructor)); } this.classMap[type] = constructor; this.abbreviations = null; }, create: function (type, opts) { var allowAbbrev = this.allowAbbreviations, realType = allowAbbrev ? this.resolve(type) : type, Cons; Cons = realType ? this.classMap[realType] : null; if (!Cons) { throw new Error('Invalid ' + this.kind + ' [' + type + '], allowed values are ' + this.knownTypes().join(', ')); } return new Cons(opts); }, loadStandard: function (dir) { var that = this; fs.readdirSync(dir).forEach(function (file) { if (file !== 'index.js' && file.indexOf('.js') === file.length - 3) { try { that.register(require(path.resolve(dir, file))); } catch (ex) { console.error(ex.message); console.error(ex.stack); throw new Error('Could not register ' + that.kind + ' from file ' + file); } } }); }, bindClassMethods: function (Cons) { var tmpKind = this.kind.charAt(0).toUpperCase() + this.kind.substring(1), //ucfirst allowAbbrev = this.allowAbbreviations; Cons.mix = Factory.mix; Cons.register = this.register.bind(this); Cons.create = this.create.bind(this); Cons.loadAll = this.loadStandard.bind(this, this.dir); Cons['get' + tmpKind + 'List'] = this.knownTypes.bind(this); if (allowAbbrev) { Cons['resolve' + tmpKind + 'Name'] = this.resolve.bind(this); } } }; Factory.mix = function (cons, proto) { Object.keys(proto).forEach(function (key) { cons.prototype[key] = proto[key]; }); }; module.exports = Factory;