CoffeeScript は JavaScript をベースに作成された新しいプログラミング言語です。CoffeeScript が提供する簡潔な構文は、Python や Ruby のファンであれば誰でも魅力的に感じることでしょう。さらに CoffeeScript は、Haskell や Lisp などの言語から発想を得た数多くの関数型プログラミング機能も兼ね備えています。
この連載の第 1 回では、単に JavaScript を使用する代わりに CoffeeScript を使用することによってもたらされるメリットについて学び、CoffeeScript の開発環境をセットアップしてスクリプトを実行しました。第 2 回では、数学の問題を解くために CoffeeScript が持つさまざまな機能を使用しながら、このプログラミング言語の詳細を探りました。
今回の記事では、CoffeeScript を使用してサンプル・アプリケーションを作成します。このアプリケーションでは、CoffeeScript のみを使用してクライアント・サイドのコードとサーバー・サイドのコードの両方を作成します。記事で使用するソース・コードはダウンロードすることもできます。
これから作成するサンプル・アプリケーションは、ユーザーが検索語を入力すると、Google と Twitter の両方を検索し、それぞれの結果を結合して表示するというものです。アプリケーションのクライアント・サイドは、ユーザーが入力したデータを取得してサーバーに送信します。そして、サーバーから検索結果が返されると、これらの結果に対応する UI 要素を作成して画面に表示します。とりあえず今回は、サーバー・サイドがどのように機能するかについては考えないこととします。
まずは、アプリケーションのデータ・モデルを定義するところから始めます。このアプリケーションは検索結果を表示することになっているので、SearchResult クラスを定義します。リスト 1 に、このクラスの定義を記載します。
リスト 1.
SearchResult 基底クラス
class SearchResult
constructor: (data) ->
@title = data.title
@link = data.link
@extras = data
toHtml: -> "<a href='#{@link}'>#{@title}</a>"
toJson: -> JSON.stringify @extras
|
SearchResult は比較的単純なクラスです。以下に、上記のコードの内容を説明します。
- まず、コンストラクターで
titleとlinkという 2 つのメンバー変数を定義します。 - これらの変数の値として、コンストラクターに渡されたデータ・オブジェクトのプロパティーを指定します。
- コンストラクターに渡されたデータを、上記プロパティー以外の残りのデータも含めて
extrasという名前のメンバー変数に格納します。前述のとおり、このアプリケーションでは 2 種類の検索結果 (Google での検索結果と Twitter での検索結果) を扱うので、このようにしておくと便利です。
SearchResultクラスには、以下の 2 つのメソッドを定義します。toHtml: CoffeeScript の文字列補間機能 (文字列内での式展開) を利用して、SearchResultインスタンスから単純な HTML 文字列を作成するメソッドです。toJson:SearchResultオブジェクトを JSON 文字列に変換するメソッドです。
リスト 1 のクラスは、検索結果を得るための基本機能を提供しています。Google と Twitter での検索結果から取得されるデータは非常に多いはずなので、このクラスをモデルとして検索結果のタイプごとにサブクラスを作成します。
リスト 2 に、Google での検索結果用に作成したクラスを記載します。
リスト 2.
GoogleSearchResult クラス
class GoogleSearchResult extends SearchResult
constructor: (data) ->
super data
@content = @extras.content
toHtml: ->
"#{super} <div class='snippet'>#{@content}</div>"
|
リスト 2 を見れば、CoffeeScript ではオブジェクト指向プログラミングがいかに簡易化されるかが一目瞭然です。GoogleSearchResult クラスはリスト 1 の SearchResult 基底クラスを継承しており、GoogleSearchResult クラスのコンストラクターはスーパークラスのコンストラクターを呼び出しています。JavaScript でクラス形式の継承を扱った経験がある読者は、それがどれほど厄介であるかはご存知のはずです。リスト 3 に、このクラスから生成した JavaScript を記載します。
リスト 3. JavaScript での
GoogleSearchResult クラス
GoogleSearchResult = (function() {
__extends(GoogleSearchResult, SearchResult);
function GoogleSearchResult(data) {
GoogleSearchResult.__super__.constructor.call(this, data);
this.content = this.extras.content;
}
GoogleSearchResult.prototype.toHtml = function() {
return "" + GoogleSearchResult.__super__.toHtml.apply(this, arguments)
+ " <div class='snippet'>" + this.content + "</div>";
};
return GoogleSearchResult;
})();
|
スーパークラスのコンストラクターを呼び出すには、スーパークラスのインスタンスを __super__ 変数
(スーパークラスを示す任意の名前にすることもできます) に保持している必要があり、その上でコンストラクター関数を明示的に呼び出す必要があります。リスト 2
をもう一度見てみると、CoffeeScript ではこの作業が大幅に簡易化されていることがわかるはずです。このサンプル・コードでは、GoogleSearchResult クラス内に content
という新規インスタンス変数を定義していることに注目してください。基本的に、この変数は検索結果が指す Web ページの HTML のスニペットです。したがって、この変数が
GoogleSearchResult にはあって、その基底クラスにないのは意外なことではありません。もう 1
つ注目する点は、toHtml メソッドのオーバーライドです。サンプル・コードはスーパークラスの toHtml メソッドを使用しますが、さらにコンテンツ・スニペットが含まれる div
を追加しています。スーパークラスの toHtml メソッドがどのようにして呼び出されるかは、リスト 3
を見ればわかります。GoogleSearchResult サブクラスを使用するということは、もちろん TwitterSearchResult サブクラスも必要だということです。リスト 4 に、このサブクラスを記載します。
リスト 4.
TwitterSearchResult クラス
class TwitterSearchResult extends SearchResult
constructor: (data) ->
super data
@source = @extras.from_user
@link = "http://twitter.com/#{@source}/status/#{@extras.id_str}"
@title = @extras.text
toHtml: ->
"<a href='http://twitter.com/#{@source}'>@#{@source}</a>: #{super}"
|
TwitterSearchResult クラスは、リスト 2 の GoogleSearchResult クラスと同じパターンに従います。つまり、TwitterSearchResult クラスのコンストラクターもスーパークラスのコンストラクターを利用します。TwitterSearchResult クラスのそれ以外の部分は以下のようになっています。
- このクラスに固有の
sourceという名前のメンバー変数を定義します。 - 文字列テンプレートと他のメンバー変数を使用して、
linkメンバー変数を作成します。 titleメンバー変数を入力データの別のフィールドにリセットします。- スーパークラスの
toHtmlメソッドをオーバーライドし、ツイートを作成したユーザーへのリンクを追加します。
ここでもやはり、CoffeeScript の文字列補間機能によって、新しいメソッドを作成する際に、スーパークラスの toHtml
メソッドを簡単に使用できるようになっています。スーパークラスの toHtml メソッドを呼び出すには、super を呼び出すだけでよいのです。super.toHtml を呼び出したくなるかもしれませんが、その必要はありません。むしろ実際には、super.toHtml を呼び出すとエラーが発生します。CoffeeScript では super を呼び出せば、スーパークラスの同じメソッドを呼び出すことを意味するため、プログラミングが多少楽になります。
これで、アプリケーションに必要なデータ構造は用意できたので、クライアント・サイドのロジックを作成する作業に取り掛かることができます。作成したコードをテストするには、実際に動作するバックエンドを使用した方が遥かに簡単ですが、今のところバックエンドはないため、次善策としてモック・データを使用することにします。
最近の Web アプリケーションのようなクライアント・サーバー型アプリケーションを構築する際には、多くの場合、アプリケーションを構成するクライアントとサーバーがデータを交換する場所となる共有インターフェースを作成し、その上でモック・データを作成することに意味があります。こうすることによって、アプリケーションのクライアントの部分とサーバーの部分を並行して開発することができます。この方法を採用すれば、クライアント・サイドとサーバー・サイドの両方で同じプログラミング言語を使用できることから、CoffeeScript の場合にはとりわけ有効です。リスト 5 に、Google での検索結果のモック・データを記載します。
リスト 5. Google での検索結果のモック・データ
mockGoogleData = [
GsearchResultClass:"GwebSearch",
link:"http://jashkenas.github.com/coffee-script/",
url:"http://jashkenas.github.com/coffee-script/",
visibleUrl:"jashkenas.github.com",
cacheUrl:"http://www.google.com/search?q\u003dcache:nuWrlCK4-v4J:jashkenas
.github.com",
title:"\u003cb\u003eCoffeeScript\u003c/b\u003e",
titleNoFormatting:"CoffeeScript",
content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a little language that
compiles into JavaScript. Underneath all of those embarrassing braces and
semicolons, JavaScript has always had a \u003cb\u003e...\u003c/b\u003e"
,
GsearchResultClass:"GwebSearch",
link:"http://en.wikipedia.org/wiki/CoffeeScript",
url:"http://en.wikipedia.org/wiki/CoffeeScript",
visibleUrl:"en.wikipedia.org",
cacheUrl:"http://www.google.com/search?q\u003dcache:wshlXQEIrhIJ
:en.wikipedia.org",
title:"\u003cb\u003eCoffeeScript\u003c/b\u003e - Wikipedia, the free
encyclopedia",
titleNoFormatting:"CoffeeScript - Wikipedia, the free encyclopedia",
content:"\u003cb\u003eCoffeeScript\u003c/b\u003e is a programming language
that transcompiles to JavaScript. The language adds syntactic sugar inspired by
Ruby, Python and Haskell to enhance \u003cb\u003e...\u003c/b\u003e"
,
GsearchResultClass:"GwebSearch",
link:"http://codelikebozo.com/why-im-switching-to-coffeescript",
url:"http://codelikebozo.com/why-im-switching-to-coffeescript",
visibleUrl:"codelikebozo.com",
cacheUrl:"http://www.google.com/search?q\u003dcache:VDKirttkw30J:
codelikebozo.com",
title:"Why I\u0026#39;m (Finally) Switching to \u003cb\u003eCoffeeScript
\u003c/b\u003e - Code Like Bozo",
titleNoFormatting:"Why I\u0026#39;m (Finally) Switching to CoffeeScript -
Code Like Bozo",
content:"Sep 5, 2011 \u003cb\u003e...\u003c/b\u003e You may have already heard
about \u003cb\u003eCoffeeScript\u003c/b\u003e and some of the hype surrounding it
but you still have found several reasons to not make the \u003cb\u003e...
\u003c/b\u003e"
]
|
このように、CoffeeScript の単純な構文を使用すれば、モック・データでさえも簡単に作成できることは明らかです。上記の例は、CoffeeScript のオブジェクト・リテラルを示しています。リスト 5 は配列となっていて、オブジェクトを示すためにインデントが追加されており、オブジェクトの各プロパティーを示すために、さらにインデントが追加されています。このコードは JSON に勝るとも劣らぬほど簡潔で、JSON における波括弧の代わりにホワイトスペースが使われています。CoffeeScript のオブジェクト・リテラルは JavaScript のリテラルに似ており、プロパティーは引用符で囲まれません。JSON では、これらのプロパティーも引用符で囲む必要があります。
同じく Twitter での検索結果のモック・データをリスト 6 に記載します。
リスト 6. Twitter での検索結果のモック・データ
mockTwitterData = [
created_at:"Wed, 09 Nov 2011 04:18:49 +0000",
from_user:"jashkenas",
from_user_id:123323498,
from_user_id_str:"123323498",
geo:null,
id:134122748057370625,
id_str:"134122748057370625",
iso_language_code:"en",
metadata:
recent_retweets:4,
result_type:"popular"
profile_image_url:"http://a3.twimg.com/profile_images/1185870726/gravatar
_normal.jpg",
source:"<a href="http://itunes.apple.com/us/app/twitter/id409789998?mt
=12" rel="nofollow">Twitter for Mac</a>",
text:""CoffeeScript [is] the closest I felt to the power I had twenty
years ago in Smalltalk" - Ward Cunningham (http://t.co/2Wve2V4l) Nice.",
to_user_id:null,
to_user_id_str:null
]
|
リスト 6 のモック・データはリスト 5 のモック・データに似ていますが、ここには Twitter 固有のフィールドがあります。この他に必要なことは、このモック・データを返すインターフェースを作成することのみです。リスト 7 に、モック・データを返すことだけを目的としたもう 1 つのクラスを記載します。
リスト 7. モック検索エンジン・クラス
class MockSearch
search: (query, callback) ->
results =
google: (new GoogleSearchResult obj for obj in mockGoogleData)
twitter: (new TwitterSearchResult obj for obj in mockTwitterData)
callback results
|
上記の MockSearch クラスには唯一のメソッドとして search という名前のメソッドがあります。この
search メソッドは引数を 2 つ取ります。その 2 つとは検索に使用する query と、callback 関数です。MockSearch は迅速に検索結果を返しますが、実際の検索はネットワークを介してサーバーと対話しなければなりません。この処理を JavaScript で行い、しかも UI がフリーズしないようにするためには、通常はコールバック関数を使用することになります。
次のステップでは、results というオブジェクトを作成します。そのために使用しているのも、同じく
CoffeeScript のオブジェクト・リテラル構文です。results オブジェクトには、google と twitter という 2
つのフィールドがあります。それぞれのフィールドの値は、配列内包を使用して表現されます。この表現により、該当するタイプの SearchResult (Google の場合は GoogleSearchResult、Twitter の場合は TwitterSearchResult) の配列が作成されます。そして最後に、callback 関数が呼び出されて、results オブジェクトがこの関数に渡されます。
モック検索が機能するようになったので、次は UI に関連したクライアント・サイドのコードを作成します。
UI をアプリケーション・コードに関連付ける前に、使用する UI について調べておきましょう。リスト 8 に記載するのは、かなり単純な Web ページです。
リスト 8. CoffeeSearch Web ページ
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>CoffeeSearch</title>
<script type="text/javascript" src="search.js"></script>
</head>
<body>
<div>
<label for="searchQuery">Keyword:</label>
<input type="text" name="searchQuery" id="searchQuery"></input>
<input type="button" value="Search" onclick="doSearch()"/>
</div>
<div class="goog" id="gr"/>
<div class="twit" id="tr"/>
</body>
</html>
|
上記の Web ページには、キーワードを入力して検索エンジンに渡すための基本フォームがあります。ここに定義されている 2
つのセクションには、検索結果を追加できるようになっています。この Web ページに JavaScript は定義されていません。代わりに、ページ全体が search.js
というファイルに収容され、このファイルが、CoffeeScript をコンパイルした JavaScript ファイルとなります。検索ボタンをクリックすると、doSearch という関数が呼び出されることに注目してください。この関数は search.js ファイルに含まれていなければなりません。search.js ファイルでまだ目にしていないのは、この関数だけです。リスト 9 に、CoffeeScript でのこの関数の定義を記載します。
リスト 9. Web ページの
doSearch 関数
@doSearch = ->
$ = (id) -> document.getElementById(id)
kw = $("searchQuery").value
appender = (id, data) ->
data.forEach (x) ->
$(id).innerHTML += "<p>#{x.toHtml()}</p>"
ms = new MockSearch
ms.search kw, (results) ->
appender("gr", results.google)
appender("tr", results.twitter)
|
お気付きかもしれませんが、この関数の先頭には @ 記号 (this のショートカット)
が追加されています。スクリプトの最上位レベルに定義された this は、グローバル・オブジェクトになります。Web
ページ内のスクリプトの場合、グローバル・オブジェクトはすなわちウィンドウ・オブジェクトなので、リスト 8 に記載した Web ページ内で参照することができます。
doSearch 関数は、わずか数行のコードでさまざまな処理を行います。コードの内容を以下に説明します。
$というローカル関数を定義します。これは基本的に、常に有用なdocument.getElementById関数のショートカットです。このローカル関数を使用して、リスト 8 の検索フォームに入力されたキーワードを取得します。appenderという別のローカル関数を定義します。この関数は、DOM 内の要素の ID と配列を引数に取ると、配列を繰り返し処理して HTML 文字列を作成し、作成した HTML 文字列をその特定の ID を持つ要素に追加します。MockSearchオブジェクトを作成し、このオブジェクトのsearchメソッドを呼び出します。- フォームから取得したキーワードを渡して、コールバック関数を作成します。
コールバック関数は
appenderを使用して、Google からの検索結果を一方のdivに追加し、Twitter からの検索結果をもう一方の div に追加します。
後は、このコード全体をコンパイルして、デプロイするだけです。図 1 に、モック・データを使用した Web ページを示します。
図 1. モック・データを使用した検索ページ
この例は、あまり印象的なものではないかもしれませんが、クライアント・サイドで必要なすべての関数を明らかにしています。サーバー・サイドのコードはまだ作成していませんが、サーバー・サイドのコードがモック・データと同じように構造化されたデータを生成する限り、アプリケーションが機能することにかなりの確信を持てるはずです。
連載のこれまでの記事では CoffeeScript の基本を探ってきましたが、この記事では CoffeeScript を使用して実際のアプリケーションを構築する作業に取り掛かりました。CoffeeScript を node.js と組み合わせれば、クライアント・サイドとサーバー・サイドの両方で同じ簡潔なプログラミング言語を使用して完全なアプリケーションを作成することができます。連載の最終回でこのサンプル・アプリケーションのサーバー・サイドを作成するときには、今回作成したコードの一部を再利用できるはずです。次回をお楽しみに。
| 内容 | ファイル名 | サイズ | ダウンロード形式 |
|---|---|---|---|
| Article source code | cs3.zip | 5KB | HTTP |
学ぶために
- この連載のこれまでの記事を読んでください。
- Node.js/: このアプリケーションについて詳しく学ぶには、ここが出発点となります。
- 「Node.js
とは一体何か?」(developerWorks、2011年5月) では、なぜ Node.js がサーバーの動作に関する概念を変えるサーバー・サイドの JavaScript インタープリターとなるかを説明しています。
- 「Node.js
をクラウド環境での開発用のフルスタックとして使用する」(developerWorks、2011年4月) で、Node.js をクラウド技術と組み合わせる方法を詳しく説明しています。
- 「Google
Web Toolkit と Eclipse Galileo を使ったハイパフォーマンスの Web 開発」(developerWorks、2009年10月): JavaScript にコンパイルするという考えは、新しいものではありません。Java プログラミング言語のファンにとって、この記事は必読です。
- 「出発進行!
Rails 3 の紹介」(developerWorks、2010年3月) を読むと、CoffeeScript が Ruby on Rails にどのように組み込まれているかがわかります。この記事で、Rails に追加されたその他の新機能を調べてください。
- Github での CoffeeScript プロジェクトの説明: CoffeeScript について学ぶには、ここが出発点となります。
- 「モバイル Web
用の Ajax アプリケーションを作成する」(developerWorks、2010年3月) で、Ajax をモバイル Web アプリケーションを使用する方法について詳しく説明しています。
- developerWorks の Technical events
and webcasts: これらのセッションで最新情報を入手してください。
- Twitter での
developerWorks: 今すぐ登録して developerWorks のツイートをフォローしてください。
製品や技術を入手するために
- Node.js: Node.js をダウンロードしてください。スケーラブルなネットワーク・プログラムを容易に構築できるようになります。
- IBM 製品の評価版のダウンロード:DB2、Lotus、Rational、Tivoli、および
WebSphere のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードするか、IBM SOA Sandbox のオンライン試用版を試してみてください。
議論するために
- developerWorks
プロフィール: 今すぐ自分のプロフィールを作成し、ウォッチ・リストを設定してください。
- developerWorks コミュニティー: ここでは他の developerWorks ユーザーとのつながりを持てる他、開発者によるブログ、フォーラム、グループ、ウィキを調べることができます。

Michael Galpin は、Google のソフトウェア・エンジニアです。彼は、『Android in iPractice』の共著者であり、developerWork にも頻繁に寄稿しています。彼が次に取り組もうとしている内容をこっそり覗くには、彼のブログを調べるか、Twitter の @michaelg または Google の +Michael Galpin で彼をフォローしてください。