<commit_before>/*
 * gaze
 * https://github.com/shama/gaze
 *
 * Copyright (c) 2013 Kyle Robinson Young
 * Licensed under the MIT license.
 */

'use strict';

// libs
var util = require('util');
var EE = require('events').EventEmitter;
var fs = require('fs');
var path = require('path');
var globule = require('globule');
var helper = require('./helper');

// globals
var delay = 10;

// `Gaze` EventEmitter object to return in the callback
function Gaze(patterns, opts, done) {
  var self = this;
  EE.call(self);

  // If second arg is the callback
  if (typeof opts === 'function') {
    done = opts;
    opts = {};
  }

  // Default options
  opts = opts || {};
  opts.mark = true;
  opts.interval = opts.interval || 100;
  opts.debounceDelay = opts.debounceDelay || 500;
  opts.cwd = opts.cwd || process.cwd();
  this.options = opts;

  // Default done callback
  done = done || function() {};

  // Remember our watched dir:files
  this._watched = Object.create(null);

  // Store watchers
  this._watchers = Object.create(null);

  // Store watchFile listeners
  this._pollers = Object.create(null);

  // Store patterns
  this._patterns = [];

  // Cached events for debouncing
  this._cached = Object.create(null);

  // Set maxListeners
  if (this.options.maxListeners) {
    this.setMaxListeners(this.options.maxListeners);
    Gaze.super_.prototype.setMaxListeners(this.options.maxListeners);
    delete this.options.maxListeners;
  }

  // Initialize the watch on files
  if (patterns) {
    this.add(patterns, done);
  }

  return this;
}
util.inherits(Gaze, EE);

// Main entry point. Start watching and call done when setup
module.exports = function gaze(patterns, opts, done) {
  return new Gaze(patterns, opts, done);
};
module.exports.Gaze = Gaze;

// Override the emit function to emit `all` events
// and debounce on duplicate events per file
Gaze.prototype.emit = function() {
  var self = this;
  var args = arguments;

  var e = args[0];
  var filepath = args[1];
  var timeoutId;

  // If not added/deleted/changed/renamed then just emit the event
  if (e.slice(-2) !== 'ed') {
    Gaze.super_.prototype.emit.apply(self, args);
    return this;
  }

  // Detect rename event, if added and previous deleted is in the cache
  if (e === 'added') {
    Object.keys(this._cached).forEach(function(oldFile) {
      if (self._cached[oldFile].indexOf('deleted') !== -1) {
        args[0] = e = 'renamed';
        [].push.call(args, oldFile);
        delete self._cached[oldFile];
        return false;
      }
    });
  }

  // If cached doesnt exist, create a delay before running the next
  // then emit the event
  var cache = this._cached[filepath] || [];
  if (cache.indexOf(e) === -1) {
    helper.objectPush(self._cached, filepath, e);
    clearTimeout(timeoutId);
    timeoutId = setTimeout(function() {
      delete self._cached[filepath];
    }, this.options.debounceDelay);
    // Emit the event and `all` event
    Gaze.super_.prototype.emit.apply(self, args);
    Gaze.super_.prototype.emit.apply(self, ['all', e].concat([].slice.call(args, 1)));
  }

  return this;
};

// Close watchers
Gaze.prototype.close = function(_reset) {
  var self = this;
  _reset = _reset === false ? false : true;
  Object.keys(self._watchers).forEach(function(file) {
    self._watchers[file].close();
  });
  self._watchers = Object.create(null);
  Object.keys(this._watched).forEach(function(dir) {
    self._unpollDir(dir);
  });
  if (_reset) {
    self._watched = Object.create(null);
    setTimeout(function() {
      self.emit('end');
      self.removeAllListeners();
    }, delay + 100);
  }
  return self;
};

// Add file patterns to be watched
Gaze.prototype.add = function(files, done) {
  if (typeof files === 'string') { files = [files]; }
  this._patterns = helper.unique.apply(null, [this._patterns, files]);
  files = globule.find(this._patterns, this.options);
  this._addToWatched(files);
  this.close(false);
  this._initWatched(done);
};

// Dont increment patterns and dont call done if nothing added
Gaze.prototype._internalAdd = function(file, done) {
  var files = [];
  if (helper.isDir(file)) {
    files = globule.find(this._patterns, this.options);
  } else {
    if (globule.isMatch(this._patterns, file, this.options)) {
      files = [file];
    }
  }
  if (files.length > 0) {
    this._addToWatched(files);
    this.close(false);
    this._initWatched(done);
  }
};

// Remove file/dir from `watched`
Gaze.prototype.remove = function(file) {
  var self = this;
  if (this._watched[file]) {
    // is dir, remove all files
    this._unpollDir(file);
    delete this._watched[file];
  } else {
    // is a file, find and remove
    Object.keys(this._watched).forEach(function(dir) {
      var index = self._watched[dir].indexOf(file);
      if (index) {
        self._unpollFile(file);
        delete self._watched[dir][index];
        return false;
      }
    });
  }
  if (this._watchers[file]) {
    this._watchers[file].close();
  }
  return this;
};

// Return watched files
Gaze.prototype.watched = function() {
  return this._watched;
};

// Returns `watched` files with relative paths to process.cwd()
Gaze.prototype.relative = function(dir, unixify) {
  var self = this;
  var relative = Object.create(null);
  var relDir, relFile, unixRelDir;
  var cwd = this.options.cwd || process.cwd();
  if (dir === '') { dir = '.'; }
  dir = helper.markDir(dir);
  unixify = unixify || false;
  Object.keys(this._watched).forEach(function(dir) {
    relDir = path.relative(cwd, dir) + path.sep;
    if (relDir === path.sep) { relDir = '.'; }
    unixRelDir = unixify ? helper.unixifyPathSep(relDir) : relDir;
    relative[unixRelDir] = self._watched[dir].map(function(file) {
      relFile = path.relative(path.join(cwd, relDir) || '', file || '');
      if (helper.isDir(file)) {
        relFile = helper.markDir(relFile);
      }
      if (unixify) {
        relFile = helper.unixifyPathSep(relFile);
      }
      return relFile;
    });
  });
  if (dir && unixify) {
    dir = helper.unixifyPathSep(dir);
  }
  return dir ? relative[dir] || [] : relative;
};

// Adds files and dirs to watched
Gaze.prototype._addToWatched = function(files) {
  for (var i = 0; i < files.length; i++) {
    var file = files[i];
    var filepath = path.resolve(this.options.cwd, file);

    var dirname = (helper.isDir(file)) ? filepath : path.dirname(filepath);
    dirname = helper.markDir(dirname);

    if (file.slice(-1) === '/') { filepath += path.sep; }
    helper.objectPush(this._watched, path.dirname(filepath) + path.sep, filepath);

    // add folders into the mix
    var readdir = fs.readdirSync(dirname);
    for (var j = 0; j < readdir.length; j++) {
      var dirfile = path.join(dirname, readdir[j]);
      if (fs.statSync(dirfile).isDirectory()) {
        helper.objectPush(this._watched, dirname, dirfile + path.sep);
      }
    }
  }
  return this;
};

Gaze.prototype._watchDir = function(dir, done) {
  try {
    this._watchers[dir] = fs.watch(dir, function(event) {
      // race condition. Let's give the fs a little time to settle down. so we
      // don't fire events on non existent files.
      setTimeout(function() {
        if (fs.existsSync(dir)) {
          done(null, dir);
        }
      }, delay + 100);
    });
  } catch (err) {
    return this._handleError(err);
  }
  return this;
};

Gaze.prototype._unpollFile = function(file) {
  if (this._pollers[file]) {
    fs.unwatchFile(file, this._pollers[file] );
    delete this._pollers[file];
  }
  return this;
};

Gaze.prototype._unpollDir = function(dir) {
  this._unpollFile(dir);
  for (var i = 0; i < this._watched[dir].length; i++) {
    this._unpollFile(this._watched[dir][i]);
  }
};

Gaze.prototype._pollFile = function(file, done) {
  var opts = { persistent: true, interval: this.options.interval };
  if (!this._pollers[file]) {
    this._pollers[file] = function(curr, prev) {
      done(null, file);
    };
    try {
      fs.watchFile(file, opts, this._pollers[file]);
    } catch (err) {
      return this._handleError(err);
    }
  }
  return this;
};

// Initialize the actual watch on `watched` files
Gaze.prototype._initWatched = function(done) {
  var self = this;
  var cwd = this.options.cwd || process.cwd();
  var curWatched = Object.keys(self._watched);
  helper.forEachSeries(curWatched, function(dir, next) {
    var files = self._watched[dir];
    // Triggered when a watched dir has an event
    self._watchDir(dir, function(event, dirpath) {
      var relDir = cwd === dir ? '.' : path.relative(cwd, dir);

      fs.readdir(dirpath, function(err, current) {
        if (err) { return self.emit('error', err); }
        if (!current) { return; }

        try {
          // append path.sep to directories so they match previous.
          current = current.map(function(curPath) {
            if (fs.existsSync(path.join(dir, curPath)) && fs.statSync(path.join(dir, curPath)).isDirectory()) {
              return curPath + path.sep;
            } else {
              return curPath;
            }
          });
        } catch (err) {
          // race condition-- sometimes the file no longer exists
        }

        // Get watched files for this dir
        var previous = self.relative(relDir);

        // If file was deleted
        previous.filter(function(file) {
          return current.indexOf(file) < 0;
        }).forEach(function(file) {
          if (!helper.isDir(file)) {
            var filepath = path.join(dir, file);
            self.remove(filepath);
            self.emit('deleted', filepath);
          }
        });

        // If file was added
        current.filter(function(file) {
          return previous.indexOf(file) < 0;
        }).forEach(function(file) {
          // Is it a matching pattern?
          var relFile = path.join(relDir, file);
          // Add to watch then emit event
          self._internalAdd(relFile, function() {
            self.emit('added', path.join(dir, file));
          });
        });

      });
    });

    // Watch for change/rename events on files
    files.forEach(function(file) {
      if (helper.isDir(file)) { return; }
      self._pollFile(file, function(err, filepath) {
        // Only emit changed if the file still exists
        // Prevents changed/deleted duplicate events
        if (fs.existsSync(filepath)) {
          self.emit('changed', filepath);
        }
      });
    });

    next();
  }, function() {

    // Return this instance of Gaze
    // delay before ready solves a lot of issues
    setTimeout(function() {
      self.emit('ready', self);
      done.call(self, null, self);
    }, delay + 100);

  });
};

// If an error, handle it here
Gaze.prototype._handleError = function(err) {
  if (err.code === 'EMFILE') {
    return this.emit('error', new Error('EMFILE: Too many opened files.'));
  }
  return this.emit('error', err);
};
<commit_msg>Avoid node v0.10 args must be strings errors.<commit_after>/*
 * gaze
 * https://github.com/shama/gaze
 *
 * Copyright (c) 2013 Kyle Robinson Young
 * Licensed under the MIT license.
 */

'use strict';

// libs
var util = require('util');
var EE = require('events').EventEmitter;
var fs = require('fs');
var path = require('path');
var globule = require('globule');
var helper = require('./helper');

// globals
var delay = 10;

// `Gaze` EventEmitter object to return in the callback
function Gaze(patterns, opts, done) {
  var self = this;
  EE.call(self);

  // If second arg is the callback
  if (typeof opts === 'function') {
    done = opts;
    opts = {};
  }

  // Default options
  opts = opts || {};
  opts.mark = true;
  opts.interval = opts.interval || 100;
  opts.debounceDelay = opts.debounceDelay || 500;
  opts.cwd = opts.cwd || process.cwd();
  this.options = opts;

  // Default done callback
  done = done || function() {};

  // Remember our watched dir:files
  this._watched = Object.create(null);

  // Store watchers
  this._watchers = Object.create(null);

  // Store watchFile listeners
  this._pollers = Object.create(null);

  // Store patterns
  this._patterns = [];

  // Cached events for debouncing
  this._cached = Object.create(null);

  // Set maxListeners
  if (this.options.maxListeners) {
    this.setMaxListeners(this.options.maxListeners);
    Gaze.super_.prototype.setMaxListeners(this.options.maxListeners);
    delete this.options.maxListeners;
  }

  // Initialize the watch on files
  if (patterns) {
    this.add(patterns, done);
  }

  return this;
}
util.inherits(Gaze, EE);

// Main entry point. Start watching and call done when setup
module.exports = function gaze(patterns, opts, done) {
  return new Gaze(patterns, opts, done);
};
module.exports.Gaze = Gaze;

// Override the emit function to emit `all` events
// and debounce on duplicate events per file
Gaze.prototype.emit = function() {
  var self = this;
  var args = arguments;

  var e = args[0];
  var filepath = args[1];
  var timeoutId;

  // If not added/deleted/changed/renamed then just emit the event
  if (e.slice(-2) !== 'ed') {
    Gaze.super_.prototype.emit.apply(self, args);
    return this;
  }

  // Detect rename event, if added and previous deleted is in the cache
  if (e === 'added') {
    Object.keys(this._cached).forEach(function(oldFile) {
      if (self._cached[oldFile].indexOf('deleted') !== -1) {
        args[0] = e = 'renamed';
        [].push.call(args, oldFile);
        delete self._cached[oldFile];
        return false;
      }
    });
  }

  // If cached doesnt exist, create a delay before running the next
  // then emit the event
  var cache = this._cached[filepath] || [];
  if (cache.indexOf(e) === -1) {
    helper.objectPush(self._cached, filepath, e);
    clearTimeout(timeoutId);
    timeoutId = setTimeout(function() {
      delete self._cached[filepath];
    }, this.options.debounceDelay);
    // Emit the event and `all` event
    Gaze.super_.prototype.emit.apply(self, args);
    Gaze.super_.prototype.emit.apply(self, ['all', e].concat([].slice.call(args, 1)));
  }

  return this;
};

// Close watchers
Gaze.prototype.close = function(_reset) {
  var self = this;
  _reset = _reset === false ? false : true;
  Object.keys(self._watchers).forEach(function(file) {
    self._watchers[file].close();
  });
  self._watchers = Object.create(null);
  Object.keys(this._watched).forEach(function(dir) {
    self._unpollDir(dir);
  });
  if (_reset) {
    self._watched = Object.create(null);
    setTimeout(function() {
      self.emit('end');
      self.removeAllListeners();
    }, delay + 100);
  }
  return self;
};

// Add file patterns to be watched
Gaze.prototype.add = function(files, done) {
  if (typeof files === 'string') { files = [files]; }
  this._patterns = helper.unique.apply(null, [this._patterns, files]);
  files = globule.find(this._patterns, this.options);
  this._addToWatched(files);
  this.close(false);
  this._initWatched(done);
};

// Dont increment patterns and dont call done if nothing added
Gaze.prototype._internalAdd = function(file, done) {
  var files = [];
  if (helper.isDir(file)) {
    files = globule.find(this._patterns, this.options);
  } else {
    if (globule.isMatch(this._patterns, file, this.options)) {
      files = [file];
    }
  }
  if (files.length > 0) {
    this._addToWatched(files);
    this.close(false);
    this._initWatched(done);
  }
};

// Remove file/dir from `watched`
Gaze.prototype.remove = function(file) {
  var self = this;
  if (this._watched[file]) {
    // is dir, remove all files
    this._unpollDir(file);
    delete this._watched[file];
  } else {
    // is a file, find and remove
    Object.keys(this._watched).forEach(function(dir) {
      var index = self._watched[dir].indexOf(file);
      if (index) {
        self._unpollFile(file);
        delete self._watched[dir][index];
        return false;
      }
    });
  }
  if (this._watchers[file]) {
    this._watchers[file].close();
  }
  return this;
};

// Return watched files
Gaze.prototype.watched = function() {
  return this._watched;
};

// Returns `watched` files with relative paths to process.cwd()
Gaze.prototype.relative = function(dir, unixify) {
  var self = this;
  var relative = Object.create(null);
  var relDir, relFile, unixRelDir;
  var cwd = this.options.cwd || process.cwd();
  if (dir === '') { dir = '.'; }
  dir = helper.markDir(dir);
  unixify = unixify || false;
  Object.keys(this._watched).forEach(function(dir) {
    relDir = path.relative(cwd, dir) + path.sep;
    if (relDir === path.sep) { relDir = '.'; }
    unixRelDir = unixify ? helper.unixifyPathSep(relDir) : relDir;
    relative[unixRelDir] = self._watched[dir].map(function(file) {
      relFile = path.relative(path.join(cwd, relDir) || '', file || '');
      if (helper.isDir(file)) {
        relFile = helper.markDir(relFile);
      }
      if (unixify) {
        relFile = helper.unixifyPathSep(relFile);
      }
      return relFile;
    });
  });
  if (dir && unixify) {
    dir = helper.unixifyPathSep(dir);
  }
  return dir ? relative[dir] || [] : relative;
};

// Adds files and dirs to watched
Gaze.prototype._addToWatched = function(files) {
  for (var i = 0; i < files.length; i++) {
    var file = files[i];
    var filepath = path.resolve(this.options.cwd, file);

    var dirname = (helper.isDir(file)) ? filepath : path.dirname(filepath);
    dirname = helper.markDir(dirname);

    if (file.slice(-1) === '/') { filepath += path.sep; }
    helper.objectPush(this._watched, path.dirname(filepath) + path.sep, filepath);

    // add folders into the mix
    var readdir = fs.readdirSync(dirname);
    for (var j = 0; j < readdir.length; j++) {
      var dirfile = path.join(dirname, readdir[j]);
      if (fs.statSync(dirfile).isDirectory()) {
        helper.objectPush(this._watched, dirname, dirfile + path.sep);
      }
    }
  }
  return this;
};

Gaze.prototype._watchDir = function(dir, done) {
  try {
    this._watchers[dir] = fs.watch(dir, function(event) {
      // race condition. Let's give the fs a little time to settle down. so we
      // don't fire events on non existent files.
      setTimeout(function() {
        if (fs.existsSync(dir)) {
          done(null, dir);
        }
      }, delay + 100);
    });
  } catch (err) {
    return this._handleError(err);
  }
  return this;
};

Gaze.prototype._unpollFile = function(file) {
  if (this._pollers[file]) {
    fs.unwatchFile(file, this._pollers[file] );
    delete this._pollers[file];
  }
  return this;
};

Gaze.prototype._unpollDir = function(dir) {
  this._unpollFile(dir);
  for (var i = 0; i < this._watched[dir].length; i++) {
    this._unpollFile(this._watched[dir][i]);
  }
};

Gaze.prototype._pollFile = function(file, done) {
  var opts = { persistent: true, interval: this.options.interval };
  if (!this._pollers[file]) {
    this._pollers[file] = function(curr, prev) {
      done(null, file);
    };
    try {
      fs.watchFile(file, opts, this._pollers[file]);
    } catch (err) {
      return this._handleError(err);
    }
  }
  return this;
};

// Initialize the actual watch on `watched` files
Gaze.prototype._initWatched = function(done) {
  var self = this;
  var cwd = this.options.cwd || process.cwd();
  var curWatched = Object.keys(self._watched);
  helper.forEachSeries(curWatched, function(dir, next) {
    dir = dir || '';
    var files = self._watched[dir];
    // Triggered when a watched dir has an event
    self._watchDir(dir, function(event, dirpath) {
      var relDir = cwd === dir ? '.' : path.relative(cwd, dir);
      relDir = relDir || '';

      fs.readdir(dirpath, function(err, current) {
        if (err) { return self.emit('error', err); }
        if (!current) { return; }

        try {
          // append path.sep to directories so they match previous.
          current = current.map(function(curPath) {
            if (fs.existsSync(path.join(dir, curPath)) && fs.statSync(path.join(dir, curPath)).isDirectory()) {
              return curPath + path.sep;
            } else {
              return curPath;
            }
          });
        } catch (err) {
          // race condition-- sometimes the file no longer exists
        }

        // Get watched files for this dir
        var previous = self.relative(relDir);

        // If file was deleted
        previous.filter(function(file) {
          return current.indexOf(file) < 0;
        }).forEach(function(file) {
          if (!helper.isDir(file)) {
            var filepath = path.join(dir, file);
            self.remove(filepath);
            self.emit('deleted', filepath);
          }
        });

        // If file was added
        current.filter(function(file) {
          return previous.indexOf(file) < 0;
        }).forEach(function(file) {
          // Is it a matching pattern?
          var relFile = path.join(relDir, file);
          // Add to watch then emit event
          self._internalAdd(relFile, function() {
            self.emit('added', path.join(dir, file));
          });
        });

      });
    });

    // Watch for change/rename events on files
    files.forEach(function(file) {
      if (helper.isDir(file)) { return; }
      self._pollFile(file, function(err, filepath) {
        // Only emit changed if the file still exists
        // Prevents changed/deleted duplicate events
        if (fs.existsSync(filepath)) {
          self.emit('changed', filepath);
        }
      });
    });

    next();
  }, function() {

    // Return this instance of Gaze
    // delay before ready solves a lot of issues
    setTimeout(function() {
      self.emit('ready', self);
      done.call(self, null, self);
    }, delay + 100);

  });
};

// If an error, handle it here
Gaze.prototype._handleError = function(err) {
  if (err.code === 'EMFILE') {
    return this.emit('error', new Error('EMFILE: Too many opened files.'));
  }
  return this.emit('error', err);
};
<|endoftext|>


<commit_before>/*---------------------------------------------------------------
  :: sails-rest
  -> adapter
---------------------------------------------------------------*/

var async   = require('async'),
    restify = require('restify'),
    url     = require('url'),
    _       = require('lodash');

module.exports = (function(){
  "use strict";

  var collections = {};

  // Private functions
  /**
   * Format result object according to schema
   * @param result result object
   * @param collectionName name of collection the result object belongs to
   * @returns {*}
   */
  function formatResult(result, collectionName, config){
    if (_.isFunction(config.beforeFormatResult)) {
      result = config.beforeFormatResult(result);
    }

    _.each(collections[collectionName].definition, function(def, key) {
      if (def.type.match(/date/i)) {
        result[key] = new Date(result[key] ? result[key] : null);
      }
    });

    if (_.isFunction(config.afterFormatResult)) {
      result = config.afterFormatResult(result);
    }

    return result;
  }

  /**
   * Format results according to schema
   * @param results array of result objects (model instances)
   * @param collectionName name of collection the result object belongs to
   * @returns {*}
   */
  function formatResults(results, collectionName, config){
    if (_.isFunction(config.beforeFormatResults)) {
      results = config.beforeFormatResults(results);
    }

    results.forEach(function(result) {
      formatResult(result, collectionName, config);
    });

    if (_.isFunction(config.afterFormatResults)) {
      results = config.afterFormatResults(results);
    }

    return results;
  }

  /**
   * Ensure results are contained in an array. Resolves variants in API responses such as `results` or `objects` instead of `[.....]`
   * @param data response data to format as results array
   * @param collectionName name of collection the result object belongs to
   * @returns {*}
   */
  function getResultsAsCollection(data, collectionName, config){
    var d = (data.objects || data.results || data),
        a = _.isArray(d) ? d : [d];

    return formatResults(a, collectionName, config);
  }

  /**
   * Makes a REST request via restify
   * @param collectionName name of collection the result object belongs to
   * @param methodName name of CRUD method being used
   * @param cb callback from method
   * @param options options from method
   * @param values values from method
   * @returns {*}
   */
  function makeRequest(collectionName, methodName, cb, options, values) {
    var r = null,
        opt = null,
        cache = collections[collectionName].cache,
        config = _.cloneDeep(collections[collectionName].config),
        connection = collections[collectionName].connection,
        restMethod = config.methods[methodName];

    // Override config settings from options if available
    if (options && _.isPlainObject(options)) {
      _.each(config, function(val, key) {
        if (_.has(options, key)) {
          config[key] = options[key];
        }
      });
    }

    var pathname = config.pathname + '/' + config.resource + (config.action ? '/' + config.action : '');

    if (options && options.where) {
      // Add id to pathname if provided
      if (options.where.id) {
        pathname += '/'+ options.where.id;
        delete options.where.id;
      }
      else if (methodName === 'destroy' || methodName == 'update') {
        // Find all and make new request for each.
        makeRequest(collectionName, 'find', function(error, results) {
          if (error) {
            cb(error);
          }
          else {
            _.each(results, function(result, i) {
              options = {
                where: {
                  id: result.id
                }
              };

              makeRequest(collectionName, methodName, (i + 1) === results.length ? cb : function(){}, options, values);
            });
          }
        }, options);

        return;
      }

      // Add where statement as query parameters if requesting via GET
      if (restMethod === 'get') {
        _.extend(config.query, options.where);
      }
      // Set opt if additional where statements are available
      else if (_.size(options.where)) {
        opt = options.where;
      }
      else {
        delete options.where;
      }
    }

    if (!opt && values) {
      opt = values;

      if (options) {
        opt = _.extend(options, opt);
      }
    }

    // Add pathname to connection
    _.extend(config, {pathname: pathname});

    // Format URI
    var uri = url.format(config);

    // Retrieve data from the cache
    if (methodName === 'find') {
      r = cache && cache.engine.get(uri);
    }

    if (r) {
      cb(null, r);
    }
    else if (_.isFunction(connection[restMethod])) {
      var path = uri.replace(connection.url.href, '/');

      var callback = function(err, req, res, obj) {
        if (err) {
          cb(err);
        }
        else {
          if (methodName === 'find') {
            r = getResultsAsCollection(obj, collectionName, config);

            if (cache) {
              cache.engine.set(uri, r);
            }
          }
          else {
            r = formatResult(obj, collectionName, config);

            if (cache) {
              cache.engine.del(uri);
            }
          }

          cb(null, r);
        }
      };

      // Make request via restify
      if (opt) {
        connection[restMethod](path, opt, callback);
      }
      else {
        connection[restMethod](path, callback);
      }
    }
    else {
      cb(new Error('Invalid REST method: ' + restMethod));
    }

    return false;
  }

  // Adapter
  var adapter = {

    syncable: false,

    defaults: {
      type: 'json',
      host: 'localhost',
      port: 80,
      protocol: 'http',
      pathname: '',
      resource: null,
      action: null,
      query: {},
      methods: {
        create: 'post',
        find: 'get',
        update: 'put',
        destroy: 'del'
      },
      beforeFormatResult: null,
      afterFormatResult: null,
      beforeFormatResults: null,
      afterFormatResults: null
    },

    registerCollection: function(collection, cb) {
      var config, clientMethod, instance;

      config       = _.extend({}, collection.defaults, collection.config);
      clientMethod = 'create' + config.type.substr(0, 1).toUpperCase() + config.type.substr(1).toLowerCase() + 'Client';

      if (!_.isFunction(restify[clientMethod])) {
        throw new Error('Invalid type provided');
      }

      instance = {
        config: {
          protocol: config.protocol,
          hostname: config.host,
          port: config.port,
          pathname: config.pathname,
          headers: config.headers,
          query: config.query,
          resource: config.resource || collection.identity,
          action: config.action,
          methods: _.extend({}, collection.defaults.methods, config.methods),
          beforeFormatResult: config.beforeFormatResult,
          afterFormatResult: config.afterFormatResult,
          beforeFormatResults: config.beforeFormatResults,
          afterFormatResults: config.afterFormatResults
        },

        connection: restify[clientMethod]({
          url: url.format({
            protocol: config.protocol,
            hostname: config.host,
            port: config.port
          })
        }),

        definition: collection.definition
      };

      if (collection.config.basicAuth) {
        instance.connection.basicAuth(config.basicAuth.username, config.basicAuth.password);
      }

      if (collection.config.cache) {
        instance.cache = collection.config.cache;
      }

      collections[collection.identity] = instance;

      cb();
    },

    create: function(collectionName, values, cb) {
      makeRequest(collectionName, 'create', cb, null, values);
    },

    find: function(collectionName, options, cb){
      makeRequest(collectionName, 'find', cb, options);
    },

    update: function(collectionName, options, values, cb) {
      makeRequest(collectionName, 'update', cb, options, values);
    },

    destroy: function(collectionName, options, cb) {
      makeRequest(collectionName, 'destroy', cb, options);
    },

    drop: function(collectionName, cb) {
      cb();
    },

    describe: function(collectionName, cb) {
      cb(null, collections[collectionName].definition);
    }
  };

  return adapter;
}());
<commit_msg>Modifying registerCollection method to be compatible with both the 0.9.x and 0.10.x branches of sails core.<commit_after>/*---------------------------------------------------------------
  :: sails-rest
  -> adapter
---------------------------------------------------------------*/

var async   = require('async'),
    restify = require('restify'),
    url     = require('url'),
    _       = require('lodash');

module.exports = (function(){
  "use strict";

  var collections = {};

  // Private functions
  /**
   * Format result object according to schema
   * @param result result object
   * @param collectionName name of collection the result object belongs to
   * @returns {*}
   */
  function formatResult(result, collectionName, config){
    if (_.isFunction(config.beforeFormatResult)) {
      result = config.beforeFormatResult(result);
    }

    _.each(collections[collectionName].definition, function(def, key) {
      if (def.type.match(/date/i)) {
        result[key] = new Date(result[key] ? result[key] : null);
      }
    });

    if (_.isFunction(config.afterFormatResult)) {
      result = config.afterFormatResult(result);
    }

    return result;
  }

  /**
   * Format results according to schema
   * @param results array of result objects (model instances)
   * @param collectionName name of collection the result object belongs to
   * @returns {*}
   */
  function formatResults(results, collectionName, config){
    if (_.isFunction(config.beforeFormatResults)) {
      results = config.beforeFormatResults(results);
    }

    results.forEach(function(result) {
      formatResult(result, collectionName, config);
    });

    if (_.isFunction(config.afterFormatResults)) {
      results = config.afterFormatResults(results);
    }

    return results;
  }

  /**
   * Ensure results are contained in an array. Resolves variants in API responses such as `results` or `objects` instead of `[.....]`
   * @param data response data to format as results array
   * @param collectionName name of collection the result object belongs to
   * @returns {*}
   */
  function getResultsAsCollection(data, collectionName, config){
    var d = (data.objects || data.results || data),
        a = _.isArray(d) ? d : [d];

    return formatResults(a, collectionName, config);
  }

  /**
   * Makes a REST request via restify
   * @param collectionName name of collection the result object belongs to
   * @param methodName name of CRUD method being used
   * @param cb callback from method
   * @param options options from method
   * @param values values from method
   * @returns {*}
   */
  function makeRequest(collectionName, methodName, cb, options, values) {
    var r = null,
        opt = null,
        cache = collections[collectionName].cache,
        config = _.cloneDeep(collections[collectionName].config),
        connection = collections[collectionName].connection,
        restMethod = config.methods[methodName];

    // Override config settings from options if available
    if (options && _.isPlainObject(options)) {
      _.each(config, function(val, key) {
        if (_.has(options, key)) {
          config[key] = options[key];
        }
      });
    }

    var pathname = config.pathname + '/' + config.resource + (config.action ? '/' + config.action : '');

    if (options && options.where) {
      // Add id to pathname if provided
      if (options.where.id) {
        pathname += '/'+ options.where.id;
        delete options.where.id;
      }
      else if (methodName === 'destroy' || methodName == 'update') {
        // Find all and make new request for each.
        makeRequest(collectionName, 'find', function(error, results) {
          if (error) {
            cb(error);
          }
          else {
            _.each(results, function(result, i) {
              options = {
                where: {
                  id: result.id
                }
              };

              makeRequest(collectionName, methodName, (i + 1) === results.length ? cb : function(){}, options, values);
            });
          }
        }, options);

        return;
      }

      // Add where statement as query parameters if requesting via GET
      if (restMethod === 'get') {
        _.extend(config.query, options.where);
      }
      // Set opt if additional where statements are available
      else if (_.size(options.where)) {
        opt = options.where;
      }
      else {
        delete options.where;
      }
    }

    if (!opt && values) {
      opt = values;

      if (options) {
        opt = _.extend(options, opt);
      }
    }

    // Add pathname to connection
    _.extend(config, {pathname: pathname});

    // Format URI
    var uri = url.format(config);

    // Retrieve data from the cache
    if (methodName === 'find') {
      r = cache && cache.engine.get(uri);
    }

    if (r) {
      cb(null, r);
    }
    else if (_.isFunction(connection[restMethod])) {
      var path = uri.replace(connection.url.href, '/');

      var callback = function(err, req, res, obj) {
        if (err) {
          cb(err);
        }
        else {
          if (methodName === 'find') {
            r = getResultsAsCollection(obj, collectionName, config);

            if (cache) {
              cache.engine.set(uri, r);
            }
          }
          else {
            r = formatResult(obj, collectionName, config);

            if (cache) {
              cache.engine.del(uri);
            }
          }

          cb(null, r);
        }
      };

      // Make request via restify
      if (opt) {
        connection[restMethod](path, opt, callback);
      }
      else {
        connection[restMethod](path, callback);
      }
    }
    else {
      cb(new Error('Invalid REST method: ' + restMethod));
    }

    return false;
  }

  // Adapter
  var adapter = {

    syncable: false,

    defaults: {
      type: 'json',
      host: 'localhost',
      port: 80,
      protocol: 'http',
      pathname: '',
      resource: null,
      action: null,
      query: {},
      methods: {
        create: 'post',
        find: 'get',
        update: 'put',
        destroy: 'del'
      },
      beforeFormatResult: null,
      afterFormatResult: null,
      beforeFormatResults: null,
      afterFormatResults: null
    },

    registerCollection: function(collection, cb) {
      var config, clientMethod, instance;

      config       = collection.defaults ? _.extend({}, collection.defaults, collection.config) : collection.config;
      clientMethod = 'create' + config.type.substr(0, 1).toUpperCase() + config.type.substr(1).toLowerCase() + 'Client';

      if (!_.isFunction(restify[clientMethod])) {
        throw new Error('Invalid type provided');
      }

      instance = {
        config: {
          protocol: config.protocol,
          hostname: config.host,
          port: config.port,
          pathname: config.pathname,
          headers: config.headers,
          query: config.query,
          resource: config.resource || collection.identity,
          action: config.action,
          methods: collection.defaults ? _.extend({}, collection.defaults.methods, config.methods) : config.methods,
          beforeFormatResult: config.beforeFormatResult,
          afterFormatResult: config.afterFormatResult,
          beforeFormatResults: config.beforeFormatResults,
          afterFormatResults: config.afterFormatResults
        },

        connection: restify[clientMethod]({
          url: url.format({
            protocol: config.protocol,
            hostname: config.host,
            port: config.port
          })
        }),

        definition: collection.definition
      };

      if (collection.config.basicAuth) {
        instance.connection.basicAuth(config.basicAuth.username, config.basicAuth.password);
      }

      if (collection.config.cache) {
        instance.cache = collection.config.cache;
      }

      collections[collection.identity] = instance;

      cb();
    },

    create: function(collectionName, values, cb) {
      makeRequest(collectionName, 'create', cb, null, values);
    },

    find: function(collectionName, options, cb){
      makeRequest(collectionName, 'find', cb, options);
    },

    update: function(collectionName, options, values, cb) {
      makeRequest(collectionName, 'update', cb, options, values);
    },

    destroy: function(collectionName, options, cb) {
      makeRequest(collectionName, 'destroy', cb, options);
    },

    drop: function(collectionName, cb) {
      cb();
    },

    describe: function(collectionName, cb) {
      cb(null, collections[collectionName].definition);
    }
  };

  return adapter;
}());
<|endoftext|>


<commit_before>var PathModule = require('path'),
    events = require('events'),
    constants = require('constants');

var CONC = 5;
function setMaxStatsAtOnce(n) { CONC = n; }

// Wildcard directory listing. There is no way that a client should
// use multiple wildcards in an FTP command, or wildcards which
// are part of a directory name rather than a filename (unless
// they're identifying a unique directory to be listed).
// So this can be pretty simple.

function statList(fsm, list, callback) {
    if (list.length == 0)
        return callback(null, []);

    var stats = [];
    var i = 0, j = 0;
    for (i = 0; i < list.length && i < CONC; ++i)
        handleFile(i);
    j = --i;

    var erroredOut = false;
    function handleFile(ii) {
        if (erroredOut)
            return;
        if (i >= list.length)
            return i == list.length + j ? finished() : null;

        fsm.stat(list[i], function (err, st) {
            if (err) {
                erroredOut = true;
                callback(err);
            }
            else {
                stats.push({ name: PathModule.basename(list[ii]),
                             stats: st });
                handleFile(++i);
            }
        });
    }

    function finished() {
        callback(null, stats);
    }
}

function matchPattern(pattern, string) {
    var pi = 0, si = 0;
    for (; si < string.length && pi < pattern.length; ++si) {
        var c = string.charAt(si);
        var pc = pattern.charAt(pi);

        if (pc == '*') {
            if (pi + 1 == pattern.length) {
                ;
            }
            else if (pattern.charAt(pi+1) == '*') {
                --si;
                ++pi;
            }
            else if (pattern.charAt(pi+1) == '?') {
                ++pi;
            }
            else if (si < string.length-1 && pattern.charAt(pi+1) == string.charAt(si)) {
                pi += 2;
            }
        }
        else if (pc == '?') {
            ++pi;
        }
        else if (pc == c) {
            ++pi;
        }
        else {
            return false;
        }
    }

    return (pi == pattern.length || (pi == pattern.length - 1 && pattern.charAt(pi) == '*')) &&
           si == string.length;
}
/*console.log(matchPattern("foo*", "foooxx") == true);
console.log(matchPattern("foo*", "foo") == true);
console.log(matchPattern("foo*", "fo") == false);
console.log(matchPattern("foo*", "") == false);
console.log(matchPattern("*foo*", "foo") == true);
console.log(matchPattern("*foo*", "foio") == false);
console.log(matchPattern("*.txt", ".txt") == true);
console.log(matchPattern("*.txt", "xxx.txt") == true);
console.log(matchPattern("*.txt", "xxx.txty") == false);
console.log(matchPattern("*.txt?", "xxx.txty") == true);
console.log(matchPattern("?a?b?c", "1a1b1c") == true);
console.log(matchPattern("?a?b?c*.foo", "1a1b1cblah.foo") == true);
console.log(matchPattern("?a?b?c*.foo", "1a1b1cblah.foob") == false);
console.log(matchPattern("???", "xyz") == true);
console.log(matchPattern("???", "xyzq") == false);
console.log(matchPattern("?", "x") == true);
console.log(matchPattern("?", "xx") == false);
console.log(matchPattern("***", "xx") == true);
console.log(matchPattern("***.foo", "xx.foo") == true);
console.log(matchPattern("***.foo", "xx.foob") == false);
console.log(matchPattern("*", "") == true);
console.log(matchPattern("?", "") == false);*/

function glob(path, fsm, callback, noWildcards) {
    if (! callback) {
        callback = fsm;
        fsm = require('fs');
    }

    var s = path.indexOf('*');
    var q = path.indexOf('?');
    var w;
    for (w = 0; !noWildcards && w < path.length && path.charAt(w) != '*' && path.charAt(w) != '?'; ++w);

    if (w == path.length) { // There are no wildcards.
        fsm.readdir(path, function (err, contents) {
            if (err) {
                if (err.errno = constants.ENOTDIR) {
                    statList(fsm, [path], function (err, list) {
                        if (err)
                            return callback(err);
                        if (list.length != 1)
                            throw new Error("Internal error in glob.js");
                        callback(null, list);
                    });
                }
                else if (err.errno = constants.ENOENT) {
                    callback(null, []);
                }
                else {
                    callback(err);
                }
            }
            else {
                statList(fsm, contents.map(function (p) { return PathModule.join(path, p); }), function (err, list) {
                    if (err)
                        callback(err);
                    else
                        callback(null, list);
                });
            }
        });

        return;
    }
    else {
        // Check that there is no '/' after the first wildcard.

        var i;
        for (i = w; i < path.length; ++i) {
            if (path.charAt(i) == '/')
                return callback(null, []);
        }

        var base = "", pattern;
        for (i = w; i >= 0; --i) {
            if (path.charAt(i) == '/') {
                base = path.substr(0, i+1)
                break;
            }
        }
        pattern = path.substr(i == 0 ? 0 : i + 1);

        // Remove any leading/trailing slashes which might still
        // be present if the path containts multiple slashes.
        for (i = 0; i < pattern.length && pattern.charAt(i) == '/'; ++i);
        if (i > 0)
            pattern = pattern.substr(i);
        for (i = base.length-1; i > 0 && base.charAt(i) == '/'; --i);
        if (i != base.length-1)
            base = base.substr(0, i+1);

        // We now have the base path in 'base' (possibly the empty string)
        // and the wildcard filename pattern in 'pattern'.

        readTheDir(false);
        function readTheDir(listingSingleDir) {
            fsm.readdir(base, function (err, contents) {
                if (err) {
                    if (err.errno == constants.ENOTDIR || err.errno == constants.ENOENT) {
                        callback(null, []);
                    }
                    else {
                        callback(err);
                    }
                }
                else {
                    var matches;
                    if (! listingSingleDir)
                        matches = contents.filter(function (n) { return matchPattern(pattern, n); });
                    else
                        matches = contents;

                    // Special case. If we have exactly one match, and it's a directory, then list
                    // the contents of that directory. (There's no reason why anyone should want
                    // to identify mutliple directories using wildcards and then list all of their
                    // contents over FTP!)
                    if (!listingSingleDir && matches.length == 1) {
                        var dir = PathModule.join(base, matches[0]);
                        fsm.stat(dir, function (err, st) {
                            if (err)
                                return callback(err);

                            if (! st.isDirectory()) {
                                doTheNormalThing();
                            }
                            else {
                                base = dir;
                                readTheDir(/*listingSingleDir=*/true);
                            }
                        });
                    }
                    else {
                        doTheNormalThing();
                    }

                    function doTheNormalThing() {
                        statList(fsm, matches.map(function (p) { return PathModule.join(base, p); }), function (err, list) {
                            if (err)
                                callback(err);
                            else
                                callback(null, list);
                        });
                    }
                }
            });
        }
    }
}

exports.glob = glob;
exports.matchPattern = matchPattern;
exports.setMaxStatsAtOnce = setMaxStatsAtOnce;
<commit_msg>Presuming comparison not assignment.<commit_after>var PathModule = require('path'),
    events = require('events'),
    constants = require('constants');

var CONC = 5;
function setMaxStatsAtOnce(n) { CONC = n; }

// Wildcard directory listing. There is no way that a client should
// use multiple wildcards in an FTP command, or wildcards which
// are part of a directory name rather than a filename (unless
// they're identifying a unique directory to be listed).
// So this can be pretty simple.

function statList(fsm, list, callback) {
    if (list.length == 0)
        return callback(null, []);

    var stats = [];
    var i = 0, j = 0;
    for (i = 0; i < list.length && i < CONC; ++i)
        handleFile(i);
    j = --i;

    var erroredOut = false;
    function handleFile(ii) {
        if (erroredOut)
            return;
        if (i >= list.length)
            return i == list.length + j ? finished() : null;

        fsm.stat(list[i], function (err, st) {
            if (err) {
                erroredOut = true;
                callback(err);
            }
            else {
                stats.push({ name: PathModule.basename(list[ii]),
                             stats: st });
                handleFile(++i);
            }
        });
    }

    function finished() {
        callback(null, stats);
    }
}

function matchPattern(pattern, string) {
    var pi = 0, si = 0;
    for (; si < string.length && pi < pattern.length; ++si) {
        var c = string.charAt(si);
        var pc = pattern.charAt(pi);

        if (pc == '*') {
            if (pi + 1 == pattern.length) {
                ;
            }
            else if (pattern.charAt(pi+1) == '*') {
                --si;
                ++pi;
            }
            else if (pattern.charAt(pi+1) == '?') {
                ++pi;
            }
            else if (si < string.length-1 && pattern.charAt(pi+1) == string.charAt(si)) {
                pi += 2;
            }
        }
        else if (pc == '?') {
            ++pi;
        }
        else if (pc == c) {
            ++pi;
        }
        else {
            return false;
        }
    }

    return (pi == pattern.length || (pi == pattern.length - 1 && pattern.charAt(pi) == '*')) &&
           si == string.length;
}
/*console.log(matchPattern("foo*", "foooxx") == true);
console.log(matchPattern("foo*", "foo") == true);
console.log(matchPattern("foo*", "fo") == false);
console.log(matchPattern("foo*", "") == false);
console.log(matchPattern("*foo*", "foo") == true);
console.log(matchPattern("*foo*", "foio") == false);
console.log(matchPattern("*.txt", ".txt") == true);
console.log(matchPattern("*.txt", "xxx.txt") == true);
console.log(matchPattern("*.txt", "xxx.txty") == false);
console.log(matchPattern("*.txt?", "xxx.txty") == true);
console.log(matchPattern("?a?b?c", "1a1b1c") == true);
console.log(matchPattern("?a?b?c*.foo", "1a1b1cblah.foo") == true);
console.log(matchPattern("?a?b?c*.foo", "1a1b1cblah.foob") == false);
console.log(matchPattern("???", "xyz") == true);
console.log(matchPattern("???", "xyzq") == false);
console.log(matchPattern("?", "x") == true);
console.log(matchPattern("?", "xx") == false);
console.log(matchPattern("***", "xx") == true);
console.log(matchPattern("***.foo", "xx.foo") == true);
console.log(matchPattern("***.foo", "xx.foob") == false);
console.log(matchPattern("*", "") == true);
console.log(matchPattern("?", "") == false);*/

function glob(path, fsm, callback, noWildcards) {
    if (! callback) {
        callback = fsm;
        fsm = require('fs');
    }

    var s = path.indexOf('*');
    var q = path.indexOf('?');
    var w;
    for (w = 0; !noWildcards && w < path.length && path.charAt(w) != '*' && path.charAt(w) != '?'; ++w);

    if (w == path.length) { // There are no wildcards.
        fsm.readdir(path, function (err, contents) {
            if (err) {
                if (err.errno == constants.ENOTDIR) {
                    statList(fsm, [path], function (err, list) {
                        if (err)
                            return callback(err);
                        if (list.length != 1)
                            throw new Error("Internal error in glob.js");
                        callback(null, list);
                    });
                }
                else if (err.errno == constants.ENOENT) {
                    callback(null, []);
                }
                else {
                    callback(err);
                }
            }
            else {
                statList(fsm, contents.map(function (p) { return PathModule.join(path, p); }), function (err, list) {
                    if (err)
                        callback(err);
                    else
                        callback(null, list);
                });
            }
        });

        return;
    }
    else {
        // Check that there is no '/' after the first wildcard.

        var i;
        for (i = w; i < path.length; ++i) {
            if (path.charAt(i) == '/')
                return callback(null, []);
        }

        var base = "", pattern;
        for (i = w; i >= 0; --i) {
            if (path.charAt(i) == '/') {
                base = path.substr(0, i+1)
                break;
            }
        }
        pattern = path.substr(i == 0 ? 0 : i + 1);

        // Remove any leading/trailing slashes which might still
        // be present if the path containts multiple slashes.
        for (i = 0; i < pattern.length && pattern.charAt(i) == '/'; ++i);
        if (i > 0)
            pattern = pattern.substr(i);
        for (i = base.length-1; i > 0 && base.charAt(i) == '/'; --i);
        if (i != base.length-1)
            base = base.substr(0, i+1);

        // We now have the base path in 'base' (possibly the empty string)
        // and the wildcard filename pattern in 'pattern'.

        readTheDir(false);
        function readTheDir(listingSingleDir) {
            fsm.readdir(base, function (err, contents) {
                if (err) {
                    if (err.errno == constants.ENOTDIR || err.errno == constants.ENOENT) {
                        callback(null, []);
                    }
                    else {
                        callback(err);
                    }
                }
                else {
                    var matches;
                    if (! listingSingleDir)
                        matches = contents.filter(function (n) { return matchPattern(pattern, n); });
                    else
                        matches = contents;

                    // Special case. If we have exactly one match, and it's a directory, then list
                    // the contents of that directory. (There's no reason why anyone should want
                    // to identify mutliple directories using wildcards and then list all of their
                    // contents over FTP!)
                    if (!listingSingleDir && matches.length == 1) {
                        var dir = PathModule.join(base, matches[0]);
                        fsm.stat(dir, function (err, st) {
                            if (err)
                                return callback(err);

                            if (! st.isDirectory()) {
                                doTheNormalThing();
                            }
                            else {
                                base = dir;
                                readTheDir(/*listingSingleDir=*/true);
                            }
                        });
                    }
                    else {
                        doTheNormalThing();
                    }

                    function doTheNormalThing() {
                        statList(fsm, matches.map(function (p) { return PathModule.join(base, p); }), function (err, list) {
                            if (err)
                                callback(err);
                            else
                                callback(null, list);
                        });
                    }
                }
            });
        }
    }
}

exports.glob = glob;
exports.matchPattern = matchPattern;
exports.setMaxStatsAtOnce = setMaxStatsAtOnce;
<|endoftext|>


<commit_before>/*
 * glob-whatev
 * http://github.com/cowboy/node-glob-whatev
 *
 * Copyright (c) 2012 "Cowboy" Ben Alman
 * Licensed under the MIT license.
 * http://benalman.com/about/license/
 */

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

// Export minimatch, in case something else needs direct access to it.
exports.minimatch = require('minimatch');

// Windows uses \ instead of / for path separators.
var win32 = process.platform === 'win32';
var pathSeparator = win32 ? '\\' : '/';
var stripWildcard = /[*?{+(].*$/;
var stripNonpath = win32 ? /[^\/\\]*$/ : /[^\/]*$/;

// On Windows, convert all \ to /.
function normalize(filepath) {
  return win32 ? filepath.replace(/\\/g, '/') : filepath;
}

// A very simple, not-at-all-efficient, synchronous file globbing util.
exports.glob = function(pattern, options) {
  if (!options) { options = {}; }
  var cwd = options.cwd || process.cwd();
  // The current absolute working directory.
  var base = normalize(path.join(cwd, pathSeparator));
  // The passed pattern, resolved to an absolute path.
  var absPattern = normalize(path.resolve(base, pattern));
  // Since path.resolve strips off trailing '/', add it back if necessary.
  if (/\/$/.test(pattern)) { absPattern += '/'; }
  // Was pattern-as-specified already absolute?
  var wasAbsolute = normalize(pattern) === absPattern;
  // Instead of recursing from the base looking for files, start recursing at
  // the farthest possible subdirectory that doesn't contain any kind of
  // wildcard characters. I may have missed one, so let me know if I have!
  var betterBase = absPattern.replace(stripWildcard, '').replace(stripNonpath, '');
  // Now that we've got a better base, we need a better pattern.
  var betterPattern = absPattern.slice(betterBase.length);
  // Don't recurse if we don't have to. Limit max depth.
  var maxDepth = betterPattern.indexOf('**') >= 0 ? (options.maxDepth || 99)
    : betterPattern.split('/').length;
  // If minimatch 'matchBase' option is true, and the pattern doesn't contain
  // ** or /, use the pattern as-specified and recurse to max.
  if (options.matchBase && !/\*\*|\//.test(pattern)) {
    maxDepth = options.maxDepth || 99;
    absPattern = pattern;
  }
  // Iterate over all sub-files and directories in a brute-force and not-at-all
  // efficient way.
  var filepaths = [];
  (function recurse(dirpath, depth) {
    // Abort if, for whatever reason, dirpath doesn't exist.
    if (!path.existsSync(dirpath)) { return; }
    // Read directory contents.
    fs.readdirSync(dirpath).forEach(function(filepath) {
      var stat;
      // Make relative path absolute.
      filepath = path.join(dirpath, filepath);
      try {
        stat = fs.statSync(filepath);
      } catch (e) {
        // Ignore files that can't be "stat"ed (such as emacs's lock files)
      }
      if (stat && stat.isDirectory()) {
        // If the path is a directory, push it onto the array, adding a
        // trailing /.
        filepaths.push(filepath + pathSeparator);
        // Recurse.
        if (depth < maxDepth) {
          recurse(filepath, depth + 1);
        }
      } else {
        // Push file path onto the array.
        filepaths.push(filepath);
      }
    });
  }(betterBase, 1));

  // Normalize filepaths and remove those that don't match pattern.
  filepaths = filepaths.map(normalize).filter(function(filepath) {
    return exports.minimatch(filepath, absPattern, options);
  });

  // If the pattern wasn't absolute, replace each absolute filepath with one
  // that is relative to the cwd.
  if (!wasAbsolute) {
    filepaths = filepaths.map(function(filepath) {
      var relPath = normalize(path.relative(cwd, filepath));
      if (/\/$/.test(filepath)) { relPath += '/'; }
      return relPath;
    });
  }

  return filepaths;
};
<commit_msg>Add migration for path.existsSync<commit_after>/*
 * glob-whatev
 * http://github.com/cowboy/node-glob-whatev
 *
 * Copyright (c) 2012 "Cowboy" Ben Alman
 * Licensed under the MIT license.
 * http://benalman.com/about/license/
 */

var fs = require('fs');
var path = require('path');
var existsSync = fs.existsSync || path.existsSync;

// Export minimatch, in case something else needs direct access to it.
exports.minimatch = require('minimatch');

// Windows uses \ instead of / for path separators.
var win32 = process.platform === 'win32';
var pathSeparator = win32 ? '\\' : '/';
var stripWildcard = /[*?{+(].*$/;
var stripNonpath = win32 ? /[^\/\\]*$/ : /[^\/]*$/;

// On Windows, convert all \ to /.
function normalize(filepath) {
  return win32 ? filepath.replace(/\\/g, '/') : filepath;
}

// A very simple, not-at-all-efficient, synchronous file globbing util.
exports.glob = function(pattern, options) {
  if (!options) { options = {}; }
  var cwd = options.cwd || process.cwd();
  // The current absolute working directory.
  var base = normalize(path.join(cwd, pathSeparator));
  // The passed pattern, resolved to an absolute path.
  var absPattern = normalize(path.resolve(base, pattern));
  // Since path.resolve strips off trailing '/', add it back if necessary.
  if (/\/$/.test(pattern)) { absPattern += '/'; }
  // Was pattern-as-specified already absolute?
  var wasAbsolute = normalize(pattern) === absPattern;
  // Instead of recursing from the base looking for files, start recursing at
  // the farthest possible subdirectory that doesn't contain any kind of
  // wildcard characters. I may have missed one, so let me know if I have!
  var betterBase = absPattern.replace(stripWildcard, '').replace(stripNonpath, '');
  // Now that we've got a better base, we need a better pattern.
  var betterPattern = absPattern.slice(betterBase.length);
  // Don't recurse if we don't have to. Limit max depth.
  var maxDepth = betterPattern.indexOf('**') >= 0 ? (options.maxDepth || 99)
    : betterPattern.split('/').length;
  // If minimatch 'matchBase' option is true, and the pattern doesn't contain
  // ** or /, use the pattern as-specified and recurse to max.
  if (options.matchBase && !/\*\*|\//.test(pattern)) {
    maxDepth = options.maxDepth || 99;
    absPattern = pattern;
  }
  // Iterate over all sub-files and directories in a brute-force and not-at-all
  // efficient way.
  var filepaths = [];
  (function recurse(dirpath, depth) {
    // Abort if, for whatever reason, dirpath doesn't exist.
    if (!existsSync(dirpath)) { return; }
    // Read directory contents.
    fs.readdirSync(dirpath).forEach(function(filepath) {
      var stat;
      // Make relative path absolute.
      filepath = path.join(dirpath, filepath);
      try {
        stat = fs.statSync(filepath);
      } catch (e) {
        // Ignore files that can't be "stat"ed (such as emacs's lock files)
      }
      if (stat && stat.isDirectory()) {
        // If the path is a directory, push it onto the array, adding a
        // trailing /.
        filepaths.push(filepath + pathSeparator);
        // Recurse.
        if (depth < maxDepth) {
          recurse(filepath, depth + 1);
        }
      } else {
        // Push file path onto the array.
        filepaths.push(filepath);
      }
    });
  }(betterBase, 1));

  // Normalize filepaths and remove those that don't match pattern.
  filepaths = filepaths.map(normalize).filter(function(filepath) {
    return exports.minimatch(filepath, absPattern, options);
  });

  // If the pattern wasn't absolute, replace each absolute filepath with one
  // that is relative to the cwd.
  if (!wasAbsolute) {
    filepaths = filepaths.map(function(filepath) {
      var relPath = normalize(path.relative(cwd, filepath));
      if (/\/$/.test(filepath)) { relPath += '/'; }
      return relPath;
    });
  }

  return filepaths;
};
<|endoftext|>


<commit_before>'use strict';

const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const util = require('util');
const Emitter = require('events').EventEmitter;

const pipe = require('pipe-io');
const through = require('through2');

util.inherits(Gzip, Emitter);

module.exports = (from, to) => {
    const emitter = new Gzip(from, to);

    process.nextTick(() => {
        emitter._start();
    });

    return emitter;
};

function Gzip(from, to) {
    const name = path.basename(from.replace(/\.gz/, ''));

    this._from = from;
    this._to = path.join(to, name);
    this._i = 0;
    this._n = 0;
    this._percent = 0;
    this._percentPrev = 0;
}

Gzip.prototype._start = function() {
    const from = this._from;
    const to = this._to;

    fs.stat(from, (e, stat) => {
        if (e)
            return this.emit('error', e);

        if (!stat.size)
            return this.emit('error', Error('archive is empty'));

        this._n = stat.size;

        const unzipStream  = zlib.createGunzip();
        const readStream = fs.createReadStream(from);
        const writeStream = fs.createWriteStream(to);
        const progressStream = this.getProgressStream();

        pipe([
            readStream,
            progressStream,
            unzipStream,
            writeStream
        ], (e) => {
            if (e)
                return this.emit('error', e);

            this.emit('file', path.basename(to));
            this.emit('end');
        });
    });
};

Gzip.prototype.getProgressStream = function() {
    return through((chunk, enc, callback) => {
        this._i += chunk.length;
        this._progress();
        callback(null, chunk);
    });
};

Gzip.prototype._progress = function() {
    const value = Math.round(this._i * 100 / this._n);

    this._percent = value;

    if (value !== this._percentPrev) {
        this._percentPrev = value;
        this.emit('progress', value);
    }
};

<commit_msg>fix(gzip) pipe-io -> pipe-io/legacy: support of node < 4<commit_after>'use strict';

const fs = require('fs');
const path = require('path');
const zlib = require('zlib');
const util = require('util');
const Emitter = require('events').EventEmitter;

const pipe = require('pipe-io/legacy');
const through = require('through2');

util.inherits(Gzip, Emitter);

module.exports = (from, to) => {
    const emitter = new Gzip(from, to);

    process.nextTick(() => {
        emitter._start();
    });

    return emitter;
};

function Gzip(from, to) {
    const name = path.basename(from.replace(/\.gz/, ''));

    this._from = from;
    this._to = path.join(to, name);
    this._i = 0;
    this._n = 0;
    this._percent = 0;
    this._percentPrev = 0;
}

Gzip.prototype._start = function() {
    const from = this._from;
    const to = this._to;

    fs.stat(from, (e, stat) => {
        if (e)
            return this.emit('error', e);

        if (!stat.size)
            return this.emit('error', Error('archive is empty'));

        this._n = stat.size;

        const unzipStream  = zlib.createGunzip();
        const readStream = fs.createReadStream(from);
        const writeStream = fs.createWriteStream(to);
        const progressStream = this.getProgressStream();

        pipe([
            readStream,
            progressStream,
            unzipStream,
            writeStream
        ], (e) => {
            if (e)
                return this.emit('error', e);

            this.emit('file', path.basename(to));
            this.emit('end');
        });
    });
};

Gzip.prototype.getProgressStream = function() {
    return through((chunk, enc, callback) => {
        this._i += chunk.length;
        this._progress();
        callback(null, chunk);
    });
};

Gzip.prototype._progress = function() {
    const value = Math.round(this._i * 100 / this._n);

    this._percent = value;

    if (value !== this._percentPrev) {
        this._percentPrev = value;
        this.emit('progress', value);
    }
};

<|endoftext|>


<commit_before>'use strict'

//----------------------------------------------------------
// modules
//----------------------------------------------------------
// node
const spawn = require('child_process').spawn
const fs = require('fs')

// npm
const chokidar = require('chokidar')
const clor = require('clor')
const P = require('bluebird')

//----------------------------------------------------------
// logic
//----------------------------------------------------------
module.exports = class Hari {
  constructor() {
    // state vars
    this.running = false
    this.reps = 0
    this.script = void 0
    this.args = void 0
  }

  // TODO jsdoc
  init() {
    return this.readPkg().then(pkg => {
      this.parseScript(pkg.hari.run)
      this.watch(pkg.hari.watch)
    })
  }

  // TODO jsdoc
  parseScript(script) {
    const ar = script.split(' ')
    this.script = ar.shift()
    if (ar.length) this.args = ar
  }

  // TODO jsdoc
  readPkg() {
    return P.promisify(fs.readFile)('./package.json', 'utf8')
      .then(json => JSON.parse(json))
  }

  // TODO jsdoc
  clear() {
    return spawn('clear', {stdio: 'inherit'})
  }

  // TODO jsdoc
  scaleTime(num) {
    return num > 12 ? num - 12 : num
  }

  // TODO jsdoc
  prepTime(num) {
    return num.length === 1 ? `0${num}` : num
  }

  // TODO jsdoc
  time() {
    const now = new Date()
    const h = this.scaleTime(now.getHours())
    const m = this.prepTime(now.getMinutes().toString())
    const s = this.prepTime(now.getSeconds().toString())
    return `${h}:${m}:${s}`
  }

  // TODO jsdoc
  header(reps) {
    const delim = `||${'-'.repeat(58)}`
    const bars = '|| '
    clor
      .blue(delim).line
      .blue(bars).blue(`Time: ${this.time()}`).line
      .blue(bars).blue(`Reps: ${reps}`).line
      .blue(delim).log()
  }

  // TODO jsdoc
  run() {
    this.header(this.reps)
    this.args
      ? spawn(this.script, this.args, {stdio: 'inherit'})
      : spawn(this.script, {stdio: 'inherit'})
  }

  // TODO jsdoc
  debounce() {
    if (!this.running) {
      this.running = true
      this.reps += 1
      this.clear().on('close', this.run.bind(this))
      setTimeout(() => this.running = false, 50)
    }
  }

  // TODO jsdoc
  watch(globs) {
    return chokidar.watch(globs).on('all', this.debounce.bind(this))
  }
}
<commit_msg>sort methods<commit_after>'use strict'

//----------------------------------------------------------
// modules
//----------------------------------------------------------
// node
const spawn = require('child_process').spawn
const fs = require('fs')

// npm
const chokidar = require('chokidar')
const clor = require('clor')
const P = require('bluebird')

//----------------------------------------------------------
// logic
//----------------------------------------------------------
module.exports = class Hari {
  constructor() {
    this.running = false
    this.reps = 0
    this.script = void 0
    this.args = void 0
  }

  // TODO jsdoc
  clear() {
    return spawn('clear', {stdio: 'inherit'})
  }

  // TODO jsdoc
  debounce() {
    if (!this.running) {
      this.running = true
      this.reps += 1
      this.clear().on('close', this.run.bind(this))
      setTimeout(() => this.running = false, 50)
    }
  }

  // TODO jsdoc
  header(reps) {
    const delim = `||${'-'.repeat(58)}`
    const bars = '|| '
    clor
      .blue(delim).line
      .blue(bars).blue(`Time: ${this.time()}`).line
      .blue(bars).blue(`Reps: ${reps}`).line
      .blue(delim).log()
  }

  // TODO jsdoc
  init() {
    return this.readPkg().then(pkg => {
      this.parseScript(pkg.hari.run)
      this.watch(pkg.hari.watch)
    })
  }

  // TODO jsdoc
  parseScript(script) {
    const ar = script.split(' ')
    this.script = ar.shift()
    if (ar.length) this.args = ar
  }

  // TODO jsdoc
  prepTime(num) {
    return num.length === 1 ? `0${num}` : num
  }

  // TODO jsdoc
  readPkg() {
    return P.promisify(fs.readFile)('./package.json', 'utf8')
      .then(json => JSON.parse(json))
  }

  // TODO jsdoc
  run() {
    this.header(this.reps)
    this.args
      ? spawn(this.script, this.args, {stdio: 'inherit'})
      : spawn(this.script, {stdio: 'inherit'})
  }

  // TODO jsdoc
  scaleTime(num) {
    return num > 12 ? num - 12 : num
  }

  // TODO jsdoc
  time() {
    const now = new Date()
    const h = this.scaleTime(now.getHours())
    const m = this.prepTime(now.getMinutes().toString())
    const s = this.prepTime(now.getSeconds().toString())
    return `${h}:${m}:${s}`
  }

  // TODO jsdoc
  watch(globs) {
    return chokidar.watch(globs).on('all', this.debounce.bind(this))
  }
}
<|endoftext|>


<commit_before>/**
 * Array.prototype.slice reference
 */
var slice = Array.prototype.slice;

/**
 * Export hash
 */
module.exports = Hash;

/**
 * Constructor
 *
 * @param {Stirng}      key   Hash key
 * @param {Cachetree}   root  Root Cachetree instance (null if top node)
 */
function Hash(key, root) {
  var type = typeof key,
      len = 0;
  if ((type === 'string' && key) || type === 'number') {
    key = [key];
  } else if (Array.isArray(key)) {
    key = slice.call(key);
  }
  if (Array.isArray(key)) len = key.length;
  if (!(!root && len === 1) && !(root && len > 1)) throw new Error('Invalid key');
  if (!root) root = this;
  // Define properties
  Object.defineProperties(this, {
    key: {
      value: key,
      writable: false
    },
    root: {
      value: root,
      writable: false
    }
  });
}

/**
 * Execute a store function that takes variable arguments
 *
 * @param   {String}  name  Function name to execute
 * @param   {Array}   args  Arguments array
 * @return  {this}    for chaining
 */
Hash.prototype._exec = function(name, args) {
  args = slice.call(args);
  var len = args.length,
      cb;
  if (len > 0 && typeof args[len - 1] === 'function') cb = args.pop();
  this.root.store[name](this.key, args, cb);
  return this;
};

/**
 * Get the values of all the given hash fields
 *
 * @param   {...String} field   Hash field(s) or omit for all
 * @param   {Function}  cb      Callback function
 * @return  {this}      for chaining
 */
Hash.prototype.get = function() {
  return this._exec('get', arguments);
};

/**
 * Set the values of given hash fields
 *
 * @param   {String|Object} field   Hash field or object
 * @param   {Object}        value   Value (if field is not object)
 * @param   {Function}      cb      Callback function
 * @return  {this}          for chaining
 */
Hash.prototype.set = function() {
  return this._exec('set', arguments);
};

/**
 * Determine if a hash field exists
 *
 * @param   {String}    field   Hash field
 * @param   {Function}  cb      Callback function
 * @return  {this}      for chaining
 */
Hash.prototype.exists = function(field, cb) {
  if (typeof field === 'function') {
    cb = field;
    field = null;
  }
  this.root.store.exists(this.key, field, cb);
  return this;
};

/**
 * Delete given hash fields
 *
 * @param   {...String} field   Hash field(s)
 * @param   {Function}  cb      Callback function
 * @return  {this}      for chaining
 */
Hash.prototype.del = function() {
  return this._exec('del', arguments);
};

/**
 * Clear the hash and all descendent hashes
 *
 * @param   {Function}  cb    Callback function
 * @return  {this} for chaining
 */
Hash.prototype.flush = function(cb) {
  var self = this;
  this.root.store.exists(this.childKey('*'), function(err, keys) {
    if (!Array.isArray(keys)) keys = [];
    keys.unshift(this.key);
    self.root.store.flush(keys, cb);
  });
  return this;
};

/**
 * Add child node(s)
 *
 * @param   {String|Object} name  Child node name or object
 * @param   {Object}        def   Child definition (if name is string)
 * @return  {this}          for chaining
 */
Hash.prototype.add = function(name, def) {
  var type = typeof name;
  if (type === 'string' || type === 'number') {
    this._add(name, def);
  } else if (name === Object(name)) {
    Object.keys(name).forEach(function(val) {
      this._add(val, name[val]);
    }, this);
  }
  return this;
};

/**
 * Add a child
 *
 * @param   {String}  name    Child name
 * @param   {Object}  def     Child definition
 * @return  {this}    for chaining
 */
Hash.prototype._add = function(name, def) {
  var self = this,
      type = typeof def,
      validateFn = false,
      children = [],
      create = function(key) {
        var hash = new Hash(self.childKey(key), self);
        children.forEach(function(val) {
          hash.add(val, def[val]);
        });
        return hash;
      };
  if (name && !this[name]) {
    // Validate the child definitiom
    if ((type === 'string' && def) || type === 'number') {
      def = { __key__: def };
    } else if (type === 'function' || def instanceof RegExp) {
      def = { __validate__: def };
    }
    // Extract child names
    if (!def) def = {};
    children = Object.keys(def).filter(function(value) {
      return value !== '__key__' && value !== '__validate__';
    });
    // Add the child
    if (def.__key__) {
      if (this.root.useProperties === true) {
        Object.defineProperty(this, name, {
          get: function() {
            return create(def.__key__);
          }
        });
      } else {
        this[name] = function() {
          return create(def.__key__);
        };
      }
    } else {
      if (def.__validate__ && typeof def.__validate__ === 'function') validateFn = true;
      this[name] = function(value) {
        var isValid = false,
            type = typeof value,
            hash;
        if (type === 'string' || type === 'number') {
          if (def.__validate__) {
            if (validateFn) {
              isValid = def.__validate__(value);
            } else {
              isValid = def.__validate__.test(value);
            }
          } else {
            if ((type === 'string' && value) || type === 'number') {
              isValid = true;
            }
          }
        }
        if (isValid !== true) throw new Error('Invalid key');
        return create(value);
      };
    }
  }
  return this;
};

/**
 * Generate a child key
 *
 * @param   {String|Number} key   Child key
 * @return  {Array}         Child key array
 */
Hash.prototype.childKey = function(key) {
  var type = typeof key,
      childKey;
  if ((type === 'string' && key) || type === 'number') {
    childKey = slice.call(this.key);
    childKey.push(key);
    return childKey;
  }
};
<commit_msg>Fixed bad root reference in child hashes<commit_after>/**
 * Array.prototype.slice reference
 */
var slice = Array.prototype.slice;

/**
 * Export hash
 */
module.exports = Hash;

/**
 * Constructor
 *
 * @param {Stirng}      key   Hash key
 * @param {Cachetree}   root  Root Cachetree instance (null if top node)
 */
function Hash(key, root) {
  var type = typeof key,
      len = 0;
  if ((type === 'string' && key) || type === 'number') {
    key = [key];
  } else if (Array.isArray(key)) {
    key = slice.call(key);
  }
  if (Array.isArray(key)) len = key.length;
  if (!(!root && len === 1) && !(root && len > 1)) throw new Error('Invalid key');
  if (!root) root = this;
  // Define properties
  Object.defineProperties(this, {
    key: {
      value: key,
      writable: false
    },
    root: {
      value: root,
      writable: false
    }
  });
}

/**
 * Execute a store function that takes variable arguments
 *
 * @param   {String}  name  Function name to execute
 * @param   {Array}   args  Arguments array
 * @return  {this}    for chaining
 */
Hash.prototype._exec = function(name, args) {
  args = slice.call(args);
  var len = args.length,
      cb;
  if (len > 0 && typeof args[len - 1] === 'function') cb = args.pop();
  this.root.store[name](this.key, args, cb);
  return this;
};

/**
 * Get the values of all the given hash fields
 *
 * @param   {...String} field   Hash field(s) or omit for all
 * @param   {Function}  cb      Callback function
 * @return  {this}      for chaining
 */
Hash.prototype.get = function() {
  return this._exec('get', arguments);
};

/**
 * Set the values of given hash fields
 *
 * @param   {String|Object} field   Hash field or object
 * @param   {Object}        value   Value (if field is not object)
 * @param   {Function}      cb      Callback function
 * @return  {this}          for chaining
 */
Hash.prototype.set = function() {
  return this._exec('set', arguments);
};

/**
 * Determine if a hash field exists
 *
 * @param   {String}    field   Hash field
 * @param   {Function}  cb      Callback function
 * @return  {this}      for chaining
 */
Hash.prototype.exists = function(field, cb) {
  if (typeof field === 'function') {
    cb = field;
    field = null;
  }
  this.root.store.exists(this.key, field, cb);
  return this;
};

/**
 * Delete given hash fields
 *
 * @param   {...String} field   Hash field(s)
 * @param   {Function}  cb      Callback function
 * @return  {this}      for chaining
 */
Hash.prototype.del = function() {
  return this._exec('del', arguments);
};

/**
 * Clear the hash and all descendent hashes
 *
 * @param   {Function}  cb    Callback function
 * @return  {this} for chaining
 */
Hash.prototype.flush = function(cb) {
  var self = this;
  this.root.store.exists(this.childKey('*'), function(err, keys) {
    if (!Array.isArray(keys)) keys = [];
    keys.unshift(this.key);
    self.root.store.flush(keys, cb);
  });
  return this;
};

/**
 * Add child node(s)
 *
 * @param   {String|Object} name  Child node name or object
 * @param   {Object}        def   Child definition (if name is string)
 * @return  {this}          for chaining
 */
Hash.prototype.add = function(name, def) {
  var type = typeof name;
  if (type === 'string' || type === 'number') {
    this._add(name, def);
  } else if (name === Object(name)) {
    Object.keys(name).forEach(function(val) {
      this._add(val, name[val]);
    }, this);
  }
  return this;
};

/**
 * Add a child
 *
 * @param   {String}  name    Child name
 * @param   {Object}  def     Child definition
 * @return  {this}    for chaining
 */
Hash.prototype._add = function(name, def) {
  var self = this,
      type = typeof def,
      validateFn = false,
      children = [],
      create = function(key) {
        var hash = new Hash(self.childKey(key), self.root);
        children.forEach(function(val) {
          hash.add(val, def[val]);
        });
        return hash;
      };
  if (name && !this[name]) {
    // Validate the child definitiom
    if ((type === 'string' && def) || type === 'number') {
      def = { __key__: def };
    } else if (type === 'function' || def instanceof RegExp) {
      def = { __validate__: def };
    }
    // Extract child names
    if (!def) def = {};
    children = Object.keys(def).filter(function(value) {
      return value !== '__key__' && value !== '__validate__';
    });
    // Add the child
    if (def.__key__) {
      if (this.root.useProperties === true) {
        Object.defineProperty(this, name, {
          get: function() {
            return create(def.__key__);
          }
        });
      } else {
        this[name] = function() {
          return create(def.__key__);
        };
      }
    } else {
      if (def.__validate__ && typeof def.__validate__ === 'function') validateFn = true;
      this[name] = function(value) {
        var isValid = false,
            type = typeof value,
            hash;
        if (type === 'string' || type === 'number') {
          if (def.__validate__) {
            if (validateFn) {
              isValid = def.__validate__(value);
            } else {
              isValid = def.__validate__.test(value);
            }
          } else {
            if ((type === 'string' && value) || type === 'number') {
              isValid = true;
            }
          }
        }
        if (isValid !== true) throw new Error('Invalid key');
        return create(value);
      };
    }
  }
  return this;
};

/**
 * Generate a child key
 *
 * @param   {String|Number} key   Child key
 * @return  {Array}         Child key array
 */
Hash.prototype.childKey = function(key) {
  var type = typeof key,
      childKey;
  if ((type === 'string' && key) || type === 'number') {
    childKey = slice.call(this.key);
    childKey.push(key);
    return childKey;
  }
};
<|endoftext|>


<commit_before>var log = require('./log');

var version = require('../package.json').version;
var args = require('./args').parse();

if (args.version) {
    console.log(version);
}

if (args.help) {
    console.log('shifter'.magenta + ('@' + version).white);
    console.log('');
    console.log('pass no arguments and shifter will build the module from the current directory');
    console.log('');
    console.log('   -v/--version            show version');
    console.log('   -h/--help               show this stuff');
    console.log('   -m/--modules <module>   limit the modules to build (array: -m foo -m bar)');
    console.log('   --lint [preferred|defaults|strict] (preferred is the default) lint mode: https://github.com/yui/yui-lint');
    console.log('   --strict                add "use strict" to module wrapper');
}

process.exit(0);
<commit_msg>Help updates<commit_after>var log = require('./log');

var version = require('../package.json').version;
var args = require('./args').parse();

if (args.version) {
    console.log(version);
}

if (args.help) {
    console.log('blazingly fast builds with shifter'.magenta + ('@' + version).white);
    console.log('');
    console.log('pass no arguments and shifter will build the module from the current directory');
    console.log('');
    console.log('   -v/--version            show version');
    console.log('   -h/--help               show this stuff');
    console.log('   -m/--modules <module>   limit the modules to build (array: -m foo -m bar)');
    console.log('   --lint [preferred|defaults|strict] (preferred is the default) lint mode: https://github.com/yui/yui-lint');
    console.log('   --strict                add "use strict" to module wrapper');
}

process.exit(0);
<|endoftext|>