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パラメータの削除。JavaScalaかは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の方がいいような気がしなくもない。

ここまでやったところで、とりあえず見た感じ動作するようになったので終了。

サクラエディタでもCDイジェクトがしたい

vimやIE、ChromeでCDイジェクトできるなら、サクラエディタでだってCDイジェクトしたくなりませんか?私はなりません!しかしサクラエディタならJScriptVBScriptでマクロを書くことができますので、以下のスクリプトをマクロとして登録することにより、いつでもCDを取り出すことができるようになります。(Windows7にて動作確認)

tarhashi/cd_eject.js

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で曜日の数値表現が非常に紛らわしい件

MySQLPHPで曜日の数値表現を使った処理を行ったら非常に紛らわしかったのでメモ。

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. 番号は無視して自動的に振られる
  1. 順序付きリストでは
  2. 前の番号を何にしても
  3. 番号は無視して自動的に振られる

リンク

[リンクのテキスト](リンクのアドレス "リンクのタイトル")

リンクのテキスト

シンタックスハイライト

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では扱いやすそう。