2012年3月31日土曜日

KotlinでJunit4系を使って自動テスト

githubみると、kotlinのテストはjunit3系(Testcaseを継承するやつ)でやっているので、
junit4系(アノテーションベースのやつ)での自動テストの実行方法を調べてみた。

テストコード

基本的にJavaでテストコード書く時と違いはない感じですね。
必要なアノテーションをimportしてあげて、メソッドの宣言時に指定してあげるだけです。
違いがあるとしたら、Javaとは異なりアノテーションを示す「@」は不要なとこですね。

あと、静的なメソッド作れないのでBeforeClassAfterClassを使っての、
テストクラスの初期処理と終了処理は実装できない感じです。(俺の知識が足りないだけかも?)

※junit4系でのテストコードの実装方法なので、ターゲットのコードは作ってません。
import org.junit.Before
import org.junit.After
import org.junit.Test

class SampleTest {

  Before fun setUp() {
    println("setUp")
  }

  After fun tearDown() {
    println("tearDown")
  }


  Test fun test1() {
    println("test1")
  }

  Test fun test2() {
    println("test2")
  }
}

実行結果

Javaと同じように、BeforeTestAfterが繰り返されているのがわかりますね。
setUp
test1
tearDown
setUp
test2
tearDown

2012年3月30日金曜日

[Oracle]ストアドファンクションの結果キャッシュ

ファンクションの結果キャッシュはOracle11gからの新機能で、この機能を使用すると同一のパラメータでストアドファンクションを呼び出した場合、2回目以降はキャッシュから結果を取り出してくれるので処理を高速化できる便利な機能。

細かい使い方は、マニュアルを見よう。

キャッシュを使った場合と使わなかった場合の性能差

実際にキャッシュを使った場合にどのぐらいパフォーマンス的に有利になるのかを測ってみました。

PL/SQLのコード

キャッシュを有効化するには、以下のようにファンクションの定義時に「RESULT_CACHE」キーワードを指定してあげるだけです。

処理的には、TEST_TABELEからパラメータで指定された値に紐付く「VAL」を取得して、返却するだけの簡単な処理になっています。
あと、ほんとに処理がスキップされて結果を返すかを確認するためにDBMS_OUTPUT.PUT_LINEでパラメータを出力してみてます。
CREATE OR REPLACE FUNCTION GET_VAL(key IN CHAR) RETURN CHAR RESULT_CACHE RELIES_ON (TEST_TABLE, HOGE)
AS
    val NVARCHAR2(100);
BEGIN
    DBMS_OUTPUT.PUT_LINE('param = ' || key);
    SELECT VAL INTO val
      FROM TEST_TABLE
     WHERE PK = key;
    RETURN val;
END;
/

テーブルの内容

PK   VAL
---- -----------
'01' 'あいうえお
'02' 'かきくけこ
'03' 'さしすせそ

sqlplusで2回連続で実行してみた

1回目の実行では、パラメータ情報が出力されているけど2回目は出力されていない。
どうやらキャッシュがちゃんと有効になっているっぽい。
19:52:02 SQL> exec :val := get_val('01');
param = 01

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.00
19:52:05 SQL> exec :val := get_val('01');

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.01

参照してるTEST_TABLEに変更加えた場合ってどう動くんだろう?といことで試してみた。
結果だけ見ると、参照テーブルに変更入るとキャッシュが無効化されて最新情報を取りに行くようですね。
20:00:19 SQL> exec :val := get_val('01');

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.00
20:00:23 SQL> update test_table set val = 'hoge' where pk = '01';

1行が更新されました。

経過: 00:00:00.00
20:00:29 SQL> commit;

コミットが完了しました。

経過: 00:00:00.00
20:00:31 SQL> exec :val := get_val('01');
param = 01

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.00

次は、関係ないテーブルに変更加えてみるとどうだろう。
結果、関係ないテーブルの変更はキャッシュに影響は与えないみたいなので想定通りの動き。
20:02:05 SQL> exec :val := get_val('01');

PL/SQLプロシージャが正常に完了しました。

経過: 00:00:00.00
20:08:17 SQL> update hoge set col1 = '1';

1行が更新されました。

経過: 00:00:00.00
20:08:21 SQL> commit;

コミットが完了しました。

経過: 00:00:00.00
20:08:24 SQL> exec :val := get_val('01');

PL/SQLプロシージャが正常に完了しました。

おわり。

2012年3月29日木曜日

TortoiseSVNをコマンドラインで使う

TortoiseSVNをコマンドラインで使う方法。
このコマンドをファイラと連携してあげれば、右クリックとかしなくてよいのでかなりいい感じになるはず。
(コマンドラインで使うだけなら、svnコマンドを使ってあげたほうが楽。)

コミット

TortoiseProc.exe /command:commit /path:[ファイルパス] /closeonend:1

※closeonendに「1」を指定してあげると、正常にコマンドが実行できた場合にはダイアログを閉じてくれます。
その他の設定値は、以下を参照。

0: ダイアログを自動的に閉じません。
1: エラーがなければ自動的に閉じます。
2: エラーや競合がなければ自動的に閉じます。
3: エラー、競合、マージがなければ自動的に閉じます。

ログの表示

TortoiseProc.exe /command:log /path:[ファイルパス]

更新

TortoiseProc.exe /command:update /path:[ファイルパス]

リポジトリブラウザの表示

TortoiseProc.exe /command:repobrowser /path:[ファイルパス]

※ファイルパスの部分は、ディレクトリに対するパスでも問題無いです。

2012年3月25日日曜日

Project Euler-Problem 2をkotlinで解いてみる

問題:http://se-bikou.blogspot.jp/2012/03/project-euler-problem-2scala.html

フィボナッチ数列をもつIterableなクラスを作って、偶数な値のみにフィルタして合計を求めてみてます。

import ext.sum
import java.util.Collection
import kotlin.util.arrayList

class Fibonacci(num : #(Int, Int), val end : Int) : Iterable {
  private var num : #(Int, Int) = num

  class InnerIterator() : Iterator {
    override val hasNext : Boolean
    get() = (num._1 + num._2) <= end

    override fun next() : Int {
      num = #(num._2, num._1 + num._2)
      return num._2
    }
  }
  override fun iterator() : Iterator {
    return InnerIterator()
  }
}

fun main(args : Array) {
  val sum = Fibonacci(#(0, 1), 4000000).filter({it % 2 == 0}).sum()
  println("sum = ${sum}")
}

2012年3月24日土曜日

JavaでAES暗号

KeyStoreで鍵を管理して、その鍵をつかって暗号化と復号化をするコード。


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;

public class Encryptor {

    /** エリアス名 */
    private static final String ALIAS_NAME = "aesKey";

    /** 暗号化キーを保存するファイル */
    private File keyFile;

    /** 認証用パスワード */
    private static final char[] KEY_PASSWORD = {'p', 'a', 's', 's', 'w', 'o',
            'r', 'd'};

    /** IV */
    private static final IvParameterSpec IV = new IvParameterSpec("1234567812345678".getBytes());

    /** keyStore */
    private KeyStore jceks;

    public static void main(
            String[] args) throws IOException {
        Encryptor encryptor = new Encryptor(new File(".keyFile"));
        
        /* 初期化(keyStoreをロード */
        encryptor.initialize();
        /* 鍵を保存 */
        encryptor.saveKey();
        /* 暗号化 */
        byte[] encrypt = encryptor.encrypt("これを暗号化するよ");
        /* 復号化 */
        byte[] decrypt = encryptor.decrypt(encrypt);
        System.out.println("new String(decrypt) = " + new String(decrypt));
    }

    public Encryptor(File keyFile) {
        this.keyFile = keyFile;
    }

    private void initialize() throws IOException {
        FileInputStream inputStream = null;
        if (keyFile.exists()) {
            inputStream = new FileInputStream(keyFile);
        }
        KeyStore jceks;
        try {
            jceks = KeyStore.getInstance("JCEKS");
            jceks.load(inputStream, KEY_PASSWORD);
        } catch (CertificateException | NoSuchAlgorithmException | KeyStoreException | IOException e) {
            throw new IllegalStateException("failed to load key store.", e);
        } finally {
            if (inputStream != null) {
                inputStream.close();
            }
        }
        this.jceks = jceks;
    }

    private byte[] encrypt(String text) {
        try {
            Key key = loadKey(ALIAS_NAME);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.ENCRYPT_MODE, key, IV);
            return cipher.doFinal(text.getBytes());
        } catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalStateException("failed to encrypt.", e);
        }
    }

    private byte[] decrypt(byte[] encryptData) {
        try {
            Key key = loadKey(ALIAS_NAME);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, key, IV);
            return cipher.doFinal(encryptData);
        } catch (IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException | UnrecoverableKeyException | NoSuchPaddingException | InvalidKeyException | InvalidAlgorithmParameterException | IllegalBlockSizeException | BadPaddingException e) {
            throw new IllegalStateException("failed to encrypt.", e);
        }
    }

    private Key loadKey(String key) throws IOException, KeyStoreException, NoSuchAlgorithmException, CertificateException, UnrecoverableKeyException {
        return jceks.getKey(ALIAS_NAME, KEY_PASSWORD);
    }

    private void saveKey() throws IOException {
        try {
            if (!jceks.isKeyEntry(ALIAS_NAME)) {
                // 任意のキーを生成してkeyStoreにセット
                KeyGenerator aes = KeyGenerator.getInstance("AES");
                jceks.setKeyEntry(ALIAS_NAME, aes.generateKey(), KEY_PASSWORD, null);
            }

            // keyStoreファイルを保存
            try (FileOutputStream outputStream = new FileOutputStream(keyFile)) {
                jceks.store(outputStream, KEY_PASSWORD);
            }
        } catch (KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
            throw new IllegalStateException("failed to store key.", e);
        }
    }
}

[Oracle]データベース運用/保守 サンプルコード

オラクルさんが公開してくれている、データベース運用/保守 サンプルコードが以下のサイトから入手出来る。

http://otn.oracle.co.jp/sample_code/tech/dba/index.html

SafeVarargsアノテーション

総称型の可変長引数を持つメソッドのワーニングを抑制するためのjdk7から追加されたアノテーション。

例えば、以下のコードをコンパイルした場合、ワーニングとして「Hoge.javaの操作は、未チェックまたは安全ではありません。」が出力される。
public class Hoge {

    public static void main(String[] args) {
        Collection collection = hoge("1", "2");
    }

    public static  Collection hoge(T... elements) {
        return Arrays.asList(elements);
    }
}

このワーニングを抑制するために、「SafeVarargs」を対象のメソッドhogeに追加をしてあげるとよい。
ちなみに、このアノテーションはstaticかfainalなメソッドにしか設定できないので、非finalなインスタンスメソッドに設定すると、以下のようなコンパイルエラーが発生する。
エラー: SafeVarargs注釈が無効です。インスタンス・メソッドhoge(T...)はfinalではありません。
Tが型変数の場合:
メソッド hoge(T...)で宣言されているT extends Object

Project Euler-Problem 2をscalaで解いてみる

問題内容

Each new term in the Fibonacci sequence is generated by adding the previous two terms. By starting with 1 and 2, the first 10 terms will be:

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...

By considering the terms in the Fibonacci sequence whose values do not exceed four million, find the sum of the even-valued terms.


訳すと・・・

フィボナッチ数列の項は前の2つの項の和である。 最初の2項を 1, 2 とすれば、最初の10項は以下の通りである。

1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
数列の項の値が400万を超えない範囲で、偶数値の項の総和を求めよ。

scalaのコード

fibo関数でフィボナッチ数列をListにつめていきます。400万を超えない範囲でとなっているので、400万を超えた時点でfibo関数から抜けます。
その結果のListから偶数でフィルタをかけて、sum関数で合計を求めて終わりです。
def fibo(n1: Int, n2: Int): List[Int] = {
  val n = n1 + n2
  if (n > 4000000) {
    Nil
  } else {
    n :: fibo(n2, n)
  }
}
val sum = fibo(0, 1).filter(_ % 2 == 0).sum
println("sum = " + sum)

2012年3月20日火曜日

最近読んだ本


Clean Coder プロフェッショナルプログラマへの道

この本は非常に面白かったです。自分がいかに社畜かを知れたのもよかったです。
何よりも、著者がIntelliJユーザと言うだけでもこの本は買いですね。




Jenkins実践入門

ciって何?ci導入してみたいんだけどって人は是非読んでみると良いです。
非常に分かりやすく書いてあるので、ci(jenkins)初心者にはかなりよいかと。




jQuery in Action

仕事でjQuery使うことになったのでとりあえず読んでみた。
jQueryの基本的な知識を得るには十分な本です。サンプルも豊富だし、セレクタやイベントハンドラを学習しやすくするための、サンプルコードがあるのもありがたい。

洋書なのがあれだけど、jQuery勉強したいなら読んでみるのもありかな。

2012年3月10日土曜日

[jQuery]拡張して関数を追加する方法

jQueryを拡張する方法。

拡張方法としては、jQueryのオブジェクトに関数を追加してあげるだけ。

jQueryオブジェクトが「$」となっているので、「$.関数名 = function(引数)」として定義してあげる。

(function ($) {
    $.showMessage = function(msg) {
        alert(msg);
    };

    console.log($.showMessage("メッセージ"));
})(jQuery);

Project Euler-Problem 1

Project Eulerなるものの存在を知ったので言語の勉強でつかってみた。

まずは、問題1をやってみた。

問題内容

If we list all the natural numbers below 10 that are multiples of 3 or 5, we get 3, 5, 6 and 9. The sum of these multiples is 23.
Find the sum of all the multiples of 3 or 5 below 1000.

訳すとこんな感じ。

10未満の自然数のうち、3 もしくは 5 の倍数になっているものは 3, 5, 6, 9 の4つがあり、 これらの合計は 23 になる。
同じようにして、1,000 未満の 3 か 5 の倍数になっている数字の合計を求めよ。

解くために作ったプログラム

Scala

val ret = (0 to 999).filter(n =>{n % 5 == 0 || n % 3 == 0}).sum
println(ret)

Kotlin

Scalaと全く同じになるかなと思ったけど、sum関数がなかったので自作してみてます。
package p1

import java.util.Collection

fun Collection<Int<.sum():Int {
    var ret = 0
    for (item in this) {
        ret += item
    }
    return ret
}
fun main(args : Array<String>) {
    val result = (1..999).filter {it % 3 == 0 || it % 5 == 0}.sum()
    println("result = ${result}")
}

Java

いたって普通のコード。
package p1;

public class P1 {

    public static void main(String[] args) {
        int ret = 0;
        for (int i = 1; i <= 999; i++) {
            if (i % 3 ==0 || i % 5 == 0) {
                ret += i;
            }
        }
        System.out.println("ret = " + ret);
    }
}

javascript

とりあえず結果はあってる。
    var Num = function(s, e) {
        this.filter = function(func) {
            var arr = new Array();
            for (i = s; i <= e; i++) {
                if (func(i)) arr.push(i);
            }
            arr.sum = function() {
                var sum = 0;
                for (i = 0; i < this.length; i++) {
                    sum += this[i];
                }
                return sum;
            };
            return arr;
        }

    };

    var ret = new Num(1, 999).filter(function(val) {
        return val % 3 == 0 || val % 5 == 0;
    }).sum();
    document.write(ret);

groovy

def sum = (1..999).grep({it % 3 == 0 || it % 5 == 0}).sum()
println "sum = $sum"

2012年3月9日金曜日

Webエンジニアのためのデータベース技術入門

Webエンジニアのためのデータベース技術入門を購入。

mysql関連書籍をいくつか出している人が書いているので、良書な予感。


2012年3月8日木曜日

[jQuery]grep関数

jQueryのgrep関数を使って、配列の内容にフィルタかけてみたメモ。

grep関数の仕様

grep(array,callback,invert)

array    -> grepをかけたい配列オブジェクト
callback -> フィルタ条件をもつ関数オブジェクト。戻り値でbooleanを返したげる。
invert   -> これをtrueにしてあげると、callback関数の条件を逆にできる。(falseのものだけをフィルタする場合は、trueにするイメージ)

    var arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
    (function ($) {
        // 5以下の要素のみを抽出
        var filter = $.grep(arr, function (value) {
            return value <= 5;
        });

        // 3つめのパラメータがtrueなので、6以上の要素のみが抽出される。
        var filter = $.grep(arr, function (value) {
            return value <= 5;
        }, true);

        // 以下のように正規表現も使えます。
        var ret = $.grep(["100", "200", "300"], function (value) {
            return value.match(/[12]00/);
        });
    })(jQuery);

2012年3月4日日曜日

[ruby on rails]IntelliJでscaffold

Intellijをつかってscaffoldingして、rakeコマンドでデータベースにテーブル作成して画面を動かしてみた。

1.scaffoldコマンドの実行


1.1.tool->Run Rails Generator...を選択



1.2.表示されたダイアログで、scaffoldコマンドを選択


1.3.scaffoldコマンドのModel名などを入力する画面が表示されるので、必要な値を入力してOKを選択

今回は、モデル名に「user」、フィールドに「userId」と「userName」を入力してみました。
このダイアログは、1つの入力エリアにscaffoldの引数をすべて入力しないといけないので正直使いづらい・・・



一応、こんな感じに型は補完してくれますがちょっと微妙

1.4.生成されたものを見てみよう

こんな感じにコントローラやモデルが出力されてます。ViewとHelperはコントローラ配下からたどれるようになっていて、モデルだけ分離されていますね。

2.次はテーブルを作ろう

scaffoldで自動生成が終わると、データベースのマイグレーションファイルが出力されるのでテーブル作ってあげよう。
今回、自動生成されたファイルはこんな感じ。
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :userId
      t.string :userName

      t.timestamps
    end
  end
end

2.1.データベースのマイグレーションをしよう

データベースのマイグレーションは、run->Resume Programからたどった先のrakeコマンドで行います。 まずは、Resume Programをメニューからたどっていきます。

2.2.debug実行用のダイアログが表示されるので、db:migrateを選択する。

このときに注意しないといけないのは、debug実行で実行しないこと。普通にクリックすると、debug実行になるのでshiftを押しながらクリックすること。

2.3.DataSourceウィンドウでテーブル出来たことを確認

3.画面動かしてみよう

画面でたっ!!簡単ですね。 画面の動かし方は、こちらを見てね。→IntelliJでruby on rails


jQueryを使うときは$の競合に気をつける

jQueryを使うときは、jQueryの別名参照の「$」を使用するけど、これの競合に注意しないといけないらしい。

例えば、こんなコードの用に「$」が既に定義されていた場合、$('body')はjQueryの呼び出しではなく、文字列変数の「$」を参照しているだけなので実行時にエラーになってしまう。(普通はこんなコードはないけど)

エラーの内容
Uncaught TypeError: Property '$' of object [object DOMWindow] is not a function

この競合を解決するために、jQueryを使うときは「$」を直接使うのではなく無名関数でwrapして使ってあげるのが一般的らしい。無名関数を使った場合は、こんな感じになるようだ。
これは、無名関数で引数を1つ(この場合は「$」)受け取っている。その引数には、最後のjQueryオブジェクトを渡してあげる。
あんまり理解出来なかったが、無名関数全体を括弧で囲んであげて、最後の「(jQuery)」部分でjQueryオブジェクトを引数に指定してあげてるようだ。