node.js製フレームワークRailwayJSで遊んでみる
こんばんは。
台風の今夜いかがお過ごしでしょうか。こちらは結構ヤバイです。今日の天気とは関係ありませんが、javascript界隈も4号以上にヤバク熱い感じになってきてます。
http://www.publickey1.jp/blog/12/javascript_mvc.html
みたいな記事が入ってきてます。
というわけでサーバーサイドJS本命node.jsの上に乗っかったRailwayJSを使ってサンプルアプリを作ってみました。ちょろちょろとは触ってたけど、わりと普通の構成のWEBアプリ作ったらどうなるの?っていう興味本位andブラ三の合成レシピをスマホで見たかったてのも理由です。
作成したアプリはhttp://csv2sl.ueebee.com/で
Excelシートをコピペすると、HTMLのテーブルに変換してページを置いておけるだけというシンプルなものです。
普通ぽいWEBアプリをnode.jsで作ってみた私個人の感想は後ほど記載します。
RailwayJSは
本家:http://railwayjs.com/
日本語:http://railwayjs.jp/
で最新の情報を得ることができます。
「Create NodeJS web apps faster with more energy, using RailwayJS」
とあってnodejs製のWEBアプリ作成を助けてくれるフレームワークです。
簡易フレームワークexpressに、Ruby on Railsの便利な機能を乗っけてきたような感じになっています。
ちなみに同じ位置づけのnode.js製フレームワークとしてはtower.jsとかです。
http://towerjs.org/
いずれもRuby on Railsを強く意識しているフレームワークだな~という感想です。
RailwayJSを選んだポイント
デフォルトでcoffee-scriptで書けるらしい(tower.jsもらしいです)
最初tower.jsに行ったんだけど・・・scaffoldingが初見で動かなかったorz
直感&なんとなく( ゜д゜)ハッ!
という理由です。
インストール
node.jsが使える環境なら、
$ npm install railway -g
とするだけです。
node.jsの環境構築等はいっぱい情報があるので省略。
アプリの雛形作成
こちらもRuby on Railsよろしく、アプリケーションの雛形を作成してくれます。
ずらずら-とファイルが作成されます。
※coffee-scriptを使うので--coffeeオプションを追加しています。
$ railway init csv2sortableList --coffee
作業ディレクトリへの移動・必要なパッケージのインストール
作成されたディレクトリに移動して、関連パッケージをインストールします。
$ cd csv2sortableList $ npm install -l
ここまでで最低限必要な作業は終了。
ここからアプリの実装に必要な作業に入ります。
scaffoldを使って基本枠を作る
$ railway g scaffold post title content updated_at
これだけで動くアプリができます。
controllerもviewもlayoutsも、Ruby on Railsと同じ構成になっています。
また中身は空っぽですがhelper、modelも同じ構成です。
サーバー起動
以下のコマンドでサーバー起動します。
$ railway s ポート番号
※デフォルトだと3000番でサーバ起動します。
CRUD動作が行えることを確認できます。
見た目もTwitterBootStrapがデフォルトで適用されているため、おしゃれな画面になってます。
Controllerの中身
以下の感じです。CoffeeScriptなのも手伝って、かなり見通しが良くなってます。
注目するポイントとしては、
before ->
のところでここがbefore_filterのような共通化したコードになっています。
, only: ['show', 'edit', 'update', 'destroy']
で適用するアクションを指定しています。
load 'application' before 'load post', -> Post.find params.id, (err, post) => if err redirect path_to.posts() else @post = post next() , only: ['show', 'edit', 'update', 'destroy'] action 'new', -> @post = new Post @title = 'New post' render() action 'create', -> Post.create body.Post, (err, post) => if err flash 'error', 'Post can not be created' @post = post @title = 'New post' render 'new' else flash 'info', 'Post created' redirect path_to.posts() action 'index', -> Post.all (err, posts) => @posts = posts @title = 'Posts index' render() action 'show', -> @title = 'Post show' render() action 'edit', -> @title = 'Post edit' render() action 'update', -> @post.updateAttributes body.Post, (err) => if !err flash 'info', 'Post updated' redirect path_to.post(@post) else flash 'error', 'Post can not be updated' @title = 'Edit post details' render 'edit' action 'destroy', -> @post.destroy (error) -> if error flash 'error', 'Can not destroy post' else flash 'info', 'Post successfully removed' send "'" + path_to.posts() + "'"
@post.save (err) =>
みたいなコードが見受けられますが、ORMとして
https://github.com/1602/jugglingdb
jugglingdbというORMを採用しています。
対応DBとしても
redis
mongodb
等が揃っており、使い勝手がいいです。(sqlite3、rowlevel-mysql等も使えるようですが試してません)
redis、mongodbとDBを複数使用できるのもポイント高いです。
ちなみにRailwayJSの公式サイトではORMの説明が弱いと感じます。jugglingdb https://github.com/1602/jugglingdb
を確認したほうが使い方の参考になりました。
rails console相当の「railway c」
こちらもRuby on Railsのrails consoleに相当する、「railway c」とすると対話型コンソール(REPL console)が利用できます。
railway c railway> User.find(53, c) Callback called with 2 arguments: _0 = null _1 = [object Object] railway> _1 { email: [Getter/Setter], password: [Getter/Setter], activationCode: [Getter/Setter], activated: [Getter/Setter], forcePassChange: [Getter/Setter], isAdmin: [Getter/Setter], id: [Getter/Setter] }
というような感じになります。コンソールはjavascriptのcallbackを取るスタイルに多少なれないと行けないかもしれません。
ループも含めコールバックを使う形になります。
helper
こちらもRuby on Railsのヘルパーに似てます。組み込みヘルパーは本家RailwayJSにだいたい書いてあります。
今回時刻表示のフォーマットヘルパーがどこにあるのかわからなかったので、以下の共通ヘルパーを作ってみました。
※ここは自動で生成されたものだとcoffee-scriptじゃないみたいです。
app/helpers/application_helper.js
require('date-utils'); module.exports = { my_date_format_helper: function(dateString){ if (dateString == null || dateString == undefined){ return ""; } else { var formatted = dateString.toFormat("YYYY-MM-DD HH24:MI"); return formatted; } } };
view側で
<%- my_date_format_helper(@post.updated_at) %>
で使用できます。
routes
アプリのURI管理は
config/routes.js
に書いていきます。
exports.routes = function (map) { map.resources('users'); map.root('posts#index'); map.resources('posts'); map.get("postsall", "postsall#index"); map.get('tos', 'static_page#tos'); // Generic routes. Add all your routes below this line // feel free to remove generic routes map.all(':controller/:action'); map.all(':controller/:action/:id'); };
これもほぼRuby on Railsのroutes.rbと同じです。
map.root:アプリトップを指定
map.resources:RESTなURI
map.get:該当するURIに対するコントローラ・アクション
etcetc...
パッケージ管理
プロジェクト直下に
package.json
というファイルがあります。ここに必要なパッケージを書いていく感じになります。
これもBundlerに似てます。
{ "name": "csv2sl" , "version": "0.0.1" , "engines": ["node >= 0.4.0"] , "main": "server.js" , "dependencies": { "ejs": "*" , "ejs-ext": "*" , "express": "~2.x" , "railway": ">= 0.2.6" , "jugglingdb": ">= 0.1.0" , "coffee-script": ">= 1.1.1" , "csv": "" , "mongoose": "" , "async": "" , "password-hash": "" , "everyauth": "" , "jade": "" , "date-utils": "*" , "js2coffee": "*" }, "devDependencies": { "nodeunit": "*" , "sinon": "*" , "semicov": "*" } , "scripts": { "test": "nodeunit test/*/*" } }
書いたパッケージのインストールは
$ npm install -l
で依存関係含めインストールしてくれます。
ざっくりとはこんな感じになります。
気をつけるところ
node.jsは基本非同期です。
なので、
hoge = [] for sheet in sheets hoge.push sheet render({hoge: hoge})
みたいなコードを書くと、Viewに変数hogeの値は[]で渡っちゃうことがあります。
なので、組み込みのnext()をうまく使うか、async.jsやstep.js等を使ってあげると楽できます。
今回はasync.jsを使ってみました。
ただそもそも非同期前提なんだしwebSocket楽に導入できるんだから、
・細かくコントローラを切って非同期で値の受け渡しをする
というのもありかなとおもいます。
その他
今回使ったライブラリは以下のとおりです。
csvパーサー
csv.js
https://github.com/wdavidw/node-csv-parser
フロー制御
async.js
https://github.com/caolan/async
oauth認証ライブラリ
everyauth
https://github.com/bnoguchi/everyauth
日付フォーマット整形
date-utils
https://github.com/JerrySievert/node-date-utils
テンプレート
jade
http://jade-lang.com/
※RailwayJSは基本はejsです。