<commit_before>var loaderUtils = require('loader-utils')
var assign = require('object-assign')
var parse = require('./parser')
var path = require('path')
var normalize = require('./normalize')
var genId = require('./gen-id')

// internal lib loaders
var selectorPath = normalize.lib('selector')
var templateLoaderPath = normalize.lib('template-loader')
var templateCompilerPath = normalize.lib('template-compiler')
var styleRewriterPath = normalize.lib('style-rewriter')

// dep loaders
var styleLoaderPath = normalize.dep('vue-style-loader')
var hotReloadAPIPath = normalize.dep('vue-hot-reload-api')

var hasBabel = false
try {
  hasBabel = !!require('babel-loader')
} catch (e) {}

var hasBuble = false
try {
  hasBuble = !!require('buble-loader')
} catch (e) {}

var rewriterInjectRE = /\b(css(?:-loader)?(?:\?[^!]+)?)(?:!|$)/

var defaultLang = {
  template: 'html',
  styles: 'css',
  script: 'js'
}

var checkNamedExports =
  'if (Object.keys(__vue_exports__).some(function (key) { return key !== "default" && key !== "__esModule" })) {' +
  'console.error("named exports are not supported in *.vue files.")}\n'

module.exports = function (content) {
  this.cacheable()
  var isServer = this.options.target === 'node'
  var loaderContext = this
  var query = loaderUtils.parseQuery(this.query)
  var options = this.options.__vueOptions__ = Object.assign({}, this.options.vue, this.vue, query)
  var filePath = this.resourcePath
  var fileName = path.basename(filePath)
  var moduleId = 'data-v-' + genId(filePath)
  var styleRewriter = styleRewriterPath + '?id=' + moduleId

  var isProduction = this.minimize || process.env.NODE_ENV === 'production'

  var needCssSourceMap =
    !isProduction &&
    this.sourceMap &&
    options.cssSourceMap !== false

  var bubleOptions = hasBuble && options.buble ? '?' + JSON.stringify(options.buble) : ''
  var defaultLoaders = {
    html: templateCompilerPath + '?id=' + moduleId,
    css: (isServer ? '' : styleLoaderPath + '!') + 'css-loader' + (needCssSourceMap ? '?sourceMap' : ''),
    js: hasBuble ? ('buble-loader' + bubleOptions) : hasBabel ? 'babel-loader' : ''
  }

  // check if there are custom loaders specified via
  // webpack config, otherwise use defaults
  var loaders = assign({}, defaultLoaders, options.loaders)

  function getRequire (type, part, index, scoped) {
    return 'require(' +
      getRequireString(type, part, index, scoped) +
    ')\n'
  }

  function getRequireString (type, part, index, scoped) {
    return loaderUtils.stringifyRequest(loaderContext,
      // disable all configuration loaders
      '!!' +
      // get loader string for pre-processors
      getLoaderString(type, part, index, scoped) +
      // select the corresponding part from the vue file
      getSelectorString(type, index || 0) +
      // the url to the actual vuefile
      filePath
    )
  }

  function getRequireForImport (type, impt, scoped) {
    return 'require(' +
      getRequireForImportString(type, impt, scoped) +
    ')\n'
  }

  function getRequireForImportString (type, impt, scoped) {
    return loaderUtils.stringifyRequest(loaderContext,
      '!!' +
      getLoaderString(type, impt, -1, scoped) +
      impt.src
    )
  }

  function addCssModulesToLoader (loader, part, index) {
    if (!part.module) return loader
    var option = options.cssModules || {}
    var DEFAULT_OPTIONS = {
      modules: true,
      importLoaders: true
    }
    var OPTIONS = {
      localIdentName: '[hash:base64]'
    }
    return loader.replace(/((?:^|!)css(?:-loader)?)(\?[^!]*)?/, function (m, $1, $2) {
      // $1: !css-loader
      // $2: ?a=b
      var query = loaderUtils.parseQuery($2)
      Object.assign(query, OPTIONS, option, DEFAULT_OPTIONS)
      if (index !== -1) {
        // Note:
        //   Class name is generated according to its filename.
        //   Different <style> tags in the same .vue file may generate same names.
        //   Append `_[index]` to class name to avoid this.
        query.localIdentName += '_' + index
      }
      return $1 + '?' + JSON.stringify(query)
    })
  }

  function getLoaderString (type, part, index, scoped) {
    var lang = part.lang || defaultLang[type]
    var loader = loaders[lang]
    var rewriter = type === 'styles' ? styleRewriter + (scoped ? '&scoped=true!' : '!') : ''
    var injectString = (type === 'script' && query.inject) ? 'inject!' : ''
    if (loader !== undefined) {
      // add css modules
      if (type === 'styles') {
        loader = addCssModulesToLoader(loader, part, index)
      }
      // inject rewriter before css/html loader for
      // extractTextPlugin use cases
      if (rewriterInjectRE.test(loader)) {
        loader = loader.replace(rewriterInjectRE, function (m, $1) {
          return ensureBang($1) + rewriter
        })
      } else {
        loader = ensureBang(loader) + rewriter
      }
      return injectString + ensureBang(loader)
    } else {
      // unknown lang, infer the loader to be used
      switch (type) {
        case 'template':
          return defaultLoaders.html + '!' + templateLoaderPath + '?raw&engine=' + lang + '!'
        case 'styles':
          loader = addCssModulesToLoader(defaultLoaders.css, part, index)
          return loader + '!' + rewriter + ensureBang(ensureLoader(lang))
        case 'script':
          return injectString + ensureBang(ensureLoader(lang))
      }
    }
  }

  // sass => sass-loader
  // sass-loader => sass-loader
  // sass?indentedsyntax!css => sass-loader?indentedSyntax!css-loader
  function ensureLoader (lang) {
    return lang.split('!').map(loader => {
      return loader.replace(/^([\w-]+)(\?.*)?/, (_, $1, $2) => {
        return (/-loader$/.test($1) ? $1 : ($1 + '-loader')) + ($2 || '')
      })
    }).join('!')
  }

  function getSelectorString (type, index) {
    return selectorPath +
      '?type=' + type +
      '&index=' + index + '!'
  }

  function ensureBang (loader) {
    if (loader.charAt(loader.length - 1) !== '!') {
      return loader + '!'
    } else {
      return loader
    }
  }

  var parts = parse(content, fileName, this.sourceMap)
  var hasScoped = parts.styles.some(function (s) { return s.scoped })
  var output = 'var __vue_exports__, __vue_options__\n'

  // css modules
  output += 'var __vue_styles__ = {}\n'
  var cssModules = {}

  // add requires for styles
  if (parts.styles.length) {
    output += '\n/* styles */\n'
    parts.styles.forEach(function (style, i) {
      var moduleName = (style.module === true) ? '$style' : style.module

      // require style
      if (isServer && !moduleName) return
      var requireString = style.src
        ? getRequireForImport('styles', style, style.scoped)
        : getRequire('styles', style, i, style.scoped)

      // setCssModule
      if (moduleName) {
        if (moduleName in cssModules) {
          loaderContext.emitError('CSS module name "' + moduleName + '" is not unique!')
          output += requireString
        } else {
          cssModules[moduleName] = true

          // `style-loader` exposes the name-to-hash map directly
          // `css-loader` exposes it in `.locals`
          // We drop `style-loader` in SSR, and add `.locals` here.
          if (isServer) {
            requireString += '.locals'
          }

          output += '__vue_styles__["' + moduleName + '"] = ' + requireString + '\n'
        }
      } else {
        output += requireString
      }
    })
  }

  // add require for script
  var script = parts.script
  if (script) {
    output += '\n/* script */\n'
    output +=
      '__vue_exports__ = ' + (
        script.src
          ? getRequireForImport('script', script)
          : getRequire('script', script)
      )
  }

  var exports =
    '__vue_options__ = __vue_exports__ = __vue_exports__ || {}\n' +
    // ES6 modules interop
    'if (\n' +
    '  typeof __vue_exports__.default === "object" ||\n' +
    '  typeof __vue_exports__.default === "function"\n' +
    ') {\n' +
      (isProduction ? '' : checkNamedExports) +
      '__vue_options__ = __vue_exports__ = __vue_exports__.default\n' +
    '}\n' +
    // constructor export interop
    'if (typeof __vue_options__ === "function") {\n' +
    '  __vue_options__ = __vue_options__.options\n' +
    '}\n' +
    // add filename in dev
    (isProduction ? '' : ('__vue_options__.__file = ' + JSON.stringify(filePath))) + '\n'

  // add require for template
  var template = parts.template
  if (template) {
    output += '\n/* template */\n'
    output += 'var __vue_template__ = ' + (
      template.src
        ? getRequireForImport('template', template)
        : getRequire('template', template)
    )
    // attach render functions to exported options
    exports +=
      '__vue_options__.render = __vue_template__.render\n' +
      '__vue_options__.staticRenderFns = __vue_template__.staticRenderFns\n'
  }

  // attach scoped id
  if (hasScoped) {
    exports += '__vue_options__._scopeId = "' + moduleId + '"\n'
  }

  if (Object.keys(cssModules).length) {
    // inject style modules as computed properties
    exports +=
      'if (!__vue_options__.computed) __vue_options__.computed = {}\n' +
      'Object.keys(__vue_styles__).forEach(function (key) {\n' +
        'var module = __vue_styles__[key]\n' +
        '__vue_options__.computed[key] = function () { return module }\n' +
      '})\n'
  }

  if (!query.inject) {
    output += exports
    // hot reload
    if (
      !isServer &&
      !isProduction &&
      (parts.script || parts.template)
    ) {
      output +=
        '\n/* hot reload */\n' +
        'if (module.hot) {(function () {\n' +
        '  var hotAPI = require("' + hotReloadAPIPath + '")\n' +
        '  hotAPI.install(require("vue"), false)\n' +
        '  if (!hotAPI.compatible) return\n' +
        '  module.hot.accept()\n' +
        '  if (!module.hot.data) {\n' +
        // initial insert
        '    hotAPI.createRecord("' + moduleId + '", __vue_options__)\n' +
        '  } else {\n' +
        // update
        '    hotAPI.reload("' + moduleId + '", __vue_options__)\n' +
        '  }\n' +
        '})()}\n'
    }
    // check functional
    if (!isProduction) {
      output +=
        'if (__vue_options__.functional) {console.error("' +
          '[vue-loader] ' + fileName + ': functional components are not ' +
          'supported and should be defined in plain js files using render ' +
          'functions.' +
        '")}\n'
    }
    // final export
    if (options.esModule) {
      output += '\nexports.__esModule = true;\nexports["default"] = __vue_exports__\n'
    } else {
      output += '\nmodule.exports = __vue_exports__\n'
    }
  } else {
    // inject-loader support
    output +=
      '\n/* dependency injection */\n' +
      'module.exports = function (injections) {\n' +
      '  __vue_exports__ = __vue_exports__(injections)\n' +
      exports +
      '  return __vue_exports__\n' +
      '}'
  }

  // done
  return output
}
<commit_msg>use plain functions<commit_after>var loaderUtils = require('loader-utils')
var assign = require('object-assign')
var parse = require('./parser')
var path = require('path')
var normalize = require('./normalize')
var genId = require('./gen-id')

// internal lib loaders
var selectorPath = normalize.lib('selector')
var templateLoaderPath = normalize.lib('template-loader')
var templateCompilerPath = normalize.lib('template-compiler')
var styleRewriterPath = normalize.lib('style-rewriter')

// dep loaders
var styleLoaderPath = normalize.dep('vue-style-loader')
var hotReloadAPIPath = normalize.dep('vue-hot-reload-api')

var hasBabel = false
try {
  hasBabel = !!require('babel-loader')
} catch (e) {}

var hasBuble = false
try {
  hasBuble = !!require('buble-loader')
} catch (e) {}

var rewriterInjectRE = /\b(css(?:-loader)?(?:\?[^!]+)?)(?:!|$)/

var defaultLang = {
  template: 'html',
  styles: 'css',
  script: 'js'
}

var checkNamedExports =
  'if (Object.keys(__vue_exports__).some(function (key) { return key !== "default" && key !== "__esModule" })) {' +
  'console.error("named exports are not supported in *.vue files.")}\n'

module.exports = function (content) {
  this.cacheable()
  var isServer = this.options.target === 'node'
  var loaderContext = this
  var query = loaderUtils.parseQuery(this.query)
  var options = this.options.__vueOptions__ = Object.assign({}, this.options.vue, this.vue, query)
  var filePath = this.resourcePath
  var fileName = path.basename(filePath)
  var moduleId = 'data-v-' + genId(filePath)
  var styleRewriter = styleRewriterPath + '?id=' + moduleId

  var isProduction = this.minimize || process.env.NODE_ENV === 'production'

  var needCssSourceMap =
    !isProduction &&
    this.sourceMap &&
    options.cssSourceMap !== false

  var bubleOptions = hasBuble && options.buble ? '?' + JSON.stringify(options.buble) : ''
  var defaultLoaders = {
    html: templateCompilerPath + '?id=' + moduleId,
    css: (isServer ? '' : styleLoaderPath + '!') + 'css-loader' + (needCssSourceMap ? '?sourceMap' : ''),
    js: hasBuble ? ('buble-loader' + bubleOptions) : hasBabel ? 'babel-loader' : ''
  }

  // check if there are custom loaders specified via
  // webpack config, otherwise use defaults
  var loaders = assign({}, defaultLoaders, options.loaders)

  function getRequire (type, part, index, scoped) {
    return 'require(' +
      getRequireString(type, part, index, scoped) +
    ')\n'
  }

  function getRequireString (type, part, index, scoped) {
    return loaderUtils.stringifyRequest(loaderContext,
      // disable all configuration loaders
      '!!' +
      // get loader string for pre-processors
      getLoaderString(type, part, index, scoped) +
      // select the corresponding part from the vue file
      getSelectorString(type, index || 0) +
      // the url to the actual vuefile
      filePath
    )
  }

  function getRequireForImport (type, impt, scoped) {
    return 'require(' +
      getRequireForImportString(type, impt, scoped) +
    ')\n'
  }

  function getRequireForImportString (type, impt, scoped) {
    return loaderUtils.stringifyRequest(loaderContext,
      '!!' +
      getLoaderString(type, impt, -1, scoped) +
      impt.src
    )
  }

  function addCssModulesToLoader (loader, part, index) {
    if (!part.module) return loader
    var option = options.cssModules || {}
    var DEFAULT_OPTIONS = {
      modules: true,
      importLoaders: true
    }
    var OPTIONS = {
      localIdentName: '[hash:base64]'
    }
    return loader.replace(/((?:^|!)css(?:-loader)?)(\?[^!]*)?/, function (m, $1, $2) {
      // $1: !css-loader
      // $2: ?a=b
      var query = loaderUtils.parseQuery($2)
      Object.assign(query, OPTIONS, option, DEFAULT_OPTIONS)
      if (index !== -1) {
        // Note:
        //   Class name is generated according to its filename.
        //   Different <style> tags in the same .vue file may generate same names.
        //   Append `_[index]` to class name to avoid this.
        query.localIdentName += '_' + index
      }
      return $1 + '?' + JSON.stringify(query)
    })
  }

  function getLoaderString (type, part, index, scoped) {
    var lang = part.lang || defaultLang[type]
    var loader = loaders[lang]
    var rewriter = type === 'styles' ? styleRewriter + (scoped ? '&scoped=true!' : '!') : ''
    var injectString = (type === 'script' && query.inject) ? 'inject!' : ''
    if (loader !== undefined) {
      // add css modules
      if (type === 'styles') {
        loader = addCssModulesToLoader(loader, part, index)
      }
      // inject rewriter before css/html loader for
      // extractTextPlugin use cases
      if (rewriterInjectRE.test(loader)) {
        loader = loader.replace(rewriterInjectRE, function (m, $1) {
          return ensureBang($1) + rewriter
        })
      } else {
        loader = ensureBang(loader) + rewriter
      }
      return injectString + ensureBang(loader)
    } else {
      // unknown lang, infer the loader to be used
      switch (type) {
        case 'template':
          return defaultLoaders.html + '!' + templateLoaderPath + '?raw&engine=' + lang + '!'
        case 'styles':
          loader = addCssModulesToLoader(defaultLoaders.css, part, index)
          return loader + '!' + rewriter + ensureBang(ensureLoader(lang))
        case 'script':
          return injectString + ensureBang(ensureLoader(lang))
      }
    }
  }

  // sass => sass-loader
  // sass-loader => sass-loader
  // sass?indentedsyntax!css => sass-loader?indentedSyntax!css-loader
  function ensureLoader (lang) {
    return lang.split('!').map(function (loader) {
      return loader.replace(/^([\w-]+)(\?.*)?/, function (_, name, query) {
        return (/-loader$/.test(name) ? name : (name + '-loader')) + (query || '')
      })
    }).join('!')
  }

  function getSelectorString (type, index) {
    return selectorPath +
      '?type=' + type +
      '&index=' + index + '!'
  }

  function ensureBang (loader) {
    if (loader.charAt(loader.length - 1) !== '!') {
      return loader + '!'
    } else {
      return loader
    }
  }

  var parts = parse(content, fileName, this.sourceMap)
  var hasScoped = parts.styles.some(function (s) { return s.scoped })
  var output = 'var __vue_exports__, __vue_options__\n'

  // css modules
  output += 'var __vue_styles__ = {}\n'
  var cssModules = {}

  // add requires for styles
  if (parts.styles.length) {
    output += '\n/* styles */\n'
    parts.styles.forEach(function (style, i) {
      var moduleName = (style.module === true) ? '$style' : style.module

      // require style
      if (isServer && !moduleName) return
      var requireString = style.src
        ? getRequireForImport('styles', style, style.scoped)
        : getRequire('styles', style, i, style.scoped)

      // setCssModule
      if (moduleName) {
        if (moduleName in cssModules) {
          loaderContext.emitError('CSS module name "' + moduleName + '" is not unique!')
          output += requireString
        } else {
          cssModules[moduleName] = true

          // `style-loader` exposes the name-to-hash map directly
          // `css-loader` exposes it in `.locals`
          // We drop `style-loader` in SSR, and add `.locals` here.
          if (isServer) {
            requireString += '.locals'
          }

          output += '__vue_styles__["' + moduleName + '"] = ' + requireString + '\n'
        }
      } else {
        output += requireString
      }
    })
  }

  // add require for script
  var script = parts.script
  if (script) {
    output += '\n/* script */\n'
    output +=
      '__vue_exports__ = ' + (
        script.src
          ? getRequireForImport('script', script)
          : getRequire('script', script)
      )
  }

  var exports =
    '__vue_options__ = __vue_exports__ = __vue_exports__ || {}\n' +
    // ES6 modules interop
    'if (\n' +
    '  typeof __vue_exports__.default === "object" ||\n' +
    '  typeof __vue_exports__.default === "function"\n' +
    ') {\n' +
      (isProduction ? '' : checkNamedExports) +
      '__vue_options__ = __vue_exports__ = __vue_exports__.default\n' +
    '}\n' +
    // constructor export interop
    'if (typeof __vue_options__ === "function") {\n' +
    '  __vue_options__ = __vue_options__.options\n' +
    '}\n' +
    // add filename in dev
    (isProduction ? '' : ('__vue_options__.__file = ' + JSON.stringify(filePath))) + '\n'

  // add require for template
  var template = parts.template
  if (template) {
    output += '\n/* template */\n'
    output += 'var __vue_template__ = ' + (
      template.src
        ? getRequireForImport('template', template)
        : getRequire('template', template)
    )
    // attach render functions to exported options
    exports +=
      '__vue_options__.render = __vue_template__.render\n' +
      '__vue_options__.staticRenderFns = __vue_template__.staticRenderFns\n'
  }

  // attach scoped id
  if (hasScoped) {
    exports += '__vue_options__._scopeId = "' + moduleId + '"\n'
  }

  if (Object.keys(cssModules).length) {
    // inject style modules as computed properties
    exports +=
      'if (!__vue_options__.computed) __vue_options__.computed = {}\n' +
      'Object.keys(__vue_styles__).forEach(function (key) {\n' +
        'var module = __vue_styles__[key]\n' +
        '__vue_options__.computed[key] = function () { return module }\n' +
      '})\n'
  }

  if (!query.inject) {
    output += exports
    // hot reload
    if (
      !isServer &&
      !isProduction &&
      (parts.script || parts.template)
    ) {
      output +=
        '\n/* hot reload */\n' +
        'if (module.hot) {(function () {\n' +
        '  var hotAPI = require("' + hotReloadAPIPath + '")\n' +
        '  hotAPI.install(require("vue"), false)\n' +
        '  if (!hotAPI.compatible) return\n' +
        '  module.hot.accept()\n' +
        '  if (!module.hot.data) {\n' +
        // initial insert
        '    hotAPI.createRecord("' + moduleId + '", __vue_options__)\n' +
        '  } else {\n' +
        // update
        '    hotAPI.reload("' + moduleId + '", __vue_options__)\n' +
        '  }\n' +
        '})()}\n'
    }
    // check functional
    if (!isProduction) {
      output +=
        'if (__vue_options__.functional) {console.error("' +
          '[vue-loader] ' + fileName + ': functional components are not ' +
          'supported and should be defined in plain js files using render ' +
          'functions.' +
        '")}\n'
    }
    // final export
    if (options.esModule) {
      output += '\nexports.__esModule = true;\nexports["default"] = __vue_exports__\n'
    } else {
      output += '\nmodule.exports = __vue_exports__\n'
    }
  } else {
    // inject-loader support
    output +=
      '\n/* dependency injection */\n' +
      'module.exports = function (injections) {\n' +
      '  __vue_exports__ = __vue_exports__(injections)\n' +
      exports +
      '  return __vue_exports__\n' +
      '}'
  }

  // done
  return output
}
<|endoftext|>


<commit_before>const getConfigFn = require('think-config').getConfigFn;
const Logger = require('think-logger3');
const Loader = require('think-loader');
const path = require('path');
const helper = require('think-helper');
const Crontab = require('think-crontab');
const fs = require('fs');

require('./think.js');
// ThinkJS root path
const thinkPath = path.join(__dirname, '..');

/**
 * think loader
 * @param {Object} options
 */
const thinkLoader = class {
  constructor(options = {}) {
    this.options = options;
  }
  /**
   * init path
   */
  initPath() {
    think.ROOT_PATH = this.options.ROOT_PATH;
    think.APP_PATH = this.options.APP_PATH;
    // set env
    if (this.options.env) {
      think.app.env = this.options.env;
    }
    // set proxy
    if (this.options.proxy) {
      think.app.proxy = this.options.proxy;
    }
  }
  /**
   * load app data
   */
  loadData() {
    // add data to koa application
    think.app.modules = think.loader.modules;
    think.app.controllers = think.loader.loadController();
    think.app.logics = think.loader.loadLogic();
    think.app.models = think.loader.loadModel();
    think.app.services = think.loader.loadService();
    think.app.routers = think.loader.loadRouter();
    think.app.validators = think.loader.loadValidator();
  }
  /**
   * load middleware
   */
  loadMiddleware() {
    const middlewares = think.loader.loadMiddleware(think.app);
    middlewares.forEach(middleware => {
      think.app.use(middleware);
    });
  }
  /**
   * load extend
   */
  loadExtend() {
    const exts = think.loader.loadExtend();
    const list = [
      ['think', think],
      ['application', think.app],
      ['context', think.app.context],
      ['request', think.app.request],
      ['response', think.app.response],
      ['controller', think.Controller.prototype],
      ['logic', think.Logic.prototype],
      ['service', think.Service.prototype]
    ];
    list.forEach(item => {
      if (!exts[item[0]]) return;
      Loader.extend(item[1], exts[item[0]]);
    });
  }
  /**
   * load crontab
   */
  loadCrontab() {
    const crontab = think.loader.loadCrontab();
    const instance = new Crontab(crontab, think.app);
    instance.runTask();
  }
  /**
   * write config to file
   * @param {Config} config
   */
  writeConfig(config) {
    const configFilepath = path.join(think.ROOT_PATH, `runtime/config`);
    helper.mkdir(configFilepath);
    fs.writeFileSync(`${configFilepath}/${think.app.env}.json`, JSON.stringify(config, undefined, 2));
  }
  /**
   * load all data
   */
  loadAll(type, isCli) {
    this.initPath();
    think.loader = new Loader(think.APP_PATH, thinkPath);

    // write config to APP_PATH/runtime/config/[env].json file
    const config = think.loader.loadConfig(think.app.env);
    think.config = getConfigFn(config, think.loader.modules.length > 0);
    think.logger = new Logger(helper.parseAdapterConfig(think.config('logger')), true);

    if (type !== 'master') {
      this.writeConfig(config);
      this.loadExtend();
      this.loadData();
      this.loadMiddleware();
      if (!isCli) {
        this.loadCrontab();
      }
    }
    think.loader.loadBootstrap(type);
  }
};

module.exports = thinkLoader;
<commit_msg>Move get controllers after services<commit_after>const getConfigFn = require('think-config').getConfigFn;
const Logger = require('think-logger3');
const Loader = require('think-loader');
const path = require('path');
const helper = require('think-helper');
const Crontab = require('think-crontab');
const fs = require('fs');

require('./think.js');
// ThinkJS root path
const thinkPath = path.join(__dirname, '..');

/**
 * think loader
 * @param {Object} options
 */
const thinkLoader = class {
  constructor(options = {}) {
    this.options = options;
  }
  /**
   * init path
   */
  initPath() {
    think.ROOT_PATH = this.options.ROOT_PATH;
    think.APP_PATH = this.options.APP_PATH;
    // set env
    if (this.options.env) {
      think.app.env = this.options.env;
    }
    // set proxy
    if (this.options.proxy) {
      think.app.proxy = this.options.proxy;
    }
  }
  /**
   * load app data
   */
  loadData() {
    // add data to koa application
    think.app.modules = think.loader.modules;
    think.app.logics = think.loader.loadLogic();
    think.app.models = think.loader.loadModel();
    think.app.services = think.loader.loadService();
    think.app.controllers = think.loader.loadController();
    think.app.routers = think.loader.loadRouter();
    think.app.validators = think.loader.loadValidator();
  }
  /**
   * load middleware
   */
  loadMiddleware() {
    const middlewares = think.loader.loadMiddleware(think.app);
    middlewares.forEach(middleware => {
      think.app.use(middleware);
    });
  }
  /**
   * load extend
   */
  loadExtend() {
    const exts = think.loader.loadExtend();
    const list = [
      ['think', think],
      ['application', think.app],
      ['context', think.app.context],
      ['request', think.app.request],
      ['response', think.app.response],
      ['controller', think.Controller.prototype],
      ['logic', think.Logic.prototype],
      ['service', think.Service.prototype]
    ];
    list.forEach(item => {
      if (!exts[item[0]]) return;
      Loader.extend(item[1], exts[item[0]]);
    });
  }
  /**
   * load crontab
   */
  loadCrontab() {
    const crontab = think.loader.loadCrontab();
    const instance = new Crontab(crontab, think.app);
    instance.runTask();
  }
  /**
   * write config to file
   * @param {Config} config
   */
  writeConfig(config) {
    const configFilepath = path.join(think.ROOT_PATH, `runtime/config`);
    helper.mkdir(configFilepath);
    fs.writeFileSync(`${configFilepath}/${think.app.env}.json`, JSON.stringify(config, undefined, 2));
  }
  /**
   * load all data
   */
  loadAll(type, isCli) {
    this.initPath();
    think.loader = new Loader(think.APP_PATH, thinkPath);

    // write config to APP_PATH/runtime/config/[env].json file
    const config = think.loader.loadConfig(think.app.env);
    think.config = getConfigFn(config, think.loader.modules.length > 0);
    think.logger = new Logger(helper.parseAdapterConfig(think.config('logger')), true);

    if (type !== 'master') {
      this.writeConfig(config);
      this.loadExtend();
      this.loadData();
      this.loadMiddleware();
      if (!isCli) {
        this.loadCrontab();
      }
    }
    think.loader.loadBootstrap(type);
  }
};

module.exports = thinkLoader;
<|endoftext|>


<commit_before>var logger = function() {}
  , _      = require('underscore');

_.extend(logger.prototype, {
  log: function(msg) {
    console.log(">>>> " + msg + " <<<<");
  }
});

module.exports = logger;
<commit_msg>the @caseywebdev way<commit_after>var logger = function() {};
var _ = require('underscore');

_.extend(logger.prototype, {
  log: function(msg) {
    console.log(">>>> " + msg + " <<<<");
  }
});

module.exports = logger;
<|endoftext|>


<commit_before>/*
 *  logger.js
 *
 *  David Janes
 *  IOTDB.org
 *  2016-04-07
 *
 *  Copyright [2013-2016] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

const os = require("os")
const util = require("util")
const events = require('events')

/**
 *  Make a new logger. Clevel people could
 *  swap this out to replace it.
 */
const make = _initd => {
    const _ = require("..")
    const self = Object.assign({})

    const initd = _.d.clone(_initd)
    initd.hostname = os.hostname()
    initd.pid = process.pid
    initd.v = 0

    const emitter = new events();
    self.on = (...rest) => emitter.on.apply(emitter, rest)

    const _log = level => (...rest) => {
        let packet = _.d.clone(initd)
        packet.level = level;
        packet.time = _.timestamp.make()

        if (rest.length) {
            if (rest[0] instanceof Error) {
                packer.err = {
                    name: error.name,
                    message: error.message,
                    stack: error.stack,
                }

                rest.shift()
            } else if (_.is.Object(rest[0])) {
                packet = Object.assign({}, packet, rest[0])
                rest.shift()
            }
        }

        if (rest.length) {
            packet.msg = util.format.apply(util.format, rest)
        }

        process.stdout.write(JSON.stringify(packet))
        process.stdout.write("\n")

        emitter.emit("event", packet)

        return packet;
    }

    self.trace = _log(10)
    self.debug = _log(20)
    self.info = _log(30)
    self.warn = _log(40)
    self.error = _log(50)
    self.fatal = _log(60)

    return self;
};

const _logd = {
    debug: true,
    info: true,
    error: true,
    warn: true,
    trace: true,
    fatal: true,
};

/**
 *  Set the logging levels.
 *
 *  If d.all is set, everything in _logd
 *  is set to that value first.
 */
const levels = d => {
    if (d.all !== undefined) {
        _.keys(_logd).forEach(key => _logd[key] = d.all);
    }

    _.mapObject(d).forEach(kv => _logd[kv[0]] = kv[1])
};

/**
 *  Turn off all logging except
 *  for fatal error messages
 */
const silent = function() {
    levels({
        all: false,
        fatal: true,
    });
};

const _mutes = [];

/**
 *  Mute based on dictionary
 */
const mute = md => _mutes.push(md);

/**
 *  Create a logger. If you don't want
 *  to use bunyan, replace 'logger.make',
 *  not this
 */
const logger = function (initd) {
    const _d = require("./d").d;
    const _logger = function() {
        let l = null;

        const _make = () => {
            if (l === null) {
                l = make(initd);
            }
        };

        const _level = level => {
            return function(...rest) {
                if (!_logd[level]) {
                    return;
                }

                if (_mutes.find(m => _d.is.subset(m, initd))) {
                    return;
                }

                _make();
                return l[level](...rest)
            }
        };

        return {
            debug: _level("debug"),
            info: _level("info"),
            warn: _level("warn"),
            error: _level("error"),
            trace: _level("trace"),
            fatal: _level("fatal"),
        }
    };

    return _logger();
};

/* --- API --- */
exports.logger = {
    make: make,
    logger: logger,
    levels: levels,
    silent: silent,
    mute: mute,
};
<commit_msg>fix logging levels (sorta)<commit_after>/*
 *  logger.js
 *
 *  David Janes
 *  IOTDB.org
 *  2016-04-07
 *
 *  Copyright [2013-2016] [David P. Janes]
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

"use strict";

const os = require("os")
const util = require("util")
const events = require('events')

/**
 *  Make a new logger. Clevel people could
 *  swap this out to replace it.
 */
const make = _initd => {
    const _ = require("..")
    const self = Object.assign({})

    const initd = _.d.clone(_initd)
    initd.hostname = os.hostname()
    initd.pid = process.pid
    initd.v = 0

    const emitter = new events();
    self.on = (...rest) => emitter.on.apply(emitter, rest)

    const _log = level => (...rest) => {
        let packet = _.d.clone(initd)
        packet.level = level;
        packet.time = _.timestamp.make()

        if (rest.length) {
            if (rest[0] instanceof Error) {
                packer.err = {
                    name: error.name,
                    message: error.message,
                    stack: error.stack,
                }

                rest.shift()
            } else if (_.is.Object(rest[0])) {
                packet = Object.assign({}, packet, rest[0])
                rest.shift()
            }
        }

        if (rest.length) {
            packet.msg = util.format.apply(util.format, rest)
        }

        process.stdout.write(JSON.stringify(packet))
        process.stdout.write("\n")

        emitter.emit("event", packet)

        return packet;
    }

    self.trace = _log(10)
    self.debug = _log(20)
    self.info = _log(30)
    self.warn = _log(40)
    self.error = _log(50)
    self.fatal = _log(60)

    return self;
};

const _logd = {
    debug: true,
    info: true,
    error: true,
    warn: true,
    trace: true,
    fatal: true,
};

/**
 *  Set the logging levels.
 *
 *  If leveld.all is set, everything in _logd
 *  is set to that value first.
 */
const levels = leveld => {
    if (leveld.all !== undefined) {
        Object.keys(_logd).forEach(key => _logd[key] = leveld.all);
    }

    Object.keys(leveld).forEach(key => _logd[key] = leveld[key])
}

/**
 *  Turn off all logging except
 *  for fatal error messages
 */
const silent = function() {
    levels({
        all: false,
        fatal: true,
    });
};

const _mutes = [];

/**
 *  Mute based on dictionary
 */
const mute = md => _mutes.push(md);

/**
 *  Create a logger. If you don't want
 *  to use bunyan, replace 'logger.make',
 *  not this
 */
const logger = function (initd) {
    const _d = require("./d").d;
    const _logger = function() {
        let l = null;

        const _make = () => {
            if (l === null) {
                l = make(initd);
            }
        };

        const _level = level => {
            return function(...rest) {
                if (!_logd[level]) {
                    return;
                }

                if (_mutes.find(m => _d.is.subset(m, initd))) {
                    return;
                }

                _make();
                return l[level](...rest)
            }
        };

        return {
            debug: _level("debug"),
            info: _level("info"),
            warn: _level("warn"),
            error: _level("error"),
            trace: _level("trace"),
            fatal: _level("fatal"),
        }
    };

    return _logger();
};

/* --- API --- */
exports.logger = {
    make: make,
    logger: logger,
    levels: levels,
    silent: silent,
    mute: mute,
};
<|endoftext|>


<commit_before>const chalk = require('chalk');
const columnify = require('columnify');
const prettyjson = require('prettyjson');

const MAX_DESCRIPTION_LENGTH = 100;

class Logger {
  static stripHtml(str) {
    str = str || '';
    return str.replace(/<(?:.|\n)*?>/gm, '');
  }

  static chalkOperation(method, path, operationId, desc) {
    let str = Logger.chalkMethod(method) + '\t' + path;
    if (operationId) str += '\n' + chalk.magenta(operationId);
    if (desc) str += '\n' + chalk.gray(desc);
    return str;
  }

  static chalkMethod(method) {
    method = method.toUpperCase();
    if (method === 'GET') return chalk.green(method);
    if (method === 'PUT' || method === 'POST' || method === 'PATCH') return chalk.yellow(method);
    if (method === 'DELETE') return chalk.red(method);
    return method;
  }

  static chalkType(type) {
    type = type || 'string';
    if (type === 'string') return chalk.green(type);
    if (type === 'integer' || type === 'number') return chalk.blue(type);
    if (type === 'boolean') return chalk.yellow(type);
    if (type === 'array' || type === 'object') return chalk.magenta(type);
    return type;
  }

  static chalkCode(code) {
    if (code.startsWith('2')) return chalk.green(code);
    if (code.startsWith('3')) return chalk.yellow(code);
    if (code.startsWith('4')) return chalk.orange(code);
    if (code.startsWith('5')) return chalk.red(code);
  }

  static padString(str, len) {
    while (str.length < len) str += ' ';
    return str;
  }

  static logColumns(cols, options) {
    options = options || {};
    options.columnSplitter = '  ';
    Logger.log(columnify(cols, options));
  }

  static logJSON(json) {
    Logger.log(prettyjson.render(json));
  }

  static logSchema(schema, indent, name) {
    indent = indent || '';
    if (indent.length > 20) return Logger.log('...\n\n')
    let toLog = name ? indent + Logger.padString(name + ': ', 14) + Logger.chalkType(schema.type) : '';
    if (schema.properties) {
      if (toLog) Logger.log(toLog);
      for (let propName in schema.properties) {
        let prop = schema.properties[propName];
        Logger.logSchema(prop, indent + '  ', propName);
      }
    } else if (schema.items) {
      if (schema.items.properties || schema.items.items) {
        if (toLog) Logger.log(toLog);
        Logger.logSchema(schema.items, indent + '  ', 'items')
      } else {
        toLog = toLog || indent + Logger.chalkType('array');
        toLog += '[' + Logger.chalkType(schema.items.type) + ']';
        Logger.log(toLog);
      }
    } else {
      if (toLog) Logger.log(toLog);
    }
  }

  static logOperation(method, path, op) {
    Logger.log(Logger.chalkOperation(method, path, op.operationId, op.summary || op.description));
  }

  static logDescription(str) {
    if (!str) return;
    str = Logger.stripHtml(str);
    if (str.length > MAX_DESCRIPTION_LENGTH) {
      str = str.substring(0, MAX_DESCRIPTION_LENGTH) + '...';
    }
    Logger.log(chalk.gray(str.trim()));
  }

  static logParameters(parameters) {
    let paramDescriptions = parameters.map(p => {
      let ret = {parameter: p.name};
      ret.type = Logger.chalkType(p.in === 'body' ? 'object': p.type);
      ret.required = p.required ? chalk.red('yes') : '';
      ret.default = p.default;
      if (p.description) {
        ret.description = chalk.gray(p.description);
      }
      return ret;
    })
    Logger.log(columnify(paramDescriptions, {
      columnSplitter: '  ',
      config: {
        description: {
          maxWidth: 80
        }
      }
    }));
  }

  static logSuccess(str) {
    str = str || 'Error';
    Logger.log(chalk.green(str));
  }
  static logError(str) {
    str = str || 'Error';
    Logger.log(chalk.red(str));
  }

  static log(str) {
    if (str === undefined) str = '';
    console.log(str);
  }
}

module.exports = Logger;
<commit_msg>truncate descriptions at first line<commit_after>const chalk = require('chalk');
const columnify = require('columnify');
const prettyjson = require('prettyjson');

const MAX_DESCRIPTION_LENGTH = 100;

class Logger {
  static stripHtml(str) {
    str = str || '';
    return str.replace(/<(?:.|\n)*?>/gm, '');
  }

  static chalkOperation(method, path, operationId, desc) {
    let str = Logger.chalkMethod(method) + '\t' + path;
    if (operationId) str += '\n' + chalk.magenta(operationId);
    if (desc) str += '\n' + chalk.gray(desc);
    return str;
  }

  static chalkMethod(method) {
    method = method.toUpperCase();
    if (method === 'GET') return chalk.green(method);
    if (method === 'PUT' || method === 'POST' || method === 'PATCH') return chalk.yellow(method);
    if (method === 'DELETE') return chalk.red(method);
    return method;
  }

  static chalkType(type) {
    type = type || 'string';
    if (type === 'string') return chalk.green(type);
    if (type === 'integer' || type === 'number') return chalk.blue(type);
    if (type === 'boolean') return chalk.yellow(type);
    if (type === 'array' || type === 'object') return chalk.magenta(type);
    return type;
  }

  static chalkCode(code) {
    if (code.startsWith('2')) return chalk.green(code);
    if (code.startsWith('3')) return chalk.yellow(code);
    if (code.startsWith('4')) return chalk.orange(code);
    if (code.startsWith('5')) return chalk.red(code);
  }

  static padString(str, len) {
    while (str.length < len) str += ' ';
    return str;
  }

  static logColumns(cols, options) {
    options = options || {};
    options.columnSplitter = '  ';
    Logger.log(columnify(cols, options));
  }

  static logJSON(json) {
    Logger.log(prettyjson.render(json));
  }

  static logSchema(schema, indent, name) {
    indent = indent || '';
    if (indent.length > 20) return Logger.log('...\n\n')
    let toLog = name ? indent + Logger.padString(name + ': ', 14) + Logger.chalkType(schema.type) : '';
    if (schema.properties) {
      if (toLog) Logger.log(toLog);
      for (let propName in schema.properties) {
        let prop = schema.properties[propName];
        Logger.logSchema(prop, indent + '  ', propName);
      }
    } else if (schema.items) {
      if (schema.items.properties || schema.items.items) {
        if (toLog) Logger.log(toLog);
        Logger.logSchema(schema.items, indent + '  ', 'items')
      } else {
        toLog = toLog || indent + Logger.chalkType('array');
        toLog += '[' + Logger.chalkType(schema.items.type) + ']';
        Logger.log(toLog);
      }
    } else {
      if (toLog) Logger.log(toLog);
    }
  }

  static logOperation(method, path, op) {
    Logger.log(Logger.chalkOperation(method, path, op.operationId, op.summary || op.description));
  }

  static logDescription(str) {
    if (!str) return;
    str = Logger.stripHtml(str);
    let newline = str.indexOf('\n');
    if (newline !== -1) str = str.substring(0, newline);
    if (str.length > MAX_DESCRIPTION_LENGTH) {
      str = str.substring(0, MAX_DESCRIPTION_LENGTH) + '...';
    }
    Logger.log(chalk.gray(str.trim()));
  }

  static logParameters(parameters) {
    let paramDescriptions = parameters.map(p => {
      let ret = {parameter: p.name};
      ret.type = Logger.chalkType(p.in === 'body' ? 'object': p.type);
      ret.required = p.required ? chalk.red('yes') : '';
      ret.default = p.default;
      if (p.description) {
        ret.description = chalk.gray(p.description);
      }
      return ret;
    })
    Logger.log(columnify(paramDescriptions, {
      columnSplitter: '  ',
      config: {
        description: {
          maxWidth: 80
        }
      }
    }));
  }

  static logSuccess(str) {
    str = str || 'Error';
    Logger.log(chalk.green(str));
  }
  static logError(str) {
    str = str || 'Error';
    Logger.log(chalk.red(str));
  }

  static log(str) {
    if (str === undefined) str = '';
    console.log(str);
  }
}

module.exports = Logger;
<|endoftext|>


<commit_before>"use strict";

var fs = require('fs');
var path = require('path');

var bunyan = require('bunyan');

var config = {
    level: 'trace', // logging.level possible values: fatal, error, warn, info, debug, trace
    logFolder: './logs',
    filename: 'app.log',
    logRequests: true,
    period: '1d', // one of "h"(hours), "d"(days), "w"(weeks), "m"(months), "y"(years)
    count: 10 // number of rotated files to keep
};

if (!fs.existsSync(config.logFolder)) {
    fs.mkdirSync(config.logFolder);
}

module.exports = bunyan.createLogger({
    name: "blue-button-fhir",
    streams: [{
        type: 'rotating-file',
        level: config.level,
        path: path.join(config.logFolder, config.filename),
        period: config.period,
        count: config.count
    }]
});
<commit_msg>Change logging dir.<commit_after>"use strict";

var fs = require('fs');
var path = require('path');

var bunyan = require('bunyan');

var config = {
    level: 'trace', // logging.level possible values: fatal, error, warn, info, debug, trace
    logFolder: './log',
    filename: 'app.log',
    logRequests: true,
    period: '1d', // one of "h"(hours), "d"(days), "w"(weeks), "m"(months), "y"(years)
    count: 10 // number of rotated files to keep
};

if (!fs.existsSync(config.logFolder)) {
    fs.mkdirSync(config.logFolder);
}

module.exports = bunyan.createLogger({
    name: "blue-button-fhir",
    streams: [{
        type: 'rotating-file',
        level: config.level,
        path: path.join(config.logFolder, config.filename),
        period: config.period,
        count: config.count
    }]
});
<|endoftext|>


<commit_before>var log4js = require('log4js');
var fs = require('fs');

var funcs = {
	'env': doEnv,
	'args': doArgs,
	'opts': doOpts
};

function getLogger(categoryName) {
	var args = arguments;
	var prefix = "";
	for (var i = 1; i < args.length; i++) {
		if(i !== args.length-1)
			prefix = prefix + args[i] + "] [";
		else
			prefix = prefix + args[i];
	}
	if (typeof categoryName === 'string') {
		// category name is __filename then cut the prefix path
		categoryName = categoryName.replace(process.cwd(), '');
	}
	var logger = log4js.getLogger(categoryName);
	var pLogger = {};
	for (var key in logger) {
		pLogger[key] = logger[key];
	}

	['log', 'debug', 'info', 'warn', 'error', 'trace', 'fatal'].forEach(function(item) {
		pLogger[item] = function() {
			var p = "";
			if (args.length > 1) {
				p = "[" + prefix + "] ";
			}
			if(args.length && process.env.LOGGER_LINE) {
				p = getLine() + ": " + p;
			}
			p = colorize(p, colours[item]);
			if(args.length) {
				arguments[0] = p + arguments[0];
			}
			logger[item].apply(logger, arguments);
		}
	});
	return pLogger;
}

/**
 * Configure the logger.
 * Configure file just like log4js.json. And support ${scope:arg-name} format property setting.
 * It can replace the placeholder in runtime.
 * scope can be:
 *     env: environment variables, such as: env:PATH
 *     args: command line arguments, such as: args:1
 *     opts: key/value from opts argument of configure function
 *
 * @param  {String|Object} config configure file name or configure object
 * @param  {Object} opts   options
 * @return {Void}
 */

function configure(config, opts) {
	config = config || process.env.LOG4JS_CONFIG;
	opts = opts || {};

	if (typeof config === 'string') {
		config = JSON.parse(fs.readFileSync(config, "utf8"));
	}

	if (config) {
		config = replaceProperties(config, opts);
	}

	if(config && config.lineDebug) {
		process.env.LOGGER_LINE = true;
	}
	// config object could not turn on the auto reload configure file in log4js
	log4js.configure(config, opts);
}

function replaceProperties(configObj, opts) {
	if (configObj instanceof Array) {
		for (var i = 0, l = configObj.length; i < l; i++) {
			configObj[i] = replaceProperties(configObj[i], opts);
		}
	} else if (typeof configObj === 'object') {
		var field;
		for (var f in configObj) {
			if (!configObj.hasOwnProperty(f)) {
				continue;
			}

			field = configObj[f];
			if (typeof field === 'string') {
				configObj[f] = doReplace(field, opts);
			} else if (typeof field === 'object') {
				configObj[f] = replaceProperties(field, opts);
			}
		}
	}

	return configObj;
}

function doReplace(src, opts) {
	if (!src) {
		return src;
	}

	var ptn = /\$\{(.*?)\}/g;
	var m, pro, ts, scope, name, defaultValue, func, res = '',
		lastIndex = 0;
	while ((m = ptn.exec(src))) {
		pro = m[1];
		ts = pro.split(':');
		if (ts.length !== 2 && ts.length !== 3) {
			res += pro;
			continue;
		}

		scope = ts[0];
		name = ts[1];
		if (ts.length === 3) {
			defaultValue = ts[2];
		}

		func = funcs[scope];
		if (!func && typeof func !== 'function') {
			res += pro;
			continue;
		}

		res += src.substring(lastIndex, m.index);
		lastIndex = ptn.lastIndex;
		res += (func(name, opts) || defaultValue);
	}

	if (lastIndex < src.length) {
		res += src.substring(lastIndex);
	}

	return res;
}

function doEnv(name) {
	return process.env[name];
}

function doArgs(name) {
	return process.argv[name];
}

function doOpts(name, opts) {
	return opts ? opts[name] : undefined;
}

function getLine() {
	var e = new Error();
	// now magic will happen: get line number from callstack
	var line = e.stack.split('\n')[3].split(':')[1];
	return line;
}

function colorizeStart(style) {
  return style ? '\x1B[' + styles[style][0] + 'm' : '';
}
function colorizeEnd(style) {
  return style ? '\x1B[' + styles[style][1] + 'm' : '';
}
/**
 * Taken from masylum's fork (https://github.com/masylum/log4js-node)
 */
function colorize (str, style) {
  return colorizeStart(style) + str + colorizeEnd(style);
}

var styles = {
    //styles
  'bold'      : [1,  22],
  'italic'    : [3,  23],
  'underline' : [4,  24],
  'inverse'   : [7,  27],
  //grayscale
  'white'     : [37, 39],
  'grey'      : [90, 39],
  'black'     : [90, 39],
  //colors
  'blue'      : [34, 39],
  'cyan'      : [36, 39],
  'green'     : [32, 39],
  'magenta'   : [35, 39],
  'red'       : [31, 39],
  'yellow'    : [33, 39]
};

var colours = {
  'all': "grey",
  'trace': "blue",
  'debug': "cyan",
  'info': "green",
  'warn': "yellow",
  'error': "red",
  'fatal': "magenta",
  'off': "grey"
};

module.exports = {
	getLogger: getLogger,
	getDefaultLogger: log4js.getDefaultLogger,

	addAppender: log4js.addAppender,
	loadAppender: log4js.loadAppender,
	clearAppenders: log4js.clearAppenders,
	configure: configure,

	replaceConsole: log4js.replaceConsole,
	restoreConsole: log4js.restoreConsole,

	levels: log4js.levels,
	setGlobalLogLevel: log4js.setGlobalLogLevel,

	layouts: log4js.layouts,
	appenders: log4js.appenders
};

configure();<commit_msg>add reload feature<commit_after>var log4js = require('log4js');
var fs = require('fs');
var util = require('util');

var funcs = {
	'env': doEnv,
	'args': doArgs,
	'opts': doOpts
};

function getLogger(categoryName) {
	var args = arguments;
	var prefix = "";
	for (var i = 1; i < args.length; i++) {
		if(i !== args.length-1)
			prefix = prefix + args[i] + "] [";
		else
			prefix = prefix + args[i];
	}
	if (typeof categoryName === 'string') {
		// category name is __filename then cut the prefix path
		categoryName = categoryName.replace(process.cwd(), '');
	}
	var logger = log4js.getLogger(categoryName);
	var pLogger = {};
	for (var key in logger) {
		pLogger[key] = logger[key];
	}

	['log', 'debug', 'info', 'warn', 'error', 'trace', 'fatal'].forEach(function(item) {
		pLogger[item] = function() {
			var p = "";
			if (args.length > 1) {
				p = "[" + prefix + "] ";
			}
			if(args.length && process.env.LOGGER_LINE) {
				p = getLine() + ": " + p;
			}
			p = colorize(p, colours[item]);
			if(args.length) {
				arguments[0] = p + arguments[0];
			}
			logger[item].apply(logger, arguments);
		}
	});
	return pLogger;
};

var configState = {};

function initReloadConfiguration(filename, reloadSecs) {
	if (configState.timerId) {
    clearInterval(configState.timerId);
    delete configState.timerId;
  }
  configState.filename = filename;
  configState.lastMTime = getMTime(filename);
  configState.timerId = setInterval(reloadConfiguration, reloadSecs * 1000);
};

function getMTime(filename) {
  var mtime;
  try {
    mtime = fs.statSync(configState.filename).mtime;
  } catch (e) {
      throw new Error("Cannot find file.");
  }
  return mtime;
};

function loadConfigurationFile(filename) {
  if (filename) {
    return JSON.parse(fs.readFileSync(filename, "utf8"));
  }
  return undefined;
};

function reloadConfiguration() {
  var mtime = getMTime(configState.filename);
  if (!mtime) {
  	return;
  }
  if (configState.lastMTime && (mtime.getTime() > configState.lastMTime.getTime())) {
    configureOnceOff(loadConfigurationFile(configState.filename));
  }
  configState.lastMTime = mtime;
};

function configureOnceOff(config) {
  if (config) {
    try {
      configureLevels(config.levels);
      if (config.replaceConsole) {
        log4js.replaceConsole();
      } else {
        log4js.restoreConsole();
      }
    } catch (e) {
      throw new Error(
        "Problem reading log4js config " + util.inspect(config) +
          ". Error was \"" + e.message + "\" (" + e.stack + ")"
      );
    }
  }
};

function configureLevels(levels) {
  if (levels) {
    for (var category in levels) {
      if (levels.hasOwnProperty(category)) {
        log4js.getLogger(category).setLevel(levels[category]);
      }
    }
  }
};

/**
 * Configure the logger.
 * Configure file just like log4js.json. And support ${scope:arg-name} format property setting.
 * It can replace the placeholder in runtime.
 * scope can be:
 *     env: environment variables, such as: env:PATH
 *     args: command line arguments, such as: args:1
 *     opts: key/value from opts argument of configure function
 *
 * @param  {String|Object} config configure file name or configure object
 * @param  {Object} opts   options
 * @return {Void}
 */

function configure(config, opts) {
	var filename = config;
	config = config || process.env.LOG4JS_CONFIG;
	opts = opts || {};

	if (typeof config === 'string') {
		config = JSON.parse(fs.readFileSync(config, "utf8"));
	}

	if (config) {
		config = replaceProperties(config, opts);
	}

	if(config && config.lineDebug) {
		process.env.LOGGER_LINE = true;
	}

	if(filename && config && config.reloadSecs) {
		initReloadConfiguration(filename, config.reloadSecs);
  }

	// config object could not turn on the auto reload configure file in log4js

	log4js.configure(config, opts);
};

function replaceProperties(configObj, opts) {
	if (configObj instanceof Array) {
		for (var i = 0, l = configObj.length; i < l; i++) {
			configObj[i] = replaceProperties(configObj[i], opts);
		}
	} else if (typeof configObj === 'object') {
		var field;
		for (var f in configObj) {
			if (!configObj.hasOwnProperty(f)) {
				continue;
			}

			field = configObj[f];
			if (typeof field === 'string') {
				configObj[f] = doReplace(field, opts);
			} else if (typeof field === 'object') {
				configObj[f] = replaceProperties(field, opts);
			}
		}
	}

	return configObj;
}

function doReplace(src, opts) {
	if (!src) {
		return src;
	}

	var ptn = /\$\{(.*?)\}/g;
	var m, pro, ts, scope, name, defaultValue, func, res = '',
		lastIndex = 0;
	while ((m = ptn.exec(src))) {
		pro = m[1];
		ts = pro.split(':');
		if (ts.length !== 2 && ts.length !== 3) {
			res += pro;
			continue;
		}

		scope = ts[0];
		name = ts[1];
		if (ts.length === 3) {
			defaultValue = ts[2];
		}

		func = funcs[scope];
		if (!func && typeof func !== 'function') {
			res += pro;
			continue;
		}

		res += src.substring(lastIndex, m.index);
		lastIndex = ptn.lastIndex;
		res += (func(name, opts) || defaultValue);
	}

	if (lastIndex < src.length) {
		res += src.substring(lastIndex);
	}

	return res;
}

function doEnv(name) {
	return process.env[name];
}

function doArgs(name) {
	return process.argv[name];
}

function doOpts(name, opts) {
	return opts ? opts[name] : undefined;
}

function getLine() {
	var e = new Error();
	// now magic will happen: get line number from callstack
	var line = e.stack.split('\n')[3].split(':')[1];
	return line;
}

function colorizeStart(style) {
  return style ? '\x1B[' + styles[style][0] + 'm' : '';
}
function colorizeEnd(style) {
  return style ? '\x1B[' + styles[style][1] + 'm' : '';
}
/**
 * Taken from masylum's fork (https://github.com/masylum/log4js-node)
 */
function colorize (str, style) {
  return colorizeStart(style) + str + colorizeEnd(style);
}

var styles = {
    //styles
  'bold'      : [1,  22],
  'italic'    : [3,  23],
  'underline' : [4,  24],
  'inverse'   : [7,  27],
  //grayscale
  'white'     : [37, 39],
  'grey'      : [90, 39],
  'black'     : [90, 39],
  //colors
  'blue'      : [34, 39],
  'cyan'      : [36, 39],
  'green'     : [32, 39],
  'magenta'   : [35, 39],
  'red'       : [31, 39],
  'yellow'    : [33, 39]
};

var colours = {
  'all': "grey",
  'trace': "blue",
  'debug': "cyan",
  'info': "green",
  'warn': "yellow",
  'error': "red",
  'fatal': "magenta",
  'off': "grey"
};

module.exports = {
	getLogger: getLogger,
	getDefaultLogger: log4js.getDefaultLogger,

	addAppender: log4js.addAppender,
	loadAppender: log4js.loadAppender,
	clearAppenders: log4js.clearAppenders,
	configure: configure,

	replaceConsole: log4js.replaceConsole,
	restoreConsole: log4js.restoreConsole,

	levels: log4js.levels,
	setGlobalLogLevel: log4js.setGlobalLogLevel,

	layouts: log4js.layouts,
	appenders: log4js.appenders
};<|endoftext|>


<commit_before>'use strict'

var Path = require('path')
var Url = require('url')
var _ = require('lodash')

module.exports = function mapper (routePlan) {
  var routes = []

  // routePlan can be an array or single object.
  routePlan = _.flatten([routePlan])

  _.each(routePlan, (routeSet) => {
    _.mapKeys(routeSet.map, (value, key) => {
      // We need both a part and pin to have a valid route.
      if (!routeSet.pin || !key) {
        return
      }

      var defaultRoute = {
        prefix: false,
        postfix: false,
        part: false,
        pin: false,
        alias: false,
        methods: [],
        autoreply: true,
        redirect: false,
        auth: false,
        secure: false
      }

      var route = {
        prefix: routeSet.prefix,
        postfix: routeSet.postfix,
        pin: routeSet.pin,
        part: key
      }

      // Minimally viable route.
      route = _.merge({}, defaultRoute, route)

      // allows custom patterns, looks for the * in "role:todo,cmd:*"
      route.pattern = route.pin.replace(':*', `:${key}`)

      // If the value a bool the route is a simple get.
      if (!_.isObject(value)) {
        route.methods.push('GET')
        route.path = buildPath(route)
        routes.push(route)
        return
      }

      // Set alias' and redirects.
      route.alias = value.alias || route.alias
      route.redirect = value.redirect || route.redirect

      // Checking specifically for false as true or missing means on.
      if (value.autoreply === false) {
        route.autoreply = false
      }

      // If there is an auth and strategy, use it.
      if (value.auth && value.auth.strategy) {
        route.auth = value.auth
      }

      // If there is a secure, set it
      if (value.secure) {
        route.secure = value.secure
      }

      _.mapKeys(value, (active, method) => {
        if (isValidMethod(method)) {
          route.methods.push(method)
        }
      })

      // build the route and push to the list
      route.path = buildPath(route)
      routes.push(route)
    })
  })

  return routes
}

// Path won't work on windows, need non lazy solution
function buildPath (route) {
  let path = null
  if (route.alias) {
    path = Path.join('/', route.alias)
  }
  else {
    const prefix = route.prefix || ''
    const part = route.part || ''
    const postfix = route.postfix || ''
    path = Path.join('/', prefix, part, postfix)
  }
  return Url.parse(path).path
}

function isValidMethod (method) {
  method = method || ''
  method = method.toString()
  method = method.toUpperCase()

  const methods = [
    'GET',
    'POST',
    'PUT',
    'HEAD',
    'DELETE',
    'OPTIONS',
    'PATCH'
  ]

  return _.includes(methods, method)
}
<commit_msg>Remove unnecessary comment<commit_after>'use strict'

var Path = require('path')
var Url = require('url')
var _ = require('lodash')

module.exports = function mapper (routePlan) {
  var routes = []

  // routePlan can be an array or single object.
  routePlan = _.flatten([routePlan])

  _.each(routePlan, (routeSet) => {
    _.mapKeys(routeSet.map, (value, key) => {
      // We need both a part and pin to have a valid route.
      if (!routeSet.pin || !key) {
        return
      }

      var defaultRoute = {
        prefix: false,
        postfix: false,
        part: false,
        pin: false,
        alias: false,
        methods: [],
        autoreply: true,
        redirect: false,
        auth: false,
        secure: false
      }

      var route = {
        prefix: routeSet.prefix,
        postfix: routeSet.postfix,
        pin: routeSet.pin,
        part: key
      }

      // Minimally viable route.
      route = _.merge({}, defaultRoute, route)

      // allows custom patterns, looks for the * in "role:todo,cmd:*"
      route.pattern = route.pin.replace(':*', `:${key}`)

      // If the value a bool the route is a simple get.
      if (!_.isObject(value)) {
        route.methods.push('GET')
        route.path = buildPath(route)
        routes.push(route)
        return
      }

      // Set alias' and redirects.
      route.alias = value.alias || route.alias
      route.redirect = value.redirect || route.redirect

      // Checking specifically for false as true or missing means on.
      if (value.autoreply === false) {
        route.autoreply = false
      }

      // If there is an auth and strategy, use it.
      if (value.auth && value.auth.strategy) {
        route.auth = value.auth
      }

      // If there is a secure, set it
      if (value.secure) {
        route.secure = value.secure
      }

      _.mapKeys(value, (active, method) => {
        if (isValidMethod(method)) {
          route.methods.push(method)
        }
      })

      // build the route and push to the list
      route.path = buildPath(route)
      routes.push(route)
    })
  })

  return routes
}

function buildPath (route) {
  let path = null
  if (route.alias) {
    path = Path.join('/', route.alias)
  }
  else {
    const prefix = route.prefix || ''
    const part = route.part || ''
    const postfix = route.postfix || ''
    path = Path.join('/', prefix, part, postfix)
  }
  return Url.parse(path).path
}

function isValidMethod (method) {
  method = method || ''
  method = method.toString()
  method = method.toUpperCase()

  const methods = [
    'GET',
    'POST',
    'PUT',
    'HEAD',
    'DELETE',
    'OPTIONS',
    'PATCH'
  ]

  return _.includes(methods, method)
}
<|endoftext|>


<commit_before>//----------------------------------------------------------------------------------------------------------------------
// Implements a model based API for JBase.
//
// @module models.js
//----------------------------------------------------------------------------------------------------------------------

var util = require('util');

var _ = require('lodash');
var oftype = require('oftype');
var Promise = require('bluebird');

var errors = require('./errors');

//----------------------------------------------------------------------------------------------------------------------

function Model(modelDef)
{
    //TODO: Would it be safe to just make this _.clone() instead?
    this.$$values = _.cloneDeep(this.$$defaultValues);

    // Dirty can be read by external things, but we prefix it with '$' to prevent property collision.
    this.$dirty = false;

    // Prevent any chance of accidentally overwriting the built in model properties
    var safeDef = _.omit(modelDef, ['$$db', '$$schema', '$$values', '$dirty', 'id', 'save', 'sync', 'toJSON', 'validate']);

    // Populate the model
    _.merge(this, safeDef);
} // end Model

function buildSchema(model)
{
    var proto = model.prototype;
    var pk = proto.$$db.options.pk ? proto.$$db.options.pk : 'id';

    proto.$$defaultValues = {};

    // Setup the id property
    Object.defineProperty(proto, 'id', {
        get: function(){ return this.$$values[pk]; },
        set: function(val)
        {
            // We only allow setting in the event that id is undefined.
            if(!this.$$values.id)
            {
                this.$dirty = true;
                this.$$values[pk] = val;
            } // end if
        }
    });

    // Setup the schema properties
    _.forIn(proto.$$schema, function(typeDef, key)
    {
        var type = typeDef.type || typeDef;

        Object.defineProperty(proto, key, {
            get: function()
            {
                switch(type)
                {
                    case Date:
                        var val = this.$$values[key];
                        return _.isDate(val) ? val : new Date(val);
                    default:
                        return this.$$values[key];
                } // end switch
            },
            set: function(newVal)
            {
                this.$dirty = true;
                switch(type)
                {
                    case Date:
                        var val = this.$$values[key];
                        this.$$values[key] =  _.isDate(val) ? val : new Date(val);
                        break;
                    default:
                        this.$$values[key] = newVal;
                        break;
                } // end switch
            }
        });

        // If we have a default configured, we set that now, and let it be overridden later.
        if(typeDef.default !== undefined)
        {
            proto.$$defaultValues[key] = _.cloneDeep(typeDef.default);
        } // end if
    });
} // end buildSchema

Model.prototype.save = function(skipValidation)
{
    var self = this;

    // First, we validate
    var validatePromise;
    if(skipValidation)
    {
        validatePromise = Promise.resolve();
    }
    else
    {
        validatePromise = this.validate();
    } // end if

    // Now, we handle saving
    return validatePromise.then(function()
    {
        if(self.$dirty)
        {
            var pk = self.$$db.options.pk ? self.$$db.options.pk : 'id';

            return self.$$db.store(self.id, self.$$values)
                .then(function(id)
                {
                    self.$$values[pk] = id;
                    self.$dirty = false;
                    return self;
                });
        }
        else
        {
            return Promise.resolve(self);
        } // end if
    });
}; // end save

Model.prototype.sync = function(force)
{
    var self = this;
    return this.$$db.get(this.id)
        .then(function(obj)
        {
            // Only update if we're not dirty, or are forcing a sync.
            if(obj && !self.$dirty || force)
            {
                self.$$values = obj;
            } // end if
        });
}; // end sync

Model.prototype.remove = function()
{
    var self = this;
    return this.$$db.remove(this.id)
        .then(function()
        {
            self.$$values = {};
        });
}; // end remove

Model.prototype.toJSON = function()
{
    return _.clone(this.$$values);
}; // end toJSON

Model.prototype.validate = function()
{
    var self = this;
    return new Promise(function(resolve, reject)
    {
        function fail(key, expectedType, message)
        {
            reject(new errors.ValidationError(key, expectedType, message));
        } // end fail

        _.forIn(self.$$schema, function(type, key)
        {
            var typeDef = {};

            // Support option objects instead of just plain types
            if(_.isPlainObject(type) && type.type)
            {
                typeDef = type;
                type = type.type;
            } // end if

            // TODO: Add support for complex types, like Arrays of a given type
            // TODO: May want to consider switching to value: https://www.npmjs.org/package/value

            // Pull put our value for easy access
            var val = self.$$values[key];

            // Validate the value
            if(val === null || val === undefined)
            {
                if(typeDef.required)
                {
                    return fail(key, type);
                } // end if
            }
            else
            {
                // Make sure we are of the specified type
                if(type === Date)
                {
                    if(isNaN(new Date(val).getTime()))
                    {
                        return fail(key, type);
                    } // end if
                }
                else if(!oftype(val, type, { primitiveObject: true }))
                {
                    return fail(key, type);
                } // end if

                // If we have a 'choices' property, make sure we are one of the allowed values
                if(_.isArray(typeDef.choices))
                {
                    // Ensure the value is an array, to simplify the logic
                    val = _.isArray(val) ? val : [val];

                    // Iterate over every value in the val array, and check it against our choices.
                    var failedVal;
                    _.each(val, function(value)
                    {
                        if(!_.contains(typeDef.choices, value))
                        {
                            failedVal = value;
                        } // end if
                    });

                    if(failedVal)
                    {
                        // This will only return the last invalid choice, as opposed to all of them. This could be
                        // refactored later, if that is decided to be worth it.
                        return fail(key, type,
                            "Key '" + key + "': Value '" + failedVal + "' is not a valid choice. Possible values: " + typeDef.choices);
                    } // end if
                } // end if
            } // end if
        });

        resolve();
    });
}; // end validate

//----------------------------------------------------------------------------------------------------------------------

function defineModel(dbName, schemaDef, dbOptions)
{
    var jbase = require('../jbase');
    var db = jbase.db(dbName, dbOptions);

    // Build a custom Model Instance
    function JDBModel(modelDef)
    {
        Model.call(this, modelDef);
    } // end JDBModel

    // Inherit from Model
    util.inherits(JDBModel, Model);

    JDBModel.prototype.$$db = db;
    JDBModel.prototype.$$schema = schemaDef;

    /**
     * The schema definition of the model.
     */
    JDBModel.schema = schemaDef;

    // Build the schema for this model.
    buildSchema(JDBModel);

    /**
     * Gets a single model instance, by id. If a document with that id is not found, it resolves to a
     * `DocumentNotFound`.
     *
     * @param {string} id - The id of the document to retrieve
     * @returns {Promise}
     * @static
     */
    JDBModel.get = function(id)
    {
        return db.get(id)
            .then(function(obj)
            {
                if(obj)
                {
                    var pk = dbOptions.pk ? dbOptions.pk : 'id';
                    var model = new JDBModel(obj);
                    model.$$values[pk] = id;
                    model.$dirty = false;

                    return model;
                }
                else
                {
                    return Promise.reject(new errors.DocumentNotFound(id));
                } // end if
            });
    }; // end get

    JDBModel.all = function()
    {
        return this.filter();
    }; // end all

    JDBModel.filter = function(filter)
    {
        return db.filter(filter)
            .then(function(filtered)
            {
                return _.reduce(filtered, function(models, value, id)
                {
                    var model = new JDBModel(value);
                    model.$$values.id = id;
                    model.$dirty = false;
                    models.push(model);

                    return models;
                }, []);
            });
    }; // end filter

    JDBModel.remove = function(filter)
    {
        return db.remove(filter);
    }; // end remove

    JDBModel.removeAll = function()
    {
        db.values = {};
        return db.sync();
    }; // end removeAll

    // Return the new model function
    return JDBModel;
} // end defineModel

//----------------------------------------------------------------------------------------------------------------------

module.exports = {
    Model: Model,
    defineModel: defineModel
};

//----------------------------------------------------------------------------------------------------------------------
<commit_msg>Switched to _.clone for default values<commit_after>//----------------------------------------------------------------------------------------------------------------------
// Implements a model based API for JBase.
//
// @module models.js
//----------------------------------------------------------------------------------------------------------------------

var util = require('util');

var _ = require('lodash');
var oftype = require('oftype');
var Promise = require('bluebird');

var errors = require('./errors');

//----------------------------------------------------------------------------------------------------------------------

function Model(modelDef)
{
    this.$$values = _.clone(this.$$defaultValues);

    // Dirty can be read by external things, but we prefix it with '$' to prevent property collision.
    this.$dirty = false;

    // Prevent any chance of accidentally overwriting the built in model properties
    var safeDef = _.omit(modelDef, ['$$db', '$$schema', '$$values', '$dirty', 'id', 'save', 'sync', 'toJSON', 'validate']);

    // Populate the model
    _.merge(this, safeDef);
} // end Model

function buildSchema(model)
{
    var proto = model.prototype;
    var pk = proto.$$db.options.pk ? proto.$$db.options.pk : 'id';

    proto.$$defaultValues = {};

    // Setup the id property
    Object.defineProperty(proto, 'id', {
        get: function(){ return this.$$values[pk]; },
        set: function(val)
        {
            // We only allow setting in the event that id is undefined.
            if(!this.$$values.id)
            {
                this.$dirty = true;
                this.$$values[pk] = val;
            } // end if
        }
    });

    // Setup the schema properties
    _.forIn(proto.$$schema, function(typeDef, key)
    {
        var type = typeDef.type || typeDef;

        Object.defineProperty(proto, key, {
            get: function()
            {
                switch(type)
                {
                    case Date:
                        var val = this.$$values[key];
                        return _.isDate(val) ? val : new Date(val);
                    default:
                        return this.$$values[key];
                } // end switch
            },
            set: function(newVal)
            {
                this.$dirty = true;
                switch(type)
                {
                    case Date:
                        var val = this.$$values[key];
                        this.$$values[key] =  _.isDate(val) ? val : new Date(val);
                        break;
                    default:
                        this.$$values[key] = newVal;
                        break;
                } // end switch
            }
        });

        // If we have a default configured, we set that now, and let it be overridden later.
        if(typeDef.default !== undefined)
        {
            proto.$$defaultValues[key] = _.cloneDeep(typeDef.default);
        } // end if
    });
} // end buildSchema

Model.prototype.save = function(skipValidation)
{
    var self = this;

    // First, we validate
    var validatePromise;
    if(skipValidation)
    {
        validatePromise = Promise.resolve();
    }
    else
    {
        validatePromise = this.validate();
    } // end if

    // Now, we handle saving
    return validatePromise.then(function()
    {
        if(self.$dirty)
        {
            var pk = self.$$db.options.pk ? self.$$db.options.pk : 'id';

            return self.$$db.store(self.id, self.$$values)
                .then(function(id)
                {
                    self.$$values[pk] = id;
                    self.$dirty = false;
                    return self;
                });
        }
        else
        {
            return Promise.resolve(self);
        } // end if
    });
}; // end save

Model.prototype.sync = function(force)
{
    var self = this;
    return this.$$db.get(this.id)
        .then(function(obj)
        {
            // Only update if we're not dirty, or are forcing a sync.
            if(obj && !self.$dirty || force)
            {
                self.$$values = obj;
            } // end if
        });
}; // end sync

Model.prototype.remove = function()
{
    var self = this;
    return this.$$db.remove(this.id)
        .then(function()
        {
            self.$$values = {};
        });
}; // end remove

Model.prototype.toJSON = function()
{
    return _.clone(this.$$values);
}; // end toJSON

Model.prototype.validate = function()
{
    var self = this;
    return new Promise(function(resolve, reject)
    {
        function fail(key, expectedType, message)
        {
            reject(new errors.ValidationError(key, expectedType, message));
        } // end fail

        _.forIn(self.$$schema, function(type, key)
        {
            var typeDef = {};

            // Support option objects instead of just plain types
            if(_.isPlainObject(type) && type.type)
            {
                typeDef = type;
                type = type.type;
            } // end if

            // TODO: Add support for complex types, like Arrays of a given type
            // TODO: May want to consider switching to value: https://www.npmjs.org/package/value

            // Pull put our value for easy access
            var val = self.$$values[key];

            // Validate the value
            if(val === null || val === undefined)
            {
                if(typeDef.required)
                {
                    return fail(key, type);
                } // end if
            }
            else
            {
                // Make sure we are of the specified type
                if(type === Date)
                {
                    if(isNaN(new Date(val).getTime()))
                    {
                        return fail(key, type);
                    } // end if
                }
                else if(!oftype(val, type, { primitiveObject: true }))
                {
                    return fail(key, type);
                } // end if

                // If we have a 'choices' property, make sure we are one of the allowed values
                if(_.isArray(typeDef.choices))
                {
                    // Ensure the value is an array, to simplify the logic
                    val = _.isArray(val) ? val : [val];

                    // Iterate over every value in the val array, and check it against our choices.
                    var failedVal;
                    _.each(val, function(value)
                    {
                        if(!_.contains(typeDef.choices, value))
                        {
                            failedVal = value;
                        } // end if
                    });

                    if(failedVal)
                    {
                        // This will only return the last invalid choice, as opposed to all of them. This could be
                        // refactored later, if that is decided to be worth it.
                        return fail(key, type,
                            "Key '" + key + "': Value '" + failedVal + "' is not a valid choice. Possible values: " + typeDef.choices);
                    } // end if
                } // end if
            } // end if
        });

        resolve();
    });
}; // end validate

//----------------------------------------------------------------------------------------------------------------------

function defineModel(dbName, schemaDef, dbOptions)
{
    var jbase = require('../jbase');
    var db = jbase.db(dbName, dbOptions);

    // Build a custom Model Instance
    function JDBModel(modelDef)
    {
        Model.call(this, modelDef);
    } // end JDBModel

    // Inherit from Model
    util.inherits(JDBModel, Model);

    JDBModel.prototype.$$db = db;
    JDBModel.prototype.$$schema = schemaDef;

    /**
     * The schema definition of the model.
     */
    JDBModel.schema = schemaDef;

    // Build the schema for this model.
    buildSchema(JDBModel);

    /**
     * Gets a single model instance, by id. If a document with that id is not found, it resolves to a
     * `DocumentNotFound`.
     *
     * @param {string} id - The id of the document to retrieve
     * @returns {Promise}
     * @static
     */
    JDBModel.get = function(id)
    {
        return db.get(id)
            .then(function(obj)
            {
                if(obj)
                {
                    var pk = dbOptions.pk ? dbOptions.pk : 'id';
                    var model = new JDBModel(obj);
                    model.$$values[pk] = id;
                    model.$dirty = false;

                    return model;
                }
                else
                {
                    return Promise.reject(new errors.DocumentNotFound(id));
                } // end if
            });
    }; // end get

    JDBModel.all = function()
    {
        return this.filter();
    }; // end all

    JDBModel.filter = function(filter)
    {
        return db.filter(filter)
            .then(function(filtered)
            {
                return _.reduce(filtered, function(models, value, id)
                {
                    var model = new JDBModel(value);
                    model.$$values.id = id;
                    model.$dirty = false;
                    models.push(model);

                    return models;
                }, []);
            });
    }; // end filter

    JDBModel.remove = function(filter)
    {
        return db.remove(filter);
    }; // end remove

    JDBModel.removeAll = function()
    {
        db.values = {};
        return db.sync();
    }; // end removeAll

    // Return the new model function
    return JDBModel;
} // end defineModel

//----------------------------------------------------------------------------------------------------------------------

module.exports = {
    Model: Model,
    defineModel: defineModel
};

//----------------------------------------------------------------------------------------------------------------------
<|endoftext|>


<commit_before>var rest = require('node-rest-client');

var init = function(router) {

	router.get('/monoprice/rss.xml', function(req, res) {

		var client = new rest.Client();

		client.get("http://www.monoprice.com/products/rssdailydeal/rss.asp", function(data, response){
			response.set('Content-Type', 'application/rss+xml');
			res.send(data.split('<script')[0]);
		 });

	});

	console.log('Ready to return fixed monoprice rss feeds at /monoprice/rss.xml');
}

module.exports.init = init;<commit_msg>Whoops, fixing Content-Type<commit_after>var rest = require('node-rest-client');

var init = function(router) {

	router.get('/monoprice/rss.xml', function(req, res) {

		var client = new rest.Client();

		client.get("http://www.monoprice.com/products/rssdailydeal/rss.asp", function(data, response){
			res.set('Content-Type', 'application/rss+xml');
			res.send(data.split('<script')[0]);
		 });

	});

	console.log('Ready to return fixed monoprice rss feeds at /monoprice/rss.xml');
}

module.exports.init = init;<|endoftext|>