<commit_before>var CHEMICAL_ELEMENTS = {
  0: {'background-color': '#F1EEFA',
      'border-color': '#AAA',
      'color': '#F1EEFA',
      'name': 'n/a',
      'symbol': ''},

  1: {'background-color': '#dde',
      'border-color': '#888',
      'color': 'black',
      'name': 'hydrogen',
      'symbol': 'H'},

  2: {'background-color': '#edd',
      'border-color': '#888',
      'color': 'black',
      'name': 'helium',
      'symbol': 'He'},
};

var BLOCK_FONT = "12px Verdana";
var DROP_DELAY = 300;
var SLIDE_DELAY = 200;

var GRID_HEIGHT = 20;
var GRID_WIDTH = 10;

var BLOCK_WIDTH, BLOCK_HEIGHT;
var BLOCK_SPACING_WIDTH, BLOCK_SPACING_HEIGHT;

var TETRINIMO_TEMPLATES = {
  jBlock: ['xx',
           'x ',
           'x '],
  lBlock: ['xx',
           ' x',
           ' x'],
  square: ['xx',
           'xx'],
  sBlock: ['x ',
           'xx',
           ' x'],
  zBlock: [' x',
           'xx',
           'x '],
  line:   ['x',
           'x',
           'x',
           'x'],
  tBlock: ['xxx',
           ' x ']
};
var processTetrinimos = function() {
  var tetraShape = new Object;
  for(var shape in TETRINIMO_TEMPLATES) {
    if( TETRINIMO_TEMPLATES.hasOwnProperty(shape)) {
      tetraShape[shape] = new Array;
      var currentShape = TETRINIMO_TEMPLATES[shape];
      for(var row in currentShape) {
        for(var col in currentShape[row]) {
          if(currentShape[row][col] == 'x') {
            tetraShape[shape].push({x: parseInt(col, 10),
                                    y: parseInt(row, 10)});
          }
        }
      }
    }
  }
  return tetraShape
}
var TETRINIMO_SHAPES = processTetrinimos();

var TetrisBoard = function(args) {
  this.board = new Array;
  this.tetrinimo = args.tetrinimo;

  for(var row = 0; row < 20; row++) {
    this.board[row] = new Array;
    for(var col = 0; col < 10; col++) {
      this.board[row][col] = 0;
    }
  };
}
TetrisBoard.prototype.blit = function(clear) {
  var element = this.tetrinimo.element;

  if(clear) {
    element = 0;
  }

  for(var block in this.tetrinimo.blocks) {
    var currentBlock = this.tetrinimo.blocks[block];
    this.board[currentBlock.y][currentBlock.x] = element;
  }
};
TetrisBoard.prototype.detectCollision = function() {
  for(var block in this.tetrinimo.blocks) {
    currentBlock = this.tetrinimo.blocks[block];
    if(currentBlock.y >= GRID_HEIGHT) {
      return 'floor';
    }
    else if(currentBlock.x < 0 || currentBlock.x >= GRID_WIDTH) {
      return 'wall';
    }
    else if(this.board[currentBlock.y][currentBlock.x] !== 0) {
      return 'block';
    }
  }
  return 'clear';
};
TetrisBoard.prototype.dropBlock = function() {
  this.blit(true);
  this.tetrinimo.drop();
  var collision = this.detectCollision();
  if(collision == 'floor' || collision == 'block') {
    this.tetrinimo.raise();
    clearInterval(this.intervalID);
  }
  this.blit();
};
TetrisBoard.prototype.slideBlock = function(direction) {
  this.blit(true);
  this.tetrinimo.slide(direction);
  this.blit();
};

var Tetrinimo = function(args) {
  this.element = args.element;
  var shape = args.shape;

  this.blocks = shape.map(function(block) {
    return {x: new Number(block.x), y: new Number(block.y)};
  });

  this.row = 0;
  this.col = 5;

  for(var block in this.blocks) {
    this.blocks[block].y += this.row;
    this.blocks[block].x += this.col;
  }
}
Tetrinimo.prototype.raise = function() {
  this.row--;
  for(var block in this.blocks) {
    this.blocks[block].y--;
  }
};
Tetrinimo.prototype.drop = function() {
  this.row++;
  for(var block in this.blocks) {
    this.blocks[block].y++;
  }
};
var directToInt = {
  'left': -1,
  'right': 1
};
Tetrinimo.prototype.slide = function(direction) {
  this.blocks.forEach(function(block) {
    block.x += directToInt[direction];
  });
};

var keyCodes = {
  37: 'left',
  38: 'up',
  39: 'right',
  40: 'down',
  8: 'backspace'
}

var View = function(args) {
  this.context = document.querySelector('canvas').getContext('2d');
  this.gameBoard = args.gameBoard;

  addEventListener('keydown', function(event) {
    var pressedKey = keyCodes[event.keyCode];
    if(pressedKey == 'left' || pressedKey == 'right') {
      event.preventDefault();
      console.log('pressedKey', pressedKey);
      if(this.pressed !== pressedKey) this.pressed = pressedKey;
    }
  });

  addEventListener('keyup', function(event) {
    var releasedKey = keyCodes[event.keyCode];
    if(releasedKey == 'left' || releasedKey == 'right') {
      event.preventDefault();
      console.log("releasedKey", releasedKey);
    }
  });
}
View.prototype.drawBoard = function(board) {
  var context = this.context
  context.lineWidth = 4;
  context.font = BLOCK_FONT;

  board.forEach(function(row, rIndex) {
    row.forEach(function(col, cIndex) {
      context.fillStyle = CHEMICAL_ELEMENTS[col]['background-color'];
      context.strokeStyle = CHEMICAL_ELEMENTS[col]['border-color'];

      context.fillRect((cIndex * BLOCK_SPACING_WIDTH) + 5, (rIndex * BLOCK_SPACING_HEIGHT) + 5, BLOCK_WIDTH, BLOCK_HEIGHT);
      context.strokeRect((cIndex * BLOCK_SPACING_WIDTH) + 5, (rIndex * BLOCK_SPACING_HEIGHT) + 5, BLOCK_WIDTH, BLOCK_HEIGHT);
      context.fillStyle = CHEMICAL_ELEMENTS[col]['color'];
      context.fillText(CHEMICAL_ELEMENTS[col].symbol, (cIndex * BLOCK_SPACING_WIDTH) + (BLOCK_SPACING_WIDTH / 2) - 4, (rIndex * BLOCK_SPACING_HEIGHT) + (BLOCK_SPACING_HEIGHT / 2) + 4);
    });
  });
};
View.prototype.animate = function(board) {
  var lastTime = null;
  var progress = true;
  var self = this;

  var animate = function(time) {
    if(lastTime) {
      var timeStep = Math.min(time - lastTime, 100) / 1000;
      progress = timeStep < 2000;
    }
    lastTime = time;
    self.drawBoard(board);
    if(progress) {
      requestAnimationFrame(animate);
    }
  }
  requestAnimationFrame(animate);
};

var ready = function(fn) {
  if(document.readyState != 'loading') {
    fn();
  }
  else {
    document.addEventListener('DOMContentLoaded', fn);
  }
}
ready(function() {
  var canvas = document.querySelector('canvas');
  BLOCK_SPACING_HEIGHT = canvas.getAttribute('height') / 20;
  BLOCK_SPACING_WIDTH = canvas.getAttribute('width') / 10;
  BLOCK_HEIGHT = BLOCK_SPACING_HEIGHT - 10;
  BLOCK_WIDTH = BLOCK_SPACING_WIDTH - 10;

  window.lineBlock = new Tetrinimo({element: 1, shape: TETRINIMO_SHAPES.line});
  window.gameBoard = new TetrisBoard({tetrinimo: lineBlock});
  window.gameView = new View({gameBoard: gameBoard});

  gameBoard.blit({tetrinimo: lineBlock});
  gameView.animate(gameBoard.board);

  gameBoard.intervalID = setInterval(function() {
    gameBoard.dropBlock(lineBlock);
  }, DROP_DELAY);
});
<commit_msg>Update View keyup listener to set pressed to null<commit_after>var CHEMICAL_ELEMENTS = {
  0: {'background-color': '#F1EEFA',
      'border-color': '#AAA',
      'color': '#F1EEFA',
      'name': 'n/a',
      'symbol': ''},

  1: {'background-color': '#dde',
      'border-color': '#888',
      'color': 'black',
      'name': 'hydrogen',
      'symbol': 'H'},

  2: {'background-color': '#edd',
      'border-color': '#888',
      'color': 'black',
      'name': 'helium',
      'symbol': 'He'},
};

var BLOCK_FONT = "12px Verdana";
var DROP_DELAY = 300;
var SLIDE_DELAY = 200;

var GRID_HEIGHT = 20;
var GRID_WIDTH = 10;

var BLOCK_WIDTH, BLOCK_HEIGHT;
var BLOCK_SPACING_WIDTH, BLOCK_SPACING_HEIGHT;

var TETRINIMO_TEMPLATES = {
  jBlock: ['xx',
           'x ',
           'x '],
  lBlock: ['xx',
           ' x',
           ' x'],
  square: ['xx',
           'xx'],
  sBlock: ['x ',
           'xx',
           ' x'],
  zBlock: [' x',
           'xx',
           'x '],
  line:   ['x',
           'x',
           'x',
           'x'],
  tBlock: ['xxx',
           ' x ']
};
var processTetrinimos = function() {
  var tetraShape = new Object;
  for(var shape in TETRINIMO_TEMPLATES) {
    if( TETRINIMO_TEMPLATES.hasOwnProperty(shape)) {
      tetraShape[shape] = new Array;
      var currentShape = TETRINIMO_TEMPLATES[shape];
      for(var row in currentShape) {
        for(var col in currentShape[row]) {
          if(currentShape[row][col] == 'x') {
            tetraShape[shape].push({x: parseInt(col, 10),
                                    y: parseInt(row, 10)});
          }
        }
      }
    }
  }
  return tetraShape
}
var TETRINIMO_SHAPES = processTetrinimos();

var TetrisBoard = function(args) {
  this.board = new Array;
  this.tetrinimo = args.tetrinimo;

  for(var row = 0; row < 20; row++) {
    this.board[row] = new Array;
    for(var col = 0; col < 10; col++) {
      this.board[row][col] = 0;
    }
  };
}
TetrisBoard.prototype.blit = function(clear) {
  var element = this.tetrinimo.element;

  if(clear) {
    element = 0;
  }

  for(var block in this.tetrinimo.blocks) {
    var currentBlock = this.tetrinimo.blocks[block];
    this.board[currentBlock.y][currentBlock.x] = element;
  }
};
TetrisBoard.prototype.detectCollision = function() {
  for(var block in this.tetrinimo.blocks) {
    currentBlock = this.tetrinimo.blocks[block];
    if(currentBlock.y >= GRID_HEIGHT) {
      return 'floor';
    }
    else if(currentBlock.x < 0 || currentBlock.x >= GRID_WIDTH) {
      return 'wall';
    }
    else if(this.board[currentBlock.y][currentBlock.x] !== 0) {
      return 'block';
    }
  }
  return 'clear';
};
TetrisBoard.prototype.dropBlock = function() {
  this.blit(true);
  this.tetrinimo.drop();
  var collision = this.detectCollision();
  if(collision == 'floor' || collision == 'block') {
    this.tetrinimo.raise();
    clearInterval(this.intervalID);
  }
  this.blit();
};
TetrisBoard.prototype.slideBlock = function(direction) {
  this.blit(true);
  this.tetrinimo.slide(direction);
  this.blit();
};

var Tetrinimo = function(args) {
  this.element = args.element;
  var shape = args.shape;

  this.blocks = shape.map(function(block) {
    return {x: new Number(block.x), y: new Number(block.y)};
  });

  this.row = 0;
  this.col = 5;

  for(var block in this.blocks) {
    this.blocks[block].y += this.row;
    this.blocks[block].x += this.col;
  }
}
Tetrinimo.prototype.raise = function() {
  this.row--;
  for(var block in this.blocks) {
    this.blocks[block].y--;
  }
};
Tetrinimo.prototype.drop = function() {
  this.row++;
  for(var block in this.blocks) {
    this.blocks[block].y++;
  }
};
var directToInt = {
  'left': -1,
  'right': 1
};
Tetrinimo.prototype.slide = function(direction) {
  this.blocks.forEach(function(block) {
    block.x += directToInt[direction];
  });
};

var keyCodes = {
  37: 'left',
  38: 'up',
  39: 'right',
  40: 'down',
  8: 'backspace'
}

var View = function(args) {
  this.context = document.querySelector('canvas').getContext('2d');
  this.gameBoard = args.gameBoard;

  addEventListener('keydown', function(event) {
    var pressedKey = keyCodes[event.keyCode];
    if(pressedKey == 'left' || pressedKey == 'right') {
      event.preventDefault();
      console.log('pressedKey', pressedKey);
      if(this.pressed !== pressedKey) this.pressed = pressedKey;
    }
  });

  addEventListener('keyup', function(event) {
    var releasedKey = keyCodes[event.keyCode];
    if(releasedKey == 'left' || releasedKey == 'right') {
      event.preventDefault();
      console.log("releasedKey", releasedKey);
      this.pressed = null;
    }
  });
}
View.prototype.drawBoard = function(board) {
  var context = this.context
  context.lineWidth = 4;
  context.font = BLOCK_FONT;

  board.forEach(function(row, rIndex) {
    row.forEach(function(col, cIndex) {
      context.fillStyle = CHEMICAL_ELEMENTS[col]['background-color'];
      context.strokeStyle = CHEMICAL_ELEMENTS[col]['border-color'];

      context.fillRect((cIndex * BLOCK_SPACING_WIDTH) + 5, (rIndex * BLOCK_SPACING_HEIGHT) + 5, BLOCK_WIDTH, BLOCK_HEIGHT);
      context.strokeRect((cIndex * BLOCK_SPACING_WIDTH) + 5, (rIndex * BLOCK_SPACING_HEIGHT) + 5, BLOCK_WIDTH, BLOCK_HEIGHT);
      context.fillStyle = CHEMICAL_ELEMENTS[col]['color'];
      context.fillText(CHEMICAL_ELEMENTS[col].symbol, (cIndex * BLOCK_SPACING_WIDTH) + (BLOCK_SPACING_WIDTH / 2) - 4, (rIndex * BLOCK_SPACING_HEIGHT) + (BLOCK_SPACING_HEIGHT / 2) + 4);
    });
  });
};
View.prototype.animate = function(board) {
  var lastTime = null;
  var progress = true;
  var self = this;

  var animate = function(time) {
    if(lastTime) {
      var timeStep = Math.min(time - lastTime, 100) / 1000;
      progress = timeStep < 2000;
    }
    lastTime = time;
    self.drawBoard(board);
    if(progress) {
      requestAnimationFrame(animate);
    }
  }
  requestAnimationFrame(animate);
};

var ready = function(fn) {
  if(document.readyState != 'loading') {
    fn();
  }
  else {
    document.addEventListener('DOMContentLoaded', fn);
  }
}
ready(function() {
  var canvas = document.querySelector('canvas');
  BLOCK_SPACING_HEIGHT = canvas.getAttribute('height') / 20;
  BLOCK_SPACING_WIDTH = canvas.getAttribute('width') / 10;
  BLOCK_HEIGHT = BLOCK_SPACING_HEIGHT - 10;
  BLOCK_WIDTH = BLOCK_SPACING_WIDTH - 10;

  window.lineBlock = new Tetrinimo({element: 1, shape: TETRINIMO_SHAPES.line});
  window.gameBoard = new TetrisBoard({tetrinimo: lineBlock});
  window.gameView = new View({gameBoard: gameBoard});

  gameBoard.blit({tetrinimo: lineBlock});
  gameView.animate(gameBoard.board);

  gameBoard.intervalID = setInterval(function() {
    gameBoard.dropBlock(lineBlock);
  }, DROP_DELAY);
});
<|endoftext|>


<commit_before>var sequence = require('futures').sequence;

exports.add = function (respond, mealID, mealtimeID, userID) {

    var seq = sequence();
    var ObjectId = module.mongoose.Types.ObjectId;

    var mealObjectID     = new ObjectId(mealID);
    var userObjectID     = new ObjectId(userID);
    var mealtimeObjectID = new ObjectId(mealtimeID);

    seq

    // amount of meals < 0
    .then(function (next) {

        module.models.meal
            .findOne({ _id: mealObjectID })
            .exec(function (err, result) {
                if (err) return respond(400, err);

                if(!result) {
                    return respond(400, {
                        statusInternal: 3,
                        statusText: 'meal not found'
                    });
                }

                if(!result.amount || result.amount < 1) {
                    return respond(400, {
                        statusInternal: 2,
                        statusText: 'amount of meal is too small'
                    });
                } else {
                    next();
                }
            });
    })

    // check if mealtime is already full
    .then(function (next) {
        var date = new Date();
        module.model.count({
            mealtime: mealtimeObjectID,
            deleted: false,
            created : {
                $gte : new Date(date.getFullYear(), date.getMonth(), date.getDate())
            }
        }).exec(function (err, count) {
            if (err) return respond(400, err);

            if (count >= module.config.mealtimelimit) {
                return respond(400, {
                    statusInternal: 4,
                    statusText: 'mealtime is full'
                });
            }

            next();
        });
    })

    // save
    .then(function (next) {

        var order = new module.model({
            meal    : mealObjectID,
            mealtime: mealtimeObjectID,
            user    : userObjectID,
        });

        order.save(function (err, results) {
            if (err) {
                respond(400, err);
            } else {
                next(order);
            }
        });

    })

    .then(function (next, order) {

        module.models.meal
            .findByIdAndUpdate(mealObjectID, {
                $inc: { amount: -1, votes: 1 }
            }, function(err, result) {
                respond(201, {
                    meal: result,
                    order: order
                });
            });
    });
};

exports.getList = function (respond, offset, limit, sort, order, dateStart, dateEnd) {

    var date   = new Date();
    limit  = limit || 30;
    offset = offset || 0;
    sort   = sort || 'created';
    order  = order == 'desc' ? '-' : '';
    if (dateStart) {
        dateStart = new Date(dateStart);
    } else {
        dateStart = new Date(date.getFullYear(), date.getMonth(), date.getDate());
    }

    if (dateEnd) {
        dateEnd = new Date(dateEnd);
    } else {
        dateEnd = new Date(dateStart.getTime() + (24 * 60 * 60 * 1000));
    }

    module.model.find({
        created : {
            $gte : dateStart,
            $lte : dateEnd
        }
    })
    .populate('mealtime')
    .populate('user', '_id username')
    .populate('meal', '_id title amount votes')
    .sort(order + sort)
    .limit(limit)
    .skip(offset)
    .exec(function (err, results) {
        if (err) return respond(400, err);

        return respond(200, results);
    });
};

exports.getListByUser = function (respond, userID, offset, limit, sort, order) {

    var ObjectId     = module.mongoose.Types.ObjectId;
    var userObjectID = null;

    var date   = new Date();
    limit  = limit || 30;
    offset = offset || 0;
    sort   = sort || 'created';
    order  = order == 'desc' ? '-' : '';

    if(userID.toString().length !== 24) {
        return respond(400, {});
    } else {
        userObjectID = new ObjectId(userID);
    }

    module.model
        .find({
            user: userObjectID
        })
        .populate('mealtime')
        .populate('user', '_id username')
        .populate('meal', '_id title amount votes')
        .sort(order + sort)
        .limit(limit)
        .skip(offset)
        .exec(function (err, results) {
            if (err) return respond(400, err);

            respond(200, results);
        });
};

exports.getFavoriteMealtimeIdByUser = function (respond, userID) {
    var ObjectId            = module.mongoose.Types.ObjectId,
        userObjectID        = null;

    if(userID.toString().length !== 24) {
        return respond(400, {});
    } else {
        userObjectID = new ObjectId(userID);
    }

    module.model
        .find({
            user: userObjectID,
            deleted : false
        })
        .populate('mealtime')
        .exec(function (err, results) {
            var ordersByMealtime        = {},
                mealtimeId,
                favoriteMealtimeId      = '',
                numberOfOrders,
                maximumNumberOfOrders   = 0;

            if (err) return respond(400, err);

            results.forEach(function (order) {
                mealtimeId = order.mealtime.id;

                if ('undefined' === typeof ordersByMealtime[mealtimeId]) {
                    ordersByMealtime[mealtimeId] = 0;
                }

                ordersByMealtime[mealtimeId]++;
            });

            for (mealtimeId in ordersByMealtime) {
                if (ordersByMealtime.hasOwnProperty(mealtimeId)) {
                    numberOfOrders = ordersByMealtime[mealtimeId];

                    if (maximumNumberOfOrders < numberOfOrders) {
                        maximumNumberOfOrders = numberOfOrders;

                        favoriteMealtimeId = mealtimeId;
                    }
                }
            }

            respond(200, favoriteMealtimeId);
        });
};

exports.count = function (respond, deleted) {

    module.model.count({
        deleted: deleted
    }, function (err, count) {
        if (err) {
            respond(400, err);
        } else {
            respond(200, count);
        }
    });

};

exports.delete = function (respond, id) {

    var seq = sequence();
    var ObjectId = module.mongoose.Types.ObjectId;
    var orderObjectID = new ObjectId(id);

    seq.then(function (next) {
        module.model
            .findById(orderObjectID)
            .populate('meal')
            .exec(function(err, result) {
                if(result.length === 0) return respond(400, 'order id not found');
                if(err) return respond(400, err);

                result.meal.update({
                    $inc: { amount: 1, votes: -1 }
                }, function(err, meal) {
                    if(meal === 0) return respond(404, 'sub document meal not found');
                    if(err) return respond(400, err);

                    next();
                });
            });
    })

    .then(function (next) {

        module.model.findByIdAndUpdate(id, {
            deleted: true
        }, function (err, order) {
            if(err) return respond(400, err);

            return respond(200, order);
        });

    });
};

module.exports = function(app, model) {

    module.db       = app.get('db');
    module.mongoose = app.get('mongoose');
    module.config   = app.get('config');

    module.models   = app.get('models');
    module.model    = app.get('models').order;

    return exports;
};
<commit_msg>Issue #33 - Sort 'My Orders' list to show newest order at the top<commit_after>var sequence = require('futures').sequence;

exports.add = function (respond, mealID, mealtimeID, userID) {

    var seq = sequence();
    var ObjectId = module.mongoose.Types.ObjectId;

    var mealObjectID     = new ObjectId(mealID);
    var userObjectID     = new ObjectId(userID);
    var mealtimeObjectID = new ObjectId(mealtimeID);

    seq

    // amount of meals < 0
    .then(function (next) {

        module.models.meal
            .findOne({ _id: mealObjectID })
            .exec(function (err, result) {
                if (err) return respond(400, err);

                if(!result) {
                    return respond(400, {
                        statusInternal: 3,
                        statusText: 'meal not found'
                    });
                }

                if(!result.amount || result.amount < 1) {
                    return respond(400, {
                        statusInternal: 2,
                        statusText: 'amount of meal is too small'
                    });
                } else {
                    next();
                }
            });
    })

    // check if mealtime is already full
    .then(function (next) {
        var date = new Date();
        module.model.count({
            mealtime: mealtimeObjectID,
            deleted: false,
            created : {
                $gte : new Date(date.getFullYear(), date.getMonth(), date.getDate())
            }
        }).exec(function (err, count) {
            if (err) return respond(400, err);

            if (count >= module.config.mealtimelimit) {
                return respond(400, {
                    statusInternal: 4,
                    statusText: 'mealtime is full'
                });
            }

            next();
        });
    })

    // save
    .then(function (next) {

        var order = new module.model({
            meal    : mealObjectID,
            mealtime: mealtimeObjectID,
            user    : userObjectID,
        });

        order.save(function (err, results) {
            if (err) {
                respond(400, err);
            } else {
                next(order);
            }
        });

    })

    .then(function (next, order) {

        module.models.meal
            .findByIdAndUpdate(mealObjectID, {
                $inc: { amount: -1, votes: 1 }
            }, function(err, result) {
                respond(201, {
                    meal: result,
                    order: order
                });
            });
    });
};

exports.getList = function (respond, offset, limit, sort, order, dateStart, dateEnd) {

    var date   = new Date();
    limit  = limit || 30;
    offset = offset || 0;
    sort   = sort || 'created';
    order  = order == 'desc' ? '-' : '';
    if (dateStart) {
        dateStart = new Date(dateStart);
    } else {
        dateStart = new Date(date.getFullYear(), date.getMonth(), date.getDate());
    }

    if (dateEnd) {
        dateEnd = new Date(dateEnd);
    } else {
        dateEnd = new Date(dateStart.getTime() + (24 * 60 * 60 * 1000));
    }

    module.model.find({
        created : {
            $gte : dateStart,
            $lte : dateEnd
        }
    })
    .populate('mealtime')
    .populate('user', '_id username')
    .populate('meal', '_id title amount votes')
    .sort(order + sort)
    .limit(limit)
    .skip(offset)
    .exec(function (err, results) {
        if (err) return respond(400, err);

        return respond(200, results);
    });
};

exports.getListByUser = function (respond, userID, offset, limit, sort, order) {

    var ObjectId     = module.mongoose.Types.ObjectId;
    var userObjectID = null;

    var date   = new Date();
    limit  = limit || 30;
    offset = offset || 0;
    sort   = sort || 'created';
    order  = order == 'asc' ? '' : '-';

    if(userID.toString().length !== 24) {
        return respond(400, {});
    } else {
        userObjectID = new ObjectId(userID);
    }

    module.model
        .find({
            user: userObjectID
        })
        .populate('mealtime')
        .populate('user', '_id username')
        .populate('meal', '_id title amount votes')
        .sort(order + sort)
        .limit(limit)
        .skip(offset)
        .exec(function (err, results) {
            if (err) return respond(400, err);

            respond(200, results);
        });
};

exports.getFavoriteMealtimeIdByUser = function (respond, userID) {
    var ObjectId            = module.mongoose.Types.ObjectId,
        userObjectID        = null;

    if(userID.toString().length !== 24) {
        return respond(400, {});
    } else {
        userObjectID = new ObjectId(userID);
    }

    module.model
        .find({
            user: userObjectID,
            deleted : false
        })
        .populate('mealtime')
        .exec(function (err, results) {
            var ordersByMealtime        = {},
                mealtimeId,
                favoriteMealtimeId      = '',
                numberOfOrders,
                maximumNumberOfOrders   = 0;

            if (err) return respond(400, err);

            results.forEach(function (order) {
                mealtimeId = order.mealtime.id;

                if ('undefined' === typeof ordersByMealtime[mealtimeId]) {
                    ordersByMealtime[mealtimeId] = 0;
                }

                ordersByMealtime[mealtimeId]++;
            });

            for (mealtimeId in ordersByMealtime) {
                if (ordersByMealtime.hasOwnProperty(mealtimeId)) {
                    numberOfOrders = ordersByMealtime[mealtimeId];

                    if (maximumNumberOfOrders < numberOfOrders) {
                        maximumNumberOfOrders = numberOfOrders;

                        favoriteMealtimeId = mealtimeId;
                    }
                }
            }

            respond(200, favoriteMealtimeId);
        });
};

exports.count = function (respond, deleted) {

    module.model.count({
        deleted: deleted
    }, function (err, count) {
        if (err) {
            respond(400, err);
        } else {
            respond(200, count);
        }
    });

};

exports.delete = function (respond, id) {

    var seq = sequence();
    var ObjectId = module.mongoose.Types.ObjectId;
    var orderObjectID = new ObjectId(id);

    seq.then(function (next) {
        module.model
            .findById(orderObjectID)
            .populate('meal')
            .exec(function(err, result) {
                if(result.length === 0) return respond(400, 'order id not found');
                if(err) return respond(400, err);

                result.meal.update({
                    $inc: { amount: 1, votes: -1 }
                }, function(err, meal) {
                    if(meal === 0) return respond(404, 'sub document meal not found');
                    if(err) return respond(400, err);

                    next();
                });
            });
    })

    .then(function (next) {

        module.model.findByIdAndUpdate(id, {
            deleted: true
        }, function (err, order) {
            if(err) return respond(400, err);

            return respond(200, order);
        });

    });
};

module.exports = function(app, model) {

    module.db       = app.get('db');
    module.mongoose = app.get('mongoose');
    module.config   = app.get('config');

    module.models   = app.get('models');
    module.model    = app.get('models').order;

    return exports;
};
<|endoftext|>


<commit_before>/*
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

'use strict';

// Include Gulp & Tools We'll Use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var merge = require('merge-stream');
var path = require('path');
var fs = require('fs');
var glob = require('glob');
var historyApiFallback = require('connect-history-api-fallback');
var packageJson = require('./package.json');
var crypto = require('crypto');

var AUTOPREFIXER_BROWSERS = [
  'ie >= 10',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

var styleTask = function (stylesPath, srcs) {
  return gulp.src(srcs.map(function(src) {
      return path.join('app', stylesPath, src);
    }))
    .pipe($.changed(stylesPath, {extension: '.css'}))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp/' + stylesPath))
    .pipe($.if('*.css', $.cssmin()))
    .pipe(gulp.dest('dist/' + stylesPath))
    .pipe($.size({title: stylesPath}));
};

// Compile and Automatically Prefix Stylesheets
gulp.task('styles', function () {
  return styleTask('styles', ['**/*.css']);
});

gulp.task('elements', function () {
  return styleTask('elements', ['**/*.css']);
});

// Lint JavaScript
gulp.task('jshint', function () {
  return gulp.src([
      'app/scripts/**/*.js',
      'app/elements/**/*.js',
      'app/elements/**/*.html'
    ])
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint.extract()) // Extract JS from .html files
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

// Optimize Images
gulp.task('images', function () {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe($.size({title: 'images'}));
});

// Copy All Files At The Root Level (app)
gulp.task('copy', function () {
  var app = gulp.src([
    'app/*',
    '!app/test',
    '!app/cache-config.json'
  ], {
    dot: true
  }).pipe(gulp.dest('dist'));

  var bower = gulp.src([
    'bower_components/**/*'
  ]).pipe(gulp.dest('dist/bower_components'));

  var elements = gulp.src(['app/elements/**/*.html'])
    .pipe(gulp.dest('dist/elements'));

  var swBootstrap = gulp.src(['bower_components/platinum-sw/bootstrap/*.js'])
    .pipe(gulp.dest('dist/elements/bootstrap'));

  var swToolbox = gulp.src(['bower_components/sw-toolbox/*.js'])
    .pipe(gulp.dest('dist/sw-toolbox'));

  var vulcanized = gulp.src(['app/elements/elements.html'])
    .pipe($.rename('elements.vulcanized.html'))
    .pipe(gulp.dest('dist/elements'));

  return merge(app, bower, elements, vulcanized, swBootstrap, swToolbox)
    .pipe($.size({title: 'copy'}));
});

// Copy Web Fonts To Dist
gulp.task('fonts', function () {
  return gulp.src(['app/fonts/**'])
    .pipe(gulp.dest('dist/fonts'))
    .pipe($.size({title: 'fonts'}));
});

// Scan Your HTML For Assets & Optimize Them
gulp.task('html', function () {
  var assets = $.useref.assets({searchPath: ['.tmp', 'app', 'dist']});

  return gulp.src(['app/**/*.html', '!app/{elements,test}/**/*.html'])
    // Replace path for vulcanized assets
    .pipe($.if('*.html', $.replace('elements/elements.html', 'elements/elements.vulcanized.html')))
    .pipe(assets)
    // Concatenate And Minify JavaScript
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    // Concatenate And Minify Styles
    // In case you are still using useref build blocks
    .pipe($.if('*.css', $.cssmin()))
    .pipe(assets.restore())
    .pipe($.useref())
    // Minify Any HTML
    .pipe($.if('*.html', $.minifyHtml({
      quotes: true,
      empty: true,
      spare: true
    })))
    // Output Files
    .pipe(gulp.dest('dist'))
    .pipe($.size({title: 'html'}));
});

// Vulcanize imports
gulp.task('vulcanize', function () {
  var DEST_DIR = 'dist/elements';

  return gulp.src('dist/elements/elements.vulcanized.html')
    .pipe($.vulcanize({
      stripComments: true,
      inlineCss: true,
      inlineScripts: true
    }))
    .pipe(gulp.dest(DEST_DIR))
    .pipe($.size({title: 'vulcanize'}));
});

// Generate config data for the <sw-precache-cache> element.
// This include a list of files that should be precached, as well as a (hopefully unique) cache
// id that ensure that multiple PSK projects don't share the same Cache Storage.
// This task does not run by default, but if you are interested in using service worker caching
// in your project, please enable it within the 'default' task.
// See https://github.com/PolymerElements/polymer-starter-kit#enable-service-worker-support
// for more context.
gulp.task('cache-config', function (callback) {
  var dir = 'dist';
  var config = {
    cacheId: packageJson.name || path.basename(__dirname),
    disabled: false
  };

  glob('{elements,scripts,styles}/**/*.*', {cwd: dir}, function(error, files) {
    if (error) {
      callback(error);
    } else {
      files.push('index.html', './', 'bower_components/webcomponentsjs/webcomponents-lite.min.js');
      config.precache = files;

      var md5 = crypto.createHash('md5');
      md5.update(JSON.stringify(config.precache));
      config.precacheFingerprint = md5.digest('hex');

      var configPath = path.join(dir, 'cache-config.json');
      fs.writeFile(configPath, JSON.stringify(config), callback);
    }
  });
});

// Clean Output Directory
gulp.task('clean', function (cb) {
  del(['.tmp', 'dist'], cb);
});

// Watch Files For Changes & Reload
gulp.task('serve', ['styles', 'elements', 'images'], function () {
  browserSync({
    port: 5000,
    notify: false,
    logPrefix: 'PSK',
    snippetOptions: {
      rule: {
        match: '',
        fn: function (snippet) {
          return snippet;
        }
      }
    },
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: {
      baseDir: ['.tmp', 'app'],
      middleware: [ historyApiFallback() ],
      routes: {
        '/bower_components': 'bower_components'
      }
    }
  });

  gulp.watch(['app/**/*.html'], reload);
  gulp.watch(['app/styles/**/*.css'], ['styles', reload]);
  gulp.watch(['app/elements/**/*.css'], ['elements', reload]);
  gulp.watch(['app/{scripts,elements}/**/{*.js,*.html}'], ['jshint']);
  gulp.watch(['app/images/**/*'], reload);
});

// Build and serve the output from the dist build
gulp.task('serve:dist', ['default'], function () {
  browserSync({
    port: 5001,
    notify: false,
    logPrefix: 'PSK',
    snippetOptions: {
      rule: {
        match: '',
        fn: function (snippet) {
          return snippet;
        }
      }
    },
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: 'dist',
    middleware: [ historyApiFallback() ]
  });
});

// Build Production Files, the Default Task
gulp.task('default', ['clean'], function (cb) {
  // Uncomment 'cache-config' after 'vulcanize' if you are going to use service workers.
  runSequence(
    ['copy', 'styles'],
    'elements',
    ['jshint', 'images', 'fonts', 'html'],
    'vulcanize', // 'cache-config',
    cb);
});

// Load tasks for web-component-tester
// Adds tasks for `gulp test:local` and `gulp test:remote`
require('web-component-tester').gulp.init(gulp);

// Load custom tasks from the `tasks` directory
try { require('require-dir')('tasks'); } catch (err) {}
<commit_msg>Gulpfile: Modify comments in title-case to sentence-case<commit_after>/*
Copyright (c) 2015 The Polymer Project Authors. All rights reserved.
This code may only be used under the BSD style license found at http://polymer.github.io/LICENSE.txt
The complete set of authors may be found at http://polymer.github.io/AUTHORS.txt
The complete set of contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt
Code distributed by Google as part of the polymer project is also
subject to an additional IP rights grant found at http://polymer.github.io/PATENTS.txt
*/

'use strict';

// Include Gulp & tools we'll use
var gulp = require('gulp');
var $ = require('gulp-load-plugins')();
var del = require('del');
var runSequence = require('run-sequence');
var browserSync = require('browser-sync');
var reload = browserSync.reload;
var merge = require('merge-stream');
var path = require('path');
var fs = require('fs');
var glob = require('glob');
var historyApiFallback = require('connect-history-api-fallback');
var packageJson = require('./package.json');
var crypto = require('crypto');

var AUTOPREFIXER_BROWSERS = [
  'ie >= 10',
  'ie_mob >= 10',
  'ff >= 30',
  'chrome >= 34',
  'safari >= 7',
  'opera >= 23',
  'ios >= 7',
  'android >= 4.4',
  'bb >= 10'
];

var styleTask = function (stylesPath, srcs) {
  return gulp.src(srcs.map(function(src) {
      return path.join('app', stylesPath, src);
    }))
    .pipe($.changed(stylesPath, {extension: '.css'}))
    .pipe($.autoprefixer(AUTOPREFIXER_BROWSERS))
    .pipe(gulp.dest('.tmp/' + stylesPath))
    .pipe($.if('*.css', $.cssmin()))
    .pipe(gulp.dest('dist/' + stylesPath))
    .pipe($.size({title: stylesPath}));
};

// Compile and automatically prefix stylesheets
gulp.task('styles', function () {
  return styleTask('styles', ['**/*.css']);
});

gulp.task('elements', function () {
  return styleTask('elements', ['**/*.css']);
});

// Lint JavaScript
gulp.task('jshint', function () {
  return gulp.src([
      'app/scripts/**/*.js',
      'app/elements/**/*.js',
      'app/elements/**/*.html'
    ])
    .pipe(reload({stream: true, once: true}))
    .pipe($.jshint.extract()) // Extract JS from .html files
    .pipe($.jshint())
    .pipe($.jshint.reporter('jshint-stylish'))
    .pipe($.if(!browserSync.active, $.jshint.reporter('fail')));
});

// Optimize images
gulp.task('images', function () {
  return gulp.src('app/images/**/*')
    .pipe($.cache($.imagemin({
      progressive: true,
      interlaced: true
    })))
    .pipe(gulp.dest('dist/images'))
    .pipe($.size({title: 'images'}));
});

// Copy all files at the root level (app)
gulp.task('copy', function () {
  var app = gulp.src([
    'app/*',
    '!app/test',
    '!app/cache-config.json'
  ], {
    dot: true
  }).pipe(gulp.dest('dist'));

  var bower = gulp.src([
    'bower_components/**/*'
  ]).pipe(gulp.dest('dist/bower_components'));

  var elements = gulp.src(['app/elements/**/*.html'])
    .pipe(gulp.dest('dist/elements'));

  var swBootstrap = gulp.src(['bower_components/platinum-sw/bootstrap/*.js'])
    .pipe(gulp.dest('dist/elements/bootstrap'));

  var swToolbox = gulp.src(['bower_components/sw-toolbox/*.js'])
    .pipe(gulp.dest('dist/sw-toolbox'));

  var vulcanized = gulp.src(['app/elements/elements.html'])
    .pipe($.rename('elements.vulcanized.html'))
    .pipe(gulp.dest('dist/elements'));

  return merge(app, bower, elements, vulcanized, swBootstrap, swToolbox)
    .pipe($.size({title: 'copy'}));
});

// Copy web fonts to dist
gulp.task('fonts', function () {
  return gulp.src(['app/fonts/**'])
    .pipe(gulp.dest('dist/fonts'))
    .pipe($.size({title: 'fonts'}));
});

// Scan your HTML for assets & optimize them
gulp.task('html', function () {
  var assets = $.useref.assets({searchPath: ['.tmp', 'app', 'dist']});

  return gulp.src(['app/**/*.html', '!app/{elements,test}/**/*.html'])
    // Replace path for vulcanized assets
    .pipe($.if('*.html', $.replace('elements/elements.html', 'elements/elements.vulcanized.html')))
    .pipe(assets)
    // Concatenate and minify JavaScript
    .pipe($.if('*.js', $.uglify({preserveComments: 'some'})))
    // Concatenate and minify styles
    // In case you are still using useref build blocks
    .pipe($.if('*.css', $.cssmin()))
    .pipe(assets.restore())
    .pipe($.useref())
    // Minify any HTML
    .pipe($.if('*.html', $.minifyHtml({
      quotes: true,
      empty: true,
      spare: true
    })))
    // Output files
    .pipe(gulp.dest('dist'))
    .pipe($.size({title: 'html'}));
});

// Vulcanize imports
gulp.task('vulcanize', function () {
  var DEST_DIR = 'dist/elements';

  return gulp.src('dist/elements/elements.vulcanized.html')
    .pipe($.vulcanize({
      stripComments: true,
      inlineCss: true,
      inlineScripts: true
    }))
    .pipe(gulp.dest(DEST_DIR))
    .pipe($.size({title: 'vulcanize'}));
});

// Generate config data for the <sw-precache-cache> element.
// This include a list of files that should be precached, as well as a (hopefully unique) cache
// id that ensure that multiple PSK projects don't share the same Cache Storage.
// This task does not run by default, but if you are interested in using service worker caching
// in your project, please enable it within the 'default' task.
// See https://github.com/PolymerElements/polymer-starter-kit#enable-service-worker-support
// for more context.
gulp.task('cache-config', function (callback) {
  var dir = 'dist';
  var config = {
    cacheId: packageJson.name || path.basename(__dirname),
    disabled: false
  };

  glob('{elements,scripts,styles}/**/*.*', {cwd: dir}, function(error, files) {
    if (error) {
      callback(error);
    } else {
      files.push('index.html', './', 'bower_components/webcomponentsjs/webcomponents-lite.min.js');
      config.precache = files;

      var md5 = crypto.createHash('md5');
      md5.update(JSON.stringify(config.precache));
      config.precacheFingerprint = md5.digest('hex');

      var configPath = path.join(dir, 'cache-config.json');
      fs.writeFile(configPath, JSON.stringify(config), callback);
    }
  });
});

// Clean output directory
gulp.task('clean', function (cb) {
  del(['.tmp', 'dist'], cb);
});

// Watch files for changes & reload
gulp.task('serve', ['styles', 'elements', 'images'], function () {
  browserSync({
    port: 5000,
    notify: false,
    logPrefix: 'PSK',
    snippetOptions: {
      rule: {
        match: '',
        fn: function (snippet) {
          return snippet;
        }
      }
    },
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: {
      baseDir: ['.tmp', 'app'],
      middleware: [ historyApiFallback() ],
      routes: {
        '/bower_components': 'bower_components'
      }
    }
  });

  gulp.watch(['app/**/*.html'], reload);
  gulp.watch(['app/styles/**/*.css'], ['styles', reload]);
  gulp.watch(['app/elements/**/*.css'], ['elements', reload]);
  gulp.watch(['app/{scripts,elements}/**/{*.js,*.html}'], ['jshint']);
  gulp.watch(['app/images/**/*'], reload);
});

// Build and serve the output from the dist build
gulp.task('serve:dist', ['default'], function () {
  browserSync({
    port: 5001,
    notify: false,
    logPrefix: 'PSK',
    snippetOptions: {
      rule: {
        match: '',
        fn: function (snippet) {
          return snippet;
        }
      }
    },
    // Run as an https by uncommenting 'https: true'
    // Note: this uses an unsigned certificate which on first access
    //       will present a certificate warning in the browser.
    // https: true,
    server: 'dist',
    middleware: [ historyApiFallback() ]
  });
});

// Build production files, the default task
gulp.task('default', ['clean'], function (cb) {
  // Uncomment 'cache-config' after 'vulcanize' if you are going to use service workers.
  runSequence(
    ['copy', 'styles'],
    'elements',
    ['jshint', 'images', 'fonts', 'html'],
    'vulcanize', // 'cache-config',
    cb);
});

// Load tasks for web-component-tester
// Adds tasks for `gulp test:local` and `gulp test:remote`
require('web-component-tester').gulp.init(gulp);

// Load custom tasks from the `tasks` directory
try { require('require-dir')('tasks'); } catch (err) {}
<|endoftext|>


<commit_before>(function(root) {
    var get   = typeof require == 'function' && require('request')
    var hyve  = typeof exports != 'undefined'?  exports : root.hyve = { }

    // ECMA-262 compatible Array#forEach polyfills
    Array.prototype.forEach = Array.prototype.forEach
    || function(fn, ctx) {
           var len = this.length >>> 0
           for (var i = 0; i < len; ++i)
               if (i in this) fn.call(ctx, this[i], i, this)
       }

    // Fills a template with data from an object
    function format(string, data) {
        return string.replace( /{{(?:#(.+?)#)?\s*(.+?)\s*}}/g
                             , function(m, cond, id) {
                                 var rv = data[id]
                                 return rv? (cond || '') + rv
                                          :  cond? m : '' })
    }

    // Pulls data from several streams and handle all them with the given
    // callback
    function stream(query, callback, custom_services) {
        var services
        services = custom_services || Object.keys(hyve.feeds)
        services.forEach(function(service){
            var options = hyve.feeds[service.toLowerCase()]
            var feed_url = format( options.feed_url,
                                     { query:  query
                                     , apikey: options.api_key })
                fetch(feed_url, service, callback)
            setInterval(function(){
                var feed_url = format( options.feed_url,
                                     { query:  query
                                     , apikey: options.api_key })
                fetch(feed_url, service, callback)
            }, options.interval)
        })
    }

    // Fetches a JSON stream
    var fetch = function() {
        var counter   = 0
        var callbacks = { }
        var head      = !get && document.getElementsByTagName('head')[0]

        // Returns a qualified identifier pointing to a callback
        function get_callback() {
            return format('hyve.callbacks.f{{id}}', { id: ++counter })
        }

        // Requires an URI using JSONP
        function jsonp(url, callback) {
            hyve.callbacks['f' + counter] = callback
            var s = document.createElement('script')
            s.setAttribute('src', url)
            head.appendChild(s)
        }

        // Requires an URI using Node.js's request library
        function request(url, callback) {
            get({uri: url}, function(error, res, data) {
                try {
                    callback(JSON.parse(data))
                }
                catch(e){
                    callback({ }, e)
                }
            })
        }

        // Abstracts fetching URIs.
        function fetch(url, service, callback) {
            var fn = pass(service, callback)
            var cb = !get && get_callback()
            url    = format(url, { callback: cb })

            var fetcher = get? request : jsonp
            fetcher(url, fn)
        }

        // Higher-order function to process the fetched data
        function pass(service, callback) {
            return function(data) {
                hyve.feeds[service].parse(data, callback)
            }
        }

        // Export the `fetch` function
        return fetch
    }()

    // Exports data to the outside world
    hyve.stream    = stream
    hyve.callbacks = []
    hyve.feeds     = {
            twitter: {
                interval : 2000,
                feed_url :'http://search.twitter.com/search.json?q={{query}}{{#&callback=#callback}}',
                parse : function(data,callback){
                    if (data.refresh_url != null){
                        this.feed_url = 'http://search.twitter.com/search.json'
                                      + data.refresh_url
                                      + '{{#&callback=#callback}}'
                    }
                    if (data.results != null){
                        data.results.forEach(function(item){
                            callback({
                                'service' : 'twitter',
                                'user' : {
                                    'id' : item.from_user_id_str,
                                    'name' : item.from_user,
                                    'avatar' : item.profile_image_url
                                },
                                'id' : item.id_str,
                                'date' : item.created_at, //TODO: normalize
                                'text' : item.text,
                                'source' : item.source
                            })
                        }
                    })
                }
            },
            identica: {
                interval : 6000,
                feed_url :'http://identi.ca/api/search.json?q={{query}}{{#&callback=#callback}}',
                parse : function(data,callback){
                    if (data.refresh_url != null){
                        this.feed_url = 'http://identi.ca/api/search.json' + data.refresh_url+ '{{#&callback=#callback}}'
                    }
                    data.results.forEach(function(item){
                        callback({
                            'service' : 'identica',
                            'user' : {
                                'id' : item.from_user_id_str,
                                'name' : item.from_user,
                                'avatar' : item.profile_image_url
                            },
                            'id' : item.id_str,
                            'date' : item.created_at, //TODO: normalize
                            'text' : item.text,
                            'source' : item.source
                        })
                    })
                }
            },
            buzz: {
                interval : 5000,
                api_key: '',
                feed_url :'https://www.googleapis.com/buzz/v1/activities/search?q={{query}}&alt=json&orderby=published{{#&callback=#callback}}{{#&key=#apikey}}',
                parse : function(data,callback){
                    if (this.orig_url == null){
                        this.orig_url = this.feed_url
                    }
                    if (data.data != null){
                        var last_date = data.data.items[0].updated.split('.')[0]
                        this.feed_url = this.orig_url.replace('{{query}}','{{query}}%20AND%20date%3E' + last_date)
                        data.data.items.forEach(function(item){
                            if (item.title != '-'){
                                callback({
                                    'service' : 'buzz',
                                    'user' : {
                                        'id' : item.actor.name,
                                        'name' : item.actor.name,
                                        'avatar' : item.actor.thumbnailUrl,
                                        'source' : item.actor.profileUrl
                                    },
                                    'id' : item.id.split(':')[3],
                                    'date' : item.published, //TODO normalize
                                    'text' : item.title,
                                    'source' : item.object.links.alternate[0].href
                                })
                            }
                        })
                    }
                }
            },
            facebook: {
                interval : 3000,
                feed_url : 'https://graph.facebook.com/search?q={{query}}&type=post{{#&callback=#callback}}',
                parse : function(data,callback){
                    if (data.data != null){
                        if (data.paging != null) {
                            this.feed_url = data.paging.previous + '{{#&callback=#callback}}'
                        }
                        data.data.forEach(function(item){
                            if (item.message != null){
                                callback({
                                    'service' : 'facebook',
                                    'user' : {
                                        'id' : item.from.id,
                                        'name' : item.from.name,
                                        'avatar' : 'http://graph.facebook.com/'+item.from.id+'/picture'
                                    },
                                    'id' : item.id,
                                    'date' : item.created_time, //TODO: normalize
                                    'text' : item.message,
                                    'source' : 'http://facebook.com/'+item.from.id
                                })
                            }
                        })
                    }
                }
            },
            reddit: {
                interval : 5000,
                feed_url : 'http://www.reddit.com/search.json?q={{query}}&sort=new{{#&jsonp=#callback}}',
                parse : function(data,callback){
                    if (data.data.children[0]){
                        if (this.orig_url == null){
                            this.orig_url = this.feed_url
                        }
                        var before = data.data.children[0].data.name
                        if (before != null){
                            this.feed_url = this.orig_url + '&before=' + before
                        }
                        data.data.children.forEach(function(item){
                            item = item.data
                            callback({
                                'service' : 'reddit',
                                'user' : {
                                    'id' : '',
                                    'name' : '',
                                    'avatar' : ''
                                },
                                'id' : item.id,
                                'date' : item.created_utc, //TODO: normalize
                                'text' : item.title,
                                'source' : item.url,
                                'thumbnail':'http://reddit.com' + item.thumbnail
                            })
                        })
                    }
                }
            },
            flickr: {
                interval : 10000,
                api_key : '',
                feed_url : 'http://api.flickr.com/services/feeds/photos_public.gne?format=json&tagmode=all&tags={{query}}{{#&jsoncallback=#callback}}&extras=date_upload,date_taken,owner_name,geo,tags,views',
                parse : function(data,callback){
                    if (this.items_seen == null){
                        this.items_seen = {};
                    }
                    data.items && data.items.forEach(function(item){
                        if (this.items_seen[item.media.m] == null){
                            this.items_seen[item.media.m] = true
                            console.log(item)
                            callback({
                                'service' : 'flickr',
                                'user' : {
                                    'id' : item.author_id,
                                    'name' : item.author,
                                    'avatar' : ''
                                },
                                'id' : '',
                                'date' : item.published, //TODO: normalize
                                'text' : item.description,
                                'source' : item.link,
                                'thumbnail':item.media.m
                            })
                        }
                    }, this)
                }
            },
            youtube: {
                interval : 8000,
                feed_url : 'http://gdata.youtube.com/feeds/api/videos?q={{query}}&time=today&orderby=published&format=5&max-results=20&v=2&alt=jsonc{{#&callback=#callback}}',
                parse : function(data,callback){
                    if (this.items_seen == null){
                        this.items_seen = {};
                    }
                    if (data.data != null){
                        data.data.items.forEach(function(item){
                            if (this.items_seen[item.id] == null){
                                this.items_seen[item.id] = true
                                callback({
                                    'service' : 'youtube',
                                    'user' : {
                                        'id' : item.uploader,
                                        'name' : item.uploader,
                                        'profile' : 'http://youtube.com/' + item.uploader,
                                        'avatar' : ''
                                    },
                                    'id' : item.id,
                                    'date' : '', //TODO: normalize
                                    'text' : item.title,
                                    'source' : 'http://youtu.be/'+ item.id,
                                    'thumbnail':'http://i.ytimg.com/vi/' + item.id + '/hqdefault.jpg'
                                })
                            }
                        }, this)
                    }
                }
            },
            wordpress: {
                interval : 10000,
                feed_url : 'http://pipes.yahoo.com/pipes/pipe.run?_id=332d9216d8910ba39e6c2577fd321a6a&_render=json&u=http%3A%2F%2Fen.search.wordpress.com%2F%3Fq%3D{{query}}%26s%3Ddate%26f%3Djson{{#&_callback=#callback}}',
                parse : function(data,callback){
                    if (this.items_seen == null){
                        this.items_seen = {};
                    }
                    if (data != null){
                        data.value.items.forEach(function(item){
                            if (this.items_seen[item.guid] == null){
                                this.items_seen[item.guid] = true
                                callback({
                                    'service' : 'wordpress',
                                    'user' : {
                                        'id' : item.author,
                                        'name' : item.author,
                                        'profile' :'',
                                        'avatar' : ''
                                    },
                                    'id' : item.id,
                                    'date' : '', //TODO: normalize
                                    'text' : item.title,
                                    'description':item.content,
                                    'source' : item.guid,
                                })
                            }
                        }, this)
                    }
                }
            }
        }
})(this);

/*
$.getJSON('http://pipes.yahoo.com/pipes/pipe.run?_id=332d9216d8910ba39e6c2577fd321a6a&_render=json&u=http%3A%2F%2Fen.search.wordpress.com%2F%3Fq%3D' + query + '%26f%3Djson&_callback=?', function(data){                                  $('
Wordpress

').appendTo($('body')).show('slow')
    $.each(data.value.items, function(i,item) {
        $('
'+ item.author + ' : ' + item.title + '
').hide().appendTo($('body')).show('slow')
    });
});
});*/
<commit_msg> missing {<commit_after>(function(root) {
    var get   = typeof require == 'function' && require('request')
    var hyve  = typeof exports != 'undefined'?  exports : root.hyve = { }

    // ECMA-262 compatible Array#forEach polyfills
    Array.prototype.forEach = Array.prototype.forEach
    || function(fn, ctx) {
           var len = this.length >>> 0
           for (var i = 0; i < len; ++i)
               if (i in this) fn.call(ctx, this[i], i, this)
       }

    // Fills a template with data from an object
    function format(string, data) {
        return string.replace( /{{(?:#(.+?)#)?\s*(.+?)\s*}}/g
                             , function(m, cond, id) {
                                 var rv = data[id]
                                 return rv? (cond || '') + rv
                                          :  cond? m : '' })
    }

    // Pulls data from several streams and handle all them with the given
    // callback
    function stream(query, callback, custom_services) {
        var services
        services = custom_services || Object.keys(hyve.feeds)
        services.forEach(function(service){
            var options = hyve.feeds[service.toLowerCase()]
            var feed_url = format( options.feed_url,
                                     { query:  query
                                     , apikey: options.api_key })
                fetch(feed_url, service, callback)
            setInterval(function(){
                var feed_url = format( options.feed_url,
                                     { query:  query
                                     , apikey: options.api_key })
                fetch(feed_url, service, callback)
            }, options.interval)
        })
    }

    // Fetches a JSON stream
    var fetch = function() {
        var counter   = 0
        var callbacks = { }
        var head      = !get && document.getElementsByTagName('head')[0]

        // Returns a qualified identifier pointing to a callback
        function get_callback() {
            return format('hyve.callbacks.f{{id}}', { id: ++counter })
        }

        // Requires an URI using JSONP
        function jsonp(url, callback) {
            hyve.callbacks['f' + counter] = callback
            var s = document.createElement('script')
            s.setAttribute('src', url)
            head.appendChild(s)
        }

        // Requires an URI using Node.js's request library
        function request(url, callback) {
            get({uri: url}, function(error, res, data) {
                try {
                    callback(JSON.parse(data))
                }
                catch(e){
                    callback({ }, e)
                }
            })
        }

        // Abstracts fetching URIs.
        function fetch(url, service, callback) {
            var fn = pass(service, callback)
            var cb = !get && get_callback()
            url    = format(url, { callback: cb })

            var fetcher = get? request : jsonp
            fetcher(url, fn)
        }

        // Higher-order function to process the fetched data
        function pass(service, callback) {
            return function(data) {
                hyve.feeds[service].parse(data, callback)
            }
        }

        // Export the `fetch` function
        return fetch
    }()

    // Exports data to the outside world
    hyve.stream    = stream
    hyve.callbacks = []
    hyve.feeds     = {
            twitter: {
                interval : 2000,
                feed_url :'http://search.twitter.com/search.json?q={{query}}{{#&callback=#callback}}',
                parse : function(data,callback){
                    if (data.refresh_url != null){
                        this.feed_url = 'http://search.twitter.com/search.json'
                                      + data.refresh_url
                                      + '{{#&callback=#callback}}'
                    }
                    if (data.results != null){
                        data.results.forEach(function(item){
                            callback({
                                'service' : 'twitter',
                                'user' : {
                                    'id' : item.from_user_id_str,
                                    'name' : item.from_user,
                                    'avatar' : item.profile_image_url
                                },
                                'id' : item.id_str,
                                'date' : item.created_at, //TODO: normalize
                                'text' : item.text,
                                'source' : item.source
                            })
                        })
                    }
                }
            },
            identica: {
                interval : 6000,
                feed_url :'http://identi.ca/api/search.json?q={{query}}{{#&callback=#callback}}',
                parse : function(data,callback){
                    if (data.refresh_url != null){
                        this.feed_url = 'http://identi.ca/api/search.json' + data.refresh_url+ '{{#&callback=#callback}}'
                    }
                    data.results.forEach(function(item){
                        callback({
                            'service' : 'identica',
                            'user' : {
                                'id' : item.from_user_id_str,
                                'name' : item.from_user,
                                'avatar' : item.profile_image_url
                            },
                            'id' : item.id_str,
                            'date' : item.created_at, //TODO: normalize
                            'text' : item.text,
                            'source' : item.source
                        })
                    })
                }
            },
            buzz: {
                interval : 5000,
                api_key: '',
                feed_url :'https://www.googleapis.com/buzz/v1/activities/search?q={{query}}&alt=json&orderby=published{{#&callback=#callback}}{{#&key=#apikey}}',
                parse : function(data,callback){
                    if (this.orig_url == null){
                        this.orig_url = this.feed_url
                    }
                    if (data.data != null){
                        var last_date = data.data.items[0].updated.split('.')[0]
                        this.feed_url = this.orig_url.replace('{{query}}','{{query}}%20AND%20date%3E' + last_date)
                        data.data.items.forEach(function(item){
                            if (item.title != '-'){
                                callback({
                                    'service' : 'buzz',
                                    'user' : {
                                        'id' : item.actor.name,
                                        'name' : item.actor.name,
                                        'avatar' : item.actor.thumbnailUrl,
                                        'source' : item.actor.profileUrl
                                    },
                                    'id' : item.id.split(':')[3],
                                    'date' : item.published, //TODO normalize
                                    'text' : item.title,
                                    'source' : item.object.links.alternate[0].href
                                })
                            }
                        })
                    }
                }
            },
            facebook: {
                interval : 3000,
                feed_url : 'https://graph.facebook.com/search?q={{query}}&type=post{{#&callback=#callback}}',
                parse : function(data,callback){
                    if (data.data != null){
                        if (data.paging != null) {
                            this.feed_url = data.paging.previous + '{{#&callback=#callback}}'
                        }
                        data.data.forEach(function(item){
                            if (item.message != null){
                                callback({
                                    'service' : 'facebook',
                                    'user' : {
                                        'id' : item.from.id,
                                        'name' : item.from.name,
                                        'avatar' : 'http://graph.facebook.com/'+item.from.id+'/picture'
                                    },
                                    'id' : item.id,
                                    'date' : item.created_time, //TODO: normalize
                                    'text' : item.message,
                                    'source' : 'http://facebook.com/'+item.from.id
                                })
                            }
                        })
                    }
                }
            },
            reddit: {
                interval : 5000,
                feed_url : 'http://www.reddit.com/search.json?q={{query}}&sort=new{{#&jsonp=#callback}}',
                parse : function(data,callback){
                    if (data.data.children[0]){
                        if (this.orig_url == null){
                            this.orig_url = this.feed_url
                        }
                        var before = data.data.children[0].data.name
                        if (before != null){
                            this.feed_url = this.orig_url + '&before=' + before
                        }
                        data.data.children.forEach(function(item){
                            item = item.data
                            callback({
                                'service' : 'reddit',
                                'user' : {
                                    'id' : '',
                                    'name' : '',
                                    'avatar' : ''
                                },
                                'id' : item.id,
                                'date' : item.created_utc, //TODO: normalize
                                'text' : item.title,
                                'source' : item.url,
                                'thumbnail':'http://reddit.com' + item.thumbnail
                            })
                        })
                    }
                }
            },
            flickr: {
                interval : 10000,
                api_key : '',
                feed_url : 'http://api.flickr.com/services/feeds/photos_public.gne?format=json&tagmode=all&tags={{query}}{{#&jsoncallback=#callback}}&extras=date_upload,date_taken,owner_name,geo,tags,views',
                parse : function(data,callback){
                    if (this.items_seen == null){
                        this.items_seen = {};
                    }
                    data.items && data.items.forEach(function(item){
                        if (this.items_seen[item.media.m] == null){
                            this.items_seen[item.media.m] = true
                            console.log(item)
                            callback({
                                'service' : 'flickr',
                                'user' : {
                                    'id' : item.author_id,
                                    'name' : item.author,
                                    'avatar' : ''
                                },
                                'id' : '',
                                'date' : item.published, //TODO: normalize
                                'text' : item.description,
                                'source' : item.link,
                                'thumbnail':item.media.m
                            })
                        }
                    }, this)
                }
            },
            youtube: {
                interval : 8000,
                feed_url : 'http://gdata.youtube.com/feeds/api/videos?q={{query}}&time=today&orderby=published&format=5&max-results=20&v=2&alt=jsonc{{#&callback=#callback}}',
                parse : function(data,callback){
                    if (this.items_seen == null){
                        this.items_seen = {};
                    }
                    if (data.data != null){
                        data.data.items.forEach(function(item){
                            if (this.items_seen[item.id] == null){
                                this.items_seen[item.id] = true
                                callback({
                                    'service' : 'youtube',
                                    'user' : {
                                        'id' : item.uploader,
                                        'name' : item.uploader,
                                        'profile' : 'http://youtube.com/' + item.uploader,
                                        'avatar' : ''
                                    },
                                    'id' : item.id,
                                    'date' : '', //TODO: normalize
                                    'text' : item.title,
                                    'source' : 'http://youtu.be/'+ item.id,
                                    'thumbnail':'http://i.ytimg.com/vi/' + item.id + '/hqdefault.jpg'
                                })
                            }
                        }, this)
                    }
                }
            },
            wordpress: {
                interval : 10000,
                feed_url : 'http://pipes.yahoo.com/pipes/pipe.run?_id=332d9216d8910ba39e6c2577fd321a6a&_render=json&u=http%3A%2F%2Fen.search.wordpress.com%2F%3Fq%3D{{query}}%26s%3Ddate%26f%3Djson{{#&_callback=#callback}}',
                parse : function(data,callback){
                    if (this.items_seen == null){
                        this.items_seen = {};
                    }
                    if (data != null){
                        data.value.items.forEach(function(item){
                            if (this.items_seen[item.guid] == null){
                                this.items_seen[item.guid] = true
                                callback({
                                    'service' : 'wordpress',
                                    'user' : {
                                        'id' : item.author,
                                        'name' : item.author,
                                        'profile' :'',
                                        'avatar' : ''
                                    },
                                    'id' : item.id,
                                    'date' : '', //TODO: normalize
                                    'text' : item.title,
                                    'description':item.content,
                                    'source' : item.guid,
                                })
                            }
                        }, this)
                    }
                }
            }
        }
})(this);

/*
$.getJSON('http://pipes.yahoo.com/pipes/pipe.run?_id=332d9216d8910ba39e6c2577fd321a6a&_render=json&u=http%3A%2F%2Fen.search.wordpress.com%2F%3Fq%3D' + query + '%26f%3Djson&_callback=?', function(data){                                  $('
Wordpress

').appendTo($('body')).show('slow')
    $.each(data.value.items, function(i,item) {
        $('
'+ item.author + ' : ' + item.title + '
').hide().appendTo($('body')).show('slow')
    });
});
});*/
<|endoftext|>


<commit_before>var should   = require('should');
var helper   = require('../support/spec_helper');
var ORM      = require('../../');

describe("Model.find() chaining", function() {
	var db = null;
	var Person = null;

	var setup = function () {
		return function (done) {
			Person = db.define("person", {
				name    : String,
				surname : String,
				age     : Number
			});
			Person.hasMany("parents");

			ORM.singleton.clear(); // clear cache

			return helper.dropSync(Person, function () {
				Person.create([{
					name    : "John",
					surname : "Doe",
					age     : 18
				}, {
					name    : "Jane",
					surname : "Doe",
					age     : 20
				}, {
					name    : "Jane",
					surname : "Dean",
					age     : 18
				}], done);
			});
		};
	};

	before(function (done) {
		helper.connect(function (connection) {
			db = connection;

			return done();
		});
	});

	after(function () {
		return db.close();
	});

	describe(".limit(N)", function () {
		before(setup());

		it("should limit results to N items", function (done) {
			Person.find().limit(2).run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 2);

				return done();
			});
		});
	});

	describe(".skip(N)", function () {
		before(setup());

		it("should skip the first N results", function (done) {
			Person.find().skip(2).order("age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 1);
				instances[0].age.should.equal(20);

				return done();
			});
		});
	});

	describe(".offset(N)", function () {
		before(setup());

		it("should skip the first N results", function (done) {
			Person.find().offset(2).order("age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 1);
				instances[0].age.should.equal(20);

				return done();
			});
		});
	});

	describe(".order('property')", function () {
		before(setup());

		it("should order by that property ascending", function (done) {
			Person.find().order("age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].age.should.equal(18);
				instances[2].age.should.equal(20);

				return done();
			});
		});
	});

	describe(".order('-property')", function () {
		before(setup());

		it("should order by that property descending", function (done) {
			Person.find().order("-age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].age.should.equal(20);
				instances[2].age.should.equal(18);

				return done();
			});
		});
	});

	describe(".order('property', 'Z')", function () {
		before(setup());

		it("should order by that property descending", function (done) {
			Person.find().order("age", "Z").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].age.should.equal(20);
				instances[2].age.should.equal(18);

				return done();
			});
		});
	});

	describe(".only('property', ...)", function () {
		before(setup());

		it("should return only those properties, others null", function (done) {
			Person.find().only("age", "surname").order("-age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].should.have.property("age");
				instances[0].should.have.property("surname", "Doe");
				instances[0].should.have.property("name", null);

				return done();
			});
		});
	});

	describe(".only('property1', ...)", function () {
		before(setup());

		it("should return only those properties, others null", function (done) {
			Person.find().only([ "age", "surname" ]).order("-age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].should.have.property("age");
				instances[0].should.have.property("surname", "Doe");
				instances[0].should.have.property("name", null);

				return done();
			});
		});
	});

	describe(".count()", function () {
		before(setup());

		it("should return only the total number of results", function (done) {
			Person.find().count(function (err, count) {
				should.equal(err, null);
				count.should.equal(3);

				return done();
			});
		});
	});

	describe(".first()", function () {
		before(setup());

		it("should return only the first element", function (done) {
			Person.find().order("-age").first(function (err, JaneDoe) {
				should.equal(err, null);

				JaneDoe.name.should.equal("Jane");
				JaneDoe.surname.should.equal("Doe");
				JaneDoe.age.should.equal(20);

				return done();
			});
		});

		it("should return null if not found", function (done) {
			Person.find({ name: "Jack" }).first(function (err, Jack) {
				should.equal(err, null);
				should.equal(Jack, null);

				return done();
			});
		});
	});

	describe(".last()", function () {
		before(setup());

		it("should return only the last element", function (done) {
			Person.find().order("age").last(function (err, JaneDoe) {
				should.equal(err, null);

				JaneDoe.name.should.equal("Jane");
				JaneDoe.surname.should.equal("Doe");
				JaneDoe.age.should.equal(20);

				return done();
			});
		});

		it("should return null if not found", function (done) {
			Person.find({ name: "Jack" }).last(function (err, Jack) {
				should.equal(err, null);
				should.equal(Jack, null);

				return done();
			});
		});
	});

	describe(".find()", function () {
		before(setup());

		it("should not change find if no arguments", function (done) {
			Person.find().find().count(function (err, count) {
				should.equal(err, null);
				count.should.equal(3);

				return done();
			});
		});

		it("should restrict conditions if passed", function (done) {
			Person.find().find({ age: 18 }).count(function (err, count) {
				should.equal(err, null);
				count.should.equal(2);

				return done();
			});
		});

		it("should restrict conditions if passed and also be chainable", function (done) {
			Person.find().find({ age: 18 }).find({ name: "Jane" }).count(function (err, count) {
				should.equal(err, null);
				count.should.equal(1);

				return done();
			});
		});

		it("should allow sql where conditions", function (done) {
			Person.find({ age: 18 }).find("LOWER(surname) LIKE 'dea%'").all(function (err, items) {
				should.equal(err, null);
				items.length.should.equal(1);

				return done();
			});
		});

		it("should allow sql where conditions with auto escaping", function (done) {
			Person.find({ age: 18 }).find("LOWER(surname) LIKE ?", ['dea%']).all(function (err, items) {
				should.equal(err, null);
				items.length.should.equal(1);

				return done();
			});
		});

		it("should append sql where conditions", function (done) {
			Person.find().find("LOWER(surname) LIKE ?", ['do%']).all(function (err, items) {
				should.equal(err, null);
				items.length.should.equal(2);

				Person.find().find("LOWER(name) LIKE ?", ['jane']).all(function (err, items) {
					should.equal(err, null);
					items.length.should.equal(2);

					Person.find().find("LOWER(surname) LIKE ?", ['do%']).find("LOWER(name) LIKE ?", ['jane']).all(function (err, items) {
						should.equal(err, null);
						items.length.should.equal(1);

						return done();
					});
				});
			});
		});

		it("should return results if passed a callback as second argument", function (done) {
			Person.find().find({ age: 18 }, function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 2);

				return done();
			});
		});
	});

	describe(".each()", function () {
		before(setup());

		it("should return a ChainInstance", function (done) {
			var chain = Person.find().each();

			chain.filter.should.be.a("function");
			chain.sort.should.be.a("function");
			chain.count.should.be.a("function");

			return done();
		});
	});

	describe(".remove()", function () {
		before(setup());

		it("should have no problems if no results found", function (done) {
			Person.find({ age: 22 }).remove(function (err) {
				should.equal(err, null);

				Person.find().count(function (err, count) {
					should.equal(err, null);

					count.should.equal(3);

					return done();
				});
			});
		});

		it("should remove results and give feedback", function (done) {
			Person.find({ age: 20 }).remove(function (err) {
				should.equal(err, null);

				Person.find().count(function (err, count) {
					should.equal(err, null);

					count.should.equal(2);

					return done();
				});
			});
		});
	});

	describe(".each()", function () {
		before(setup());

		it("should return a ChainFind", function (done) {
			var chain = Person.find({ age: 22 }).each();

			chain.should.be.a("object");
			chain.filter.should.be.a("function");
			chain.sort.should.be.a("function");
			chain.count.should.be.a("function");
			chain.get.should.be.a("function");
			chain.save.should.be.a("function");

			return done();
		});

		describe(".count()", function () {
			it("should return the total filtered items", function (done) {
				Person.find().each().filter(function (person) {
					return (person.age > 18);
				}).count(function (count) {
					count.should.equal(1);

					return done();
				});
			});
		});

		describe(".sort()", function () {
			it("should return the items sorted using the sorted function", function (done) {
				Person.find().each().sort(function (first, second) {
					return (first.age < second.age);
				}).get(function (people) {
					should(Array.isArray(people));

					people.length.should.equal(3);
					people[0].age.should.equal(20);
					people[2].age.should.equal(18);

					return done();
				});
			});
		});

		describe(".save()", function () {
			it("should save items after changes", function (done) {
				Person.find({ surname: "Dean" }).each(function (person) {
					person.age.should.not.equal(45);
					person.age = 45;
				}).save(function () {
					Person.find({ surname: "Dean" }, function (err, people) {
						should(Array.isArray(people));

						people.length.should.equal(1);
						people[0].age.should.equal(45);

						return done();
					});
				});
			});
		});

		describe("if passing a callback", function () {
			it("should use it to .forEach()", function (done) {
				Person.find({ surname: "Dean" }).each(function (person) {
					person.fullName = person.name + " " + person.surname;
				}).get(function (people) {
					should(Array.isArray(people));

					people.length.should.equal(1);
					people[0].fullName = "Jane Dean";

					return done();
				});
			});
		});

		describe(".hasAccessor() for hasOne associations", function () {
		    it("should be chainable", function (done) {
				Person.find({ name: "John" }, function (err, John) {
					should.equal(err, null);

					var Justin = new Person({
						name : "Justin",
						age  : 45
					});

					John[0].setParents([ Justin ], function (err) {
						should.equal(err, null);

						Person.find().hasParents(Justin).all(function (err, people) {
						    should.equal(err, null);

							should(Array.isArray(people));

							people.length.should.equal(1);
							people[0].name.should.equal("John");

							return done();
						});
					});
				});
			});
		});
	});
});
<commit_msg>Update tests to document 'where' not 'find'<commit_after>var should   = require('should');
var helper   = require('../support/spec_helper');
var ORM      = require('../../');

describe("Model.find() chaining", function() {
	var db = null;
	var Person = null;

	var setup = function () {
		return function (done) {
			Person = db.define("person", {
				name    : String,
				surname : String,
				age     : Number
			});
			Person.hasMany("parents");

			ORM.singleton.clear(); // clear cache

			return helper.dropSync(Person, function () {
				Person.create([{
					name    : "John",
					surname : "Doe",
					age     : 18
				}, {
					name    : "Jane",
					surname : "Doe",
					age     : 20
				}, {
					name    : "Jane",
					surname : "Dean",
					age     : 18
				}], done);
			});
		};
	};

	before(function (done) {
		helper.connect(function (connection) {
			db = connection;

			return done();
		});
	});

	after(function () {
		return db.close();
	});

	describe(".limit(N)", function () {
		before(setup());

		it("should limit results to N items", function (done) {
			Person.find().limit(2).run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 2);

				return done();
			});
		});
	});

	describe(".skip(N)", function () {
		before(setup());

		it("should skip the first N results", function (done) {
			Person.find().skip(2).order("age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 1);
				instances[0].age.should.equal(20);

				return done();
			});
		});
	});

	describe(".offset(N)", function () {
		before(setup());

		it("should skip the first N results", function (done) {
			Person.find().offset(2).order("age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 1);
				instances[0].age.should.equal(20);

				return done();
			});
		});
	});

	describe(".order('property')", function () {
		before(setup());

		it("should order by that property ascending", function (done) {
			Person.find().order("age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].age.should.equal(18);
				instances[2].age.should.equal(20);

				return done();
			});
		});
	});

	describe(".order('-property')", function () {
		before(setup());

		it("should order by that property descending", function (done) {
			Person.find().order("-age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].age.should.equal(20);
				instances[2].age.should.equal(18);

				return done();
			});
		});
	});

	describe(".order('property', 'Z')", function () {
		before(setup());

		it("should order by that property descending", function (done) {
			Person.find().order("age", "Z").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].age.should.equal(20);
				instances[2].age.should.equal(18);

				return done();
			});
		});
	});

	describe(".only('property', ...)", function () {
		before(setup());

		it("should return only those properties, others null", function (done) {
			Person.find().only("age", "surname").order("-age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].should.have.property("age");
				instances[0].should.have.property("surname", "Doe");
				instances[0].should.have.property("name", null);

				return done();
			});
		});
	});

	describe(".only('property1', ...)", function () {
		before(setup());

		it("should return only those properties, others null", function (done) {
			Person.find().only([ "age", "surname" ]).order("-age").run(function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 3);
				instances[0].should.have.property("age");
				instances[0].should.have.property("surname", "Doe");
				instances[0].should.have.property("name", null);

				return done();
			});
		});
	});

	describe(".count()", function () {
		before(setup());

		it("should return only the total number of results", function (done) {
			Person.find().count(function (err, count) {
				should.equal(err, null);
				count.should.equal(3);

				return done();
			});
		});
	});

	describe(".first()", function () {
		before(setup());

		it("should return only the first element", function (done) {
			Person.find().order("-age").first(function (err, JaneDoe) {
				should.equal(err, null);

				JaneDoe.name.should.equal("Jane");
				JaneDoe.surname.should.equal("Doe");
				JaneDoe.age.should.equal(20);

				return done();
			});
		});

		it("should return null if not found", function (done) {
			Person.find({ name: "Jack" }).first(function (err, Jack) {
				should.equal(err, null);
				should.equal(Jack, null);

				return done();
			});
		});
	});

	describe(".last()", function () {
		before(setup());

		it("should return only the last element", function (done) {
			Person.find().order("age").last(function (err, JaneDoe) {
				should.equal(err, null);

				JaneDoe.name.should.equal("Jane");
				JaneDoe.surname.should.equal("Doe");
				JaneDoe.age.should.equal(20);

				return done();
			});
		});

		it("should return null if not found", function (done) {
			Person.find({ name: "Jack" }).last(function (err, Jack) {
				should.equal(err, null);
				should.equal(Jack, null);

				return done();
			});
		});
	});

	describe(".find()", function () {
		before(setup());

		it("should not change find if no arguments", function (done) {
			Person.find().find().count(function (err, count) {
				should.equal(err, null);
				count.should.equal(3);

				return done();
			});
		});

		it("should restrict conditions if passed", function (done) {
			Person.find().find({ age: 18 }).count(function (err, count) {
				should.equal(err, null);
				count.should.equal(2);

				return done();
			});
		});

		it("should restrict conditions if passed and also be chainable", function (done) {
			Person.find().find({ age: 18 }).find({ name: "Jane" }).count(function (err, count) {
				should.equal(err, null);
				count.should.equal(1);

				return done();
			});
		});

		it("should allow sql where conditions", function (done) {
			Person.find({ age: 18 }).where("LOWER(surname) LIKE 'dea%'").all(function (err, items) {
				should.equal(err, null);
				items.length.should.equal(1);

				return done();
			});
		});

		it("should allow sql where conditions with auto escaping", function (done) {
			Person.find({ age: 18 }).where("LOWER(surname) LIKE ?", ['dea%']).all(function (err, items) {
				should.equal(err, null);
				items.length.should.equal(1);

				return done();
			});
		});

		it("should append sql where conditions", function (done) {
			Person.find().where("LOWER(surname) LIKE ?", ['do%']).all(function (err, items) {
				should.equal(err, null);
				items.length.should.equal(2);

				Person.find().where("LOWER(name) LIKE ?", ['jane']).all(function (err, items) {
					should.equal(err, null);
					items.length.should.equal(2);

					Person.find().where("LOWER(surname) LIKE ?", ['do%']).where("LOWER(name) LIKE ?", ['jane']).all(function (err, items) {
						should.equal(err, null);
						items.length.should.equal(1);

						return done();
					});
				});
			});
		});

		it("should return results if passed a callback as second argument", function (done) {
			Person.find().find({ age: 18 }, function (err, instances) {
				should.equal(err, null);
				instances.should.have.property("length", 2);

				return done();
			});
		});
	});

	describe(".each()", function () {
		before(setup());

		it("should return a ChainInstance", function (done) {
			var chain = Person.find().each();

			chain.filter.should.be.a("function");
			chain.sort.should.be.a("function");
			chain.count.should.be.a("function");

			return done();
		});
	});

	describe(".remove()", function () {
		before(setup());

		it("should have no problems if no results found", function (done) {
			Person.find({ age: 22 }).remove(function (err) {
				should.equal(err, null);

				Person.find().count(function (err, count) {
					should.equal(err, null);

					count.should.equal(3);

					return done();
				});
			});
		});

		it("should remove results and give feedback", function (done) {
			Person.find({ age: 20 }).remove(function (err) {
				should.equal(err, null);

				Person.find().count(function (err, count) {
					should.equal(err, null);

					count.should.equal(2);

					return done();
				});
			});
		});
	});

	describe(".each()", function () {
		before(setup());

		it("should return a ChainFind", function (done) {
			var chain = Person.find({ age: 22 }).each();

			chain.should.be.a("object");
			chain.filter.should.be.a("function");
			chain.sort.should.be.a("function");
			chain.count.should.be.a("function");
			chain.get.should.be.a("function");
			chain.save.should.be.a("function");

			return done();
		});

		describe(".count()", function () {
			it("should return the total filtered items", function (done) {
				Person.find().each().filter(function (person) {
					return (person.age > 18);
				}).count(function (count) {
					count.should.equal(1);

					return done();
				});
			});
		});

		describe(".sort()", function () {
			it("should return the items sorted using the sorted function", function (done) {
				Person.find().each().sort(function (first, second) {
					return (first.age < second.age);
				}).get(function (people) {
					should(Array.isArray(people));

					people.length.should.equal(3);
					people[0].age.should.equal(20);
					people[2].age.should.equal(18);

					return done();
				});
			});
		});

		describe(".save()", function () {
			it("should save items after changes", function (done) {
				Person.find({ surname: "Dean" }).each(function (person) {
					person.age.should.not.equal(45);
					person.age = 45;
				}).save(function () {
					Person.find({ surname: "Dean" }, function (err, people) {
						should(Array.isArray(people));

						people.length.should.equal(1);
						people[0].age.should.equal(45);

						return done();
					});
				});
			});
		});

		describe("if passing a callback", function () {
			it("should use it to .forEach()", function (done) {
				Person.find({ surname: "Dean" }).each(function (person) {
					person.fullName = person.name + " " + person.surname;
				}).get(function (people) {
					should(Array.isArray(people));

					people.length.should.equal(1);
					people[0].fullName = "Jane Dean";

					return done();
				});
			});
		});

		describe(".hasAccessor() for hasOne associations", function () {
		    it("should be chainable", function (done) {
				Person.find({ name: "John" }, function (err, John) {
					should.equal(err, null);

					var Justin = new Person({
						name : "Justin",
						age  : 45
					});

					John[0].setParents([ Justin ], function (err) {
						should.equal(err, null);

						Person.find().hasParents(Justin).all(function (err, people) {
						    should.equal(err, null);

							should(Array.isArray(people));

							people.length.should.equal(1);
							people[0].name.should.equal("John");

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