2014年6月20日金曜日

UnityでMono.Cecilを使う時の勘所

UnityでMono.Cecilの使う時の勘所

Mono.Cecilとは?

Mono.Cecilとは、.NETのCIL(共通中間言語)を操作するためのライブラリです。 .NETのDLLやexeを、生成したり改変したりすることが可能になります。

何に使うの?

主に

  • アスペクト指向言語の実装
  • コードの最適化
  • コードの難読化
  • リフレクションを使っている部分を置き換えて高速化
のために使われることが多いです。 ただし、かなりの黒魔術っぷりなのでむやみやたらには使わないようにしましょう。

Unityで使うときのはまりどころ

実機上(iPhoneやandroid)ではMono.Cecilは使えません

この辺は、.NET2.0subsetで動かすことになると思いますので、CILをいじるためのクラスをつかうMono.Cecilは使えません。 なので、基本はMono.CecilはEditorディレクトリ内にいれて、Editor上でのみで使用することになります。 iPhoneの場合は、AOTの為原理的に使用不可能です。 androidはJITなので、もしかしたらfullsetなら動くかもしれませんが未検証です。

.NET 2.0でビルドしましょう

.NET2.0用にビルドしないと動きません。

また、ビルドするときはMonoDevelopは絶対に使わないようにしましょう。MonoDevelopが糞なので、完全な.NET2.0のバイナリにビルドしてくれません。

手順としては、
  1. Mono.Cecilのソースコードをcloneしてくる
  2. Mono.Cecil.slnをXamarin studioやVisual studioで開く
  3. コンフィグレーションをnet_2_0_Release(またはdebug)にする
  4. プロジェクトオプション>ビルド>一般のTarget frameworkをMono / .NET 2.0に変更
  5. 一応クリーンをして、プロジェクトを全てビルド
  6. {Mono.Cecil}/bin/net_2_0_Releaseに出力されているdllをUnityプロジェクトのEditorディレクトリにコピー
これでUnityのEditor機能を作る際にMono.Cecilを使えるようになります。

デバッグ情報をきちんと出力しましょう

デフォルトだと、デバッグ情報を生成してくれません。そのため、DLLを改変した場合、例外のスタックとレースのファイルと行数が激しくずれてしまいます。 なので、ここを参考に、DLL出力時にデバッグシンボルを保存する設定をきちんとしてあげましょう。

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を保存するだけでローカルでも動きます。

2012年10月23日火曜日

sbt 0.12.Xでscala.tools.nsc.MissingRequirementErrorが出て、compile出来ない件

sbtのバージョンを0.11.3から、0.12.1に上げてみたところ、プロジェクトをコンパイルしようとすると
@sbt console
> compile

[info] Compiling 1 Scala source to PROJECT_PATH\target\scala-2.9.2\classes...
[trace] Stack trace suppressed: run 'last compile:compile' for the full output.
[error] (compile:compile) scala.tools.nsc.MissingRequirementError: object scala not found.
[error] Total time: 0 s, completed 2012/10/23 12:38:06
な感じのエラーが出るようになりました。いろいろ調べた結果、
libraryDependencies := ...
サンプルコード(というほどでもないが;; みたいな感じに、依存ライブラリを上書きしてしまっていることが原因でした。 解決方法は、
  1. :=は使わず、+=や<+=などを使う
  2. :=で上書きした後に、
    libraryDependencies <++= (autoScalaLibrary, sbtPlugin, scalaVersion) apply Classpaths.autoLibraryDependency
    
    で、必要なライブラリを追加する。
のどちらかになります。

2012年8月6日月曜日

MySQLでupdateしてinsertするとDeadLockが発生する

負荷テストしているときに、Deadlockの起きるはずのないところでDeadlockが起きたので、いろいろ調べた記録。
原因は、1つのTransaction内でupdateをかけてみて更新件数が0の場合、insertをするという操作をしているところでした。updateを行った際にそのKeyが存在しないと行ロックを広く取ってしまうことが原因みたいです。(あくまでらしいです。ソースコードなどまではおっていません。)
始めはO/R Mapper周りのバグかなと思ってたので、MySQL側の挙動であることを確定させるために検証コードも書いてみました。
ちなみに、Thread数が1ではこの問題は発生しません。

Sample code

App.scala
import java.sql.DriverManager
import scala.util.Random

object App{


  def main(args : Array[String]) {
  
    Class.forName("com.mysql.jdbc.Driver")
    val con = DriverManager.getConnection("jdbc:mysql://localhost/test", username, password);
    val st = con.createStatement()
    st.executeUpdate("""drop table DeadLockCheck""")
    st.executeUpdate("""create table if not exists DeadLockCheck(
    id INT PRIMARY KEY,
    c INT DEFAULT 0);""")
    st.close()
    con.close()
    val threads = (0 until 5).map(i =>{
      val t = new MyThread()
      t.start()
      t
    })
    threads.foreach(_.join)
    
  }
}

object MyThread{
  var random = new Random
}
class MyThread extends Thread{
  
  override def run() = {
    
    for(i <- 0 until 50){
      val con = DriverManager.getConnection("jdbc:mysql://localhost/test", username, password);
      con.setAutoCommit(false);
      val st = con.createStatement()
      val id = MyThread.random.nextInt.abs
      
      if(st.executeUpdate("""update DeadLockCheck set c = c + 1 where id = %s""".format(id)) <= 0){
        st.executeUpdate("""insert DeadLockCheck values(%s,1)""".format(id))
      }
      con.commit()
      
      st.close()
      con.close()
    }
  
  }

}


build.sbt
libraryDependencies += "mysql" % "mysql-connector-java" % "5.1.21"

結果

[info] Running App
[error] (Thread-82) com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
[error] (Thread-85) com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
[error] (Thread-84) com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
        at com.mysql.jdbc.Util.getInstance(Util.java:386)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1065)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4074)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4006)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2629)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2713)
        at com.mysql.jdbc.StatementImpl.executeUpdate(StatementImpl.java:1794)
        at com.mysql.jdbc.StatementImpl.executeUpdate(StatementImpl.java:1712)
        at MyThread$$anonfun$run$1.apply$mcVI$sp(App.scala:43)
        at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:75)
        at MyThread.run(App.scala:36)
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
        at com.mysql.jdbc.Util.getInstance(Util.java:386)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1065)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4074)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4006)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2629)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2713)
        at com.mysql.jdbc.StatementImpl.executeUpdate(StatementImpl.java:1794)
        at com.mysql.jdbc.StatementImpl.executeUpdate(StatementImpl.java:1712)
        at MyThread$$anonfun$run$1.apply$mcVI$sp(App.scala:43)
        at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:75)
        at MyThread.run(App.scala:36)
com.mysql.jdbc.exceptions.jdbc4.MySQLTransactionRollbackException: Deadlock found when trying to get lock; try restarting transaction
        at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
        at sun.reflect.NativeConstructorAccessorImpl.newInstance(Unknown Source)
        at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(Unknown Source)
        at java.lang.reflect.Constructor.newInstance(Unknown Source)
        at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
        at com.mysql.jdbc.Util.getInstance(Util.java:386)
        at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1065)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4074)
        at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4006)
        at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2468)
        at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2629)
        at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2713)
        at com.mysql.jdbc.StatementImpl.executeUpdate(StatementImpl.java:1794)
        at com.mysql.jdbc.StatementImpl.executeUpdate(StatementImpl.java:1712)
        at MyThread$$anonfun$run$1.apply$mcVI$sp(App.scala:43)
        at scala.collection.immutable.Range.foreach$mVc$sp(Range.scala:75)
        at MyThread.run(App.scala:36)
[success] Total time: 1 s, completed 2012/08/06 19:01:10

参考

InnoDBのネクストキーロックによるデッドロックの例

2012年7月29日日曜日

Windows上でredmine2.0.3 + MySQLを動かす

1. Rubyの環境構築

Rubyインストーラーダウンロードページから、
  • Ruby 1.9.3-p194
  • DevKit-tdm-32-4.5.2-20111229-1559-sfx.exe
をダウンロードする。
Rubyは、installerを実行すればOK。
※この時、Ruby のインストール先を変更する場合は、ディレクトリにスペースが含まれていないところにする。
DevKitは、実行すると解凍先を聞いてくるので、適当な場所に解凍して解答したディレクトリでコマンドプロンプトから
  ruby dk.rb init
  ruby dk.rb install
を実行してインストールする。

2. MySQLの準備

MySQLを準備し、
  • redmine
  • redmine_develop
のデータベースを作成しておく。

3. Redmineのインストール

Reamine から、2.0.3をダウンロードしてきて、サーバーを実行したい所に解凍する。
redmineを解凍したディレクトリで
  gem install bundler
  bundle install --without development test rmagick
を実行する。 (RMagickはWindows環境だとインストールが大変なので、除外しておきます。)

4. conf/database.yml設定

conf/database.yml.exampleをコピーして名前をdatabase.ymlに変更する。
database.ymlのadapterをmysqlからmysql2に変更し、DBのusernameとpasswordも変更する。
  rake generate_secret_token
  rake db:migrate
  rake redmine:load_default_data


5. サーバーの起動

  script/rails server webrick
を実行すればOK。

production modeで起動したい場合は、
  RAILS_ENV=production rake generate_secret_token
  RAILS_ENV=production rake db:migrate
  RAILS_ENV=production rake redmine:load_default_data
でDBを準備して、
  ruby script/rails server webrick -e production
でサーバーを起動。

とらぶるしゅーてぃんぐ

 `require': Incorrect MySQL client library version! This gem was compiled for 6.0.0 but the client library is 5.0.20a. (RuntimeError)
のエラーが出る場合 まず
gem install mysql2
を実行し、結果に表示されるzipファイルをダウンロードする。
※私が実行したときは6.0.2だったので以下のURLからダウンロードしました。 http://dev.mysql.com/get/Downloads/Connector-C/mysql-connector-c-noinstall-6.0.2-win32.zip/from/pick ダウンロードしファイルを解凍して、中のlib\libmysql.dllをRubyの$RUBY_HOME$\bin以下にコピーしてあげればOKです。


参考にしたページ

2012年5月30日水曜日

ScalaからAWS SDK for Javaを使ってDynamoDBをいじったTips

AWS SDK for Javaのバージョンは、1.3.10です。
使ってみた時に詰まった点をメモってみました。


1. ResourceNotFoundExceptionが出る原因


以下のような例外が発生する場合の原因と対処法です。

[error] (run-main) Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: QSQDQO1RMAIDU4S2G6QNCR7S3JVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ResourceNotFoundException, AWS Error Message: Requested resource not found
Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: QSQDQO1RMAIDU4S2G6QNCR7S3JVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ResourceNotFoundException,AWS Error Message: Requested resource not found

原因1 Endpointが設定されていない

val client : AmazonDynamoDBClient = ...// init
client.setEndpont("http://dynamodb.ap-northeast-1.amazonaws.com"); // <- 東京RegionのEndpoint設定。
自分のDynamoDBを使用しているRegionを設定しましょう。Endpoingの一覧はこちら

原因2 Tableが作成されていない

指定したTableが存在しない場合にも同じ例外が発生します。Tableが正常に作成されているかと、プログラム中でTable名の指定が間違ってないかを確認しましょう。
Tableがまだ作成されていない場合は、ManagedConsoleから作成してあげましょう。


2. ValidationExceptionが出る原因


以下のような例外が発生する場合の原因と対処法です。
[error] (run-main) Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: HE5SG08RJAMBQQUBJ031DRGRTVVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ValidationException, AWS Error Message: The provided key element does not match the schema
Status Code: 400, AWS Service: AmazonDynamoDB, AWS Request ID: HE5SG08RJAMBQQUBJ031DRGRTVVV4KQNSO5AEMVJF66Q9ASUAAJG, AWS Error Code: ValidationException, AWS Error Message: The provided key element does not match the schema

原因1 HashKeyまたはRangeKeyの型が違う


HashKeyとRangeKeyの型が間違っている場合に良く起こります。 Table定義を確認して、StringとNumber型の指定が正しいことを確認してください。

new AttributeValue().withS("hogehoge") //Table定義ではNumberになっている場合コレが原因になる



3. O/R mapperを使ってRangeKeyにDate型を指定したけど、queryで上手くフィルターできない


O/R mapperでDateを使用する場合は、DynamoDB上ではISO8601の日付フォーマットの文字列として保存されます。
import com.amazonaws.util.DateUtils

DateUtils dateUtils = new DateUtils();

dateUtils.formatIso8601Date(date)
dateUtils.parseIso8601Date(dateString)

で、変換できます。queryやscan時のConditionで指定する場合は、このメソッドを使いましょう。


4. ScalaからAWS SDKのO/R mapperを使う


Java Beansにしか対応していないので、通常のScalaPlainObjectでは上手く認識してくれません。
基本的には、@BeansInfoアノテーションをつければ、getter/setterを生成してくれるので変数の定義に関しては問題ありません。
しかし、varにつけたアノテーションは、コンパイルされるタイミングで、同名のprivate fieldのほうに付けれられてしまいます。
そのため、AWS SDKのアノテーションが有効にならないので、地道に手作業でgetter/setterを作成してアノテーションをつけないと行けません。
また、case classにしておくと勝手にequalsやhashCode関数をオーバーライドしてくれるので便利ですが、引数なしのコンストラクタもあわせて定義しておいてあげましょう。

NG

import scala.reflect.BeanProperty
import com.amazonaws.services.dynamodb.datamodeling._

case class Hoge( @BeansInfo @DynamoDBHashKey(attributeName="Id") var id : Long,
 @BeansInfo var nickname : String){
  def this() = this(0,"")
}

OK

import scala.reflect.BeanProperty
import com.amazonaws.services.dynamodb.datamodeling._

case class Hoge(var id : Long,
 @BeansInfo var nickname : String){
  def this() = this(0,"")

  @DynamoDBHashKey(attributeName="Id")
  def getId() : Long = id
  def setId(id : Long) = this.id = id;
}



5. O/R Mapper時にクラスの継承を行うと変数が上手くマップされない


幾つかのテーブルが同じようなテーブルで同じ変数持ってるから、継承で共通化しちゃえと思って継承を使ってみたところ、上手くO/R Mapperがフィールドをマップしてくれませんでした。
回避方法は、「継承元のクラスにDynamoDBTableアノテーションを付ける」になります。

NG

class Hoge(... , var hoge : String){
  def this() = this(...) 
  ...
   def getHoge():String = hoge
   def setHoge( hoge : String){ this.hoge = hoge}
}
@DynamoDBTable(tableName="FugaTable")
class Fuga extends Hoge

OK

@DynamoDBTable(tableName="DummyAnnotation") //<- これ重要
class Hoge ...


@DynamoDBTable(tableName="FugaTable")
class Fuga extends Hoge

ちなみに、こうなっちゃう原因はDynamoDBRefrector@isRelevantGetter(DynamoDBRefrector.java:92)の
private boolean isRelevantGetter(Method m) {
    return (m.getName().startsWith("get") || m.getName().startsWith("is")) && m.getParameterTypes().length == 0
        && m.getDeclaringClass().getAnnotation(DynamoDBTable.class) != null
        && !m.isAnnotationPresent(DynamoDBIgnore.class);
}
で、メソッドのDeclaringClassのアノテーションを取ってしまっちゃってるからです。

2012年5月11日金曜日

pythonでファイルやディレクトリを列挙 まとめ

いつもファイルとかディレクトリを列挙しようとするたびにググり続けているので、自分でまとめてみた。


指定したディレクトリのすべてのファイルとディレクトリを列挙

import os

for onlyFilename in os.listdir("mydir"):
    print onlyFilename

--------------------
'dir1'
'dir2'
'file1.txt'
'file2.txt'
'file2.py'
常にファイル、ディレクトリ名だけを返します。

特定の拡張子のファイルだけを列挙

import glob

for filenameWithPathAsString in glob.glob("mytexts/*.txt"):
    print absFilePathAsString

--------------------
'mytexts/hoge.txt'
'mytexts/wahoo.txt'
...

出力には、指定したPathも付けられています

ディレクトリのみを列挙

import glob
import os
import os.path

for filename in glob.glob("mydir/*"):
    if os.path.isdir(filename):
        print filename

--------------------
'mydir/dir1'
'mydir/dir2'
...



再帰的に全てのファイル、ディレクトリを列挙

for path,dirs,files in os.walk("mydir"):
  print path,dirs,files
深さ優先でディレクトリを掘って行きます。 例
"." -> "./dir1" -> "./dir1/hoge" -> "./dir2" -> "./dir2/hoge"

2012年4月24日火曜日

specs2で並列テストをOffにする

ScalaのBDDテストフレームワークのspecs2に関するTips
ありがた迷惑な機能として、specs2ではテスト実行時に自動で並列実行してくれます。
テスト実行は早くなりますが、DBの絡んだテストやファイルの絡んだテストでは問題になることも多々あります。
ただし、次のようにするだけで簡単に逐次実行に変更可能なので並列テストでの問題が出た場合は試してみてください。


import scala.specs2.Specification
// or
import scala.specs2.mutable.Specification

class MediaProxyTest extends Specification{
  
  sequencial // <- この1行を付けるだけ

  /* test codes +/


}

2012年2月22日水曜日

giter8でprivate repositoryにアクセス

Readme腐ってる

Linux,Macなら、
http://help.github.com/set-your-user-name-email-and-github-token/
の通りに設定すればOK

Windows環境はgit.exeにパス通していれば、同じ設定を行えば良いが、
そうでない場合は
GITHUB_USERとGITHUB_TOKENの環境変数を定義してあげればOK

2012年2月14日火曜日

scalaで簡単Proxyクラス実装。

ProxyデザインパターンやDecoratorデザインパターンを使いたいことがよくあると思いますが、いちいちすべてのメソッドを実装するのはめんどくさいですよね?
そんな時に便利なscalaの実装を紹介


class User(var id : Long,var name : String,var gender : Int)

class ProxyUser(private val user : User){
def name : String = if(gender == 1) "Mr. " + user.name else "Ms. " + user.name
}

object ProxyUser{
implicit def toUser(proxyUser : ProxyUser) = proxyUser.user
}



Companion objectにimplicit conversionを指定しておくだけで完了。
あとは、ProxyしたりDecorateしたりしたいメソッドを定義してやるだけでOK。

使い方はこんな感じ

val pu = new ProxyUser(new User(1,"Taro",1))

println("ID:" + pu.id)
println("name:" + pu.name)
pu.name = "Joro"
println("name:" + pu.name)

> ID:1
> name:Mr. Taro
> name:Mr. Jiro

2012年2月13日月曜日

Squerylでunion

scalaのO/R mapperのSquerylでunionを行う方法

複合Keyとかでin句が使えない場合のunion構文の実装方法のメモ

Queryクラスのunionメソッドを使います。



val table = Table[CompositeKey2Object]

val ids = List( (1,2),(1,3),(2,5) )

inTransaction{
def toQuery( id : (Int,Int) ) = {
from(table)(t => where( (t.pk1 === id._1) and (t.pk2 === id._2)))
}
ids match{
case id :: tails => {
ids.foldLeft(toQuery(id))( (query,id) => query.union(toQuery(id))).toList
}
case _ => Nil
}
}

2012年1月23日月曜日

JDBCで使えるURL一覧

JDBCに指定するURLとDriverClassのまとめ




DB名DriverClass種別url
MySQLcom.mysql.jdbc.Driverサーバーjdbc:mysql://[server_name]:[port=3306]/[database_name]
H2DBorg.h2.Driverサーバーjdbc:h2:tcp://[server]:[port]/[path]/[database_name]
ファイルjdbc:h2:file:[path][database_name]
メモリjdbc:h2:mem:[database_name]
Oracleoracle.jdbc.driver.OracleDriverサーバーjdbc:oracle:thin:@[server_name]:[port=1521]:[database_name]
Microsoft SQL servercom.microsoft.jdbc.sqlserver.SQLServerDriveサーバーjdbc:microsoft:sqlserver://[server_name]:[port=1433]/[database_name]; DatabaseName=[database_name]
Derbyorg.apache.derby.jdbc.EmbeddedDriverファイルjdbc:derby:[path]/[database_name];create=true
メモリjdbc:derby:memory:[database_name]
PostgreSQLorg.postgresql.Driverサーバーjdbc:postgresql://[server_name]:[port=5432]/[database_name]
Sqliteorg.sqlite.JDBCファイルjdbc:sqlite:[path]/[database_name]
org.sqlite.JDBC

2012年1月7日土曜日

Scalaの標準jsonライブラリの使い方

意外にscala標準ライブラリのjsonライブラリの説明をしてくれているサイトが少ないので自分で試したことをまとめ。



ちなみに、簡単なJSONをパースするだけなら、標準ライブラリでいい気はするけど、
オブジェクトのJSON化したいなら、sjsongsonなどのライブラリを使用したほうがよさそうです。



JSONのパース



JSONのパースはscala.util.parsing.json.JSONクラスを使います。parseFullとparseRawの2つのメソッドがありますが、動作は同等です。パースしたJSONは以下のようなscalaのクラスにマップされます。

JSON typeJSON.parseFullJSON.parseRaw
JSON MapMap[String,Any]JSONObject
JSON ArrayList[Any]JSONArray
文字列StringString
数値DoubleDouble


コードではこのような感じになります。

import scala.util.parsing.json.JSON;

val json : Object[Any] = JSON.parseFull("""
{
"key1" : "string",
"key2" : 20,
"key3" : ["Array",1],
"key4" : {
"key5" : "hoge"
}
}
""")

/*
このようなオブジェクトが帰ります
json.get == Map(
"key1" -> "string",
"key2" -> 20.0,
"key3" -> List("Array",1.0),
"key4" -> Map( "key5" -> "hoge")
)

*/



数値がDoubleにマップされるのに注意してください。
また、jsonフォーマットが間違っている場合などは、戻り値がNoneとなります。




JSON化



JSON化にはJSONObject,JSONArrayを使います。


JSONObject(Map(
"key1" -> "string",
"key2" -> 20,
"key3" -> JSONArray(List("Array",1)),
"key4" -> JSONObject(Map( "key5" -> "hoge"))
)))


JSONObjectやJSONArrayで囲まないとだめなのが超イケてない。

2012年1月6日金曜日

Scalatraでオレオレ例外をErrorHandling

Scalatraでは、errorメソッドを使うことで処理中に投げられた特定の例外を捕まえて、途中処理を挟むことができます。そのまま例外を投げ返すことで、HttpStatus=500(Internal server error)の結果をブラウザへと返してくれます。


class MyFilter extends ScalatraFilter {

error{
case e : MyException => {
// do anything.
throw e
}
}
}



ただ場合によっては、500番以外のHttpStatusCodeも返したいということもあると思います。
その時は次のようにすることで、特定の例外をキャッチして好きな結果を返すことができます。




class MyFilter extends ScalatraFilter {

error{
case e : NullPointerException => {
response.setStatus(200)
response.addHeader("Content-type","text/plain")
renderResponse("ガッ")
}
case e : AzunyanException => {
response.setStatus(400)
response.addHeader("Content-type","text/plain")
renderResponse("ペロペロ")
}
}
}

2011年12月14日水曜日

Mavenでたまにchecksumエラーで依存を解決できない

最近、mavenで依存性解決しようとすると、
Checksum validation failed, no checksums available from the repository

なエラーでてライブラリを取ってこれないことがある。

レポジトリを確認してみると、本来はpomやjarとともにそのmd5やshaのハッシュ値ファイルも置かれているはずが、それらのレポジトリにはそのファイルが無くどうもそのせいでChecksumが出来ずに失敗してしまっているっぽい。最近だと例えば
org.scala-tools.testing:scalacheck_2.9.0-1:jar:1.9
とか失敗したんだけど、どうもsbt(というかivy?)でデプロイされてそうなやつっぽい。

とりあえず解決策としては、--lax-checksumsのオプションを付けてMavenを実行してやれば解決はする。

PS.ちなみにIntelliJを使っているなら、Run/Debug Configuration > General > Checksum PolicyをWarnにすれば、--lax-checksumsオプションを付けて実行してくれる。

2011年9月22日木曜日

Javaのクラスに同名、同引数のメソッドが定義される件

javaの仕様?と思われますが、ある特定の条件下でリフレクションを使用した場合、困ったことになります。

例えば、このようなクラスを定義します。

scala

trait FirstDef[T]{
def id : T
}

class Conc extends FirstDef[Long]{
def id = 23L
}


java

interfaceFirstDef{
public T id();
}

class Conc implements FirstDef{
public Long id() = 23L
}

そして、ConcクラスをReflectionにかけると・・・


> classOf[Conc].getMethods(println(_))

public long Conc.id()
public java.lang.Object Conc.id()
public final native void java.lang.Object.wait(long) th
public final void java.lang.Object.wait() throws java.l
public final void java.lang.Object.wait(long,int) throw
public boolean java.lang.Object.equals(java.lang.Object
public java.lang.String java.lang.Object.toString()
public native int java.lang.Object.hashCode()
public final native java.lang.Class java.lang.Object.ge
public final native void java.lang.Object.notify()
public final native void java.lang.Object.notifyAll()



なんと、引数なしのid()メソッドが2つ定義されてしまいます!!!
しかし、戻り値はlongとjava.lang.Object。
Reflectionをがりがり使うライブラリを使用している場合、高確率でバグが出ちゃいます。

これは、継承しているTrait(Interface)の型テンプレートを実現するためと思われ、回避不可能です。Reflectionを使用するライブラリを作る場合はこのようなことがあるということに気をつけましょう。

2011年8月5日金曜日

Githubの小技

githubでの小技紹介


便利度★★★



Sourceタブで、tキーを押すとソース検索モードになります。
Pushされているファイルの中から簡単にファイルを探し出せます。




便利度★★



Wikiに、直接MarkdownやTexitileフォーマットのファイルをPushして編集できます。
Wiki -> Git access から見れるレポジトリがWiki用のレポジトリになっています。