Sooey

2011-08-01 12:10:27 +0900

Asset Pipelineを備えたRails 3.1のリリースに向けて、その根幹を担うSprocketsをちょっと触っておくことにした。

SprocketsはRubyで書かれたJavaScriptプリプロセッサで、複数のJavaScriptソースファイルを1つにまとめるのが主な機能。それによって以下のようなメリットがある。

  • 複数のサイトやアプリケーションで共通するコードを再利用可能な形で切り出した構成にできる
    • JavaScriptプラグインという単位でCSSや画像もまとめることができる
  • JavaScriptファイルを1つにまとめてブラウザに読ませることによる高速化
    • HTTPリクエストの回数を減らすことができる
  • JavaScriptをコメント付きの複数のファイルおよびディレクトリとして整理できる
    • ソースコードをサイトやアプリケーションのルートディレクトリ外で管理することもできる
    • コメントは最終的に除去されてブラウザに渡されるため、沢山のコメントを書いても問題にならない

インストール

今回はRails 3.1には触れないので、Sprocketsを普通にgemとしてインストールする。

$ gem install sprockets
Fetching: sprockets-1.0.2.gem (100%)
Successfully installed sprockets-1.0.2
1 gem installed

インストールが完了するとsprocketizeコマンドが利用できるようになる。

$ sprocketize
Usage: sprocketize [options] filename [filename ...]
    -C, --directory=DIRECTORY    Change to DIRECTORY before doing anything
    -I, --include-dir=DIRECTORY  Adds the directory to the Sprockets load path
    -a, --asset-root=DIRECTORY   Copy provided assets into DIRECTORY
    -h, --help                   Shows this help message

Sprockets向けの記法

Sprocketsで処理されるJavaScriptでは、requireprovideという2つのディレクティブを使用することができるほか、YAMLファイルで定義した定数値を埋め込むことができる。

コメント

Sprocketsではソースファイル内のコメントを以下のように扱う。

  • //=で始まるコメントはSprocketsのディレクティブとして扱われる
  • 結合後のファイルには含まれないコメント
    • ソースファイル中の//コメント
    • /** ... **/のようなPDocコメント(PDocはprototype.jsなどで使用されているAPIドキュメント用の記法&ツール)
  • 結合後のファイルにも含まれるコメント
    • ソースファイル中の/* ... */コメント

requireディレクティブ

requireディレクティブを使うと、指定したファイルがその位置に展開された状態で結合後のファイルへの書き出しが行われる。指定したファイルが既にrequiredで処理されていた場合は何もしない。

// ロードパス内にあるjquery.jsを読み込む
//= require <jquery>

ファイル名を<...>で指定した場合は、ロードパス内を順にたどってjquery.jsを探し、最初に見つかったファイルが処理対象となる。ファイルが見つからない場合はエラーが発生する。

// 同じディレクトリにあるjquery.jsを読み込む
//= require "jquery"

ファイル名を"..."で指定した場合は、ロードパスの探索は行われず、処理中のファイルと同じディレクトリにあるjquery.jsが探される。

外部のパッケージやライブラリ、プラグインなどを読む場合は<...>を使い、プロジェクト内のソースファイルを読む場合は"..."を使うのが一般的な使い方のようだ。

サブディレクトリ内のソースファイルを読む場合は以下のように指定する。

//= require <foolib/foo>

この場合は、ロードパスの中でfoolibというディレクトリ内にあるfoo.jsというファイルが探される。

provideディレクティブ

CSSや画像、HTMLなどのアセットファイルをJavaScriptプラグインと関連付けるためにはprovideディレクティブを使用する。

JavaScriptファイル内に記述したprovideディレクティブは、現在のJavaScriptファイルがアセットファイルに依存することを示し、JavaScriptファイルに関連するファイル群を一括してドキュメントルートにコピーする機能が利用できるようになる。

例えば、以下のようなディレクトリ構造で、4つの画像ファイル、1つのCSSファイル、2つのJavaScriptファイルを含むcolor_pickerプラグインの場合、

plugins/color_picker/assets/images/color_picker/arrow.png
plugins/color_picker/assets/images/color_picker/circle.png
plugins/color_picker/assets/images/color_picker/hue.png
plugins/color_picker/assets/images/color_picker/saturation_and_brightness.png
plugins/color_picker/assets/stylesheets/color_picker.css
plugins/color_picker/src/color.js
plugins/color_picker/src/color_picker.js

Sprocketsのロードパスにはplugins/color_picker/src/が含まれるようになるという前提で考え、plugins/color_picker/src/color_picker.jsファイルには以下のようなディレクティブを記述する。

//= require "color"
//= provide "../assets"

こうすると、あるアプリケーション内のJavaScriptファイルにrequire <color_picker>という記述があった場合に、Sprocketsによるプラグインの参照が行われ、provideディレクティブによって明示されたアセットファイルがそのアプリケーションのドキュメントルートにコピーされる(ドキュメントルートのimagesおよびstylesheets以下にコピーされる)。

定数の展開

constants.ymlというYAMLファイルで定義した定数を、<%= ... %>という記法で展開することができる。

例えばcolor_pickerプラグインの場合、plugins/color_picker/src/constants.ymlというファイルに以下のような定数の定義を書き、

COLOR_PICKER_VERSION: 1.0.0
COLOR_PICKER_AUTHOR: Sam Stephenson <sam@example.org>

JavaScriptからは<%= ... %>で参照する。

/* Color Picker plugin, version <%= COLOR_PICKER_VERSION %>
 * (c) 2009 <%= COLOR_PICKER_AUTHOR %>
 * Distributed under the terms of an MIT-style license */

var ColorPicker = {
  VERSION: '<%= COLOR_PICKER_VERSION %>',
  ...
};

定数名はグローバルに扱われ、ロードパス内で参照できるので注意。指定された定数が見つからない場合は、エラーが発生してプリプロセス処理は中断する。

つかいかた

Sprockets本体はRubyライブラリになっていて、それを利用した以下のようなツールが同梱されている。

  • コマンドラインツール(sprocketize
  • Railsプラグイン(sprockets-rails
  • その他の環境用のCGIスクリプト

今回は、コマンドラインツールとして呼び出すパターンと、ライブラリとしてRubyのコードから呼び出すパターンを試してみる。

コマンドラインから使う

sprocketizeコマンドは引数にソースファイルとオプションを指定すると、ソースファイルを結合したものを出力するので、それをファイルにリダイレクトして保存します。

コマンドラインオプション。

-C, --directory=DIRECTORY    処理前にディレクトリを移動する
-I, --include-dir=DIRECTORY  ロードパスにディレクトリを追加する
-a, --asset-root=DIRECTORY   アセットをディレクトリにコピーする
-h, --help                   ヘルプメッセージを表示する
-v, --version                バージョンを表示する

使用例。

$ sprocketize -I app/javascripts \
              -I vendor/sprockets/prototype/src \
              -I vendor/sprockets/color_picker/src \
              --asset-root=public \
              app/javascripts/*.js > public/sprockets.js

上記のコマンドでは、app/javascripts/*.jsが1つにまとめてpublic/sprockets.jsとして保存される。その際、

  • app/javascripts
  • vendor/sprockets/prototype/src
  • vendor/sprockets/color_picker/src

の3つのディレクトリがロードパスに追加され、requireディレクティブで参照されるファイルの探索が行われる。また、requireで読み込んだファイル中にprovideディレクティブが記述されていた場合は、その値に応じたアセットファイルがpublic以下にコピーされる。

ライブラリとして使う

RubyのコードからSprocketsを使用する場合は、以下のようにSprockets::SecretaryクラスとSprockets::Concatenationクラスを利用する。

# オプションを指定してSprockets::Secretaryオブジェクトを生成する
secretary = Sprockets::Secretary.new(
  :asset_root   => "public",
  :load_path    => ["vendor/sprockets/*/src", "vendor/plugins/*/javascripts"],
  :source_files => ["app/javascripts/application.js", "app/javascripts/**/*.js"]
)

# ソースファイルの結合を行うSprockets::Concatenationオブジェクトを生成する
concatenation = secretary.concatenation

# 結合結果をファイルに保存する
concatenation.save_to("public/sprockets.js")

# アセットファイルをコピーする
secretary.install_assets

Sprockets::Secretary.newには以下のようなオプションが指定できる。

  • :root
    • ロードパスやソースファイルとして指定されるディレクトリの起点となる場所
    • デフォルトはカレントディレクトリ(.
  • :asset_root
    • アプリケーションのドキュメントルートまたはWebサーバーがファイルを配信するディレクトリ
  • :load_path
    • JavaScriptソースファイルを探す際に対象となるディレクトリ名の配列
    • 絶対パスまたは相対パス(相対パスの場合は:rootからの相対指定となる)
    • 配列に格納された順に探索が行われる
  • :source_files
    • 処理対象のJavaScriptソースファイルの配列
    • 絶対パスまたは相対パス(相対パスの場合は:rootからの相対指定となる)
    • 配列に格納された順に1つずつ処理が行われる
  • :expand_paths
    • load_pathおよびsource_filesに含まれるパスをシェルのグロブルールで展開するかどうかをtruefalseで指定する
    • :load_path => ["vendor/sprockets/*/src"]などが展開されるかどうか
    • デフォルトはtrue

まとめ

Sprockets自体はprototype.jsなどのパッケージングでも利用されており、RubyやRailsとは無関係のプロジェクトでも重宝しそう。Rails 3.1での利用が前提ならば、実行環境はおいといてとりあえずディレクティブの働きだけを理解すればよさそう。

2011-07-19 18:56:01 +0900

HerokuのAPIを叩くのにはユーザー名とパスワードを送る必要があると思っていたのですが、いつの間にかHerokuのMy AccountページにAPIキーが表示されるようになっていて、herokuコマンドもAPIキーを利用する形になっていたようです(知らなかった)。

自分のマシンの~/.heroku/credentialsには今もユーザー名とパスワードがベタに格納されている状態だったので、いったんcredentialsを削除してからherokuコマンドを呼んでユーザー名とパスワードを入力したところ、credentialsにはユーザー名とAPIキーが格納されるようになりました。

$ rm ~/.heroku/credentials
$ heroku list

2011-07-08 02:21:58 +0900

7月7日に、MTL(Media Technology Labs)さんの素敵な会場をお借りしてHeroku-ja Meetup #1を開催しました。

Heroku-ja Meetup #1 | Flickr - Photo Sharing!

会場提供&Ustream配信などでサポートしていただいたMTLの@kawanetさんと@hiroohiさん、アバウトな時間配分だったにも関わらず絶妙なペース配分でトークしてくださったスピーカーのみなさん(@mitsuhiroさん、@pwimさん、@mat_akiさん、@june29さん、@komagataさん、@yebiharaさん)、そして参加していただいたみなさん、どうもありがとうございました。フィーリングで計画・運営したわりに実り多いイベントになり、とても面白かったです。参加者は総勢55名でした。

今回のトークは以下のような内容でした(スライドは順次公開されると思いますが、とりあえずURLのわかるものだけリンクしておきます)。

みなさんのトークを聞いて「みんな結構Herokuを使い込んでいるなー」と改めて思いました。Herokuを使うような人たちは何か問題が起こっても自己解決できちゃうスキルの人が多く、生活の知恵があんまり表に出てこなくてもったいないので今回のように気軽に情報交換できる場を増やしていきたいですね。

後半はビアバッシュとかBeerTalkというノリで、参加者同士あちこちで色々交流する時間でした。

こういうイベントでは勉強会+懇親会という流れが多いと思うのですが、そうするとどうしても「発表する人」と「聞きに来るお客さん」に意識が分かれてしまい、懇親会はコアな人だけの集まり…となってしまいがちな印象があります(or 勇気を出して参加したけど隅っこでカナッペをつまむ的な)。もちろんそれがハマるコミュニティもあるとは思うのですが、個人的にHeroku&Herokuユーザーには参加者同士での情報交換とかコミュニケーションをメインに集まって欲しかったのと、Heroku好きにはそれがしっくり来る雰囲気もあるんじゃないかなと感じていたので今回のようなシームレスにご歓談に移行する流れにしたのでした。

このやり方にもいい面悪い面があると思いますし、どの会場でもできるわけではないのですが(今回MTLさんにお願いした理由の1つがそれ)、みなさん話が弾まれていたようでホッとしました。ただ、まだHerokuユーザーでない方や普段あまりコミュニティに参加したことのない方へのフォローがもう少し出来ればよかったかな、という点で課題は残りました。

今回、私がHerokuのユーザーMeetupをやりたいなーと思った理由は、平たく言うと「2時間とか3時間ずっとHerokuの話をしたい・聞きたい」というそれだけです。Rubyにもクラウドにも活発な素晴らしいコミュニティはたくさんありますが、

  • Rubyの集まりだとHerokuユーザーじゃない人もいる
  • PaaSの集まりだとGAEユーザーやAzureユーザーもいる
  • クラウドの集まりだとプログラマーじゃない人もいる

というように、ずっと「Heroku、Heroku」とだけ言ってるわけにはいきません。それが、この日は「Dyno」や「Worker」だけでフツーに話が盛り上がるという実に素晴らしいヘンな空間だったわけで、いろいろヤバかったですね。

最後に、セールスフォース・ドットコムの@mitsuhiroさんからは、なかなか知ることができないHerokuの社内やワークスタイルなどを紹介していただき、またHerokuのノベルティもゲットして来られるなど、イベントを盛り上げるためにご尽力いただいてとても助かりました!ありがとうございました。

Herokuの買収発表以降、セールスフォースさんは本国でも日本でも「セールスフォースの文化とHerokuの文化のすり合わせ」にとても気を配っているんだなあとは薄々感じていましたが、今回@mitsuhiroさんとお話させていただいて感じたのは「Heroku社やHerokuユーザーコミュニティに配慮したほうがよいことがある」ということを理屈じゃなくて肌感覚でわかっている方だなあということでした。ユーザーコミュニティに絶妙な距離感で接することができる方がいるというのは、Heroku社にとってもコミュニティにとってもすごく幸運なことじゃないでしょうか。

後半褒め殺しみたいになってしまいましたが、この1日でHerokuがより好きになったというお話でした。

※日本語でHerokuの情報交換したい!という方はHeroku-ja - Google グループにもぜひご参加ください。

2011-07-06 00:02:27 +0900

ラグジュアリーなWebサービスの可能性。

TAG Heuer、€4700(約55万円)からの高級Androidスマートフォン『LINK』を発表 - ガジェット通信という記事を見て思いましたが、「選ばれたエグゼクティブ達に贈るラグジュアリーなWebサービス」というアプローチはどうでしょう。

『洗練された操作性に優美なフィードバックを備えたワンランク上のTodoアプリが月額15万円』とかやったらひょっとして売れるかもしれない。