2013年7月17日水曜日

chillを使ってクラス定義変更に強いシリアライズを実現

Java向けのシリアライズライブラリであるkryoと、そのscalaラッパーのchillを使って、クラスの変更に強いシリアライズ手法の実現方法のメモです。

javaのデフォルトのシリアライズだと、SerializableVersionをきちんと付けておかないと、クラスの定義を変更しフィールドを追加した時に過去のデータから復元できなくなります。
この挙動が悪いとは言いませんが、キャッシュの際にこの挙動をされるとクラス変更をするたびにキャッシュがクリアされてしまうことになったり、不意に過去のデータがよみなくなるなど、厳密な挙動では不便な場合が多々あります。なので、kryoを使って、クラスの定義変更に強いシリアライズが出来ないか試してみました。
まず、kryoをそのまま使うと、scalaのcase classがデシリアライズ出来ないので、ここは素直にtwitter様が作ってくれているchillを使いました。
また、kryoもデフォルトではクラスのフィールドを追加したり削除したりすると、過去のデータからはシリアライズできなくなるので、少々設定をいじる必要があります。以下がその方法です。


build.sbt
scalaVersion := "2.10.0"

libraryDependencies += "com.twitter" %% "chill" % "0.2.3"

ソース

import com.esotericsoftware.kryo.serializers.CompatibleFieldSerializer
import com.twitter.chill._

// kryoのインスタンスを取得
val kryo = KryoBijection.getKryo
// デフォルトのSerializerを、フィールド情報も保存してくれるCompatibleFieldSerializerに変更 
kryo.setDefaultSerializer( classOf[CompatibleFieldSerializer[Any]])

//新しいKryoInjectionインスタンスを作成
val Injection = KryoInjection.instance(kryo)

// シリアライズ
val bytes : Array[Byte] = Injection(Hoge(1,2,"hoge"))

// デシリアライズ
val des : Option[Any] = Injection.invert(bytes)

println(des)// Some(Hoge(1,2,"hoge"))


2013年4月17日水曜日

ローカルで削除されたファイルを一括でgit rmする

Mac環境で、ローカルのファイルを削除してgitのindexだけが残ってしまっている場合に、一括でgit rmする方法の紹介です。

コマンドは以下の通り。これをコピペして実行してください。
git status | grep deleted: | cut -c 15- | sed -e 's/ /\\ /g' | xargs git rm

やっていることは、

1. git statusでindexの変更取得
2. grep deleted: で削除されたファイルの行だけを抜き出す。
3. cut -c 15- でファイルのパスにあたる15文字以降だけを抜き出す
4. sed -e 's/ /\\ /g'で、ぱすに空白が含まれてしまっている場合にエスケープする
5. 抜き出されたパスをgit rmする

という手順になります。
行程3の部分が、gitのバージョンによって変化するかもしれないのでもしうまくいかない場合は
git status | grep deleted: | cut -c 15-
だけで実行してみて、正しくパスが取得できているかを確認してみてください。

2013年3月3日日曜日

Scala2.10のReflectionで、class constructorを取得する

まだ書きかけですが、とりあえずさらしておきます。
後日、もう少しバージョンアップします。

やりたい事は、ScalaのReflectionを使ってクラスのコンストラクタを取得して新しいインスタンスを作るです。
case classも、これでパラメータの取得が可能です。

build.sbt
scalaVersion := "2.10.0"

libraryDependencies += "org.scala-lang" % "scala-reflect" % "2.10.0"
App.scala


import scala.reflect.runtime.universe._
import scala.reflect._

case class Hoge(val name : String)


object App{

  def main(args : Array[String] ) {
    println(newFromParamsMap[Hoge](Map("name" -> "hoge")))
  }
  
  
  def newFromParamsMap[T](params : Map[String,Any])(implicit tt : TypeTag[T],ct : ClassTag[T]) : T = {
    
    val t = typeOf[T]
    
    // Primary constructorの取得
    val primaryConstructor = t.declarations.collectFirst({
      case m : MethodSymbol if m.isPrimaryConstructor => m
    })
    
    if(!primaryConstructor.isDefined){
      throw new Exception(s"Class ${t} doesn't have primary constructor.")
    }
    
    //コンストラクタのパラメーターを取得してくる
    //後日型のチェック機能も入れます。
    val constructorValues = primaryConstructor.get.paramss.map( _params => {
      _params.map( {
        case t : TermSymbol => {
          val p = params(t.name.encoded.trim)
          /*if( ! t.typeSignatur =:= typeOf(p.getClass){
            throw new Exception("Parameter :${t.name.encoded} doesn't match type!)
          }*/
          p
        }
      })
    })
    // 実行のためのMirror取得
    val mirror = tt.mirror
    
    val classMirror = mirror.reflectClass(t.typeSymbol.asClass)
    val constructorMirror = classMirror.reflectConstructor(primaryConstructor.get)
    
    constructorMirror(constructorValues.flatten :_*).asInstanceOf[T]
  }
  
  
}

2013年1月11日金曜日

lift-mapperのモデルのコードジェネレータ作ってみた

lift-mapperのmodelの雛形のコードジェネレータ作ってみました。 Lift-mapper model generator ソースは全部htmlにベタ書きしているので、htmlを保存するだけでローカルでも動きます。