sequelize/sequelize — pull request #748 by durango (2013-07-05 20:49:05+00:00)
Title: Scopes and building smart where objects closes #386

Several things in this commit:

1. New scope() functionality, works very similar to ActiveRecord's scope.

2. Big internal changes (but as of right now only related to scope()) made for the {where} object when using finders (se Utils.smartWhere). Could be useful for some, it basically breaks down several where objects into one concise object.

3. New where finders.. 'eq', 'like', 'nlike', 'notlike', and 'not' (not in). 4. New finder shortcuts, '..' for between and '..' for not between

## Usage

```js
sequelize.define('table', {
username: DataTypes.STRING,
email: DataTypes.STRING,
status: DataTypes.STRING,
accessLevel: DataTypes.INTEGER
}, {
defaultScope: {
where: {
status: 'active'
}
},
scopes: {
orderScope: {
order: 'accessLevel DESC'
},
limitScope: {
limit: 2
},
sequelizeTeam: {
where: ['email LIKE \'%@sequelizejs.com\'']
},
lowAccess: {
where: {
accessLevel: 5
}
},
specificAccess: function(value) {
return {
where: {
accessLevel: value
}
}
},
isActive: {
where: {
status: 'active'
}
},
isSuspended: function(value) {
return {
where: {
status: 'suspended'
}
}
},
}
})

// Any select methods will now prepend the where object with the defaultScope
table.findAll().success(function(users){
console.log(users) // will only return users with status='active'
})

// You can also amend to scopes in any of the select/finder calls...
table.findAll({
where: {
email: {
like: '%@sequelizejs.com' // This is new too!
}
}
}).success(function(users){
console.log(users) // will only return users with status='active' and emails like '%@sequelizejs.com'
})

// NOTE: When calling a predefined scope, the defaultScope is no longer taken into consideration and complete scraped
// To call a predefined scope
table.scope('sequelizeTeam').findAll().success(function(users){
// notice the where clause is NOT "WHERE status='active'" but simply "WHERE email LIKE '%@sequelizejs.com'
console.log(users) // will return users with email LIKE '%@sequelizejs.com'
})

// You can combined scopes...
table.scope('orderScope', 'limitScope', 'sequelizeTeam').findAll().success(function(users){
console.log(users) // will return users with email LIKE '%@sequelizejs.com', order by accessLevel, and limited to 2...
})

// alternatively you can do.. ['orderScope', 'limitScope', 'sequelizeTeam'] within scope()

// You can also call function'd scopes... it's just a simple .apply() method
table.scope({method: ['specificAccess', 10]}).findAll().success(function(users){
console.log(users) // will return users with an accessLevel of 10
})

// If your function had more than one argument just simply keep adding to the array
// e.g. {method: ['someScope', 'hello', 'world', 'third_argument', 'fourth!']}

// Sometimes your scopes will clash together like so...
table.scope('isActive', 'isSuspended').success(function(users){
// WHERE status='active' AND status='suspended'
})

// You can apply a "merge" attribute to your scope in order to let sequelize know which value to overwrite/merge.
table.scope('isActive', {merge: 'isSuspended'}).success(function(users){
// WHERE status='suspended'
})

// ...yes you can merge method scopes
table.scope('lowAccess', {merge: true, method: ['specificAccess', 10]}).success(function(users) {
console.log(users) // WHERE accessLevel=10 and NOT accessLevel=5 AND accessLevel=10
})

// if you don't want to use a scope, simply call scope with no arguments
table.scope().findAll()

// you can alternatively just throw a falsy value into the first argument
table.scope(false).findAll()
```