はじめに
Rails で Backbone.js を使ってアプリを開発するときは、決まって backbone-rails を使っていた。
…んだけど、Backbone.js の v1.0.0 が出たというのに、backbone-rails は未だ対応してない(2013/07/02 現在)。
JavaScript ライブラリを gem でインストールすることに違和感あったし、
この際なんで backbone-rails にサヨナラすることに決めた。
これから JavaScript ライブラリは Bower で管理していく。
以下は移行メモ。
Bower をインストール
あらかじめ Node を homebrew や nodebrew や nvm を使ってインストールしておき、
npm install -g bower
を実行して Bower をグローバルにインストールする。
-g オプションは必須。
Rails プロジェクトのルート(RAILS_ROOT) に .bowerrc と components.json を作成する。
まず .bowerrc。
{
"directory": "vendor/assets/components",
"json": "component.json"
}
RAILS_ROOT/vendor/assets/components 下にインストールするように設定している。
次に components.json。
{
"dependencies": {
"jquery": "latest",
"jquery-ujs": "latest",
"underscore": "latest",
"backbone": "latest"
}
}
Backbone.js を Rails の csrf-token や params に対応させる、rails-backbone の backbone_rails_sync.js が
Backbone.js 1.0.0 に対応してなかった。
フォークして修正するより、Backbone.sync のソースコードをもとに実装し直したほうが早そうだったんで、
同じようなものを自作してみた。
(function($) {
var methodMap = {
'create': 'POST',
'update': 'PUT',
'patch': 'PATCH',
'delete': 'DELETE',
'read': 'GET'
};
var getUrl = function(object) {
if (!(object && object.url)) return null;
return _.isFunction(object.url) ? object.url() : object.url;
};
var urlError = function() {
throw new Error('A "url" property or function must be specified');
};
Backbone.sync = function(method, model, options) {
var type = methodMap[method];
_.defaults(options || (options = {}), {
emulateHTTP: Backbone.emulateHTTP,
emulateJSON: Backbone.emulateJSON
});
var params = { type: type, dataType: 'json' };
if (!options.url) {
params.url = getUrl(model) || urlError();
}
if (options.data == null && model && (method === 'create' || method === 'update' || method === 'patch')) {
params.contentType = 'application/json';
var data = {};
var attrs = options.attrs || model.toJSON(options);
if (model.paramRoot) {
data[model.paramRoot] = attrs;
} else {
data = attrs;
}
params.data = JSON.stringify(data);
}
if (options.emulateJSON) {
params.contentType = 'application/x-www-form-urlencoded';
params.data = params.data ? {model: params.data} : {};
}
if (options.emulateHTTP && (type === 'PUT' || type === 'DELETE' || type === 'PATCH')) {
params.type = 'POST';
if (options.emulateJSON) params.data._method = type;
var beforeSend = options.beforeSend;
options.beforeSend = function(xhr) {
xhr.setRequestHeader('X-HTTP-Method-Override', type);
if (beforeSend) return beforeSend.apply(this, arguments);
};
}
if (params.type !== 'GET' && !options.emulateJSON) {
params.processData = false;
}
if (params.type === 'PATCH' && noXhrPatch) {
params.xhr = function() {
return new ActiveXObject("Microsoft.XMLHTTP");
};
}
options.beforeSend = _.wrap(options.beforeSend, function(func, xhr) {
if (!options.noCSRF) {
var token = $('meta[name="csrf-token"]').attr('content');
if (token) {
xhr.setRequestHeader('X-CSRF-Token', token);
}
}
if (func) {
return func(this, xhr);
}
});
var xhr = options.xhr = Backbone.ajax(_.extend(params, options));
model.trigger('request', model, xhr, options);
return xhr;
};
})(Backbone.$);
あとは
bower install
を実行すると、ライブラリが RAILS_ROOT/vendor/assets/components 下にインストールされる。
アセットパイプラインで読み込めるように設定する
RAILS_ROOT/config/application.rb を編集し、アセットのパスを追加する。
config.assets.paths << Rails.root.join("vendor", "assets", "components")
app/assets/javascripts/application.js を編集し、利用するライブラリを require する。
RAILS_ROOT/app/assets/javascripts/backbone を読み込むのは、backbone-rails の慣習。
この下に Backbone.js を使ったコードを置いている。
これで Backbone.js 導入完了
あとは RAILS_ROOT/app/assets/javascripts/backbone ディレクトリの中に、Backbone.js のモデルやビューを実装していく。
まとめ
JavaScript ライブラリの管理を gem から Bower に移行した。
これでライブラリがバージョンアップしたとき素早く対応できる。
今後、新規に Rails アプリを作る場合も、JavaScript ライブラリは Bower で管理する予定。
こうなってくると、Grunt や Yeoman も導入したくなってきた。
シングルページアプリケーションでは Rails で Web API を実装し、
クライアント側は Backbone.js と Grunt や Bower や Yoeman 等を使って実装する、
って感じのクライアントとサーバーが疎結合な構成にしていきたい。