<commit_before>'use strict'

let fs = require('fs')
let path = require('path')
let _ = require('lodash')
let assert = require('assert-plus')
let mkdirp = require('mkdirp')
let del = require('del')
let uuid = require('node-uuid')

let imponderous = require('./')

class Model {
  constructor (keyOrData) {
    let args = Array.prototype.slice.call(arguments)

    if (_.isString(keyOrData)) {
      this._key = keyOrData
      args.shift()
    }

    this.data = _.extend({}, this.defaults/*, ...args*/)

    // TODO: Use ES6 spread operator
    _.each(args, (obj) => {
      _.extend(this.data, obj)
    })

    // Setup getters/setters
    _.each(this.constructor.schema, (props, field) => {
      Object.defineProperty(this, field, {
        enumerable: true,
        configurable: true,
        get: function () {
          return this.get(field)
        },
        set: function (v) {
          return this.set(field, v)
        }
      })
    })
  }

  // ----- Document methods -----

  // return the document key
  get key () {
    if (_.isUndefined(this._key)) {
      let keyField = _.keys(this.schema).filter(field => {
        return this.schema[field].key
      })

      assert.ok(keyField.length === 0 || keyField.length === 1, 'Schema fields has multiple "key" properties')

      if (keyField.length > 0) {
        keyField = _.first(keyField)

        this._key = this.get(keyField)
      }

      if (_.isUndefined(this._key)) {
        // TODO: Consider using uuid.v4 instead
        this._key = uuid.v1()
      }
    }

    return this._key
  }

  // when the key is updated, we need to update the collection cache,
  // and reindex (remove old key) along with removing the old file
  set key (newKey) {
    if (this._key) {
      this._oldKey = this._key
    }

    this._key = newKey

    if (this.constructor.cache[this._oldKey]) {
      this.constructor.cache[this._key] = this
      delete this.constructor.cache[this._oldKey]
    }
  }

  get (field) {
    return this._magicGetterSetter(this.data, field)
  }

  set (field, value) {
    if (_.isString(field) || _.isArray(field)) {
      return this._magicGetterSetter(this.data, field, value)
    }

    // TODO: Use ES6 spread operator
    _.each(arguments, (obj) => {
      _.extend(this.data, obj)
    })
  }

  _magicGetterSetter (obj, is, value) {
    if (typeof is === 'string') {
      return this._magicGetterSetter(obj, is.split('.'), value)
    } else if (is.length === 1 && value !== undefined) {
      obj[is[0]] = value
      return value
    } else if (is.length === 0) {
      return obj
    } else {
      return this._magicGetterSetter(obj[is[0]], is.slice(1), value)
    }
  }

  rm (field) {
    function reduce (obj, is) {
      if (is.length === 1) {
        try {
          delete obj[is[0]]
        } catch (ex) {
          // this means it didn't exist to begin with
        }
      } else if (is.length === 0) {
        return obj
      } else {
        return reduce(obj[is[0]], is.slice(1))
      }
    }

    // covert all fields requested to be deleted to path ararys
    let fields = _.map(arguments, (field) => {
      if (typeof field === 'string') {
        return field.split('.')
      } else {
        return field
      }
    })

    // do the deleting
    _.each(fields, (field) => {
      reduce(this.data, field)
    })

    return this
  }

  isValid (silent) {
    // ensure all requires fields are present
    let required = this.constructor.required
    assert.arrayOfString(required, 'Model.required')
    let missing = _.filter(required, (field) => {
      return _.isUndefined(this.get(field))
    })

    if (missing.length > 0) {
      if (!silent) {
        throw new Error('Missing required fields of: ' + missing.join(', '))
      }

      return false
    }

    // check values of properties
    // TODO

    return true
  }

  toJSON () {
    return this.data
  }

  toObject () {
    return this.data
  }

  toString () {
    return imponderous.formatter.toString(this.data)
  }

  get indexes () {
    return _.pick(this.data, this.constructor.indexed)
  }

  // return location of this document
  get file () {
    assert.string(this.key)
    assert.string(imponderous.formatter.ext)
    let filename = this.key + imponderous.formatter.ext
    let file = path.join(this.constructor.root, filename)
    return file
  }

  // save document to disk
  save (data) {
    if (_.isObject(data)) {
      this.set(data)
    }

    return new Promise((resolve, reject) => {
      if (!this.isValid()) {
        reject(new errors.ValidationError())
      }

      if (_.isFunction(this.onPreSave)) {
        let preSave = this.onPreSave()

        if (preSave === false) {
          reject(new errors.PreSaveFailError())
        } else if (preSave instanceof Promise) {
          // preSave.
          // TODO
        }
      }

      this.forceSave().then(resolve, reject)
    })
  }

  // save regardless of validation concerns, risky!!!
  forceSave () {
    if (arguments.length > 0) {
      this.set.apply(this, arguments)
    }

    let contents = imponderous.formatter.toString(this.data)

    return new Promise((resolve, reject) => {
      // ensure the colleciton directory exists
      mkdirp(this.constructor.root, (err) => {
        if (err) {
          reject(err)
          return
        }

        // delete old document if necessary
        if (this._oldKey) {
          let oldFile = path.join(this.constructor.root, this._oldKey + imponderous.formatter.ext)
          fs.unlinkSync(oldFile)
          // TODO: Make this use async
        }

        // write the document file
        fs.writeFile(this.file, contents, 'utf8', (err) => {
          if (err) {
            return reject(err)
          }

          // update the index
          if (this.constructor.indexed.length > 0) {
            this.constructor.reindex().then(() => {
              resolve(this)
            }, reject)
          } else {
            resolve(this)
          }
        })
      })
    })
  }

  // destroy a document, deleting it from disk
  destroy () {
    return new Promise((resolve, reject) => {
      fs.unlink(this.file, function (err) {
        if (err) {
          reject(err)
        } else {
          resolve()
        }
      })
    })
  }

  // load document's data from disk
  _load () {
    return new Promise((resolve, reject) => {
      fs.readFile(this.file, 'utf8', (err, contents) => {
        if (err) {
          if (err.code === 'ENOENT' && err.errno === -2) {
            return reject(new errors.DoesNotExistError())
          }

          return reject(err)
        }

        try {
          this.data = imponderous.formatter.toObject(contents)
        } catch (ex) {
          reject(ex)
        } finally {
          if (!_.isUndefined(this.constructor.cache)) {
            this.constructor.cache[this.key] = this
          }

          resolve(this)
        }
      })
    })
  }

  // ----- Collection methods -----

  // retrieve a document by its key
  static get (key) {
    if (!this.cache) {
      this.cache = {}
    }

    // look in cache, if doesn't exist, load document
    if (_.isUndefined(this.cache[key])) {
      let doc = new this(key)
      return doc._load()
    }

    // we still need to return a promise to keep the API consistent
    return new Promise((resolve, reject) => {
      resolve(this.cache[key])
    })
  }

  // static destroy (key) {}

  // delete an entire collection
  static destroyAll (collectionName) {
    return del(path.join(this.root, '**'))
  }

  // find documents matching search
  static find (query, options) {
    assert.object(query, 'query')
    assert.ok(_.keys(query).length > 0, 'query')
    options = options || {}

    return new Promise((resolve, reject) => {
      this._loadIndex().catch(reject).then((index) => {
        index = _.clone(index)

        if (_.keys(index).length === 0) {
          return resolve(options.limit === 1 ? null : [])
        }

        let items = {}

        _.each(index, (fields, key) => {
          let matches = _.every(query, (value, field) => {
            return fields[field] === value
          })

          if (matches) {
            items[key] = fields
          }
        })

        if (options.limit === 1) {
          items = items.slice(0, 1)
        }

        let docs = _.map(items, (fields, key) => {
          return this.get(key, false)
        })

        resolve(docs)
      })
    })
  }

  static findOne (params) {
    return this.find(params, { limit: 1 })
  }

  static all (loadAll) {
    return new Promise((resolve, reject) => {
      fs.readdir(this.root, (err, files) => {
        if (err) {
          return reject(err)
        }

        files = _.without(files, 'index.json')

        let docs = {}

        let promises = _.map(files, (filename) => {
          let key = path.basename(filename, imponderous.formatter.ext)
          assert.notEqual(key, filename)
          let doc = new this(key)
          docs[key] = doc
          if (loadAll) {
            return doc._load()
          }
        })

        if (loadAll) {
          Promise.all(promises).then(() => {
            resolve(docs)
          })
        } else {
          resolve(docs)
        }
      })
    })
  }

  // return the pluralized name for the collection
  static get collectionName () {
    return this.name.toLowerCase().trim() + 's'
  }

  // return the directory for this collection
  static get root () {
    return path.join(imponderous.root, this.collectionName)
  }

  // return the absolute path to the index file
  static get indexFile () {
    return path.join(this.root, 'index.json')
  }

  // return the default values for model, based on schema
  static get defaults () {
    return _.mapValues(this.schema, 'default')
  }

  // return the list of fields that are required, based on schema
  static get required () {
    return _.keys(this.schema).filter(field => {
      return this.schema[field].required
    })
  }

  // return the list of fields that should be indexed, based on schema
  static get indexed () {
    return _.keys(this.schema).filter(field => {
      return this.schema[field].index
    })
  }

  static reindex (useCache) {
    useCache = useCache !== false

    // using the cache assumes that the index file has some content,
    // and that it is still accurate, so we only need to update it
    // with the pending changes we know of
    if (useCache && this.cache) {
      // this.cache
      // TODO
    }

    this.all(true).then((docs) => {
      let data = _.mapValues(docs, 'indexes')
      data = imponderous.formatter.toString(data)

      fs.writeFile(this.indexFile, data, 'utf8', function (err) {
        if (err) {
          console.error(err)
        } else {
          console.log('Write index')
        }
      })
    })
  }

  static _loadIndex () {
    return new Promise((resolve, reject) => {
      fs.readFile(this.indexFile, 'utf8', (err, contents) => {
        if (err) {
          // If file does not exist, then no index has been created,
          // that's not necessarily an error
          if (err.code === 'ENOENT' && err.errno === -2) {
            resolve({})
          }

          reject(err)
          return
        }

        try {
          let data = imponderous.formatter.toObject(contents)
          resolve(data)
        } catch (ex) {
          reject(new Error('Invalid JSON formatting for collection index'))
        }
      })
    })
  }
}

module.exports = Model
<commit_msg>Removed use of unused argument, collectionName, from destroyAll method.<commit_after>'use strict'

let fs = require('fs')
let path = require('path')
let _ = require('lodash')
let assert = require('assert-plus')
let mkdirp = require('mkdirp')
let del = require('del')
let uuid = require('node-uuid')

let imponderous = require('./')

class Model {
  constructor (keyOrData) {
    let args = Array.prototype.slice.call(arguments)

    if (_.isString(keyOrData)) {
      this._key = keyOrData
      args.shift()
    }

    this.data = _.extend({}, this.defaults/*, ...args*/)

    // TODO: Use ES6 spread operator
    _.each(args, (obj) => {
      _.extend(this.data, obj)
    })

    // Setup getters/setters
    _.each(this.constructor.schema, (props, field) => {
      Object.defineProperty(this, field, {
        enumerable: true,
        configurable: true,
        get: function () {
          return this.get(field)
        },
        set: function (v) {
          return this.set(field, v)
        }
      })
    })
  }

  // ----- Document methods -----

  // return the document key
  get key () {
    if (_.isUndefined(this._key)) {
      let keyField = _.keys(this.schema).filter(field => {
        return this.schema[field].key
      })

      assert.ok(keyField.length === 0 || keyField.length === 1, 'Schema fields has multiple "key" properties')

      if (keyField.length > 0) {
        keyField = _.first(keyField)

        this._key = this.get(keyField)
      }

      if (_.isUndefined(this._key)) {
        // TODO: Consider using uuid.v4 instead
        this._key = uuid.v1()
      }
    }

    return this._key
  }

  // when the key is updated, we need to update the collection cache,
  // and reindex (remove old key) along with removing the old file
  set key (newKey) {
    if (this._key) {
      this._oldKey = this._key
    }

    this._key = newKey

    if (this.constructor.cache[this._oldKey]) {
      this.constructor.cache[this._key] = this
      delete this.constructor.cache[this._oldKey]
    }
  }

  get (field) {
    return this._magicGetterSetter(this.data, field)
  }

  set (field, value) {
    if (_.isString(field) || _.isArray(field)) {
      return this._magicGetterSetter(this.data, field, value)
    }

    // TODO: Use ES6 spread operator
    _.each(arguments, (obj) => {
      _.extend(this.data, obj)
    })
  }

  _magicGetterSetter (obj, is, value) {
    if (typeof is === 'string') {
      return this._magicGetterSetter(obj, is.split('.'), value)
    } else if (is.length === 1 && value !== undefined) {
      obj[is[0]] = value
      return value
    } else if (is.length === 0) {
      return obj
    } else {
      return this._magicGetterSetter(obj[is[0]], is.slice(1), value)
    }
  }

  rm (field) {
    function reduce (obj, is) {
      if (is.length === 1) {
        try {
          delete obj[is[0]]
        } catch (ex) {
          // this means it didn't exist to begin with
        }
      } else if (is.length === 0) {
        return obj
      } else {
        return reduce(obj[is[0]], is.slice(1))
      }
    }

    // covert all fields requested to be deleted to path ararys
    let fields = _.map(arguments, (field) => {
      if (typeof field === 'string') {
        return field.split('.')
      } else {
        return field
      }
    })

    // do the deleting
    _.each(fields, (field) => {
      reduce(this.data, field)
    })

    return this
  }

  isValid (silent) {
    // ensure all requires fields are present
    let required = this.constructor.required
    assert.arrayOfString(required, 'Model.required')
    let missing = _.filter(required, (field) => {
      return _.isUndefined(this.get(field))
    })

    if (missing.length > 0) {
      if (!silent) {
        throw new Error('Missing required fields of: ' + missing.join(', '))
      }

      return false
    }

    // check values of properties
    // TODO

    return true
  }

  toJSON () {
    return this.data
  }

  toObject () {
    return this.data
  }

  toString () {
    return imponderous.formatter.toString(this.data)
  }

  get indexes () {
    return _.pick(this.data, this.constructor.indexed)
  }

  // return location of this document
  get file () {
    assert.string(this.key)
    assert.string(imponderous.formatter.ext)
    let filename = this.key + imponderous.formatter.ext
    let file = path.join(this.constructor.root, filename)
    return file
  }

  // save document to disk
  save (data) {
    if (_.isObject(data)) {
      this.set(data)
    }

    return new Promise((resolve, reject) => {
      if (!this.isValid()) {
        reject(new errors.ValidationError())
      }

      if (_.isFunction(this.onPreSave)) {
        let preSave = this.onPreSave()

        if (preSave === false) {
          reject(new errors.PreSaveFailError())
        } else if (preSave instanceof Promise) {
          // preSave.
          // TODO
        }
      }

      this.forceSave().then(resolve, reject)
    })
  }

  // save regardless of validation concerns, risky!!!
  forceSave () {
    if (arguments.length > 0) {
      this.set.apply(this, arguments)
    }

    let contents = imponderous.formatter.toString(this.data)

    return new Promise((resolve, reject) => {
      // ensure the colleciton directory exists
      mkdirp(this.constructor.root, (err) => {
        if (err) {
          reject(err)
          return
        }

        // delete old document if necessary
        if (this._oldKey) {
          let oldFile = path.join(this.constructor.root, this._oldKey + imponderous.formatter.ext)
          fs.unlinkSync(oldFile)
          // TODO: Make this use async
        }

        // write the document file
        fs.writeFile(this.file, contents, 'utf8', (err) => {
          if (err) {
            return reject(err)
          }

          // update the index
          if (this.constructor.indexed.length > 0) {
            this.constructor.reindex().then(() => {
              resolve(this)
            }, reject)
          } else {
            resolve(this)
          }
        })
      })
    })
  }

  // destroy a document, deleting it from disk
  destroy () {
    return new Promise((resolve, reject) => {
      fs.unlink(this.file, function (err) {
        if (err) {
          reject(err)
        } else {
          resolve()
        }
      })
    })
  }

  // load document's data from disk
  _load () {
    return new Promise((resolve, reject) => {
      fs.readFile(this.file, 'utf8', (err, contents) => {
        if (err) {
          if (err.code === 'ENOENT' && err.errno === -2) {
            return reject(new errors.DoesNotExistError())
          }

          return reject(err)
        }

        try {
          this.data = imponderous.formatter.toObject(contents)
        } catch (ex) {
          reject(ex)
        } finally {
          if (!_.isUndefined(this.constructor.cache)) {
            this.constructor.cache[this.key] = this
          }

          resolve(this)
        }
      })
    })
  }

  // ----- Collection methods -----

  // retrieve a document by its key
  static get (key) {
    if (!this.cache) {
      this.cache = {}
    }

    // look in cache, if doesn't exist, load document
    if (_.isUndefined(this.cache[key])) {
      let doc = new this(key)
      return doc._load()
    }

    // we still need to return a promise to keep the API consistent
    return new Promise((resolve, reject) => {
      resolve(this.cache[key])
    })
  }

  // static destroy (key) {}

  // delete an entire collection
  static destroyAll () {
    return del(path.join(this.root, '**'))
  }

  // find documents matching search
  static find (query, options) {
    assert.object(query, 'query')
    assert.ok(_.keys(query).length > 0, 'query')
    options = options || {}

    return new Promise((resolve, reject) => {
      this._loadIndex().catch(reject).then((index) => {
        index = _.clone(index)

        if (_.keys(index).length === 0) {
          return resolve(options.limit === 1 ? null : [])
        }

        let items = {}

        _.each(index, (fields, key) => {
          let matches = _.every(query, (value, field) => {
            return fields[field] === value
          })

          if (matches) {
            items[key] = fields
          }
        })

        if (options.limit === 1) {
          items = items.slice(0, 1)
        }

        let docs = _.map(items, (fields, key) => {
          return this.get(key, false)
        })

        resolve(docs)
      })
    })
  }

  static findOne (params) {
    return this.find(params, { limit: 1 })
  }

  static all (loadAll) {
    return new Promise((resolve, reject) => {
      fs.readdir(this.root, (err, files) => {
        if (err) {
          return reject(err)
        }

        files = _.without(files, 'index.json')

        let docs = {}

        let promises = _.map(files, (filename) => {
          let key = path.basename(filename, imponderous.formatter.ext)
          assert.notEqual(key, filename)
          let doc = new this(key)
          docs[key] = doc
          if (loadAll) {
            return doc._load()
          }
        })

        if (loadAll) {
          Promise.all(promises).then(() => {
            resolve(docs)
          })
        } else {
          resolve(docs)
        }
      })
    })
  }

  // return the pluralized name for the collection
  static get collectionName () {
    return this.name.toLowerCase().trim() + 's'
  }

  // return the directory for this collection
  static get root () {
    return path.join(imponderous.root, this.collectionName)
  }

  // return the absolute path to the index file
  static get indexFile () {
    return path.join(this.root, 'index.json')
  }

  // return the default values for model, based on schema
  static get defaults () {
    return _.mapValues(this.schema, 'default')
  }

  // return the list of fields that are required, based on schema
  static get required () {
    return _.keys(this.schema).filter(field => {
      return this.schema[field].required
    })
  }

  // return the list of fields that should be indexed, based on schema
  static get indexed () {
    return _.keys(this.schema).filter(field => {
      return this.schema[field].index
    })
  }

  static reindex (useCache) {
    useCache = useCache !== false

    // using the cache assumes that the index file has some content,
    // and that it is still accurate, so we only need to update it
    // with the pending changes we know of
    if (useCache && this.cache) {
      // this.cache
      // TODO
    }

    this.all(true).then((docs) => {
      let data = _.mapValues(docs, 'indexes')
      data = imponderous.formatter.toString(data)

      fs.writeFile(this.indexFile, data, 'utf8', function (err) {
        if (err) {
          console.error(err)
        } else {
          console.log('Write index')
        }
      })
    })
  }

  static _loadIndex () {
    return new Promise((resolve, reject) => {
      fs.readFile(this.indexFile, 'utf8', (err, contents) => {
        if (err) {
          // If file does not exist, then no index has been created,
          // that's not necessarily an error
          if (err.code === 'ENOENT' && err.errno === -2) {
            resolve({})
          }

          reject(err)
          return
        }

        try {
          let data = imponderous.formatter.toObject(contents)
          resolve(data)
        } catch (ex) {
          reject(new Error('Invalid JSON formatting for collection index'))
        }
      })
    })
  }
}

module.exports = Model
<|endoftext|>


<commit_before>'use strict'
const sourceMapSupport = require('source-map-support')
const StackUtils = require('stack-utils')
const path = require('path')
const tapDir = path.resolve(__dirname, '..')
const osHomedir = require('os-homedir')

const resc = str =>
  str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')

// Ignore tap if it's a dependency, or anything
// in this lib folder.
// don't skip when developing on tap itself
const skip = (process.cwd() !== tapDir ||
  +process.env.TAP_DEV_SHORTSTACK === 1) &&
  +process.env.TAP_DEV_LONGSTACK !== 1
? [
    /node_modules[\/\\]tap[\/\\]/,
    new RegExp(resc(path.resolve(osHomedir(), '.node-spawn-wrap-')) + '.*'),
    new RegExp(resc(tapDir) + '\\b', 'i')
  ].concat(/* istanbul ignore next */ require.resolve
    ? [
        new RegExp(resc(require.resolve('function-loop'))),
        new RegExp(resc(require.resolve('esm'))),
      ]
    : [])
: +process.env.TAP_DEV_LONGSTACK !== 1 ? [
    new RegExp(resc(require.resolve('esm'))),
  ]
: []

sourceMapSupport.install({environment:'node'})

let nodeInternals = []
try {
  nodeInternals = StackUtils.nodeInternals()
} catch (error) {
  // Do nothing.
}

module.exports = new StackUtils({
  internals: nodeInternals.concat(skip),
  wrapCallSite: sourceMapSupport.wrapCallSite
})
<commit_msg>Filter nyc out of stack prints<commit_after>'use strict'
const sourceMapSupport = require('source-map-support')
const StackUtils = require('stack-utils')
const path = require('path')
const tapDir = path.resolve(__dirname, '..')
const osHomedir = require('os-homedir')

const resc = str =>
  str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&')

// Ignore tap if it's a dependency, or anything
// in this lib folder.
// don't skip when developing on tap itself
const skip = (process.cwd() !== tapDir ||
  +process.env.TAP_DEV_SHORTSTACK === 1) &&
  +process.env.TAP_DEV_LONGSTACK !== 1
? [
    /node_modules[\/\\]tap[\/\\]/,
    new RegExp(resc(path.resolve(osHomedir(), '.node-spawn-wrap-')) + '.*'),
    new RegExp(resc(tapDir) + '\\b', 'i')
  ].concat(/* istanbul ignore next */ require.resolve
    ? [
        new RegExp(resc(require.resolve('function-loop'))),
        new RegExp(resc(require.resolve('esm'))),
        new RegExp(resc(require.resolve('nyc').replace(/(node_modules[\/\\]nyc).*$/, '$1'))),
      ]
    : [])
: +process.env.TAP_DEV_LONGSTACK !== 1 ? [
    new RegExp(resc(require.resolve('esm'))),
    new RegExp(resc(require.resolve('nyc').replace(/(node_modules[\/\\]nyc).*$/, '$1'))),
  ]
: []

sourceMapSupport.install({environment:'node'})

let nodeInternals = []
try {
  nodeInternals = StackUtils.nodeInternals()
} catch (error) {
  // Do nothing.
}

module.exports = new StackUtils({
  internals: nodeInternals.concat(skip),
  wrapCallSite: sourceMapSupport.wrapCallSite
})
<|endoftext|>


<commit_before>'use strict';
/**
 * Created by vedi on 01/12/14.
 */

const _ = require('lodash');

const ACTIONS = {
  SELECT: 'select',
  SELECT_ONE: 'selectOne',
  INSERT: 'insert',
  REPLACE: 'replace',
  UPDATE: 'update',
  DELETE: 'delete',
  COUNT: 'count'
};

class RestifizerScope {

  constructor(action, transport, contextFactory) {
    this.action = action;
    if (contextFactory) {
      this.context = contextFactory();
    } else {
      this.context = {};
    }
  }

  getActionName() {
    return this.action.name;
  }

  checkActionName() {
    for (let i = 0, j = arguments.length; i < j; i++) {
      if (this.action.name === arguments[i]) {
        return true;
      }
    }

    return false;
  }

  isSelect() {
    return this.checkActionName(ACTIONS.SELECT, ACTIONS.SELECT_ONE, ACTIONS.COUNT);
  }

  isChanging() {
    return this.isInsert() || this.isUpdate() || this.isDelete();
  }

  isInsert() {
    return this.checkActionName(ACTIONS.INSERT);
  }

  isUpdate() {
    return this.checkActionName(ACTIONS.UPDATE, ACTIONS.REPLACE);
  }

  isDelete() {
    return this.checkActionName(ACTIONS.DELETE);
  }

  isSelectOne() {
    return this.checkActionName(ACTIONS.SELECT_ONE);
  }

  isReplace() {
    return this.checkActionName(ACTIONS.REPLACE);
  }

  isCount() {
    return this.checkActionName(ACTIONS.COUNT);
  }

  getTransportData() {
    return transportData;
  }

  getQ() {
    return this.transport.getQ(this);
  }

  getBody() {
    return this.transport.getBody(this);
  }

  getParams() {
    return this.transport.getParams(this);
  }

  getFields() {
    return this.transport.getFields(this);
  }

  getFilter() {
    return this.transport.getFilter(this);
  }

  getOrderBy() {
    return this.transport.getOrderBy(this);
  }

  getPagination() {
    return this.transport.getPagination(this);
  }

  getUser() {
    return this.transport.getUser(this);
  }
}

RestifizerScope.ACTIONS = ACTIONS;

module.exports = RestifizerScope;
<commit_msg>remove getUser from scope<commit_after>'use strict';
/**
 * Created by vedi on 01/12/14.
 */

const _ = require('lodash');

const ACTIONS = {
  SELECT: 'select',
  SELECT_ONE: 'selectOne',
  INSERT: 'insert',
  REPLACE: 'replace',
  UPDATE: 'update',
  DELETE: 'delete',
  COUNT: 'count'
};

class RestifizerScope {

  constructor(action, transport, contextFactory) {
    this.action = action;
    if (contextFactory) {
      this.context = contextFactory();
    } else {
      this.context = {};
    }
  }

  getActionName() {
    return this.action.name;
  }

  checkActionName() {
    for (let i = 0, j = arguments.length; i < j; i++) {
      if (this.action.name === arguments[i]) {
        return true;
      }
    }

    return false;
  }

  isSelect() {
    return this.checkActionName(ACTIONS.SELECT, ACTIONS.SELECT_ONE, ACTIONS.COUNT);
  }

  isChanging() {
    return this.isInsert() || this.isUpdate() || this.isDelete();
  }

  isInsert() {
    return this.checkActionName(ACTIONS.INSERT);
  }

  isUpdate() {
    return this.checkActionName(ACTIONS.UPDATE, ACTIONS.REPLACE);
  }

  isDelete() {
    return this.checkActionName(ACTIONS.DELETE);
  }

  isSelectOne() {
    return this.checkActionName(ACTIONS.SELECT_ONE);
  }

  isReplace() {
    return this.checkActionName(ACTIONS.REPLACE);
  }

  isCount() {
    return this.checkActionName(ACTIONS.COUNT);
  }

  getTransportData() {
    return transportData;
  }

  getQ() {
    return this.transport.getQ(this);
  }

  getBody() {
    return this.transport.getBody(this);
  }

  getParams() {
    return this.transport.getParams(this);
  }

  getFields() {
    return this.transport.getFields(this);
  }

  getFilter() {
    return this.transport.getFilter(this);
  }

  getOrderBy() {
    return this.transport.getOrderBy(this);
  }

  getPagination() {
    return this.transport.getPagination(this);
  }

}

RestifizerScope.ACTIONS = ACTIONS;

module.exports = RestifizerScope;
<|endoftext|>


<commit_before>var querystring = require('querystring'),
    http = require('follow-redirects').http,
    fs = require('fs'),
    p = require('path'),
    url = require('url');

var Modem = function(opts) {
  this.socketPath = opts.socketPath;
  this.host = opts.host;
  this.port = opts.port;
};

Modem.prototype.dial = function(options, callback) {
  var opts, address, data;
  var self = this;

  if (options.options) {
    opts = options.options;
  }

  if(this.host) {
    address = url.resolve(this.host, options.path);
  } else {
    address = options.path;
  }

  if(options.path.indexOf('?') !== -1) {
    if (opts && Object.keys(opts).length > 0) {
      address += querystring.stringify(opts);
    } else {
      address = address.substring(0, address.length - 1);
    }
  }

  var optionsf = {
    path: address,
    method: options.method
  };

  optionsf.headers = {};
  if(options.file) {
    data = fs.readFileSync(p.resolve(options.file));
    optionsf.headers['Content-Type'] = 'application/tar';
  } else if(opts && options.method === 'POST') {
    data = JSON.stringify(opts);
    optionsf.headers['Content-Type'] = 'application/json';
  }

  if(data) {
    optionsf.headers['Content-Length'] = data.length;
  }

  if(this.socketPath) {
    optionsf.socketPath = this.socketPath;
  } else {
    optionsf.hostname = url.parse(options.path).hostname;
    optionsf.port = url.parse(options.path).port;
  }

  //console.log(optionsf);

  var req = http.request(optionsf, function() {});

  req.on('response', function(res) {
    if (options.isStream) {
      res.setEncoding('utf8');
      self.buildPayload(null, options.isStream, options.statusCodes, res, null, callback);
    } else {
      var chunks = '';
      res.on('data', function(chunk) {
        chunks += chunk;
      });

      res.on('end', function() {
        var json;
        try {
          json = JSON.parse(chunks);
        } catch(e) {
          json = chunks.split(/\n/);
        }
        self.buildPayload(null, options.isStream, options.statusCodes, res, json, callback);
      });
    }
  });

  req.on('error', function(error) {
    self.buildPayload(error, options.isStream, options.statusCodes, {}, null, callback);
  });

  if(data) {
    req.write(data);
  }
  req.end();
};

Modem.prototype.buildPayload = function(err, isStream, statusCodes, res, json, cb) {
  if (err) cb(err, null);

  if (typeof json === 'string' && !json.isJson()) {
    json = '{"msg": "' + json.replace(/[^a-zA-Z ]/g, "") + '"}';
  } else if (typeof json === 'string') {
    json = JSON.parse(json);
  }

  //console.log(json);

  if (statusCodes[res.statusCode] !== true) {
    var msg = 'HTTP response code is ' + res.statusCode + ' which indicates an error: ' + statusCodes[res.statusCode];
    cb(msg, json);
  } else {
    if (isStream) {
      cb(null, res);
    } else {
      cb(null, json);
    }
  }
};

module.exports = Modem;
<commit_msg>tcp fix<commit_after>var querystring = require('querystring'),
    http = require('follow-redirects').http,
    fs = require('fs'),
    p = require('path'),
    url = require('url');

var Modem = function(opts) {
  this.socketPath = opts.socketPath;
  this.host = opts.host;
  this.port = opts.port;
};

Modem.prototype.dial = function(options, callback) {
  var opts, address, data;
  var self = this;

  if (options.options) {
    opts = options.options;
  }

  if(this.host) {
    address = url.resolve(this.host + ':' + this.port, options.path);
  } else {
    address = options.path;
  }

  if(options.path.indexOf('?') !== -1) {
    if (opts && Object.keys(opts).length > 0) {
      address += querystring.stringify(opts);
    } else {
      address = address.substring(0, address.length - 1);
    }
  }

  var optionsf = {
    path: address,
    method: options.method
  };

  optionsf.headers = {};
  if(options.file) {
    data = fs.readFileSync(p.resolve(options.file));
    optionsf.headers['Content-Type'] = 'application/tar';
  } else if(opts && options.method === 'POST') {
    data = JSON.stringify(opts);
    optionsf.headers['Content-Type'] = 'application/json';
  }

  if(data) {
    optionsf.headers['Content-Length'] = data.length;
  }

  if(this.socketPath) {
    optionsf.socketPath = this.socketPath;
  } else {
    optionsf.hostname = url.parse(address).hostname;
    optionsf.port = url.parse(address).port;
  }

  //console.log(optionsf);

  var req = http.request(optionsf, function() {});

  req.on('response', function(res) {
    if (options.isStream) {
      res.setEncoding('utf8');
      self.buildPayload(null, options.isStream, options.statusCodes, res, null, callback);
    } else {
      var chunks = '';
      res.on('data', function(chunk) {
        chunks += chunk;
      });

      res.on('end', function() {
        var json;
        try {
          json = JSON.parse(chunks);
        } catch(e) {
          json = chunks.split(/\n/);
        }
        self.buildPayload(null, options.isStream, options.statusCodes, res, json, callback);
      });
    }
  });

  req.on('error', function(error) {
    self.buildPayload(error, options.isStream, options.statusCodes, {}, null, callback);
  });

  if(data) {
    req.write(data);
  }
  req.end();
};

Modem.prototype.buildPayload = function(err, isStream, statusCodes, res, json, cb) {
  if (err) cb(err, null);

  if (typeof json === 'string' && !json.isJson()) {
    json = '{"msg": "' + json.replace(/[^a-zA-Z ]/g, "") + '"}';
  } else if (typeof json === 'string') {
    json = JSON.parse(json);
  }

  //console.log(json);

  if (statusCodes[res.statusCode] !== true) {
    var msg = 'HTTP response code is ' + res.statusCode + ' which indicates an error: ' + statusCodes[res.statusCode];
    cb(msg, json);
  } else {
    if (isStream) {
      cb(null, res);
    } else {
      cb(null, json);
    }
  }
};

module.exports = Modem;
<|endoftext|>


<commit_before>var types = require("./types");
var Type = types.Type;
var namedTypes = types.namedTypes;
var Node = namedTypes.Node;
var Expression = namedTypes.Expression;
var isArray = types.builtInTypes.array;
var hasOwn = Object.prototype.hasOwnProperty;
var b = types.builders;

function Scope(path, parentScope) {
    if (!(this instanceof Scope)) {
        throw new Error("Scope constructor cannot be invoked without 'new'");
    }
    if (!(path instanceof require("./node-path"))) {
        throw new Error("");
    }
    ScopeType.assert(path.value);

    var depth;

    if (parentScope) {
        if (!(parentScope instanceof Scope)) {
            throw new Error("");
        }
        depth = parentScope.depth + 1;
    } else {
        parentScope = null;
        depth = 0;
    }

    Object.defineProperties(this, {
        path: { value: path },
        node: { value: path.value },
        isGlobal: { value: !parentScope, enumerable: true },
        depth: { value: depth },
        parent: { value: parentScope },
        bindings: { value: {} },
        types: { value: {} },
    });
}

var scopeTypes = [
    // Program nodes introduce global scopes.
    namedTypes.Program,

    // Function is the supertype of FunctionExpression,
    // FunctionDeclaration, ArrowExpression, etc.
    namedTypes.Function,

    // In case you didn't know, the caught parameter shadows any variable
    // of the same name in an outer scope.
    namedTypes.CatchClause
];

var ScopeType = Type.or.apply(Type, scopeTypes);

Scope.isEstablishedBy = function(node) {
    return ScopeType.check(node);
};

var Sp = Scope.prototype;

// Will be overridden after an instance lazily calls scanScope.
Sp.didScan = false;

Sp.declares = function(name) {
    this.scan();
    return hasOwn.call(this.bindings, name);
};

Sp.declaresType = function(name) {
    this.scan();
    return hasOwn.call(this.types, name);
};

Sp.declareTemporary = function(prefix) {
    if (prefix) {
        if (!/^[a-z$_]/i.test(prefix)) {
            throw new Error("");
        }
    } else {
        prefix = "t$";
    }

    // Include this.depth in the name to make sure the name does not
    // collide with any variables in nested/enclosing scopes.
    prefix += this.depth.toString(36) + "$";

    this.scan();

    var index = 0;
    while (this.declares(prefix + index)) {
        ++index;
    }

    var name = prefix + index;
    return this.bindings[name] = types.builders.identifier(name);
};

Sp.injectTemporary = function(identifier, init) {
    identifier || (identifier = this.declareTemporary());

    var bodyPath = this.path.get("body");
    if (namedTypes.BlockStatement.check(bodyPath.value)) {
        bodyPath = bodyPath.get("body");
    }

    bodyPath.unshift(
        b.variableDeclaration(
            "var",
            [b.variableDeclarator(identifier, init || null)]
        )
    );

    return identifier;
};

Sp.scan = function(force) {
    if (force || !this.didScan) {
        for (var name in this.bindings) {
            // Empty out this.bindings, just in cases.
            delete this.bindings[name];
        }
        scanScope(this.path, this.bindings, this.types);
        this.didScan = true;
    }
};

Sp.getBindings = function () {
    this.scan();
    return this.bindings;
};

Sp.getTypes = function () {
    this.scan();
    return this.types;
};

function scanScope(path, bindings, scopeTypes) {
    var node = path.value;
    ScopeType.assert(node);

    if (namedTypes.CatchClause.check(node)) {
        // A catch clause establishes a new scope but the only variable
        // bound in that scope is the catch parameter. Any other
        // declarations create bindings in the outer scope.
        addPattern(path.get("param"), bindings);

    } else {
        recursiveScanScope(path, bindings, scopeTypes);
    }
}

function recursiveScanScope(path, bindings, scopeTypes) {
    var node = path.value;

    if (path.parent &&
        namedTypes.FunctionExpression.check(path.parent.node) &&
        path.parent.node.id) {
        addPattern(path.parent.get("id"), bindings);
    }

    if (!node) {
        // None of the remaining cases matter if node is falsy.

    } else if (isArray.check(node)) {
        path.each(function(childPath) {
            recursiveScanChild(childPath, bindings, scopeTypes);
        });

    } else if (namedTypes.Function.check(node)) {
        path.get("params").each(function(paramPath) {
            addPattern(paramPath, bindings);
        });

        recursiveScanChild(path.get("body"), bindings, scopeTypes);

    } else if (namedTypes.TypeAlias && namedTypes.TypeAlias.check(node)) {
        addTypePattern(path.get("id"), scopeTypes);

    } else if (namedTypes.VariableDeclarator.check(node)) {
        addPattern(path.get("id"), bindings);
        recursiveScanChild(path.get("init"), bindings, scopeTypes);

    } else if (node.type === "ImportSpecifier" ||
               node.type === "ImportNamespaceSpecifier" ||
               node.type === "ImportDefaultSpecifier") {
        addPattern(
            // Esprima used to use the .name field to refer to the local
            // binding identifier for ImportSpecifier nodes, but .id for
            // ImportNamespaceSpecifier and ImportDefaultSpecifier nodes.
            // ESTree/Acorn/ESpree use .local for all three node types.
            path.get(node.local ? "local" :
                     node.name ? "name" : "id"),
            bindings
        );

    } else if (Node.check(node) && !Expression.check(node)) {
        types.eachField(node, function(name, child) {
            var childPath = path.get(name);
            if (childPath.value !== child &&
                    !(Array.isArray(childPath.value) && Array.isArray(child) &&
                    childPath.value.length === 0 && child.length === 0)) {
                throw new Error("");
            }
            recursiveScanChild(childPath, bindings, scopeTypes);
        });
    }
}

function recursiveScanChild(path, bindings, scopeTypes) {
    var node = path.value;

    if (!node || Expression.check(node)) {
        // Ignore falsy values and Expressions.

    } else if (namedTypes.FunctionDeclaration.check(node)) {
        addPattern(path.get("id"), bindings);

    } else if (namedTypes.ClassDeclaration &&
               namedTypes.ClassDeclaration.check(node)) {
        addPattern(path.get("id"), bindings);

    } else if (ScopeType.check(node)) {
        if (namedTypes.CatchClause.check(node)) {
            var catchParamName = node.param.name;
            var hadBinding = hasOwn.call(bindings, catchParamName);

            // Any declarations that occur inside the catch body that do
            // not have the same name as the catch parameter should count
            // as bindings in the outer scope.
            recursiveScanScope(path.get("body"), bindings, scopeTypes);

            // If a new binding matching the catch parameter name was
            // created while scanning the catch body, ignore it because it
            // actually refers to the catch parameter and not the outer
            // scope that we're currently scanning.
            if (!hadBinding) {
                delete bindings[catchParamName];
            }
        }

    } else {
        recursiveScanScope(path, bindings, scopeTypes);
    }
}

function addPattern(patternPath, bindings) {
    var pattern = patternPath.value;
    namedTypes.Pattern.assert(pattern);

    if (namedTypes.Identifier.check(pattern)) {
        if (hasOwn.call(bindings, pattern.name)) {
            bindings[pattern.name].push(patternPath);
        } else {
            bindings[pattern.name] = [patternPath];
        }

    } else if (namedTypes.ObjectPattern &&
               namedTypes.ObjectPattern.check(pattern)) {
        patternPath.get('properties').each(function(propertyPath) {
            var property = propertyPath.value;
            if (namedTypes.Pattern.check(property)) {
                addPattern(propertyPath, bindings);
            } else  if (namedTypes.Property.check(property)) {
                addPattern(propertyPath.get('value'), bindings);
            } else if (namedTypes.SpreadProperty &&
                       namedTypes.SpreadProperty.check(property)) {
                addPattern(propertyPath.get('argument'), bindings);
            }
        });

    } else if (namedTypes.ArrayPattern &&
               namedTypes.ArrayPattern.check(pattern)) {
        patternPath.get('elements').each(function(elementPath) {
            var element = elementPath.value;
            if (namedTypes.Pattern.check(element)) {
                addPattern(elementPath, bindings);
            } else if (namedTypes.SpreadElement &&
                       namedTypes.SpreadElement.check(element)) {
                addPattern(elementPath.get("argument"), bindings);
            }
        });

    } else if (namedTypes.PropertyPattern &&
               namedTypes.PropertyPattern.check(pattern)) {
        addPattern(patternPath.get('pattern'), bindings);

    } else if ((namedTypes.SpreadElementPattern &&
                namedTypes.SpreadElementPattern.check(pattern)) ||
               (namedTypes.SpreadPropertyPattern &&
                namedTypes.SpreadPropertyPattern.check(pattern))) {
        addPattern(patternPath.get('argument'), bindings);
    }
}

function addTypePattern(patternPath, types) {
    var pattern = patternPath.value;
    namedTypes.Pattern.assert(pattern);

    if (namedTypes.Identifier.check(pattern)) {
        if (hasOwn.call(types, pattern.name)) {
            types[pattern.name].push(patternPath);
        } else {
            types[pattern.name] = [patternPath];
        }

    }
}

Sp.lookup = function(name) {
    for (var scope = this; scope; scope = scope.parent)
        if (scope.declares(name))
            break;
    return scope;
};

Sp.lookupType = function(name) {
    for (var scope = this; scope; scope = scope.parent)
        if (scope.declaresType(name))
            break;
    return scope;
};

Sp.getGlobalScope = function() {
    var scope = this;
    while (!scope.isGlobal)
        scope = scope.parent;
    return scope;
};

module.exports = Scope;
<commit_msg>Decompose a pathHasValue helper function.<commit_after>var types = require("./types");
var Type = types.Type;
var namedTypes = types.namedTypes;
var Node = namedTypes.Node;
var Expression = namedTypes.Expression;
var isArray = types.builtInTypes.array;
var hasOwn = Object.prototype.hasOwnProperty;
var b = types.builders;

function Scope(path, parentScope) {
    if (!(this instanceof Scope)) {
        throw new Error("Scope constructor cannot be invoked without 'new'");
    }
    if (!(path instanceof require("./node-path"))) {
        throw new Error("");
    }
    ScopeType.assert(path.value);

    var depth;

    if (parentScope) {
        if (!(parentScope instanceof Scope)) {
            throw new Error("");
        }
        depth = parentScope.depth + 1;
    } else {
        parentScope = null;
        depth = 0;
    }

    Object.defineProperties(this, {
        path: { value: path },
        node: { value: path.value },
        isGlobal: { value: !parentScope, enumerable: true },
        depth: { value: depth },
        parent: { value: parentScope },
        bindings: { value: {} },
        types: { value: {} },
    });
}

var scopeTypes = [
    // Program nodes introduce global scopes.
    namedTypes.Program,

    // Function is the supertype of FunctionExpression,
    // FunctionDeclaration, ArrowExpression, etc.
    namedTypes.Function,

    // In case you didn't know, the caught parameter shadows any variable
    // of the same name in an outer scope.
    namedTypes.CatchClause
];

var ScopeType = Type.or.apply(Type, scopeTypes);

Scope.isEstablishedBy = function(node) {
    return ScopeType.check(node);
};

var Sp = Scope.prototype;

// Will be overridden after an instance lazily calls scanScope.
Sp.didScan = false;

Sp.declares = function(name) {
    this.scan();
    return hasOwn.call(this.bindings, name);
};

Sp.declaresType = function(name) {
    this.scan();
    return hasOwn.call(this.types, name);
};

Sp.declareTemporary = function(prefix) {
    if (prefix) {
        if (!/^[a-z$_]/i.test(prefix)) {
            throw new Error("");
        }
    } else {
        prefix = "t$";
    }

    // Include this.depth in the name to make sure the name does not
    // collide with any variables in nested/enclosing scopes.
    prefix += this.depth.toString(36) + "$";

    this.scan();

    var index = 0;
    while (this.declares(prefix + index)) {
        ++index;
    }

    var name = prefix + index;
    return this.bindings[name] = types.builders.identifier(name);
};

Sp.injectTemporary = function(identifier, init) {
    identifier || (identifier = this.declareTemporary());

    var bodyPath = this.path.get("body");
    if (namedTypes.BlockStatement.check(bodyPath.value)) {
        bodyPath = bodyPath.get("body");
    }

    bodyPath.unshift(
        b.variableDeclaration(
            "var",
            [b.variableDeclarator(identifier, init || null)]
        )
    );

    return identifier;
};

Sp.scan = function(force) {
    if (force || !this.didScan) {
        for (var name in this.bindings) {
            // Empty out this.bindings, just in cases.
            delete this.bindings[name];
        }
        scanScope(this.path, this.bindings, this.types);
        this.didScan = true;
    }
};

Sp.getBindings = function () {
    this.scan();
    return this.bindings;
};

Sp.getTypes = function () {
    this.scan();
    return this.types;
};

function scanScope(path, bindings, scopeTypes) {
    var node = path.value;
    ScopeType.assert(node);

    if (namedTypes.CatchClause.check(node)) {
        // A catch clause establishes a new scope but the only variable
        // bound in that scope is the catch parameter. Any other
        // declarations create bindings in the outer scope.
        addPattern(path.get("param"), bindings);

    } else {
        recursiveScanScope(path, bindings, scopeTypes);
    }
}

function recursiveScanScope(path, bindings, scopeTypes) {
    var node = path.value;

    if (path.parent &&
        namedTypes.FunctionExpression.check(path.parent.node) &&
        path.parent.node.id) {
        addPattern(path.parent.get("id"), bindings);
    }

    if (!node) {
        // None of the remaining cases matter if node is falsy.

    } else if (isArray.check(node)) {
        path.each(function(childPath) {
            recursiveScanChild(childPath, bindings, scopeTypes);
        });

    } else if (namedTypes.Function.check(node)) {
        path.get("params").each(function(paramPath) {
            addPattern(paramPath, bindings);
        });

        recursiveScanChild(path.get("body"), bindings, scopeTypes);

    } else if (namedTypes.TypeAlias && namedTypes.TypeAlias.check(node)) {
        addTypePattern(path.get("id"), scopeTypes);

    } else if (namedTypes.VariableDeclarator.check(node)) {
        addPattern(path.get("id"), bindings);
        recursiveScanChild(path.get("init"), bindings, scopeTypes);

    } else if (node.type === "ImportSpecifier" ||
               node.type === "ImportNamespaceSpecifier" ||
               node.type === "ImportDefaultSpecifier") {
        addPattern(
            // Esprima used to use the .name field to refer to the local
            // binding identifier for ImportSpecifier nodes, but .id for
            // ImportNamespaceSpecifier and ImportDefaultSpecifier nodes.
            // ESTree/Acorn/ESpree use .local for all three node types.
            path.get(node.local ? "local" :
                     node.name ? "name" : "id"),
            bindings
        );

    } else if (Node.check(node) && !Expression.check(node)) {
        types.eachField(node, function(name, child) {
            var childPath = path.get(name);
            if (! pathHasValue(childPath, child)) {
                throw new Error("");
            }
            recursiveScanChild(childPath, bindings, scopeTypes);
        });
    }
}

function pathHasValue(path, value) {
  if (path.value === value) {
    return true;
  }

  // Empty arrays are probably produced by defaults.emptyArray, in which
  // case is makes sense to regard them as equivalent, if not ===.
  if (Array.isArray(path.value) &&
      path.value.length === 0 &&
      Array.isArray(value) &&
      value.length === 0) {
    return true;
  }

  return false;
}

function recursiveScanChild(path, bindings, scopeTypes) {
    var node = path.value;

    if (!node || Expression.check(node)) {
        // Ignore falsy values and Expressions.

    } else if (namedTypes.FunctionDeclaration.check(node)) {
        addPattern(path.get("id"), bindings);

    } else if (namedTypes.ClassDeclaration &&
               namedTypes.ClassDeclaration.check(node)) {
        addPattern(path.get("id"), bindings);

    } else if (ScopeType.check(node)) {
        if (namedTypes.CatchClause.check(node)) {
            var catchParamName = node.param.name;
            var hadBinding = hasOwn.call(bindings, catchParamName);

            // Any declarations that occur inside the catch body that do
            // not have the same name as the catch parameter should count
            // as bindings in the outer scope.
            recursiveScanScope(path.get("body"), bindings, scopeTypes);

            // If a new binding matching the catch parameter name was
            // created while scanning the catch body, ignore it because it
            // actually refers to the catch parameter and not the outer
            // scope that we're currently scanning.
            if (!hadBinding) {
                delete bindings[catchParamName];
            }
        }

    } else {
        recursiveScanScope(path, bindings, scopeTypes);
    }
}

function addPattern(patternPath, bindings) {
    var pattern = patternPath.value;
    namedTypes.Pattern.assert(pattern);

    if (namedTypes.Identifier.check(pattern)) {
        if (hasOwn.call(bindings, pattern.name)) {
            bindings[pattern.name].push(patternPath);
        } else {
            bindings[pattern.name] = [patternPath];
        }

    } else if (namedTypes.ObjectPattern &&
               namedTypes.ObjectPattern.check(pattern)) {
        patternPath.get('properties').each(function(propertyPath) {
            var property = propertyPath.value;
            if (namedTypes.Pattern.check(property)) {
                addPattern(propertyPath, bindings);
            } else  if (namedTypes.Property.check(property)) {
                addPattern(propertyPath.get('value'), bindings);
            } else if (namedTypes.SpreadProperty &&
                       namedTypes.SpreadProperty.check(property)) {
                addPattern(propertyPath.get('argument'), bindings);
            }
        });

    } else if (namedTypes.ArrayPattern &&
               namedTypes.ArrayPattern.check(pattern)) {
        patternPath.get('elements').each(function(elementPath) {
            var element = elementPath.value;
            if (namedTypes.Pattern.check(element)) {
                addPattern(elementPath, bindings);
            } else if (namedTypes.SpreadElement &&
                       namedTypes.SpreadElement.check(element)) {
                addPattern(elementPath.get("argument"), bindings);
            }
        });

    } else if (namedTypes.PropertyPattern &&
               namedTypes.PropertyPattern.check(pattern)) {
        addPattern(patternPath.get('pattern'), bindings);

    } else if ((namedTypes.SpreadElementPattern &&
                namedTypes.SpreadElementPattern.check(pattern)) ||
               (namedTypes.SpreadPropertyPattern &&
                namedTypes.SpreadPropertyPattern.check(pattern))) {
        addPattern(patternPath.get('argument'), bindings);
    }
}

function addTypePattern(patternPath, types) {
    var pattern = patternPath.value;
    namedTypes.Pattern.assert(pattern);

    if (namedTypes.Identifier.check(pattern)) {
        if (hasOwn.call(types, pattern.name)) {
            types[pattern.name].push(patternPath);
        } else {
            types[pattern.name] = [patternPath];
        }

    }
}

Sp.lookup = function(name) {
    for (var scope = this; scope; scope = scope.parent)
        if (scope.declares(name))
            break;
    return scope;
};

Sp.lookupType = function(name) {
    for (var scope = this; scope; scope = scope.parent)
        if (scope.declaresType(name))
            break;
    return scope;
};

Sp.getGlobalScope = function() {
    var scope = this;
    while (!scope.isGlobal)
        scope = scope.parent;
    return scope;
};

module.exports = Scope;
<|endoftext|>


<commit_before>/*
 * Monads for NodeJS/CommonJS.
 *
 * A maybe monad modeled after the Scala/Option and Haskell/Maybe types.
 *
 * An either monad, modeled after Scala's Either type.
 *
 * Chris Lewis - chris@thegodcode.net
 */

exports.Nothing = {
  get: function(dflt) { return dflt; },
  map: function(f) { return this; },
  bind: function(f) { return this; },
  isEmpty: function() { return true; }
};

exports.Just = function Just(value) {
  this.get = function(dflt) {
    return value;
  };

  this.map = function(f) {
    return new exports.Just(f(value));
  };

  this.bind = function(f) {
    return f(value);
  };

  this.isEmpty = function() { return false; };
};

exports.maybe = function(value) {
  return value == null ? exports.Nothing : new exports.Just(value);
};

exports.ex = function(f) {
  return function(value) {
    try {
      return new exports.Just(f(value));
    } catch(e) {
      return exports.Nothing;
    }
  }
};

/* Either. */
var Either = function(value, isLeft) {
  this.isLeft = function() { return isLeft; };
  this.isRight = function() { return !isLeft; };
  this.fold = function(fLeft, fRight) {
    return (this.isLeft() ? fLeft : fRight)(value);
  };
};

exports.left = function(value) {
  return new Either(value, true);
};

exports.right = function(value) {
  return new Either(value, false);
};

exports.either = function(value) {
  return (value == null ? exports.left : exports.right)(value);
};

exports.eitherEx = function(f) {
  return function(value) {
    try {
      return new exports.right(f(value));
    } catch(e) {
      return exports.left(e);
    }
  }
};
<commit_msg>explain ex/eitherEx constructors<commit_after>/*
 * Monads for NodeJS/CommonJS.
 *
 * A maybe monad modeled after the Scala/Option and Haskell/Maybe types.
 *
 * An either monad, modeled after Scala's Either type.
 *
 * Chris Lewis - chris@thegodcode.net
 */

exports.Nothing = {
  get: function(dflt) { return dflt; },
  map: function(f) { return this; },
  bind: function(f) { return this; },
  isEmpty: function() { return true; }
};

exports.Just = function Just(value) {
  this.get = function(dflt) {
    return value;
  };

  this.map = function(f) {
    return new exports.Just(f(value));
  };

  this.bind = function(f) {
    return f(value);
  };

  this.isEmpty = function() { return false; };
};

exports.maybe = function(value) {
  return value == null ? exports.Nothing : new exports.Just(value);
};

/*
 * Wrap a function that may throw an exception. The wrapper will
 * pass its argument to the wrappee, catching any exceptions and
 * returning a Maybe (Nothing on exception, Just otherwise).
 */
exports.ex = function(f) {
  return function(value) {
    try {
      return new exports.Just(f(value));
    } catch(e) {
      return exports.Nothing;
    }
  }
};

/* Either. */
var Either = function(value, isLeft) {
  this.isLeft = function() { return isLeft; };
  this.isRight = function() { return !isLeft; };
  this.fold = function(fLeft, fRight) {
    return (this.isLeft() ? fLeft : fRight)(value);
  };
};

exports.left = function(value) {
  return new Either(value, true);
};

exports.right = function(value) {
  return new Either(value, false);
};

exports.either = function(value) {
  return (value == null ? exports.left : exports.right)(value);
};

/*
 * Like ex, but results in an Either. When Left, the either will
 * contain the thrown exception.
 */
exports.eitherEx = function(f) {
  return function(value) {
    try {
      return new exports.right(f(value));
    } catch(e) {
      return exports.left(e);
    }
  }
};
<|endoftext|>


<commit_before>// Copyright 2014 Joyent, Inc.  All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

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

var assert = require('assert-plus');
var clone = require('clone');
var nfs = require('nfs');
var once = require('once');
var statvfs = require('statvfs');

var auth = require('./auth');
var common = require('./nfs/common');

///--- Globals

var sprintf = util.format;

var MNTPATHLEN = 1024; // This is defined by the RFC

///--- API

function ensure_allowed(req, res, next) {
    assert.object(req.connection, 'req.connection');

    var saddr = req.connection.address();
    var ipaddr = saddr.address;

    // hosts_deny entries are optional
    // if the address is present, disallow the mount
    if (req.hosts_deny && req.hosts_deny[ipaddr]) {
        req.log.warn('mountd request from (%s) denied', ipaddr);
        res.error(nfs.MNT3ERR_ACCES);
        next(false);
        return;
    }

    // hosts_allow entries are optional
    // if hosts_allow exists, then the address must be preset or we disallow
    // the mount
    if (req.hosts_allow && !req.hosts_allow[ipaddr]) {
        req.log.warn('mountd request from (%s) was not allowed', ipaddr);
        res.error(nfs.MNT3ERR_ACCES);
        next(false);
        return;
    }

    next();
}

function ensure_exports(req, res, next) {
    assert.string(req.dirpath, 'req.dirpath');

    var p = path.normalize(req.dirpath);

    if (p.length > MNTPATHLEN) {
        res.error(nfs.MNT3ERR_NAMETOOLONG);
        next(false);
        return;
    }

    // export entries are optional
    if (req.exports && !req.exports[p]) {
        res.error(nfs.MNT3ERR_NOENT);
        next(false);
        return;
    }

    req._dirpath = p;
    next();
}

function ensure_manta_dir(req, res, next) {
    var p = req._dirpath;

    req.log.debug('ensure_manta_directory(%s): entered', p);
    req.manta.info(p, function (err, info) {
        if (err) {
            req.log.warn(err, 'ensure_manta_directory: info(%s) failed', p);
            res.error(nfs.MNT3ERR_SERVERFAULT);
            next(false);
            return;
        }

        if (info.extension !== 'directory') {
            req.log.warn({
                path: p,
                info: info
            }, 'mount: manta location is not a directory');
            res.error(nfs.MNT3ERR_NOTDIR);
            next(false);
            return;
        }

        req.log.debug({
            info: info
        }, 'ensure_manta_directory(%s): done', p);

        req.info = info;
        next();
    });
}

function mount(call, reply, next) {
    var log = call.log;

    log.debug('mount(%s): entered', call._dirpath);
    call.fs.stat(call._dirpath, function (serr, dummystats) {
        if (serr) {
            log.warn(serr, 'mount(%s): failed to stat', call._dirpath);
            reply.error(nfs.MNT3ERR_SERVERFAULT);
            next(false);
            return;
        }

        call.fs.lookup(call._dirpath, function (lerr, fhandle) {
            if (lerr) {
                log.warn(lerr, 'mount(%s): failed to lookup', call._dirpath);
                reply.error(nfs.MNT3ERR_SERVERFAULT);
                next(false);
                return;
            }

            var saddr = call.connection.address();
            var ipaddr = saddr.address;

            reply.setFileHandle(fhandle);
            log.info('mount(%s) from (%s): done -> %s', call._dirpath, ipaddr,
                fhandle);
            reply.send();
            next();

            // We assume the client is going to immediately do an ls, so just
            // cache the root directory
            call.fs.readdir(call._dirpath, function () {});
        });
    });
}

function umount(call, reply, next) {
    var log = call.log;

    // We don't invoke call.fs.shutdown here since the server is still running
    // and they may want to mount again later.

    var saddr = call.connection.address();
    var ipaddr = saddr.address;
    log.info('umount(%s) from (%s) done', call._dirpath, ipaddr);
    reply.send();
    next();
}

function createMountServer(opts) {
    assert.object(opts, 'options');
    assert.optionalObject(opts.exports, 'options.exports');
    assert.optionalObject(opts.hosts_allow, 'options.hosts_allow');
    assert.optionalObject(opts.hosts_deny, 'options.hosts_deny');
    assert.object(opts.log, 'options.log');
    assert.object(opts.manta, 'options.manta');
    assert.object(opts.fs, 'options.fs');

    var s = nfs.createMountServer({
        log: opts.log
    });

    s.use(auth.authorize);
    s.use(function setup(req, res, next) {
        req.exports = opts.exports;
        req.hosts_allow = opts.hosts_allow;
        req.hosts_deny = opts.hosts_deny;
        req.manta = opts.manta;
        req.fs = opts.fs;
        next();
    });
    s.mnt(ensure_allowed,
          ensure_exports,
          ensure_manta_dir,
          mount);

    s.umnt(ensure_allowed,
          ensure_exports,
          umount);

    s.on('after', function (name, call, reply, err) {
        opts.log.debug({
            procedure: name,
            rpc_call: call,
            rpc_reply: reply,
            err: err
        }, 'mountd: %s handled', name);
    });

    return (s);
}

///--- Exports

module.exports = {
    createMountServer: createMountServer
};
<commit_msg>MANTA-1844 use correct IP for host validation<commit_after>// Copyright 2014 Joyent, Inc.  All rights reserved.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

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

var assert = require('assert-plus');
var clone = require('clone');
var nfs = require('nfs');
var once = require('once');
var statvfs = require('statvfs');

var auth = require('./auth');
var common = require('./nfs/common');

///--- Globals

var sprintf = util.format;

var MNTPATHLEN = 1024; // This is defined by the RFC

///--- API

function ensure_allowed(req, res, next) {
    assert.object(req.connection, 'req.connection');

    var ipaddr = req.connection.remoteAddress;

    // hosts_deny entries are optional
    // if the address is present, disallow the mount
    if (req.hosts_deny && req.hosts_deny[ipaddr]) {
        req.log.warn('mountd request from (%s) denied', ipaddr);
        res.error(nfs.MNT3ERR_ACCES);
        next(false);
        return;
    }

    // hosts_allow entries are optional
    // if hosts_allow exists, then the address must be preset or we disallow
    // the mount
    if (req.hosts_allow && !req.hosts_allow[ipaddr]) {
        req.log.warn('mountd request from (%s) was not allowed', ipaddr);
        res.error(nfs.MNT3ERR_ACCES);
        next(false);
        return;
    }

    next();
}

function ensure_exports(req, res, next) {
    assert.string(req.dirpath, 'req.dirpath');

    var p = path.normalize(req.dirpath);

    if (p.length > MNTPATHLEN) {
        res.error(nfs.MNT3ERR_NAMETOOLONG);
        next(false);
        return;
    }

    // export entries are optional
    if (req.exports && !req.exports[p]) {
        res.error(nfs.MNT3ERR_NOENT);
        next(false);
        return;
    }

    req._dirpath = p;
    next();
}

function ensure_manta_dir(req, res, next) {
    var p = req._dirpath;

    req.log.debug('ensure_manta_directory(%s): entered', p);
    req.manta.info(p, function (err, info) {
        if (err) {
            req.log.warn(err, 'ensure_manta_directory: info(%s) failed', p);
            res.error(nfs.MNT3ERR_SERVERFAULT);
            next(false);
            return;
        }

        if (info.extension !== 'directory') {
            req.log.warn({
                path: p,
                info: info
            }, 'mount: manta location is not a directory');
            res.error(nfs.MNT3ERR_NOTDIR);
            next(false);
            return;
        }

        req.log.debug({
            info: info
        }, 'ensure_manta_directory(%s): done', p);

        req.info = info;
        next();
    });
}

function mount(call, reply, next) {
    var log = call.log;

    log.debug('mount(%s): entered', call._dirpath);
    call.fs.stat(call._dirpath, function (serr, dummystats) {
        if (serr) {
            log.warn(serr, 'mount(%s): failed to stat', call._dirpath);
            reply.error(nfs.MNT3ERR_SERVERFAULT);
            next(false);
            return;
        }

        call.fs.lookup(call._dirpath, function (lerr, fhandle) {
            if (lerr) {
                log.warn(lerr, 'mount(%s): failed to lookup', call._dirpath);
                reply.error(nfs.MNT3ERR_SERVERFAULT);
                next(false);
                return;
            }

            var ipaddr = call.connection.remoteAddress;

            reply.setFileHandle(fhandle);
            log.info('mount(%s) from (%s): done -> %s', call._dirpath, ipaddr,
                fhandle);
            reply.send();
            next();

            // We assume the client is going to immediately do an ls, so just
            // cache the root directory
            call.fs.readdir(call._dirpath, function () {});
        });
    });
}

function umount(call, reply, next) {
    var log = call.log;

    // We don't invoke call.fs.shutdown here since the server is still running
    // and they may want to mount again later.

    var ipaddr = call.connection.remoteAddress;
    log.info('umount(%s) from (%s) done', call._dirpath, ipaddr);
    reply.send();
    next();
}

function createMountServer(opts) {
    assert.object(opts, 'options');
    assert.optionalObject(opts.exports, 'options.exports');
    assert.optionalObject(opts.hosts_allow, 'options.hosts_allow');
    assert.optionalObject(opts.hosts_deny, 'options.hosts_deny');
    assert.object(opts.log, 'options.log');
    assert.object(opts.manta, 'options.manta');
    assert.object(opts.fs, 'options.fs');

    var s = nfs.createMountServer({
        log: opts.log
    });

    s.use(auth.authorize);
    s.use(function setup(req, res, next) {
        req.exports = opts.exports;
        req.hosts_allow = opts.hosts_allow;
        req.hosts_deny = opts.hosts_deny;
        req.manta = opts.manta;
        req.fs = opts.fs;
        next();
    });
    s.mnt(ensure_allowed,
          ensure_exports,
          ensure_manta_dir,
          mount);

    s.umnt(ensure_allowed,
          ensure_exports,
          umount);

    s.on('after', function (name, call, reply, err) {
        opts.log.debug({
            procedure: name,
            rpc_call: call,
            rpc_reply: reply,
            err: err
        }, 'mountd: %s handled', name);
    });

    return (s);
}

///--- Exports

module.exports = {
    createMountServer: createMountServer
};
<|endoftext|>


<commit_before>/*
 * Runs a customised PhoneGap App server for a Sencha Touch App.
 * Copyright (C) 2014 Anthony O'Hagan  tony@ohagan.name
 */

require('terminal-colors');
var path = require('path');
var fs = require('fs');
var dirs = require('./dirs');
var server = require('connect-phonegap');

// Configure .www and .www_<platform> server options
function addServerOptions(options) {
    dirs.init(true);    // flag allows server to start in either Sencha or PhoneGap folders
    options.vdir = {};

    if (dirs.isSencha()) {
        options.vdir.www = ".";   // Non platform source files
        options.watches = options.watch.sencha;
    } else if (dirs.isPhoneGapOrCordova()) {
        options.vdir.www = "www"; // Non platform source files
        options.watches = options.watch.cordova;
    } else {
        console.error("You need to start the server in a Sencha Touch or PhoneGap project directory".lightRed);
        return;
    }

    // PhoneGap/Cordova platforms configured
    var platformsDir = path.join(dirs.pgDir, 'platforms');
    fs.readdirSync(platformsDir).forEach(function(platform) {
        var wwwPlatformDir = path.join(dirs.pgDir, 'platforms', platform, platform === "android" ? 'assets/www' : 'www');
        options.vdir[platform] = wwwPlatformDir;
        if (!fs.existsSync(wwwPlatformDir)) {
            console.warn(wwwPlatformDir.lightGreen + (": Cannot find " + platform + " platform directory").lightYellow);
        }
    });

    return options;
}

module.exports = function(options, args) {
    if (args.length > 0) options.dir = args.shift();
    if (options.dir) {
        if (!fs.existsSync(options.dir)) throw options.dir + ": Directory not found";
        process.chdir(options.dir);
    }

    options = addServerOptions(options);  // Must perform *after* chdir()

    // All paths except platform/plugin JS files are relative to cwd.
    console.log(('Starting in ' + process.cwd() + ' ... ').lightGreen);

    var reloads = 0;

    server.listen(options)
        .on('error', function(msg) {
            console.log(msg.lightRed);
        })
        .on('warn', function(msg) {
            console.log(msg.lightYellow);
        })
        .on('log', function(statusCode, url, staticpath) {
            if (url == '/__api__/autoreload') {
                if (options.debug) process.stdout.write('\033[0GReloads: ' + ('' + reloads++).lightGreen);
            } else {
                if (reloads > 0) { console.log(); }
                var msg = statusCode;
                if (url) { msg += ' ' + url; }
                if (statusCode != 200) { msg = msg.lightYellow; }
                console.log(msg);

                if (staticpath) {
                    console.log('  ' + staticpath.lightGreen);
                }
                reloads = 0;
            }
        })
        .on('complete', function(data) {
            console.log('\nCtrl-C to stop the server\n');
            //console.log('Listening on ', (data.address + ':' + data.port).lightYellow);  // already displayed by 'server'
        });
};<commit_msg>Inform user what to do when socket port is in use.<commit_after>/*
 * Runs a customised PhoneGap App server for a Sencha Touch App.
 * Copyright (C) 2014 Anthony O'Hagan  tony@ohagan.name
 */

require('terminal-colors');
var path = require('path');
var fs = require('fs');
var dirs = require('./dirs');
var server = require('connect-phonegap');

// Configure .www and .www_<platform> server options
function addServerOptions(options) {
    dirs.init(true);    // flag allows server to start in either Sencha or PhoneGap folders
    options.vdir = {};

    if (dirs.isSencha()) {
        options.vdir.www = ".";   // Non platform source files
        options.watches = options.watch.sencha;
    } else if (dirs.isPhoneGapOrCordova()) {
        options.vdir.www = "www"; // Non platform source files
        options.watches = options.watch.cordova;
    } else {
        console.error("You need to start the server in a Sencha Touch or PhoneGap project directory".lightRed);
        return;
    }

    // PhoneGap/Cordova platforms configured
    var platformsDir = path.join(dirs.pgDir, 'platforms');
    fs.readdirSync(platformsDir).forEach(function(platform) {
        var wwwPlatformDir = path.join(dirs.pgDir, 'platforms', platform, platform === "android" ? 'assets/www' : 'www');
        options.vdir[platform] = wwwPlatformDir;
        if (!fs.existsSync(wwwPlatformDir)) {
            console.warn(wwwPlatformDir.lightGreen + (": Cannot find " + platform + " platform directory").lightYellow);
        }
    });

    return options;
}

module.exports = function(options, args) {
    if (args.length > 0) options.dir = args.shift();
    if (options.dir) {
        if (!fs.existsSync(options.dir)) throw options.dir + ": Directory not found";
        process.chdir(options.dir);
    }

    options = addServerOptions(options);  // Must perform *after* chdir()

    // All paths except platform/plugin JS files are relative to cwd.
    console.log(('Starting in ' + process.cwd() + ' ... ').lightGreen);

    var reloads = 0;

    server.listen(options)
        .on('error', function(err) {
            if (err.code === 'EADDRINUSE') {
                console.log(("Socket port " + options.port + " is already in use.").lightRed);
                console.log("Stop the other server or select a different port via:".lightGreen);
                console.log(("--port port option OR set port in ~/.stlive.config").lightGreen);
            } else {
                console.log(err.message.lightRed);
            }
        })
        .on('warn', function(err) {
            console.log(err.message.lightYellow);
        })
        .on('log', function(statusCode, url, staticpath) {
            if (url == '/__api__/autoreload') {
                if (options.debug) process.stdout.write('\033[0GReloads: ' + ('' + reloads++).lightGreen);
            } else {
                if (reloads > 0) { console.log(); }
                var msg = statusCode;
                if (url) { msg += ' ' + url; }
                if (statusCode != 200) { msg = msg.lightYellow; }
                console.log(msg);

                if (staticpath) {
                    console.log('  ' + staticpath.lightGreen);
                }
                reloads = 0;
            }
        })
        .on('complete', function(data) {
            console.log('\nCtrl-C to stop the server\n');
            //console.log('Listening on ', (data.address + ':' + data.port).lightYellow);  // already displayed by 'server'
        });
};<|endoftext|>


<commit_before>var _ = require('underscore'),
    db = require('./db'),
    moment = require('moment'),
    users = require('./users');

module.exports = {
    /**
     * Sites is a synthetic collection which has as its value the number of sites a particular user has.
     *
     * Count gets total number and options is ignored for now.
     */
    count: function(options, callback) {
        var sites = db.coll('sites');

        sites.find({}, function(err, cursor) {
            var count = 0;

            if (err) {
                return callback(err);
            }

            cursor.toArray(function(err, sites) {
                _.each(sites, function(site) {
                    count += site.value;
                });

                callback(null, count);
            });
        });
    },
    /**
     * Lists sites for a given user.
     *
     * Will not work as intended for admin users.
     *
     * @param {String} options.user_id to get sites for
     * @param {String} options.search tests the site's url, title & type by the string
     */
    list: function(options, callback) {
        var query = {},
            userColl = db.coll('users');

        if (options.user_id && !options.isAdmin) {
            query._id = options.user_id;
        }

        userColl.findOne(query, function(err, user) {
            var sites;

            if (err) {
                callback(err);
            } else {
                sites = _.sortBy(user.sites, function(site) {
                    return site.url;
                });

                if (options.search) {
                    sites = _.filter(sites, function(site) {
                        var re = new RegExp(options.search, 'i');

                        return re.test(site.url) || re.test(site.title) || re.test(site.type);
                    });
                }
                callback(null, {
                    total: user.sites.length,
                    items: sites
                });
            }
        });
    },
    /**
     * Lists all sites.
     *
     */
    listAll: function(callback) {
        var userColl = db.coll('users');

        userColl.find({
            $orderby: {
                country: 1
            },
            $query: {
                admin: false
            }
        }, function(err, cursor) {
            if (err) {
                return callback(err);
            }

            cursor.toArray(function(err, users) {
                var sites = [];

                _.each(users, function(user) {
                    // country is of the form 'AF - Afghanistan'
                    var country = user.country || '';

                    country = country.split(' - ')[1] || '';

                    _.each(user.sites, function(site) {
                        // country is defined on user, not the site
                        if (site.active) {
                            sites.push(_.extend({}, site, {
                                country: country,
                                username: user.username
                            }));
                        }
                    });
                });

                callback(null, sites);
            });
        });
    },

    /**
     * Adds a new site.
     *
     * @param {String} user_id the user to add the site to
     * @param {String} title
     * @param {String} type
     * @param {String} url
     */
    insert: function(options, callback) {
        var userColl = db.coll('users'),
            now = new Date();

        userColl.update({
            _id: db.getId(options.user_id)
        }, {
            $push: {
                sites: {
                    active: true,
                    created_at: now,
                    _id: new db.ObjectID(),
                    last_modified: now,
                    title: options.title,
                    type: options.type,
                    url: options.url
                }
            }
        }, callback);
    },
    /**
     * Convenience method to update the sites array for a user.
     *
     * Depends on the sites collection having already been updated; simply persists the changes to the db.
     *
     * @param {Object} user
     * @param {String} user.user_id
     * @param {Array} user.sites
     * @param {Object} site the object to set the last_modified on
     */
    updateSites: function(options, callback) {
        options.site.last_modified = new Date();

        users.update({
            _id: options.user._id
        }, {
            $set: {
                sites: options.user.sites
            }
        }, callback);
    },
    /**
     * Get the user, site, date for a date_id
     *
     * @param {String} date_id
     * @param {Function} callback
     */
    getDate: function(date_id, callback) {
        users.getUser({
            "sites.publication_dates._id": {
                $all: [ db.getId(date_id) ] // see http://stackoverflow.com/questions/17303833/mongodb-nested-array-query
            }
        }, function(err, user) {
            var site,
                publication_date;

            if (err) {
                return callback(err);
            } else if (!user) {
                return callback("Could not find publication date with id " + date_id);
            }

            site = _.find(user.sites, function(site) {
                _.each(site.publication_dates, function(date) {
                    if (date._id && date._id.toString() === date_id) {
                        publication_date = date;
                    }
                });
                return !!publication_date;
            });

            if (!site) {
                return callback("Could not find publication date with id  " + date_id);
            }

            callback(null, user, site, publication_date);
        });
    },
    /**
     * Get the user & site for a site_id
     *
     * @param {String} site_id
     * @param {Function} callback
     */
    getSite: function(site_id, callback) {
        users.getUser({
            "sites._id": db.getId(site_id)
        }, function(err, user) {
            var site;

            if (err) {
                return callback(err);
            } else if (!user) {
                return callback("Could not find site with id " + site_id);
            }

            site = _.find(user.sites, function(site) {
                return site._id && site._id.toString() === site_id;
            });

            if (!site) {
                return callback("Could not find site with id  " + site_id);
            }

            _.defaults(site, {
                publication_dates: []
            });

            callback(null, user, site);
        });
    },
    /**
     * Removes the site with the matching created_at date.
     *
     * @param {Object} options
     * @param {String} options.site_id
     * @param {Function} callback
     */
    remove: function(options, callback) {
        module.exports.getSite(options.site_id, function(err, user, site) {
            if (err) {
                return callback(err);
            }

            user.sites = _.without(user.sites, site);

            module.exports.updateSites({
                site: site,
                user: user
            }, callback);
        });
    },
    /**
     * Add a publication date to the site with the matching created_at.
     *
     * @param {String} options.site_id
     * @param {Date} options.publication_date the Date to add to the site.
     */
    addDate: function(options, callback) {
        module.exports.getSite(options.site_id, function(err, user, site) {
            if (err) {
                return callback(err);
            }

            site.publication_dates.push({
                _id: new db.ObjectID(),
                date: moment(options.publication_date).toDate()
            });
            site.publication_dates = _.sortBy(site.publication_dates, function(d) {
                return d.date.getTime();
            });

            module.exports.updateSites({
                site: site,
                user: user
            }, callback);
        });
    },
    /**
     * Delete all publication dates with the matching created_at.
     *
     * @param {String} options.date_id
     * @param {Function} callback
     */
    deleteDate: function(options, callback) {
        module.exports.getDate(options.date_id, function(err, user, site, date) {
            if (err) {
                return callback(err);
            }

            site.publication_dates = _.reject(site.publication_dates, function(d) {
                return d._id === date._id;
            });

            module.exports.updateSites({
                site: site,
                user: user
            }, callback);
        });
    },
    /**
     * Change the active state of a site.
     *
     * @param {String} options.site_id
     * @param {Boolean} options.active
     */
    changeActiveStatus: function(options, callback) {
        module.exports.getSite(options.site_id, function(err, user, site) {
            if (err) {
                return callback(err);
            }

            site.active = options.active;

            module.exports.updateSites({
                site: site,
                user: user
            }, callback);
        });
    },
    /**
     * Make a site active.
     *
     * @param {String} options.site_id
     * @param {Function} callback
     */
    enable: function(options, callback) {
        options.active = true;
        module.exports.changeActiveStatus(options, callback);
    },
    /**
     * Make a site inactive.
     *
     * @param {String} options.site_id
     * @param {Function} callback
     */
    disable: function(options, callback) {
        options.active = false;
        module.exports.changeActiveStatus(options, callback);
    }
};
<commit_msg>Add validation which asserts that a URL must be unique.<commit_after>var _ = require('underscore'),
    async = require('async'),
    db = require('./db'),
    moment = require('moment'),
    users = require('./users');

module.exports = {
    /**
     * Sites is a synthetic collection which has as its value the number of sites a particular user has.
     *
     * Count gets total number and options is ignored for now.
     */
    count: function(options, callback) {
        var sites = db.coll('sites');

        sites.find({}, function(err, cursor) {
            var count = 0;

            if (err) {
                return callback(err);
            }

            cursor.toArray(function(err, sites) {
                _.each(sites, function(site) {
                    count += site.value;
                });

                callback(null, count);
            });
        });
    },
    /**
     * Tests whether a given URL is already being watched. Falsey URLs are never watched.
     *
     * @param {String} url
     * @param {Function} callback
     */
    isWatched: function(url, callback) {
        var userColl = db.coll('users');

        if (url) {
            url = url.trim();

            userColl.findOne({
                "sites.url": url
            }, function(err, user) {
                callback(err, !!user);
            });
        } else {
            callback(null, false);
        }
    },
    /**
     * Lists sites for a given user.
     *
     * Will not work as intended for admin users.
     *
     * @param {String} options.user_id to get sites for
     * @param {String} options.search tests the site's url, title & type by the string
     */
    list: function(options, callback) {
        var query = {},
            userColl = db.coll('users');

        if (options.user_id && !options.isAdmin) {
            query._id = options.user_id;
        }

        userColl.findOne(query, function(err, user) {
            var sites;

            if (err) {
                callback(err);
            } else {
                sites = _.sortBy(user.sites, function(site) {
                    return site.url;
                });

                if (options.search) {
                    sites = _.filter(sites, function(site) {
                        var re = new RegExp(options.search, 'i');

                        return re.test(site.url) || re.test(site.title) || re.test(site.type);
                    });
                }
                callback(null, {
                    total: user.sites.length,
                    items: sites
                });
            }
        });
    },
    /**
     * Lists all sites.
     *
     */
    listAll: function(callback) {
        var userColl = db.coll('users');

        userColl.find({
            $orderby: {
                country: 1
            },
            $query: {
                admin: false
            }
        }, function(err, cursor) {
            if (err) {
                return callback(err);
            }

            cursor.toArray(function(err, users) {
                var sites = [];

                _.each(users, function(user) {
                    // country is of the form 'AF - Afghanistan'
                    var country = user.country || '';

                    country = country.split(' - ')[1] || '';

                    _.each(user.sites, function(site) {
                        // country is defined on user, not the site
                        if (site.active) {
                            sites.push(_.extend({}, site, {
                                country: country,
                                username: user.username
                            }));
                        }
                    });
                });

                callback(null, sites);
            });
        });
    },

    /**
     * Adds a new site.
     *
     * @param {String} user_id the user to add the site to
     * @param {String} title
     * @param {String} type
     * @param {String} url
     */
    insert: function(options, callback) {
        var userColl = db.coll('users'),
            now = new Date();

        async.waterfall([
            function(callback) {
                module.exports.isWatched(options.url, callback);
            },
            function(isWatched, callback) {
                if (isWatched) {
                    callback("URL is already being watched.");
                } else {
                    userColl.update({
                        _id: db.getId(options.user_id)
                    }, {
                        $push: {
                            sites: {
                                active: true,
                                created_at: now,
                                _id: new db.ObjectID(),
                                last_modified: now,
                                title: options.title,
                                type: options.type,
                                url: options.url
                            }
                        }
                    }, callback);
                }
            }
        ], callback);

    },
    /**
     * Convenience method to update the sites array for a user.
     *
     * Depends on the sites collection having already been updated; simply persists the changes to the db.
     *
     * @param {Object} user
     * @param {String} user.user_id
     * @param {Array} user.sites
     * @param {Object} site the object to set the last_modified on
     */
    updateSites: function(options, callback) {
        options.site.last_modified = new Date();

        users.update({
            _id: options.user._id
        }, {
            $set: {
                sites: options.user.sites
            }
        }, callback);
    },
    /**
     * Get the user, site, date for a date_id
     *
     * @param {String} date_id
     * @param {Function} callback
     */
    getDate: function(date_id, callback) {
        users.getUser({
            "sites.publication_dates._id": {
                $all: [ db.getId(date_id) ] // see http://stackoverflow.com/questions/17303833/mongodb-nested-array-query
            }
        }, function(err, user) {
            var site,
                publication_date;

            if (err) {
                return callback(err);
            } else if (!user) {
                return callback("Could not find publication date with id " + date_id);
            }

            site = _.find(user.sites, function(site) {
                _.each(site.publication_dates, function(date) {
                    if (date._id && date._id.toString() === date_id) {
                        publication_date = date;
                    }
                });
                return !!publication_date;
            });

            if (!site) {
                return callback("Could not find publication date with id  " + date_id);
            }

            callback(null, user, site, publication_date);
        });
    },
    /**
     * Get the user & site for a site_id
     *
     * @param {String} site_id
     * @param {Function} callback
     */
    getSite: function(site_id, callback) {
        users.getUser({
            "sites._id": db.getId(site_id)
        }, function(err, user) {
            var site;

            if (err) {
                return callback(err);
            } else if (!user) {
                return callback("Could not find site with id " + site_id);
            }

            site = _.find(user.sites, function(site) {
                return site._id && site._id.toString() === site_id;
            });

            if (!site) {
                return callback("Could not find site with id  " + site_id);
            }

            _.defaults(site, {
                publication_dates: []
            });

            callback(null, user, site);
        });
    },
    /**
     * Removes the site with the matching created_at date.
     *
     * @param {Object} options
     * @param {String} options.site_id
     * @param {Function} callback
     */
    remove: function(options, callback) {
        module.exports.getSite(options.site_id, function(err, user, site) {
            if (err) {
                return callback(err);
            }

            user.sites = _.without(user.sites, site);

            module.exports.updateSites({
                site: site,
                user: user
            }, callback);
        });
    },
    /**
     * Add a publication date to the site with the matching created_at.
     *
     * @param {String} options.site_id
     * @param {Date} options.publication_date the Date to add to the site.
     */
    addDate: function(options, callback) {
        module.exports.getSite(options.site_id, function(err, user, site) {
            if (err) {
                return callback(err);
            }

            site.publication_dates.push({
                _id: new db.ObjectID(),
                date: moment(options.publication_date).toDate()
            });
            site.publication_dates = _.sortBy(site.publication_dates, function(d) {
                return d.date.getTime();
            });

            module.exports.updateSites({
                site: site,
                user: user
            }, callback);
        });
    },
    /**
     * Delete all publication dates with the matching created_at.
     *
     * @param {String} options.date_id
     * @param {Function} callback
     */
    deleteDate: function(options, callback) {
        module.exports.getDate(options.date_id, function(err, user, site, date) {
            if (err) {
                return callback(err);
            }

            site.publication_dates = _.reject(site.publication_dates, function(d) {
                return d._id === date._id;
            });

            module.exports.updateSites({
                site: site,
                user: user
            }, callback);
        });
    },
    /**
     * Change the active state of a site.
     *
     * @param {String} options.site_id
     * @param {Boolean} options.active
     */
    changeActiveStatus: function(options, callback) {
        module.exports.getSite(options.site_id, function(err, user, site) {
            if (err) {
                return callback(err);
            }

            site.active = options.active;

            module.exports.updateSites({
                site: site,
                user: user
            }, callback);
        });
    },
    /**
     * Make a site active.
     *
     * @param {String} options.site_id
     * @param {Function} callback
     */
    enable: function(options, callback) {
        options.active = true;
        module.exports.changeActiveStatus(options, callback);
    },
    /**
     * Make a site inactive.
     *
     * @param {String} options.site_id
     * @param {Function} callback
     */
    disable: function(options, callback) {
        options.active = false;
        module.exports.changeActiveStatus(options, callback);
    }
};
<|endoftext|>