CoffeeScript の最初の一杯: 第 3 回 クライアントで CoffeeScript を使用する

この連載では、JavaScript をベースに作成された人気の高い CoffeeScript プログラミング言語について詳しく探ります。CoffeeScript は、多くのベスト・プラクティスに従った効率的な JavaScript コードにコンパイルされます。この JavaScript コードは Web ブラウザーで実行することも、Node.js などのサーバー・アプリケーション用の技術で使用することもできます。連載の第 1 回では CoffeeScript の導入方法と、このプログラミング言語が開発者にもたらすメリットについて説明し、第 2 回では CoffeeScript を使用していくつかのプログラミング問題を解く方法を説明しました。今回の記事では、CoffeeScript を使用して完全なアプリケーションを作成する方法を説明します。

Michael Galpin, Software Engineer, Google

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



2012年 3月 15日

はじめに

CoffeeScript は JavaScript をベースに作成された新しいプログラミング言語です。CoffeeScript が提供する簡潔な構文は、Python や Ruby のファンであれば誰でも魅力的に感じることでしょう。さらに CoffeeScript は、Haskell や Lisp などの言語から発想を得た数多くの関数型プログラミング機能も兼ね備えています。

この連載の第 1 回では、単に JavaScript を使用する代わりに CoffeeScript を使用することによってもたらされるメリットについて学び、CoffeeScript の開発環境をセットアップしてスクリプトを実行しました。第 2 回では、数学の問題を解くために CoffeeScript が持つさまざまな機能を使用しながら、このプログラミング言語の詳細を探りました。

今回の記事では、CoffeeScript を使用してサンプル・アプリケーションを作成します。このアプリケーションでは、CoffeeScript のみを使用してクライアント・サイドのコードとサーバー・サイドのコードの両方を作成します。記事で使用するソース・コードはダウンロードすることもできます。


CoffeeScript で作成する検索アプリケーション

頻繁に使用される略語

  • JSON: JavaScript Object Notation
  • UI: User Interface

これから作成するサンプル・アプリケーションは、ユーザーが検索語を入力すると、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 は比較的単純なクラスです。以下に、上記のコードの内容を説明します。

  • まず、コンストラクターで titlelink という 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 クラスはリスト 1SearchResult 基底クラスを継承しており、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 クラスは、リスト 2GoogleSearchResult クラスと同じパターンに従います。つまり、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&quot; 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 オブジェクトには、googletwitter という 2 つのフィールドがあります。それぞれのフィールドの値は、配列内包を使用して表現されます。この表現により、該当するタイプの SearchResult (Google の場合は GoogleSearchResult、Twitter の場合は TwitterSearchResult) の配列が作成されます。そして最後に、callback 関数が呼び出されて、results オブジェクトがこの関数に渡されます。

モック検索が機能するようになったので、次は UI に関連したクライアント・サイドのコードを作成します。


ブラウザーですべてを 1 つに統合する

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 codecs3.zip5KB

参考文献

学ぶために

製品や技術を入手するために

  • Node.js: Node.js をダウンロードしてください。スケーラブルなネットワーク・プログラムを容易に構築できるようになります。
  • IBM 製品の評価版のダウンロード:DB2、Lotus、Rational、Tivoli、および WebSphere のアプリケーション開発ツールとミドルウェア製品を体験するには、評価版をダウンロードするか、IBM SOA Sandbox のオンライン試用版を試してみてください。

議論するために

コメント

developerWorks: サイン・イン

必須フィールドは(*)で示されます。


IBM ID が必要ですか?
IBM IDをお忘れですか?


パスワードをお忘れですか?
パスワードの変更

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


お客様が developerWorks に初めてサインインすると、お客様のプロフィールが作成されます。会社名を非表示とする選択を行わない限り、プロフィール内の情報(名前、国/地域や会社名)は公開され、投稿するコンテンツと一緒に表示されますが、いつでもこれらの情報を更新できます。

送信されたすべての情報は安全です。

ディスプレイ・ネームを選択してください



developerWorks に初めてサインインするとプロフィールが作成されますので、その際にディスプレイ・ネームを選択する必要があります。ディスプレイ・ネームは、お客様が developerWorks に投稿するコンテンツと一緒に表示されます。

ディスプレイ・ネームは、3文字から31文字の範囲で指定し、かつ developerWorks コミュニティーでユニークである必要があります。また、プライバシー上の理由でお客様の電子メール・アドレスは使用しないでください。

必須フィールドは(*)で示されます。

3文字から31文字の範囲で指定し

「送信する」をクリックすることにより、お客様は developerWorks のご使用条件に同意したことになります。 ご使用条件を読む

 


送信されたすべての情報は安全です。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=60
Zone=Web development
ArticleID=801287
ArticleTitle=CoffeeScript の最初の一杯: 第 3 回 クライアントで CoffeeScript を使用する
publish-date=03152012