gist

2012年3月8日木曜日

Tower.jsの全体像を見てみるの巻。

Tower.jsは、Ruby on railsやCakePHPの人には、すぐに理解できるパッケージでありましょう。ドキュメントも途中のようですが、敷居は低そうです。

前回のエントリー「さらばExpressよ!Tower.jsを試すの巻」に続き、今回は Tower.jsの全体像を見て行きたいと思います。

Tower.jsでは膨大なパッケージが使われていますが、実際、重要そうなモジュールはこんな感じかな、と。

Tower.Applicationクラス

アプリケーションの定義クラス。各アプリはTower.Applicationを継承して設定してきます。

tower new hogehogeコマンドで自動的に生成されます。Expressと比較してコードが劇的に減っているのがわかります。そして美しい。

# config/application.coffee
class App extends Tower.Application
  @configure ->
    @use "favicon", Tower.publicPath + "/favicon.ico"
    @use "static",  Tower.publicPath, maxAge: Tower.publicCacheDuration
    @use "profiler" if Tower.env != "production"
    @use "logger"
    @use "query"
    @use "cookieParser", Tower.session.secret
    @use "session", Tower.session.key
    @use "bodyParser"
    @use "csrf"
    @use "methodOverride", "_method"
    @use Tower.Middleware.Agent
    @use Tower.Middleware.Location
    @use Tower.Middleware.Router

module.exports = global.App = App

Tower.Modelクラス

モデルを定義するクラス。各モデルはTower.Modelを継承して設定してきます。モデル同士の関連(belongsTo, hasMany)もココで定義します。ちょっとした変換もここで。

tower generate scaffold <モデル名>で生成できます。

# app/models/post.coffee
class App.Post extends Tower.Model
  @field "title"
  @field "body"
  @field "tags", type: ["String"], default: []
  @field "slug"

  @belongsTo "author", type: "User"

  @hasMany "comments", as: "commentable"

  @before "validate", "slugify"

  slugify: ->;
    @set "slug", @get("title").replace(/^[a-z0-9]+/g, "-").toLowerCase()

Tower.Routeクラス

ルーティング定義。セッションの生成、破棄もココ。

# config/routes.coffee
Tower.Route.draw ->
  @match "/login", "sessions#new", via: "get", as: "login"
  @match "/logout", "sessions#destroy", via: "get", as: "logout"

  @resources "posts", ->
    @resources "comments"

  @namespace "admin", ->
    @resources "users"
    @resources "posts", ->
      @resources "comments"

  @constraints subdomain: /^api$/, ->
    @resources "posts", ->
      @resources "comments"

  @match "(/*path)", to: "application#index", via: "get"

ビュー

描画部分を定義。CoffeeKupで記述。Bootstrap2に対応!

tower generate view <ビュー名> コマンドで生成できます。

# app/views/posts/new.coffee
formFor "post", (f) ->
  f.fieldset (fields) ->
    fields.field "title", as: "string"
    fields.field "body", as: "text"

  f.fieldset (fields) ->
    fields.submit "Submit"

Layout。指定しなければ、このレイアウトでラップされる。partialだけ書けば良い。

# app/views/layouts/application.coffee
doctype 5
html ->
  head ->
    meta charset: "utf-8"

    title t("title")

    meta name: "description", content: t("description")
    meta name: "keywords", content: t("keywords")
    meta name: "robots", content: t("robots")
    meta name: "author", content: t("author")

    csrfMetaTag()

    appleViewportMetaTag width: "device-width", max: 1, scalable: false

    stylesheets "lib", "vendor", "application"

    javascriptTag "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"
    javascripts "vendor", "lib", "application"

  body role: "application", ->
    if hasContentFor "templates"
      yield "templates"

    nav id: "navigation", role: "navigation", ->
      div class: "frame", ->
        partial "shared/navigation"

    header id: "header", role: "banner", ->
      div class: "frame", ->
        partial "shared/header"

    section id: "body", role: "main", ->
      div class: "frame", ->
        yields "body"
        aside id: "sidebar", role: "complementary", ->
          if hasContentFor "sidebar"
            yields "sidebar"

    footer id: "footer", role: "contentinfo", ->
      div class: "frame", ->
        partial "shared/footer"

  if hasContentFor "popups"
    aside id: "popups", ->
      yields "popups"

  if hasContentFor "bottom"
    yields "bottom"

Tower.Controllerクラス

モデルとビューを操作するコントローラクラス。あるアクションに対してモデルとビューをどうするか定義。モデリングにもよるけど、1つのモデルに対して大体1個定義。

tower generate controller コマンドで生成できます。


# app/controllers/postsController.coffee
class App.PostsController extends Tower.Controller
  index: ->
    App.Post.all (error, posts) =>
      @render "index", locals: posts: posts

  new: ->
    @post = new App.Post
    @render "new"

  create: ->
    @post = new App.Post(@params.post)

    super (success, failure) ->
      @success.html -> @render "posts/edit"
      @success.json -> @render text: "success!"
      @failure.html -> @render text: "Error", status: 404
      @failure.json -> @render text: "Error", status: 404

  show: ->
    App.Post.find @params.id, (error, post) =>
      @render "show"

  edit: ->
    App.Post.find @params.id, (error, post) =>
      @render "edit"

  update: ->
    App.Post.find @params.id, (error, post) =>
      post.updateAttributes @params.post, (error) =>
        @redirectTo action: "show"

  destroy: ->
    App.Post.find @params.id, (error, post) =>
      post.destroy (error) =>
        @redirectTo action: "index"

データベースの定義

どのデータベースを使うか。開発用、テスト用、デモ用、本番用など用途に応じて定義可能。

# config/databases.coffee
module.exports =
  mongodb:
    development:
      name: "app-development"
      port: 27017
      host: "127.0.0.1"
    test:
      name: "app-test"
      port: 27017
      host: "127.0.0.1"
    staging:
      name: "app-staging"
      port: 27017
      host: "127.0.0.1"
    production:
      name: "app-production"
      port: 27017
      host: "127.0.0.1"

多言語の定義

日本語の場合、たぶんjp.coffeeで定義。多言語化は始めからやっておくと吉。

# config/locales/en.coffee
module.exports =
  hello: "world"
  forms:
    titles:
      signup: "Signup"
  pages:
    titles:
      home: "Welcome to %{site}"
  posts:
    comments:
      none: "No comments"
      one: "1 comment"
      other: "%{count} comments"
  messages:
    past:
      none: "You never had any messages"
      one: "You had 1 message"
      other: "You had %{count} messages"
    present:
      one: "You have 1 message"
    future:
      one: "You might have 1 message"

こんなもんかな。

全体的にコードが美しいです。ドキュメントはまだまだ足りませんが、Rails-erやCakePHP-erには読まなくとも大体は理解できるようになっているので、学習時間が少なくて済むのがいいところでしょうか。

0 件のコメント: