ハンズオン中はごめんなさいをするしかなかったのですが、 Skinny Framework 1.0.6 から使えるようになりました!
$ ./skinny debug [port]
とすると、リモートデバッグを有効にしてアプリが起動します。 後はお使いのIDEのDebuggerから指定したポートに接続すれば、 Break Pointなど自由に貼ることができます。
port番号を省略した場合はデフォルトで 5005 ポートを使用します。
Skinny ORM の Associations で、DB上でも NOT NULL
だし、byDefault
で必ず join
するのに、フィールドを Option
型で宣言しなきゃいけないのがキモいんだけど?
2014-04-12 00:27 追記
innerJoinWithDefaults
もしくは innerJoin
を使用して、extract
中から関連の apply
を呼んで Option
を回避できるようです。> ありがとうございます @seratch
case class Employee private (
id: EmployeeId,
name: String,
companyId: CompanyId,
company: Company
)
object Employee extends SkinnyCRUDMapper[Employee] {
innerJoinWithDefaults(Company, (e, c) => sqls.eq(e.companyId, c.id)).byDefault
override def extract(rs: WrappedResultSet, rn: ResultName[Employee]): Employee = new Employee(
id = rs.get(rn.id),
name = rs.get(rn.name),
companyId = rs.get(rn.companyId),
company = Company(rs)
)
}
2014-04-12 00:27 削除
気持ちは痛いほどわかります。
とは言え、逆からの関連を辿る場合など、join しないケースが実際にはありえるので、型としては Option
にしておく方が望ましいと思います。
ただ、アプリケーションの殆どで join するケースしかあり得ないと想定できるのであれば、確かに毎回 Option
を剥がすのも面倒なので、下記のように直接アクセスできるようなアクセサを提供するのも検討できます。
case class Employee private (
id: EmployeeId,
name: String,
companyId: CompanyId,
private val companyOpt: Option[Company] = None
) {
lazy val company: Company = companyOpt.get // 常にjoinして取得する前提なのでここで例外でたら
// データがおかしいかコードにバグがある
}
object Employee extends SkinnyCRUDMapper[Employee] {
belongsTo[Company](Company, (e, c) => e.copy(companyOpt = c)).byDefault
override def extract(rs: WrappedResultSet, rn: ResultName[Employee]): Employee = new Employee(
id = rs.get(rn.id),
name = rs.get(rn.name),
companyId = rs.get(rn.companyId)
)
}
以下のようなコードで画面上に奇妙な Some(Some(
という表示がされていました。
class EmployeesController extends ApplicationController {
def show = params.getAs[EmployeeId]("id").map { id =>
Employee.findById(id).map { emp =>
set("employee", emp)
render("/employees/show")
}
}
...snip
}
params.getAs[EmployeeId]
は Option[EmployeeId]
を返します。
そして Employee.findById(id)
は Option[Employee]
を返します。
したがって、この show
メソッドは Option[Option[String]]
を返す事になっていました。
このコードは本来下記のようにする必要があったのです。
class EmployeesController extends ApplicationController {
def show = params.getAs[EmployeeId]("id").flatMap { id =>
Employee.findById(id).map { emp =>
set("employee", emp)
render("/employees/show")
}
} getOrElse halt(404)
...snip
}
これはなかなか検出しづらいですね。
render
を使うような場合は、戻り値の型アノテーションを省略せずに
def show: String = ...
のようにしておくと、うっかり Option
を返すコードを書いてしまった時にコンパイラが型チェックで検出してくれるので、慣れないうちは上記のようにしておくのもありかもしれません。
余談ですが、今回の様な Option
がネストする場合というのは結構多発します。
そういった場合はfor式を使うとすっきり書けるケースがあります。
def show = (for {
id <- params.getAs[EmployeeId]("id")
emp <- Employee.findById(id)
} yield {
set("employee", emp)
render("/employees/show")
}) getOrElse halt(404)
${application_root}/src/main/webapp/
以下にファイルを置くと、contextPath 以下から参照できます。
株式会社サムライズム さんの動きに期待しましょう!
現在日本語化に向けてのアンケート を行っているようです。
Skinny Framework 1.0.6 から簡単に変更できるようになりました。
${application_root}/project/Build.scala
を開いて
port in container.Configuration := 8080
の行を好きなポート番号に変更すればOKです。
extract で必ず join されているものをとる手段はあります。どうしても Option を回避したいならこれですね。
https://github.com/skinny-framework/skinny-framework/blob/1.0.6/orm/src/test/scala/skinny/orm/models.scala#L30
https://github.com/skinny-framework/skinny-framework/blob/1.0.6/orm/src/test/scala/skinny/orm/models.scala#L70