Play2.0で書いたサンプルを2.1対応にしてみる
Play2.1が出たので、試しに以前書いたサンプルをマイグレーションガイドを見ながら2.1対応にしてみた。
https://github.com/tarhashi/Play-AngularJS-Sample/tree/2.1
project/plugins.sbtを修正
addSbtPlugin("play" % "sbt-plugin" % "2.1.0")
mainを修正
project/Build.scalaにimportを追加
import play.Project._
play.Projectへの修正と、mainLangパラメータの削除。JavaかScalaかはappDependenciesにjavaCoreが入ってるかどうかで判別するようだ。
val main = play.Project(apName, appVersion, appDependencies).settings( //... )
sbtのバージョンを修正
project/build.propertiesを修正
sbt.version=0.12.2
ここまでやったところで、マイグレーションの通り以下を実行。
play clean play ~run
すると、以下のエラーが出た。
[error] (*:update) sbt.ResolveException: unresolved dependency: org.squeryl#squeryl_2.10;0.9.5-2: not found [error] unresolved dependency: com.github.tototoshi#lift-json-play-module_2.10;0.1: not found
ここからはプロジェクト独自の修正がメイン。
squerylのバージョンを変更
project/Build.scalaを修正して対応したバージョンにする。
val appDependencies = Seq( "org.squeryl" %% "squeryl" % "0.9.5-6" ) //... libraryDependencies ++= Seq( //... "org.squeryl" %% "squeryl" % "0.9.5-6" )
appDependenciesにjdbcを追加
次に、object db is not a member of package play.apiとエラーが出たのだが、appDependenciesにjdbcを追加すればいいようなので追加。
val appDependencies = Seq( jdbc, "org.squeryl" %% "squeryl" % "0.9.5-6" )
lift-json-play-moduleの部分の対応
Jsonの処理についてはPlayのJsonモジュールに書き換えて対応してみた。でもlift-jsonの方がいいような気がしなくもない。
ここまでやったところで、とりあえず見た感じ動作するようになったので終了。
Zend Framework 1.12.0で最も長いクラス名を探してみる
これはZend Framework Advent Calendar19日目の記事でもなんでもありませんし、開催されてるのかも知りません。
ここ1ヶ月半ぐらい仕事で久しぶりにZend Framework1系を使ってますけど、PHP5.2時代に擬似namespaceを実現したようなクラス名の命名規約になっていてとにかくクラス名が長いのが気になる。そんな中でもいったいどのクラスが一番長いか探しだしてみた。焼酎お湯割り数杯が入った状態で書いてるので何か間違ってるかもしれませんがあまり気にしないでください。
<?php $path = '/path/to/ZendFramework-1.12.0/library/'; $max = 0; $maxName = ''; foreach(new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path)) as $file) { if (strpos($file->getPathname(), '.php') === false) continue; $className = str_replace(array($path, '.php', '/'), array('', '', '_'), $file->getPathname()); if (strlen($className) > $max) { $max = strlen($className); $maxName = $className; } } echo 'className: ', $maxName, ' length: ', $max, "¥n";
実行してみると、以下のクラス名が得られました。
className: Zend_Service_DeveloperGarden_Response_ConferenceCall_AddConferenceTemplateParticipantResponseType length: 97
Zend_Service_DeveloperGarden_Response_ConferenceCall_AddConferenceTemplateParticipantResponseType、なんと97文字。ちなみにZend Framework1系のコーディング規約では1行の長さは80文字以内を目指しましょう、なのですけどクラス名だけで大幅オーバー。
Zend Framework1系は、ソースも読みやすいしまあ悪くはなかった(チューニングしないとかなり遅いけど)ですけど、そろそろ卒業してZend Framework2系(これも結構遅いらしい?)だとかSymfony2だとかに移行するといいんじゃないですかね。
MySQLとPHPで曜日の数値表現が非常に紛らわしい件
MySQLとPHPで曜日の数値表現を使った処理を行ったら非常に紛らわしかったのでメモ。
MySQLの曜日関数
MySQLには日付から曜日の数値表現を求める関数が2つある。 DAYOFWEEK関数と、WEEKDAY関数である。 この2つの関数が単にエイリアスであればいいのだが、 それぞれ違う結果を返す。
DAYOFWEEK関数
まずは、DAYOFWEEK関数の結果を見てみる。
SELECT DAYOFWEEK('2012-12-02') , DAYOFWEEK('2012-12-03') , DAYOFWEEK('2012-12-04') , DAYOFWEEK('2012-12-05') , DAYOFWEEK('2012-12-06') , DAYOFWEEK('2012-12-07') , DAYOFWEEK('2012-12-08') \G
上記のSQL実行すると、結果は以下のようになる。
*************************** 1. row *************************** DAYOFWEEK('2012-12-02'): 1 DAYOFWEEK('2012-12-03'): 2 DAYOFWEEK('2012-12-04'): 3 DAYOFWEEK('2012-12-05'): 4 DAYOFWEEK('2012-12-06'): 5 DAYOFWEEK('2012-12-07'): 6 DAYOFWEEK('2012-12-08'): 7 1 row in set (0.00 sec)
2012-12-02が日曜日なので、日曜日スタートで1から始まり、土曜日が7で終了、という形式である。
WEEKDAY関数
次に、WEEKDAY関数の結果を見てみる。
SELECT WEEKDAY('2012-12-02') , WEEKDAY('2012-12-03') , WEEKDAY('2012-12-04') , WEEKDAY('2012-12-05') , WEEKDAY('2012-12-06') , WEEKDAY('2012-12-07') , WEEKDAY('2012-12-08') \G
こちらを実行すると、次のような結果を得られる。
*************************** 1. row *************************** WEEKDAY('2012-12-02'): 6 WEEKDAY('2012-12-03'): 0 WEEKDAY('2012-12-04'): 1 WEEKDAY('2012-12-05'): 2 WEEKDAY('2012-12-06'): 3 WEEKDAY('2012-12-07'): 4 WEEKDAY('2012-12-08'): 5 1 row in set (0.00 sec)
WEEKDAY関数では、月曜日スタートで0から始まり、日曜日が6で終了、という形式である。
PHPの曜日の数値表現
PHPの曜日の数値表現もフォーマット文字列'w'と'N'の2種類で得られる結果が存在する。
まずはwの結果。
<?php for($i = 2; $i <= 8; ++$i) { $d = new DateTime(); $d->setDate(2012, 12, $i); echo $d->format('Y-m-d'),': ',$d->format('w'),"\n"; }
上記の結果は以下のようになる。
2012-12-02: 0 2012-12-03: 1 2012-12-04: 2 2012-12-05: 3 2012-12-06: 4 2012-12-07: 5 2012-12-08: 6
次にNの結果。
<?php for($i = 2; $i <= 8; ++$i) { $d = new DateTime(); $d->setDate(2012, 12, $i); echo $d->format('Y-m-d'),': ',$d->format('N'),"\n"; }
上記の結果は以下のようになる。
2012-12-02: 7 2012-12-03: 1 2012-12-04: 2 2012-12-05: 3 2012-12-06: 4 2012-12-07: 5 2012-12-08: 6
…MySQLと同じ形式が無い。 ということで、DAYOFWEEK関数と'w'もしくはWEEKDAY関数と'N'の組み合わせで1補正する、というように気をつける必要がある。
もしくはMySQLはDATE_FORMAT関数で%Wを指定、PHPではフォーマット文字'l'を指定してやれば'Sunday'のような文字列で一致するのでこれで扱ってやるのがいいのかもしれない…。
参考
はてなBlogのMarkdownモードがシンタックスハイライトに対応してた
最近記事を書いてなかったわけですが、久しぶりに試してみたら知らない間にシンタックスハイライトに対応してた。これはうれしいですね。
require 'redcarpet' markdown = Redcarpet.new("Hello World!") puts markdown.to_html
はてなBlogのMarkdownモードを使ってみる
はてなBlogがMarkdownに対応したらしいので使ってみます。
段落
Markdownでは、空行で段落が分かれます。
2つ目の段落です。
Markdownでは、空行で段落が分かれます。
2つ目の段落です。
HTML埋め込み
テーブルタグを埋め込んでみる。
<table>
<tr>
<td>Foo</td>
<td>Bar</td>
</tr>
</table>
テーブルタグを埋め込んでみる。
Foo | Bar |
見出し
# レベル1の見出し
## レベル2の見出し
### レベル3の見出し
#### レベル4の見出し
レベル1の見出し
===============
レベル2の見出し
---------------
レベル1の見出し
レベル2の見出し
レベル3の見出し
レベル4の見出し
レベル1の見出し
レベル2の見出し
引用
> 引用文です。
> ああああ
>
> いいいい
引用文です。 ああああ
いいいい
リスト
順序無しリスト
* あいうえお
+ かきくけこ
- さしすせそ
- あいうえお
- かきくけこ
- さしすせそ
順序付きリスト
1. 順序付きリストでは
5. 前の番号を何にしても
8. 番号は無視して自動的に振られる
- 順序付きリストでは
- 前の番号を何にしても
- 番号は無視して自動的に振られる
リンク
[リンクのテキスト](リンクのアドレス "リンクのタイトル")
シンタックスハイライト
GitHub Flavored Markdownですが
```ruby
require 'redcarpet'
markdown = Redcarpet.new("Hello World!")
puts markdown.to_html
```
ruby
require 'redcarpet'
markdown = Redcarpet.new("Hello World!")
puts markdown.to_html
これは対応していないみたい。残念。
Play framework 2.0でMongoDBを使ってみる
普段はMySQLか、もしくはPostgreSQLな仕事ばかりでMongoDBを使うようなことは無かったので、一度MongoDBを触ってみようと思い、記事の作成とそこにコメントを付けていくようなアプリを書いてみた。ログインはめんどいので割愛。
play-mongo-sample
ODM
MongoDBへのアクセスには、Salatというscalaのケースクラスとの変換をやってくれるORMならぬODMを使用。Play framework(scala)からはプラグインのplay-salatを使うと簡単に扱えるようだ。1.0.9だとライブラリ依存関係の解決でエラーが出てしまう(https://github.com/leon/play-salat/issues/28)ので、1.1-SNAPSHOTで。
記事の構造
MongoDBだと埋め込みオブジェクトが持てるので、記事にコメントのオブジェクトを埋め込むような形で扱うことにする。こんな感じ。
{ title: "タイトル", body: "本文", comments: [ { body: "コメント1" }, { body: "コメント2" } ] }
Model
modelsパッケージにPost.scalaを作成し、記事の構造を定義したケースクラスと記事の操作を書いていく。
ケースクラスはこんな感じ。
case class Comment( body: String ) case class Post( @Key("_id")id: ObjectId = new ObjectId, title: String, body: String, comments: List[Comment] = List() )
SalatのModelCompanion traitを使うと基本的な操作が提供されるようで便利。さらにそこへコメントの追加処理などを定義していく。$pushを使ったupdateのMongoDBObjectの入れ子は面倒。。。
object Post extends ModelCompanion[Post, ObjectId]{ val dao = new SalatDAO[Post, ObjectId](collection = mongoCollection("posts")) {} def findOneById(id: String): Option[Post] = { try { dao.findOneById(new ObjectId(id)) }catch { case e: IllegalArgumentException => None case _ => None } } def addComment(p: Post, comment: Comment) = { update( MongoDBObject("_id" -> p.id), MongoDBObject( "$push" -> MongoDBObject( "comments" -> MongoDBObject( "body" -> comment.body ) ) ), false, false, new WriteConcern) } }
controller, form
controllers/Posts.scalaに必要なアクションを定義し、routesに追加。本当は記事内容の修正や削除なんかも必要だけど、記事の作成と埋め込みオブジェクトのコメントの追加のところをやってみたかっただけなので割愛。あと、ところどころかなりいい加減。エラー処理とか。
最初Formをこんな風に定義していて、ちょっとはまってしまった。
val postForm = Form( mapping( "id" -> ignored(new ObjectId), "title" -> nonEmptyText, "body" -> nonEmptyText )(Post.apply)(Post.unapply) )
これだとObjectIdが重複して2件目以降の保存が失敗してしまったので、次のように修正。
val postForm = Form( mapping( "title" -> nonEmptyText, "body" -> nonEmptyText ) ((title: String, body: String) => Post(title = title, body = body)) ((post: Post) => Some(post.title, post.body)) )
とりあえずPlay frameworkで触ってみたけど、アプリからMongoDBを使うところに関してはそれほど難しくなさそうな感じ。オブジェクトとのマッピングに関してはSQLよりもよっぽど自然にできそうだし。他の言語でも有名どころでは大体ODMが揃ってるみたいだし。
データモデリングに関しては、1対多の関係を配列で埋め込むようにしたり、多対多の関係をそれぞれIDの配列を持ち合うような形を基本として考えて、そこから検索の都合やデータ量の都合などが要件に合うように崩していくような感じでいいんだろうか。
参考
MongoDBの薄い本(The Little MongoDB Book)
短いチュートリアルの中に色々重要なことがまとまってるように感じました。
MongoDBにおける関連(Relation)のスキーマ設計
MongoDBでのデータモデリングについて。ここでいうパターン2やパターン4を多用できたほうがMongoDBでは扱いやすそう。