<commit_before>/*jslint bitwise: true, nomen: true, plusplus: true, white: true */

/*!
* Mediator.js Library v0.9.7
* https://github.com/ajacksified/Mediator.js
*
* Copyright 2013, Jack Lawson
* MIT Licensed (http://www.opensource.org/licenses/mit-license.php)
*
* For more information: http://thejacklawson.com/2011/06/mediators-for-modularized-asynchronous-programming-in-javascript/index.html
* Project on GitHub: https://github.com/ajacksified/Mediator.js
*
* Last update: October 19 2013
*/

(function(global, factory) {
  'use strict';

  if(typeof exports !== 'undefined') {
    // Node/CommonJS
    exports.Mediator = factory();
  } else if(typeof define === 'function' && define.amd) {
    // AMD
    define('mediator-js', [], function() {
      global.Mediator = factory();
      return global.Mediator;
    });
  } else {
    // Browser global
    global.Mediator = factory();
  }
}(this, function() {
  'use strict';

  // We'll generate guids for class instances for easy referencing later on.
  // Subscriber instances will have an id that can be refernced for quick
  // lookups.

  function guidGenerator() {
    var S4 = function() {
       return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
    };

    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
  }

  // Subscribers are instances of Mediator Channel registrations. We generate
  // an object instance so that it can be updated later on without having to
  // unregister and re-register. Subscribers are constructed with a function
  // to be called, options object, and context.

  function Subscriber(fn, options, context){
    if(!(this instanceof Subscriber)) {
      return new Subscriber(fn, options, context);
    }

    this.id = guidGenerator();
    this.fn = fn;
    this.options = options;
    this.context = context;
    this.channel = null;
  }

  Subscriber.prototype = {
    // Mediator.update on a subscriber instance can update its function,context,
    // or options object. It takes in an object and looks for fn, context, or
    // options keys.

    update: function(options){
      if(options){
        this.fn = options.fn || this.fn;
        this.context = options.context || this.context;
        this.options = options.options || this.options;
        if(this.channel && this.options && this.options.priority !== undefined) {
            this.channel.setPriority(this.id, this.options.priority);
        }
      }
    }
  };

  function Channel(namespace, parent){
    if(!(this instanceof Channel)) {
      return new Channel(namespace);
    }

    this.namespace = namespace || "";
    this._subscribers = [];
    this._channels = [];
    this._parent = parent;
    this.stopped = false;
  }

  // A Mediator channel holds a list of sub-channels and subscribers to be fired
  // when Mediator.publish is called on the Mediator instance. It also contains
  // some methods to manipulate its lists of data; only setPriority and
  // StopPropagation are meant to be used. The other methods should be accessed
  // through the Mediator instance.

  Channel.prototype = {
    addSubscriber: function(fn, options, context){
      var subscriber = new Subscriber(fn, options, context);

      if(options && options.priority !== undefined){
        // Cheap hack to either parse as an int or turn it into 0. Runs faster
        // in many browsers than parseInt with the benefit that it won't
        // return a NaN.
        options.priority = options.priority >> 0;

        if(options.priority < 0){ options.priority = 0; }
        if(options.priority >= this._subscribers.length){ options.priority = this._subscribers.length-1; }

        this._subscribers.splice(options.priority, 0, subscriber);
      }else{
        this._subscribers.push(subscriber);
      }

      subscriber.channel = this;

      return subscriber;
    },

    // The channel instance is passed as an argument to the mediator subscriber,
    // and further subscriber propagation can be called with
    // channel.StopPropagation().
    stopPropagation: function(){
      this.stopped = true;
    },

    getSubscriber: function(identifier){
      var x = 0,
          y = this._subscribers.length;

      for(x, y; x < y; x++){
        if(this._subscribers[x].id === identifier || this._subscribers[x].fn === identifier){
          return this._subscribers[x];
        }
      }
    },

    // Channel.setPriority is useful in updating the order in which Subscribers
    // are called, and takes an identifier (subscriber id or named function) and
    // an array index. It will not search recursively through subchannels.

    setPriority: function(identifier, priority){
      var oldIndex = 0,
          x = 0,
          sub, firstHalf, lastHalf, y;

      for(x = 0, y = this._subscribers.length; x < y; x++){
        if(this._subscribers[x].id === identifier || this._subscribers[x].fn === identifier){
          break;
        }
        oldIndex ++;
      }

      sub = this._subscribers[oldIndex];
      firstHalf = this._subscribers.slice(0, oldIndex);
      lastHalf = this._subscribers.slice(oldIndex+1);

      this._subscribers = firstHalf.concat(lastHalf);
      this._subscribers.splice(priority, 0, sub);
    },

    addChannel: function(channel){
      this._channels[channel] = new Channel((this.namespace ? this.namespace + ':' : '') + channel, this);
    },

    hasChannel: function(channel){
      return this._channels.hasOwnProperty(channel);
    },

    returnChannel: function(channel){
      return this._channels[channel];
    },

    removeSubscriber: function(identifier){
      var x = this._subscribers.length - 1;

      // If we don't pass in an id, we're clearing all
      if(!identifier){
        this._subscribers = [];
        return;
      }

      // Going backwards makes splicing a whole lot easier.
      for(x; x >= 0; x--) {
        if(this._subscribers[x].fn === identifier || this._subscribers[x].id === identifier){
          this._subscribers[x].channel = null;
          this._subscribers.splice(x,1);
        }
      }
    },

    // This will publish arbitrary arguments to a subscriber and then to parent
    // channels.

    publish: function(data){
      var x = 0,
          y = this._subscribers.length,
          called = false,
          subscriber, l,
          subsBefore,subsAfter;

      // Priority is preserved in the _subscribers index.
      for(x, y; x < y; x++) {
        called = false;

        if(!this.stopped){
          subscriber = this._subscribers[x];
          subsBefore = this._subscribers.length;
          if(subscriber.options !== undefined && typeof subscriber.options.predicate === "function"){
            if(subscriber.options.predicate.apply(subscriber.context, data)){
              subscriber.fn.apply(subscriber.context, data);
              called = true;
            }
          }else{
            subscriber.fn.apply(subscriber.context, data);
            called = true;
          }
          subsAfter = this._subscribers.length;
          y = subsAfter;
          if (subsAfter === subsBefore - 1){
            x--;
          }
        }

        if(called && subscriber.options && subscriber.options !== undefined){
          subscriber.options.calls--;

          if(subscriber.options.calls < 1){
            this.removeSubscriber(subscriber.id);
            y--;
            x--;
          }
        }
      }

      if(this._parent){
        this._parent.publish(data);
      }

      this.stopped = false;
    }
  };

  function Mediator() {
    if(!(this instanceof Mediator)) {
      return new Mediator();
    }

    this._channels = new Channel('');
  }

  // A Mediator instance is the interface through which events are registered
  // and removed from publish channels.

  Mediator.prototype = {

    // Returns a channel instance based on namespace, for example
    // application:chat:message:received

    getChannel: function(namespace){
      var channel = this._channels,
          namespaceHierarchy = namespace.split(':'),
          x = 0,
          y = namespaceHierarchy.length;

      if(namespace === ''){
        return channel;
      }

      if(namespaceHierarchy.length > 0){
        for(x, y; x < y; x++){

          if(!channel.hasChannel(namespaceHierarchy[x])){
            channel.addChannel(namespaceHierarchy[x]);
          }

          channel = channel.returnChannel(namespaceHierarchy[x]);
        }
      }

      return channel;
    },

    // Pass in a channel namespace, function to be called, options, and context
    // to call the function in to Subscribe. It will create a channel if one
    // does not exist. Options can include a predicate to determine if it
    // should be called (based on the data published to it) and a priority
    // index.

    subscribe: function(channelName, fn, options, context){
      var channel = this.getChannel(channelName);

      options = options || {};
      context = context || {};

      return channel.addSubscriber(fn, options, context);
    },

    // Pass in a channel namespace, function to be called, options, and context
    // to call the function in to Subscribe. It will create a channel if one
    // does not exist. Options can include a predicate to determine if it
    // should be called (based on the data published to it) and a priority
    // index.

    once: function(channelName, fn, options, context){
      options = options || {};
      options.calls = 1;

      return this.subscribe(channelName, fn, options, context);
    },

    // Returns a subscriber for a given subscriber id / named function and
    // channel namespace

    getSubscriber: function(identifier, channel){
      return this.getChannel(channel || "").getSubscriber(identifier);
    },

    // Remove a subscriber from a given channel namespace recursively based on
    // a passed-in subscriber id or named function.

    remove: function(channelName, identifier){
      this.getChannel(channelName).removeSubscriber(identifier);
    },

    // Publishes arbitrary data to a given channel namespace. Channels are
    // called recursively downwards; a post to application:chat will post to
    // application:chat:receive and application:chat:derp:test:beta:bananas.
    // Called using Mediator.publish("application:chat", [ args ]);

    publish: function(channelName){
      var args = Array.prototype.slice.call(arguments, 1),
          channel = this.getChannel(channelName);

      args.push(channel);

      this.getChannel(channelName).publish(args);
    }
  };

  // Alias some common names for easy interop
  Mediator.prototype.on = Mediator.prototype.subscribe;
  Mediator.prototype.bind = Mediator.prototype.subscribe;
  Mediator.prototype.emit = Mediator.prototype.publish;
  Mediator.prototype.trigger = Mediator.prototype.publish;
  Mediator.prototype.off = Mediator.prototype.remove;

  // Finally, expose it all.

  Mediator.Channel = Channel;
  Mediator.Subscriber = Subscriber;
  Mediator.version = "0.9.7";

  return Mediator;
}));

<commit_msg>Add read-only mode to getChannel<commit_after>/*jslint bitwise: true, nomen: true, plusplus: true, white: true */

/*!
* Mediator.js Library v0.9.7
* https://github.com/ajacksified/Mediator.js
*
* Copyright 2013, Jack Lawson
* MIT Licensed (http://www.opensource.org/licenses/mit-license.php)
*
* For more information: http://thejacklawson.com/2011/06/mediators-for-modularized-asynchronous-programming-in-javascript/index.html
* Project on GitHub: https://github.com/ajacksified/Mediator.js
*
* Last update: October 19 2013
*/

(function(global, factory) {
  'use strict';

  if(typeof exports !== 'undefined') {
    // Node/CommonJS
    exports.Mediator = factory();
  } else if(typeof define === 'function' && define.amd) {
    // AMD
    define('mediator-js', [], function() {
      global.Mediator = factory();
      return global.Mediator;
    });
  } else {
    // Browser global
    global.Mediator = factory();
  }
}(this, function() {
  'use strict';

  // We'll generate guids for class instances for easy referencing later on.
  // Subscriber instances will have an id that can be refernced for quick
  // lookups.

  function guidGenerator() {
    var S4 = function() {
       return (((1+Math.random())*0x10000)|0).toString(16).substring(1);
    };

    return (S4()+S4()+"-"+S4()+"-"+S4()+"-"+S4()+"-"+S4()+S4()+S4());
  }

  // Subscribers are instances of Mediator Channel registrations. We generate
  // an object instance so that it can be updated later on without having to
  // unregister and re-register. Subscribers are constructed with a function
  // to be called, options object, and context.

  function Subscriber(fn, options, context){
    if(!(this instanceof Subscriber)) {
      return new Subscriber(fn, options, context);
    }

    this.id = guidGenerator();
    this.fn = fn;
    this.options = options;
    this.context = context;
    this.channel = null;
  }

  Subscriber.prototype = {
    // Mediator.update on a subscriber instance can update its function,context,
    // or options object. It takes in an object and looks for fn, context, or
    // options keys.

    update: function(options){
      if(options){
        this.fn = options.fn || this.fn;
        this.context = options.context || this.context;
        this.options = options.options || this.options;
        if(this.channel && this.options && this.options.priority !== undefined) {
            this.channel.setPriority(this.id, this.options.priority);
        }
      }
    }
  };

  function Channel(namespace, parent){
    if(!(this instanceof Channel)) {
      return new Channel(namespace);
    }

    this.namespace = namespace || "";
    this._subscribers = [];
    this._channels = [];
    this._parent = parent;
    this.stopped = false;
  }

  // A Mediator channel holds a list of sub-channels and subscribers to be fired
  // when Mediator.publish is called on the Mediator instance. It also contains
  // some methods to manipulate its lists of data; only setPriority and
  // StopPropagation are meant to be used. The other methods should be accessed
  // through the Mediator instance.

  Channel.prototype = {
    addSubscriber: function(fn, options, context){
      var subscriber = new Subscriber(fn, options, context);

      if(options && options.priority !== undefined){
        // Cheap hack to either parse as an int or turn it into 0. Runs faster
        // in many browsers than parseInt with the benefit that it won't
        // return a NaN.
        options.priority = options.priority >> 0;

        if(options.priority < 0){ options.priority = 0; }
        if(options.priority >= this._subscribers.length){ options.priority = this._subscribers.length-1; }

        this._subscribers.splice(options.priority, 0, subscriber);
      }else{
        this._subscribers.push(subscriber);
      }

      subscriber.channel = this;

      return subscriber;
    },

    // The channel instance is passed as an argument to the mediator subscriber,
    // and further subscriber propagation can be called with
    // channel.StopPropagation().
    stopPropagation: function(){
      this.stopped = true;
    },

    getSubscriber: function(identifier){
      var x = 0,
          y = this._subscribers.length;

      for(x, y; x < y; x++){
        if(this._subscribers[x].id === identifier || this._subscribers[x].fn === identifier){
          return this._subscribers[x];
        }
      }
    },

    // Channel.setPriority is useful in updating the order in which Subscribers
    // are called, and takes an identifier (subscriber id or named function) and
    // an array index. It will not search recursively through subchannels.

    setPriority: function(identifier, priority){
      var oldIndex = 0,
          x = 0,
          sub, firstHalf, lastHalf, y;

      for(x = 0, y = this._subscribers.length; x < y; x++){
        if(this._subscribers[x].id === identifier || this._subscribers[x].fn === identifier){
          break;
        }
        oldIndex ++;
      }

      sub = this._subscribers[oldIndex];
      firstHalf = this._subscribers.slice(0, oldIndex);
      lastHalf = this._subscribers.slice(oldIndex+1);

      this._subscribers = firstHalf.concat(lastHalf);
      this._subscribers.splice(priority, 0, sub);
    },

    addChannel: function(channel){
      this._channels[channel] = new Channel((this.namespace ? this.namespace + ':' : '') + channel, this);
    },

    hasChannel: function(channel){
      return this._channels.hasOwnProperty(channel);
    },

    returnChannel: function(channel){
      return this._channels[channel];
    },

    removeSubscriber: function(identifier){
      var x = this._subscribers.length - 1;

      // If we don't pass in an id, we're clearing all
      if(!identifier){
        this._subscribers = [];
        return;
      }

      // Going backwards makes splicing a whole lot easier.
      for(x; x >= 0; x--) {
        if(this._subscribers[x].fn === identifier || this._subscribers[x].id === identifier){
          this._subscribers[x].channel = null;
          this._subscribers.splice(x,1);
        }
      }
    },

    // This will publish arbitrary arguments to a subscriber and then to parent
    // channels.

    publish: function(data){
      var x = 0,
          y = this._subscribers.length,
          called = false,
          subscriber, l,
          subsBefore,subsAfter;

      // Priority is preserved in the _subscribers index.
      for(x, y; x < y; x++) {
        called = false;

        if(!this.stopped){
          subscriber = this._subscribers[x];
          subsBefore = this._subscribers.length;
          if(subscriber.options !== undefined && typeof subscriber.options.predicate === "function"){
            if(subscriber.options.predicate.apply(subscriber.context, data)){
              subscriber.fn.apply(subscriber.context, data);
              called = true;
            }
          }else{
            subscriber.fn.apply(subscriber.context, data);
            called = true;
          }
          subsAfter = this._subscribers.length;
          y = subsAfter;
          if (subsAfter === subsBefore - 1){
            x--;
          }
        }

        if(called && subscriber.options && subscriber.options !== undefined){
          subscriber.options.calls--;

          if(subscriber.options.calls < 1){
            this.removeSubscriber(subscriber.id);
            y--;
            x--;
          }
        }
      }

      if(this._parent){
        this._parent.publish(data);
      }

      this.stopped = false;
    }
  };

  function Mediator() {
    if(!(this instanceof Mediator)) {
      return new Mediator();
    }

    this._channels = new Channel('');
  }

  // A Mediator instance is the interface through which events are registered
  // and removed from publish channels.

  Mediator.prototype = {

    // Returns a channel instance based on namespace, for example
    // application:chat:message:received. If readOnly is true we
    // will refrain from creating non existing channels.

    getChannel: function(namespace, readOnly){
      var channel = this._channels,
          namespaceHierarchy = namespace.split(':'),
          x = 0,
          y = namespaceHierarchy.length;

      if(namespace === ''){
        return channel;
      }

      if(namespaceHierarchy.length > 0){
        for(x, y; x < y; x++){

          if(!channel.hasChannel(namespaceHierarchy[x])){
            if (readOnly) {
              channel = null;
              break;
            } else {
              channel.addChannel(namespaceHierarchy[x]);
            }
          }

          channel = channel.returnChannel(namespaceHierarchy[x]);
        }
      }

      return channel;
    },

    // Pass in a channel namespace, function to be called, options, and context
    // to call the function in to Subscribe. It will create a channel if one
    // does not exist. Options can include a predicate to determine if it
    // should be called (based on the data published to it) and a priority
    // index.

    subscribe: function(channelName, fn, options, context){
      var channel = this.getChannel(channelName, false);

      options = options || {};
      context = context || {};

      return channel.addSubscriber(fn, options, context);
    },

    // Pass in a channel namespace, function to be called, options, and context
    // to call the function in to Subscribe. It will create a channel if one
    // does not exist. Options can include a predicate to determine if it
    // should be called (based on the data published to it) and a priority
    // index.

    once: function(channelName, fn, options, context){
      options = options || {};
      options.calls = 1;

      return this.subscribe(channelName, fn, options, context);
    },

    // Returns a subscriber for a given subscriber id / named function and
    // channel namespace

    getSubscriber: function(identifier, channelName){
      var channel = this.getChannel(channelName || "", true);
      if (channel == null) {
        return null;
      }

      return channel.getSubscriber(identifier);
    },

    // Remove a subscriber from a given channel namespace recursively based on
    // a passed-in subscriber id or named function.

    remove: function(channelName, identifier){
      var channel = this.getChannel(channelName || "", true);
      if (channel == null) {
        return false;
      }

      channel.removeSubscriber(identifier);
    },

    // Publishes arbitrary data to a given channel namespace. Channels are
    // called recursively downwards; a post to application:chat will post to
    // application:chat:receive and application:chat:derp:test:beta:bananas.
    // Called using Mediator.publish("application:chat", [ args ]);

    publish: function(channelName){
      var channel = this.getChannel(channelName || "", true);
      if (channel == null) {
        return null;
      }

      var args = Array.prototype.slice.call(arguments, 1);

      args.push(channel);

      channel.publish(args);
    }
  };

  // Alias some common names for easy interop
  Mediator.prototype.on = Mediator.prototype.subscribe;
  Mediator.prototype.bind = Mediator.prototype.subscribe;
  Mediator.prototype.emit = Mediator.prototype.publish;
  Mediator.prototype.trigger = Mediator.prototype.publish;
  Mediator.prototype.off = Mediator.prototype.remove;

  // Finally, expose it all.

  Mediator.Channel = Channel;
  Mediator.Subscriber = Subscriber;
  Mediator.version = "0.9.7";

  return Mediator;
}));
<|endoftext|>


<commit_before>const fs     = require("fs")
    , moment = require("moment")

var Utils          = require("./utils")
  , Migration      = require("./migration")
  , DataTypes      = require("./data-types")

module.exports = (function() {
  var Migrator = function(sequelize, options) {
    this.sequelize = sequelize
    this.options   = Utils._.extend({
      path: __dirname + '/../migrations',
      from: null,
      to: null,
      logging: console.log
    }, options || {})

    if (this.options.logging === true) {
      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
      this.options.logging = console.log
    }

    if (this.options.logging == console.log) {
      // using just console.log will break in node < 0.6
      this.options.logging = function(s) { console.log(s) }
    }
  }

  Object.defineProperty(Migrator.prototype, "queryInterface", {
    get: function() {
      return this.sequelize.getQueryInterface()
    }
  })

  Migrator.prototype.migrate = function(options) {
    var self = this

    options = Utils._.extend({
      method: 'up'
    }, options || {})

    return new Utils.CustomEventEmitter(function(emitter) {
      self.getUndoneMigrations(function(err, migrations) {
        if (err) {
          emitter.emit('error', err)
        } else {
          var chainer = new Utils.QueryChainer
            , from    = migrations[0]

          if (options.method === 'down') {
            from = migrations[0]
            migrations.reverse()
          }

          migrations.forEach(function(migration) {
            chainer.add(migration, 'execute', [options], {
              before: function(migration) {
                if (self.options.logging !== false) {
                  self.options.logging('Executing migration: ' + migration.filename)
                }
              },
              after: function(migration) {
                if (self.options.logging !== false) {
                  self.options.logging('Executed migration: ' + migration.filename)
                }
              },
              success: function(migration, callback) {
                if (options.method === 'down') {
                  deleteUndoneMigration.call(self, from, migration, callback)
                } else {
                  saveSuccessfulMigration.call(self, from, migration, callback)
                }
              }
            })
          })

          chainer
            .runSerially({ skipOnError: true })
            .success(function() { emitter.emit('success', null) })
            .error(function(err) { emitter.emit('error', err) })
        }
      })
    }).run()
  }

  Migrator.prototype.getUndoneMigrations = function(callback)  {
    var self = this

    var filterFrom = function(migrations, from, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isAfter(from, options) })
      callback && callback(null, result)
    }
    var filterTo = function(migrations, to, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isBefore(to, options) })
      callback && callback(null, result)
    }

    var migrationFiles = fs.readdirSync(this.options.path).filter(function(file) {
      return /\.js$/.test(file)
    })

    var migrations = migrationFiles.map(function(file) {
      return new Migration(self, self.options.path + '/' + file)
    })

    migrations = migrations.sort(function(a,b){
      return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0])
    })

    if (this.options.from) {
      filterFrom(migrations, this.options.from, function(err, migrations) {
        if (self.options.to) {
          filterTo(migrations, self.options.to, callback)
        } else {
          callback && callback(null, migrations)
        }
      })
    } else {
      getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) {
        if (lastMigrationId) {
          filterFrom(migrations, lastMigrationId, function(err, migrations) {
            if (self.options.to) {
              filterTo(migrations, self.options.to, callback)
            } else {
              callback && callback(null, migrations)
            }
          }, { withoutEqual: true })
        } else {
          if (self.options.to) {
            filterTo(migrations, self.options.to, callback)
          } else {
            callback && callback(null, migrations)
          }
        }
      }).error(function(err) {
        callback && callback(err, null)
      })
    }
  }

  Migrator.prototype.findOrCreateSequelizeMetaDAO = function(syncOptions) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var storedDAO   = self.sequelize.daoFactoryManager.getDAO('SequelizeMeta')
        , SequelizeMeta = storedDAO

      if (!storedDAO) {
        SequelizeMeta = self.sequelize.define('SequelizeMeta', {
          from: DataTypes.STRING,
          to:   DataTypes.STRING
        }, {
          timestamps: false
        })
      }

      // force sync when model has newly created or if syncOptions are passed
      if (!storedDAO || syncOptions) {
        SequelizeMeta
          .sync(syncOptions || {})
          .success(function() { emitter.emit('success', SequelizeMeta) })
          .error(function(err) { emitter.emit('error', err) })
      } else {
        emitter.emit('success', SequelizeMeta)
      }
    }).run()
  }

  // private

  var getLastMigrationFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
        SequelizeMeta.find({ order: 'id DESC' }).success(function(meta) {
          emitter.emit('success', meta ? meta : null)
        }).error(function(err) { emitter.emit('error', err) })
      }).error(function(err) { emitter.emit(err) })
    }).run()
  }

  var getLastMigrationIdFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      getLastMigrationFromDatabase.call(self)
        .success(function(meta) {
          emitter.emit('success', meta ? meta.to : null)
        })
        .error(function(err) {
          emitter.emit('error', err)
        })
    }).run()
  }

  var getFormattedDateString = function(s) {
    var result = null

    try {
      result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
    } catch(e) {
      throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
    }

    return result
  }

  var stringToDate = function(s) {
    return moment(getFormattedDateString(s), "YYYYMMDDHHmmss")
  }

  var saveSuccessfulMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .create({ from: from.migrationId, to: to.migrationId })
        .success(callback)
    })
  }

  var deleteUndoneMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .find({ where: { from: from.migrationId.toString(), to: to.migrationId.toString() } })
        .success(function(meta) {
          meta.destroy().success(callback)
        })
    })
  }

  return Migrator
})()
<commit_msg>option to change filesFiltes on Migrator<commit_after>const fs     = require("fs")
    , moment = require("moment")

var Utils          = require("./utils")
  , Migration      = require("./migration")
  , DataTypes      = require("./data-types")

module.exports = (function() {
  var Migrator = function(sequelize, options) {
    this.sequelize = sequelize
    this.options   = Utils._.extend({
      path: __dirname + '/../migrations',
      from: null,
      to: null,
      logging: console.log,
      filesFilter: /\.js$/
    }, options || {})

    if (this.options.logging === true) {
      console.log('DEPRECATION WARNING: The logging-option should be either a function or false. Default: console.log')
      this.options.logging = console.log
    }

    if (this.options.logging == console.log) {
      // using just console.log will break in node < 0.6
      this.options.logging = function(s) { console.log(s) }
    }
  }

  Object.defineProperty(Migrator.prototype, "queryInterface", {
    get: function() {
      return this.sequelize.getQueryInterface()
    }
  })

  Migrator.prototype.migrate = function(options) {
    var self = this

    options = Utils._.extend({
      method: 'up'
    }, options || {})

    return new Utils.CustomEventEmitter(function(emitter) {
      self.getUndoneMigrations(function(err, migrations) {
        if (err) {
          emitter.emit('error', err)
        } else {
          var chainer = new Utils.QueryChainer
            , from    = migrations[0]

          if (options.method === 'down') {
            from = migrations[0]
            migrations.reverse()
          }

          migrations.forEach(function(migration) {
            chainer.add(migration, 'execute', [options], {
              before: function(migration) {
                if (self.options.logging !== false) {
                  self.options.logging('Executing migration: ' + migration.filename)
                }
              },
              after: function(migration) {
                if (self.options.logging !== false) {
                  self.options.logging('Executed migration: ' + migration.filename)
                }
              },
              success: function(migration, callback) {
                if (options.method === 'down') {
                  deleteUndoneMigration.call(self, from, migration, callback)
                } else {
                  saveSuccessfulMigration.call(self, from, migration, callback)
                }
              }
            })
          })

          chainer
            .runSerially({ skipOnError: true })
            .success(function() { emitter.emit('success', null) })
            .error(function(err) { emitter.emit('error', err) })
        }
      })
    }).run()
  }

  Migrator.prototype.getUndoneMigrations = function(callback)  {
    var self = this

    var filterFrom = function(migrations, from, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isAfter(from, options) })
      callback && callback(null, result)
    }
    var filterTo = function(migrations, to, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isBefore(to, options) })
      callback && callback(null, result)
    }

    var migrationFiles = fs.readdirSync(this.options.path).filter(function(file) {
      return self.options.filesFilter.test(file)
    })

    var migrations = migrationFiles.map(function(file) {
      return new Migration(self, self.options.path + '/' + file)
    })

    migrations = migrations.sort(function(a,b){
      return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0])
    })

    if (this.options.from) {
      filterFrom(migrations, this.options.from, function(err, migrations) {
        if (self.options.to) {
          filterTo(migrations, self.options.to, callback)
        } else {
          callback && callback(null, migrations)
        }
      })
    } else {
      getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) {
        if (lastMigrationId) {
          filterFrom(migrations, lastMigrationId, function(err, migrations) {
            if (self.options.to) {
              filterTo(migrations, self.options.to, callback)
            } else {
              callback && callback(null, migrations)
            }
          }, { withoutEqual: true })
        } else {
          if (self.options.to) {
            filterTo(migrations, self.options.to, callback)
          } else {
            callback && callback(null, migrations)
          }
        }
      }).error(function(err) {
        callback && callback(err, null)
      })
    }
  }

  Migrator.prototype.findOrCreateSequelizeMetaDAO = function(syncOptions) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var storedDAO   = self.sequelize.daoFactoryManager.getDAO('SequelizeMeta')
        , SequelizeMeta = storedDAO

      if (!storedDAO) {
        SequelizeMeta = self.sequelize.define('SequelizeMeta', {
          from: DataTypes.STRING,
          to:   DataTypes.STRING
        }, {
          timestamps: false
        })
      }

      // force sync when model has newly created or if syncOptions are passed
      if (!storedDAO || syncOptions) {
        SequelizeMeta
          .sync(syncOptions || {})
          .success(function() { emitter.emit('success', SequelizeMeta) })
          .error(function(err) { emitter.emit('error', err) })
      } else {
        emitter.emit('success', SequelizeMeta)
      }
    }).run()
  }

  // private

  var getLastMigrationFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
        SequelizeMeta.find({ order: 'id DESC' }).success(function(meta) {
          emitter.emit('success', meta ? meta : null)
        }).error(function(err) { emitter.emit('error', err) })
      }).error(function(err) { emitter.emit(err) })
    }).run()
  }

  var getLastMigrationIdFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      getLastMigrationFromDatabase.call(self)
        .success(function(meta) {
          emitter.emit('success', meta ? meta.to : null)
        })
        .error(function(err) {
          emitter.emit('error', err)
        })
    }).run()
  }

  var getFormattedDateString = function(s) {
    var result = null

    try {
      result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
    } catch(e) {
      throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
    }

    return result
  }

  var stringToDate = function(s) {
    return moment(getFormattedDateString(s), "YYYYMMDDHHmmss")
  }

  var saveSuccessfulMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .create({ from: from.migrationId, to: to.migrationId })
        .success(callback)
    })
  }

  var deleteUndoneMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaDAO().success(function(SequelizeMeta) {
      SequelizeMeta
        .find({ where: { from: from.migrationId.toString(), to: to.migrationId.toString() } })
        .success(function(meta) {
          meta.destroy().success(callback)
        })
    })
  }

  return Migrator
})()
<|endoftext|>


<commit_before>const fs     = require("fs")
    , path   = require("path")
    , moment = require("moment")

var Utils          = require("./utils")
  , Migration      = require("./migration")
  , DataTypes      = require("./data-types")

module.exports = (function() {
  var Migrator = function(sequelize, options) {
    this.sequelize = sequelize
    this.options   = Utils._.extend({
      path: __dirname + '/../migrations',
      from: null,
      to: null,
      logging: true
    }, options || {})
  }

  Object.defineProperty(Migrator.prototype, "queryInterface", {
    get: function() {
      return this.sequelize.getQueryInterface()
    }
  })

  Migrator.prototype.migrate = function(options) {
    var self = this

    options = Utils._.extend({
      method: 'up'
    }, options || {})

    return new Utils.CustomEventEmitter(function(emitter) {
      self.getUndoneMigrations(function(err, migrations) {
        if(err) {
          emitter.emit('failure', err)
        } else {
          var chainer = new Utils.QueryChainer

          if(options.method == 'down')
            migrations.reverse()

          migrations.forEach(function(migration) {
            chainer.add(migration, 'execute', [options], {
              before: function(migration) {
                if(self.options.logging)
                  console.log('Executing migration: ' + migration.filename)
              },
              after: function(migration) {
                if(self.options.logging)
                  console.log('Executed migration: ' + migration.filename)
              },
              success: function(migration, callback) {
                saveSuccessfulMigration.call(self, migrations[0], migration, callback)
              }
            })
          })

          chainer
            .runSerially({ skipOnError: true })
            .success(function() { emitter.emit('success', null) })
            .error(function(err) { emitter.emit('failure', err) })
        }
      })
    }).run()
  }

  Migrator.prototype.getUndoneMigrations = function(callback)  {
    var self = this

    var filterFrom = function(migrations, from, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isAfter(from, options) })
      callback && callback(null, result)
    }
    var filterTo = function(migrations, to, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isBefore(to, options) })
      callback && callback(null, result)
    }

    var migrationFiles = fs.readdirSync(this.options.path)

    var migrations = migrationFiles.map(function(file) {
      return new Migration(self, self.options.path + '/' + file)
    })

    migrations = migrations.sort(function(a,b){
      return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0])
    })

    if(this.options.from) {
      filterFrom(migrations, this.options.from, function(err, migrations) {
        if(self.options.to)
          filterTo(migrations, self.options.to, callback)
        else
          callback && callback(null, migrations)
      })
    } else {
      getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) {
        if(lastMigrationId) {
          filterFrom(migrations, lastMigrationId, function(err, migrations) {
            if(self.options.to)
              filterTo(migrations, self.options.to, callback)
            else
              callback && callback(null, migrations)
          }, { withoutEqual: true })
        } else {
          if(self.options.to)
            filterTo(migrations, self.options.to, callback)
          else
            callback && callback(null, migrations)
        }
      }).error(function(err) {
        callback && callback(err, null)
      })
    }
  }

  Migrator.prototype.findOrCreateSequelizeMetaModel = function(syncOptions) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var storedModel   = self.sequelize.modelFactoryManager.getModel('SequelizeMeta')
        , SequelizeMeta = storedModel

      if(!storedModel) {
        SequelizeMeta = self.sequelize.define('SequelizeMeta', {
          from: DataTypes.STRING,
          to:   DataTypes.STRING
        })
      }

      // force sync when model has newly created or if syncOptions are passed
      if(!storedModel || syncOptions) {
        SequelizeMeta
          .sync(syncOptions || {})
          .success(function() { emitter.emit('success', SequelizeMeta) })
          .error(function(err) { emitter.emit('failure', err) })
      } else {
        emitter.emit('success', SequelizeMeta)
      }
    }).run()
  }

  // private

  var getLastMigrationFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.findOrCreateSequelizeMetaModel().success(function(SequelizeMeta) {
        SequelizeMeta.find({ order: 'id DESC' }).success(function(meta) {
          emitter.emit('success', meta ? meta : null)
        }).error(function(err) { emitter.emit('failure', err) })
      }).error(function(err) { emitter.emit(err) })
    }).run()
  }

  var getLastMigrationIdFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      getLastMigrationFromDatabase.call(self)
        .success(function(meta) {
          emitter.emit('success', meta ? meta.to : null)
        })
        .error(function(err) {
          emitter.emit('failure', err)
        })
    }).run()
  }

  var getFormattedDateString = function(s) {
    var result = null

    try {
      result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
    } catch(e) {
      throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
    }

    return result
  }

  var stringToDate = function(s) {
    return moment(getFormattedDateString(s), "YYYYMMDDHHmmss")
  }

  var saveSuccessfulMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaModel().success(function(SequelizeMeta) {
      SequelizeMeta
        .create({ from: from.migrationId, to: to.migrationId })
        .success(callback)
    })
  }

  return Migrator
})()
<commit_msg>delete SequelizeMeta database entry after undoing a migration<commit_after>const fs     = require("fs")
    , path   = require("path")
    , moment = require("moment")

var Utils          = require("./utils")
  , Migration      = require("./migration")
  , DataTypes      = require("./data-types")

module.exports = (function() {
  var Migrator = function(sequelize, options) {
    this.sequelize = sequelize
    this.options   = Utils._.extend({
      path: __dirname + '/../migrations',
      from: null,
      to: null,
      logging: true
    }, options || {})
  }

  Object.defineProperty(Migrator.prototype, "queryInterface", {
    get: function() {
      return this.sequelize.getQueryInterface()
    }
  })

  Migrator.prototype.migrate = function(options) {
    var self = this

    options = Utils._.extend({
      method: 'up'
    }, options || {})

    return new Utils.CustomEventEmitter(function(emitter) {
      self.getUndoneMigrations(function(err, migrations) {
        if(err) {
          emitter.emit('failure', err)
        } else {
          var chainer = new Utils.QueryChainer
            , from    = migrations[0]

          if(options.method == 'down') {
            migrations.reverse()
            from = migrations[0]
          }

          migrations.forEach(function(migration) {
            chainer.add(migration, 'execute', [options], {
              before: function(migration) {
                if(self.options.logging)
                  console.log('Executing migration: ' + migration.filename)
              },
              after: function(migration) {
                if(self.options.logging)
                  console.log('Executed migration: ' + migration.filename)
              },
              success: function(migration, callback) {
                if(options.method == 'down')
                  deleteUndoneMigration.call(self, from, migration, callback)
                else
                  saveSuccessfulMigration.call(self, from, migration, callback)
              }
            })
          })

          chainer
            .runSerially({ skipOnError: true })
            .success(function() { emitter.emit('success', null) })
            .error(function(err) { emitter.emit('failure', err) })
        }
      })
    }).run()
  }

  Migrator.prototype.getUndoneMigrations = function(callback)  {
    var self = this

    var filterFrom = function(migrations, from, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isAfter(from, options) })
      callback && callback(null, result)
    }
    var filterTo = function(migrations, to, callback, options) {
      var result = migrations.filter(function(migration) { return migration.isBefore(to, options) })
      callback && callback(null, result)
    }

    var migrationFiles = fs.readdirSync(this.options.path)

    var migrations = migrationFiles.map(function(file) {
      return new Migration(self, self.options.path + '/' + file)
    })

    migrations = migrations.sort(function(a,b){
      return parseInt(a.filename.split('-')[0]) - parseInt(b.filename.split('-')[0])
    })

    if(this.options.from) {
      filterFrom(migrations, this.options.from, function(err, migrations) {
        if(self.options.to)
          filterTo(migrations, self.options.to, callback)
        else
          callback && callback(null, migrations)
      })
    } else {
      getLastMigrationIdFromDatabase.call(this).success(function(lastMigrationId) {
        if(lastMigrationId) {
          filterFrom(migrations, lastMigrationId, function(err, migrations) {
            if(self.options.to)
              filterTo(migrations, self.options.to, callback)
            else
              callback && callback(null, migrations)
          }, { withoutEqual: true })
        } else {
          if(self.options.to)
            filterTo(migrations, self.options.to, callback)
          else
            callback && callback(null, migrations)
        }
      }).error(function(err) {
        callback && callback(err, null)
      })
    }
  }

  Migrator.prototype.findOrCreateSequelizeMetaModel = function(syncOptions) {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      var storedModel   = self.sequelize.modelFactoryManager.getModel('SequelizeMeta')
        , SequelizeMeta = storedModel

      if(!storedModel) {
        SequelizeMeta = self.sequelize.define('SequelizeMeta', {
          from: DataTypes.STRING,
          to:   DataTypes.STRING
        })
      }

      // force sync when model has newly created or if syncOptions are passed
      if(!storedModel || syncOptions) {
        SequelizeMeta
          .sync(syncOptions || {})
          .success(function() { emitter.emit('success', SequelizeMeta) })
          .error(function(err) { emitter.emit('failure', err) })
      } else {
        emitter.emit('success', SequelizeMeta)
      }
    }).run()
  }

  // private

  var getLastMigrationFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      self.findOrCreateSequelizeMetaModel().success(function(SequelizeMeta) {
        SequelizeMeta.find({ order: 'id DESC' }).success(function(meta) {
          emitter.emit('success', meta ? meta : null)
        }).error(function(err) { emitter.emit('failure', err) })
      }).error(function(err) { emitter.emit(err) })
    }).run()
  }

  var getLastMigrationIdFromDatabase = function() {
    var self = this

    return new Utils.CustomEventEmitter(function(emitter) {
      getLastMigrationFromDatabase.call(self)
        .success(function(meta) {
          emitter.emit('success', meta ? meta.to : null)
        })
        .error(function(err) {
          emitter.emit('failure', err)
        })
    }).run()
  }

  var getFormattedDateString = function(s) {
    var result = null

    try {
      result = s.match(/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/).slice(1, 6).join('-')
    } catch(e) {
      throw new Error(s + ' is no valid migration timestamp format! Use YYYYMMDDHHmmss!')
    }

    return result
  }

  var stringToDate = function(s) {
    return moment(getFormattedDateString(s), "YYYYMMDDHHmmss")
  }

  var saveSuccessfulMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaModel().success(function(SequelizeMeta) {
      SequelizeMeta
        .create({ from: from.migrationId, to: to.migrationId })
        .success(callback)
    })
  }

  var deleteUndoneMigration = function(from, to, callback) {
    var self = this

    self.findOrCreateSequelizeMetaModel().success(function(SequelizeMeta) {
      SequelizeMeta
        .find({ from: from.migrationId, to: to.migrationId })
        .success(function(meta) {
          meta.destroy().success(callback)
        })
    })
  }

  return Migrator
})()
<|endoftext|>


<commit_before>var utils = require('../utils')
  , nodes = require('../nodes');

/**
 * Test if `val` matches the given `pattern`.
 *
 * Examples:
 *
 *     match('^foo(bar)?', foo)
 *     match('^foo(bar)?', foobar)
 *     match('^foo(bar)?', 'foo')
 *     match('^foo(bar)?', 'foobar')
 *     // => true
 *
 *     match('^foo(bar)?', 'bar')
 *     // => false
 *
 * @param {String} pattern
 * @param {String|Ident} val
 * @return {Boolean}
 * @api public
 */

module.exports = function match(pattern, val){
  utils.assertType(pattern, 'string', 'pattern');
  utils.assertString(val, 'val');
  var re = new RegExp(pattern.val);
  return new nodes.Boolean(re.test(val.string));
};
<commit_msg>Updated `match` function<commit_after>var utils = require('../utils')
  , nodes = require('../nodes');

/**
 * retrieves the matches when matching a `val`(string)
 * against a `pattern`(regular expression).
 *
 * Examples:
 *   $regex = '^(height|width)?([<>=]{1,})(.*)'
 *
 *   match($regex,'height>=sm')
 * 	 // => ('height>=sm' 'height' '>=' 'sm')
 * 	 // => also truthy
 *
 *   match($regex, 'lorem ipsum')
 *   // => null
 *
 * @param {String} pattern
 * @param {String|Ident} val
 * @param {String|Ident} [flags='']
 * @return {String|Null}
 * @api public
 */

module.exports = function match(pattern, val, flags){
  utils.assertType(pattern, 'string', 'pattern');
  utils.assertString(val, 'val');

  if (flags) {
    utils.assertString(val, 'string');
    flags = flags.string
  } else {
    flags = ''
  }

  var re = new RegExp(pattern.val, flags);
  return val.string.match(re);
};
<|endoftext|>


<commit_before>let async = require("async"),
    debug = require("debug")("nuimojs"),
    EventEmitter = require("events").EventEmitter;

// Direction is now deprecated, use Swipe, Fly, or Area instead
const Direction = {
    LEFT: 0,
    RIGHT: 1,
    UP: 2,
    DOWN: 3
};

const Swipe = {
    LEFT: 0,
    RIGHT: 1,
    UP: 2,
    DOWN: 3
};

const Fly = {
    LEFT: 0,
    RIGHT: 1
};

const Area = {
    LEFT: 4,
    RIGHT: 5,
    TOP: 6,
    BOTTOM: 7
};

//Configuration bits, see https://github.com/nathankunicki/nuimojs/pull/12
const Options = {
    //1, 1st (Binair 0b00000001) is used for the matrix.
    //2, 2nd bit (Binair 0b00000010) not used yet.
    //4, 3rd bit (Binair 0b00000100) not used yet.
    //8, 4th bit (Binair 0b00001000) not used yet.
    ONION_SKINNING: 16,//5th bit, Binair: 0b00010000 used for onion skinning effect (smooth transition between matrices, https://en.wikipedia.org/wiki/Onion_skinning)
    BUILTIN_MATRIX: 32 //6th bit, Binair: 0b00100000 used for displaying builtin matrices, when enabled, the first byte of the matrix indicates which builtin matrix is displayed. (Currently not documented which builtin matrixes are avaialable)
    //64, 7th bit (Binair 0b01000000) not used yet.
    //128,8th bit (Binair 0b10000000) not used yet.
};

const UUID = {
    Service: {
        BATTERY_STATUS: "180f",
        DEVICE_INFORMATION: "180a",
        LED_MATRIX: "f29b1523cb1940f3be5c7241ecb82fd1",
        USER_INPUT_EVENTS: "f29b1525cb1940f3be5c7241ecb82fd2"
    },
    Characteristic: {
        BATTERY_LEVEL: "2a19",
        DEVICE_INFORMATION: "2a29",
        FLY: "f29b1526cb1940f3be5c7241ecb82fd2",
        SWIPE: "f29b1527cb1940f3be5c7241ecb82fd2",
        ROTATION: "f29b1528cb1940f3be5c7241ecb82fd2",
        BUTTON_CLICK: "f29b1529cb1940f3be5c7241ecb82fd2"
    }
};

class Device extends EventEmitter {

    constructor (peripheral) {
        super();

        this.deviceType = "nuimo";

        this._peripheral = peripheral;
        this._LEDCharacteristic = null;
        this._batteryLevel = 100;
        this._rssi = -100; // Initialize as -100 - no signal
    }

    static get Direction () {
        return Direction;
    }

    static get Swipe () {
        return Swipe;
    }

    static get Fly () {
        return Fly;
    }

    static get Area () {
        return Area;
    }

    static get Options(){
        return Options;
    }

    get uuid () {
        return this._peripheral.uuid;
    }

    get batteryLevel () {
        return this._batteryLevel;
    }

    get rssi () {
        return this._rssi;
    }

    connect (callback) {

        let self = this;

        let batteryReady = false;
        let LEDReady = false;
        let userInputs = 0;

        this._peripheral.connect((err) => {

            this._rssi = this._peripheral.rssi;

            let rssiUpdateInterval = setInterval(() => {
                this._peripheral.updateRssi((err, rssi) => {
                    if (!err) {
                        if (this._rssi != rssi) {
                            this._rssi = rssi;
                            self.emit("rssiChange", rssi);
                        }
                    }
                });
            }, 2000);

            self._peripheral.on("disconnect", () => {
               clearInterval(rssiUpdateInterval);
            });

            self._peripheral.discoverServices([], (error, services) => {

                debug("Service discovery started");

                let serviceIndex = 0;

                async.whilst(
                    function () {
                        return (serviceIndex < services.length);
                    },
                    function (callback) {

                        let service = services[serviceIndex];

                        service.discoverCharacteristics([], (error, characteristics) => {

                            let characteristicIndex = 0;

                            async.whilst(
                                function () {
                                    return (characteristicIndex < characteristics.length);
                                },
                                function (callback) {

                                    let characteristic = characteristics[characteristicIndex];

                                    switch (service.uuid) {
                                        case UUID.Service.BATTERY_STATUS:
                                            batteryReady = true;
                                            debug("Found Battery characteristic");
                                            self._subscribeToCharacteristic(characteristic, self._handleBatteryChange.bind(self));
                                            characteristic.read();
                                            break;
                                        case UUID.Service.LED_MATRIX:
                                            self._LEDCharacteristic = characteristic;
                                            LEDReady = true;
                                            debug("Found LED characteristic");
                                            break;
                                        case UUID.Service.USER_INPUT_EVENTS:
                                            switch (characteristic.uuid) {
                                                case UUID.Characteristic.BUTTON_CLICK:
                                                    debug("Found Button Click characteristic");
                                                    self._subscribeToCharacteristic(characteristic, self._handleClick.bind(self));
                                                    break;
                                                case UUID.Characteristic.FLY:
                                                    debug("Found Fly characteristic");
                                                    self._subscribeToCharacteristic((characteristic), self._handleFlying.bind(self));
                                                    break;
                                                case UUID.Characteristic.ROTATION:
                                                    debug("Found Rotation characteristic");
                                                    self._subscribeToCharacteristic(characteristic, self._handleRotation.bind(self));
                                                    break;
                                                case UUID.Characteristic.SWIPE:
                                                    debug("Found Swipe characteristic");
                                                    self._subscribeToCharacteristic(characteristic, self._handleTouchSwipe.bind(self));
                                                    break;
                                            }
                                            userInputs++;
                                            break;
                                    }

                                    characteristicIndex++;
                                    return callback();

                                },
                                function (err) {
                                    serviceIndex++;
                                    return callback();
                                }
                            );
                        });
                    },
                    function (err) {

                        debug("Service discovery finished");

                        if (err !== null || batteryReady === false || LEDReady === false || userInputs < 5) {
                            self._peripheral.disconnect();
                            debug("Force disconnect");
                        } else {
                            debug("Emit connect");
                            self.emit("connect");
                        }

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

    setLEDMatrix (matrixData, brightness, timeout, options) {

        if (this._LEDCharacteristic) {
            let buf = Buffer.alloc(13);

            if (matrixData instanceof Buffer) {
                matrixData.copy(buf);
            } else {
                this._LEDArrayToBuffer(matrixData).copy(buf);
            }

            //Config bits are optional.
            if(typeof options === "number"){//Senic explained that they use config bits
                buf[10] += options;//These are encoded in the unused bits of the buffer
            }
            if(typeof options === "object"){
                if(options.onion_skinning == true){
                    buf[10] += 0b00010000;
                }
                if(options.builtin_matrix == true){
                    buf[10] += 0b00100000;
                }
            }
            //Using configbit = 16 (fifth bit is set, 0b00010000) will enable "Onion Skinning" effect in fading.
            //Other config bits aren't yet used or defined.

            buf[11] = brightness;
            buf[12] = Math.floor(timeout / 100);

            this._LEDCharacteristic.write(buf, true);
        } else {
            this.emit("error", new Error("Not fully connected"));
        }
    }

    _LEDArrayToBuffer (arr) {
        let buf = Buffer.alloc(11);

        for (let i = 0; i < 11; i++) {
            buf[i] = parseInt(arr.slice(i*8, i*8+8).reverse().join(""), 2);
        }

        return buf;
    }

    _subscribeToCharacteristic (characteristic, callback) {
        characteristic.on("read", (data, isNotification) => {
            return callback(data);
        });
        characteristic.subscribe((err) => {
            if (err) {
                this.emit("error", err);
            }
        });
    }

    _handleBatteryChange (data) {
        this._batteryLevel = data[0];
        debug("Battery level %s%", data[0]);
        this.emit("batteryLevelChange", data[0]);
    }

    _handleTouchSwipe (data) {
        let direction = data[0];
        if (direction <= 3) {
            this.emit("swipe", direction);
        } else {
            this.emit("touch", direction);
        }
        switch (direction) {
            case (Swipe.LEFT):
                debug("Swipe left");
                this.emit("swipeLeft");
                break;
            case (Swipe.RIGHT):
                debug("Swipe right");
                this.emit("swipeRight");
                break;
            case (Swipe.UP):
                debug("Swipe up");
                this.emit("swipeUp");
                break;
            case (Swipe.DOWN):
                debug("Swipe down");
                this.emit("swipeDown");
                break;
            case (Area.LEFT):
                debug("Touch left");
                this.emit("touchLeft");
                break;
            case (Area.RIGHT):
                debug("Touch right");
                this.emit("touchRight");
                break;
            case (Area.TOP):
                debug("Touch top");
                this.emit("touchTop");
                break;
            case (Area.BOTTOM):
                debug("Touch bottom");
                this.emit("touchBottom");
                break;
        }
    }

    _handleClick (data) {
        if (data[0] === 0) {
            debug("Button released");
            this.emit("release");
        } else {
            debug("Button pressed");
            this.emit("press");
        }
    }

    _handleRotation (data) {
        let amount = data.readInt16LE();
        debug("Rotate %s", amount);
        this.emit("rotate", amount);
    }

    _handleFlying (data) {

        let gesture = data[0],
            amount = data[1];

        switch (gesture) {
            case 0:
            case 1:
            case 2:
                let direction = gesture,
                    speed = amount;
                this.emit("fly", direction, speed); break;
                switch (direction) {
                    case (Fly.LEFT):
                        debug("Fly left %s", speed);
                        this.emit("flyLeft", speed);
                        break;
                    case (Fly.RIGHT):
                        debug("Fly right %s", speed);
                        this.emit("flyRight", speed);
                        break;
                }
                break;
            case 4:
                debug("Detect %s", amount);
                this.emit("detect", amount);
                this.emit("distance", amount);
                break;
        }
    }
}

module.exports = Device;
<commit_msg>Update device.js<commit_after>let async = require("async"),
    debug = require("debug")("nuimojs"),
    EventEmitter = require("events").EventEmitter;

// Direction is now deprecated, use Swipe, Fly, or Area instead
const Direction = {
    LEFT: 0,
    RIGHT: 1,
    UP: 2,
    DOWN: 3
};

const Swipe = {
    LEFT: 0,
    RIGHT: 1,
    UP: 2,
    DOWN: 3
};

const Fly = {
    LEFT: 0,
    RIGHT: 1
};

const Area = {
    LEFT: 4,
    RIGHT: 5,
    TOP: 6,
    BOTTOM: 7
};

//Configuration bits, see https://github.com/nathankunicki/nuimojs/pull/12
const Options = {
    //1, 1st (Binair 0b00000001) is used for the matrix.
    //2, 2nd bit (Binair 0b00000010) not used yet.
    //4, 3rd bit (Binair 0b00000100) not used yet.
    //8, 4th bit (Binair 0b00001000) not used yet.
    ONION_SKINNING: 16,//5th bit, Binair: 0b00010000 used for onion skinning effect (smooth transition between matrices, https://en.wikipedia.org/wiki/Onion_skinning)
    BUILTIN_MATRIX: 32 //6th bit, Binair: 0b00100000 used for displaying builtin matrices, when enabled, the first byte of the matrix indicates which builtin matrix is displayed. (Currently not documented which builtin matrixes are avaialable)
    //64, 7th bit (Binair 0b01000000) not used yet.
    //128,8th bit (Binair 0b10000000) not used yet.
};

const UUID = {
    Service: {
        BATTERY_STATUS: "180f",
        DEVICE_INFORMATION: "180a",
        LED_MATRIX: "f29b1523cb1940f3be5c7241ecb82fd1",
        USER_INPUT_EVENTS: "f29b1525cb1940f3be5c7241ecb82fd2"
    },
    Characteristic: {
        BATTERY_LEVEL: "2a19",
        DEVICE_INFORMATION: "2a29",
        FLY: "f29b1526cb1940f3be5c7241ecb82fd2",
        SWIPE: "f29b1527cb1940f3be5c7241ecb82fd2",
        ROTATION: "f29b1528cb1940f3be5c7241ecb82fd2",
        BUTTON_CLICK: "f29b1529cb1940f3be5c7241ecb82fd2"
    }
};

class Device extends EventEmitter {

    constructor (peripheral) {
        super();

        this.deviceType = "nuimo";

        this._peripheral = peripheral;
        this._LEDCharacteristic = null;
        this._batteryLevel = 100;
        this._rssi = -100; // Initialize as -100 - no signal
    }

    static get Direction () {
        return Direction;
    }

    static get Swipe () {
        return Swipe;
    }

    static get Fly () {
        return Fly;
    }

    static get Area () {
        return Area;
    }

    static get Options(){
        return Options;
    }

    get uuid () {
        return this._peripheral.uuid;
    }

    get batteryLevel () {
        return this._batteryLevel;
    }

    get rssi () {
        return this._rssi;
    }

    connect (callback) {

        let self = this;

        let batteryReady = false;
        let LEDReady = false;
        let userInputs = 0;

        this._peripheral.connect((err) => {

            this._rssi = this._peripheral.rssi;

            let rssiUpdateInterval = setInterval(() => {
                this._peripheral.updateRssi((err, rssi) => {
                    if (!err) {
                        if (this._rssi != rssi) {
                            this._rssi = rssi;
                            self.emit("rssiChange", rssi);
                        }
                    }
                });
            }, 2000);

            self._peripheral.on("disconnect", () => {
               clearInterval(rssiUpdateInterval);
            });

            self._peripheral.discoverServices([], (error, services) => {

                debug("Service discovery started");

                let serviceIndex = 0;

                async.whilst(
                    function () {
                        return (serviceIndex < services.length);
                    },
                    function (callback) {

                        let service = services[serviceIndex];

                        service.discoverCharacteristics([], (error, characteristics) => {

                            let characteristicIndex = 0;

                            async.whilst(
                                function () {
                                    return (characteristicIndex < characteristics.length);
                                },
                                function (callback) {

                                    let characteristic = characteristics[characteristicIndex];

                                    switch (service.uuid) {
                                        case UUID.Service.BATTERY_STATUS:
                                            batteryReady = true;
                                            debug("Found Battery characteristic");
                                            self._subscribeToCharacteristic(characteristic, self._handleBatteryChange.bind(self));
                                            characteristic.read();
                                            break;
                                        case UUID.Service.LED_MATRIX:
                                            self._LEDCharacteristic = characteristic;
                                            LEDReady = true;
                                            debug("Found LED characteristic");
                                            break;
                                        case UUID.Service.USER_INPUT_EVENTS:
                                            switch (characteristic.uuid) {
                                                case UUID.Characteristic.BUTTON_CLICK:
                                                    debug("Found Button Click characteristic");
                                                    self._subscribeToCharacteristic(characteristic, self._handleClick.bind(self));
                                                    break;
                                                case UUID.Characteristic.FLY:
                                                    debug("Found Fly characteristic");
                                                    self._subscribeToCharacteristic((characteristic), self._handleFlying.bind(self));
                                                    break;
                                                case UUID.Characteristic.ROTATION:
                                                    debug("Found Rotation characteristic");
                                                    self._subscribeToCharacteristic(characteristic, self._handleRotation.bind(self));
                                                    break;
                                                case UUID.Characteristic.SWIPE:
                                                    debug("Found Swipe characteristic");
                                                    self._subscribeToCharacteristic(characteristic, self._handleTouchSwipe.bind(self));
                                                    break;
                                            }
                                            userInputs++;
                                            break;
                                    }

                                    characteristicIndex++;
                                    return callback();

                                },
                                function (err) {
                                    serviceIndex++;
                                    return callback();
                                }
                            );
                        });
                    },
                    function (err) {

                        debug("Service discovery finished");

                        if (err !== null || batteryReady === false || LEDReady === false || userInputs < 5) {
                            self._peripheral.disconnect();
                            debug("Force disconnect");
                        } else {
                            debug("Emit connect");
                            self.emit("connect");
                        }

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

    setLEDMatrix (matrixData, brightness, timeout, options) {

        if (this._LEDCharacteristic) {
            let buf = Buffer.alloc(13);

            if (matrixData instanceof Buffer) {
                matrixData.copy(buf);
            } else {
                this._LEDArrayToBuffer(matrixData).copy(buf);
            }

            //Config bits are optional.
            if(typeof options === "number"){//Senic explained that they use config bits
                buf[10] += options;//These are encoded in the unused bits of the buffer
            }
            if(typeof options === "object"){
                if(options.onion_skinning == true){
                    buf[10] += Options.ONION_SKINNING;
                }
                if(options.builtin_matrix == true){
                    buf[10] += Options.BUILTIN_MATRIX;
                }
            }
            //Using configbit = 16 (fifth bit is set, 0b00010000) will enable "Onion Skinning" effect in fading.
            //Other config bits aren't yet used or defined.

            buf[11] = brightness;
            buf[12] = Math.floor(timeout / 100);

            this._LEDCharacteristic.write(buf, true);
        } else {
            this.emit("error", new Error("Not fully connected"));
        }
    }

    _LEDArrayToBuffer (arr) {
        let buf = Buffer.alloc(11);

        for (let i = 0; i < 11; i++) {
            buf[i] = parseInt(arr.slice(i*8, i*8+8).reverse().join(""), 2);
        }

        return buf;
    }

    _subscribeToCharacteristic (characteristic, callback) {
        characteristic.on("read", (data, isNotification) => {
            return callback(data);
        });
        characteristic.subscribe((err) => {
            if (err) {
                this.emit("error", err);
            }
        });
    }

    _handleBatteryChange (data) {
        this._batteryLevel = data[0];
        debug("Battery level %s%", data[0]);
        this.emit("batteryLevelChange", data[0]);
    }

    _handleTouchSwipe (data) {
        let direction = data[0];
        if (direction <= 3) {
            this.emit("swipe", direction);
        } else {
            this.emit("touch", direction);
        }
        switch (direction) {
            case (Swipe.LEFT):
                debug("Swipe left");
                this.emit("swipeLeft");
                break;
            case (Swipe.RIGHT):
                debug("Swipe right");
                this.emit("swipeRight");
                break;
            case (Swipe.UP):
                debug("Swipe up");
                this.emit("swipeUp");
                break;
            case (Swipe.DOWN):
                debug("Swipe down");
                this.emit("swipeDown");
                break;
            case (Area.LEFT):
                debug("Touch left");
                this.emit("touchLeft");
                break;
            case (Area.RIGHT):
                debug("Touch right");
                this.emit("touchRight");
                break;
            case (Area.TOP):
                debug("Touch top");
                this.emit("touchTop");
                break;
            case (Area.BOTTOM):
                debug("Touch bottom");
                this.emit("touchBottom");
                break;
        }
    }

    _handleClick (data) {
        if (data[0] === 0) {
            debug("Button released");
            this.emit("release");
        } else {
            debug("Button pressed");
            this.emit("press");
        }
    }

    _handleRotation (data) {
        let amount = data.readInt16LE();
        debug("Rotate %s", amount);
        this.emit("rotate", amount);
    }

    _handleFlying (data) {

        let gesture = data[0],
            amount = data[1];

        switch (gesture) {
            case 0:
            case 1:
            case 2:
                let direction = gesture,
                    speed = amount;
                this.emit("fly", direction, speed); break;
                switch (direction) {
                    case (Fly.LEFT):
                        debug("Fly left %s", speed);
                        this.emit("flyLeft", speed);
                        break;
                    case (Fly.RIGHT):
                        debug("Fly right %s", speed);
                        this.emit("flyRight", speed);
                        break;
                }
                break;
            case 4:
                debug("Detect %s", amount);
                this.emit("detect", amount);
                this.emit("distance", amount);
                break;
        }
    }
}

module.exports = Device;
<|endoftext|>


<commit_before>var o = require('atom').o(module)
var _o = require('bond')._o(module)
var __ = require('fiber').__.main(module)
var assert = require('assert')
var BSON = require('leafnode').BSON
var EJSON = require('mongodb-extended-json')
var carbond = require('../')
var assertRequests = require('./test-helper').assertRequests

/*******************************************************************************
 * parameter parsing tests
 */
var parser = o({
  _type: '../lib/ParameterParser',
})

var tests = [
  {
    datum: undefined,
    definition: {
      name: 'x',
      schema: { type: 'Undefined' }
    },
    result: undefined
  },

  {
    datum: '{ "$undefined": true }',
    definition: {
      name: 'x',
      schema: { type: 'Undefined' }
    },
    result: undefined
  },

  {
    datum: undefined,
    definition: {
      name: 'x',
      schema: { type: 'number' },
      default: 2
    },
    result: 2
  },

  {
    datum: '',
    definition: {
      name: 'x',
      schema: { type: 'number' },
      default: 2
    },
    result: 2
  },

  {
    datum: null, // XXX I think this is right but might interact strangely with qs parser so revisit this
    definition: {
      name: 'x',
      schema: { type: 'number' },
      default: 2
    },
    result: 2
  },

  {
    datum: "null",
    definition: {
      name: 'x',
      schema: { type: 'null' },
    },
    result: null
  },

  {
    datum: 3,
    definition: {
      name: 'x',
      schema: { type: 'number' }
    },
    error: true // should error since 3 is not a string
  },

  {
    datum: '3',
    definition: {
      name: 'x',
      schema: { type: 'number' }
    },
    result: 3
  },

  {
    datum: '"3"',
    definition: {
      name: 'x',
      schema: { type: 'string' }
    },
    result: "3"
  },

  {
    datum: 'true',
    definition: {
      name: 'x',
      schema: { type: 'boolean' }
    },
    result: true
  },

  {
    datum: 'hello',
    definition: {
      name: 'x',
      schema: { type: 'string'}
    },
    result: "hello"
  },

  {
    datum: '"hello"',
    definition: {
      name: 'x',
      schema: { type: 'string'}
    },
    result: "hello"
  },

  {
    datum: '{ "a": 1 }',
    definition: {
      name: 'x',
      schema: { type: 'object'}
    },
    result: { a:1 }
  },

  {
    datum: '[{ "a": 1 }]',
    definition: {
      name: 'x',
      schema: { type: 'array'}
    },
    result: [{ a:1 }]
  },

  {
    datum: '{ "$date": "1970-01-01T00:00:00.000Z" }',
    definition: {
      name: 'x',
      schema: { type: 'Date'}
    },
    result: new Date(0)
  },

]

function runTests() {
  var value
  var definition
  tests.forEach(function(test) {
    value = undefined
    definition = undefined
    definition = o(test.definition, '../lib/OperationParameter')
    if (test.error) {
      assert.throws(function() {
        value = parser.parse(test.datum, definition)
      })
    } else {
      try {
        value = parser.processParameterValue(test.datum, definition)
      } catch (e) {
        throw new Error("Error during test " + EJSON.stringify(test) + ". " + e)
      }
      assert.deepEqual(test.result, value, EJSON.stringify(test))
    }
  })
}

runTests()

<commit_msg>adding ObjectId tests<commit_after>var o = require('atom').o(module)
var _o = require('bond')._o(module)
var __ = require('fiber').__.main(module)
var assert = require('assert')
var ObjectId = require('leafnode').mongodb.ObjectId
var BSON = require('leafnode').BSON
var EJSON = require('mongodb-extended-json')
var carbond = require('../')
var assertRequests = require('./test-helper').assertRequests

/*******************************************************************************
 * parameter parsing tests
 */
var parser = o({
  _type: '../lib/ParameterParser',
})

var tests = [
  {
    datum: undefined,
    definition: {
      name: 'x',
      schema: { type: 'Undefined' }
    },
    result: undefined
  },

  {
    datum: '{ "$undefined": true }',
    definition: {
      name: 'x',
      schema: { type: 'Undefined' }
    },
    result: undefined
  },

  {
    datum: undefined,
    definition: {
      name: 'x',
      schema: { type: 'number' },
      default: 2
    },
    result: 2
  },

  {
    datum: '',
    definition: {
      name: 'x',
      schema: { type: 'number' },
      default: 2
    },
    result: 2
  },

  {
    datum: null, // XXX I think this is right but might interact strangely with qs parser so revisit this
    definition: {
      name: 'x',
      schema: { type: 'number' },
      default: 2
    },
    result: 2
  },

  {
    datum: "null",
    definition: {
      name: 'x',
      schema: { type: 'null' },
    },
    result: null
  },

  {
    datum: 3,
    definition: {
      name: 'x',
      schema: { type: 'number' }
    },
    error: true // should error since 3 is not a string
  },

  {
    datum: '3',
    definition: {
      name: 'x',
      schema: { type: 'number' }
    },
    result: 3
  },

  {
    datum: '"3"',
    definition: {
      name: 'x',
      schema: { type: 'string' }
    },
    result: "3"
  },

  {
    datum: 'true',
    definition: {
      name: 'x',
      schema: { type: 'boolean' }
    },
    result: true
  },

  {
    datum: 'hello',
    definition: {
      name: 'x',
      schema: { type: 'string'}
    },
    result: "hello"
  },

  {
    datum: '"hello"',
    definition: {
      name: 'x',
      schema: { type: 'string'}
    },
    result: "hello"
  },

  {
    datum: '{ "a": 1 }',
    definition: {
      name: 'x',
      schema: { type: 'object'}
    },
    result: { a:1 }
  },

  {
    datum: '[{ "a": 1 }]',
    definition: {
      name: 'x',
      schema: { type: 'array'}
    },
    result: [{ a:1 }]
  },

  {
    datum: '{ "$date": "1970-01-01T00:00:00.000Z" }',
    definition: {
      name: 'x',
      schema: { type: 'Date'}
    },
    result: new Date(0)
  },

  {
    datum: '{ "$oid": "57394f46d1236fa5367749e9" }',
    definition: {
      name: 'x',
      schema: { type: 'ObjectId'}
    },
    result: new ObjectId("57394f46d1236fa5367749e9")
  },

  {
    datum: '57394f46d1236fa5367749e9',
    definition: {
      name: 'x',
      schema: { type: 'ObjectId'}
    },
    result: new ObjectId("57394f46d1236fa5367749e9")
  },

]

function runTests() {
  var value
  var definition
  tests.forEach(function(test) {
    value = undefined
    definition = undefined
    definition = o(test.definition, '../lib/OperationParameter')
    if (test.error) {
      assert.throws(function() {
        value = parser.parse(test.datum, definition)
      })
    } else {
      try {
        value = parser.processParameterValue(test.datum, definition)
      } catch (e) {
        throw new Error("Error during test " + EJSON.stringify(test) + ". " + e)
      }
      assert.deepEqual(test.result, value, EJSON.stringify(test))
    }
  })
}

runTests()

<|endoftext|>


<commit_before>/*jshint node:true */

"use strict";

var mongoose = require("mongoose"),
	resource = require("./resource"),
	utils = require("./utils"),

	DocumentArray = mongoose.Types.DocumentArray,
	Embedded = mongoose.Types.Embedded,
	DocumentArraySchema = mongoose.Schema.Types.DocumentArray,
	EmbeddedSchema = mongoose.Schema.Types.Embedded,

	rxAllDots = /\./g,
	rxAllDollars = /\$/g,
	rxAllAsterisks = /\*/g;

function makePathRegexp(pattern) {
	pattern = pattern.replace(rxAllDots, ".")
	                 .replace(rxAllDollars, "[^.]+")
	                 .replace(rxAllAsterisks, ".+");

	return new RegExp("^" + pattern + "$");
}

function applyOverrides(resourcePath, overrides, context, definition) {
	overrides.forEach(function(override) {
		if (override.regexp.exec(resourcePath)) {
			Object.keys(override.methods).forEach(function(method) {
				definition[method] = override.methods[method].bind(context);
			});
		}
	});

	return definition;
}

function getPath(obj, path) {
	var parts = path.split(".");

	while (parts.length) {
		if (!obj) {
			return;
		}

		obj = obj[parts.shift()];
	}

	return obj;
}

function getObject(req, doc, urlpath, options) {
	var obj = doc.toObject(options.toObject);

	utils.addHref(req, obj, urlpath);

	if (doc.schema) {
		doc.schema.eachPath(function(path, type) {
			var suburlpath = path.replace(rxAllDots, "/");

			if (type instanceof DocumentArraySchema) {
				var objs = getPath(obj, path);

				if (objs) {
					objs.forEach(function(subobj) {
						utils.addHref(req, subobj, urlpath + "/" + suburlpath + "/" + subobj._id);
					});
				}
			}
		});
	}

	return obj;
}

/**
 * Mongoose document property resource helper
 */
function mongooseValueResource(urlpath, doc, path, options) {
	var value = doc.get(path);

	return applyOverrides(
		urlpath,
		options.overrides,
		value,
		{
			get: function(req, cb) {
				process.nextTick(function() {
					cb(null, value);
				});
			},

			put: function(req, patch, cb) {
				doc.set(path, req.body);
				doc.save(function(err) {
					cb(err, doc.get(path));
				});
			}
		}
	);
}

/**
 * Mongoose document resource helper
 */
function mongooseDocResource(urlpath, doc, options) {
	return applyOverrides(
		urlpath,
		options.overrides,
		doc,
		{
			sub: function(id, cb) {
				var subitem = doc.get(id),
					suburlpath = urlpath + "/" + id;

				if (subitem instanceof DocumentArray) {
					subitem = mongooseDocArrayResource(suburlpath, doc, id, options);
				} else if (subitem instanceof Embedded) {
					subitem = mongooseDocResource(suburlpath, subitem, options);
				} else {
					subitem = mongooseValueResource(suburlpath, doc, id, options);
				}

				process.nextTick(function() {
					cb(null, subitem);
				});
			},

			get: function(req, cb) {
				var body = getObject(req, doc, urlpath, options);

				process.nextTick(function() {
					cb(null, body);
				});
			},

			put: function(req, patch, cb) {
				var resource = this;

				doc.update(req.body, function(err) {
					if (err) {
						cb(err);
					} else {
						resource.get(req, cb);
					}
				});
			},

			del: function(req, cb) {
				doc.remove(cb);
			}
		}
	);
}

/**
 * Mongoose document array resource helper
 */
function mongooseDocArrayResource(urlpath, doc, path, options) {
	var docArray = doc.get(path);

	return applyOverrides(
		urlpath,
		options.overrides,
		docArray,
		{
			sub: function(id, cb) {
				var subdoc = docArray.id(id);

				process.nextTick(function() {
					cb(null, subdoc ? mongooseDocResource(urlpath + "/" + id, subdoc, options) : null);
				});
			},

			count: function(req, cb) {
				process.nextTick(function() {
					cb(null, docArray.length);
				});
			},

			list: function(req, offset, limit, cb) {
				var sdocs;

				if (limit > 0) {
					sdocs = docArray.slice(offset, offset+limit);
				} else {
					sdocs = docArray.slice(offset);
				}

				process.nextTick(function() {
					cb(null, sdocs.map(function(sdoc) {
						return getObject(req, sdoc, urlpath + "/" + sdoc._id, options);
					}));
				});
			},

			post: function(req, cb) {
				docArray.push(req.body);
				doc.save(cb);
			}
		}
	);
}

/**
 * Define a REST resource that gives access to a Mongoose model collection
 *
 * @param name resource name
 * @param model Mongoose model
 * @param options resource options object
 *
 * Possible option keys:
 *   "sort"		  mongodb sort query, defaults to no sort
 *   "query"      optional, function to create a mongoose query.  Defaults
 *                to calling model.find().
 *   "toObject"   options to pass to MongooseDocument#toObject calls
 *   "overrides"  method overrides, see below
 *
 * Method overrides are possible by passing an object whose keys are resource
 * path patterns:
 *
 *     {
 *         "resourcename": {
 *              get: function(req, cb) { ... }
 *         },
 *         "resourcename/property": {
 *              // Disable get
 *              get: undefined
 *         },
 *     }
 *
 * A "$" character in a path pattern will match any single path element:
 *
 *     {
 *         // Will match /resourcename/a/foo/b
 *         // but not /resourcename/a/foo/bar/b
 *         "resourcename/a/$/b": {
 *              get: function(req, cb) { ... }
 *         }
 *     }
 *
 * A "*" character in a path pattern will match one or more path elements:
 *
 *     {
 *         // Will match /resourcename/a/foo/b and /resourcename/a/foo/bar/b
 *         // but not /resourcename/a/b
 *         // (additional space added to prevent closing this comment)
 *         "resourcename/a/* /b": {
 *              get: function(req, cb) { ... }
 *         }
 *     }
 *
 * All methods overrides receive the current object (Model, Document,
 * DocumentArray or property value) as `this`.
 */
function mongooseResource(name, model, options) {
	options = options || {};
	options.query = options.query || function() { return model.find(); };
	options.overrides = Object.keys(options.overrides || {}).map(function(pattern) {
		return {
			regexp: makePathRegexp(pattern),
			methods: options.overrides[pattern]
		};
	});

	resource(name, applyOverrides(
		name,
		options.overrides,
		model,
		{
			sub: function(id, cb) {
				options.query().find({ _id: id }).findOne(function(err, item) {
					cb(err, item ? mongooseDocResource(name + "/" + id, item, options) : null);
				});
			},

			count: function(req, cb) {
				return model.count(cb);
			},

			list: function(req, offset, limit, cb) {
				var query = options.query().skip(offset).limit(limit);

				if (options.sort) {
					query = query.sort(options.sort);
				}

				return query.exec(function(err, items) {
					cb(err, items.map(function(item) {
						return getObject(req, item, name + "/" + item._id, options);
					}));
				});
			},

			post: function(req, cb) {
				model.create(req.body, cb);
			}
		}
	));
}

module.exports = mongooseResource;
<commit_msg>mongooseRessource: move current object to overrides 1st argument<commit_after>/*jshint node:true */

"use strict";

var mongoose = require("mongoose"),
	resource = require("./resource"),
	utils = require("./utils"),

	DocumentArray = mongoose.Types.DocumentArray,
	Embedded = mongoose.Types.Embedded,
	DocumentArraySchema = mongoose.Schema.Types.DocumentArray,
	EmbeddedSchema = mongoose.Schema.Types.Embedded,

	rxAllDots = /\./g,
	rxAllDollars = /\$/g,
	rxAllAsterisks = /\*/g;

function makePathRegexp(pattern) {
	pattern = pattern.replace(rxAllDots, ".")
	                 .replace(rxAllDollars, "[^.]+")
	                 .replace(rxAllAsterisks, ".+");

	return new RegExp("^" + pattern + "$");
}

function applyOverrides(resourcePath, overrides, context, definition) {
	overrides.forEach(function(override) {
		if (override.regexp.exec(resourcePath)) {
			Object.keys(override.methods).forEach(function(method) {
				if (!override.methods[method]) {
					delete definition[method];
				} else {
					definition[method] = override.methods[method].bind(null, context);
				}
			});
		}
	});

	return definition;
}

function getPath(obj, path) {
	var parts = path.split(".");

	while (parts.length) {
		if (!obj) {
			return;
		}

		obj = obj[parts.shift()];
	}

	return obj;
}

function getObject(req, doc, urlpath, options) {
	var obj = doc.toObject(options.toObject);

	utils.addHref(req, obj, urlpath);

	if (doc.schema) {
		doc.schema.eachPath(function(path, type) {
			var suburlpath = path.replace(rxAllDots, "/");

			if (type instanceof DocumentArraySchema) {
				var objs = getPath(obj, path);

				if (objs) {
					objs.forEach(function(subobj) {
						utils.addHref(req, subobj, urlpath + "/" + suburlpath + "/" + subobj._id);
					});
				}
			}
		});
	}

	return obj;
}

/**
 * Mongoose document property resource helper
 */
function mongooseValueResource(urlpath, doc, path, options) {
	var value = doc.get(path);

	return applyOverrides(
		urlpath,
		options.overrides,
		value,
		{
			get: function(req, cb) {
				process.nextTick(function() {
					cb(null, value);
				});
			},

			put: function(req, patch, cb) {
				doc.set(path, req.body);
				doc.save(function(err) {
					cb(err, doc.get(path));
				});
			}
		}
	);
}

/**
 * Mongoose document resource helper
 */
function mongooseDocResource(urlpath, doc, options) {
	return applyOverrides(
		urlpath,
		options.overrides,
		doc,
		{
			sub: function(id, cb) {
				var subitem = doc.get(id),
					suburlpath = urlpath + "/" + id;

				if (subitem instanceof DocumentArray) {
					subitem = mongooseDocArrayResource(suburlpath, doc, id, options);
				} else if (subitem instanceof Embedded) {
					subitem = mongooseDocResource(suburlpath, subitem, options);
				} else {
					subitem = mongooseValueResource(suburlpath, doc, id, options);
				}

				process.nextTick(function() {
					cb(null, subitem);
				});
			},

			get: function(req, cb) {
				var body = getObject(req, doc, urlpath, options);

				process.nextTick(function() {
					cb(null, body);
				});
			},

			put: function(req, patch, cb) {
				var resource = this;

				doc.update(req.body, function(err) {
					if (err) {
						cb(err);
					} else {
						resource.get(req, cb);
					}
				});
			},

			del: function(req, cb) {
				doc.remove(cb);
			}
		}
	);
}

/**
 * Mongoose document array resource helper
 */
function mongooseDocArrayResource(urlpath, doc, path, options) {
	var docArray = doc.get(path);

	return applyOverrides(
		urlpath,
		options.overrides,
		docArray,
		{
			sub: function(id, cb) {
				var subdoc = docArray.id(id);

				process.nextTick(function() {
					cb(null, subdoc ? mongooseDocResource(urlpath + "/" + id, subdoc, options) : null);
				});
			},

			count: function(req, cb) {
				process.nextTick(function() {
					cb(null, docArray.length);
				});
			},

			list: function(req, offset, limit, cb) {
				var sdocs;

				if (limit > 0) {
					sdocs = docArray.slice(offset, offset+limit);
				} else {
					sdocs = docArray.slice(offset);
				}

				process.nextTick(function() {
					cb(null, sdocs.map(function(sdoc) {
						return getObject(req, sdoc, urlpath + "/" + sdoc._id, options);
					}));
				});
			},

			post: function(req, cb) {
				docArray.push(req.body);
				doc.save(cb);
			}
		}
	);
}

/**
 * Define a REST resource that gives access to a Mongoose model collection
 *
 * @param name resource name
 * @param model Mongoose model
 * @param options resource options object
 *
 * Possible option keys:
 *   "sort"		  mongodb sort query, defaults to no sort
 *   "query"      optional, function to create a mongoose query.  Defaults
 *                to calling model.find().
 *   "toObject"   options to pass to MongooseDocument#toObject calls
 *   "overrides"  method overrides, see below
 *
 * Method overrides are possible by passing an object whose keys are resource
 * path patterns:
 *
 *     {
 *         "resourcename": {
 *              get: function(obj, req, cb) { ... }
 *         },
 *         "resourcename/property": {
 *              // Disable get
 *              get: undefined
 *         },
 *     }
 *
 * A "$" character in a path pattern will match any single path element:
 *
 *     {
 *         // Will match /resourcename/a/foo/b
 *         // but not /resourcename/a/foo/bar/b
 *         "resourcename/a/$/b": {
 *              get: function(obj, req, cb) { ... }
 *         }
 *     }
 *
 * A "*" character in a path pattern will match one or more path elements:
 *
 *     {
 *         // Will match /resourcename/a/foo/b and /resourcename/a/foo/bar/b
 *         // but not /resourcename/a/b
 *         // (additional space added to prevent closing this comment)
 *         "resourcename/a/* /b": {
 *              get: function(obj, req, cb) { ... }
 *         }
 *     }
 *
 * All methods overrides work exactly the same as normal yarm methods, except
 * they receive the current object (Model, Document, DocumentArray or property
 * value) they first argument in addition.
 */
function mongooseResource(name, model, options) {
	options = options || {};
	options.query = options.query || function() { return model.find(); };
	options.overrides = Object.keys(options.overrides || {}).map(function(pattern) {
		return {
			regexp: makePathRegexp(pattern),
			methods: options.overrides[pattern]
		};
	});

	resource(name, applyOverrides(
		name,
		options.overrides,
		model,
		{
			sub: function(id, cb) {
				options.query().find({ _id: id }).findOne(function(err, item) {
					cb(err, item ? mongooseDocResource(name + "/" + id, item, options) : null);
				});
			},

			count: function(req, cb) {
				return model.count(cb);
			},

			list: function(req, offset, limit, cb) {
				var query = options.query().skip(offset).limit(limit);

				if (options.sort) {
					query = query.sort(options.sort);
				}

				return query.exec(function(err, items) {
					cb(err, items.map(function(item) {
						return getObject(req, item, name + "/" + item._id, options);
					}));
				});
			},

			post: function(req, cb) {
				model.create(req.body, cb);
			}
		}
	));
}

module.exports = mongooseResource;
<|endoftext|>