pig's diary

何でも忘れるので万年初心者ね

ビットマスクによるフラグ

参考:goog.ui.Control

複数の状態(STATE)を、1つの変数で保持する。例えば、DISABLED状態であり、同時にHOVER状態でもある状態を1つの変数で表現する。以下、そのときのフラグの上げ下げのしかた。

まずビットマスクに使う定数を用意する。1,2,4,8,16,32...とする。

/**
 * @enum {number}
 */
var STATE = {
  DISABLED: 1,
  HOVER: 2,
  ACTIVE: 4,
  FOCUSED: 8
};

/**
 * @type {number}
 */
var state  = 0;

フラグが上がっているかの確認。if文で使うときは、 if (state & STATE.DISABLED) で判定できる。まだ、どのフラグも上がってない。

console.log(!!(state & STATE.DISABLED));  // false
console.log(!!(state & STATE.HOVER));     // false
console.log(!!(state & STATE.ACTIVE));    // false
console.log(!!(state & STATE.FOCUSED));   // false

それでは、2つのフラグを上げてみる。STATE.DISABLEDとSTATE.HOVERだけ。2つのやり方があり、同じ意味。

state |= STATE.DISABLED;
state = state | STATE.HOVER;

console.log(!!(state & STATE.DISABLED));  // true
console.log(!!(state & STATE.HOVER));     // true
console.log(!!(state & STATE.ACTIVE));    // false
console.log(!!(state & STATE.FOCUSED));   // false

他のフラグには影響を与えていないのが分かる。

次に、挙げたフラグを、下げてみる。これにも2つやり方があり、同じ意味。

state &= ~STATE.DISABLED;
state = state & ~STATE.HOVER;

console.log(!!(state & STATE.DISABLED));  // false
console.log(!!(state & STATE.HOVER));     // false
console.log(!!(state & STATE.ACTIVE));    // false
console.log(!!(state & STATE.FOCUSED));   // false

続いて、いっぺんに複数のフラグを上げてみる。

state = STATE.DISABLED | STATE.HOVER | STATE.ACTIVE;

console.log(!!(state & STATE.DISABLED));  // true
console.log(!!(state & STATE.HOVER));     // true
console.log(!!(state & STATE.ACTIVE));    // true
console.log(!!(state & STATE.FOCUSED));   // false

上記は、初期値の時点でいくつかのフラグを上げておきたいときなどに使える。代入文であることからも分かるように、元のフラグを無視して上書きする。

全てのフラグを下げるには、0を代入する。

state = 0;

console.log(!!(state & STATE.DISABLED));  // false
console.log(!!(state & STATE.HOVER));     // false
console.log(!!(state & STATE.ACTIVE));    // false
console.log(!!(state & STATE.FOCUSED));   // false

gentoo + kde + ibus で、ブラウザに日本語入力できない

gentoo + kde + ibus + ibus-mozc で日本語が使えるようになった!
でも、ブラウザにだけ日本語入力できませんでした。

http://code.google.com/p/ibus/issues/detail?id=1349

gtk USEフラグを上げ、emerge --update --newuse ibus したら、日本語が使えるようになりました。

CentOS6、nginx -> node 最小構成

僕はセキュリティとかに詳しくない人間です。実運用は十分ご注意ください。

環境

さくらVPS 1G

$ cat /etc/redhat-release 
CentOS release 6.2 (Final)

nginx インストール

$ sudo vim /etc/yum.repos.d/nginx.repo
# /etc/yum.repos.d/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=0
enabled=1
$ sudo yum install nginx
$ sudo nginx # :80 でlistenできてるか、ブラウザで確認する。「Welcome to nginx!」ならOK
$ sudo nginx -s quit

nodeでサーバを起動

$ cd
$ vim ./test_server.js
// ./test_server.js
var http = require('http');
http.createServer(function (req, res) {
    res.writeHead(200, {'Content-Type': 'text/plain'});
    res.end('YEAH!!!!!!\n');
}).listen(3000, '127.0.0.1');
console.log('Server running at http://127.0.0.1:1337/');
$ npm install forever -g # foreverお気に入りです 何度も生き返してくれました 
$ forever start test_server.js # nodeサーバが127.0.0.1:3000で起動

nginx 設定ファイル編集

$ sudo vim /etc/nginx/conf.d/mynginx.conf # 新規ファイル
# /etc/nginx/conf.d/mynginx.conf 
server {
    listen        80;
    server_name   localhost;

    location / {
        proxy_pass    http://localhost:3000; # :3000 に投げ渡す
        break;
    }
}

nginxサーバ起動

$ sudo service nginx start # nginx サーバ起動

ブラウザで、:80 にアクセス。nodeサーバの返す「YEAH!!!!!」が表示されました。

設定、もっと

  • nginx の設定回り。もっとかっこいい設定にして、ちゃんとしたリバースプロキシサーバにする。

yesod-test-0.2.0.2 でビルドエラー

今直し中だそうです(僕はHaskell1ミリも読めない)

Mac OS 10.7.3
yesod-core version:1.0.0.2

$ cabal-dev install && yesod --dev devel

localhost:3000 が立ち上がるはずが、だめ。
最後のほうのログ:

cabal: Error: some packages failed to install:
yeah-0.0.0 depends on yesod-test-0.2.0.2 which failed to install.
yesod-test-0.2.0.2 failed during the building phase. The exception was:
ExitFailure 1

そしたら

.. Thanks for the heads up, I'm in the process of fixing it right now.
Actually, I'd recommend waiting until I post the new yesod-platform, ...

http://groups.google.com/group/yesodweb/browse_thread/thread/ffbc9ea24d002c05/7b12f9532ced65d2?show_docid=7b12f9532ced65d2

expressでOAuth。Twitterに投稿。

なんだかよく分からなかった。やっとできた。基本的なことが分かってないんだね。

追)githubにサンプルを作りました https://github.com/piglovesyou/express-twitter-oauth-sample

やること:

  1. 下準備。ライブラリの準備。
  2. app.get('/', routes.index);。ログインしてなかったら、/login にリダイレクト。
  3. app.get('/login', routes.login); ログイン画面。ユーザーに「Twitterでログイン」をクリックしてもらう。
  4. app.get('/auth/twitter', routes.auth.twitter); request_token を取得。取得したら、authenticate を叩く。
  5. app.get('/auth/twitter/callback', routes.auth.twitter.callback); verifierを取得。取得したら、access_tokenを取得。
  6. 準備完了。ユーザーのscreen_name を出して、ログイン中画面にユーザーをお招き。
  7. app.post('/post', routes.post); アプリ経由でtweet を投稿してみる。
  8. app.get('/logout', routes.logout); ログアウト。セッションを破壊。大爆発。

1. 下準備。ライブラリの準備。

https://dev.twitter.com/appsで、アプリを登録。今回はtweetしたいので、read and write に設定しておく。
expressは、sessionを使うオプションでプロジェクトを作成。
npm install oauth しておいて、./routes/index.js で読みこむ。 ./routes/index.js の先頭で、oauth の設定をする(インスタンス作り)。

// ./routes/index.js
var OAuth       = require('oauth').OAuth;
var oa = new OAuth(
  "https://twitter.com/oauth/request_token",
  "https://twitter.com/oauth/access_token", 
  "Your consumer key is here.",
  "Your consumer secret is here.",
  "1.0",
  "http://localhost.com:3000/auth/twitter/callback",
  "HMAC-SHA1");

これでTwitterアプリ用のoauthインスタンスができた。

2. app.get('/', routes.index);

このページは、ログインユーザーしか見れなくする。
ログインしてる人のために、テンプレート「index」が必要。
ログインしてない人は、ただちに /login に飛んでもらう。

exports.index = function (req, res) {
  if(req.session.oauth && req.session.oauth.access_token) {
    res.render('index', {
      screen_name: req.session.twitter.screen_name
    });
  } else {
    res.redirect("/login");
  }
};

3. app.get('/login', routes.login);

ログインしてない人が、「ログイン」リンクをクリックしてもらうためのページ。無くてもいい。ここに「Twitterでログイン」「Facebookでログイン」の2つをあとで置こうかなと思ってます。今は、Twitterだけ。
テンプレート「login」が要ります。

exports.login = function (req, res) {
  if(req.session.oauth && req.session.oauth.access_token) {
  } else {
    res.render('login');
  }
};

4. app.get('/auth/twitter', routes.auth.twitter);

認証のためのURL。テンプレートはなし。アクセスと同時に、oauthインスタンスにrequest_tokenを取りに行かせる。
request_tokenをtwitterにもらったら、ユーザーにそれを握らせて、アプリの認証画面に行ってもらう。あと、貰ったほかのものはsessionにしまっておく。ユーザーが「許可」してくれることを祈って待つ。

exports.auth = {};
exports.auth.twitter = function(req, res){
  oa.getOAuthRequestToken(function(error, oauth_token, oauth_token_secret, results){
    if (error) {
      res.send("yeah no. didn't work.")
    } else {
      req.session.oauth = {};
      req.session.oauth.token = oauth_token;
      req.session.oauth.token_secret = oauth_token_secret;
      res.redirect('https://twitter.com/oauth/authenticate?oauth_token='+oauth_token)
    }
  });
};

5. app.get('/auth/twitter/callback', routes.auth.twitter.callback);

OAuthをnew したところと同じcallback用のURL。これはTwitterのアプリ設定画面でも同じものを登録しとく必要がある。
ユーザーが「許可します」ボタンを押してくれたら、Twitterはユーザーをここに連れてくる。パラメータには、ユーザーからのOKサインである「verifier」が入っているので、ありがたく貰っておく。
verifierと、さっきsessionにしまったものをきっちり揃えて、Twitterにお願いしに行く。書類をそろえて、「このユーザーと懇意にさせてください」とお願いする。書類に不備がなければ、OKのハンコがもらえる。OKハンコは「access_token」と「access_token_secret」の2つ。これがずっと欲しかったもの。目的を達成したから、それをsessionに大事にしまって、ユーザーをログイン中画面に連れていく。

exports.auth.twitter.callback = function(req, res, next){
  if (req.session.oauth) {
    req.session.oauth.verifier = req.query.oauth_verifier;
    var oauth = req.session.oauth;
    oa.getOAuthAccessToken(oauth.token, oauth.token_secret, oauth.verifier, 
        function(error, oauth_access_token, oauth_access_token_secret, results){
          if (error){
            res.send("yeah something broke.");
          } else {
            req.session.oauth.access_token = oauth_access_token;
            req.session.oauth.access_token_secret = oauth_access_token_secret;
            req.session.twitter = results;
            res.redirect("/");
          }
        }
    );
  } else
    next(new Error("you're not supposed to be here."));
};

6. 準備完了。

ユーザーのscreen_name を出して、「ログイン中画面」にユーザーをお招きする。

exports.index = function (req, res) {
  if(req.session.oauth && req.session.oauth.access_token) {
    // コッチ。
    res.render('index', {
      screen_name: req.session.twitter.screen_name
    , title: 'Express'
    });
  } else {
    res.redirect("/login");
  }
};

これで「Twitterでログイン」ができました。

7. app.post('/post', routes.post);

このアプリ経由で、Tweetしてもらう。
ここは、xhrで叩く想定。ユーザーがここにPOSTすると、textパラメータの値をTwitterに投稿するしくみにする。

クライアントサイドのHTMLとJSはこんな。さきにjQueryを読んでおく。

// index.jade
form#tweet
  input(type="text", name="text")
  input(type="submit", value="ツイート。")
// client side script
$('#tweet').submit(function (e) {
  var text = e.target.text.value;
  $.ajax({
    url: '/post'
  , type: 'POST'
  , data: {text: text}
  });
  return false;
});

うん。
nodeは、リクエストを受け取ると、こんどはoauthインスタンスにリクエストを投げさせる。引数に、TwitterのOKハンコであるaccess_tokenとaccess_token_secretを渡す(ユーザ特定のヒントとユーザ許可済みのしるし)。oauthインスタインスは、Twitterに特別なリクエストを投げてくれる。
特別っていうか、セッションに保存しているtokenやらいろいろを使って、特別なヘッダを作って、リクエストを飛ばす。
このときどんなリクエストを投げて欲しいかを、Twitterは丁寧に解説している。

... At a very simplified level, Twitter's implementation requires that requests needing authorization contain an additional HTTP Authorization header with enough information to answer the questions listed above. ...

POST /1/statuses/update.json?include_entities=true HTTP/1.1
Accept: */*
Connection: close
User-Agent: OAuth gem v0.4.4
Content-Type: application/x-www-form-urlencoded
Authorization:
OAuth oauth_consumer_key="xvz1evFS4wEEPTGEFPHBog",
oauth_nonce="kYjzVBB8Y0ZFabxSWbWovY3uYSQ2pTgmZeNu2VS4cg",
oauth_signature="tnnArxj06cWHq44gCs1OSKk%2FjLY%3D",
oauth_signature_method="HMAC-SHA1",
oauth_timestamp="1318622958",
oauth_token="370773112-GmHxMAgYyLbNEtIKZeRNFsMKPR9EyMZeS9weJAEb",
oauth_version="1.0"
Content-Length: 76
Host: api.twitter.com

status=Hello%20Ladies%20%2b%20Gentlemen%2c%20a%20signed%20OAuth%20request%21

https://dev.twitter.com/docs/auth/authorizing-request

consumer_keyやら、access_tokenやら。もう必要なものは全部持っているんだけど、この特別なヘッダ(Authorization header)を作るのが大変。いろいろ混ぜたりこねたりしなくちゃいけない。
これをやってくれるのが、node-oauthモジュール。node-oauthの中身をのぞくと、たとえばaccess_token_secretを使って、まぜたりこねたりして、シグネチャーを作っているのが分かる。絶対に作りたくないね。それをやってくれる。ありがたいね、本当の話。

exports.post = function (req, res) {
  if(req.session.oauth && req.session.oauth.access_token) {
    var text = req.body.text;
    oa.post(
      'https://api.twitter.com/1/statuses/update.json',
      req.session.oauth.access_token, 
      req.session.oauth.access_token_secret,
      {"status": text},
      function (err, data, response) {
        if (err) {
          res.send('too bad.' + JSON.stringify(err));
        } else {
          res.send('posted successfully...!');
        }
      });
  } else {
    res.send('fail.');
  }
};

登録しているアプリ経由で、投稿できたはず。

8. app.get('/logout', routes.logout);

ログアウトする。
ログアウトのリンクをどっかに置いておいて、テンプレート「logout」も作っておく。テンプレートには「完全にログアウトしたから安心するように」と書いておく。

exports.logout = function (req, res) {
  req.session.destroy();
  res.render('logout');
};

ボンッッ。おしまい。


参考:

express でファイルアップロード

参考:

nodeでファイルアップロードする方法、難しい順。

1. req.on('data') で、ちょこちょこデータを溜め込んでいく。(僕には無理っぽい)
2. それを楽にするformidable というモジュール。丁寧に 'progress' というイベントまでemit してくれる。
3. express のapp.configure で app.use(express.bodyParser()) していたら、req.files で更に簡単にupload できる。formidable は必要なくなる。

express でファイルアップロードを書く

ただアップロードする他に、ファイルを配置するディレクトリを作ったり、DBにファイルを保存したりしてみました。
でも、ほとんどhttp://www.hacksparrow.com/handle-file-uploads-in-express-node-js.htmlの内容です。
html:

<form action="/upload" method="post" enctype="multipart/form-data">
  <input type="file" name="upfile"/>
  <input type="submit" value="送信"/>
</form>

server:

/* app.js */
var TMP_DIR = './tmp';
app.configure(function(){
  .
  .
  app.use(express.bodyParser({
    uploadDir: TMP_DIR /* 追記 */
  }));
  .
  .
});
.
.
app.get('/', routes.index);
app.post('/upload', routes.upload); /* 追記 */
.
/* route/index.js */

/*
 * GET home page.
 */


// Require modules.
var mkdirp = require('mkdirp') /* $ npm install mkdirp */
  , fs = require('fs')
  , _ = require('underscore')
  , path = require('path')
  , db = require('../resources/server/db');



// Pad string function.
_.mixin({
  pad: function (target, length) {
    var padStr = '0';
    var target = target.toString();
    var result = target;
    var num = length - target.length;
    for (var i=0; i<num; i++) {
      result = padStr + result;
    }
    return result;
  }
});



// Handler for GET '/'.
exports.index = function(req, res){
  db.Files.find({}, [], {}, function (err, files) {
    if (err) return;
    res.render('index', { 
      title: 'Express'
    , IS_PRODUCTION: process.env.NODE_ENV === 'production' // new
    , files: files ? files : []
    })
  });
}


// Handler for POST '/upload'.
var UPLOAD_BASEPATH = __dirname + '/../public/uploaded/';

var getUpfileDir = function () {
  var d = new Date();
  return d.getFullYear() + _.pad(d.getMonth() + 1, 2) + '/' + _.pad(d.getDate() + '/', 2);
};

exports.upload = function(req, res){
  var upfile = req.files.upfile
  if (upfile) {
    var tmpPath = './' + upfile.path;
    var saveDir = getUpfileDir();
    var dir_ = path.normalize(UPLOAD_BASEPATH + saveDir);
    if (!path.existsSync(dir_)) mkdirp.sync(dir_);
    var savePath = saveDir + path.basename(tmpPath) + path.extname(upfile.name);
    var targetPath = path.normalize(UPLOAD_BASEPATH + savePath);

    fs.rename(tmpPath, targetPath, function (err) {
      if (err) throw err;
      var file = new db.Files();
      file = _.extend(file, {
        title: req.body.title
      , name: upfile.name
      , type: upfile.type
      , path: savePath
      , createdAt: new Date()
      });
      file.save(function (err) {
        res.redirect('/');
      });
    });
  }
};
/* resources/server/db.js */

// Setting mongoose.
var mongoose = require('mongoose');
var FileUploadSchema = new mongoose.Schema({
  title: String
, name: String
, type: String
, path: String
, createdAt: Date
});

mongoose.model('Files', FileUploadSchema)
mongoose.connect('mongodb://localhost/fileupload')

exports.Files = mongoose.model('Files')

その他

  • <input type="file" name="upfile" multiple="multiple" / >と書くと、複数ファイルを同時にアップロードできる(対応ブラウザが限られると思います、ご注意ください)
  • その場合は req.files.upfile がオブジェクトじゃなくて配列になる。

node のexpress で Closure Library

node で Closure Library をやる一例です。

やること

  1. express プロジェクトを作る
  2. 使うnodeモジュールのインストール
  3. Closure Library を落とす
  4. Closure Compiler を落とす
  5. 実験用のapp.jsコードを置く
  6. production モードとそうでないモードを分ける
  7. Cakefileにタスクを書く
  8. 配備してみる

1. まず、express プロジェクトをつくります

$ express my_project
$ cd my_project

2. ./package.json を編集して、こんなふうにします。

/* ./package.json */
{
    "name": "application-name"
  , "version": "0.0.1"
  , "private": true
  , "dependencies": {
      "express": "2.5.8"
    , "jade": ">= 0.0.1"
    , "q": ">= 0.8.2"      // new
    , "muffin": ">= 0.2.7" // new
    , "underscore": ""     // new
  }
}
追記:underscoreを追加

nodeモジュールをインストールします。

$ npm install
...
q@0.8.2 ./node_modules/q 
└── event-queue@0.2.0
jade@0.20.3 ./node_modules/jade 
├── mkdirp@0.3.0
└── commander@0.5.2
express@2.5.8 ./node_modules/express 
├── mime@1.2.4
├── mkdirp@0.3.0
├── qs@0.4.2
└── connect@1.8.5
muffin@0.2.7 ./node_modules/muffin 
├── temp@0.2.0
├── coffee-script@1.1.3
├── docco@0.3.0
├── uglify-js@1.1.0
├── q-fs@0.1.17 (q-io@0.0.10 qq@0.3.2 fs-boot@0.0.7)
├── q@0.7.1 (event-queue@0.2.0)
├── glob@3.0.1 (graceful-fs@1.1.5 fast-list@1.0.2 inherits@1.0.0 minimatch@0.1.5)
└── prompt@0.1.12

3. Closure Library を落っことします。

$ svn checkout http://closure-library.googlecode.com/svn/trunk/ ./public/closure-library

publicフォルダの下には、置きたくないんですけどね。でも置きます。public/closure-library/closure/goog/base.js をhttpで読まないといけないので、ここに置いているんです。ハードリンクとか貼る人もいるんでしょうか?

4. Closure Compiler を落っことします。

$ mkdir -p library/closure-compiler
$ wget -P library/closure-compiler/ http://closure-compiler.googlecode.com/files/compiler-latest.tar.gz
$ tar -C library/closure-compiler/ -xf library/closure-compiler/compiler-latest.tar.gz
$ ls library/closure-compiler/
COPYING			compiler-latest.tar.gz
README			compiler.jar

ふーっ。ライブラリとかの準備はできました。

5. 次に、自分で書くクライアントサイドのコードを置く場所をつくります。あと、実験用のコードも置いておきます。

$ mkdir -p resources/client
$ touch resources/client/app.js

./resources/client/app.js には試しにこう書いておきます。

/* ./resources/client/app.js */
goog.provide('my.app');
goog.require('goog.dom');

my.app = function () {
  var elm = goog.dom.createDom('h1', {
    style: 'font-size: 600%'
  }, 'yeah..');
  goog.dom.append(document.body, elm);
};

goog.exportSymbol('my.app', my.app);

./resources/client 配下にファイルを並べて、goog.provide/ goog.require していく感じです。

6. production モードと開発モードの区別ができるようにします。
作戦としては、

  • node を「NODE_ENV=production node app.js」で起動したときは本番用(JSを小さくします)。
  • ただの「node app.js」で起動したときは、開発用(ビルドにできるだけ時間をかけないようにします)。

まず、サーバ側のコードの修正です。
./routes/index.js

/* ./routes/index.js */
exports.index = function(req, res){
  res.render('index', { 
    title: 'Express'
  , IS_PRODUCTION: process.env.NODE_ENV === 'production' // new
  })
};

次に、この値をHTMLテンプレート側で拾って、クライアント側のjsファイルの読み分けをします。

// ./view/layout.jade
!!!
html
  head
    title= title
    link(rel='stylesheet', href='/stylesheets/style.css')
    
    // new code
    - if (IS_PRODUCTION)
      script(src='/javascripts/app-min.js')
    - else
      script(src='/closure-library/closure/goog/base.js')
      script(src='/javascripts/app.js')

  // modified
  body(onload='my.app()')!= body

OK。だと思います。ついでに、onload でスクリプトを実行するように書いておきました。

7. Cakefile にタスクを書いて、./public/javascript/app.js を作ってもらいます。
以下、muffin を使いたいのでcoffeescriptが必要です。coffeescriptをインストールしておく必要があります(npm install -g coffee-script)。

プロジェクト直下に Cakefile というファイルを作って、こんなファイルにします。
※修正しました。muffinだとoutputするやりかたが分からなかったのでネイティブのプロセス使った。
Cakefile


# Include required libraries.

muffin = require 'muffin'
child  = require 'child_process'
Q      = require 'q'
_      = require 'underscore'



# Options.

option '-w', '--watch', 'Keep watching file modifying.'
option '-c', '--compile', 'Compile client scripts as ADVANCED_OPTIMIZATIONS.'



# Constants.

commonArgs = "
  public/closure-library/closure/bin/build/closurebuilder.py
  --root=./public/closure-library/
  --root=./resources/client/
  --namespace=\"my.app\" "

COMMAND = 
  BUILD: commonArgs + "
  --output_mode=script
  --output_file=./public/javascripts/app.js
  "
  COMPILE: commonArgs + "
  --output_mode=compiled 
  --output_file=./public/javascripts/app-min.js
  --compiler_jar=./library/closure-compiler/compiler.jar
  --compiler_flags=\"--compilation_level=ADVANCED_OPTIMIZATIONS\""



# Tasks.

stamp = ( ->
  startTime = -1
  return {
    start: ->
      startTime = Date.now()  if startTime is -1
    end: ->
      t = if startTime isnt -1 then Date.now() - startTime else 0
      startTime = -1
      return t
  }
)()

execCommand = (command) ->
  stamp.start()
  child.exec command, (error, stdout, stderr) ->
    console.log """
    \n\n======
    #{stderr}
    --IT TOOK #{stamp.end() / 1000} sec.--\n
    """

task 'build', 'Make closure builder build scripts.', (options) ->
  command = if options.compile then COMMAND.COMPILE else COMMAND.BUILD
  if !options.watch
    execCommand(command)
  else
    isFirst = true
    muffin.run
      files: './**/*'
      options: options
      map:
        'client/.*?\.js': (matches) ->
          return  if isFirst
          execCommand(command)
      after: ->
        execCommand(command)  if isFirst
        isFirst = false

これで、cakeタスクが使えるようになりました。

  • 開発用に、更新監視しながらビルドするとき: cake -w build
  • 本番用にコンパイルするとき。:cake -c build

8. ビルドしてみましょう。

$ cake build
The "sys" module is now called "util". It should have a similar interface.
public/closure-library/closure/bin/build/closurebuilder.py: Scanning paths...
public/closure-library/closure/bin/build/closurebuilder.py: 797 sources scanned.
public/closure-library/closure/bin/build/closurebuilder.py: Building dependency tree..

$ node app.js


だ 大丈夫そうですね。

本番環境用はどうでしょうか?

$ cake -c build
The "sys" module is now called "util". It should have a similar interface.
public/closure-library/closure/bin/build/closurebuilder.py: Scanning paths...
public/closure-library/closure/bin/build/closurebuilder.py: 797 sources scanned.
public/closure-library/closure/bin/build/closurebuilder.py: Building dependency tree..
public/closure-library/closure/bin/build/closurebuilder.py: Compiling with the following command: java -jar ./library/closure-compiler/compiler.jar --js public/closure-library/closure/goog/base.js --js public/closure-library/closure/goog/debug/error.js --js public/closure-library/closure/goog/string/string.js --js public/closure-library/closure/goog/asserts/asserts.js --js public/closure-library/closure/goog/array/array.js --js public/closure-library/closure/goog/dom/classes.js --js public/closure-library/closure/goog/object/object.js --js public/closure-library/closure/goog/dom/tagname.js --js public/closure-library/closure/goog/useragent/useragent.js --js public/closure-library/closure/goog/math/size.js --js public/closure-library/closure/goog/dom/browserfeature.js --js public/closure-library/closure/goog/math/coordinate.js --js public/closure-library/closure/goog/dom/dom.js --js resources/client/app.js --compilation_level=ADVANCED_OPTIMIZATIONS
public/closure-library/closure/bin/build/closurebuilder.py: JavaScript compilation succeeded.

$ NODE_ENV=production node app.js


できました。
これで、さて、何作ろう。

      • -

追記:Cakefileをちょっと修正しました。深い階層のjsを更新したら、階層の数だけ無駄にcompileしてた。