gist

2012年4月10日火曜日

Node.js「脱」初心者!CoffeeBoxでブログの作り方を学ぼう!

CoffeeBoxは、Node.js、CoffeeScript、MondoDbで作成されたシンプルなブログアプリです。

そこそこ本格的で「学べる」コードだと思いますので、インストールから触りだけ読んでみたいと思います。

UIもキレイです。

インストールして起動まで一気に。

$ git clone https://github.com/qiao/coffee-box
$ cd coffee-box/
$ npm install
$ mongod --dbpath ~/MongoDb (MongoDBを起動しておきます)
$ node index.js
coffee-box server listening on port 3000 in development mode


http://localhost:3000 にアクセスします。

Login画面。OpenIDを使えます。

新規投稿です。

投稿完了。

ダッシュボード。

コードを少し見てみます。

ファイル構成です。

index.js
server.coffee
lib
├── markdown.coffee
├── marked.js
├── require_dir.coffee
└── taglist.coffee
public/
├── 404.html
├── favicon.ico
└── images
    ├── CREDIT
    └── fabric_1.png
scripts/
└── populate.coffee
config/
├── config.coffee
├── routes.coffee
└── site.json
app
├── assets
│   ├── javascripts
│   │   ├── application.coffee
│   │   ├── bootstrap-tabs.js
│   │   ├── comments.coffee
│   │   ├── jquery.elastic.js
│   │   ├── jquery.js
│   │   ├── jquery.textarea.js
│   │   ├── preview.coffee
│   │   ├── session.coffee
│   │   └── site.coffee
│   └── stylesheets
│       ├── application.styl
│       ├── bootstrap.css
│       ├── comments.styl
│       ├── dashboard.styl
│       ├── posts.styl
│       ├── pygments.css
│       ├── session.styl
│       └── site.styl
├── controllers
│   ├── comments_controller.coffee
│   ├── dashboard_controller.coffee
│   ├── posts_controller.coffee
│   └── session_controller.coffee
├── helpers
│   ├── comments_helper.coffee
│   └── posts_helper.coffee
├── models
│   ├── comment.coffee
│   └── post.coffee
└── views
    ├── comments
    │   ├── _comment.jade
    │   ├── _comments.jade
    │   ├── _form.jade
    │   └── _spam.jade
    ├── dashboard
    │   ├── _comments.jade
    │   ├── _posts.jade
    │   └── index.jade
    ├── layouts
    │   ├── _footer.jade
    │   ├── _header.jade
    │   └── layout.jade
    ├── posts
    │   ├── _form.jade
    │   ├── edit.jade
    │   ├── index.jade
    │   ├── new.jade
    │   └── show.jade
    └── session
        └── new.jade

MVCにわかれています。ViewテンプレートにはJadeを使っています。スタイルシートはStylus。

次に依存パッケージを見ていきます。

coffee-box@0.0.6 /Users/inouetomoyuki/Projects/node/coffee-box
├── async@0.1.18 
├── coffee-script@1.2.0 
├─┬ connect-assets@2.1.9 
│ ├── connect-file-cache@0.2.4 
│ ├── mime@1.2.2 
│ ├─┬ snockets@1.3.4 
│ │ ├── dep-graph@1.0.1 
│ │ └── uglify-js@1.0.7 
│ └── underscore@1.1.7 
├─┬ express@2.5.8 
│ ├─┬ connect@1.8.6 
│ │ └── formidable@1.0.9 
│ ├── mime@1.2.4 
│ ├── mkdirp@0.3.0 
│ └── qs@0.4.2 
├── express-messages@0.0.2 
├── gravatar@1.0.6 
├─┬ jade@0.22.1 
│ ├── commander@0.5.2 
│ └── mkdirp@0.3.0 
├── moment@1.5.1 
├─┬ mongoose@2.5.13 
│ ├── hooks@0.2.0 
│ └─┬ mongodb@0.9.9-7 
│   └── bson@0.0.4 
├── openid@0.4.1 
├─┬ pygments@0.1.2 
│ └── underscore@1.3.1 
├─┬ rss@0.0.3 
│ ├── logging@2.0.16 
│ └── xml@0.0.7 
├─┬ stylus@0.25.0 
│ ├── cssom@0.2.3 
│ ├── debug@0.6.0 
│ └── mkdirp@0.3.1 
├── uglify-js@1.2.6 
└── validator@0.4.5 

Expressが使われています。MongoDBのODMにはMongooseです。エラー表示やステータス表示にexpress-message。connect-assetsで、Rails Asset PipelineっぽくCSSやらJSやらまとめているようです。

server.coffeeを見てみます。

express = require 'express'
app = module.exports = express.createServer()

require('./config/config') app 
require('./config/routes') app 

app.listen 3000
console.log "coffee-box server listening on port #{app.address().port} " +
  "in #{app.settings.env} mode"

Expressを使っています。ConfigとRouterがあるみたい。

ルータを見ていきます。

config/routes.coffee(一部)

module.exports = (app) ->

  # get controllers from app settings
  controllersGetter    = app.settings.controllersGetter
  PostsController      = controllersGetter.getPostsController     app
  CommentsController   = controllersGetter.getCommentsController  app
  SessionController    = controllersGetter.getSessionController   app
  DashboardController  = controllersGetter.getDashboardController app

  # middleware for finding all posts published as individual pages
  findPages = PostsController.findPages
  # middleware for requiring login
  requireLogin = SessionController.requireLogin

  app.get '/'                    , findPages                , PostsController.index

  app.get  '/posts.:format?'     , findPages                , PostsController.index
  app.get  POST_SHOW_PATTERN     , findPages                , PostsController.show
  app.get  '/posts/new.:format?' , requireLogin , findPages , PostsController.new
  app.get  POST_EDIT_PATTERN     , requireLogin , findPages , PostsController.edit
  app.post '/posts.:format?'     , requireLogin             , PostsController.create
  app.put  POST_SHOW_PATTERN     , requireLogin             , PostsController.update
  app.del  POST_SHOW_PATTERN     , requireLogin             , PostsController.destroy
  app.post '/posts/preview'                                 , PostsController.preview

Controllerを登録して、ルーティングを決めています。例えば app.get '/'だったら、PostControllerのindex関数にルーティングされていることがわかります。

Postコントローラを見ていきます。

post_controller.coffee(一部)

    index: (req, res, next) ->
      # check pagination param: /posts/?page=2
      pageNo = parseInt(req.query['page'], 10) or 1

      POSTS_PER_PAGE = 5
      Post.countPostPages POSTS_PER_PAGE, (err, totalPages) ->
        Post.getPostsOfPage pageNo, POSTS_PER_PAGE, (err, posts) ->
          return res.redirect '500' if err?
          res.render 'posts/index'
            posts:      posts
            pageNo:     pageNo
            totalPages: totalPages

Post.getPostsOfPage がモデルを呼び出しているところ。res.renderでViewのposts/index.jadeにデータを投げ込んでレンダリングしています。

Postモデルを見ていきます。

app/models/post.coffee

async    = require 'async'
mongoose = require 'mongoose'
Schema   = mongoose.Schema

{markdown} = require('../../lib/markdown')
{makeTagList} = require('../../lib/taglist')

{CommentSchema} = require('./comment')

PostSchema = new Schema
  title:
    type: String
    required: true
  content:
    type: String
  rawContent:
    type: String
  slug:
    type: String
    required: true
    unique: true
  comments:
    type: [CommentSchema]

(中略)

PostSchema.statics.getPostsOfPage = (pageNo, postsPerPage, callback) ->
  @countPostPages postsPerPage, (err, count) =>
    return callback err, null if err?
    query =
      public: true
      asPage: false
    options =
      skip:   (pageNo - 1) * postsPerPage
      limit:  postsPerPage
      sort:   [['createdAt', 'desc']]
    @find query, {}, options, callback

mongooseを使ってスキーマを作っています。getPostsOfPage メソッドで、MongoDbにクエリを投げています。

Postのindexビューを見ていきます。

app/views/posts/index.jade(一部)

// entry list
ul.unstyled#entry-list
  each post in posts
    li!= partial('show', { locals: { post: post } })

(略)

Postコントローラの、res.renderでPostモデルから取得したデータを postsに入れています。jadeでループさせて表示させています。個々のPostデータの表示には、パーシャルを使って show.jade で表示させています。

app/views/posts/show.jade

article.entry.post
  != messages()
  header.entry-header
    h2.entry-title
      a(href=postPath(post))= post.title
    .entry-meta
      .entry-day= postDay(post)
      .entry-month= postMonth(post)
  .entry-content
    != post.content 
        
  != partial('comments/comments', { post: post, comments: post.comments })

全体的な流れはこんな感じです。

CoffeeBoxのコードはMVCでの組み方がよくわかります。ブログではログインの方法やセッションの持たせ方、設定などの方法、Stylusでの組み方、Assetの使い方についても理解しやすいです。

まだ改善できるところもありますし、CoffeeBoxは、コールドリーディングにはちょうどいい素材かもしれません。これがすべてではありませんが、脱初心者を目指す方はぜひ。

0 件のコメント: