彩湖サイクリング
先週末はびア部のなかじまさん、ひらやさんと荒川の彩湖に行ってきました。 ひらやさんはロードが届いてから初参加。
コース
体調が良くなく、様子を見ながらの集合でちょっと遅れて9:30スタート。 荒川沿いを通らず、埼玉をうろうろして11:00くらいに彩湖到着。 適度に休みつつ、ぐるぐる回って17:00くらいに帰宅しました。
ライトな感じでーということで彩湖になったのですが、結局トータル70kmくらい走りました。 体調悪かったものの、心肺も脚も意外と調子がよかったので無理せず走れました。あと、明るいうちに帰って来れたのもよかった。
荒川は何度も通ってるんですが、彩湖に行ったのは初めてです。 景色はちょっと単調ですが、道も広く適度にアップダウンがあるので走るのは楽しいです。 風も強くなく晴れていたので、他のローディーの方もたくさんいました。
近くの公園にはhaskellっぽい遊具もあって、エンジニアの運動にはぴったりな場所です(?)。
注意点というほどのことでもないですが、コンビニやご飯食べれそうなお店が周囲に少なそうだったので、行くときは事前に用意しておいた方がいいかもです。
装備
ほとんど前回と同じ格好でしたが、日中のみの走行だったので寒くはなかったです。 むしろ暑いくらい?
安売りしてたので、今回は新しくシューズカバーも装備して行きました。おかげで足も寒くなかったし、風にも勝てた気がします![要出典]
その他
もう春も間近なので、これからの季節のサイクリングが楽しみです。
知り合いも続々と自転車を購入してるようだし、ロード仲間の友達が近所に引っ越してくるので(徒歩5分!)、今年はたくさん出かけようと思います。
elastic beanstalkの設定ファイルを活用する
最近AWSを使う仕事がちょくちょくあります。
そのうちの一つに、git submoduleに多数依存しているプロジェクトをelastic beanstalkに移行するというものがありました。
elastic beanstalkではデプロイするだけではsubmoduleを自動で取ってきてくれないので、依存するsubmoduleをプロジェクトの管理下に直接含めたり、デプロイ時に手動で更新するなどの対応が必要です。
今回はこれをelastic beanstalkの設定ファイルを使って解決してみました。
elastic beanstalkの設定ファイル
設定ファイルはyaml形式のファイルで、プロジェクトルートに.ebextentions/*.config
として置くことで利用することができます。
containerの種類やOSによって設定できることが多少異なりますが、どれもとても便利です。 例えば、linuxの場合、
- パッケージマネージャからパッケージのインストール
- ユーザやグループの作成
- ファイルの新規作成/配置
- シェルのコマンドの実行
- サービスの設定
- コンテナの設定
などが色々なことができます。
詳しくは下記ドキュメントを参照。
git submoduleを使ったプロジェクトのセットアップ例
今回は以下のような設定ファイルを書いてみました。
# .ebextentions/01-git-submodule.config container_commands: 10-rm-dirs: command: 'rm -r submodules/hoge src/fuga/piyo' 11-git-init: command: 'git init' 20-git-add-1: cwd: '/var/app/ondeck/' command: 'git submodule add git://github.com/hoge/hoge.git submodules/hoge' 21-git-checkout-1: cwd: '/var/app/ondeck/submodules/hoge' command: 'git checkout e1ad2bde9c2cf12e65a89ef5eadcccd048f1af0c' 30-git-add-2: cwd: '/var/app/ondeck/' command: 'git submodule add git://github.com/fuga/piyo.git src/fuga/piyo' 31-git-checkout-2: cwd: '/var/app/ondeck/src/fuga/piyo' command: 'git checkout d49febf9eaf66bd89205f1e39b0290dfcbec5dfd'
やっていることとしては、
- git initする
- submoduleの展開先ディレクトリを削除する
- git submodule addし直す
- 指定したコミットをチェックアウトする
という感じです。
各submoduleの設定は.git/config
に書き込まれているのですが、デプロイするプロジェクトには.git
が存在しないため上記ではその代わりの作業を行っています。
submoduleが増えるたびに追記する必要があるのでちょっと泥臭いですが、それ以外では余計な運用が増えないメリットもあります。
知っとくといいかもしれないこと
ディレクトリ
container_command
実行時には/var/app/ondeck
がワーキングディレクトリとなっています。
ここはプロジェクトのzipが展開される準備用のディレクトリで、ここから/var/app/current
にコピーされてデプロイが完了します。
設定ファイルの適用履歴
設定ファイルの適用内容はログに書き出されるので、設定失敗時に見ると役立ちます。
ログはelastic beanstalkのマネジメントコンソールからLogs
ページでSnapshot Logs
で取得できます。
まとめ
こんな感じで環境設定をテキスト管理できるのはなにかと便利ですね。 ただし設定ファイルはデプロイ毎に実行されて時間がかかるので、AMIも同時に活用するとよりいい感じだと思います。
elastic beanstalkは最近使い始めたので知らないことだらけです。 今回のgit submoduleのやり方を含め、「こうすると便利だよ!」という情報があればぜひ教えてください!
play-slickを使う
先日play-slickを使ってみたのでそのメモ。
play-slickとは
その名の通り、playframeworkでslickを使うためのプラグインです。
play-slickを使うと
- playframeworkのデフォルトのDB設定をそのまま使い回せる
- slickのスキーマ変更に合わせて自動でevolution用のmigrationファイルを作成してくれる
- セッション周りの扱いが楽になる
といったメリットがあります。
ちなみにplay-slickはplayframework 2.3に統合される予定とのこと。
環境
今回試した環境です。
セットアップ
playframeworkのセットアップは省略。
プラグインの追加
まずbuild.sbt
に利用するプラグインを追記していきます。
// build.sbt libraryDependencies ++= Seq( // other plugins // ... "com.typesafe.slick" %% "slick" % "2.0.0", "org.slf4j" % "slf4j-nop" % "1.6.4", "com.typesafe.play" % "play-slick_2.10" % "0.6.0.1" "org.jumpmind.symmetric.jdbc" % "mariadb-java-client" % "1.1.1", )
ここで注意が必要なのはplay-slickのバージョンで、playframeworkとslickのバージョンによって使えるバージョンが異なるようです。
playframework | slick | play-slick |
---|---|---|
2.3.x | 2.0.x | 0.7.x |
2.2.x | 2.0.x | 0.6.x |
2.2.x | 1.0.x | 0.5.x |
2.1.x | 1.0.x | 0.4.x |
(2014-07-27編集: 更新された公式ドキュメントに合わせて修正しました)
DBの設定
通常のplayframeworkアプリケーションと同様に、application.conf
にDBの接続情報を追記します。
mariadb用の設定となっているところは適宜変更してください。
# conf/application.conf db.default.driver=org.mariadb.jdbc.Driver db.default.url="jdbc:mariadb://localhost/dbname" db.default.user=dbuser db.default.password=dbpass slick.default="models.*"
ここでslick.default
というパラメタを指定していますが、これはevlolution用にmigrationファイルを作成する対象のクラスを指定するために利用されるそうです。
今回はmodels
パッケージ以下にslickの用クラスを定義するためそのように指定しています。
modelの実装
idと名前を持つユーザ用のテーブルとそれを操作するクラスを考えたときに、基本形はこんな形になると思います。
// app/models/user.scala package models import play.api.db.slick.Config.driver.simple._ case class User(id: Option[Int], name: String) class Users(tag: Tag) extends Table[User](tag, "users") { def id = column[Int]("id", O.PrimaryKey, O.AutoInc) def name = column[String]("name", O.NotNull) def * = (id.?, name) <> (User.tupled, User.unapply _) } object Users extends DAO { def findById(id: Int)(implicit s: Session): Option[User] = { Users filter { _.id === id } firstOption } def insert(user: User)(implicit s: Session) { Users += user } }
まずユーザ情報をUser
ケースクラスとして実装し、usersテーブルの表現としてTable[User]
を継承するUsers
クラスを実装しています。これはslickのLifted Embeddingの書式そのままです。
今回はここでUsers
クラスのコンパニオンオブジェクトとしてDAOを定義しています。
controllerはこのDAOを利用することでロジックをcontrollerに持ち込ませなくて済みます。
このコンパニオンオブジェクトが継承しているDAO
は以下のように定義しています。
// app/models/DAO.scala import scala.slick.lifted.TableQuery private[models] trait DAO { val Users = TableQuery[Users] val Posts = TableQuery[Posts] ... }
こうすることで他のテーブルを跨ぐクエリ発行するメソッドを実装できるようになります。
controllerの実装
// app/Application.scala package controllers import models._ import play.api.db.slick._ import play.api.mvc._ object Application extends Controller { def index = DBAction { implicit rs => Ok(views.html.index(Users.findAll)) } }
controllerではDBAction
を使うことによりセッションの生成を自動で行ってくれます。
そのためDAO側ではimplicit session: Session
を受け取るよう定義する必要があります。
まとめ
以上のようにセッション周りを考えずにDBを扱うことができるので、ロジックに集中して実装できると思います。
おまけ: slick-playとCake Pattern
play-slickにはProfile
というcake patternを簡単に実装するための仕組みがあり、公式サンプルに実装例があります。
- https://github.com/freekh/play-slick/blob/master/src/main/scala/play/api/db/slick/Profile.scala
- https://github.com/freekh/play-slick/tree/master/samples/play-slick-cake-sample
ただこのサンプルだとcontrollerで直接slickのクエリを利用していてあまりイケてない感じだったのでDAOを使う方式に書き直してみました。
が、DAO
を継承するクラスが増えるたびにインスタンスを作らなければならなくなり、微妙な感じに。
良いやり方があったら教えてください!
# conf/application.conf slick.default="models.current.dao.*"
// app/models/user.scala package models import play.api.db.slick.{Profile, Session} case class User(id: Int, name: String) trait UserComponent { this: Profile => import profile.simple._ class Users(tag: Tag) extends Table[User](tag, "users") { def id = column[Int]("id", O.PrimaryKey) def name = column[String]("name", O.NotNull) def * = (id, name) <> (User.tupled, User.unapply _) } } class UsersDAO(implicit dao: DAO) { import dao._ import profile.simple._ def findByName(id: Int)(implicit s: Session): Option[User] = { Users filter { _.id === id } firstOption } def insert(user: User)(implicit s: Session) { Users += user } }
// app/models/DAO package models import scala.slick.driver.JdbcProfile import scala.slick.lifted.TableQuery import play.api.db.slick.{Profile, DB} class DAO(override val profile: JdbcProfile) extends UserComponent with Profile { val Users = TableQuery[Users] } object current { implicit val dao = new DAO(DB(play.api.Play.current).driver) val Users = new UsersDAO }