Life is TraversableOnce

programming / Java / Scala / Rust

Skinny Tutorial #2 試し書き(2)

第2章の続きです。

Userリソースの確認

skinny runコマンドからJettyを起動し、localhost:8080/usersにブラウザから接続してみます。

少し話は逸れますが、Skinny Frameworkが提供する強力な機能のひとつに、skinny.Skinnyクラスの存在があります。

このクラスはフレームワークが提供するさまざまな変数への参照を保持しているので、たとえばロケールごとのメッセージを取得したいときや、リクエストパラメータを取得したいときなど、クラスを大量にインポートすることなく、必要なものを取りだすことができるようになっています。

TODO Skinnyクラスの詳細をちゃんと確認すること

scaffoldで作成したSSPファイルからも、ボタンや入力フォームのタイトル表示などに、このskinny.Skinnyクラスのインスタンスを利用し、ロケールごとのメッセージ定義ファイルmessages.confに定義された文言を参照しています。

ここで本題のデモアプリケーションの確認に戻りますが、わたしの環境では、user.nameキーに対応するメッセージとして、Windowsのログオンアカウント名が表示される現象が発生しました。

f:id:letten:20150907232101j:plain

導入のさいに何か見落としているか、あるいは、sbtがホームディレクトリとしてログオンアカウント名のフォルダを探しに行っていた記憶があるので、そのあたりで覚えたものが表示されているのかもしれませんね。

今回は、原因究明を急ぎたいものでもないので、messages.confuser.nameキーを別のキーに変更し、SSPファイルの対応箇所を書き換えて、正常な表記となることを確認しました。

user {
  ...
  id="ID"
  dispname="Name" // <= name から変更
  email="Email"
}

f:id:letten:20150907232653j:plain

Micropostの作成

CRUD操作が可能であることを確認し、Bootstrapのすばらしい自動生成GUIを堪能したところで、駆け足ですが、このままMicropostリソースの作成に進みます。

この課題の肝は、他のリソースとの関係性を持つリソースの実装方法を確認することでしょう。

まずはUserの場合と同様に、scaffoldでひな形を作成します。

skinny g scaffold microposts micropost content:String userId:Int

マイグレーションを適用し、ブラウザから挙動を一通り確認して、まずはMicropostに最大文字数の制限をかけてみます。 バリデーションはリソースに対応するコントローラで実装されているので、初期値を書き換えて、contentフィールドの最大長を140文字にしてみましょう。

class MiropostsController extends SkinnyResource with ApplicationController
...
  override def createForm = validation(createParams,
    paramKey("content") is required & maxLength(140),
    paramKey("user_id") is required & numeric & intValue
  )
...
  override def updateForm = validation(updateParams,
    paramKey("content") is required & maxLength(140),
    paramKey("user_id") is required & numeric & intValue

f:id:letten:20150907234316j:plain

入力チェック機構が働いていますね!

バリデーションの使用方法を確認したところで、いよいよリソースの関連付けを記述します。 MicropostからUserへの依存を表現するために、MicropostのエンティティであるケースクラスにOption[User]型のフィールドを追加し、初期値にNoneを指定します。関連付け自体は、コンパニオンオブジェクトであるDAOにbelongsToメソッドとして定義します。

case class Micropost(
  id: Long,
  content: String,
  userId: Int,
  user: Option[User] = None,    // <= 追加
  createdAt: DateTime,
  updatedAt: DateTime
)

object Micropost extends SkinnyCRUDMapper[Micropost] with TimestampsFeature[Micropost] {
  belongsTo[User](User, (m ,u) => m.copy(user = u))    // <= 追加

同様に、Userモデルのエンティティには関連する複数の記事を表すフィールドとして、Seq[Micropost]型のmicropostsを追加、DAOには次のようなhasMany定義を作成します。

case class User(
  ...
  //  以下を追加
  microposts: Seq[Micropost] = Nil,
  ...
)

object User extends SkinnyCRUDMapper[User] with TimestampsFeature[User] {
...
  //  これ以下を追加
  lazy val micropostsRef = hasMany[Micropost](
    many = Micropost -> Micropost.defaultAlias,
    on = (u, m) => sqls.eq(u.id, m.userId),
    merge = (user, microposts) => user.copy(microposts = microposts)
  )

これらの関連付けを表すメソッドは、skinny.orm.feature.AssociationsFeatureトレイトで提供されています。わたしのデモアプリケーションでは、公開されているSkinny ORMのテストクラスを参考に実装しています。

まだちゃんと理解していないので断言はできませんが、どちらも外部キーとして他のテーブル(リソース)を参照し、関連先のエンティティをメンバとして保管できるようにしているようです。関連付けが一対一の場合はOption、多対一の場合はSeqに納められます。

以上でRailsチュートリアルの二章を、駆け足ですが体験することができました。 関連付けの挙動は、別途機会をもうけてconsoleから確認してみたいと思います。

次の投稿では、テスト駆動開発を本格的に始めるために、Skinnyが採用しているテスティングフレームワーク、ScalaTestの調査ができれば……と考えております。

続く

Skinny Tutorial #1 試し書き(1)

さっそくRails Tutorialの第1章をはじめるべきなのですが、環境構築にまだとりかかれていないので、そちらにはあらためて時間を割くことにして、第2章のデモアプリケーションを試してみたいと思います。


はじめに

現時点での学習環境をメモしておきましょう。

サンプルプロジェクトの導入

第2章の目的は、scaffoldジェネレータの機能を紹介すること、RESTアーキテクチャにおけるWebプログラミングの手法を学ぶこととありますので、本稿でもSkinny Frameworkのscaffoldコマンドを実行してみます。

Skinny Frameworkの導入は、公開されている依存ライブラリ同梱済みのサンプルプロジェクトを利用することとし、sbtの設定なども初期値をそのまま使用します。

バージョン管理も省略しますので、gitへのパブリッシュは行いません。

デモアプリケーションの要件

チュートリアル第2章の課題は、ユーザ(User)がごく短い記事(Micropost)を投稿できるマイクロブログです。必要なモデリングは例題のものにならうとして、scaffoldジェネレータを利用したUserリソースの作成にとりかかります。

公式ドキュメントによれば、Skinnyのコマンドラインツールからscaffoldジェネレータ・スクリプトを起動するさい、次のパラメータが必要となるようです。

skinny g scaffold:[Viewテンプレート] [リソース名(複数)] [リソース名(単一)] [フィールド名:フィールドの型(オプションでDB上の型)]

Viewテンプレートの指定は、Skinnyのビュー層を担うScalateが複数のテンプレートに対応しているために存在している項目でしょう。ここではデフォルトのSSPを使います。

Userモデルは名前とメールアドレスを持つとありますので、scaffoldジェネレータの引数は次のようになりました。Skinnyでは、リソース名はキャメルケースで記述する必要があるようです。

skinny g scaffold users user name:String email:String

実行結果はこのようになりました。

[info] Running TaskRunner generate:scaffold users user name:String email:String
 *** Skinny Generator Task ***
  "src\main\scala\controller\ApplicationController.scala" skipped.
  "src\main\scala\controller\UsersController.scala" created.
  "src\main\scala\controller\Controllers.scala" modified.
  "src\test\scala\controller\UsersControllerSpec.scala" created.
  "src\test\scala\integrationtest\UsersController_IntegrationTestSpec.scala" created.
  "src\test\resources\factories.conf" modified.
  "src\main\scala\model\User.scala" created.
  "src\test\scala\model\UserSpec.scala" created.
  "src\main\webapp\WEB-INF\views\users\_form.html.ssp" created.
  "src\main\webapp\WEB-INF\views\users\new.html.ssp" created.
  "src\main\webapp\WEB-INF\views\users\edit.html.ssp" created.
  "src\main\webapp\WEB-INF\views\users\index.html.ssp" created.
  "src\main\webapp\WEB-INF\views\users\show.html.ssp" created.
  "src\main\resources\messages.conf" modified.
  "src\main\resources\db\migration\V20150906221545__Create_users_table.sql" created.

[success] Total time: 45 s, completed 2015/09/06 22:15:47
C:\dev\stutorials>

Railsのログと見比べてみると、SkinnyではAsset関連のファイルは生成されないようですが、これはデフォルトでフロントエンドのロジックをBootstrapに任せているためでしょうか?

テストクラスも自動生成されています。

次に、アプリケーションを起動して確認するために、さくさくとmigrateも実行してしまいます。

skinny db:migrate

自動的にdb/migration/以下のフォルダにあるsqlファイルが読み込まれ、既定のH2データベースにテーブルが作成されました。

(つづく)

Scalatraドキュメント私家訳 ルーティング(1)

正確な訳文ではありません。 またこの翻訳は何らかの権利を侵害する目的で公開するものではありません。 内容に問題を発見された方は、お手数ですが訳者までご連絡ください。


ルーティング

まずはScalatraのルーティングについて。

Webアプリケーションには、受け取ったHTTPリクエストをサーバ上のコードに関連づけ、実行させるしくみが必要です。Scalatraでは、これをルート(routes)とアクション(actions)という道具によって実現します。 http://www.yourapp.org/articlesというURLに対するPOSTリクエストを受け取ったとき、リクエストパラメータの情報をもとに、新しいArticle(記事)オブジェクトを作成するプログラムを考えてみましょう。このとき、リクエストがPOSTメソッドで送信され、要求されているパスが/articlesである、という2種類の情報がルートです。そして、実行されるコードがアクションです。 アクションについては、このガイドの次項で詳しく解説します。

簡単な例

Scalatraにおけるルートは、HTTPメソッド(GET、POST、PUT、DELETE)とURLパターンとのペアの形をとります。RESTアーキテクチャの慣例にならったコントローラクラスは、次のようになるでしょう。

class Articles extends ScalatraServlet {

  get("/articles/:id") {  //  <= この部分がルートのマッチングを行うマッチャー
    // ここにアクションを記述する
    // この例では、特定のidによって識別される記事を表示する
  }

  post("/articles") {
    // 記事の送信・新規作成を行うアクション
  }

  put("/articles/:id") {
    // 特定のidに対応する記事を更新する
  }

  delete("/articles/:id") {
    // 特定のidに対応する記事を削除する
  }
}

この例に示した4つのルートと、続くブロック内にあるアクションは、これだけで単純なブログ機能の基礎になり得るものです。例ではアクションをスタブにしていますが、実際のアプリケーションでは、コメント行の部分に、モデルの取得や保存、ビューの表示といった処理のためのコードが入ることになります。

続きは後ほど。

原文:

Routes | HTTP | Scalatra guides

記法

この記事から、Markdownの練習を始めました。

Skinny Tutorial計画

前置き

学習のために、Ruby on Rails チュートリアル:実例を使って Rails を学ぼうを例題として、演習課題をSkinnyで解いていきます。
Railsを使ったことも、あまつさえRubyを読んだこともないので、Railsに詳しい方が記事をご覧になったらたいそうお叱りを受けそうですが……。

SkinnyがScala on Railsをうたっているとはいえ、同じテクノロジで実現しているわけではないので、実習の途中で問題が起こるかもしれません。
始める前に、自分のために心がまえを書き留めておきましょう。

強行はしない

演習はチュートリアルの最初から順に、できるだけ素直に解いていきますが、どうしても実行できない場合は省略します。あとから解法をさぐるために保留にするのもよいでしょう。

継続する

毎日少しずつでも課題を進めます。取り組む時間が全く取れないときも、チュートリアルにたいして考えたことなどを記事にしてみましょう。

いまのところ、ごく基本的ですがこのようなものでしょうか。
思いついたら書き足していくことにします。

チュートリアルに取り組む目的として、gitやHerokuといった開発のスタンダードツールに親しむこと、テスト駆動開発を実践することも含んでいますので、Skinnyが提供しているツールでどのようなことができるのか、事前準備として知っておく必要がありそうです。

こんにちは

主にJavaScalaについての独習の記録をつけようと筆をとりました。

手はじめに、いま関心のあるScalatra、Skinnyなどの情報から集めていきましょう。

未来の私へ:継続を心がけましょう。

def makePromise(blog: Future[Blog]): Unit = {
  blog match {
    case b if b.isEmpty => println("write something")
    case _ =>
  }
}