Subsecciones

Añadiendo Persistencia: Posts con MongoDB y Mongoose

Let’s add a dependency on mongoose to our project and freeze it at version 4.0.1. As always, run npm install to bring it in.
$ npm install --save mongoose
$ grep mongoose package.json 
    "mongoose": "~4.0.1"

Definiendo el Modelo y Probando que Funciona

Now we’ll create an initial test to just test mongoose out.

~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ cat test/post-test.coffee 
mongoose = require 'mongoose'
Post     = require '../models/Post'
chai     = require 'chai'
expect   = chai.expect

describe 'Post', ->
  before (done) ->
    mongoose.connect 'mongodb://localhost/coffeepress', ->
      Post.remove done
  it 'should create a new post', (done) ->
    post = new Post(title:'First!', body:'First post!')
    post.save ->
      Post.findOne _id: post._id, (err, retrievedPost) ->
        expect(retrievedPost.title).eql "First!"
        expect(retrievedPost.body).eql "First post!"
        done()
~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ gulp test
[11:05:16] Using gulpfile ~/local/src/javascript/expressjs/clase-express-coffee/gulpfile.js
[11:05:16] Starting 'mocha'...

module.js:340
    throw err;
          ^
Error: Cannot find module '../models/Post'

Now let’s implement our model:

$ mkdir models
$ cat models/Post.coffee 
mongoose = require 'mongoose'

Post = new mongoose.Schema(
  title: String
  body: String
)

module.exports = mongoose.model 'Post', Post

Si ejecutamos las pruebas obtenemos un fallo porque el servidor mongod no está arrancado:
$ gulp test
[11:34:22] Using gulpfile ~/local/src/javascript/expressjs/clase-express-coffee/gulpfile.js
[11:34:22] Starting 'mocha'...

  Post
    1) "before all" hook

  routes
    index
      ok: should display index with posts
    new post
      ok: should display the add post page

  2 passing (2s)
  1 failing

  1) Post "before all" hook:
     Error: timeout of 2000ms exceeded. Ensure the done() callback is being called in this test.
Será mejor extender el gulpfile un poco para controlar el estado del servidor mongod antes y después de las pruebas:
[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ cat gulpfile.coffee 
gulp = require('gulp')
shell = require('gulp-shell')
fs = require('fs')

# run coffee server via nodemon https://github.com/remy/nodemon
gulp.task 'default', ->
  gulp.src('').pipe shell( 'DEBUG=coffeepress:* nodemon bin/www.coffee' )

gulp.task 'test', [ 'mocha' ]

# run mocha
gulp.task 'mocha', ->
  gulp.src('')
    .pipe shell "mocha --compilers coffee:coffee-script/register --invert --grep 'feature' -R spec"

# run mongod server
gulp.task 'mongod', ->
  gulp.src('')
    .pipe shell([ 'mongod --config mongod.conf 2>1 > /usr/local/var/mongodb/salida &' ])

# kill mongod server
gulp.task 'killmongo', ->
    fs.readFile '/usr/local/var/mongodb/mongo.pid', 'utf8', (err, pid) ->
      return console.log(err) if (err)
      console.log("killing #{pid}")
      gulp.src('').pipe shell("kill #{pid}")

# show mongod PID
gulp.task 'ps', ->
  gulp.src('')
    .pipe shell( 'ps -fA | grep mongod')
Este es el fichero de configuración para mongod que estoy usando:
[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ cat mongod.conf 
# Store data in /usr/local/var/mongodb instead of the default /data/db
dbpath = /usr/local/var/mongodb

# Append logs to /usr/local/var/mongo.log
logpath = /usr/local/var/mongodb/mongo.log
logappend = true

# Save the PID of the daemon on that file
pidfilepath = /usr/local/var/mongodb/mongo.pid

# Only accept local connections
bind_ip = 127.0.0.1

Ahora podemos fácilmente comprobar si el servidor mongo está activo:

[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ gulp ps
[14:23:52] Using gulpfile ~/local/src/javascript/expressjs/clase-express-coffee/gulpfile.js
[14:23:52] Starting 'ps'...
  501 33200 33199   0  2:23PM ttys011    0:00.01 /bin/sh -c ps -fA | grep mongod
  501 33202 33200   0  2:23PM ttys011    0:00.00 grep mongod
[14:23:53] Finished 'ps' after 107 ms
Vemos que no. Lo arrancamos:
[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ gulp mongod
[14:23:59] Using gulpfile ~/local/src/javascript/expressjs/clase-express-coffee/gulpfile.js
[14:23:59] Starting 'mongod'...
[14:24:00] Finished 'mongod' after 37 ms
Comprobamos que efectivamente está corriendo:
[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ gulp ps
[14:24:11] Using gulpfile ~/local/src/javascript/expressjs/clase-express-coffee/gulpfile.js
[14:24:11] Starting 'ps'...
  501 33212     1   0  2:24PM ttys011    0:00.25 mongod --config mongod.conf
  501 33228 33227   0  2:24PM ttys011    0:00.00 /bin/sh -c ps -fA | grep mongod
  501 33230 33228   0  2:24PM ttys011    0:00.00 grep mongod
[14:24:11] Finished 'ps' after 82 ms
Ejecutamos las pruebas:
[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ gulp test
[14:24:21] Using gulpfile ~/local/src/javascript/expressjs/clase-express-coffee/gulpfile.js
[14:24:21] Starting 'mocha'...


  Post
    ok: should create a new post

  routes
    index
      ok: should display index with posts
    new post
      ok: should display the add post page

  3 passing (179ms)

[14:24:22] Finished 'mocha' after 1.4 s
[14:24:22] Starting 'test'...
[14:24:22] Finished 'test' after 17 microseg
Vemos que la prueba should create a new post pasa.

Si lo deseamos podemos parar el servidor mongod:

[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ gulp killmongo
[14:24:42] Using gulpfile ~/local/src/javascript/expressjs/clase-express-coffee/gulpfile.js
[14:24:42] Starting 'killmongo'...
[14:24:42] Finished 'killmongo' after 537 microseg
killing 33212
Comprobamos que - efectivamente - el proceso no existe:
[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ gulp ps
[14:24:45] Using gulpfile ~/local/src/javascript/expressjs/clase-express-coffee/gulpfile.js
[14:24:45] Starting 'ps'...
  501 33274 33270   0  2:24PM ttys011    0:00.00 /bin/sh -c ps -fA | grep mongod
  501 33276 33274   0  2:24PM ttys011    0:00.00 grep mongod
[14:24:46] Finished 'ps' after 89 ms

Usando la Base de Datos MongoDB en vez de un Array en Nuestro Ejemplo

Now let’s refit our routes to use the Post model instead of an in memory array.

En el fichero app.coffee cargamos la librería mongoose y conectamos con la base de datos:

...
bodyParser = require('body-parser')

mongoose = require "mongoose"
mongoose.connect 'mongodb://localhost/coffeepress'

routes = require('./routes/index')
...

Los mayores cambios los hacemos en las rutas:

[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ cat routes/index.coffee 
express = require('express')
Post = require '../models/Post'
debug = require('debug')('coffeepress:server')
util = require 'util'

module.exports =
  index: (req, res) ->
    Post.find {}, (err, posts) ->
      res.render "index",
        title: "My Blog"
        posts: posts
  newPost: (req, res) ->
    res.render('add_post', {title: "Write New Post"})
  addPost: (req, res) ->
    post = req.body
    new Post(post).save ->
      res.redirect '/'
  viewPost: (req, res) ->
    Post.findById req.params.id, (err, post) ->
      res.render 'post', post: post, title: post.title

Acabaremos mejorando un poco las pruebas:

[~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ cat test/routes-test.coffee 
chai = require 'chai'
expect = chai.expect
mongoose = require "mongoose"
Post     = require "../models/Post"

routes = require '../routes/index'

describe 'routes', ->
  req = 
    params: {}
    body: {}
  res = 
    redirect: (route) ->
      # do nothing
    render: (view, vars) -> 
      # do nothing
  before (done) ->
    mongoose.connect 'mongodb://localhost/coffeepress', ->
      Post.remove done

  describe 'index', ->
    it "should display index with posts", (done) ->
      res.render = (view, vars) -> # redefinimos render
        expect(view).to.be.equal 'index'
        expect(vars.title).to.be.equal 'My Blog'
        expect(vars.posts).deep.equal []
        done()
      routes.index(req, res)

  describe 'new post', ->
    it "should display the add post page", (done)->
      res.render = (view, vars) -> # redefinimos render
        expect(view).to.be.equal 'add_post'
        expect(vars.title).to.be.equal 'Write New Post'
        done()
      routes.newPost(req, res)
    it "should add a new post when posted to", (done) ->
      req.body = 
        title: "My Post!"
        body: "My wonderful post."

      routes.addPost req, redirect: (route) ->
        expect(route).eql "/"
        routes.index req, render: (view, vars) ->
          expect(view).equal "index"
          expect(vars.posts[0].title).eql 'My Post!'
          expect(vars.posts[0].body).eql "My wonderful post."
          done()
Cuando las ejecutamos tenemos:
~/javascript/expressjs/clase-express-coffee(preparapl20042015)]$ gulp test
[22:14:06] Using gulpfile ~/local/src/javascript/expressjs/clase-express-coffee/gulpfile.js
[22:14:06] Starting 'mocha'...


  Post
    ok: should create a new post

  routes
    index
      ok: should display index with posts
    new post
      ok: should display the add post page
      ok: should add a new post when posted to


  4 passing (165ms)

[22:14:07] Finished 'mocha' after 1.42 s
[22:14:07] Starting 'test'...
[22:14:07] Finished 'test' after 17 micros

Casiano Rodríguez León
2016-03-27