printlnすら打つのが面倒
そんなときはこちら。
implicit class PutsSupport(private val sc: StringContext) extends AnyVal { def ps(args: Any*) = println(sc.s(args: _*)) } // usage ps"Hello, World!"
絶賛放置しておりますが続きを書いていきたいですね〜〜。
執事さんがほしい
今日はJenkinsさんについて調べたり、sbtのドキュメントを読んでいるうちに一日が終わってしまいました。 明日からはSkinny Tutorialのペースを上げていきたいところです!
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とは、「アプリケーションからの応答が、クライアント(ブラウザ)との間にのみ存在する一時的な『状態』に依存せず、アプリケーションのバックエンドで実体化するリソースによって保証されるプログラミング手法」というようなものかな、と考えています。 アプリケーションがクライアントとリソースとの媒介者であることを徹底する、ともいえるでしょうか。
自信を持って断言できるようになるまで、研鑽を積んでいきましょう。