前回はEmber Dataを使って永続化されたデータを扱う方法を解説しました。今回は、
今回紹介するEmber CLIはNode.
- コードの生成
- JavaScriptのコンパイル
- テストのサポート
- デプロイ用のビルド
前準備
さっそく、
インストール
まずは前準備としてNode.
次はNode.
$ npm install -g npm
では、
$ npm install -g ember-cli
インストールが完了したら、emberコマンドが使えるようになります。本稿の対象バージョンは次の通りです。
$ ember -v version: 0.2.0 node: 0.12.0 npm: 2.7.1
ここで、
Could not find watchman, falling back to NodeWatcher for file system events
このままでも特に動作に問題はありませんが、
以上でインストールが完了しました。ここからはEmber CLIを実際に使ってみましょう。
コードの生成
Ember CLIにはコードを自動生成する機能があり、example-app
という名前でアプリケーションを作成することにします。
$ ember new example-app
生成されたファイルとディレクトリのうち特に重要な以下のものについて解説します。
app |
Ember. |
---|---|
bower. |
Bower用の依存ライブラリのバージョン管理ファイルです。JavaScriptやCSSなど、 |
config/ |
アプリケーションの設定を記述するファイルです。 |
dist | ビルドされたファイルが配置されるディレクトリです。詳しには後述します。 |
package. |
Node. |
public |
ビルドが必要ない配布用のファイルを配置するためのディレクトリです。このディレクトリに置かれたファイルは、 |
tests | テストファイルを配置するためのディレクトリです。 |
生成されたファイルについてひと通りおさえたので、
$ ember server
このコマンドで開発用のサーバが起動します。では、
次のメッセージが表示されるのが確認できたでしょうか?
Welcome to Ember.js
これでEmber CLIが生成したアプリケーションのひな形の動作を確認できたので、
次はテンプレートを作成します。Ember CLIの自動生成コマンドember generate
を利用してindex
テンプレートを作成します。
$ ember generate template index
このコマンドを実行すると、app/
というファイルが生成されます。
これまでの記事では、script
タグを利用して以下のようにテンプレートを定義してきました。
<script type="text/x-handlebars" date-template-name="index">
</script>
ただこの方法だと複数のテンプレートを定義する場合、
一方Ember CLIでは、date-template
に対応するテンプレート名がファイル名になり、app/
ディレクトリに保管されます
では、app/
を編集して表示を確認してみましょう。次の内容をテンプレートに記述してください。
Ember CLI example
そしてブラウザでアプリケーションにアクセスすると、
Welcome to Ember.js Ember CLI example
では続いてコントローラを作成してみましょう。
$ ember generate controller index
このコマンドを実行すると、
app/controllers/index.js
import Ember from 'ember';
export default Ember.Controller.extend({
});
ここで初めて登場したimport
/export
について解説します。
JavaScriptのコンパイル
この記述は、import
文で外部のモジュールを取得し、export
文でAPIを外部に公開します。とは言っても、
Ember CLIはモジュールのファイル名を手がかりに必要なクラスを探索します。この仕組みについては後ほど詳しく解説します。
まずはほかのクラスも生成しつつ全体の流れを掴んでみましょう。次はRouteです。
$ ember generate route index
Routeを生成する際はあわせてtemplateも生成されます。app/
を上書きするか確認されるので、y
をタイプしてください。
[?] Overwrite app/templates/index.hbs? Yes, overwrite
生成したファイルを次のように変更してください。
app/routes/index.js
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
return {
name: 'Tomster'
};
}
});
app/controllers/index.js
import Ember from 'ember';
export default Ember.Controller.extend({
hello: function() {
return 'Hello, ' + this.get('model.name');
}.property('model.name')
});
app/templates/index.hbs
{{hello}}!
ここまで準備ができたら、
Welcome to Ember.js Hello, Tomster!
ここまでの動きから、
module syntaxのメリット
モジュールをコンパイルするという仕組みは一見複雑に見えるかもしれませんが、
- 1. クラスがアプリケーション名に依存しなくなる
App.
だとIndexRoute App
というアプリケーションのインスタンスが存在するまでクラスを定義できません。そして、アプリケーション名が異なる別のアプリケーションで再利用できません。 - 2. クラスの定義順序を気にしなくてよくなる
あるクラスを継承して別のクラスを定義したい場合、
これまでの仕組みだと必ず親クラスの定義が先に記述される必要がありました。しかしローダを利用した仕組みでは import
によってクラスが必要になった際に読み込まれるため、クラスがファイルのどの部分で定義されているかというのは重要ではなくなります。
そのため、
$ ember install:addon <アドオン名>
ここまでで、
テストのサポート
Ember CLIはテストの実行をサポートしています。
テスト実行はテスト支援ツールであるtestemによって行われます。これには次の利点があります。
- 仮想ブラウザ/実ブラウザを含む複数のブラウザでテストが実行できる
- ソースコードの変更を検知してテストが自動で実行される
また、
ember test
シェル経由でテストを実行しTAP形式ででテスト結果を出力します。CI環境向けの実行方法です。
ember test --server
テスト用のサーバを起動して、
ファイルの変更を検知して自動でテストを実行します。手元で開発しながらテストを実行する際に適した実行方法です。
Ember CLIのデフォルトの設定では、
$ npm install -g phantomjs
では、
Unitテスト
実はさきほどクラスのひな形を生成した際、tests/
ディレクトリの中に、
ここではtests/
を見てみましょう。
import {
moduleFor,
test
} from 'ember-qunit';
moduleFor('route:index', {
// Specify the other units that are required for this test.
// needs: ['controller:foo']
});
test('it exists', function(assert) {
var route = this.subject();
assert.ok(route);
});
新しく登場した部分を解説します。
moduleFor
ember-qunit
が提供するヘルパーです。引数で指定したモジュールをセットアップします。ここではroutes/
をセットップし、index テスト実行中に this.
で取得できるようにします。subject() また、
次のオプションを指定できます。 beforeEach
:テスト実行前に行う処理を記述する関数を設定します。afterEach
:テストを実行後に行う処理を記述する関数を設定します。needs
:依存するモジュールを記述すると、テスト実行時にメインのモジュールと一緒にセットップされます。
クラスを拡張していく際、
Acceptanceテスト
次は、
例えば、
まずはテストのひな形を作成します。
$ ember generate acceptance-test post-an-article
このコマンドを実行すると、
// tests/acceptance/post-an-article-test.js
import Ember from 'ember';
import {
module,
test
} from 'qunit';
import startApp from 'example-app/tests/helpers/start-app';
var application;
module('Acceptance: PostAnArticle', {
beforeEach: function() {
application = startApp();
},
afterEach: function() {
Ember.run(application, 'destroy');
}
});
test('visiting /post-an-article', function(assert) {
visit('/post-an-article');
andThen(function() {
assert.equal(currentPath(), 'post-an-article');
});
});
ポイントは次の通りです。
beforeEach
でアプリケーションを生成afterEach
でアプリケーションを破棄することで、テストケース毎に新しいアプリケーションを利用しています。 実際にユーザが行うであろう操作をエミュレートしています。
ここでは
visit()
メソッドを使って、画面遷移を行っています。このメソッドはEmber Test Helpersによって提供されています。
Ember Test Helpersが提供するメソッドは次の通りです。
- 「アプリケーションに対して操作を行うメソッド」
次のメソッドは非同期処理で実行されます。これは、
アニメーションや画面遷移など時間のかかる操作が挟まった場合でも、 メインスレッドをブロックすることなく指定した操作を順番を行うための工夫です。
visit(url) |
引数で指定したurl に遷移します。
|
---|---|
fillIn(selector, text) |
selector で指定したDOMに対して、value 属性にtext を設定します。フォームを埋める際に利用します。
|
click(selector) |
selector で指定したDOMに対してクリックイベントを発火させます。リンクやボタンをクリックする際に利用します。
|
keyEvent(selector, type, keyCode) |
selector で指定したDOMに対してtype (mousemoveやkeypressなどの)キー操作のイベントを発火させます。その際、keyCode を指定可能です。
|
triggerEvent(selector, type, options) |
selector で指定したDOMに対してtype のイベントを発火させます。イベントはjQery. オブジェクトとして発火します。options はjQery. にそのまま渡されます。
|
- 「アプリケーションに対して確認を行うメソッド」
次のメソッドは同期処理で実行されます。アプリケーションに対しての確認は何かをブロックすることはないため、
同期的にメソッドが実行されます。
find(selector, context) |
selector で指定したDOMを取得します。context を指定すると、 |
---|---|
currentPath() | 現在のURLのパスを取得します。 |
currentRouteName() | 現在のRoute名を取得します。 |
currentURL() | 現在のURLを取得します。 |
同期実行のメソッドと非同期実行のメソッドを混在させたテストシナリオを作成する場合、
andThen(function() {
// ここで確認を行う
});
andThen
はスケジュールされている非同期処理の完了を待ってからコールバックを実行する関数です。
以上を踏まえて、
// tests/acceptance/post-an-article-test.js
import Ember from 'ember';
import {
module,
test
} from 'qunit';
import startApp from 'example-app/tests/helpers/start-app';
var application;
module('Acceptance: PostAnArticle', {
beforeEach: function() {
application = startApp();
},
afterEach: function() {
Ember.run(application, 'destroy');
}
});
test('Post an article', function(assert) {
visit('/post/new');
fillIn('input.title', 'Ember CLIについて');
fillIn('input.body', '今回はAcceptanceテストの紹介です。');
click('input.submit');
andThen(function() {
assert.equal(find('.alert-success').text(), '記事を作成しました');
});
});
以上、
デプロイ用のビルド
アプリケーションの開発とテストができるようになったので、
Ember CLIはソースコードを本番環境へのデプロイするためにビルドする機能があります。ビルドによってアプリケーションのために必要なファイルはdist
ディレクトリに配置されるので、
ソースコードのビルド
ビルドは次のコマンドで実行します。
$ ember build --environment=production
dist/
ディレクトリには次のファイルが生成されました。
$ ls dist/assets example-app-1a34084e096955fac1190e92a12cbe92.js vendor-8ddd9c2582308c7a3a851e4d8333d4f0.js example-app-d41d8cd98f00b204e9800998ecf8427e.css vendor-d41d8cd98f00b204e9800998ecf8427e.css
ここで何が起こっているのかを見ていきましょう。
- コンパイル
ソースコードがコンパイルされ、
dist/
に配置されます。ここ最近では、assets JavaScript/ CSSを直接記述せずCoffeeScriptやSassでソースコードを記述するプロジェクトも増えてきているのではないでしょうか。Ember CLIはこのような言語のコンパイルにも対応しています。また、 先ほど解説したmodule syntaxのコンパイルもこのタイミングで行われます。 - 結合
app
以下のファイルはアプリケーション名( example-app
)、app
以外から読み込んでいるファイルはvendor
という名前のファイルに結合されます。これは、ソースコードをひとつひとつ配布するよりも、 一つのファイルに結合した方がHTTPリクエストのオーバヘッドを節約できるためです [6]。
- 圧縮
配布するファイルは極力サイズを小さくすることでファイル転送にかかるコストを下げられます。Ember CLIはファイルから空白文字や改行コードの削除、
さらにはソースコードの最適化を行って極力ファイルサイズを小さくします。また、 設定によっては画像ファイルについても圧縮を行えます。 - ハッシュ値の付与
生成されたファイルにはファイルの内容のmd5チェックサムが付与されます。これはファイルをWebサーバに配置した際にキャッシュを有効にすることを意図したものです。もしファイルの中身に変更があればハッシュ値付きのファイル名も変更されます。そうすることで、
意図しないキャッシュが効いてしまうことを防いでいます。もし不要である場合はオプションで無効にできます [7]。また、 CSSの中から参照している画像のパスもハッシュ値が付与されたものに書き換えられます。
このビルドの仕組みはBroccoliというフロントエンドのためのビルドツールを利用しています。ビルドの設定を変更したい場合、Broccoli.
に設定を記述します。
以上、
最後に
今回は開発ツールであるEmer CLIを解説しました。実は今回で
今後もEmber.
それでは短い間でしたがお付き合いいただきありがとうございました。