Life is TraversableOnce

programming / Java / Scala / Rust

アドバンス!

今日は、応用情報処理技術者試験(だったかな?)の日です!

近頃はひょんなことから足を踏み入れたScala.jsが思いの外たのしくて、そのドキュメントをずっと追いかけています。 Skinnyも追いかけないと……。

いま一番欲しいものは、ToolBoxと同じ方法で文字列をScalaのASTにパースしてくれるライブラリです……。

謎が解けました

1ヶ月近く開いてしまった……。

messages.confのuser.nameプロパティがOSのカレントログインユーザ名になる現象、TypesafeConfigReaderがシステムプロパティを引っ張ってきているようですね。

Typesafe Configは設定値の継承やオーバーライドができたはずなので、回避策がありそうです。

Skinnyチュートリアルは、3章についてはカバーしているので、次は5章へ進みたいと思います。

Skinny Tutorial #4 机上のクーロン(2)

関係のないおはなし

はてなブログの記事を書くときは主にiPhone版のアプリを利用しています。

統合テスト

今日も帰りの電車の中からコードを書いていこうと思います。 前回作成したStaticPagesControllerにViewを用意する前に、コントローラの統合テストを済ませてしまいましょう。

Skinny Frameworkのテストでは、テスティングフレームワークであるScalaTestを採用した書き方を推奨しているようです。 scaffoldで自動生成したテストスイートの場合、ソースファイル1件ごとに対する単体テストでは、ScalaTestのFunSpecクラスにMatcherトレイトをミックスインしたテストコードを基本としています。Matcherを使うことで、テストの合否を自然言語に近いかたちで書くことができます。

ですが今回は、Railsチュートリアルでも統合テスト(integration test)を題材としていますので、単体テストは省略して統合テストに進んでしまいましょう。

統合テストでは、アプリケーションの機能に焦点を当て、アーキテクチャの各層、クラス間の連携も考慮して、アプリケーションからの出力を検証しています。

何はともあれ、公式ドキュメントを参考に、静的なコンテンツであるHomeページが表示されることを検証するテストを書いてみましょう。

class StaticPagesIntegrationTest extends ScalatraFlatSpec with SkinnyTestSupport {
  addFilter(Controllers.staticPages, "/*")

  it "should show static home page" in {
    get("/staticpages/home") {
      status should equal(200)
      body should include("Home")
    }
  }
}

上記のコードでは、ステータスコードが200(OK)であること、レスポンスボディにHomeの文字列が含まれていることを検証しています。 RailsにおけるRspecでのテストコードにかなり近いフィーリングで記述できているように思います。

使用しているコンポーネントを見てみましょう。

スーパークラスScalatraFlatSpecクラスは、ScalaTestのテスト記述スタイルのひとつであるFlatSpecをScalatraから利用しやすくするラッパーで、Scalatraが提供しているクラスです。ScalaTestではテストをさまざまなスタイルで書くことができるように、スタイルごとのトレイトがたくさん用意されていますが、FlatSpecは振る舞い駆動開発の入門者におすすめの記法だそうです。

ミックスインしているSkinnyTestSupportはSkinnyのユーティリティトレイトで、今回はあまり関係ありませんが、テストコードからセッションスコープに情報を注入する機能を提供します。

このテストをこのまま実行しても、当然合格はできません。

TODO テストの結果を追記すること

レスポンスボディに要求された文字列を追加するべく、Viewの準備を進めていきます。

sspとレイアウト

Railsチュートリアルの第3章では、かなり後半になるまで登場しませんが、Viewテンプレートでコンポーネントを共通化し、テンプレート間で流用できるようにするレイアウトというものがあり、Skinny(Scalate)にもこのしくみが存在しています。

view/layout/フォルダ以下にあるdefault.sspが、sspテンプレートの場合のレイアウトを定義しているファイルです。 アクションで描画される内容は、このレイアウトの中に流し込まれることになります。

そのため、homeページに必要な内容はそれほど多くありません。まだコントローラから値の受け渡しなどもしないので、単純に必要な項目だけを実装しましょう。

ルーティングから期待されるファイル名は、view/staticpages/home.html.sspです。

<h3>Home</h3>
<p>
  Skinny Tutorial サンプルアプリケーション
</p>

動的なページへ

例題に沿って、このHomeページのタイトルをコントローラから変数として受け取るように改造していきます。

今回の例では、タイトルは個々のテンプレートではなく、レイアウトテンプレートで定義されています。Scalateには、個別のテンプレートの内部からレイアウトの属性を変更するメソッドも用意されていますが、わたしのアプリケーションではdefault.sspを直接変更してしまうことにします。

<%@ val body: String %>
<%@ val title: String %>
<html>
<head>
  <title>Skinny Tutorial Sample | ${title}</title>
</head>
<body>
  ...

  ${unescape(body)}

  ...
</body>
</html>

上記はScalateのサンプルをほとんどそのまま書き写したものですが、Skinnyのgenerateで生成されるレイアウトも、Bootstrapを読み込むためのscriptタグがあるだけで、同じような内容になっているはずです。

ビューを修正したので、コントローラからリクエストスコープで参照できる値を設定し、テンプレートに引き渡す必要があります。 Homeページのためのアクションに、タイトルを指定する文を追加します。

class StaticPagesController extends ApplicationController {
  def home = {
    set("title", "Home")
    render("staticpages/home")
  }
  ...
}

(つづく)

Skinny Tutorial #3 机上のクーロン(1)

ほぼ静的なページ

移動中に時間が取れそうなので、机上でのシミュレーションになりますが、第3章へトライしていきましょう。 実際のアプリケーションでの動作確認も、もちろん追記していきますよ!

あたらしいプロジェクトを用意して、まずは静的なページのコントローラからです。 チュートリアルではgenerateスクリプトを活用していますが、せっかくなのでいちから書いてみることにします。 わたしはキーボードをタイプするのが好きなのです。

/* ---- StaticPagesController.scala */
class StaticPagesController extends ApplicationController {
  def home = render("staticpages/home")
  def help = render("staticpages/help")
}

/* ---- Controllers.scala */
object Controllers {
  def mount(ctx: SkinnyContext): Unit = {
    root.mount(ctx)
    staticPages.mount(ctx) // 追加
    ...
  }

  ...

  object staticPages extends StaticPagesController with Routes {
    val homeUrl = get("/staticpages/home")(home).as('home)
    val helpUrl = get("/staticpages/help")(help).as('help)
  }
}

一つめのStaticPagesControllerが、静的なページたちに対応するコントローラ本体で、実行するアクションを定義しているクラスです。renderメソッドは、指定した位置にあるViewテンプレートを描画する指示を行っています。

二つめのControllersは、サンプルアプリケーションにも最初から用意されているオブジェクトで、各コントローラを、リクエストの種類(HTTPメソッドの種類)と要求されたURLから構成されるルーティングに対応させる役割を持っています。 Scalatraのサーブレット/フィルタをラップしたもので、最下部のstaticPagesオブジェクトがルートに対してアクションの解決を行っています。

val homeUrl = get("/staticpages/home")(home).as('home)

getメソッドは、リクエストがHTTPのGETメソッドで送信された場合のルーティング定義であることを表します。第1引数の文字列がマッチさせるためのURLパターン、第2引数でスーパークラスのアクションメソッドを引き渡しています。最後に、Skinny独自の処理として、ルーティング全体をシンボルで識別できるようにしています。

このコードでは、コントローラをインスタンス化するさいにコントローラクラスを継承したシングルトンのかたちをとっていますが、Skinny Frameworkの公式ドキュメントには、コントローラを直接newインスタンス化し、Routesトレイトをミックスインしたvalとして用意する例も紹介されています。

これでviewテンプレートさえ用意すれば、静的なページを表示できるようになりました。

思いのほか時間をかけてしまったので、シミュレーションの続きと動作確認は次回としましょう。 テストの書き方も、早く実践してみたいですね。

(続く)

RESTアーキテクチャで一休み

現在の認識

Webアプリケーションは、Webブラウザによって操作され、HTTPブロトコルによる通信だけでなんらかの機能を実現するアプリケーションと認識しています。

Railsチュートリアルを読んでいると、RESTアーキテクチャという言葉が繰り返し出てきますが、いったいどういうものなのでしょうか。

第7章までの印象では、(Railsの哲学における)RESTとは、「アプリケーションからの応答が、クライアント(ブラウザ)との間にのみ存在する一時的な『状態』に依存せず、アプリケーションのバックエンドで実体化するリソースによって保証されるプログラミング手法」というようなものかな、と考えています。 アプリケーションがクライアントとリソースとの媒介者であることを徹底する、ともいえるでしょうか。

自信を持って断言できるようになるまで、研鑽を積んでいきましょう。