手始めにMembase調べてみた

Python Hackathon で BigCouch の発表をする予定だったんですが、Membase と CouchOne の合併をうけて、、、Membase のほうに俄然興味が映ってしまったのでその辺をささっと調べて発表してきました。

内容は薄いです...

が、、、、もうね、 StrategicSqilte3 とかどの辺がStrategicなのかと小一時間問い詰めたいぐらいorzな気分です。

Moxi Proxy とか vBucket Client とかそれなりにいい感じでなので、仕事でも使おうかなーって思ってたんですが、、、すまん、SQLite3 の運用設計なんてしたくないから使いません*1

*1:この手のセッション系KVSで重要なのってデータのExpirationとそれに伴うメモリ/ディスクからの消滅のライフサイクルだと思ってるんですが、肝心のそれが、sqlite3 の vacuum 発行してね! って、ぶっちゃけ怖くてやってられませんww

CSSフレームワークもやればできる子(改訂版)

前のエントリ でやったほうほうだと、@import のパスを解決したりするのが面倒だったり、 複数ファイルで同じファイルを @import でマクロとして使うようにすると、コンパイル結果のファイルに重複定義が現れたり、、、ということで余り案配がよろしくないのです。

というわけで、少し改訂しました。

/**
 * addCssMacroFeature.js
 * Extends connect compiler middleware to support CSS framework
 */
var fs = require('fs');
var macros = [];
module.exports = function(filePath){
  macros.push(fs.readFileSync(filePath) + "\n");
}
// extend connect compiler
var compilers = require('connect/middleware/compiler').compilers;
['sass', 'less'].forEach(function(key){
  var compiler = compilers[key];
  var org = compiler.compile;
  compiler.compile = function(str, fn){
    // add the macro
    var macro = macros.join('');
    org(macro, function(err, mcompiled){
      if(err){
        console.error("macro compile failed " + err);
        fn(err);
      }else{
        org(macro + str, function(err, compiled){
          if( err ){
            console.error("concated compile failed " + err);
            fn(err);
          }else{
            fn(null, compiled.substr(mcompiled.length));
          }
        });
      }
    });
  };
});

これで、どんなフレームワークにも対応です。Expressの起動スクリプト

var addCssMacro = require('addCssMacroFeature.js');
addCssMacro('public/stylesheets/screen.css');
addCssMacro('public/stylesheets/print.css');

とやれば、screen.css, print.css に定義された .span-1 などはすべてマクロとしてコンパイラで利用することができます。マクロ部分しかコンパイル時に取り込まないので、screen.css や print.css などをリンクし忘れると余り意味がないですが。その辺の依存関係解決までやろうとすると大変ですね。

CSSフレームワークだってやればできる子

はてなブックマーク - CSSフレームワーク『BlueTrip』が想像以上にすごい - present

盛り上がってますね。

私のようなCSSの文法はわかるけど効率よくCSS書くにはどうしたらいいの?とか、ぶっちゃけ細かいところめんどくさくて span-24 とか、そりゃやりたくなるよ、という人が多い一方で、「おいおい、そんなことしたら font-size="10.5pt" とHTMLに書いているのとかわらんだろうがー」とご立腹な人もいるわけです。

span-24 とかやるのは「デザインとHTMLを分離する目的で使うのではなくて、単にspan-24って書いて別ファイルにした方が効率的に配信できるじゃん」と割り切ってスルーする、というのもあるんですが、ちょっとカウンタークレームをがんばってみました。

less ですよ、less

要するに、span-24をHTMLファイルに書くんじゃなくて、CSSに書けばいいでしょ、ってことで。がんばります。

まずHTML。

      <link rel="stylesheet" href="/stylesheets/sunrise.css" type="text/css" />
      ...

      <h1 class="logo">
        <a href="/">Sunrise</a>
        <span class="subTitle">Application container framework powered by node.js</span>
      </h1>

これならHTMLとして文句ないでしょう。

次デザイン。BlueTrip じゃなくてすいません。Blueprint のほうが慣れてしまっているので、その screen.css ファイルを /stylesheets/screen.less というファイルに保存して、次に、 /stylesheets/sunrise.less というファイルを作ります。

@import 'screen';

h1.logo {
    .span-24;
}

これで http://xxxx:yyyy/stylesheets/sunrise.css にアクセスすると...

/* -----------------------------------------------------------------------


 Blueprint CSS Framework 1.0
 http://blueprintcss.org

   * Copyright (c) 2007-Present. See LICENSE for more info.
   * See README for instructions on how to use Blueprint.
   * For credits and origins, see AUTHORS.
   * This is a compressed file. See the sources in the 'src' directory.

----------------------------------------------------------------------- */
/* reset.css */
...
h1.logo {
  float: left;
  margin-right: 10px;
  width: 950px;
}

こうなるんですよ。

どうやった?

less というCSSコンパイラを使います。

http://lesscss.org/

これを使うと、CSSを拡張してプログラムっぽく書くことができます。なので、BluePrint の span-24 をマクロ展開するようなクラス定義を作ることができます。これでHTMLに出さずにBluePrint/CSSフレームワークを使うことができる!!

less ってどうやって使うの?

javascript なので、less.js をブラウザに読み込ませて、、、いやいや、そこは node.js で。

node.js上のWebフレームワークである express フレームワークでは標準でlessに対応しています。staticファイルとして .less のファイルを置けば勝手にコンパイルしてCSSの形ではいてくれます。

が、ちょっと問題があって コンパイル時の @import のサーチパスがうまく設定されていない。なので @import 'screen'; と普通にやってしまうとコンパイルエラーになります。

うーんと悩んででた解決策。

var compilers = require('connect/middleware/compiler').compilers;
compilers.less.compile = function(str, fn){
  var less = require('less');
  try {
    var parser = new less.Parser({paths: ['path/to/root/of/your/stylesheet/']});
    parser.parse(str, function(err, root){
      fn(err, root.toCSS());
    });
  } catch (err) {
    fn(err);
  }
}

コンパイラの設定を外側から書き換えてみました。これを起動時に呼び出すようにしておけば、screen.css に書かれた命令をマクロとして自分のCSSで使えますよ。

express で whitespace を含むデータを受け取れないバグ

が今発生中。

パッチ書くかーと思っていろいろ調べていたら根っこの問題で既にpull requestが上がっているのを見つけたので静観します。

https://github.com/visionmedia/node-querystring/issues#issue/3

いつのまにか bodyDecoder の実装が node.js にはいっている querystring モジュールを使うのではなく node-querystring モジュール (npm名はqs) を使うように変更されていて、後者の方でバグっていた、というオチでした。

すぐ直るでしょうし、パッチ部分をローカルに当てれば回避はできます。

絶賛開発中 #3 プロトタイプ

まだちょっとCSSとかがひどいのと(Blueprint.cssを入れた)、markdown がなんかうまく機能していないような気がするんですが、 node.js を使って CouchApp のアプリケーションコンテナを作る、という感じのものが形になりつつある状況。

https://github.com/yssk22/sunrise

git clone して npm install 叩けば入ります。で、

$ sunrise:create foo
$ node foo/boot.js

これだけで(CouchDB が auth = admin/password, port = 5984 という前提で)、すぐ動く状態にはなりました。

コンテナを作るときにいろんなアプリがーってことを考えるんですが、とりあえず経験値的にはブログっぽいの作れれば何でも作れる印象なので、ブログっぽいものがデフォルトで入った状態。

あとは 適当に CouchApp 作って、node.js でハンドラを書くだけ。たぶんハンドラ書くのも面倒なぐらいの CouchApp だけで動くアプリ用のプロキシも書くと思う。

  • npm は CouchDB でレプリカできる
  • sunrise アプリは CouchApp だから CouchDB でレプリカできる

ということで、node.js さえ動けば、CouchApp だけでは難しいアプリケーションも複製できるようなことを想定した作りになっています(node.js用のコードもデザインドキュメントに放り込む感じ)。node.js をどうやってレプリカするかはあまり考えていないけど、かなりポテンシャルあるからwebOS見たいにデフォルトで入るところも増えるんじゃないかなという楽観モード...

そろそろ 0.1 リリースに向けて

  • アプリの構造/作り方 等のドキュメント
  • せっかくnode使っているので CouchDB _changes フィードと WebSocket を利用したチャットアプリ
  • 細かいバグ修正

をいれたら、0.1 のタグを切ってリリースしつつ、さくらVPSで動かそうかなぁという感じです。

CSS/ロゴデザインしてくれる人とか、CouchApp いろいろ作ってくれる人とかの手を借りたい感じですがその辺は公開後に実際にプロダクションで動かしつつ募集でしょうかね。

絶賛開発中 #2

とりあえず、rsync すらめんどくさくなったので githubに登録しておく。

yssk22/sunrise - GitHub

アプリは CouchDBレプリケーションでコピーできるけど、コンテナがまだその辺未対応なので。example はこれから作ります。(今あるヤツはさすがにgitにのっけるには的な...)

これを書きながら、貯まってきたノウハウは書籍にまとめつつあります。今、既存のJS資産を利用する的な話を書いているところで、

https://github.com/yssk22/sunrise/blob/master/lib/sunrise/vendor/php.js

とか見ると、24500行もある、JavaScriptによるPHP関数の実装なんですが、元々クライアント向けに作られていたものを、がっちゃんこして node.js で使えるようにしたものです。メモリに乗っかるので、ファイルサイズは心配してないです。

https://github.com/yssk22/sunrise/blob/master/lib/sunrise/vendor/sanitizer.js

は G様ご謹製のHTMLサニタイズライブラリ。Cajaプロジェクトのhtml_sanitizer.js というフJS部分を引っこ抜いてきました。HTML はある程度許容したい、的な用途にはぴったりのライブラリがあるもんです。ほかに、node-markdown という Markdown ライブラリも使ってますが、こちらは別の方が クライアント向けに作られていた Showdown.js というライブラリを node で使えるようにパッケージ化したもの。

node.js のブクマで「これでライブラリがあればなー」っていうのをよく見かけるんですが、あるんですよ、すぐそばに。ちょちょっとnode.jsで使えるようにするために必要な作業があるぐらいで。

で、この「node.js で使えるようにする」というのはそれほど大変な作業じゃないので、汎用的な方法を本で紹介しよう、という作業もあわせてやっています。。。。実はコード書く作業が楽しくなって、本の方がおろそかになっているのは余り大きな声では言えませんが...

絶賛開発中

今こんなのを作っています。

exports.setup = function(site, done){
  site.all('/posts/admin/*',
           oauth.requiredWith('twitter'));

  site.install('posts', function(app){
    var posts = app.model;
    site.get('/',
             $.title('Home'),
             posts.byTag('news'),
             $.render('index.ejs'));

    done();
  });
}

node.js とCouchDB で動く、アプリケーションコンテナ的な何か。

フロントエンドからサーバーサイド、果てはデータベースまですべてJavaScriptでやってやろーじゃん、的な勢いで。

普通にアプリを作るだけならCouchDBだけでもいいんですが、認証情報を持ちたくないので、認証プロセスが Erlang で OAuth 拡張を書くか、アプリサーバーを用意するかの2択になるので、後者を選びました。

site はコンテナそのもので、基本的なレイアウトなどの情報をもつExpress Serverです。あ、Express Server は node.js のWebアプリフレームワーク

で、そこに install コマンドを使って CouchApp 込みの Express Server を追加していくとURIが作られて、アプリケーションが使える。俗に言うContent Management System のプラグインみたいなヤツ。 app.model というのが CouchDB で扱うモデルデータへのラッパーみたいなヤツ。byTag は実際には

 byTag : function(params, options){
    if( typeof(params) == 'string' ){
      params = {
        tag: params
      };
    }
   ...
   return this.bind('view', 'posts/by_tag', params, options);
 }

な感じになっているJSなんです。で、サーバーサイドでは cradle っていう node.js 用のCouchDBクライアントにバインドしているんですが、クライアントサイドでは jquery.couch.js 辺りにバインドするようにすれば node.js なくなっても動き続けるよね? 的なことを視野に入れつつどこまでやれるか。

    site.get('/',
             $.title('Home'),
             posts.byTag('news'),
             $.render('index.ejs'));

の部分をクライアントで動かす方法を考えればいいだけのような気がするんですが。

node.js で使うJavaScript部分も当然CouchDBの中に放り込んでいます。

もうちょっとしたら、ソースを公開というか、これで CouchDB JP ホストできるんじゃないかと、と画策中。