text:
# Node.js - Aula 08 - Exercício
**user:** [ednilsonamaral](https://github.com/ednilsonamaral)
**autor:** Ednilson Amaral

## 1 - Insira 5 pokemons novos, na coleção `pokemons`, escolha 3 e os adicione em um array e uma nova coleção chamada `meus-pokemons`, utilizando o `ObjectId`. Adicione o `required` em campos que ache obrigatório no *Schema* do Pokemon.

### Inserindo 5 pokemons novos

`crud.js`
```js
'use strict';

const mongoose = require('mongoose');
const dbURI = 'mongodb://localhost/be-mean-pokemons';

mongoose.connect(dbURI);

const Schema = mongoose.Schema;

const _schema = {
  name: {type: String, required: true, match: /^./i},
  description: {type: String, required: true, match: /^./i},
  type: {type: String, required: true, match: /^./i},
  attack: {type: Number, min: 1},
  height: {type: Number}
};

const PokemonSchema = new Schema(_schema);
const PokemonModel = mongoose.model('pokemons', PokemonSchema);

//inserindo os 5 novos pokemons
const data = [
  {
    name: 'Educamon',
    description: 'O pokemon mais educadinho do universo',
    type: 'educador',
    attack: 76,
    height: 1.7
  },

  {
    name: 'NFLMon',
    description: 'O pokemon mais QB, fanático por NFL, qualquer um que se aproxime ele dá um tackle',
    type: 'player',
    attack: 105,
    height: 2.3
  },

  {
    name: 'JiuJiteroMon',
    description: 'Faixa preta 3º grau',
    type: 'jiujitsu',
    attack: 153,
    height: 1.6
  },

  {
    name: 'BaianoMon',
    description: 'O pokemon mais preguiçoso do universo',
    type: 'preguiça',
    attack: 51,
    height: 1.7
  },

  {
    name: 'GoogleMon',
    description: 'O pokemon mais googlet do universo',
    type: 'google',
    attack: 99,
    height: 1.7
  },
];

PokemonModel.create(data, (err, data) => {
  if (err) return console.log('Erro: ', err);
  console.log('Inserido(s): ', data);
});
```

Saída no terminal:

```
$ node app.js
Mongoose default connection error: Error: Trying to open unclosed connection.
Mongoose default connection connected to mongodb://localhost/be-mean-pokemons
Mongoose default connection is open
Inserido(s):  [ { _id: 56d841324d9d0e4b13764e93,
    height: 1.7,
    attack: 76,
    type: 'educador',
    description: 'O pokemon mais educadinho do universo',
    name: 'Educamon',
    __v: 0 },
  { _id: 56d841324d9d0e4b13764e94,
    height: 2.3,
    attack: 105,
    type: 'player',
    description: 'O pokemon mais QB, fanático por NFL, qualquer um que se aproxime ele dá um tackle',
    name: 'NFLMon',
    __v: 0 },
  { _id: 56d841324d9d0e4b13764e95,
    height: 1.6,
    attack: 153,
    type: 'jiujitsu',
    description: 'Faixa preta 3º grau',
    name: 'JiuJiteroMon',
    __v: 0 },
  { _id: 56d841324d9d0e4b13764e96,
    height: 1.7,
    attack: 51,
    type: 'preguiça',
    description: 'O pokemon mais preguiçoso do universo',
    name: 'BaianoMon',
    __v: 0 },
  { _id: 56d841324d9d0e4b13764e97,
    height: 1.7,
    attack: 99,
    type: 'google',
    description: 'O pokemon mais googlet do universo',
    name: 'GoogleMon',
    __v: 0 } ]
```

### Escolhendo 3 pokemons e inserindo em uma nova coleção com o `ObjectId`.

`ref.js`

```js
'use strict';

const mongoose = require('mongoose');
const dbURI = 'mongodb://localhost/be-mean-pokemons';

mongoose.connect(dbURI);

const Schema = mongoose.Schema;

const _schema = {
  pokemons: [{type: Schema.Types.ObjectId, ref: 'pokemons'}]
};

const PokemonSchema = new Schema(_schema);
const PokemonModel = mongoose.model('meus-pokemons', PokemonSchema);

//inserindo os 3 pokemons escolhidos com ObjectID
const data = {
  pokemons: ['56d841324d9d0e4b13764e97', '56d841324d9d0e4b13764e96', '56d841324d9d0e4b13764e95']
};

PokemonModel.create(data, (err, data) => {
  if (err) return console.log('Erro: ', err);
  console.log('Inserido(s): ', data);
});
```

Saída no terminal:

```
$ node app.js
Mongoose default connection error: Error: Trying to open unclosed connection.
Mongoose default connection connected to mongodb://localhost/be-mean-pokemons
Mongoose default connection is open
Inserido(s):  { pokemons:
   [ 56d841324d9d0e4b13764e97,
     56d841324d9d0e4b13764e96,
     56d841324d9d0e4b13764e95 ],
  _id: 56d843ae813254aa1312e7a7,
  __v: 0 }
```

## 2 - Crie um *Schema* de exemplo com validação para os campos (utilizar Arquitetura Atômica, ou seja cada campo sendo um Schema separado):

* email;
* cpf;
* cnpj;
* url;
* ip.

`app.js`

```js
'use strict';

require('./db/config');

const CRUD = require('./controller');

const data = {
  email: 'ednilsonamaral.ti@gmail.com',
  cpf: 12345678,
  cnpj: 123654,
  url: 'www.ednilsonamaral.com.br',
  ip: '192.168.0.1'
};

CRUD.create(data);
```

`schema.js`

```js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const email = require('./fields/field-email');
const cpf = require('./fields/field-cpf');
const cnpj = require('./fields/field-cnpj');
const url = require('./fields/field-url');
const ip = require('./fields/field-ip');

const _schema = {
  email,
  cpf,
  cnpj,
  url,
  ip
};

module.exports = new Schema(_schema);
```

`model.js`

```js
module.exports = function (Schema, ModelName) {
  const mongoose = require('mongoose');
  return mongoose.model(ModelName, Schema);
};
```

`controller.js`

```js
'use strict';

const Schema = require('./schema');
const Model = require('./model')(Schema, 'atomicos');

const CRUD = {
  create: function(data) {
    console.log("create: ", data);
    const AmoticosModel = new Model(data);
    AmoticosModel.save(function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Inseriu:', data);
    });
  },
  retrieve: function(query) {
    Model.find(query, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Buscou:', data);
    });
  },
  update: function(query, mod, options) {
    var options = options || {};
    Model.update(query, mod, options, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Alterou:', data);
    });
  },
  delete: function(query) {
    Model.remove(query, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Deletou:', data);
    });
  },
};

module.exports = CRUD;
```

`fields`

```js
//field-email.js
module.exports = {
  type: String,
  match:
    [
      /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/,
      'Preencha um e-mail válido!'
    ],
    required: true
  }

//field-cpf.js
module.exports = {type: Number, minlength: 3, maxlength: 11, required: true}

//field-cnpj.js
module.exports = {type: Number, minlength: 3}

//field-url.js
module.exports = {type: String, required: true, match: /([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/}

//field-ip.js
module.exports = {type: String, maxlenght: 15}
```

Saída no terminal:

```
$ node app.js
create:  { email: 'ednilsonamaral.ti@gmail.com',
  cpf: 12345678,
  cnpj: 123654,
  url: 'www.ednilsonamaral.com.br',
  ip: '192.168.0.1' }
Mongoose default connection connected to mongodb://localhost/bancoTeste
Mongoose default connection is open
Inseriu: { _id: 56d84cfdac4d9e351638cce0,
  ip: '192.168.0.1',
  url: 'www.ednilsonamaral.com.br',
  cnpj: 123654,
  cpf: 12345678,
  email: 'ednilsonamaral.ti@gmail.com',
  __v: 0 }
```

## 3 - Dê 3 exemplos **diferentes**, de cada, utilizando as funções:

### Schema e Model

```js
'use strict';

const mongoose = require('mongoose');
const dbURI = 'mongodb://localhost/be-mean-pokemons';

mongoose.connect(dbURI);

const Schema = mongoose.Schema;
const _schema = {
  name: {type: String, required: true, match: /^./i},
  description: {type: String, required: true, match: /^./i},
  type: {type: String, required: true, match: /^./i},
  attack: {type: Number, min: 1},
  height: {type: Number}
};

const PokemonSchema = new Schema(_schema);

const Model = mongoose.model('pokemons', PokemonSchema);
```

### `findAndModify`

`findAndModify.js`

```js
'use strict';

const mongoose = require('mongoose');
const dbURI = 'mongodb://localhost/be-mean-pokemons';

mongoose.connect(dbURI);

const Schema = mongoose.Schema;

const _schema = {
  name: {type: String, match: /^./i},
  description: {type: String, match: /^./i},
  type: {type: String, match: /^./i},
  attack: {type: Number, min: 1},
  height: {type: Number}
};

const PokemonSchema = new Schema(_schema);

PokemonSchema.statics.findAndModify = function (query, sort, doc, options, callback) {
  return this.collection.findAndModify(query, sort, doc, options, callback);
};

const Model = mongoose.model('pokemons', PokemonSchema);

const query  = {attack: {$lte: 51}};
const mod = {type: 'new york giants'};
const opt = {multi: true};

Model.findAndModify(query, [], mod, opt, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterou: ', data);
});
```

Saída no terminal:

```
$ node findAndModify.js
Alterou:  { lastErrorObject: { updatedExisting: true, n: 1 },
  value:
   { _id: 56aeb9d04bb106b114749436,
     name: 'Nerdmon',
     description: 'Deu um upgrade, pode melhorar, parça!',
     type: 'nerd',
     attack: 51,
     defense: 888,
     height: 1.8,
     __v: 0 },
  ok: 1 }
```

Na documentação do Mongoose diz que não possui o `findAndModify`, então, com a ajuda de alguns colegas e pesquisando na net, foi mencionado uma alternativa, utilizando o `.statics`, conforme exemplo acima.

###`findOneAndUpdate`

#### Exemplo 1

`findOneAndUpdate.js`

```js
const query = {name: /nflmon/i};
const mod = {type: 'professional'};
const options = {};

Model.findOneAndUpdate(query, mod, options, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterou: ', data);
})
```

Saída no terminal:

```
$ node findOneAndUpdate.js
Alterou:  { __v: 0,
  height: 2.3,
  attack: 105,
  type: 'player',
  description: 'O pokemon mais QB, fanático por NFL, qualquer um que se aproxime ele dá um tackle',
  name: 'NFLMon',
  _id: 56d841324d9d0e4b13764e94 }
```

#### Exemplo 2

`findOneAndUpdate.js`

```js
const query = {attack: {$lte: 50}};
const mod = {attack: 999};
const options = {multi: true};

Model.findOneAndUpdate(query, mod, options, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterou: ', data);
});
```

Saída no terminal:

```
$ node findOneAndUpdate.js
Alterou:  { moves: [ 'engole fogo', 'assopra veneno', 'desvio' ],
  active: false,
  height: 0.4,
  attack: 49,
  type: 'grama',
  description: 'Chicote de trepadeira',
  name: 'Bulbassauro',
  _id: 564cff04f9025dedb2553204 }
```

#### Exemplo 3

`findOneAndUpdate.js`

```js
const query = {$and: [{type: 'nerd'}, {attack: {$gte: 40}}]};
const mod = {attack: 51, description: 'Deu um upgrade, pode melhorar, parça!'};
const options = {multi: true};

Model.findOneAndUpdate(query, mod, options, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterou: ', data);
});
```

Saída no terminal:

```
$ node findOneAndUpdate.js
Alterou:  { __v: 0,
  height: 1.8,
  defense: 888,
  attack: 49,
  type: 'nerd',
  description: 'O pokemon mais nerd que já existiu, que existe ou que existirá',
  name: 'Nerdmon',
  _id: 56aeb9d04bb106b114749436 }
```

###`findOneAndRemove`

#### Exemplo 1

`findOneAndRemove.js`

```js
const query = {attack: null};

Model.findOneAndRemove(query, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Removeu: ', data);
});
```

Saída no terminal:

```
$ node findOneAndRemove.js
Removeu:  { description: 'Sem maiores informações',
  moves: [],
  defense: null,
  height: null,
  attack: null,
  name: 'AindaNaoExisteMom',
  _id: 564de099fc7e5880d64a877e }
```

#### Exemplo 2

`findOneAndRemove.js`

```js
const query = {$and: [{type: 'google'}, {height: {$gte: 1.5}}]};

Model.findOneAndRemove(query, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Removeu: ', data);
});
```

Saída no terminal:

```
$ node findOneAndRemove.js
Removeu:  { __v: 0,
  height: 1.7,
  attack: 99,
  type: 'google',
  description: 'O pokemon mais googlet do universo',
  name: 'GoogleMon',
  _id: 56d841324d9d0e4b13764e97 }
```

#### Exemplo 3

`findOneAndRemove.js`

```js
const query = {height: {$lte: 1.7}};

Model.findOneAndRemove(query, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Removeu: ', data);
});
```

Saída no terminal:

```
$ node findOneAndRemove.js
Removeu:  { moves: [ 'engole fogo', 'assopra veneno', 'desvio' ],
  active: false,
  height: 0.4,
  attack: 999,
  type: 'grama',
  description: 'Chicote de trepadeira',
  name: 'Bulbassauro',
  _id: 564cff04f9025dedb2553204 }
```

## 4 - Crie 1 *Schema* com todo CRUD funcional e métodos especiais, que agrupe:

* virtuals;
* getters & setters;
* method & static;
* embedded document;
* plugins;
* middlewares.

Realizei a importação do `fighters.json` no *database* para trabalhar com o CRUD em cima de alguns documentos já presentes, e, também adicionei um novo documento via `app.js`.

`fighters.json`

```js
{name: {first: 'Demetrious', last: 'Johnson'}, age: 29, weight_class: 'Flyweight'}
{name: {first: 'Joseph', last: 'Benavidez'}, age: 31, weight_class: 'Flyweight'}
{name: {first: 'Conor', last: 'McGregor'}, age: 27, weight_class: 'Featherweight'}
{name: {first: 'José', last: 'Aldo'}, age: 29, weight_class: 'Featherweight'}
{name: {first: 'Domminick', last: 'Cruz'}, age: 30, weight_class: 'Bantamweight'}
```

Importando:

```
$ mongoimport --db bancoTeste --collection fighters --drop --file fighters.json
2016-03-07T14:13:25.769-0300  connected to: localhost
2016-03-07T14:13:25.770-0300  dropping: bancoTeste.fighters
2016-03-07T14:13:26.206-0300  imported 5 documents
```

Abaixo, os arquivos do CRUD funcional com os métodos solicitados.

`config.js`

```js
const mongoose = require('mongoose');
const dbURI = 'mongodb://localhost/bancoTeste';

mongoose.connect(dbURI);

mongoose.connection.on('connected', function () {
  console.log('Mongoose default connection connected to ' + dbURI);
});

mongoose.connection.on('error',function (err) {
  console.log('Mongoose default connection error: ' + err);
});

mongoose.connection.on('disconnected', function () {
  console.log('Mongoose default connection disconnected');
});

mongoose.connection.on('open', function () {
  console.log('Mongoose default connection is open');
});

process.on('SIGINT', function() {
  mongoose.connection.close(function () {
    console.log('Mongoose default connection disconnected through app termination');
    process.exit(0);
  });
});
```

`app.js`

```js
'use strict';

require('./db/config');

const CRUD = require('./controller');

//inserindo novo documento
const hab1 = {
  title: 'Jiu Jitsu',
  since: 2005
};

const hab2 = {
  title: 'Muay Thai',
  since: 1999
};

const hab3 = {
  title: 'Boxe',
  since: 2001
};

const data = {
  name: {
    first: 'Anderson',
    last: 'Silva'
  },
  age: 42,
  skills: [hab1, hab2, hab3],
  weight_class: 'Middleweight'
};

CRUD.create(data);

//pesquisando um documento
const query = "56ddce078b09ec822118e4c7";

CRUD.retrieve(query);

//pesquisando através de um method
const query = {name: {first: 'Mauro', last: 'Filho'}, weight_class: /featherweight/i};

CRUD.retrieve_method(query);

//pesquisando através de uma static
const query = {name: {first: 'Anderson', last: 'Silva'}};

CRUD.retrieve_static(query);

//pesquisando através de middleware pre count
const query = {age: 29};

CRUD.retrieve_middleware(query);
```

`controller.js`

```js
'use strict';

const mongoose = require('mongoose');

const Schema = require('./schema');
const Model = require('./model')(Schema, 'fighters');

const CRUD = {
  create: function(data) {
    console.log("create: ", data);
    const FighterModel = new Model(data);
    FighterModel.save(function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Inseriu:', data);
    });
  },
  retrieve: function(query) {
    Schema
      .virtual('name.full')
      .get(function (){
        return this.name.first + ' ' + this.name.last;
      });

    Model.findById(query, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Nome: ', data.name.full, '\nCategoria de Peso: ', data.weight_class, '\nIdade: ', data.age);
    });
  },
  retrieve_method: function(query){
    Schema.methods.findSimilarType = function findSimilarType (callback) {
      return this.model('MesmaCategoria', Schema);
    };

    const MethodModel = mongoose.model('MethodModel', Schema);
    const outroFighter = new MethodModel(query);

    outroFighter.findSimilarType(function (err, data){
      if (err) return console.log('ERRO: ', err);
      return data.forEach((fighters) => console.log('fighter: ', fighters));
    });
  },
  retrieve_static: function(query) {
    Schema.statics.search = function (name, callback) {
      return this.where('name', new RegExp(name, 'i')).exec(callback);
    };

    const StaticModel = mongoose.model('StaticModel', Schema);
    const maisOutro = new StaticModel(query);

    maisOutro.search(query, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return data.forEach((fighters) => console.log('fighter: ', fighters));
    });
  },
  retrieve_middleware: function(query){
    const countQuery = Model.where(query).count((err, count) => {
      if (err) return console.log('ERRO: ', err);
      return console.log('Existem ' + count + ' lutadores com a mesma idade!');
    });
  },
  update: function(query, mod, options) {
    var options = options || {};
    Model.update(query, mod, options, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Alterou:', data);
    });
  },
  delete: function(query) {
    Model.remove(query, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Deletou:', data);
    });
  },
};

module.exports = CRUD;
```

`model.js`

```js
module.exports = function (Schema, ModelName) {
  const mongoose = require('mongoose');
  return mongoose.model(ModelName, Schema);
};
```

`schema.js`

```js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const name = require('./fields/field-name');
const age = require('./fields/field-age');
const weight_class = require('./fields/field-weight_class');
const created_at = require('./fields/field-created_at');

const skill = new Schema({
  title: String,
  since: Number
});

const _schema = {
  name,
  age,
  weight_class,
  skills: [skill],
  created_at
};

module.exports = new Schema(_schema);
```

Aqui estão os *fields*:

```js
//field-name.js
module.exports = {
  first: {type: String, match: /^./i},
  last: {type: String, match: /^./i}
},{
  toObject: {virtuals: true},
  toJSON: {virtuals: true}
}

//field-age.js
module.exports = {type: Number, min: 18}

//field-skill.js
module.exports = {type: [skill]}

//field-weight_class.js
function toUpper (v) {
  return v.toUpperCase();
}

module.exports = {
  type: String,
  match: /^./i,
  get: toUpper
}

//field-created_at.js
module.exports = { type: Date, default: Date.now }
```

## 5 - Crie 1 *Schema* para `password` com criptografia e arquitetura atômica.
* use SHA256 com SALT como criptografia;
* use middleware com pre save;
* use methods.

`app.js`

```js
'use strict';

require('./db/config');

const CRUD = require('./controller');
const Model = require('./model');

const data = {
  name: {
    first: 'Ednilson',
    last: 'Amaral'
  },
  password: '123456987'
}

CRUD.create(data);
```

`controller.js`

```js
'use strict';

const mongoose = require('mongoose');

const Schema = require('./schema');
const Model = require('./model')(Schema, 'userPass');

const CRUD = {
  create: function(data) {
    console.log("create: ", data);
    SenhaModel.save(function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Inseriu:', data);
    });
  },
  retrieve: function(query) {
    Schema
      .virtual('name.full')
      .get(function (){
        return this.name.first + ' ' + this.name.last;
      });

    Model.findById(query, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Nome: ', data.name.full, '\nSenha: ', data.password);
    });
  },
  update: function(query, mod, options) {
    var options = options || {};
    Model.update(query, mod, options, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Alterou:', data);
    });
  },
  delete: function(query) {
    Model.remove(query, function (err, data) {
      if (err) return console.log('ERRO: ', err);
      return console.log('Deletou:', data);
    });
  },
};

module.exports = CRUD;
```

`schema.js`

```js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const name = require('./fields/field-name');
const password = require('./fields/field-password');

function criaUsuario (){
  const novoUsuario = new Schema({
    name,
    password
  });

  novoUsuario.methods.vaiCriptografar = function (){
    const crypto = require('crypto');
    //fazendo a mágica acontecer
    const salt = crypto.randomBytes(128).toString('base64');
    const chave = crypto.pbkdf2Sync(this.password, 'salt', 100000, 256, 'sha256');

    return chave.toString('hex');
  };

  novoUsuario.pre('save', true, function (next, done) {
    console.log('Senha SEM criptografia: ', '${this.password}');
    //fazendo a mágica acontecer
    this.password = this.vaiCriptografar();
    console.log('\nSenha COM criptografia: ', '${this.password}');
    next();
  });

  return mongoose.model('SenhaModel', novoUsuario);
}

module.exports = exports = criaUsuario();
```

`model.js`

```js
module.exports = function (Schema, ModelName) {
  const mongoose = require('mongoose');
  return mongoose.model(ModelName, Schema);
};
```

`fields`

```js
//field-name
module.exports = {
  first: {type: String, match: /^./i},
  last: {type: String, match: /^./i}
},{
  toObject: {virtuals: true},
  toJSON: {virtuals: true}
}

//field-password
module.exports = {type: String, match: /^./i}
```

`config.js`

```js
const mongoose = require('mongoose');
const dbURI = 'mongodb://localhost/bancoTeste';

mongoose.connect(dbURI);

mongoose.connection.on('connected', function () {
  console.log('Mongoose default connection connected to ' + dbURI);
});

mongoose.connection.on('error',function (err) {
  console.log('Mongoose default connection error: ' + err);
});

mongoose.connection.on('disconnected', function () {
  console.log('Mongoose default connection disconnected');
});

mongoose.connection.on('open', function () {
  console.log('Mongoose default connection is open');
});

process.on('SIGINT', function() {
  mongoose.connection.close(function () {
    console.log('Mongoose default connection disconnected through app termination');
    process.exit(0);
  });
});
```

Saída no terminal:

```
Senha SEM criptografia: 123456987
Senha COM criptografia: B4FBE57144DF88EEE69838FEE592DFE7FC250722CBCDB217CC89E67295FD8073
```


text:
# Node.js - Aula 08 - Exercício

**User:** [gkal19](https://github.com/gkal19)

**Autor:** Gabriel Kalani

**Data** 1465801860

#### 1 - Insira 5 pokemons novos, na coleção pokemons, escolha 3 e os adicione em um array e uma nova coleção chamada meus-pokemons, utilizando o ObjectId. Adicione o required em campos que ache obrigatório no Schema do Pokemon.
```js
'use strict';

const mongoose = require('mongoose');
const dbURI = 'mongodb://localhost/be-mean-pokemons';
mongoose.connect(dbURI);
const Schema = mongoose.Schema;

const _schema = {
  name: {type: String, required: true, match: /^./i},
  description: {type: String, required: true, match: /^./i},
  type: {type: String, required: true, match: /^./i},
  attack: {type: Number, min: 1},
  height: {type: Number}
};
const PokemonSchema = new Schema(_schema);
const PokemonModel = mongoose.model('pokemons', PokemonSchema);

const data = [
  {
    name: 'Harry James Potter',
    description: 'Harry James Potter',
    type: 'bruxo',
    attack: 76,
    height: 1.7
  },

  {
    name: 'Hermione Granger',
    description: 'Hermione Granger',
    type: 'bruxa',
    attack: 105,
    height: 2.3
  },

  {
    name: 'Ronald Weasley',
    description: 'Ronald Weasley',
    type: 'bruxo',
    attack: 153,
    height: 1.6
  },

  {
    name: 'Neville Longbottom',
    description: 'Neville Longbottom',
    type: 'bruxo',
    attack: 51,
    height: 1.7
  },

  {
    name: 'Luna Lovegood',
    description: 'Luna Lovegood',
    type: 'bruxa',
    attack: 99,
    height: 1.7
  },
];

PokemonModel.create(data, (err, data) => {
  if (err) return console.log('Erro: ', err);
  console.log('Inserido(s): ', data);
});
```

#### Escolher 3 pokemons e inserindo em uma nova coleção com o ObjectId.
```js
'use strict';

const mongoose = require('mongoose');
const dbURI = 'mongodb://localhost/be-mean-instagram';
mongoose.connect(dbURI);
const Schema = mongoose.Schema;

const _schema = {
  pokemons: [{type: Schema.Types.ObjectId, ref: 'pokemons'}]
};

const PokemonSchema = new Schema(_schema);
const PokemonModel = mongoose.model('myPokemons', PokemonSchema);

const data = {
  pokemons: ['575c12969d2f2ed60edbb52b', '575c12969d2f2ed60edbb52c', '575c12969d2f2ed60edbb52d']
};

PokemonModel.create(data, (err, data) => {
  if (err) return console.log('Erro: ', err);
  console.log('Inserido(s): ', data);
});
```

#### 2 - Crie um Schema de exemplo com validação para os campos (utilizar Arquitetura Atômica, ou seja cada campo sendo um Schema separado):
> Os campos utilizados serão: CPF, E-mail, CNPJ, URL e IP.

`app.js`

```js
'use strict';

require('./db/config');

const CRUD = require('./controller');

const data = {
  email: 'gabrielsilva1956@gmail.com',
  cpf: 12345678,
  cnpj: 123456,
  url: 'www.gabrielkalani.com.br',
  ip: '192.168.0.1'
};

CRUD.create(data);
```

`schema.js`

```js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const email = require('./fields/field-email');
const cpf = require('./fields/field-cpf');
const cnpj = require('./fields/field-cnpj');
const url = require('./fields/field-url');
const ip = require('./fields/field-ip');

const _schema = {
  email,
  cpf,
  cnpj,
  url,
  ip
};

module.exports = new Schema(_schema);
```

`model.js`

```js
module.exports = function (Schema, ModelName) {
  const mongoose = require('mongoose');
  return mongoose.model(ModelName, Schema);
};
```

`controller.js`

```js
'use strict';

const Schema = require('./schema');
const Model = require('./model')(Schema, 'atomicos');

const CRUD = {
  create(data) {
    console.log("create: ", data);
    const AmoticosModel = new Model(data);
    AmoticosModel.save((err, data) => {
      if (err) return console.log('ERRO: ', err);
      return console.log('Inseriu:', data);
    });
  },
  retrieve(query) {
    Model.find(query, (err, data) => {
      if (err) return console.log('ERRO: ', err);
      return console.log('Buscou:', data);
    });
  },
  update(query, mod, options) {
    var options = options || {};
    Model.update(query, mod, options, (err, data) => {
      if (err) return console.log('ERRO: ', err);
      return console.log('Alterou:', data);
    });
  },
  delete(query) {
    Model.remove(query, (err, data) => {
      if (err) return console.log('ERRO: ', err);
      return console.log('Deletou:', data);
    });
  },
};

module.exports = CRUD;
```

`fields`

```js
//field-email.js
module.exports = {
  type: String,
  match:
    [
      /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/,
      'Preencha um e-mail válido!'
    ],
    required: true
  }

//field-cpf.js
module.exports = {type: Number, minlength: 3, maxlength: 11, required: true}

//field-cnpj.js
module.exports = {type: Number, minlength: 3}

//field-url.js
module.exports = {type: String, required: true, match: /([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?/}

//field-ip.js
module.exports = {type: String, maxlenght: 15}
```

Saída no terminal:

```
$ node app.js
create:  { email: 'gabrielsilva1956@gmail.com',
  cpf: 12345678,
  cnpj: 123456,
  url: 'www.gabrielkalani.com.br',
  ip: '192.168.0.1' }
Mongoose default connection connected to mongodb://localhost/be-mean-instagram
Mongoose default connection is open
Inseriu: { _id: 56d84cfdac419e351638ase0,
  ip: '192.168.0.1',
  url: 'www.gabrielkalani.com.br',
  cnpj: 123456,
  cpf: 12345678,
  email: 'gabrielsilva1956@gmail.com',
  __v: 0 }
```

#### 3 - Dê 3 exemplos diferentes, de cada, utilizando as funções:
`findAndModify`,`findOneAndUpdate` e `findOneAndRemove`.

#### Schema e Model
```js
'use strict';

const mongoose = require('mongoose');
const db = 'mongodb://localhost/be-mean-instagram';

mongoose.connect( db );

const Schema = mongoose.Schema;
const _schema = {
  name: { type: String, required: true, match: /^./i },
  description: { type: String, required: true, match: /^./i},
  type: { type: String, required: true, match: /^./i},
  attack: { type: Number, min: 1}
};

const PokemonSchema = new Schema(_schema);

const Model = mongoose.model('pokemons', PokemonSchema);
```

#### findAndModify
```js
'use strict';

const mongoose = require( 'mongoose' );
const db = 'mongodb://localhost/be-mean-instagram';
mongoose.connect(db);

const Schema = mongoose.Schema;

const _schema = {
  name: {type: String, match: /^./i},
  description: {type: String, match: /^./i},
  type: {type: String, match: /^./i},
  attack: {type: Number, min: 1}
};

const PokemonSchema = new Schema(_schema);

PokemonSchema.statics.findAndModify = function (query, sort, doc, options, callback) {
  return this.collection.findAndModify(query, sort, doc, options, callback);
};

const Model = mongoose.model('pokemons', PokemonSchema);

const query  = { attack: {$lte: 5 } };
const mod = { type: 'eletric' };
const options = { multi: true };

Model.findAndModify(query, [], mod, options, (err, data) => {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterado: ', data);
});

// Output Terminal
Alterado:  { value: null, ok: 1 }
```

#### findOneAndUpdate

##### Exemplo 1
```js
const query = { name: /Pikachu/i};
const mod = { type: 'Electric' };

Model.findOneAndUpdate(query, mod, {}, (err, data) => {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterado: ', data);
})

// Output Terminal
Alterado:  { value: null, ok: 1 }
```
##### Exemplo 2
```js
const query = { attack: {$lte: 10}};
const mod = {attack: 50};
const options = {multi: true};

Model.findOneAndUpdate(query, mod, options, (err, data) => {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterado: ', data);
});

//Output Terminal
Alterado:  { value: null, ok: 1 }
```
##### Exemplo 3
```js
const query = {$and: [{type: 'fire'}, {attack: {$gte: 5}}]};
const mod = {attack: 51, description: 'update attack'};
const options = {multi: true};

Model.findOneAndUpdate(query, mod, options, (err, data) => {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterado: ', data);
});

//Output Terminal
Alterado:  { value: null, ok: 1 }
```

#### findOneAndRemove
##### Exemplo 1
```js
const query = { attack: null };

Model.findOneAndRemove(query, (err, data) => {
  if (err) return console.log('Erro: ', err);
  return console.log('Removeu: ', data);
});

//Output
Removido:  { value: null, ok: 1 }
```

##### Exemplo 2
```js
const query = { $and: [{type: 'grass'}, {attack: {$gte: 100}}]};

Model.findOneAndRemove(query, (err, data) => {
  if (err) return console.log('Erro: ', err);
  return console.log('Removeu: ', data);
});

//Output
Removido:  { value: null, ok: 1 }
```

##### Exemplo 3
```js
const query = {attack: {$lte: 10}};

Model.findOneAndRemove(query, (err, data) => {
  if (err) return console.log('Erro: ', err);
  return console.log('Removeu: ', data);
});

//Output
Removido:  { value: null, ok: 1 }
```

#### 4 - Crie 1 Schema com todo CRUD funcional e métodos especiais, que agrupe:
> virtuals, getters & setters, method & static, embedded, document ,plugins, middlewares.

<br>
> Primeiramente criei um json com o nome de 5 bruxos da série Harry Potter e os importei para o banco `be-mean-instagram`:
```json
{name: {first: 'Harry', last: 'Potter'}, age: 36, type: 'Student'}
{name: {first: 'Hermione', last: 'Granger'}, age: 36, type: 'Student'}
{name: {first: 'Ronald', last: 'Weasley'}, age: 37, type: 'Student'}
{name: {first: 'Luna', last: 'Lovegood'}, age: 35, type: 'Student'}
{name: {first: 'Alvus', last: 'Dumbledore'}, age: 135, type: 'Master'}
```

```js
// app.js
'use strict';

require('./db/config');

const CRUD = require('./controller');

const hab1 = {
  title: 'Abaffiato',
  since: 2005
};

const hab2 = {
  title: 'Liberacorpus',
  since: 1999
};

const hab3 = {
  title: 'Levicorpus',
  since: 2001
};

const data = {
  name: {
    first: 'Severo',
    last: 'Snape'
  },
  age: 38,
  skills: [hab1, hab2, hab3],
  type: 'Master'
};

CRUD.create(data);

// Search
const query = "575c32ed6cc3d63b8a6d5f0f";

CRUD.retrieve(query);

// Method Searc
const query = {name: {first: 'Harry', last: 'Potter'}, class: /Student/i};

CRUD.retrieve_method(query);

// Static Search
const query = {name: {first: 'Alvus', last: 'Dumbledore'}};

CRUD.retrieve_static(query);

// Pre-Count Middleware Search
const query = {age: 37};

CRUD.retrieve_middleware(query);
```

```js
// Controller.js
'use strict';

const mongoose = require('mongoose');

const Schema = require('./schema');
const Model = require('./model')(Schema, 'bruxos');

const CRUD = {
  create(data) {
    console.log("Criado: ", data);
    const bruxoModel = new Model(data);
    bruxoModel.save((err, data) => {
      if (err) return console.log('ERRO: ', err);
      return console.log('Inseriu:', data);
    });
  },
  retrieve(query) {
    Schema
      .virtual('name.full')
      .get(function (){
        return `${this.name.first} ${this.name.last}`;
      });

    Model.findById(query, (err, data) => {
      if (err) return console.log('ERRO: ', err);
      return console.log('Nome: ', data.name.full, '\nTipo de bruxo: ', data.weight_class, '\nIdade: ', data.age);
    });
  },
  retrieve_method(query) {
    Schema.methods.findSimilarType = function findSimilarType (callback) {
      return this.model('Categoria', Schema);
    };

    const MethodModel = mongoose.model('MethodModel', Schema);
    const outrobruxo = new MethodModel(query);

  },
  retrieve_static(query) {
    Schema.statics.search = function (name, callback) {
      return this.where('name', new RegExp(name, 'i')).exec(callback);
    };

    const StaticModel = mongoose.model('StaticModel', Schema);

  },
  retrieve_middleware(query) {
    const countQuery = Model.where(query).count((err, count) => {
      if (err) return console.log('ERRO: ', err);
      return console.log(`Existem ${count} bruxos com a mesma idade!`);
    });
  },
  update(query, mod, options) {
    var options = options || {};
    Model.update(query, mod, options, (err, data) => {
      if (err) return console.log('ERRO: ', err);
      return console.log('Alterado:', data);
    });
  },
  delete(query) {
    Model.remove(query, (err, data) => {
      if (err) return console.log('ERRO: ', err);
      return console.log('Deletado:', data);
    });
  },
};

module.exports = CRUD;
```
##### Resultado
```shell
[nodemon] starting `node app.js`
Criado:  { name: { first: 'Severo', last: 'Snape' },
  age: 38,
  skills:
   [ { title: 'Abaffiato', since: 2005 },
     { title: 'Liberacorpus', since: 1999 },
     { title: 'Levicorpus', since: 2001 } ],
  type: 'Master' }
Mongoose default connection connected to mongodb://localhost/be-mean-instagram
Mongoose default connection is open
Nome:  Harry Potter
Tipo de bruxo:  undefined
Idade:  36
Existem 2 bruxos com a mesma idade!
Inseriu: { name: { last: 'Snape', first: 'Severo' },
  skills:
   [ { _id: 575c488ae3ab5d5d172c4273,
       since: 2005,
       title: 'Abaffiato' },
     { _id: 575c488ae3ab5d5d172c4272,
       since: 1999,
       title: 'Liberacorpus' },
     { _id: 575c488ae3ab5d5d172c4271,
       since: 2001,
       title: 'Levicorpus' } ],
  created_at: Sat Jun 11 2016 17:21:14 GMT+0000 (UTC),
  _id: 575c488ae3ab5d5d172c4270,
  type: 'Master',
  age: 38,
  __v: 0 }
```

#### 5 - Crie 1 Schema para password com criptografia e arquitetura atômica.
> use SHA256 com SALT como criptografia;use middleware com pre-save;use methods.

> O código aqui iria ficar grande demais e sem querer, exclui um bocado de arquivos responsáveis pelo resultado. Então colocarei o resultado que compilou corretamente:
```shell
Senha Inserida: gabrielkalani
Senha Criptografada: e895d4f51ccd9b2aa46196b833346752d089bdb38fccd50a2ab1c0451ceee34e

repo_language: JavaScript

mime_type: text/plain


text:
# Node.js - Aula 08 - Exercício
**user:** [tuchedsf](https://github.com/tuchedsf)
**autor:** Diego Ferreira

## Exercícios Aula 8
### 1- Insira 5 pokemons novos, na coleção `pokemons`, escolha 3 e os adicione em um array e uma nova coleção chamada `meus-pokemons`, utilizando o `ObjectId`. Adicione o `required` em campos que ache obrigatório no *Schema* do Pokemon.

model.js
```js
'use strict';
module.exports = function(Schema, ModelName) {
  const mongoose = require('mongoose');
  return mongoose.model(ModelName, Schema);
}
```

Pokemon-schema.js
```js
'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const name = require('../fields/field-name');
const description = require('../fields/field-description');
const attack = require('../fields/field-attack');
const defense  = require('../fields/field-defense');
const height  = require('../fields/field-height');
const cor = require('../fields/field-cor');
const created_at = require('../fields/field-created_at');

const _schema = new Schema({
  name,
  description,
  attack,
  defense,
  height,
  cor,
  created_at
});

//exportando o schema para poder ser visivel
module.exports = exports = _schema;
```

####Fields (Abaixo apenas os fields com required true, para não ficar maior ainda o exercício)
field-cor.js
```js
module.exports = {type: String, enum: ['Red', 'Blue', 'Green'], required:true};
```

field-name.js
```js
module.exports = {type : String, required:true};
```

field-description.js
```js
module.exports =  {type: String,  minlength : 5, maxlength : 50, required: true};
```

Pokemon-controller.js
```js
'use strict';
const PokemonSchema = require('../model/schema/Pokemon-schema');
const Model = require('../model/model')(PokemonSchema,'Pokemon');
const error = require('../util/func_error');
const success = require('../util/func_success');

const CRUD = {
  create : function (data) {
    Model.create(data).then(success , error);
  },
  retrive : function (query) {
    let promise = Model.find(query).exec();
    promise.then(success, error);
  },
  update : function (query,mod, options) {
    options = options || {} //{ multi: true }
    let promise = Model.update(query,mod, options).exec();
    promise.then(success, error);
  },
  delete : function (query) {
      Model.remove(query).then(success , error);
  }
}

module.exports = CRUD;
```

app.js (Adicionando 5 pokemons)
```js
'use strict';
require('./db/config');
const CRUD = require('./controller/Pokemon-controller');
const array = [
{
    name : "Yoytubemon",
    description: "engana os pokes projetando videos na tela",
    attack: 95,
    defense : 20,
    height : 1,
    cor: "Red"
  },
  {
    name : "Gramosauro",
    description: "Pokemon especialista em grama",
    attack: 40,
    defense : 30,
    height : 0.4,
    cor: "Green"
  },
  {
    name : "SublimeTextmon",
    description: "Pokemon especialista em comandos sublime",
    attack: 82,
    defense : 60,
    height : 0.5,
    cor: "Blue"
  },
  {
    name : "BE-MEANMON",
    description: "Pokemon especialista fullstak MEAN",
    attack: 95,
    defense : 80,
    height : 3,
    cor: "Blue"
  },
  {
    name : "SublimeTextmon",
    description: "Pokemon especialista em grama",
    attack: 82,
    defense : 60,
    height : 0.5,
    cor: "Green"
  },
  {
    name: "Poke atom",
    description: "Pokemon criado no atom design",
    attack: 95,
    defense : 84,
    height : 2,
    cor: 'Blue'
  }
]
CRUD.create(array);
```

Saida Terminal
```
MacBook-Air-Diego:8mongoose-atomic diego$ node app.js
Mongo default connection connected to mongodb://localhost/be-mean-instagram
Mongo default connection open
[ { created_at: Sun May 15 2016 10:26:53 GMT-0300 (BRT),
    _id: 5738791dbf16d8dd3bf3d227,
    cor: 'Red',
    height: 1,
    defense: 20,
    attack: 95,
    description: 'engana os pokes projetando videos na tela',
    name: 'Yoytubemon',
    __v: 0 },
  { created_at: Sun May 15 2016 10:26:53 GMT-0300 (BRT),
    _id: 5738791dbf16d8dd3bf3d228,
    cor: 'Green',
    height: 0.4,
    defense: 30,
    attack: 40,
    description: 'Pokemon especialista em grama',
    name: 'Gramosauro',
    __v: 0 },
  { created_at: Sun May 15 2016 10:26:53 GMT-0300 (BRT),
    _id: 5738791dbf16d8dd3bf3d229,
    cor: 'Blue',
    height: 0.5,
    defense: 60,
    attack: 82,
    description: 'Pokemon especialista em comandos sublime',
    name: 'SublimeTextmon',
    __v: 0 },
  { created_at: Sun May 15 2016 10:26:53 GMT-0300 (BRT),
    _id: 5738791dbf16d8dd3bf3d22a,
    cor: 'Blue',
    height: 3,
    defense: 80,
    attack: 95,
    description: 'Pokemon especialista fullstak MEAN',
    name: 'BE-MEANMON',
    __v: 0 },
  { created_at: Sun May 15 2016 10:26:53 GMT-0300 (BRT),
    _id: 5738791dbf16d8dd3bf3d22b,
    cor: 'Green',
    height: 0.5,
    defense: 60,
    attack: 82,
    description: 'Pokemon especialista em grama',
    name: 'SublimeTextmon',
    __v: 0 },
  { created_at: Sun May 15 2016 10:26:53 GMT-0300 (BRT),
    _id: 5738791dbf16d8dd3bf3d22c,
    cor: 'Blue',
    height: 2,
    defense: 84,
    attack: 95,
    description: 'Pokemon criado no atom design',
    name: 'Poke atom',
    __v: 0 } ]
```

####Meus pokemons

Field-meusPokemons.js
```js
module.exports = (Schema) => {
  return [{type: Schema.Types.ObjectId, ref: 'Pokemons'}];
}
```

Meus-pokemons-schema.js
```js
'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const name = require('../fields/field-name');
const meusPokemons = require('../fields/field-meusPokemons')(Schema);
const created_at = require('../fields/field-created_at');

const _schema = new Schema({
  name,
  meusPokemons,
  created_at
});

module.exports = exports = _schema;
```

Meus-pokemons-controller.js
```js
'use strict';

const MeusPokemonSchema = require('../model/schema/Meus-pokemons-schema');
const Model = require('../model/model')(MeusPokemonSchema,'Meus-Pokemons');
const error = require('../util/func_error');
const success = require('../util/func_success');

const CRUD = {
  create : function (data) {
    Model.create(data).then(success , error);
  },
  retrive : function (query) {
    let promise = Model.find(query).exec();
    promise.then(success, error);
  },
  update : function (query,mod, options) {
    options = options || {} //{ multi: true }
    let promise = Model.update(query,mod, options).exec();
    promise.then(success, error);
  },
  delete : function (query) {
      Model.remove(query).then(success , error);
  }
}

module.exports = CRUD;
```

app.js (adicionando 3 pelo objectid)
```js
'use strict';
require('./db/config');
const CRUD = require('./controller/Pokemon-controller');
const MeusPokemonsController = require('./controller/Meus-pokemons-controller');
const data = {
  name: 'Minha Nova Coleção',
  meusPokemons: ['57294bdfb6de032bd6811516', '5738791dbf16d8dd3bf3d228','57256bacb6315cbdb70fc87e']
}
MeusPokemonsController.create(data);
```

Saida Terminal
```
MacBook-Air-Diego:8mongoose-atomic diego$ node app.js
Mongo default connection connected to mongodb://localhost/be-mean-instagram
Mongo default connection open
{ meusPokemons:
   [ 57294bdfb6de032bd6811516,
     5738791dbf16d8dd3bf3d228,
     57256bacb6315cbdb70fc87e ],
  created_at: Sun May 15 2016 10:47:46 GMT-0300 (BRT),
  _id: 57387e028922acff3c43ab3a,
  name: 'Minha Nova Coleção',
  __v: 0 }
```

### 2 - Crie um *Schema* de exemplo com validação para os campos (utilizar Arquitetura Atômica, ou seja cada campo sendo um Schema separado):
* email
* cpf
* cnpj
* url
* ip

field-email.js
```js
'use strict';

const _set = (v) => v.toLowerCase();

const _validate = (email) => /ˆ\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(email);

const _index = true;

const _field_email = {
  type: String,
  set: _set,
  trim: true,
  unique: true,
  index: _index,
  validate: [_validate, "E-mail inválido: ({VALUE}) - Padrão: xxx@xxx.com"],
  required : true
};

module.exports = _field_email;
```

field-cpf.js
```js
'use strict';
const _validate = (cpf) => /^[\d]{3}\.[\d]{3}\.[\d]{3}\-[\d]{2}$/.test(cpf);
const _index = true;

const field_cpf = {
  type: String,
  unique: true,
  index: _index,
  validate: [_validate, "CPF inválido: ({VALUE}) - Formato: 000.000.000-00"],
};

module.exports = field_cpf;
```

field-cnpj.js
```js
'use strict';
const _validate = (cnpj) => /^\d{2}\.\d{3}\.\d{3}\/\d{4}\-\d{2}$/.test(cnpj);
const _index = true;

const field_cnpj = {
  type: String,
  unique: true,
  index: _index,
  validate: [_validate, "CNPJ inválido: ({VALUE})"],
};

module.exports = field_cnpj;
```

field-url.js
```js
'use strict';
const _validate = (url) =>  /(http|ftp|https):\/\/[\w-]+(\.[\w-]+)+([\w.,@?^=%&:\/~+#-]*[\w@?^=%&\/~+#-])?/.test(url);

const field_url = {
  type: String,
  validate: [_validate, "Url inválida: ({VALUE}) - nao coleque http"],
};

module.exports = field_url;
```

field-ip.js
```js
'use strict';
const _validate = (ip) => /^(((1?[1-9]?|10|2[0-4])\d|25[0-5])($|\.(?!$))){4}$/.test(ip);

const field_ip = {
  type: String,
  validate: [_validate, "IP inválido: ({VALUE})"],
};

module.exports = field_ip;
```

schema.js
```js
'use strict';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const name = require('../fields/field-email');
const cpf = require('../fields/field-cpf');
const cnpj = require('../fields/field-cnpj');
const url  = require('../fields/field-url');
const ip  = require('../fields/field-ip');

const _schema = new Schema({
  email,
  cpf,
  cnpj,
  url,
  ip
});

module.exports = exports = _schema;
```

### 3 - Dê 3 exemplos **diferentes**, de cada, utilizando as funções:

####`findAndModify`
####`findOneAndUpdate`
####`findOneAndRemove`

Para este exercício fora utilizados os arquivos do exercício 1, pokemons, e re-colocados apenas os arquivos que foram modificados:

Os arquivos schema e controller abaixo foram alterados para contemplarem os 3 comandods:

Schema.js
```js
'use strict';

const mongoose = require('mongoose');
const util = require('util');
const Schema = mongoose.Schema;

const name = require('../fields/field-name');
const description = require('../fields/field-description');
const attack = require('../fields/field-attack');
const defense  = require('../fields/field-defense');
const height  = require('../fields/field-height');
const cor = require('../fields/field-cor');
const created_at = require('../fields/field-created_at');

const _schema = new Schema({
  name,
  description,
  attack,
  defense,
  height,
  cor,
  created_at
});

// findAndModify
_schema.statics.findAndModify = function (query, sort, doc, options, callback) {
  return this.collection.findAndModify(query, sort, doc, options, callback);
};
//exportando o schema para poder ser visivel no controlador
module.exports = exports = _schema;
```

Pokemon-controller.js
```js
'use strict';

// aposntamento para o arquivo com a definição do schema
const PokemonSchema = require('../model/schema/Pokemon-schema');
//const pokemonModel = mongoose.model('pokemons', PokemonSchema);
// apontamento para o model e passando o schema como parametro.
const Model = require('../model/model')(PokemonSchema,'Pokemon');
const error = require('../util/func_error');
const success = require('../util/func_success');

const CRUD = {
  create : function (data) {
    Model.create(data).then(success , error);
  },
  retrive : function (query) {
    //const query = {$and :[{attack : 40}, {defense : 40}]}
    let promise = Model.find(query).exec();
    promise.then(success, error);
  },
  update : function (query,mod, options) {
    //const queryUpdate = {"_id" : "572f4a42cf4c206bfcaa7480"}
    //const mod = {name: "Poke Promise", defense : 50}
    options = options || {} //{ multi: true }
    let promise = Model.update(query,mod, options).exec();
    promise.then(success, error);
  },
  delete : function (query) {
          //const queryDelete = {"_id" : "572f4a42cf4c206bfcaa7480"}

      Model.remove(query).then(success , error);
  },
  findAndModify: function(query, mod, opt){
    Model.findAndModify(query, [], mod, opt, function (err, data) {
    if (err) return console.log('Erro: ', err);
      return console.log('Alterou: ', data);
    });
  },
  findOneAndUpdate: function (query, mod, opt) {
      Model.findOneAndUpdate(query, mod, opt, function (err, data) {
      if (err) return console.log('Erro: ', err);
      return console.log('Alterou: ', data);
    });
  },
  findOneAndRemove: function (query) {
    Model.findOneAndRemove(query, function (err, data) {
    if (err) return console.log('Erro: ', err);
      return console.log('Removeu: ', data);
    });
  }
}

module.exports = CRUD;
```

- findAndModify
FindAndModify fiz com base no stackoverflow pois pela documentação do mongoose (http://mongoosejs.com/docs/api.html#query-js) este comando é feito atraves do findOneAndUpdate, findOneAndRemove, etc...

app.js - com chamada ao método
```js
'use strict';

//apontamento arquivo configuracao banco de dados.
require('./db/config');
const CRUD = require('./controller/Pokemon-controller');
const MeusPokemonsController = require('./controller/Meus-pokemons-controller');

const query = {defense : 30}
const mod = {defense: 32};
const opt = {multi: true};
CRUD.findAndModify(query,mod,opt);

const query = {defense : 32}
const mod = {defense: 30};
const opt = {multi: true};
CRUD.findAndModify(query,mod,opt);
```

Resultado
```
MacBook-Air-Diego:8mongoose-atomic diego$ node app.js
Mongo default connection connected to mongodb://localhost/be-mean-instagram
Mongo default connection open
Alterou:  { lastErrorObject: { updatedExisting: true, n: 1 },
  value:
   { _id: 57256bacb6315cbdb70fc87e,
     name: 'Tuchemon',
     description: 'Tartaruga tuche',
     type: 'reptile',
     defense: 30,
     height: 20,
     attack: 0,
     __v: 0 },
  ok: 1 }

MacBook-Air-Diego:8mongoose-atomic diego$ node app.js
Mongo default connection connected to mongodb://localhost/be-mean-instagram
Mongo default connection open
Alterou:  { lastErrorObject: { updatedExisting: true, n: 1 },
  value: { _id: 57256bacb6315cbdb70fc87e, defense: 32 },
  ok: 1 }
```

- findOneAndUpdate

app.js
```js
'use strict';

//apontamento arquivo configuracao banco de dados.
require('./db/config');
const CRUD = require('./controller/Pokemon-controller');
const MeusPokemonsController = require('./controller/Meus-pokemons-controller');

console.log("Exemplo 1");
const query = {name: /SublimeTextmon/i};
const mod = {cor: 'Red'};
const opt = {};
CRUD.findOneAndUpdate(query,mod,opt);

console.log("Exemplo 2");
const query2 = {$and : [{name: /SublimeTextmon/i}, {cor : 'Green'}]};
const mod2 = {cor: 'Red'};
CRUD.findOneAndUpdate(query2,mod2,opt);

console.log("Exemplo 3");
const query3 = {height: {$lt : 2} };
const mod3 = {height: 2.05};
CRUD.findOneAndUpdate(query3,mod3,opt);
```

Resultado
```
MacBook-Air-Diego:8mongoose-atomic diego$ node app.js
Exemplo 1
Exemplo 2
Exemplo 3
Mongo default connection connected to mongodb://localhost/be-mean-instagram
Mongo default connection open
Alterou:  { created_at: Sun May 15 2016 10:26:53 GMT-0300 (BRT),
  __v: 0,
  cor: 'Blue',
  height: 0.5,
  defense: 60,
  attack: 82,
  description: 'Pokemon especialista em comandos sublime',
  name: 'SublimeTextmon',
  _id: 5738791dbf16d8dd3bf3d229 }
Alterou:   { created_at: Sun May 15 2016 10:26:53 GMT-0300 (BRT),
    __v: 0,
    cor: 'Green',
    height: 0.5,
    defense: 60,
    attack: 82,
    description: 'Pokemon especialista em grama',
    name: 'SublimeTextmon',
    _id: 5738791dbf16d8dd3bf3d22b }
Alterou:  { created_at: Tue May 03 2016 22:09:51 GMT-0300 (BRT),
  __v: 0,
  cor: 'Green',
  height: 0.4,
  defense: 30,
  attack: 40,
  description: 'Pokemon especialista em grama',
  name: 'Gramosauro',
  _id: 57294bdfb6de032bd6811516 }
```

- findOneAndRemove

app.js
```js
'use strict';

//apontamento arquivo configuracao banco de dados.
require('./db/config');
const CRUD = require('./controller/Pokemon-controller');
const MeusPokemonsController = require('./controller/Meus-pokemons-controller');

console.log("Exemplo 1");
const query = {name: /SublimeTextmon/i};
CRUD.findOneAndRemove(query);

console.log("Exemplo 2");
const query2 = {cor : 'Green'};
CRUD.findOneAndRemove(query2);

console.log("Exemplo 3");
const query3 = {height: {$lt : 2} };
CRUD.findOneAndRemove(query3);
```

Resultado
```
MacBook-Air-Diego:8mongoose-atomic diego$ node app.js
Exemplo 1
Exemplo 2
Exemplo 3
Mongo default connection connected to mongodb://localhost/be-mean-instagram
Mongo default connection open
Removeu:  { created_at: Sun May 15 2016 10:26:53 GMT-0300 (BRT),
  __v: 0,
  cor: 'Red',
  height: 0.5,
  defense: 60,
  attack: 82,
  description: 'Pokemon especialista em comandos sublime',
  name: 'SublimeTextmon',
  _id: 5738791dbf16d8dd3bf3d229 }
Removeu:  { created_at: Tue May 03 2016 22:09:51 GMT-0300 (BRT),
  __v: 0,
  cor: 'Green',
  height: 2.05,
  defense: 30,
  attack: 40,
  description: 'Pokemon especialista em grama',
  name: 'Gramosauro',
  _id: 57294bdfb6de032bd6811516 }
Removeu:  { created_at: Wed May 18 2016 11:16:39 GMT-0300 (BRT),
  cor: 'Blue',
  height: 0.7,
  defense: 40,
  attack: 40,
  description: 'Pokemon que altera inserindo',
  name: 'Nerdmon',
  _id: 57294fe05daea5abb1cb1c4f }
```

### 4 - Crie 1 *Schema* com todo CRUD funcional e métodos especiais, que agrupe:
* virtuals;
* getters & setters;
* method & static;
* embedded document;
* plugins;
* middlewares.

config
```js
//importar o mongoose
const mongoose = require('mongoose');
const uriDB = 'mongodb://localhost/lista-livros';
//criar uma conexão com mongo
mongoose.connect(uriDB);

mongoose.connection.on('connected', function(){
 console.log("Mongo default connection connected to " + uriDB);
});

mongoose.connection.on('error', function(err){
 console.log("Mongo default connection error" + err);
});

mongoose.connection.on('disconnected', function(){
 console.log("Mongo default connection disconnected");
});

mongoose.connection.on('open', function(){
 console.log("Mongo default connection open");
});

process.on('SIGINT',function(){
    mongoose.connection.close(function(){
       console.log("The connection is closed");
        process.exit(0);
    });
});
```

actions
```js
//action-create.js
'use strict';
const callback = require('./callback');
module.exports = (Model, obj, coment) => {
  let livro = new Model(obj);
  if (coment.titulo != null) {
    livro.comentariosLeitores.push( coment );
  }
  Model.create(livro, callback);
}

//action-find.js
'use strict';
const callback = require('./callback');
module.exports = (Model, query) => {
  Model.find(query, callback);
}

//action-findOne.js
'use strict';
const callback = require('./callback');
module.exports = (Model, query) => {
  Model.findOne(query, callback);
};

//action-findTituloCompleto.js
'use strict';
module.exports = (Model, id) => {
  Model.findById(id, (err,data) => {
    if (err) console.log(err);
    console.log("Titulo Completo: " + data.titulo.completo);
  });
};

//action-remove.js
'use strict';
const callback = require('./callback');
module.exports = (Model, query) => {
  Model.remove(query, callback);
};

//action-update.js
'use strict';
const callback = require('./callback');
module.exports = (Model, query, mod, options) => {
  Model.update(query,mod,options, callback);
}

//callback.js
'use strict';
module.exports = (err, data) => {
  if (err) console.log('Erro:', err);
  else console.log('RETORNOU:', data);
};
```

quarks
```js
//quark-matchNumber.js
module.exports = /\d/g;

//quark-toLowerCase.js
'use strict';
module.exports = (v) => v.toLowerCase();

//quark-toUpperCase.js
'use strict';
module.exports = (v) => v.toUpperCase();

//quark-val-GT0.js
'use strict';
module.exports = {
  validator: (v) => v >= 0
  , message: '{VALUE} precisa ser maior que 0'
};

//quark-val-numberGTE0LTE10.js
'use strict';
module.exports = {
  validator: (v) => v >= 0 || v <= 10
  , message: '{VALUE} precisa ser maior igual a 0 ou menor igual a 10!'
};

//quark-val-strLenghtGTE2.js
'use strict';
module.exports = {
  validator: (v) => v.length >= 2
  , message: '{VALUE} precisa ser maior que 2 caracteres'
};
```

atoms
```js
//atom-autor.js
'use strict';
const Atom_autor = {
  type: String
, validate: require('./../quarks/quark-val-srtLenghtGTE2')
, required: true
, index: true
}
module.exports = Atom_autor;

//atom-created_at.js
'use strict';
const Atom_created_at = {
  type: Date
, default: Date.now
}
module.exports = Atom_created_at;

//atom-genero.js
'use strict';
const Atom_genero = {
  type: String
, set: require('./../quarks/quark-toLowerCase')
, validate: require('./../quarks/quark-val-srtLenghtGTE2')
, required: true
, index: true
}
module.exports = Atom_genero;

//atom-notaLivro.js
'use strict';
const Atom_notaLivro = {
  type: Number
, default: 0
, required : true
, validate: require('./../quarks/quark-val-numberGTE0LTE10')
, match: require('./../quarks/quark-matchNumber')
}
module.exports = Atom_notaLivro;

//atom-paginas.js
'use strict';
const Atom_paginas = {
  type: Number
, default: 0
, required : true
, validate: require('./../quarks/quark-val-GT0')
, match: require('./../quarks/quark-matchNumber')
}
module.exports = Atom_paginas;

//atom-resumo.js
'use strict';
const Atom_resumo = {
  type: String
}
module.exports = Atom_resumo;

//atom-subtitulo.js
'use strict';
const Atom_subtitulo = {
  type: String
, set: require('./../quarks/quark-toUpperCase')
}
module.exports = Atom_subtitulo;

//atom-titulo.js
'use strict';
const Atom_titulo = {
  type: String
, set: require('./../quarks/quark-toUpperCase')
, validate: require('./../quarks/quark-val-srtLenghtGTE2')
, required: true
, index: true
}
module.exports = Atom_titulo;

//atom-tituloComent.js
'use strict';
const Atom_tituloComent = {
  type: String
, validate: require('./../quarks/quark-val-srtLenghtGTE2')
, required: true
}
module.exports = Atom_tituloComent;
```

molecules
```js
//molecule-comentario.js
'use strict';
const mongoose = require('mongoose');
const _schema = {
  titulo: require('../atoms/atom-tituloComent'),
  autor: require('../atoms/atom-autor'),
  notaLivro: require('../atoms/atom-paginas'),
  comentario: require('../atoms/atom-resumo'),
  created_at: require('../atoms/atom-created_at')
};
const comentSchema = new mongoose.Schema(_schema);
module.exports = comentSchema;

//molecule-livro.js
'use strict';
const mongoose = require('mongoose');
const comentariosSchema = require('../molecules/molecule-comentarios');
const lastMod = require('../plugins/plugin-lastModif');
const _schema = {
  livro: {
    titulo: require('../atoms/atom-titulo'),
    subtitulo: require('../atoms/atom-subtitulo'),
  },
  autor: require('../atoms/atom-autor'),
  paginas: require('../atoms/atom-paginas'),
  genero: require('../atoms/atom-genero'),
  comentariosLeitores: [comentariosSchema],
  created_at: require('../atoms/atom-created_at')
};

const livroSchema = new mongoose.Schema(  _schema
  , { toObject: { virtuals: true }
  , toJSON: { virtuals: true }
});

//virtual
livroSchema.virtual('titulo.completo').get(function () {
  return  this.livro.titulo + ' - ' + this.livro.subtitulo;
});

//static
livroSchema.statics.findByAutor = function (username, callback) {
  this.find({ autor: new RegExp(username, 'i') }, callback);
};

//method
livroSchema.methods.findSimilarGenero = function (callback) {
  return this.model('Livros').find({ genero: this.genero }, callback);
}

//plugin e middleware
livroSchema.plugin(lastMod);
module.exports = livroSchema;
```

plugins
```js
//plugin-lastModif.js
module.exports = exports = function lastModifiedPlugin (schema, options) {
  schema.add({lastMod: Date })
  schema.pre('save', function (next) {
    this.lastMod = new Date
    next()
  })
  if (options && options.index) {
    schema.path('lastMod').index(options.index)
  }
}
```

organisms
```js
//model.js
'use strict';
module.exports = function(Schema, ModelName) {
  const mongoose = require('mongoose');
  return mongoose.model(ModelName, Schema);
}

//organism-livros.js
'use strict';
const livrosSchema = require('../molecules/molecule-livro');
const livrosModel = require('./model')(livrosSchema,'Livros');
const CRUD = {
  create: (obj, coment) => {
    require('../actions/action-create')(livrosModel, obj, coment);
  }
  , find: (query) => {
    require('../actions/action-find')(livrosModel, query);
  }
  , findOne: (query) => {
    require('../actions/action-findOne')(livrosModel, query);
  }
  , update: (query, mod, options) => {
    require('../actions/action-update')(livrosModel, query, mod, options);
  }
  , remove: (query) => {
    require('../actions/action-remove')(livrosModel, query);
  }
  , findTituloCompletoByIdLivro: (id) => {
    require('../actions/action-findTituloCompletoByIdLivro')(livrosModel, id);
  }
  , findByAutor: (autor) => {
    livrosModel.findByAutor(autor,require('../actions/callback'));
  }
  , findSimilarGenero: (genero) => {
    var livroBusca = new livrosModel(genero);
    livroBusca.findSimilarGenero(require('../actions/callback'));
  }
};

module.exports = CRUD;
```
app.js
```js
require('./config/db-config');

const livroModel = require('./organisms/organism-livros');
const comentarioModel = require('./organisms/organism-comentarios');

const comentario = { titulo: "Muito bom",autor: "Diego Ferreira",notaLivro: 10,
  comentario: "Livro muito bom com ele temos uma noção e sensação de estar presente na 2 guerra"
};

const livro1 = { livro: { titulo: "Os Bebês de Auschwitz" , subtitulo: ""} , autor: "Holden, Wendy" , paginas: 368 , genero: 'Biografias e Memórias'};

const livro2 = { livro: { titulo: "A Elite da Tropa" , subtitulo: "" }, autor: "Luiz Eduardo Soares" , paginas: 230 , genero: 'Policial'};
const livro3 = { livro: { titulo: "A Elite da Tropa 2" , subtitulo: "O inimigo agora é outro" }, autor: "Luiz Eduardo Soares" , paginas: 200 , genero: 'Policial'};
const livro4 = { livro: { titulo: "O fim da pobresa" , subtitulo: "Como acabar com a miséria mundial nos próximos 20 anos" }, autor: "Jeffrey D. Sachs" , paginas: 450 , genero: 'Auto-Ajuda'};
const livro5 = { livro: { titulo: "O Menino Maluquinho" , subtitulo: "De panela na cabeça" }, autor: "Ziraldo" , paginas: 50 , genero: 'infantil'};

livroModel.create(livro1, comentario);

livroModel.find({paginas: {$gt : 300}});

livroModel.findOne({autor: "Holden, Wendy"});

const query = {paginas: {$lt : 100}};
const mod = { paginas : 62};
const options = {};
livroModel.update(query,mod,options);

const optionsMultiTrue = {multi : true};
livroModel.update(query,mod,optionsMultiTrue);

const queryRemove = {paginas : 62};
livroModel.remove(queryRemove);

//virtuals findTituloCompletoByIdLivro
livroModel.findTituloCompletoByIdLivro('5743a14679a2a7405d96c6f2');

//statics findByAutor
livroModel.findByAutor('Holden');

//method findSimilarGenero
livroModel.findSimilarGenero({genero: 'Policial'});
```

Resultado:
```
//executei apenas 1 create pela funcionalidade o restante foi import banco
////livroModel.create(livro1, comentario);
RETORNOU: { livro: { titulo: 'OS BEBÊS DE AUSCHWITZ', subtitulo: '' },
  paginas: 368,
  comentariosLeitores:
   [ { notaLivro: 10,
       created_at: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
       _id: 5743a09f450c7f1f5d01f1c0,
       comentario: 'Livro muito bom com ele temos uma noção e sensação de estar presente na 2 guerra',
       autor: 'Diego Ferreira',
       titulo: 'Muito bom',
       id: '5743a09f450c7f1f5d01f1c0' } ],
  created_at: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
  autor: 'Holden, Wendy',
  genero: 'biografias e memórias',
  _id: 5743a09f450c7f1f5d01f1bf,
  lastMod: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
  __v: 0,
  titulo: { completo: 'OS BEBÊS DE AUSCHWITZ - ' },
  id: '5743a09f450c7f1f5d01f1bf' }

//livroModel.find({paginas: {$gt : 300}});
RETORNOU: [ { livro: { titulo: 'OS BEBÊS DE AUSCHWITZ', subtitulo: '' },
    paginas: 368,
    comentariosLeitores: [ [Object] ],
    created_at: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
    __v: 0,
    autor: 'Holden, Wendy',
    genero: 'biografias e memórias',
    lastMod: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
    _id: 5743a09f450c7f1f5d01f1bf,
    titulo: { completo: 'OS BEBÊS DE AUSCHWITZ - ' },
    id: '5743a09f450c7f1f5d01f1bf' },
  { livro:
     { titulo: 'O FIM DA POBRESA',
       subtitulo: 'COMO ACABAR COM A MISÉRIA MUNDIAL NOS PRÓXIMOS 20 ANOS' },
    paginas: 450,
    comentariosLeitores: [],
    created_at: Mon May 23 2016 21:33:10 GMT-0300 (BRT),
    __v: 0,
    autor: 'Jeffrey D. Sachs',
    genero: 'auto-ajuda',
    lastMod: Mon May 23 2016 21:33:10 GMT-0300 (BRT),
    _id: 5743a14679a2a7405d96c6f2,
    titulo: { completo: 'O FIM DA POBRESA - COMO ACABAR COM A MISÉRIA MUNDIAL NOS PRÓXIMOS 20 ANOS' },
    id: '5743a14679a2a7405d96c6f2' } ]

//livroModel.findOne({autor: "Holden, Wendy"});
RETORNOU: { livro: { titulo: 'OS BEBÊS DE AUSCHWITZ', subtitulo: '' },
  paginas: 368,
  comentariosLeitores:
   [ { notaLivro: 10,
       created_at: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
       _id: 5743a09f450c7f1f5d01f1c0,
       comentario: 'Livro muito bom com ele temos uma noção e sensação de estar presente na 2 guerra',
       autor: 'Diego Ferreira',
       titulo: 'Muito bom',
       id: '5743a09f450c7f1f5d01f1c0' } ],
  created_at: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
  __v: 0,
  autor: 'Holden, Wendy',
  genero: 'biografias e memórias',
  lastMod: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
  _id: 5743a09f450c7f1f5d01f1bf,
  titulo: { completo: 'OS BEBÊS DE AUSCHWITZ - ' },
  id: '5743a09f450c7f1f5d01f1bf' }

//livroModel.update(query,mod,options);
RETORNOU: { ok: 1, nModified: 1, n: 1 }

//livroModel.remove(queryRemove);
RETORNOU: { result: { ok: 1, n: 1 }, ... }

//livroModel.findTituloCompletoByIdLivro('5742336aed21ba1f5119e7e3');
Titulo Completo: O FIM DA POBRESA - COMO ACABAR COM A MISÉRIA MUNDIAL NOS PRÓXIMOS 20 ANOS

//livroModel.findByAutor('Holden');
RETORNOU: [ { livro: { titulo: 'OS BEBÊS DE AUSCHWITZ', subtitulo: '' },
    paginas: 368,
    comentariosLeitores: [ [Object] ],
    created_at: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
    __v: 0,
    autor: 'Holden, Wendy',
    genero: 'biografias e memórias',
    lastMod: Mon May 23 2016 21:30:23 GMT-0300 (BRT),
    _id: 5743a09f450c7f1f5d01f1bf,
    titulo: { completo: 'OS BEBÊS DE AUSCHWITZ - ' },
    id: '5743a09f450c7f1f5d01f1bf' } ]

//livroModel.findSimilarGenero({genero: 'policial'});
RETORNOU: [ { livro:
     { titulo: 'A ELITE DA TROPA 2',
       subtitulo: 'O INIMIGO AGORA É OUTRO' },
    paginas: 200,
    comentariosLeitores: [],
    created_at: Mon May 23 2016 21:33:10 GMT-0300 (BRT),
    __v: 0,
    autor: 'Luiz Eduardo Soares',
    genero: 'policial',
    lastMod: Mon May 23 2016 21:33:10 GMT-0300 (BRT),
    _id: 5743a14679a2a7405d96c6f1,
    titulo: { completo: 'A ELITE DA TROPA 2 - O INIMIGO AGORA É OUTRO' },
    id: '5743a14679a2a7405d96c6f1' },
  { livro: { titulo: 'A ELITE DA TROPA', subtitulo: '' },
    paginas: 230,
    comentariosLeitores: [],
    created_at: Mon May 23 2016 21:33:10 GMT-0300 (BRT),
    __v: 0,
    autor: 'Luiz Eduardo Soares',
    genero: 'policial',
    lastMod: Mon May 23 2016 21:33:10 GMT-0300 (BRT),
    _id: 5743a14679a2a7405d96c6f0,
    titulo: { completo: 'A ELITE DA TROPA - ' },
    id: '5743a14679a2a7405d96c6f0' } ]

```

### 5 - Crie 1 *Schema* para `password` com criptografia e arquitetura atômica.
* use SHA256 com SALT como criptografia;
* use middleware com pre save;
* use methods.

field-created_at.js
```js
module.exports = { type: Date, default: Date.now };
```

field-password.js
```js
'use strict';

const _validate = (v) => v.length >= 6 && v.length <= 10;

const _field_password = {
  type: String,
  validate: [ _validate, "Senha precisa ser maior que 6 e menor que 10 caracteres"],
  require: true
};

module.exports = _field_password;
```

password-schema.js
```js
'use strict';

const mongoose = require('mongoose');
const crypto = require('crypto');

const password = require('../fields/field-password');
const created_at = require('../fields/field-created_at');

const _schema = {
  password,
  created_at
};

const PasswordSchema = new mongoose.Schema(_schema);

PasswordSchema.methods.cryptoPassword = function (){
  const key = crypto.pbkdf2Sync(this.password, 'salt', 100000, 256, 'sha256');
  return key.toString('hex');
};

PasswordSchema.pre('save', function( next ){
      console.log("sem criptografia:" + `${this.password}` );
      this.password = this.cryptoPassword();
      next();
   });

module.exports = PasswordSchema;
```

password-model.js
```js
'use strict';

const passwordSchema = require('../model/schemas/Password-schema');
const PasswordModel = require('../model/model')(passwordSchema,'Senha');

const save = (obj) => {
  const novoObj = new PasswordModel(obj);
  novoObj.save( (err,data) => {
      if (err) console.log(err);
        console.log("Password salvo com sucesso: " + data);
      });
};

const CRUD = {
  save
};

module.exports = CRUD;
```

password-controller.js
```js
'use strict';
const passwordModel = require('../model/password_model');

const CRUD = {
  save : passwordModel.save
}

module.exports = CRUD;
```

app-passoword.js
```js
'use strict';

require('./db/config');

const passwordController = require('./controller/password_controller');

const obj = { password : "tuchedsf"};

passwordController.save(obj);
```

Resultado:
```
MacBook-Air-Diego:08mongoose-user diego$ node app-password.js
sem criptografia: tuchedsf
Mongo default connection connected to mongodb://localhost/mongoose-user-test
Mongo default connection open
Password salvo com sucesso: { created_at: Thu May 19 2016 21:41:49 GMT-0300 (BRT),
  _id: 573e5d4dbf94a3841dd8c76b,
  password: '1a0dc0b6d40feedafb73d9ca48b18ecd7c343efd8f73cd71f9b933bd8f5cfcbb4dffb86dfdd2c47329bd1e788a02bf3885c64dd231be4a326196002affc9a5a77e79606f6a56cbb6a8ad87dcabf52573da0f99f8c49a7509cd39901e320ebcc3461552793be5c63804a9cac7a37403e63721a94ce48c5199511599b6a7c56237ad2d03ace2470cab725a27fa789b374ce0287ceb44d52a84fde6733b09ceaf751e6fe48fee668fdcaa9fdf6ce89c92f5648609487d05b5643b1a63883df0cdf06063c04274461c86c1be97fa1ac4abecb26474e8c07d1046b81915dfe7bf139f52e59a053b80889543fbefd08ce300bb42916bf6d179b4e885347da1395e372c',
  __v: 0 }
```


text:
# Node.js - Aula 08 - Exercício

**user:** [FranciscoValerio](https://github.com/FranciscoValerio)

**autor:** Francisco Henrique Ruiz Valério

> Exercícios Aula 08 parte 02 (plugins).

1 - Crie um novo schema como nome de User, e use o plugin timestemp nele, esse schema deve ter email e password.

**Schema User criado:**
```JS
'use strict';

const mongoose = require( 'mongoose' );
const Schema = mongoose.Schema;
const timestemp = require( './plugins/timestemp' );

const userSchema = new Schema({
   email: { type: String, required: true},
   password: { type: String, required: true}
});

userSchema.plugin( timestemp );

module.exports = mongoose.model( 'User', userSchema );
```

2 - Crie um novo plugin que adicione o um novo campo ao schema User, o novo campo de ser O Sexo do tipo String.

**Plugin field-sexo:**
```JS
'use strict';

function fieldsexo(schema, options){
   schema.add( { sexo: { type: String } } );

   function preUpdate(next){
      console.log('pre update has calling');
      next();
   }
   schema.pre('update', preUpdate);
}

module.exports = fieldsexo;
```

**Schema User:**
```JS
'use strict';

const mongoose = require( 'mongoose' );
const Schema = mongoose.Schema;
const timestemp = require( './plugins/timestemp' );
const fieldsexo = require( './plugins/field-sexo' );

const userSchema = new Schema({
   email: { type: String, required: true},
   password: { type: String, required: true}
});

userSchema.plugin( timestemp  );
userSchema.plugin( fieldsexo );

module.exports = mongoose.model( 'User', userSchema );
```

3 - Salve 5 novos Users com email, password e Sexo, vindo dos plugins.

**Fonte utilizado na gravação dos 5 usuários:**

```JS
'use strict';

const model = require( './user' );
const mongoose = require( 'mongoose' );

mongoose.connect( 'mongodb://localhost/test' );

var user1 = {
   email: 'francisco@bla.com',
   password: '1234',
   sexo: 'M'
};

var user2 = {
   email: 'henrique@ble.com',
   password: '45678',
   sexo: 'M'
};

var user3 = {
   email: 'jujuzita@jju.com',
   password: 'jujuzita',
   sexo: 'F'
};

var user4 = {
   email: 'adebair@abc.com',
   password: 'hhhsss',
   sexo: 'M'
};

var user5 = {
   email: 'musum@biritis.com',
   password: 'cacildis',
   sexo: 'M'
};

model.create(user1, createPost );
model.create(user2, createPost );
model.create(user3, createPost );
model.create(user4, createPost );
model.create(user5, createPost );

function createPost(err, post ){
   if ( err ) throw err;
   else {
      console.log(post);
      process.exit(1);
   }
}
```

```
   {
      _id: 56cd003d418f55fc1d01df83,
      created_at: Tue Feb 23 2016 21:58:37 GMT-0300 (Hora oficial do Brasil),
      update_at: Tue Feb 23 2016 21:58:37 GMT-0300 (Hora oficial do Brasil),
      sexo: 'M',
      password: '1234',
      email: 'francisco@bla.com',
      __v: 0
   }

   {
      _id: 56cd011d4850b19410396fa9,
      created_at: Tue Feb 23 2016 22:02:21 GMT-0300 (Hora oficial do Brasil),
      update_at: Tue Feb 23 2016 22:02:21 GMT-0300 (Hora oficial do Brasil),
      sexo: 'M',
      password: '45678',
      email: 'henrique@ble.com',
      __v: 0
   }

   {
      _id: 56cd015d181995241519e3df,
      created_at: Tue Feb 23 2016 22:03:25 GMT-0300 (Hora oficial do Brasil),
      update_at: Tue Feb 23 2016 22:03:25 GMT-0300 (Hora oficial do Brasil),
      sexo: 'F',
      password: 'jujuzita@jju.com',
      email: 'Juju',
      __v: 0
   }

   {
      _id: 56cd0181dd5101bc01bde118,
      created_at: Tue Feb 23 2016 22:04:01 GMT-0300 (Hora oficial do Brasil),
      update_at: Tue Feb 23 2016 22:04:01 GMT-0300 (Hora oficial do Brasil),
      sexo: 'M',
      password: 'hhhsss',
      email: 'adebair@abc.com',
      __v: 0
   }

   {
      _id: 56cd019fde9639f808c62d71,
      created_at: Tue Feb 23 2016 22:04:31 GMT-0300 (Hora oficial do Brasil),
      update_at: Tue Feb 23 2016 22:04:31 GMT-0300 (Hora oficial do Brasil),
      sexo: 'M',
      password: 'cacildis',
      email: 'musum@biritis.com',
      __v: 0
   }

```

> Exercícios Aula 08 parte 04 (Middleware).

1 - Criar um middleware pre com um erro e propagar o erro para quem o chamou, salvar o result e botar no md deste exercício.

**Arquivo contendo o schema de pokemon e o error no pre:**
```JS
const mongoose = require( 'mongoose' );
const util = require( 'util' );
const Schema = mongoose.Schema;
const ObjectId = Schema.ObjectId;
const log = require( './log' );

mongoose.connect( 'mongodb://localhost/be-mean-instagram' );

function pokemonHandler(){
   const schema = new Schema({
      id : ObjectId,
      name: { type: String, trin: true },
      type: { type: String, trin: true },
      attack: { type: Number },
      defense: { type: Number },
      height: { type: Number },
      description: { type: String, trin: true }
   });

   schema.pre('save', next => {
      util.log('before save');
      const err = new Error( 'same error');
      next(err);
   });

   return mongoose.model('Pokemon', schema);
}

module.exports = exports = pokemonHandler();
```

**Arquivo que chama o schema do pokemon:**
```JS
'use strict';

const Pokemon = require( './pokemon-error' );
const pokemon = new Pokemon( {
   name: 'Francisco',
   description: 'great',
   type: 'fisher',
   attack: 70,
   defense: 99,
   height: 1.85
} );

pokemon.save(err => {
   if (err){
      console.log(err);
   }
});
```

**result:**
```
23 Feb 22:23:58 - before save
[Error: same error]
```

2 - Adicione o módulo de log, aos dois schemas do blog, e faça com que gere um arquivo blog-models.md com pelo menos 6 operações no blog save() ou delete().

**users-schema.js**
```JS
'use strict';

const mongoose = require( 'mongoose' );
const Schema = mongoose.Schema;
const Post = require( './posts-schema' );
const util = require( 'util' );
const log = require( './log' );

let userSchema = new Schema({
   name: { type: String, required: true },
   email: { type: String, required: true, uniq: true }
});

module.exports = mongoose.model('User', userSchema );

userSchema.post('remove', user =>{
   Post.remove({ _user: user._id}.postsRemoved );
});

function postsRemoved(err, ok){
   util.log(err || ok.result);
}

userSchema.post('save', user =>{
   log( 'user', `${user.name} email: ${user.email} saved.` );
   console.log(user);
});

```

**posts-schema.js**
```JS
'use strict';

const mongoose = require( 'mongoose' );
const Schema = mongoose.Schema;
const log = require( './log' );

let postsSchema = new Schema({
   title: String,
   body: String,
   userEmail: String,
   _user: {
      type: Schema.ObjectId,
      ref: 'User'
   }
});

module.exports = mongoose.model('Post', postsSchema );

postsSchema.post('save', post =>{
   log( 'post', `${post.title} user: ${post.userEmail} saved.` );
   console.log(post);
});

```

**Arquivo blog-models.md**
```
user: Francisco email: francisco@bla.com saved.: Wed Feb 24 2016 19:09:14 GMT-0300 (Hora oficial do Brasil)
user: Henrique email: henrique@ble.com saved.: Wed Feb 24 2016 19:10:03 GMT-0300 (Hora oficial do Brasil)
post: Mongoose Middleware user: francisco@bla.com saved.: Wed Feb 24 2016 19:14:51 GMT-0300 (Hora oficial do Brasil)
post: Class 08 user: henrique@ble.com saved.: Wed Feb 24 2016 19:17:45 GMT-0300 (Hora oficial do Brasil)
post: Node.Js user: henrique@ble.com saved.: Wed Feb 24 2016 19:18:21 GMT-0300 (Hora oficial do Brasil)
post: Node.Js user: henrique@ble.com saved.: Wed Feb 24 2016 19:18:37 GMT-0300 (Hora oficial do Brasil)
post: MongoDB user: francisco@bla.com saved.: Wed Feb 24 2016 19:19:04 GMT-0300 (Hora oficial do Brasil)
post: Express user: francisco@bla.com saved.: Wed Feb 24 2016 19:19:25 GMT-0300 (Hora oficial do Brasil)
```

3 - Crie um middleware usando o exemplo do blog, para gera um log com nome titulo dos post que foi buscado "dica use o find".

**Arquivo find-blog.js**
```JS
'use strict';

const Post = require( './posts-schema' );

Post.find({userEmail: 'francisco@bla.com'}, { _id: 0, body: 0, __v: 0, _user: 0 }, () =>{});
```

**Resultado do LOG**
```
buscado:
   {
      title: 'Mongoose Middleware',
      userEmail: 'francisco@bla.com'
   },
   {
      title: 'Mongoose Middleware',
      userEmail: 'francisco@bla.com'
   },
   {
      title: 'Mongoose Middleware',
      userEmail: 'francisco@bla.com'
   },
   {
      title: 'Mongoose Middleware',
      userEmail: 'francisco@bla.com'
   },
   {
      title: 'Mongoose Middleware',
      userEmail: 'francisco@bla.com'
   },
   {
      title: 'Mongoose Middleware',
      userEmail: 'francisco@bla.com'
   },
   {
      title: 'Mongoose Middleware',
      userEmail: 'francisco@bla.com'
   },
   {
      title: 'MongoDB',
      userEmail: 'francisco@bla.com'
   },
   {
      title: 'Express',
      userEmail: 'francisco@bla.com'
   }
   : Wed Feb 24 2016 20:11:17 GMT-0300 (Hora oficial do Brasil)
```

> Exercícios Aula 08 final (Geral).

1. Insira 5 pokemons novos, na coleção pokemons, escolha 3 e os adicione em um array e uma nova coleção chamada meus-pokemons, utilizando o ObjectId. Adicione o required em campos que ache obrigatório no Schema do Pokemon.

**Arquivo para inserir os pokemons**
```JS
var Mongoose = require('Mongoose');

var db = Mongoose.connection;

db.on('error', console.error);
db.once('open', function() {
  console.log('Conectado.')

  var pokemonSchema = new Mongoose.Schema({
     name: { type: String, required: true },
     attack: { type: Number, required: true },
     created_at: { type: Date, default: Date.now },
     active: Boolean,
     hasCreditCookie: Boolean
  });

  pokemonSchema.statics.findAllWithCreditCookies = function(callback) {
    return this.find({ hasCreditCookie: true }, callback);
  };

  var Pokemon = Mongoose.model('Pokemon', pokemonSchema);

  for (var i =1; i<=5; i++){
    var pokemon = new Pokemon({
      name: 'Pokemon ' + i,
      attack: 22 + i,
      hasCreditCookie: true
    });

    pokemon.save(function(err, pokemon) {
      if (err) return console.error(err);
      console.log('Salvo: ' + pokemon);
    });
  }
});

Mongoose.connect('mongodb://localhost/be-mean-instagram');
```

**Pokemons novos**
```
Salvo: { created_at: Sun Feb 28 2016 17:32:52 GMT-0300 (Hora oficial do Brasil),
  _id: 56d35974f1819cb01c8eae28,
  hasCreditCookie: true,
  attack: 23,
  name: 'Pokemon 1',
  __v: 0 }
Salvo: { created_at: Sun Feb 28 2016 17:32:52 GMT-0300 (Hora oficial do Brasil),
  _id: 56d35974f1819cb01c8eae29,
  hasCreditCookie: true,
  attack: 24,
  name: 'Pokemon 2',
  __v: 0 }
Salvo: { created_at: Sun Feb 28 2016 17:32:52 GMT-0300 (Hora oficial do Brasil),
  _id: 56d35974f1819cb01c8eae2a,
  hasCreditCookie: true,
  attack: 25,
  name: 'Pokemon 3',
  __v: 0 }
Salvo: { created_at: Sun Feb 28 2016 17:32:52 GMT-0300 (Hora oficial do Brasil),
  _id: 56d35974f1819cb01c8eae2b,
  hasCreditCookie: true,
  attack: 26,
  name: 'Pokemon 4',
  __v: 0 }
Salvo: { created_at: Sun Feb 28 2016 17:32:52 GMT-0300 (Hora oficial do Brasil),
  _id: 56d35974f1819cb01c8eae2c,
  hasCreditCookie: true,
  attack: 27,
  name: 'Pokemon 5',
  __v: 0 }
```

**Arquivo de schema**
```JS
const mongoose = require('mongoose');
const Schema = mongoose.Schema;

const _schema = {
  pokemons:  [{type: Schema.Types.ObjectId, ref: 'pokemons'}]
}

const pokemonSchema = new Schema(_schema);

const data = {
  pokemons: ['56d35974f1819cb01c8eae28', '56d35974f1819cb01c8eae29', '56d35974f1819cb01c8eae2a']
};

const Model = mongoose.model('meuspokemons', pokemonSchema);
const poke = new Model(data);
poke.save(function (err, data) {
  if (err) return console.log('ERRO: ', err);
  console.log('Inseriu: ', data)
})

module.exports = pokemonSchema;
```

**Arquivo app.js**
```JS
require('./config');

const Schema = require('./schema');
```

**Pokemons inseridos**
```
Inseriu:  { pokemons:
   [ 56d35974f1819cb01c8eae28,
     56d35974f1819cb01c8eae29,
     56d35974f1819cb01c8eae2a ],
  _id: 56d35cd54031a9cc132e50a0,
  __v: 0 }
```

2. Crie um Schema de exemplo com validação para os campos (utilizar Arquitetura Atômica, ou seja cada campo sendo um Schema separado):
    - email
    - cpf
    - cnpj
    - url
    - ip

**Arquivo schema.js**
```JS
const mongoose = require('mongoose');
const Schema   = mongoose.Schema;

const email = require( './fields/field-email' );
const cpf   = require( './fields/field-cpf' );
const cnpj  = require( './fields/fields-cnpj' );
const url   = require( './fields/fields-url' );
const ip    = require( './fields/fields-ip' );

const _schema = {
   email,
   cpf,
   cnpj,
   url,
   ip
};

module.exports = new Schema( _schema );
```

**Arquivo field-email**
```JS
const _set = (v) => v.toLowerCase();
const _validate = (v) => /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,3})+$/.test(v);

const Field = {
  type: String
, set: _set
, validate: [_validate, 'Email ({VALUE}) inválido!']
, required: true
}

module.exports = Field;
```

**Arquivo field-cpf**
```JS
const _validate = (v) => /^(\d{3}\.?\d{3}\.?\d{3}\-?\d{2})$/.test(v);

const Field = {
  type: String
, validate: [_validate, 'CPF ({VALUE}) inválido!']
, required: true
}

module.exports = Field;
```

**Arquivo field-cnpj**
```JS
const _validate = (v) => /^(\d{2}\.?\d{3}\.?\d{3}\/?\d{4}-?\d{2})$/.test(v);

const Field = {
  type: String
, validate: [_validate, 'CNPJ ({VALUE}) inválido!']
, required: true
}

module.exports = Field;
```

**Arquivo field-url**
```JS
const _validate = (v) => /^((http|https|ftp|ftps):\/\/)?([a-z0-9\-]+\.)?[a-z0-9\-]+\.[a-z0-9]{2,4}(\.[a-z0-9]{2,4})?(\/.*)?$/.test(v);

const Field = {
  type: String
, validate: [_validate, 'URL ({VALUE}) inválida!']
, required: true
}

module.exports = Field;
```

**Arquivo field-ip**
```JS
const _validate = (v) => /^(((1[0-9]|[1-9]?)[0-9]|2([0-4][0-9]|5[0-5]))\.){3}((1[0-9]|[1-9]?)[0-9]|2([0-4][0-9]|5[0-5]))$/.test(v);

const Field = {
  type: String
, validate: [_validate, 'IP ({VALUE}) inválido!']
, required: true
}

module.exports = Field;
```

3. Dê 3 exemplos diferentes, para cada, utilizando as funções:
   - findAndModify
   - findOneAndUpdate
   - findOneAndRemove

**Schema e model utilizados**
```JS
'use strict';

const mongoose = require('mongoose');
const db = 'mongodb://localhost/be-mean-instagram';

mongoose.connect( db );

const Schema = mongoose.Schema;
const _schema = {
  name: { type: String, required: true, match: /^./i },
  description: { type: String, required: true, match: /^./i},
  type: { type: String, required: true, match: /^./i},
  attack: { type: Number, min: 1}
};

const PokemonSchema = new Schema(_schema);

const Model = mongoose.model('pokemons', PokemonSchema);
```

**findAndModify**
```JS
'use strict';

const mongoose = require( 'mongoose' );
const db = 'mongodb://localhost/be-mean-instagram';

mongoose.connect( db );

const Schema = mongoose.Schema;

const _schema = {
  name: {type: String, match: /^./i},
  description: {type: String, match: /^./i},
  type: {type: String, match: /^./i},
  attack: {type: Number, min: 1}
};

const PokemonSchema = new Schema(_schema);

PokemonSchema.statics.findAndModify = function (query, sort, doc, options, callback) {
  return this.collection.findAndModify(query, sort, doc, options, callback);
};

const Model = mongoose.model('pokemons', PokemonSchema);

const query  = { attack: {$lte: 5 } };
const mod = { type: 'eletric' };
const options = { multi: true };

Model.findAndModify(query, [], mod, options, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterou: ', data);
});
```

```
Resultado:

$ node findAndModify.js
Alterou:  { lastErrorObject: { updatedExisting: true, n: 1 },
  value:
   { _id: 56aeb9d04bb106b114749436,
     name: 'Pokemon 3',
     description: 'pokemon 3',
     type: 'eletric',
     attack: 51
     __v: 0 },
  ok: 1 }
```

**findOneAndUpdate**

*Exemplo 1*
```JS
use 'strict';

const query = { name: /pokemon/i};
const mod = { type: 'grass' };

Model.findOneAndUpdate(query, mod, {}, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterou: ', data);
})
```
```
Resultado:

Alterou:  { __v: 0,
  attack: 105,
  type: 'teste',
  description: 'pokemon 3',
  name: 'Pokemon 3',
  _id: 56aeb9d04bb106b114749436 }
```

*Exemplo 2*

```JS
'use strict';

const query = { attack: {$lte: 10}};
const mod = {attack: 56};
const options = {multi: true};

Model.findOneAndUpdate(query, mod, options, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterou: ', data);
});
```

```
Resultado:

Alterou:  { active: false,
  attack: 9,
  type: 'fire',
  description: 'dronzitoo',
  name: 'Charmander',
  _id: 56a9539b4b828d9c186b53d5 }
```

*Exemplo 3*
```JS
'use strict';

const query = {$and: [{type: 'fire'}, {attack: {$gte: 5}}]};
const mod = {attack: 51, description: 'update attack'};
const options = {multi: true};

Model.findOneAndUpdate(query, mod, options, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Alterou: ', data);
});
```

```
Resultado:

Alterou:  { __v: 0,
  defense: 888,
  attack: 56,
  type: 'fire',
  description: 'update attack',
  name: 'Charmander',
  _id: 56a9539b4b828d9c186b53d5 }
```

**findOneAndRemove**

*Exemplo 1*

```JS
'use strict';

const query = { $and: [{type: 'grass'}, {attack: {$gte: 100}}]};

Model.findOneAndRemove(query, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Removeu: ', data);
});
```
```
Resultado:

Removeu:  { __v: 0,
  attack: 105,
  type: 'grass',
  description: 'pokemon 3',
  name: 'Pokemon 3',
  _id: 56aeb9d04bb106b114749436 }
```

*Exemplo 2*

```JS
'use strict';

const query = { attack: null };

Model.findOneAndRemove(query, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Removeu: ', data);
});
```
```
Resultado:

Removeu:  { description: 'Fracomon',
  defense: null,
  attack: null,
  name: 'fracote',
  _id: 564de099fc7e5880d64a877e }
```

*Exemplo 3*

```JS
'use strict';

const query = {attack: {$lte: 10}};

Model.findOneAndRemove(query, function (err, data) {
  if (err) return console.log('Erro: ', err);
  return console.log('Removeu: ', data);
});
```

```
Resultado:

Removeu:  { active: false,
  attack: 56,
  type: 'fire',
  description: 'update attack',
  name: 'Charmander',
  _id: 56a9539b4b828d9c186b53d5 }
```

4. Crie 1 Schema com todo CRUD funcional e métodos especiais, que agrupe:
   - virtuals
   - getters & setters
   - method & static
   - embedded document
   - plugins
   - middlewares

**Repositorio:** [Exercicio04](https://github.com/FranciscoValerio/exercicio04-nodejs-aula08)
Devido a esse exercício ter ficado muito grande eu achei melhor criar um repositório para ele.

5. Crie 1 Schema para password com criptografia e arquitetura atômica.
   - use SHA256 com SALT como criptografia;
   - use middleware com pre save;
   - use methods.

**Schema.js**
```JS
'use strict';

const mongoose = require( 'mongoose' );
const Schema   = mongoose.Schema;

function passwordHandler(){
   const passwordSchema = new Schema({
      password: require('./fields/field-password')
   });

   passwordSchema.methods.criptografa = function () {
      const crypto = require('crypto');
      const salt = crypto.randomBytes(128).toString('base64');
      const key = crypto.pbkdf2Sync( this.password, 'salt', 100000, 256, 'sha256');

      return key.toString('hex');
   };

   passwordSchema.pre('save', true, function( next, done ){
      console.log("inserir:", `${this.password}` );
      this.password = this.criptografa()
      console.log("criptografado:", `${this.password}` );
      next();
   });

   return mongoose.model('Password', passwordSchema);
}
module.exports = exports = passwordHandler();
```

**create-password.js**
```JS
'use strict';

const mongoose = require( 'mongoose' );
const models   = require( './index' );

mongoose.connect( 'mongodb://localhost/test' );

let pass = new models.Password();

pass.password = "abc@123";
pass.save();
```

**Resultado:**
```
inserir: abc@123
criptografado: 43efb96f67348450f85afadc9d6c740a3a62f1b9aaa63c08bc6c67f0f5e12f2e44ec2feba91183811df1ede96c53e3e0b2f6f7d19d946f7071e421e5d961e9791896747a4745179c86e59d370b559790e26b1b00a6be22fab2b5cf37da830b6f02586ea63d01105b195717f6de46249463b186ddb8b02f84d9c4aa47ed60a958544795314e0ce7999f58f92d23a2fa46abee6ea77036d223b8cb10dc637e4ad7eba0156ba4b57f9b4a62e0fd26434dec141469ff8b6605cd756a25a0470e30d6c63376e23b77ff18cef80d07ec5605235aea5e35a28c63ff5a3b60e531ab63a198548798e7e9f071a2c3c170dd817b550c1060ca7896ef072f67ae3072ab31b9
```


text:
# NodeJS - Aula 05 - Exercício
**Autor**: Igor luíz
**Github**: [Halfeld](https://github.com/Halfeld)
**Data**: 1457856832082

## Instale algum gerenciador de versão do Node.js e instale a versão mais atual como padrão, enquando escrevo é a versão 5.4.0.

#### Instalando `nvm`
```sh
➜  ~ curl https://raw.githubusercontent.com/creationix/nvm/v0.23.2/install.sh | bash
```

#### Instalando versão mais atual do node
```sh
[root@fedora Igor]# nvm install 5.7.1
######################################################################## 100.0%
Checksums empty
Now using node v5.7.1
```

## Inicie um projeto novo para essa aula com `npm` e instale, salvando no package.json.
- 1 Dependência local
```sh
➜  ~ npm install --save commander
```
- 1 Dependência local de desenvolvimento
```sh
➜  ~ npm install --save-dev chai
```
- 1 Dependência local opcional
```sh
➜  ~ npm install --optional chalk
```

## Crie e execute um script, via `npm`, que mostre uma mensagem no console com a global, que possui caminho para o diretório atual.

#### Package.json
```js
{
  "name": "npm",
  "version": "1.0.0",
  "description": "",
  "main": "dirname.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dirname": "node dirname.js"
  },
  "author": "",
  "license": "ISC"
}

```

#### Arquivo dirname.js
```js
console.log(__dirname);
```

#### Saída no console
```sh
(λ.npm.git:(master) ✗) ➜  npm run dirname

> npm@1.0.0 dirname /home/Igor/Documents/Be MEAN/NodeJS/Exercícios/be-mean-instagram-nodejs/exercises/practice/class-5/npm
> node dirname.js

/home/Igor/Documents/Be MEAN/NodeJS/Exercícios/be-mean-instagram-nodejs/exercises/practice/class-5/npm
```

## Cite 5 globais do Node.js e pelo menos 1 exemplo de cada.

#### Atraza a execução da função no tempo escolhido
```js
setTimeout(function() {
    console.log('Um teste com setTimeout');
}, 1000);
```

#### Retira o atraza do setTimeout
```js
clearTimeout(myfunction);
```

#### Retorna o diretório atual
```js
console.log(__dirname);
```

#### Chama a função de sempre que passar o tempo definido
```js
setInterval(function() {
    console.log('Um teste com setInterval');
}, 1000);
```

#### Retira o intervalo de  setInterval
```js
clearInterval(myfunction);
```
## Explique como funciona e de um exemplo de `process`.

É um instancia de EventEmitter, geralmente usamos para capturar eventos disparando callbacks ou não

```js
process.platform // Retorna a plataforma usada 'darwin', 'freebsd', 'linux', 'sunos' ou 'win32'
```

---
---
---

## Criar um arquivo
```js
var fs = require('fs');

fs.writeFile('./content.txt', 'Hello people', 'utf-8', function(err){
  if(err) throw err;
  console.log('OK');
});
```

## Ler um arquivo
```js
var fs = require('fs');

fs.readFile('./content.txt', 'utf-8', function(err, data){
  if(err) throw err;
  console.log(data);
});
```

## Editar conteúdo desse arquivo
```js

var fs = require('fs');

fs.readFile('./content.txt', 'utf-8', function(err, data){
  if(err) throw err;
  console.log('content: ', data); // Hello people

  fs.writeFile('./pokemons.txt', 'Hello gays', {encoding: 'utf8', flag: 'a+'}, function(err){
    if(err) throw err;
    console.log('OK');
  });
});
```

## Deletar arquivo
```js
var fs = require('fs');

fs.unlink('./trash.txt', function(err){
  if(err) throw err;
  console.log('OK');
});
```

## Renomear arquivo
```js
var fs = require('fs');

fs.rename('old.js', 'new.js', function(err){
  if(err) throw err;
  console.log('OK');
});

```

## Desafio: Criar um servidor web de arquivos estáticos: .css, .html, .js e etc...

#### Codigo

```js

'use strict';

const http = require('http'),
      path = require('path'),
      fs   = require('fs');

const index = fs.readFileSync(path.join(__dirname, 'index.html'));

const server = http.createServer((req, res) => {

    let _path = req.url,
        _contentType;

    if(_path == '/') {
        _path = '/index.html';
    }

    fs.readFile(__dirname + _path, (err, data) => {
        if(err) throw err;

        switch(path.extname(_path)) {
            case '.js':
                _contentType = "text/javascript";
                break;
            case '.css':
                _contentType = "text/css";
                break;
            case '.png':
                _contentType = "text/png";
                break;
            default:
                _contentType = "text/html";
        }
        res.writeHead(200, {"Content-Type": _contentType});
        res.end(data);
    });

    console.log(req.url);

});

server.listen(4000, () => console.log('Servidor no ar!'));
```

#### screen shots

![index](https://raw.githubusercontent.com/Halfeld/be-mean-instagram-nodejs/master/exercises/theory/files/index.png)
---
![css](https://raw.githubusercontent.com/Halfeld/be-mean-instagram-nodejs/master/exercises/theory/files/css.png)
---
![javascript](https://raw.githubusercontent.com/Halfeld/be-mean-instagram-nodejs/master/exercises/theory/files/js.png)
---
![not found](https://raw.githubusercontent.com/Halfeld/be-mean-instagram-nodejs/master/exercises/theory/files/not-found.png)


text:
# Node.js - Aula 05 - Exercício
**user:** [hc3](https://github.com/hc3)
**autor:** Eliel das Virgens
**date:** 20/03/2016

#01.Instale algum gerenciador de versão do Node.js e instale a versão mais atual como padrão, enquanto escrevo é a versão 5.4.0
```
hc3@darkSide:~/Bemean/Repositorios/be-mean-instagram-nodejs-exercises/class-05$ nvm ls
       v0.11.16
         v5.6.0
->       system
stable -> 5.6 (-> v5.6.0) (default)
unstable -> 0.11 (-> v0.11.16) (default)
hc3@darkSide:~/Bemean/Repositorios/be-mean-instagram-nodejs-exercises/class-05$ nvm use 5.6
Now using node v5.6.0
hc3@darkSide:~/Bemean/Repositorios/be-mean-instagram-nodejs-exercises/class-05$ node -v
v5.6.0

```

#02.Inicie um projeto novo para essa aula, com o npm e instale, salvando no package.json
```
hc3@darkSide:~/Bemean/Repositorios/estoqueMEAN$ npm init
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sane defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
name: (estoqueMEAN) estoqueMEAN
version: (0.0.0) 0.0.1
description: sistema para controlar estoque e servicos
entry point: (index.js) app.js
test command:
git repository: (https://github.com/hc3/estoqueMEAN.git)
keywords: MEAN , javascript , Webschool
author: hc3
license: (BSD-2-Clause) hc3PL
About to write to /home/hc3/Bemean/Repositorios/estoqueMEAN/package.json:

{
  "name": "estoqueMEAN",
  "version": "0.0.1",
  "description": "sistema para controlar estoque e servicos",
  "main": "app.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "repository": {
    "type": "git",
    "url": "https://github.com/hc3/estoqueMEAN.git"
  },
  "keywords": [
    "MEAN",
    "javascript",
    "Webschool"
  ],
  "author": "hc3",
  "license": "hc3PL",
  "bugs": {
    "url": "https://github.com/hc3/estoqueMEAN/issues"
  }
}

Is this ok? (yes) yes

```

#03.Crie e execute um script, via npm, que mostre uma mensagem no console com a global, que possui caminho para o diretório atual.
lets go make smoke
lgms.js
```
console.log('lets go make smoke!',__dirname);
```
run
```
node lgms.js
lets go make smoke! /home/hc3/Bemean/Repositorios/estoqueMEAN
```

#04.Cite 5 globais do Node.js e pelo menos 1 exemplo de cada.
__dirname mostra o diretório atual
```
console.log(__dirname)
/home/hc3/Bemean/Repositorios/estoqueMEAN
```

setTimeout()
chama um callback após um tempo
```
function shows(){
    console.log("HAHAHA");
}

setTimeout(shows, 3000);

node ex4.js
HAHAHA
```

clearTimeout()
utilizada para cronometro
```
function clareando(){
   console.log( "HAHAHA 2 SEG STOP");
}

var t = setTimeout(clareando, 5000);

clearTimeout(t);
```

process

```
process.on('exit', (code) => {
  console.log('fim processo ', code);
});

fim processo 0
```